Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.37% covered (warning)
85.37%
70 / 82
83.33% covered (warning)
83.33%
10 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
LexemeHandler
85.37% covered (warning)
85.37%
70 / 82
83.33% covered (warning)
83.33%
10 / 12
25.81
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
 factory
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
 getActionOverrides
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
 makeEmptyEntity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makeEntityRedirectContent
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 supportsRedirects
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newEntityContent
23.08% covered (danger)
23.08%
3 / 13
0.00% covered (danger)
0.00%
0 / 1
29.30
 makeEntityId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEntityType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSpecialPageForCreation
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIdForTitle
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
 normalizeFragmentToId
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare( strict_types = 1 );
4
5namespace Wikibase\Lexeme\MediaWiki\Content;
6
7use Article;
8use MediaWiki\Context\IContextSource;
9use MediaWiki\Title\Title;
10use Psr\Container\ContainerInterface;
11use UnexpectedValueException;
12use Wikibase\DataModel\Entity\EntityId;
13use Wikibase\DataModel\Entity\EntityIdParser;
14use Wikibase\DataModel\Entity\EntityRedirect;
15use Wikibase\DataModel\Services\Lookup\EntityLookup;
16use Wikibase\Lexeme\DataAccess\Store\LemmaLookup;
17use Wikibase\Lexeme\Domain\Model\Form;
18use Wikibase\Lexeme\Domain\Model\FormId;
19use Wikibase\Lexeme\Domain\Model\Lexeme;
20use Wikibase\Lexeme\Domain\Model\LexemeId;
21use Wikibase\Lexeme\Domain\Model\LexemeSubEntityId;
22use Wikibase\Lexeme\Domain\Model\Sense;
23use Wikibase\Lexeme\Domain\Model\SenseId;
24use Wikibase\Lexeme\MediaWiki\Actions\LexemeHistoryAction;
25use Wikibase\Lexeme\MediaWiki\Actions\ViewLexemeAction;
26use Wikibase\Lexeme\Presentation\Formatters\LexemeTermFormatter;
27use Wikibase\Lexeme\WikibaseLexemeServices;
28use Wikibase\Lib\Store\EntityContentDataCodec;
29use Wikibase\Lib\Store\EntityIdLookup;
30use Wikibase\Repo\Actions\EditEntityAction;
31use Wikibase\Repo\Actions\SubmitEntityAction;
32use Wikibase\Repo\Content\EntityHandler;
33use Wikibase\Repo\Content\EntityHolder;
34use Wikibase\Repo\Content\EntityInstanceHolder;
35use Wikibase\Repo\Search\Fields\FieldDefinitions;
36use Wikibase\Repo\Validators\EntityConstraintProvider;
37use Wikibase\Repo\Validators\ValidatorErrorLocalizer;
38use Wikibase\Repo\WikibaseRepo;
39
40/**
41 * @license GPL-2.0-or-later
42 * @author Amir Sarabadani <ladsgroup@gmail.com>
43 */
44class LexemeHandler extends EntityHandler {
45
46    private EntityIdLookup $entityIdLookup;
47
48    private EntityLookup $entityLookup;
49
50    private LemmaLookup $lemmaLookup;
51    private LexemeTermFormatter $lexemeTermFormatter;
52
53    public function __construct(
54        EntityContentDataCodec $contentCodec,
55        EntityConstraintProvider $constraintProvider,
56        ValidatorErrorLocalizer $errorLocalizer,
57        EntityIdParser $entityIdParser,
58        EntityIdLookup $entityIdLookup,
59        EntityLookup $entityLookup,
60        FieldDefinitions $lexemeFieldDefinitions,
61        LemmaLookup $lemmaLookup,
62        LexemeTermFormatter $lexemeTermFormatter,
63        ?callable $legacyExportFormatDetector = null
64    ) {
65        parent::__construct(
66            LexemeContent::CONTENT_MODEL_ID,
67            null, // TODO: this is unused in the parent class and has a TODO to be removed
68            $contentCodec,
69            $constraintProvider,
70            $errorLocalizer,
71            $entityIdParser,
72            $lexemeFieldDefinitions,
73            $legacyExportFormatDetector
74        );
75
76        $this->entityIdLookup = $entityIdLookup;
77        $this->entityLookup = $entityLookup;
78        $this->lemmaLookup = $lemmaLookup;
79        $this->lexemeTermFormatter = $lexemeTermFormatter;
80    }
81
82    /**
83     * This is intended to be used in the entity types wiring.
84     */
85    public static function factory( ContainerInterface $services, IContextSource $context ): self {
86        return new self(
87            WikibaseRepo::getEntityContentDataCodec( $services ),
88            WikibaseRepo::getEntityConstraintProvider( $services ),
89            WikibaseRepo::getValidatorErrorLocalizer( $services ),
90            WikibaseRepo::getEntityIdParser( $services ),
91            WikibaseRepo::getEntityIdLookup( $services ),
92            WikibaseRepo::getEntityLookup( $services ),
93            WikibaseRepo::getFieldDefinitionsFactory( $services )
94                ->getFieldDefinitionsByType( Lexeme::ENTITY_TYPE ),
95            WikibaseLexemeServices::getLemmaLookup( $services ),
96            new LexemeTermFormatter(
97                $context
98                    ->msg( 'wikibaselexeme-presentation-lexeme-display-label-separator-multiple-lemma' )
99                    ->escaped()
100            )
101        );
102    }
103
104    /**
105     * @see ContentHandler::getActionOverrides
106     */
107    public function getActionOverrides(): array {
108        return [
109            'history' => function (
110                Article $article,
111                IContextSource $context
112            ) {
113                return new LexemeHistoryAction(
114                    $article,
115                    $context,
116                    $this->entityIdLookup,
117                    $this->lemmaLookup,
118                    $this->lexemeTermFormatter
119                );
120            },
121            'view' => ViewLexemeAction::class,
122            'edit' => EditEntityAction::SPEC,
123            'submit' => SubmitEntityAction::SPEC,
124        ];
125    }
126
127    public function makeEmptyEntity(): Lexeme {
128        return new Lexeme();
129    }
130
131    public function makeEntityRedirectContent( EntityRedirect $redirect ): LexemeContent {
132        $title = $this->getTitleForId( $redirect->getTargetId() );
133        return LexemeContent::newFromRedirect( $redirect, $title );
134    }
135
136    /** @inheritDoc */
137    public function supportsRedirects(): bool {
138        return true;
139    }
140
141    /**
142     * @see EntityHandler::newEntityContent
143     */
144    protected function newEntityContent( ?EntityHolder $entityHolder = null ): LexemeContent {
145        if ( $entityHolder !== null && $entityHolder->getEntityType() === Form::ENTITY_TYPE ) {
146            $formId = $entityHolder->getEntityId();
147            if ( !( $formId instanceof FormId ) ) {
148                throw new UnexpectedValueException( '$formId must be a FormId' );
149            }
150            $lexemeId = $formId->getLexemeId();
151            $entityHolder = new EntityInstanceHolder( $this->entityLookup->getEntity( $lexemeId ) );
152        }
153        if ( $entityHolder !== null && $entityHolder->getEntityType() === Sense::ENTITY_TYPE ) {
154            $senseId = $entityHolder->getEntityId();
155            if ( !( $senseId instanceof SenseId ) ) {
156                throw new UnexpectedValueException( '$senseId must be a SenseId' );
157            }
158            $lexemeId = $senseId->getLexemeId();
159            $entityHolder = new EntityInstanceHolder( $this->entityLookup->getEntity( $lexemeId ) );
160        }
161        return new LexemeContent( $entityHolder );
162    }
163
164    /**
165     * @param string $id
166     */
167    public function makeEntityId( $id ): LexemeId {
168        return new LexemeId( $id );
169    }
170
171    public function getEntityType(): string {
172        return Lexeme::ENTITY_TYPE;
173    }
174
175    public function getSpecialPageForCreation(): string {
176        return 'NewLexeme';
177    }
178
179    public function getIdForTitle( Title $target ): EntityId {
180        $lexemeId = parent::getIdForTitle( $target );
181
182        if ( $target->hasFragment() ) {
183            $id = $this->normalizeFragmentToId( $target );
184            // TODO use an EntityIdParser (but parent's $this->entityIdParser is currently private)
185            if ( preg_match( FormId::PATTERN, $id ) ) {
186                $lexemeSubEntityId = new FormId( $id );
187            }
188            if ( preg_match( SenseId::PATTERN, $id ) ) {
189                $lexemeSubEntityId = new SenseId( $id );
190            }
191
192            if (
193                isset( $lexemeSubEntityId ) &&
194                $lexemeSubEntityId->getLexemeId()->equals( $lexemeId )
195            ) {
196                return $lexemeSubEntityId;
197            }
198        }
199
200        return $lexemeId;
201    }
202
203    private function normalizeFragmentToId( Title $target ): string {
204        $fragment = $target->getFragment();
205        if ( strpos( $fragment, LexemeSubEntityId::SUBENTITY_ID_SEPARATOR ) === false ) {
206            $fragment = $target->getText() . LexemeSubEntityId::SUBENTITY_ID_SEPARATOR . $fragment;
207        }
208        return $fragment;
209    }
210
211}