Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
63 / 63 |
|
100.00% |
10 / 10 |
CRAP | |
100.00% |
1 / 1 |
MainSnakContext | |
100.00% |
63 / 63 |
|
100.00% |
10 / 10 |
27 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSnakRank | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSnakStatement | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSnakGroup | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
4 | |||
getBestStatementsPerPropertyId | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
getStatementsWithSameQualifiersForProperties | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
5 | |||
haveSameQualifiers | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
7 | |||
getSnaksWithPropertyId | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getCursor | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare( strict_types = 1 ); |
4 | |
5 | namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Context; |
6 | |
7 | use LogicException; |
8 | use Wikibase\DataModel\Entity\PropertyId; |
9 | use Wikibase\DataModel\Entity\StatementListProvidingEntity; |
10 | use Wikibase\DataModel\Snak\Snak; |
11 | use Wikibase\DataModel\Snak\SnakList; |
12 | use Wikibase\DataModel\Statement\Statement; |
13 | use Wikibase\DataModel\Statement\StatementList; |
14 | |
15 | /** |
16 | * A constraint check context for the main snak of a statement. |
17 | * |
18 | * @license GPL-2.0-or-later |
19 | */ |
20 | class MainSnakContext extends AbstractContext { |
21 | |
22 | private Statement $statement; |
23 | |
24 | public function __construct( StatementListProvidingEntity $entity, Statement $statement ) { |
25 | parent::__construct( $entity, $statement->getMainSnak() ); |
26 | |
27 | $this->statement = $statement; |
28 | } |
29 | |
30 | public function getType(): string { |
31 | return self::TYPE_STATEMENT; |
32 | } |
33 | |
34 | public function getSnakRank(): ?int { |
35 | return $this->statement->getRank(); |
36 | } |
37 | |
38 | public function getSnakStatement(): Statement { |
39 | return $this->statement; |
40 | } |
41 | |
42 | public function getSnakGroup( string $groupingMode, array $separators = [] ): array { |
43 | /** @var StatementList $statements */ |
44 | $statements = $this->entity->getStatements(); |
45 | switch ( $groupingMode ) { |
46 | case Context::GROUP_NON_DEPRECATED: |
47 | $statements = $statements->getByRank( [ |
48 | Statement::RANK_NORMAL, |
49 | Statement::RANK_PREFERRED, |
50 | ] ); |
51 | break; |
52 | case Context::GROUP_BEST_RANK: |
53 | $statements = $this->getBestStatementsPerPropertyId( $statements ); |
54 | break; |
55 | default: |
56 | throw new LogicException( 'Unknown $groupingMode ' . $groupingMode ); |
57 | } |
58 | return $this->getStatementsWithSameQualifiersForProperties( |
59 | $this->statement, |
60 | $statements, |
61 | $separators |
62 | )->getMainSnaks(); |
63 | } |
64 | |
65 | private function getBestStatementsPerPropertyId( StatementList $statements ): StatementList { |
66 | $allBestStatements = new StatementList(); |
67 | foreach ( $statements->getPropertyIds() as $propertyId ) { |
68 | $bestStatements = $statements->getByPropertyId( $propertyId ) |
69 | ->getBestStatements(); |
70 | foreach ( $bestStatements as $bestStatement ) { |
71 | $allBestStatements->addStatement( $bestStatement ); |
72 | } |
73 | } |
74 | return $allBestStatements; |
75 | } |
76 | |
77 | /** |
78 | * Returns the statements of a statement list |
79 | * which for a set of propert IDs have the same qualifiers as a certain statement. |
80 | * “unknown value” qualifiers are considered different from each other. |
81 | * |
82 | * @param Statement $currentStatement |
83 | * @param StatementList $allStatements |
84 | * @param PropertyId[] $qualifierPropertyIds |
85 | * @return StatementList |
86 | */ |
87 | private function getStatementsWithSameQualifiersForProperties( |
88 | Statement $currentStatement, |
89 | StatementList $allStatements, |
90 | array $qualifierPropertyIds |
91 | ): StatementList { |
92 | $similarStatements = new StatementList(); |
93 | foreach ( $allStatements as $statement ) { |
94 | if ( $statement === $currentStatement ) { |
95 | // if the statement has an “unknown value” qualifier, |
96 | // it might be considered different from itself, |
97 | // so add it explicitly to ensure it’s always included |
98 | $similarStatements->addStatement( $statement ); |
99 | continue; |
100 | } |
101 | foreach ( $qualifierPropertyIds as $qualifierPropertyId ) { |
102 | if ( !$this->haveSameQualifiers( $currentStatement, $statement, $qualifierPropertyId ) ) { |
103 | continue 2; |
104 | } |
105 | } |
106 | $similarStatements->addStatement( $statement ); |
107 | } |
108 | return $similarStatements; |
109 | } |
110 | |
111 | /** |
112 | * Tests whether two statements have the same qualifiers with a certain property ID. |
113 | * “unknown value” qualifiers are considered different from each other. |
114 | */ |
115 | private function haveSameQualifiers( Statement $s1, Statement $s2, PropertyId $propertyId ): bool { |
116 | $q1 = $this->getSnaksWithPropertyId( $s1->getQualifiers(), $propertyId ); |
117 | $q2 = $this->getSnaksWithPropertyId( $s2->getQualifiers(), $propertyId ); |
118 | |
119 | if ( $q1->count() !== $q2->count() ) { |
120 | return false; |
121 | } |
122 | |
123 | foreach ( $q1 as $qualifier ) { |
124 | switch ( $qualifier->getType() ) { |
125 | case 'value': |
126 | case 'novalue': |
127 | if ( !$q2->hasSnak( $qualifier ) ) { |
128 | return false; |
129 | } |
130 | break; |
131 | case 'somevalue': |
132 | return false; // all “unknown value”s are considered different from each other |
133 | } |
134 | } |
135 | |
136 | // a SnakList cannot contain the same snak more than once, |
137 | // so if every snak of q1 is also in q2 and their cardinality is identical, |
138 | // then they must be entirely identical |
139 | return true; |
140 | } |
141 | |
142 | /** |
143 | * Returns the snaks of the given snak list with the specified property ID. |
144 | */ |
145 | private function getSnaksWithPropertyId( SnakList $allSnaks, PropertyId $propertyId ): SnakList { |
146 | $snaks = new SnakList(); |
147 | /** @var Snak $snak */ |
148 | foreach ( $allSnaks as $snak ) { |
149 | if ( $snak->getPropertyId()->equals( $propertyId ) ) { |
150 | $snaks->addSnak( $snak ); |
151 | } |
152 | } |
153 | return $snaks; |
154 | } |
155 | |
156 | public function getCursor(): ContextCursor { |
157 | return new MainSnakContextCursor( |
158 | $this->entity->getId()->getSerialization(), |
159 | $this->statement->getPropertyId()->getSerialization(), |
160 | $this->getStatementGuid( $this->statement ), |
161 | $this->statement->getMainSnak()->getHash() |
162 | ); |
163 | } |
164 | |
165 | } |