Completed
Push — master ( 1a1840...6d8e1d )
by
unknown
06:19
created

RangeChecker   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 214
Duplicated Lines 2.8 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 29
lcom 1
cbo 13
dl 6
loc 214
rs 10
c 1
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A getSupportedContextTypes() 0 7 1
A getDefaultContextTypes() 0 7 1
C checkConstraint() 0 58 12
A parseRangeParameter() 0 21 3
D getViolationMessage() 6 32 9
A checkConstraintParameters() 0 17 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker;
4
5
use DataValues\DataValue;
6
use DataValues\TimeValue;
7
use Wikibase\DataModel\Entity\ItemId;
8
use Wikibase\DataModel\Entity\PropertyId;
9
use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup;
10
use Wikibase\DataModel\Snak\PropertyValueSnak;
11
use WikibaseQuality\ConstraintReport\Constraint;
12
use WikibaseQuality\ConstraintReport\ConstraintCheck\Cache\DependencyMetadata;
13
use WikibaseQuality\ConstraintReport\ConstraintCheck\Cache\Metadata;
14
use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker;
15
use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context;
16
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException;
17
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser;
18
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\NowValue;
19
use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\RangeCheckerHelper;
20
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage;
21
use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
22
use WikibaseQuality\ConstraintReport\ConstraintParameterRenderer;
23
use WikibaseQuality\ConstraintReport\Role;
24
use Wikibase\DataModel\Statement\Statement;
25
26
/**
27
 * @author BP2014N1
28
 * @license GPL-2.0-or-later
29
 */
30
class RangeChecker implements ConstraintChecker {
31
32
	/**
33
	 * @var PropertyDataTypeLookup
34
	 */
35
	private $propertyDataTypeLookup;
36
37
	/**
38
	 * @var ConstraintParameterParser
39
	 */
40
	private $constraintParameterParser;
41
42
	/**
43
	 * @var RangeCheckerHelper
44
	 */
45
	private $rangeCheckerHelper;
46
47
	/**
48
	 * @var ConstraintParameterRenderer
49
	 */
50
	private $constraintParameterRenderer;
51
52
	public function __construct(
53
		PropertyDataTypeLookup $propertyDataTypeLookup,
54
		ConstraintParameterParser $constraintParameterParser,
55
		RangeCheckerHelper $rangeCheckerHelper,
56
		ConstraintParameterRenderer $constraintParameterRenderer
57
	) {
58
		$this->propertyDataTypeLookup = $propertyDataTypeLookup;
59
		$this->constraintParameterParser = $constraintParameterParser;
60
		$this->rangeCheckerHelper = $rangeCheckerHelper;
61
		$this->constraintParameterRenderer = $constraintParameterRenderer;
62
	}
63
64
	/**
65
	 * @codeCoverageIgnore This method is purely declarative.
66
	 */
67
	public function getSupportedContextTypes() {
68
		return [
69
			Context::TYPE_STATEMENT => CheckResult::STATUS_COMPLIANCE,
70
			Context::TYPE_QUALIFIER => CheckResult::STATUS_COMPLIANCE,
71
			Context::TYPE_REFERENCE => CheckResult::STATUS_COMPLIANCE,
72
		];
73
	}
74
75
	/**
76
	 * @codeCoverageIgnore This method is purely declarative.
77
	 */
78
	public function getDefaultContextTypes() {
79
		return [
80
			Context::TYPE_STATEMENT,
81
			Context::TYPE_QUALIFIER,
82
			Context::TYPE_REFERENCE,
83
		];
84
	}
85
86
	/**
87
	 * Checks 'Range' constraint.
88
	 *
89
	 * @param Context $context
90
	 * @param Constraint $constraint
91
	 *
92
	 * @throws ConstraintParameterException
93
	 * @return CheckResult
94
	 */
95
	public function checkConstraint( Context $context, Constraint $constraint ) {
96
		if ( $context->getSnakRank() === Statement::RANK_DEPRECATED ) {
97
			return new CheckResult( $context, $constraint, [], CheckResult::STATUS_DEPRECATED );
98
		}
99
100
		$parameters = [];
101
		$constraintParameters = $constraint->getConstraintParameters();
102
103
		$snak = $context->getSnak();
104
105
		if ( !$snak instanceof PropertyValueSnak ) {
106
			// nothing to check
107
			return new CheckResult( $context, $constraint, $parameters, CheckResult::STATUS_COMPLIANCE );
108
		}
109
110
		$dataValue = $snak->getDataValue();
111
112
		list( $min, $max ) = $this->parseRangeParameter(
113
			$constraintParameters,
114
			$constraint->getConstraintTypeItemId(),
115
			$dataValue->getType()
116
		);
117
		$parameterKey = $dataValue->getType() === 'quantity' ? 'quantity' : 'date';
118
		if ( $min !== null ) {
119
			$parameters['minimum_' . $parameterKey] = [ $min ];
120
		}
121
		if ( $max !== null ) {
122
			$parameters['maximum_' . $parameterKey] = [ $max ];
123
		}
124
125
		if ( $this->rangeCheckerHelper->getComparison( $min, $dataValue ) > 0 ||
126
			 $this->rangeCheckerHelper->getComparison( $dataValue, $max ) > 0
127
		) {
128
			$message = $this->getViolationMessage(
129
				$context->getSnak()->getPropertyId(),
130
				$dataValue,
131
				$min,
132
				$max
133
			);
134
			$status = CheckResult::STATUS_VIOLATION;
135
		} else {
136
			$message = null;
137
			$status = CheckResult::STATUS_COMPLIANCE;
138
		}
139
140
		if (
141
			$dataValue instanceof  TimeValue &&
0 ignored issues
show
Bug introduced by
The class DataValues\TimeValue does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
142
			( $min instanceof NowValue || $max instanceof NowValue ) &&
143
			$this->rangeCheckerHelper->isFutureTime( $dataValue )
144
		) {
145
			$dependencyMetadata = DependencyMetadata::ofFutureTime( $dataValue );
146
		} else {
147
			$dependencyMetadata = DependencyMetadata::blank();
148
		}
149
150
		return ( new CheckResult( $context, $constraint, $parameters, $status, $message ) )
151
			->withMetadata( Metadata::ofDependencyMetadata( $dependencyMetadata ) );
152
	}
153
154
	/**
155
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
156
	 * @param string $constraintTypeItemId used in error messages
157
	 * @param string $type 'quantity' or 'time' (can be data type or data value type)
158
	 *
159
	 * @throws ConstraintParameterException if the parameter is invalid or missing
160
	 * @return DataValue[] a pair of two data values, either of which may be null to signify an open range
161
	 */
162
	private function parseRangeParameter( array $constraintParameters, $constraintTypeItemId, $type ) {
163
		switch ( $type ) {
164
			case 'quantity':
165
				return $this->constraintParameterParser->parseQuantityRangeParameter(
166
					$constraintParameters,
167
					$constraintTypeItemId
168
				);
169
			case 'time':
170
				return $this->constraintParameterParser->parseTimeRangeParameter(
171
					$constraintParameters,
172
					$constraintTypeItemId
173
				);
174
		}
175
176
		throw new ConstraintParameterException(
177
			( new ViolationMessage( 'wbqc-violation-message-value-needed-of-types-2' ) )
178
				->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
179
				->withDataValueType( 'quantity' )
180
				->withDataValueType( 'time' )
181
		);
182
	}
183
184
	/**
185
	 * @param PropertyId $predicate
186
	 * @param DataValue $value
187
	 * @param DataValue|null $min
188
	 * @param DataValue|null $max
189
	 *
190
	 * @return ViolationMessage
191
	 */
192
	private function getViolationMessage( PropertyId $predicate, DataValue $value, $min, $max ) {
193
		// possible message keys:
194
		// wbqc-violation-message-range-quantity-closed
195
		// wbqc-violation-message-range-quantity-leftopen
196
		// wbqc-violation-message-range-quantity-rightopen
197
		// wbqc-violation-message-range-time-closed
198
		// wbqc-violation-message-range-time-closed-leftnow
199
		// wbqc-violation-message-range-time-closed-rightnow
200
		// wbqc-violation-message-range-time-leftopen
201
		// wbqc-violation-message-range-time-leftopen-rightnow
202
		// wbqc-violation-message-range-time-rightopen
203
		// wbqc-violation-message-range-time-rightopen-leftnow
204
		$messageKey = 'wbqc-violation-message-range';
205
		$messageKey .= '-' . $value->getType();
206
		// at least one of $min, $max is set, otherwise there could be no violation
207
		$messageKey .= '-' . ( $min !== null ? ( $max !== null ? 'closed' : 'rightopen' ) : 'leftopen' );
208
		if ( $min instanceof NowValue ) {
209
			$messageKey .= '-leftnow';
210
		} elseif ( $max instanceof  NowValue ) {
211
			$messageKey .= '-rightnow';
212
		}
213
		$message = ( new ViolationMessage( $messageKey ) )
214
			->withEntityId( $predicate, Role::PREDICATE )
215
			->withDataValue( $value, Role::OBJECT );
216 View Code Duplication
		if ( $min !== null && !( $min instanceof NowValue ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
217
			$message = $message->withDataValue( $min, Role::OBJECT );
218
		}
219 View Code Duplication
		if ( $max !== null && !( $max instanceof  NowValue ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
220
			$message = $message->withDataValue( $max, Role::OBJECT );
221
		}
222
		return $message;
223
	}
224
225
	public function checkConstraintParameters( Constraint $constraint ) {
226
		$constraintParameters = $constraint->getConstraintParameters();
227
		$exceptions = [];
228
		try {
229
			// we don’t have a data value here, so get the type from the property instead
230
			// (the distinction between data type and data value type is irrelevant for 'quantity' and 'time')
231
			$type = $this->propertyDataTypeLookup->getDataTypeIdForProperty( $constraint->getPropertyId() );
232
			$this->parseRangeParameter(
233
				$constraintParameters,
234
				$constraint->getConstraintTypeItemId(),
235
				$type
236
			);
237
		} catch ( ConstraintParameterException $e ) {
238
			$exceptions[] = $e;
239
		}
240
		return $exceptions;
241
	}
242
243
}
244