Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.06% covered (success)
97.06%
66 / 68
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
TargetRequiredClaimChecker
97.06% covered (success)
97.06%
66 / 68
66.67% covered (warning)
66.67%
2 / 3
14
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
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%
48 / 48
100.00% covered (success)
100.00%
1 / 1
7
 checkConstraintParameters
88.24% covered (warning)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 1
3.01
1<?php
2
3namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Checker;
4
5use Wikibase\DataModel\Entity\EntityIdValue;
6use Wikibase\DataModel\Entity\ItemId;
7use Wikibase\DataModel\Services\Lookup\EntityLookup;
8use Wikibase\DataModel\Snak\PropertyValueSnak;
9use Wikibase\DataModel\Statement\Statement;
10use Wikibase\DataModel\Statement\StatementListProvider;
11use WikibaseQuality\ConstraintReport\Constraint;
12use WikibaseQuality\ConstraintReport\ConstraintCheck\Cache\DependencyMetadata;
13use WikibaseQuality\ConstraintReport\ConstraintCheck\Cache\Metadata;
14use WikibaseQuality\ConstraintReport\ConstraintCheck\ConstraintChecker;
15use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context;
16use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConnectionCheckerHelper;
17use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterException;
18use WikibaseQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintParameterParser;
19use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage;
20use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
21use WikibaseQuality\ConstraintReport\Role;
22
23/**
24 * @author BP2014N1
25 * @license GPL-2.0-or-later
26 */
27class TargetRequiredClaimChecker implements ConstraintChecker {
28
29    /**
30     * @var EntityLookup
31     */
32    private $entityLookup;
33
34    /**
35     * @var ConstraintParameterParser
36     */
37    private $constraintParameterParser;
38
39    /**
40     * @var ConnectionCheckerHelper
41     */
42    private $connectionCheckerHelper;
43
44    public function __construct(
45        EntityLookup $lookup,
46        ConstraintParameterParser $constraintParameterParser,
47        ConnectionCheckerHelper $connectionCheckerHelper
48    ) {
49        $this->entityLookup = $lookup;
50        $this->constraintParameterParser = $constraintParameterParser;
51        $this->connectionCheckerHelper = $connectionCheckerHelper;
52    }
53
54    /**
55     * @codeCoverageIgnore This method is purely declarative.
56     */
57    public function getSupportedContextTypes() {
58        return self::ALL_CONTEXT_TYPES_SUPPORTED;
59    }
60
61    /**
62     * @codeCoverageIgnore This method is purely declarative.
63     */
64    public function getDefaultContextTypes() {
65        return Context::ALL_CONTEXT_TYPES;
66    }
67
68    /** @codeCoverageIgnore This method is purely declarative. */
69    public function getSupportedEntityTypes() {
70        return self::ALL_ENTITY_TYPES_SUPPORTED;
71    }
72
73    /**
74     * Checks 'Target required claim' constraint.
75     *
76     * @param Context $context
77     * @param Constraint $constraint
78     *
79     * @throws ConstraintParameterException
80     * @return CheckResult
81     */
82    public function checkConstraint( Context $context, Constraint $constraint ) {
83        if ( $context->getSnakRank() === Statement::RANK_DEPRECATED ) {
84            return new CheckResult( $context, $constraint, CheckResult::STATUS_DEPRECATED );
85        }
86
87        $constraintParameters = $constraint->getConstraintParameters();
88        $constraintTypeItemId = $constraint->getConstraintTypeItemId();
89
90        $propertyId = $this->constraintParameterParser->parsePropertyParameter(
91            $constraintParameters,
92            $constraintTypeItemId
93        );
94
95        $items = $this->constraintParameterParser->parseItemsParameter(
96            $constraintParameters,
97            $constraintTypeItemId,
98            false
99        );
100
101        $snak = $context->getSnak();
102
103        if ( !$snak instanceof PropertyValueSnak ) {
104            // nothing to check
105            return new CheckResult( $context, $constraint, CheckResult::STATUS_COMPLIANCE );
106        }
107
108        $dataValue = $snak->getDataValue();
109
110        /*
111         * error handling:
112         *   type of $dataValue for properties with 'Target required claim' constraint has to be 'wikibase-entityid'
113         */
114        if ( !$dataValue instanceof EntityIdValue ) {
115            $message = ( new ViolationMessage( 'wbqc-violation-message-value-needed-of-type' ) )
116                ->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
117                ->withDataValueType( 'wikibase-entityid' );
118            return new CheckResult( $context, $constraint, CheckResult::STATUS_VIOLATION, $message );
119        }
120
121        $targetEntityId = $dataValue->getEntityId();
122        $targetEntity = $this->entityLookup->getEntity( $targetEntityId );
123        if ( !$targetEntity instanceof StatementListProvider ) {
124            $message = new ViolationMessage( 'wbqc-violation-message-target-entity-must-exist' );
125            return new CheckResult( $context, $constraint, CheckResult::STATUS_VIOLATION, $message );
126        }
127
128        /*
129         * 'Target required claim' can be defined with
130         *   a) a property only
131         *   b) a property and a number of items (each combination forming an individual claim)
132         */
133        if ( $items === [] ) {
134            $requiredStatement = $this->connectionCheckerHelper->findStatementWithProperty(
135                $targetEntity->getStatements(),
136                $propertyId
137            );
138        } else {
139            $requiredStatement = $this->connectionCheckerHelper->findStatementWithPropertyAndItemIdSnakValues(
140                $targetEntity->getStatements(),
141                $propertyId,
142                $items
143            );
144        }
145
146        if ( $requiredStatement !== null ) {
147            $status = CheckResult::STATUS_COMPLIANCE;
148            $message = null;
149        } else {
150            $status = CheckResult::STATUS_VIOLATION;
151            $message = ( new ViolationMessage( 'wbqc-violation-message-target-required-claim' ) )
152                ->withEntityId( $targetEntityId, Role::SUBJECT )
153                ->withEntityId( $propertyId, Role::PREDICATE )
154                ->withItemIdSnakValueList( $items, Role::OBJECT );
155        }
156
157        return ( new CheckResult( $context, $constraint, $status, $message ) )
158            ->withMetadata( Metadata::ofDependencyMetadata(
159                DependencyMetadata::ofEntityId( $targetEntityId ) ) );
160    }
161
162    public function checkConstraintParameters( Constraint $constraint ) {
163        $constraintParameters = $constraint->getConstraintParameters();
164        $constraintTypeItemId = $constraint->getConstraintTypeItemId();
165        $exceptions = [];
166        try {
167            $this->constraintParameterParser->parsePropertyParameter(
168                $constraintParameters,
169                $constraintTypeItemId
170            );
171        } catch ( ConstraintParameterException $e ) {
172            $exceptions[] = $e;
173        }
174        try {
175            $this->constraintParameterParser->parseItemsParameter(
176                $constraintParameters,
177                $constraintTypeItemId,
178                false
179            );
180        } catch ( ConstraintParameterException $e ) {
181            $exceptions[] = $e;
182        }
183        return $exceptions;
184    }
185
186}