Failed Conditions
Push — master ( 2b713c...735d0a )
by Alex
16s queued 13s
created

validateValueCanBeWrittenAsXmlElementAnnotation()   C

Complexity

Conditions 15
Paths 44

Size

Total Lines 78
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 47
nc 44
nop 4
dl 0
loc 78
rs 5.9166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
6
namespace AlgoWeb\ODataMetadata\Edm\Validation\Internal;
7
8
use AlgoWeb\ODataMetadata\Edm\Validation\EdmError;
9
use AlgoWeb\ODataMetadata\Edm\Validation\EdmErrorCode;
10
use AlgoWeb\ODataMetadata\Edm\Validation\ValidationContext;
11
use AlgoWeb\ODataMetadata\EdmConstants;
12
use AlgoWeb\ODataMetadata\EdmUtil;
13
use AlgoWeb\ODataMetadata\Exception\InvalidOperationException;
14
use AlgoWeb\ODataMetadata\Interfaces\IEntityType;
15
use AlgoWeb\ODataMetadata\Interfaces\IFunction;
16
use AlgoWeb\ODataMetadata\Interfaces\IModel;
17
use AlgoWeb\ODataMetadata\Interfaces\INamedElement;
18
use AlgoWeb\ODataMetadata\Interfaces\ISchemaElement;
19
use AlgoWeb\ODataMetadata\Interfaces\IStructuralProperty;
20
use AlgoWeb\ODataMetadata\Interfaces\Values\IStringValue;
21
use AlgoWeb\ODataMetadata\Interfaces\Values\IValue;
22
use AlgoWeb\ODataMetadata\StringConst;
23
use AlgoWeb\ODataMetadata\Structure\HashSetInternal;
24
use Exception;
25
use SplObjectStorage;
26
use Throwable;
27
use XMLReader;
28
29
abstract class ValidationHelper
30
{
31
    public static function isEdmSystemNamespace(string $namespaceName): bool
32
    {
33
        return ($namespaceName == EdmConstants::TransientNamespace ||
34
        $namespaceName == EdmConstants::EdmNamespace);
35
    }
36
37
    public static function addMemberNameToHashSet(
38
        INamedElement $item,
39
        HashSetInternal $memberNameList,
40
        ValidationContext $context,
41
        EdmErrorCode $errorCode,
42
        string $errorString,
43
        bool $suppressError
44
    ) {
45
        $name = $item instanceof ISchemaElement ? $item->fullName() : $item->getName();
46
        if ($memberNameList->add($name)) {
47
            if (!$suppressError) {
48
                EdmUtil::checkArgumentNull($item->location(), 'item->Location');
49
                $context->addError($item->location(), $errorCode, $errorString);
50
            }
51
            return false;
52
        }
53
54
        return true;
55
    }
56
57
    /**
58
     * @param  IStructuralProperty[] $properties
59
     * @return bool
60
     */
61
    public static function allPropertiesAreNullable(array $properties): bool
62
    {
63
        return count(array_filter($properties, function (IStructuralProperty $item) {
64
            try {
65
                return $item->getType()->getNullable();
66
            } catch (Throwable $e) {
67
                throw new InvalidOperationException($e->getMessage());
68
            }
69
        })) === count($properties);
70
    }
71
72
    /**
73
     * @param  IStructuralProperty[] $properties
74
     * @return bool
75
     */
76
    public static function hasNullableProperty(array $properties): bool
77
    {
78
        return count(array_filter($properties, function (IStructuralProperty $item) {
79
            try {
80
                return $item->getType()->getNullable();
81
            } catch (Throwable $e) {
82
                throw new InvalidOperationException($e->getMessage());
83
            }
84
        })) > 0;
85
    }
86
87
    /**
88
     * @param  IStructuralProperty[] $set
89
     * @param  IStructuralProperty[] $subset
90
     * @return bool
91
     */
92
    public static function propertySetIsSubset(array $set, array $subset): bool
93
    {
94
        return count(array_diff($subset, $set)) === 0;
95
    }
96
97
    /**
98
     * @param  IStructuralProperty[] $set1
99
     * @param  IStructuralProperty[] $set2
100
     * @return bool
101
     */
102
    public static function propertySetsAreEquivalent(array $set1, array $set2): bool
103
    {
104
        if (count($set1) != count($set2)) {
105
            return false;
106
        }
107
        for ($i = count($set1) -1; $i > -1; --$i) {
108
            if ($set1[$i] !== $set2[$i]) {
109
                return false;
110
            }
111
        }
112
        return true;
113
    }
114
115
    public static function validateValueCanBeWrittenAsXmlElementAnnotation(
116
        IValue $value,
117
        ?string $annotationNamespace,
118
        ?string $annotationName,
119
        ?EdmError &$error
120
    ): bool {
121
        if (!($value instanceof IStringValue)) {
122
            $error = new EdmError(
123
                $value->location(),
124
                EdmErrorCode::InvalidElementAnnotation(),
125
                StringConst::EdmModel_Validator_Semantic_InvalidElementAnnotationNotIEdmStringValue()
126
            );
127
            return false;
128
        }
129
130
        $rawString = $value->getValue();
131
        $reader    = new XMLReader();
132
        $reader->XML($rawString);
133
        try {
134
            $eof = true;
135
            // Skip to root element.
136
            if ($reader->nodeType != XMLReader::ELEMENT) {
137
                while ($reader->read() && $reader->nodeType != XMLReader::ELEMENT) {
138
                    if ($reader->nodeType == XMLReader::ELEMENT) {
139
                        $eof = false;
140
                        break;
141
                    }
142
                }
143
            }
144
145
            // The annotation must be an element.
146
            if ($eof) {
147
                $error = new EdmError(
148
                    $value->location(),
149
                    EdmErrorCode::InvalidElementAnnotation(),
150
                    StringConst::EdmModel_Validator_Semantic_InvalidElementAnnotationValueInvalidXml()
151
                );
152
                return false;
153
            }
154
155
            // The root element must correspond to the term of the annotation
156
            $elementNamespace = $reader->namespaceURI;
157
            $elementName      = $reader->localName;
158
159
            if (EdmUtil::isNullOrWhiteSpaceInternal($elementNamespace) ||
160
                EdmUtil::isNullOrWhiteSpaceInternal($elementName)) {
161
                $error = new EdmError(
162
                    $value->location(),
163
                    EdmErrorCode::InvalidElementAnnotation(),
164
                    StringConst::EdmModel_Validator_Semantic_InvalidElementAnnotationNullNamespaceOrName()
165
                );
166
                return false;
167
            }
168
169
            if (!((null === $annotationNamespace || $elementNamespace == $annotationNamespace) &&
170
                  (null === $annotationName || $elementName == $annotationName))) {
171
                $error = new EdmError(
172
                    $value->location(),
173
                    EdmErrorCode::InvalidElementAnnotation(),
174
                    StringConst::EdmModel_Validator_Semantic_InvalidElementAnnotationMismatchedTerm()
175
                );
176
                return false;
177
            }
178
179
            // Parse the entire fragment to determine if the XML is valid
180
            /* @noinspection PhpStatementHasEmptyBodyInspection */
181
            while ($reader->read()) {
182
            }
183
184
            $error = null;
185
            return true;
186
        } catch (Exception $e) {
187
            $error = new EdmError(
188
                $value->location(),
189
                EdmErrorCode::InvalidElementAnnotation(),
190
                StringConst::EdmModel_Validator_Semantic_InvalidElementAnnotationValueInvalidXml()
191
            );
192
            return false;
193
        }
194
    }
195
196
    public static function isInterfaceCritical(EdmError $error): bool
197
    {
198
        $errVal = $error->getErrorCode()->getValue();
199
200
        return $errVal >= EdmErrorCode::InterfaceCriticalPropertyValueMustNotBeNull()->getValue() &&
201
               $errVal <= EdmErrorCode::InterfaceCriticalCycleInTypeHierarchy()->getValue();
202
    }
203
204
    public static function itemExistsInReferencedModel(
205
        IModel $model,
206
        string $fullName,
207
        bool $checkEntityContainer
208
    ): bool {
209
        foreach ($model->getReferencedModels() as $referenced) {
210
            if (self::checkItemReference($fullName, $checkEntityContainer, $referenced)) {
211
                return true;
212
            }
213
        }
214
215
        return false;
216
    }
217
218
    // Take function name to avoid recomputing it
219
    public static function functionOrNameExistsInReferencedModel(
220
        IModel $model,
221
        IFunction $function,
222
        string $functionFullName,
223
        bool $checkEntityContainer
224
    ): bool {
225
        foreach ($model->getReferencedModels() as $referenced) {
226
            if (self::checkFunctionOrNameReference($function, $functionFullName, $checkEntityContainer, $referenced)) {
227
                return true;
228
            }
229
        }
230
231
        return false;
232
    }
233
234
    public static function typeIndirectlyContainsTarget(
235
        IEntityType $source,
236
        IEntityType $target,
237
        SplObjectStorage $visited,
238
        IModel $context
239
    ): bool {
240
        if (!$visited->offsetExists($source)) {
241
            $visited->offsetSet($source, true);
242
            $visited[$source] = true;
243
            if ($source->isOrInheritsFrom($target)) {
244
                return true;
245
            }
246
247
            foreach ($source->navigationProperties() as $navProp) {
248
                if ($navProp->containsTarget() && self::typeIndirectlyContainsTarget(
249
                    $navProp->toEntityType(),
250
                    $target,
251
                    $visited,
252
                    $context
253
                )) {
254
                    return true;
255
                }
256
            }
257
258
            foreach ($context->findAllDerivedTypes($source) as $derived) {
259
                if ($derived instanceof IEntityType && self::typeIndirectlyContainsTarget(
260
                    $derived,
261
                    $target,
262
                    $visited,
263
                    $context
264
                )) {
265
                    return true;
266
                }
267
            }
268
        }
269
270
        return false;
271
    }
272
273
    /**
274
     * @param  string $fullName
275
     * @param  bool   $checkEntityContainer
276
     * @param  IModel $referenced
277
     * @return bool
278
     */
279
    protected static function checkItemReference(string $fullName, bool $checkEntityContainer, IModel $referenced): bool
280
    {
281
        if (self::checkExistsCore($referenced, $fullName, $checkEntityContainer)) {
282
            return true;
283
        }
284
        $functionList = $referenced->findDeclaredFunctions($fullName) ?? [];
285
        return 0 != count($functionList);
286
    }
287
288
    /**
289
     * @param  IFunction $function
290
     * @param  string    $functionFullName
291
     * @param  bool      $checkEntityContainer
292
     * @param  IModel    $referenced
293
     * @return bool
294
     */
295
    protected static function checkFunctionOrNameReference(
296
        IFunction $function,
297
        string $functionFullName,
298
        bool $checkEntityContainer,
299
        IModel $referenced
300
    ): bool {
301
        if (self::checkExistsCore($referenced, $functionFullName, $checkEntityContainer)) {
302
            return true;
303
        }
304
        $functionList = $referenced->findDeclaredFunctions($functionFullName) ?? [];
305
        return 0 < count(array_filter($functionList, [$function, 'IsFunctionSignatureEquivalentTo']));
306
    }
307
308
    protected static function checkExistsCore(IModel $referenced, string $fullName, bool $checkEntityContainer): bool
309
    {
310
        return $referenced->findDeclaredType($fullName) != null ||
311
               $referenced->findDeclaredValueTerm($fullName) != null ||
312
               ($checkEntityContainer && $referenced->findDeclaredEntityContainer($fullName) != null);
313
    }
314
}
315