Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.57% |
69 / 70 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
EntitySchemaPatcher | |
98.57% |
69 / 70 |
|
80.00% |
4 / 5 |
22 | |
0.00% |
0 / 1 |
patchSchema | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
patchFingerprint | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
1 | |||
patchTermlist | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
patchTerm | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
10.01 | |||
patchString | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | |
3 | declare( strict_types = 1 ); |
4 | |
5 | namespace EntitySchema\Services\Diff; |
6 | |
7 | use Diff\DiffOp\AtomicDiffOp; |
8 | use Diff\DiffOp\Diff\Diff; |
9 | use Diff\DiffOp\DiffOp; |
10 | use Diff\DiffOp\DiffOpAdd; |
11 | use Diff\DiffOp\DiffOpChange; |
12 | use Diff\DiffOp\DiffOpRemove; |
13 | use Diff\Patcher\PatcherException; |
14 | use EntitySchema\Services\Converter\FullArrayEntitySchemaData; |
15 | |
16 | /** |
17 | * @license GPL-2.0-or-later |
18 | */ |
19 | class EntitySchemaPatcher { |
20 | |
21 | /** |
22 | * @param FullArrayEntitySchemaData $baseSchema |
23 | * @param Diff $patch |
24 | * |
25 | * @return FullArrayEntitySchemaData |
26 | * |
27 | * @throws PatcherException throws exception if patch cannot be applied |
28 | */ |
29 | public function patchSchema( FullArrayEntitySchemaData $baseSchema, Diff $patch ): FullArrayEntitySchemaData { |
30 | $patchedSchema = $this->patchFingerprint( $baseSchema->data, $patch ); |
31 | |
32 | $patchedSchema['schemaText'] = $this->patchString( |
33 | $baseSchema->data['schemaText'] ?? '', |
34 | $patch['schemaText'] ?? null |
35 | ); |
36 | |
37 | return new FullArrayEntitySchemaData( $patchedSchema ); |
38 | } |
39 | |
40 | private function patchFingerprint( array $baseSchema, Diff $patch ): array { |
41 | $aliasGroupPatcher = new AliasGroupListPatcher(); |
42 | |
43 | $patchedSchema = [ |
44 | 'labels' => $this->patchTermlist( |
45 | $baseSchema['labels'] ?? [], |
46 | $patch['labels'] ?? null |
47 | ), |
48 | 'descriptions' => $this->patchTermlist( |
49 | $baseSchema['descriptions'] ?? [], |
50 | $patch['descriptions'] ?? null |
51 | ), |
52 | 'aliases' => $aliasGroupPatcher->patchAliasGroupList( |
53 | $baseSchema['aliases'] ?? [], |
54 | $patch['aliases'] ?? null |
55 | ), |
56 | ]; |
57 | |
58 | return $patchedSchema; |
59 | } |
60 | |
61 | private function patchTermlist( array $terms, Diff $patch = null ): array { |
62 | if ( $patch === null ) { |
63 | return $terms; |
64 | } |
65 | foreach ( $patch as $lang => $diffOp ) { |
66 | $terms = $this->patchTerm( $terms, $lang, $diffOp ); |
67 | } |
68 | return $terms; |
69 | } |
70 | |
71 | private function patchTerm( array $terms, string $lang, AtomicDiffOp $diffOp ): array { |
72 | switch ( true ) { |
73 | case $diffOp instanceof DiffOpAdd: |
74 | if ( !empty( $terms[$lang] ) ) { |
75 | throw new PatcherException( 'Term already exists' ); |
76 | } |
77 | $terms[$lang] = $diffOp->getNewValue(); |
78 | break; |
79 | |
80 | case $diffOp instanceof DiffOpChange: |
81 | if ( empty( $terms[$lang] ) |
82 | || $terms[$lang] !== $diffOp->getOldValue() |
83 | ) { |
84 | throw new PatcherException( 'Term had been changed' ); |
85 | } |
86 | $terms[$lang] = $diffOp->getNewValue(); |
87 | break; |
88 | |
89 | case $diffOp instanceof DiffOpRemove: |
90 | if ( !empty( $terms[$lang] ) |
91 | && $terms[$lang] !== $diffOp->getOldValue() |
92 | ) { |
93 | throw new PatcherException( 'Term had been changed' ); |
94 | } |
95 | unset( $terms[$lang] ); |
96 | break; |
97 | |
98 | default: |
99 | throw new PatcherException( 'Invalid terms diff' ); |
100 | } |
101 | |
102 | return $terms; |
103 | } |
104 | |
105 | /** |
106 | * @param string $base |
107 | * @param DiffOp|null $diffOp |
108 | * |
109 | * @return string |
110 | */ |
111 | private function patchString( string $base, DiffOp $diffOp = null ): string { |
112 | switch ( true ) { |
113 | case $diffOp instanceof DiffOpAdd: |
114 | $from = ''; |
115 | $to = $diffOp->getNewValue(); |
116 | break; |
117 | case $diffOp instanceof DiffOpRemove: |
118 | $from = $diffOp->getOldValue(); |
119 | $to = ''; |
120 | break; |
121 | case $diffOp instanceof DiffOpChange: |
122 | $from = $diffOp->getOldValue(); |
123 | $to = $diffOp->getNewValue(); |
124 | break; |
125 | default: |
126 | $from = $to = null; |
127 | } |
128 | if ( $from !== $to ) { |
129 | $ok = wfMerge( |
130 | $from, |
131 | $to, |
132 | $base, |
133 | $result |
134 | ); |
135 | if ( !$ok ) { |
136 | throw new PatcherException( 'Patching the Schema failed because it has been changed.' ); |
137 | } |
138 | return trim( $result ); |
139 | } |
140 | |
141 | return $base; |
142 | } |
143 | |
144 | } |