Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
70.93% covered (warning)
70.93%
61 / 86
37.50% covered (danger)
37.50%
3 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
LexemePatcher
70.93% covered (warning)
70.93%
61 / 86
37.50% covered (danger)
37.50%
3 / 8
57.16
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 canPatchEntityType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 patchEntity
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
3
 getPatchedItemId
60.00% covered (warning)
60.00%
6 / 10
0.00% covered (danger)
0.00%
0 / 1
6.60
 patchNextFormId
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
 patchNextSenseId
12.50% covered (danger)
12.50%
1 / 8
0.00% covered (danger)
0.00%
0 / 1
14.72
 patchForms
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
7.01
 patchSenses
36.84% covered (danger)
36.84%
7 / 19
0.00% covered (danger)
0.00%
0 / 1
19.34
1<?php
2
3namespace Wikibase\Lexeme\Domain\Diff;
4
5use Diff\DiffOp\Diff\Diff;
6use Diff\DiffOp\DiffOpAdd;
7use Diff\DiffOp\DiffOpChange;
8use Diff\DiffOp\DiffOpRemove;
9use Diff\Patcher\PatcherException;
10use InvalidArgumentException;
11use Wikibase\DataModel\Entity\EntityDocument;
12use Wikibase\DataModel\Entity\ItemId;
13use Wikibase\DataModel\Services\Diff\EntityDiff;
14use Wikibase\DataModel\Services\Diff\EntityPatcherStrategy;
15use Wikibase\DataModel\Services\Diff\StatementListPatcher;
16use Wikibase\DataModel\Services\Diff\TermListPatcher;
17use Wikibase\Lexeme\Domain\Model\Lexeme;
18use Wikibase\Lexeme\Domain\Model\LexemePatchAccess;
19use Wikimedia\Assert\Assert;
20
21/**
22 * @license GPL-2.0-or-later
23 * @author Amir Sarabadani <ladsgroup@gmail.com>
24 * @author Thiemo Kreuz
25 */
26class LexemePatcher implements EntityPatcherStrategy {
27
28    /**
29     * @var TermListPatcher
30     */
31    private $termListPatcher;
32
33    /**
34     * @var StatementListPatcher
35     */
36    private $statementListPatcher;
37
38    /**
39     * @var FormPatcher
40     */
41    private $formPatcher;
42
43    /**
44     * @var SensePatcher
45     */
46    private $sensePatcher;
47
48    public function __construct() {
49        $this->termListPatcher = new TermListPatcher();
50        $this->statementListPatcher = new StatementListPatcher();
51        $this->formPatcher = new FormPatcher();
52        $this->sensePatcher = new SensePatcher();
53    }
54
55    /**
56     * @param string $entityType
57     *
58     * @return bool
59     */
60    public function canPatchEntityType( $entityType ) {
61        return $entityType === Lexeme::ENTITY_TYPE;
62    }
63
64    /**
65     * @param Lexeme $lexeme
66     * @param LexemeDiff $patch
67     *
68     * @throws InvalidArgumentException
69     */
70    public function patchEntity( EntityDocument $lexeme, EntityDiff $patch ) {
71        Assert::parameterType( Lexeme::class, $lexeme, '$lexeme' );
72        Assert::parameterType( LexemeDiff::class, $patch, '$patch' );
73
74        $this->termListPatcher->patchTermList( $lexeme->getLemmas(), $patch->getLemmasDiff() );
75
76        $this->statementListPatcher->patchStatementList(
77            $lexeme->getStatements(),
78            $patch->getClaimsDiff()
79        );
80
81        $itemId = $this->getPatchedItemId( $patch->getLexicalCategoryDiff() );
82        if ( $itemId !== false ) {
83            $lexeme->setLexicalCategory( $itemId );
84        }
85
86        $itemId = $this->getPatchedItemId( $patch->getLanguageDiff() );
87        if ( $itemId !== false ) {
88            $lexeme->setLanguage( $itemId );
89        }
90
91        $this->patchNextFormId( $lexeme, $patch );
92        $this->patchForms( $lexeme, $patch );
93
94        $this->patchNextSenseId( $lexeme, $patch );
95        $this->patchSenses( $lexeme, $patch );
96    }
97
98    /**
99     * @param Diff $patch
100     *
101     * @throws PatcherException
102     * @return ItemId|null|false False in case the diff is valid, but does not contain a change.
103     */
104    private function getPatchedItemId( Diff $patch ) {
105        if ( $patch->isEmpty() ) {
106            return false;
107        }
108
109        $diffOp = $patch['id'];
110
111        switch ( true ) {
112            case $diffOp instanceof DiffOpAdd:
113                return $diffOp->getNewValue();
114
115            case $diffOp instanceof DiffOpChange:
116                return $diffOp->getNewValue();
117
118            case $diffOp instanceof DiffOpRemove:
119                return null;
120        }
121
122        throw new PatcherException( 'Invalid ItemId diff' );
123    }
124
125    private function patchNextFormId( Lexeme $entity, LexemeDiff $patch ) {
126        // FIXME: Why is this a loop? The nextFormId field is not an array!
127        foreach ( $patch->getNextFormIdDiff() as $nextFormIdDiff ) {
128            if ( !( $nextFormIdDiff instanceof DiffOpChange ) ) {
129                throw new PatcherException( 'Invalid forms list diff' );
130            }
131
132            $newNumber = $nextFormIdDiff->getNewValue();
133            if ( $newNumber > $entity->getNextFormId() ) {
134                $entity->patch( static function ( LexemePatchAccess $patchAccess ) use ( $newNumber ) {
135                    $patchAccess->increaseNextFormIdTo( $newNumber );
136                } );
137            }
138        }
139    }
140
141    private function patchNextSenseId( Lexeme $entity, LexemeDiff $patch ) {
142        // FIXME: Same as above
143        foreach ( $patch->getNextSenseIdDiff() as $nextSenseIdDiff ) {
144            if ( !( $nextSenseIdDiff instanceof DiffOpChange ) ) {
145                throw new PatcherException( 'Invalid senses list diff' );
146            }
147
148            $newNumber = $nextSenseIdDiff->getNewValue();
149            if ( $newNumber > $entity->getNextSenseId() ) {
150                $entity->patch( static function ( LexemePatchAccess $patchAccess ) use ( $newNumber ) {
151                    $patchAccess->increaseNextSenseIdTo( $newNumber );
152                } );
153            }
154        }
155    }
156
157    private function patchForms( Lexeme $lexeme, LexemeDiff $patch ) {
158        foreach ( $patch->getFormsDiff() as $formDiff ) {
159            switch ( true ) {
160                case $formDiff instanceof AddFormDiff:
161                    $form = $formDiff->getAddedForm();
162                    $lexeme->patch(
163                        static function ( LexemePatchAccess $patchAccess ) use ( $form ) {
164                            $patchAccess->addForm( $form );
165                        }
166                    );
167                    break;
168
169                case $formDiff instanceof RemoveFormDiff:
170                    $lexeme->removeForm( $formDiff->getRemovedFormId() );
171                    break;
172
173                case $formDiff instanceof ChangeFormDiffOp:
174                    try {
175                        $form = $lexeme->getForm( $formDiff->getFormId() );
176                    } catch ( \OutOfRangeException $e ) {
177                        // form does not exist (anymore? may have been removed), nothing to patch (T326768)
178                        break;
179                    }
180                    $this->formPatcher->patch( $form, $formDiff );
181                    break;
182
183                default:
184                    throw new PatcherException( 'Invalid forms list diff: ' . get_class( $formDiff ) );
185            }
186        }
187    }
188
189    private function patchSenses( Lexeme $lexeme, LexemeDiff $patch ) {
190        foreach ( $patch->getSensesDiff() as $senseDiff ) {
191            switch ( true ) {
192                case $senseDiff instanceof AddSenseDiff:
193                    $sense = $senseDiff->getAddedSense();
194                    $lexeme->patch(
195                        static function ( LexemePatchAccess $patchAccess ) use ( $sense ) {
196                            $patchAccess->addSense( $sense );
197                        }
198                    );
199                    break;
200
201                case $senseDiff instanceof RemoveSenseDiff:
202                    $lexeme->removeSense( $senseDiff->getRemovedSenseId() );
203                    break;
204
205                case $senseDiff instanceof ChangeSenseDiffOp:
206                    try {
207                        $sense = $lexeme->getSense( $senseDiff->getSenseId() );
208                    } catch ( \OutOfRangeException $e ) {
209                        // sense does not exist (anymore? may have been removed), nothing to patch (T284061)
210                        break;
211                    }
212                    $this->sensePatcher->patchEntity( $sense, $senseDiff );
213                    break;
214
215                default:
216                    throw new PatcherException( 'Invalid senses list diff: ' . get_class( $senseDiff ) );
217            }
218        }
219    }
220
221}