Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.39% |
61 / 62 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
AllowedUnitsChecker | |
98.39% |
61 / 62 |
|
80.00% |
4 / 5 |
20 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getSupportedContextTypes | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
getDefaultContextTypes | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
getSupportedEntityTypes | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
checkConstraint | |
100.00% |
36 / 36 |
|
100.00% |
1 / 1 |
9 | |||
checkUnitless | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
standardize | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
checkConstraintParameters | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker; |
4 | |
5 | use DataValues\UnboundedQuantityValue; |
6 | use Wikibase\DataModel\Entity\ItemId; |
7 | use Wikibase\DataModel\Snak\PropertyValueSnak; |
8 | use Wikibase\Lib\Units\UnitConverter; |
9 | use WikibaseQuality\ConstraintReport\Constraint; |
10 | use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker; |
11 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context; |
12 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException; |
13 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser; |
14 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\UnitsParameter; |
15 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage; |
16 | use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult; |
17 | use WikibaseQuality\ConstraintReport\Role; |
18 | |
19 | /** |
20 | * @author Lucas Werkmeister |
21 | * @license GPL-2.0-or-later |
22 | */ |
23 | class AllowedUnitsChecker implements ConstraintChecker { |
24 | |
25 | /** |
26 | * @var ConstraintParameterParser |
27 | */ |
28 | private $constraintParameterParser; |
29 | |
30 | /** |
31 | * @var UnitConverter|null |
32 | */ |
33 | private $unitConverter; |
34 | |
35 | /** |
36 | * @param ConstraintParameterParser $constraintParameterParser |
37 | * @param UnitConverter|null $unitConverter |
38 | */ |
39 | public function __construct( |
40 | ConstraintParameterParser $constraintParameterParser, |
41 | UnitConverter $unitConverter = null |
42 | ) { |
43 | $this->constraintParameterParser = $constraintParameterParser; |
44 | $this->unitConverter = $unitConverter; |
45 | } |
46 | |
47 | /** |
48 | * @codeCoverageIgnore This method is purely declarative. |
49 | */ |
50 | public function getSupportedContextTypes() { |
51 | return self::ALL_CONTEXT_TYPES_SUPPORTED; |
52 | } |
53 | |
54 | /** |
55 | * @codeCoverageIgnore This method is purely declarative. |
56 | */ |
57 | public function getDefaultContextTypes() { |
58 | return Context::ALL_CONTEXT_TYPES; |
59 | } |
60 | |
61 | /** @codeCoverageIgnore This method is purely declarative. */ |
62 | public function getSupportedEntityTypes() { |
63 | return self::ALL_ENTITY_TYPES_SUPPORTED; |
64 | } |
65 | |
66 | /** |
67 | * Checks an “allowed units” constraint. |
68 | * |
69 | * @param Context $context |
70 | * @param Constraint $constraint |
71 | * |
72 | * @throws ConstraintParameterException |
73 | * @return CheckResult |
74 | */ |
75 | public function checkConstraint( Context $context, Constraint $constraint ) { |
76 | $constraintParameters = $constraint->getConstraintParameters(); |
77 | $unitsParameter = $this->constraintParameterParser |
78 | ->parseUnitsParameter( |
79 | $constraintParameters, |
80 | $constraint->getConstraintTypeItemId() |
81 | ); |
82 | |
83 | $snak = $context->getSnak(); |
84 | if ( !$snak instanceof PropertyValueSnak ) { |
85 | // nothing to check |
86 | return new CheckResult( $context, $constraint, CheckResult::STATUS_COMPLIANCE ); |
87 | } |
88 | |
89 | $dataValue = $snak->getDataValue(); |
90 | if ( !$dataValue instanceof UnboundedQuantityValue ) { |
91 | $message = ( new ViolationMessage( 'wbqc-violation-message-value-needed-of-type' ) ) |
92 | ->withEntityId( new ItemId( $constraint->getConstraintTypeItemId() ), Role::CONSTRAINT_TYPE_ITEM ) |
93 | ->withDataValueType( 'quantity' ); |
94 | return new CheckResult( $context, $constraint, CheckResult::STATUS_VIOLATION, $message ); |
95 | } |
96 | |
97 | if ( $dataValue->getUnit() === '1' ) { |
98 | return $this->checkUnitless( $context, $constraint, $unitsParameter, $snak ); |
99 | } |
100 | |
101 | $status = CheckResult::STATUS_VIOLATION; |
102 | $actualUnit = $this->standardize( $dataValue )->getUnit(); |
103 | foreach ( $unitsParameter->getUnitQuantities() as $unitQuantity ) { |
104 | $allowedUnit = $this->standardize( $unitQuantity )->getUnit(); |
105 | if ( $actualUnit === $allowedUnit ) { |
106 | $status = CheckResult::STATUS_COMPLIANCE; |
107 | break; |
108 | } |
109 | } |
110 | |
111 | if ( $status === CheckResult::STATUS_VIOLATION ) { |
112 | if ( $unitsParameter->getUnitItemIds() === [] ) { |
113 | $message = ( new ViolationMessage( 'wbqc-violation-message-units-none' ) ) |
114 | ->withEntityId( $snak->getPropertyId(), Role::CONSTRAINT_PROPERTY ); |
115 | } else { |
116 | $messageKey = $unitsParameter->getUnitlessAllowed() ? |
117 | 'wbqc-violation-message-units-or-none' : |
118 | 'wbqc-violation-message-units'; |
119 | $message = ( new ViolationMessage( $messageKey ) ) |
120 | ->withEntityId( $snak->getPropertyId(), Role::CONSTRAINT_PROPERTY ) |
121 | ->withEntityIdList( $unitsParameter->getUnitItemIds(), Role::CONSTRAINT_PARAMETER_VALUE ); |
122 | } |
123 | } else { |
124 | $message = null; |
125 | } |
126 | |
127 | return new CheckResult( $context, $constraint, $status, $message ); |
128 | } |
129 | |
130 | /** |
131 | * @param Context $context |
132 | * @param Constraint $constraint |
133 | * @param UnitsParameter $unitsParameter |
134 | * @param PropertyValueSnak $snak |
135 | * @return CheckResult |
136 | */ |
137 | private function checkUnitless( |
138 | Context $context, |
139 | Constraint $constraint, |
140 | UnitsParameter $unitsParameter, |
141 | PropertyValueSnak $snak |
142 | ) { |
143 | if ( $unitsParameter->getUnitlessAllowed() ) { |
144 | $message = null; |
145 | $status = CheckResult::STATUS_COMPLIANCE; |
146 | } else { |
147 | $message = ( new ViolationMessage( 'wbqc-violation-message-units' ) ) |
148 | ->withEntityId( $snak->getPropertyId(), Role::CONSTRAINT_PROPERTY ) |
149 | ->withEntityIdList( $unitsParameter->getUnitItemIds(), Role::CONSTRAINT_PARAMETER_VALUE ); |
150 | $status = CheckResult::STATUS_VIOLATION; |
151 | } |
152 | |
153 | return new CheckResult( $context, $constraint, $status, $message ); |
154 | } |
155 | |
156 | /** |
157 | * Convert $value to standard units. |
158 | * |
159 | * @param UnboundedQuantityValue $value |
160 | * @return UnboundedQuantityValue |
161 | */ |
162 | private function standardize( UnboundedQuantityValue $value ) { |
163 | if ( $this->unitConverter === null ) { |
164 | return $value; |
165 | } |
166 | |
167 | $standard = $this->unitConverter->toStandardUnits( $value ); |
168 | if ( $standard !== null ) { |
169 | return $standard; |
170 | } else { |
171 | return $value; |
172 | } |
173 | } |
174 | |
175 | public function checkConstraintParameters( Constraint $constraint ) { |
176 | $constraintParameters = $constraint->getConstraintParameters(); |
177 | $exceptions = []; |
178 | try { |
179 | $this->constraintParameterParser->parseItemsParameter( |
180 | $constraintParameters, |
181 | $constraint->getConstraintTypeItemId(), |
182 | true |
183 | ); |
184 | } catch ( ConstraintParameterException $e ) { |
185 | $exceptions[] = $e; |
186 | } |
187 | return $exceptions; |
188 | } |
189 | |
190 | } |