Failed Conditions
Pull Request — master (#44)
by Alex
03:10
created

VocabularyAnnotationInaccessibleTarget::__invoke()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
cc 2
eloc 9
c 2
b 1
f 1
nc 2
nop 2
dl 0
loc 12
rs 9.9666
1
<?php
2
3
declare(strict_types=1);
4
5
6
namespace AlgoWeb\ODataMetadata\Edm\Validation\ValidationRules\IVocabularyAnnotation;
7
8
use AlgoWeb\ODataMetadata\Edm\Validation\EdmErrorCode;
9
use AlgoWeb\ODataMetadata\Edm\Validation\ValidationContext;
10
use AlgoWeb\ODataMetadata\EdmUtil;
11
use AlgoWeb\ODataMetadata\Interfaces\Annotations\IVocabularyAnnotation;
12
use AlgoWeb\ODataMetadata\Interfaces\IEdmElement;
13
use AlgoWeb\ODataMetadata\Interfaces\IEntityContainer;
14
use AlgoWeb\ODataMetadata\Interfaces\IEntitySet;
15
use AlgoWeb\ODataMetadata\Interfaces\IFunction;
16
use AlgoWeb\ODataMetadata\Interfaces\IFunctionImport;
17
use AlgoWeb\ODataMetadata\Interfaces\IFunctionParameter;
18
use AlgoWeb\ODataMetadata\Interfaces\IProperty;
19
use AlgoWeb\ODataMetadata\Interfaces\ISchemaType;
20
use AlgoWeb\ODataMetadata\Interfaces\IStructuredType;
21
use AlgoWeb\ODataMetadata\Interfaces\ITerm;
22
use AlgoWeb\ODataMetadata\StringConst;
23
24
/**
25
 * Validates that a vocabulary annotations target can be found through the model containing the annotation.
26
 *
27
 * @package AlgoWeb\ODataMetadata\Edm\Validation\ValidationRules\IVocabularyAnnotation
28
 */
29
class VocabularyAnnotationInaccessibleTarget extends VocabularyAnnotationRule
30
{
31
    public function __invoke(ValidationContext $context, ?IEdmElement $annotation)
32
    {
33
        assert($annotation instanceof IVocabularyAnnotation);
34
        $target      = $annotation->getTarget();
35
        $foundTarget = $this->findTarget($context, $target);
36
37
        if (!$foundTarget) {
38
            EdmUtil::checkArgumentNull($annotation->Location(), 'annotation->Location');
39
            $context->AddError(
40
                $annotation->Location(),
41
                EdmErrorCode::BadUnresolvedTarget(),
42
                StringConst::EdmModel_Validator_Semantic_InaccessibleTarget(EdmUtil::FullyQualifiedName($target))
43
            );
44
        }
45
    }
46
47
    /**
48
     * @param ValidationContext $context
49
     * @param \AlgoWeb\ODataMetadata\Interfaces\IVocabularyAnnotatable $target
50
     * @return bool
51
     */
52
    protected function findTarget(
53
        ValidationContext $context,
54
        \AlgoWeb\ODataMetadata\Interfaces\IVocabularyAnnotatable $target
55
    ): bool {
56
        $foundTarget = false;
57
        $entityContainer = $target;
58
        if ($entityContainer instanceof IEntityContainer) {
59
            $foundTarget = ($context->getModel()->findEntityContainer($entityContainer->FullName()) != null);
60
            return $foundTarget;
61
        }
62
        $entitySet = $target;
63
        if ($entitySet instanceof IEntitySet) {
64
            $container = $entitySet->getContainer();
65
            if ($container != null && $container instanceof IEntityContainer) {
66
                $foundTarget = ($container->findEntitySet($entitySet->getName()) != null);
0 ignored issues
show
Bug introduced by
It seems like $entitySet->getName() can also be of type null; however, parameter $setName of AlgoWeb\ODataMetadata\In...tainer::findEntitySet() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

66
                $foundTarget = ($container->findEntitySet(/** @scrutinizer ignore-type */ $entitySet->getName()) != null);
Loading history...
67
                return $foundTarget;
68
            }
69
            return false;
70
        }
71
        $schemaType = $target;
72
        if ($schemaType instanceof ISchemaType) {
73
            $foundTarget = ($context->getModel()->FindType($schemaType->FullName()) != null);
74
            return $foundTarget;
75
        }
76
        $term = $target;
77
        if ($term instanceof ITerm) {
78
            $foundTarget = ($context->getModel()->FindValueTerm($term->FullName()) != null);
79
            return $foundTarget;
80
        }
81
        $function = $target;
82
        if ($function instanceof IFunction) {
83
            $foundTarget = count($context->getModel()->FindFunctions($function->FullName())) > 0;
84
            return $foundTarget;
85
        }
86
        $functionImport = $target;
87
        EdmUtil::checkArgumentNull($functionImport->getName(), 'functionImport->getName');
0 ignored issues
show
Bug introduced by
The method getName() does not exist on AlgoWeb\ODataMetadata\In...\IVocabularyAnnotatable. It seems like you code against a sub-type of said class. However, the method does not exist in AlgoWeb\ODataMetadata\Li...Internal\Bad\BadElement or AlgoWeb\ODataMetadata\Library\Internal\Bad\BadType or AlgoWeb\ODataMetadata\Li...l\Bad\BadPrimitiveValue or AlgoWeb\ODataMetadata\Li...\BadEntityReferenceType or AlgoWeb\ODataMetadata\Li...l\Bad\BadCollectionType or AlgoWeb\ODataMetadata\Li...l\Bad\BadStructuredType or AlgoWeb\ODataMetadata\Li...Internal\Bad\BadRowType. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

87
        EdmUtil::checkArgumentNull($functionImport->/** @scrutinizer ignore-call */ getName(), 'functionImport->getName');
Loading history...
88
        if ($functionImport instanceof IFunctionImport) {
89
            $funcName = $functionImport->getName();
90
            $foundTarget = count($functionImport->getContainer()->findFunctionImports($funcName)) > 0;
91
            return $foundTarget;
92
        }
93
        $typeProperty = $target;
94
        if ($typeProperty instanceof IProperty) {
95
            $declaringType = $typeProperty->getDeclaringType();
96
            assert($declaringType instanceof ISchemaType);
97
            $declaringTypeFullName = EdmUtil::FullyQualifiedName($declaringType);
98
            EdmUtil::checkArgumentNull($declaringTypeFullName, 'declaringTypeFullName');
99
            EdmUtil::checkArgumentNull($typeProperty->getName(), 'typeProperty->getName');
100
            $modelType = $context->getModel()->FindType($declaringTypeFullName);
101
            if ($modelType !== null && $modelType instanceof IStructuredType) {
102
                // If we can find a structured type with this name in the model check if it
103
                // has a property with this name
104
                $foundTarget = ($modelType->findProperty($typeProperty->getName()) != null);
105
                return $foundTarget;
106
            }
107
            return false;
108
        }
109
        $functionParameter = $target;
110
        if ($functionParameter instanceof IFunctionParameter) {
111
            $paramName = $functionParameter->getName();
112
            $declaringFunction = $functionParameter->getDeclaringFunction();
113
            if ($declaringFunction != null && $declaringFunction instanceof IFunction) {
114
                // For all functions with this name declared in the model check if it has
115
                // a parameter with this name
116
                $functions = $context->getModel()->FindFunctions($declaringFunction->FullName());
117
                return 0 < count(array_filter($functions, function (IFunction $func) use ($paramName) {
118
                    return null !== $func->findParameter($paramName);
0 ignored issues
show
Bug introduced by
It seems like $paramName can also be of type null; however, parameter $name of AlgoWeb\ODataMetadata\In...onBase::findParameter() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
                    return null !== $func->findParameter(/** @scrutinizer ignore-type */ $paramName);
Loading history...
119
                }));
120
            } else {
121
                $declaringFunctionImport = $functionParameter->getDeclaringFunction();
122
                if ($declaringFunctionImport != null && $declaringFunctionImport instanceof IFunctionImport) {
123
                    $container = $declaringFunctionImport->getContainer();
124
                    assert($container instanceof IEntityContainer);
125
                    foreach ($container->findFunctionImports(
126
                        $declaringFunctionImport->getName()
0 ignored issues
show
Bug introduced by
It seems like $declaringFunctionImport->getName() can also be of type null; however, parameter $functionName of AlgoWeb\ODataMetadata\In...::findFunctionImports() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
                        /** @scrutinizer ignore-type */ $declaringFunctionImport->getName()
Loading history...
127
                    ) as $currentFunction) {
128
                        if ($currentFunction->findParameter($functionParameter->getName()) != null) {
129
                            $foundTarget = true;
130
                            break;
131
                        }
132
                    }
133
                }
134
            }
135
        } else {
136
            // Only validate annotations targeting elements that can be found via the
137
            // model API.
138
            // E.g. annotations targeting annotations will not be valid without this branch.
139
            $foundTarget = true;
140
        }
141
        return $foundTarget;
142
    }
143
}
144