1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker; |
4
|
|
|
|
5
|
|
|
use DataValues\DataValue; |
6
|
|
|
use Wikibase\DataModel\Entity\PropertyId; |
7
|
|
|
use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup; |
8
|
|
|
use Wikibase\DataModel\Snak\PropertyValueSnak; |
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\NowValue; |
15
|
|
|
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\RangeCheckerHelper; |
16
|
|
|
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage; |
17
|
|
|
use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult; |
18
|
|
|
use WikibaseQuality\ConstraintReport\ConstraintParameterRenderer; |
19
|
|
|
use WikibaseQuality\ConstraintReport\Role; |
20
|
|
|
use Wikibase\DataModel\Statement\Statement; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @author BP2014N1 |
24
|
|
|
* @license GNU GPL v2+ |
25
|
|
|
*/ |
26
|
|
|
class RangeChecker implements ConstraintChecker { |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var PropertyDataTypeLookup |
30
|
|
|
*/ |
31
|
|
|
private $propertyDataTypeLookup; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var ConstraintParameterParser |
35
|
|
|
*/ |
36
|
|
|
private $constraintParameterParser; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var RangeCheckerHelper |
40
|
|
|
*/ |
41
|
|
|
private $rangeCheckerHelper; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var ConstraintParameterRenderer |
45
|
|
|
*/ |
46
|
|
|
private $constraintParameterRenderer; |
47
|
|
|
|
48
|
|
|
public function __construct( |
49
|
|
|
PropertyDataTypeLookup $propertyDataTypeLookup, |
50
|
|
|
ConstraintParameterParser $constraintParameterParser, |
51
|
|
|
RangeCheckerHelper $rangeCheckerHelper, |
52
|
|
|
ConstraintParameterRenderer $constraintParameterRenderer |
53
|
|
|
) { |
54
|
|
|
$this->propertyDataTypeLookup = $propertyDataTypeLookup; |
55
|
|
|
$this->constraintParameterParser = $constraintParameterParser; |
56
|
|
|
$this->rangeCheckerHelper = $rangeCheckerHelper; |
57
|
|
|
$this->constraintParameterRenderer = $constraintParameterRenderer; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @codeCoverageIgnore This method is purely declarative. |
62
|
|
|
*/ |
63
|
|
|
public function getSupportedContextTypes() { |
64
|
|
|
return [ |
65
|
|
|
Context::TYPE_STATEMENT => CheckResult::STATUS_COMPLIANCE, |
66
|
|
|
Context::TYPE_QUALIFIER => CheckResult::STATUS_COMPLIANCE, |
67
|
|
|
Context::TYPE_REFERENCE => CheckResult::STATUS_COMPLIANCE, |
68
|
|
|
]; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @codeCoverageIgnore This method is purely declarative. |
73
|
|
|
*/ |
74
|
|
|
public function getDefaultContextTypes() { |
75
|
|
|
return [ |
76
|
|
|
Context::TYPE_STATEMENT, |
77
|
|
|
Context::TYPE_QUALIFIER, |
78
|
|
|
Context::TYPE_REFERENCE, |
79
|
|
|
]; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Checks 'Range' constraint. |
84
|
|
|
* |
85
|
|
|
* @param Context $context |
86
|
|
|
* @param Constraint $constraint |
87
|
|
|
* |
88
|
|
|
* @throws ConstraintParameterException |
89
|
|
|
* @return CheckResult |
90
|
|
|
*/ |
91
|
|
|
public function checkConstraint( Context $context, Constraint $constraint ) { |
92
|
|
|
if ( $context->getSnakRank() === Statement::RANK_DEPRECATED ) { |
93
|
|
|
return new CheckResult( $context, $constraint, [], CheckResult::STATUS_DEPRECATED ); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$parameters = []; |
97
|
|
|
$constraintParameters = $constraint->getConstraintParameters(); |
98
|
|
|
|
99
|
|
|
$snak = $context->getSnak(); |
100
|
|
|
|
101
|
|
|
if ( !$snak instanceof PropertyValueSnak ) { |
102
|
|
|
// nothing to check |
103
|
|
|
return new CheckResult( $context, $constraint, $parameters, CheckResult::STATUS_COMPLIANCE ); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
$dataValue = $snak->getDataValue(); |
107
|
|
|
|
108
|
|
|
list( $min, $max ) = $this->constraintParameterParser->parseRangeParameter( |
109
|
|
|
$constraintParameters, |
110
|
|
|
$constraint->getConstraintTypeItemId(), |
111
|
|
|
$dataValue->getType() |
112
|
|
|
); |
113
|
|
|
$parameterKey = $dataValue->getType() === 'quantity' ? 'quantity' : 'date'; |
114
|
|
|
if ( $min !== null ) { |
115
|
|
|
$parameters['minimum_' . $parameterKey] = [ $min ]; |
116
|
|
|
} |
117
|
|
|
if ( $max !== null ) { |
118
|
|
|
$parameters['maximum_' . $parameterKey] = [ $max ]; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
if ( $this->rangeCheckerHelper->getComparison( $min, $dataValue ) > 0 || |
122
|
|
|
$this->rangeCheckerHelper->getComparison( $dataValue, $max ) > 0 |
123
|
|
|
) { |
124
|
|
|
$message = $this->getViolationMessage( |
125
|
|
|
$context->getSnak()->getPropertyId(), |
126
|
|
|
$dataValue, |
127
|
|
|
$min, |
128
|
|
|
$max |
129
|
|
|
); |
130
|
|
|
$status = CheckResult::STATUS_VIOLATION; |
131
|
|
|
} else { |
132
|
|
|
$message = null; |
133
|
|
|
$status = CheckResult::STATUS_COMPLIANCE; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
return new CheckResult( $context, $constraint, $parameters, $status, $message ); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* @param PropertyId $predicate |
141
|
|
|
* @param DataValue $value |
142
|
|
|
* @param DataValue|null $min |
143
|
|
|
* @param DataValue|null $max |
144
|
|
|
* |
145
|
|
|
* @return ViolationMessage |
146
|
|
|
*/ |
147
|
|
|
private function getViolationMessage( PropertyId $predicate, DataValue $value, $min, $max ) { |
148
|
|
|
// possible message keys: |
149
|
|
|
// wbqc-violation-message-range-quantity-closed |
150
|
|
|
// wbqc-violation-message-range-quantity-leftopen |
151
|
|
|
// wbqc-violation-message-range-quantity-rightopen |
152
|
|
|
// wbqc-violation-message-range-time-closed |
153
|
|
|
// wbqc-violation-message-range-time-closed-leftnow |
154
|
|
|
// wbqc-violation-message-range-time-closed-rightnow |
155
|
|
|
// wbqc-violation-message-range-time-leftopen |
156
|
|
|
// wbqc-violation-message-range-time-leftopen-rightnow |
157
|
|
|
// wbqc-violation-message-range-time-rightopen |
158
|
|
|
// wbqc-violation-message-range-time-rightopen-leftnow |
159
|
|
|
$messageKey = 'wbqc-violation-message-range'; |
160
|
|
|
$messageKey .= '-' . $value->getType(); |
161
|
|
|
// at least one of $min, $max is set, otherwise there could be no violation |
162
|
|
|
$messageKey .= '-' . ( $min !== null ? ( $max !== null ? 'closed' : 'rightopen' ) : 'leftopen' ); |
163
|
|
|
if ( $min instanceof NowValue ) { |
164
|
|
|
$messageKey .= '-leftnow'; |
165
|
|
|
} elseif ( $max instanceof NowValue ) { |
166
|
|
|
$messageKey .= '-rightnow'; |
167
|
|
|
} |
168
|
|
|
$message = ( new ViolationMessage( $messageKey ) ) |
169
|
|
|
->withEntityId( $predicate, Role::PREDICATE ) |
170
|
|
|
->withDataValue( $value, Role::OBJECT ); |
171
|
|
View Code Duplication |
if ( $min !== null && !( $min instanceof NowValue ) ) { |
|
|
|
|
172
|
|
|
$message = $message->withDataValue( $min, Role::OBJECT ); |
173
|
|
|
} |
174
|
|
View Code Duplication |
if ( $max !== null && !( $max instanceof NowValue ) ) { |
|
|
|
|
175
|
|
|
$message = $message->withDataValue( $max, Role::OBJECT ); |
176
|
|
|
} |
177
|
|
|
return $message; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
View Code Duplication |
public function checkConstraintParameters( Constraint $constraint ) { |
|
|
|
|
181
|
|
|
$constraintParameters = $constraint->getConstraintParameters(); |
182
|
|
|
$exceptions = []; |
183
|
|
|
try { |
184
|
|
|
// we don’t have a data value here, so get the type from the property instead |
185
|
|
|
// (the distinction between data type and data value type is irrelevant for 'quantity' and 'time') |
186
|
|
|
$type = $this->propertyDataTypeLookup->getDataTypeIdForProperty( $constraint->getPropertyId() ); |
187
|
|
|
$this->constraintParameterParser->parseRangeParameter( |
188
|
|
|
$constraintParameters, |
189
|
|
|
$constraint->getConstraintTypeItemId(), |
190
|
|
|
$type |
191
|
|
|
); |
192
|
|
|
} catch ( ConstraintParameterException $e ) { |
193
|
|
|
$exceptions[] = $e; |
194
|
|
|
} |
195
|
|
|
return $exceptions; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
} |
199
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.