Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.39% covered (success)
98.39%
61 / 62
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
AllowedUnitsChecker
98.39% covered (success)
98.39%
61 / 62
80.00% covered (warning)
80.00%
4 / 5
20
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
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% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
1 / 1
9
 checkUnitless
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 standardize
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 checkConstraintParameters
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker;
4
5use DataValues\UnboundedQuantityValue;
6use Wikibase\DataModel\Entity\ItemId;
7use Wikibase\DataModel\Snak\PropertyValueSnak;
8use Wikibase\Lib\Units\UnitConverter;
9use WikibaseQuality\ConstraintReport\Constraint;
10use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker;
11use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context;
12use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException;
13use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser;
14use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\UnitsParameter;
15use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage;
16use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
17use WikibaseQuality\ConstraintReport\Role;
18
19/**
20 * @author Lucas Werkmeister
21 * @license GPL-2.0-or-later
22 */
23class 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}