Passed
Push — static-analysis ( c3aabf...c8fdad )
by SignpostMarv
03:22
created

SchemaReaderLoadAbstraction   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 555
Duplicated Lines 0 %

Test Coverage

Coverage 98.73%

Importance

Changes 0
Metric Value
dl 0
loc 555
ccs 310
cts 314
cp 0.9873
rs 6.8539
c 0
b 0
f 0
wmc 54

24 Methods

Rating   Name   Duplication   Size   Complexity  
A loadExtensionChildNodes() 0 11 3
B loadImport() 0 29 5
B loadComplexTypeBeforeCallbackCallback() 0 21 5
B loadUnion() 0 25 3
B loadSimpleType() 0 32 2
A loadRestriction() 0 3 1
A loadAttributeOrElementDef() 0 17 2
A loadComplexTypeFromChildNode() 0 52 2
A loadSequenceChildNodeLoadSequence() 0 6 1
B loadExtensionChildNode() 0 38 1
A loadGroup() 0 3 1
A loadExtension() 0 14 2
A loadSequence() 0 11 3
B loadImportFresh() 0 25 5
A loadAttributeGroup() 0 3 1
A loadElementDef() 0 3 1
A loadComplexType() 0 22 1
A addGroupAsElement() 0 18 1
A loadList() 0 18 2
A loadSequenceChildNodeLoadGroup() 0 10 1
A loadAttributeDef() 0 3 1
B loadSequenceChildNodeLoadElement() 0 26 4
B loadSequenceNormaliseMax() 0 10 5
B loadSequenceChildNode() 0 38 1

How to fix   Complexity   

Complex Class

Complex classes like SchemaReaderLoadAbstraction often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SchemaReaderLoadAbstraction, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace GoetasWebservices\XML\XSDReader;
3
4
use Closure;
5
use DOMDocument;
6
use DOMElement;
7
use DOMNode;
8
use DOMNodeList;
9
use GoetasWebservices\XML\XSDReader\Exception\IOException;
10
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
11
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
12
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
13
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
14
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
15
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
16
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
17
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
18
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
19
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
21
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
22
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
23
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
24
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
25
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
26
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
27
use GoetasWebservices\XML\XSDReader\Schema\Item;
28
use GoetasWebservices\XML\XSDReader\Schema\Schema;
29
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
30
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
31
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
32
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
33
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
34
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
35
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
36
use RuntimeException;
37
38
abstract class SchemaReaderLoadAbstraction extends SchemaReaderSchemaAbstraction
39
{
40
    /**
41
    * @return Closure
42
    */
43 135
    protected function loadAttributeGroup(Schema $schema, DOMElement $node)
44
    {
45 135
        return AttributeGroup::loadAttributeGroup($this, $schema, $node);
46
    }
47
48
    /**
49
    * @param bool $attributeDef
50
    *
51
    * @return Closure
52
    */
53 135
    protected function loadAttributeOrElementDef(
54
        Schema $schema,
55
        DOMElement $node,
56
        $attributeDef
57
    ) {
58 135
        $name = $node->getAttribute('name');
59 135
        if ($attributeDef) {
60 135
            $attribute = new AttributeDef($schema, $name);
61 135
            $schema->addAttribute($attribute);
62 45
        } else {
63 135
            $attribute = new ElementDef($schema, $name);
64 135
            $schema->addElement($attribute);
65
        }
66
67
68
        return function () use ($attribute, $node) {
69 135
            $this->fillItem($attribute, $node);
70 135
        };
71
    }
72
73
    /**
74
    * @return Closure
75
    */
76 135
    protected function loadAttributeDef(Schema $schema, DOMElement $node)
77
    {
78 135
        return $this->loadAttributeOrElementDef($schema, $node, true);
79
    }
80
81
    /**
82
    * @param int|null $max
83
    *
84
    * @return int|null
85
    */
86 135
    protected static function loadSequenceNormaliseMax(DOMElement $node, $max)
87
    {
88
        return
89
        (
90 135
            (is_int($max) && (bool) $max) ||
91 135
            $node->getAttribute("maxOccurs") == "unbounded" ||
92 135
            $node->getAttribute("maxOccurs") > 1
93 45
        )
94 90
            ? 2
95 135
            : null;
96
    }
97
98
    /**
99
    * @param int|null $max
100
    */
101 135
    protected function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null)
102
    {
103 135
        $max = static::loadSequenceNormaliseMax($node, $max);
104
105 135
        foreach ($node->childNodes as $childNode) {
106 135
            if ($childNode instanceof DOMElement) {
107 135
                $this->loadSequenceChildNode(
108 135
                    $elementContainer,
109 135
                    $node,
110 135
                    $childNode,
111 90
                    $max
112 45
                );
113 45
            }
114 45
        }
115 135
    }
116
117
    /**
118
    * @param int|null $max
119
    */
120 135
    protected function loadSequenceChildNode(
121
        ElementContainer $elementContainer,
122
        DOMElement $node,
123
        DOMElement $childNode,
124
        $max
125
    ) {
126
        $commonMethods = [
127
            [
128 135
                ['sequence', 'choice', 'all'],
129 135
                [$this, 'loadSequenceChildNodeLoadSequence'],
130
                [
131 135
                    $elementContainer,
132 135
                    $childNode,
133 135
                    $max,
134 45
                ],
135 45
            ],
136 45
        ];
137
        $methods = [
138
            'element' => [
139 135
                [$this, 'loadSequenceChildNodeLoadElement'],
140
                [
141 135
                    $elementContainer,
142 135
                    $node,
143 135
                    $childNode,
144 90
                    $max
145 45
                ]
146 45
            ],
147
            'group' => [
148 135
                [$this, 'loadSequenceChildNodeLoadGroup'],
149
                [
150 135
                    $elementContainer,
151 135
                    $node,
152 90
                    $childNode
153 45
                ]
154 45
            ],
155 45
        ];
156
157 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
158 135
    }
159
160
    /**
161
    * @param int|null $max
162
    */
163 135
    protected function loadSequenceChildNodeLoadSequence(
164
        ElementContainer $elementContainer,
165
        DOMElement $childNode,
166
        $max
167
    ) {
168 135
        $this->loadSequence($elementContainer, $childNode, $max);
169 135
    }
170
171
    /**
172
    * @param int|null $max
173
    */
174 135
    protected function loadSequenceChildNodeLoadElement(
175
        ElementContainer $elementContainer,
176
        DOMElement $node,
177
        DOMElement $childNode,
178
        $max
179
    ) {
180 135
        if ($childNode->hasAttribute("ref")) {
181
            /**
182
            * @var ElementDef $referencedElement
183
            */
184 135
            $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
185 135
            $element = ElementRef::loadElementRef(
186 135
                $referencedElement,
187 90
                $childNode
188 45
            );
189 45
        } else {
190 135
            $element = Element::loadElement(
191 135
                $this,
192 135
                $elementContainer->getSchema(),
193 90
                $childNode
194 45
            );
195
        }
196 135
        if (is_int($max) && (bool) $max) {
197 135
            $element->setMax($max);
198 45
        }
199 135
        $elementContainer->addElement($element);
200 135
    }
201
202 135
    protected function loadSequenceChildNodeLoadGroup(
203
        ElementContainer $elementContainer,
204
        DOMElement $node,
205
        DOMElement $childNode
206
    ) {
207 135
        $this->addGroupAsElement(
208 135
            $elementContainer->getSchema(),
209 135
            $node,
210 135
            $childNode,
211 90
            $elementContainer
212 45
        );
213 135
    }
214
215 135
    protected function addGroupAsElement(
216
        Schema $schema,
217
        DOMElement $node,
218
        DOMElement $childNode,
219
        ElementContainer $elementContainer
220
    ) {
221
        /**
222
        * @var Group $referencedGroup
223
        */
224 135
        $referencedGroup = $this->findSomething(
225 135
            'findGroup',
226 135
            $schema,
227 135
            $node,
228 135
            $childNode->getAttribute("ref")
229 45
        );
230
231 135
        $group = GroupRef::loadGroupRef($referencedGroup, $childNode);
232 135
        $elementContainer->addElement($group);
233 135
    }
234
235
    /**
236
    * @return Closure
237
    */
238 135
    protected function loadGroup(Schema $schema, DOMElement $node)
239
    {
240 135
        return Group::loadGroup($this, $schema, $node);
241
    }
242
243
    /**
244
    * @return BaseComplexType
245
    */
246 135
    protected function loadComplexTypeBeforeCallbackCallback(
247
        Schema $schema,
248
        DOMElement $node
249
    ) {
250 135
        $isSimple = false;
251
252 135
        foreach ($node->childNodes as $childNode) {
253 135
            if ($childNode->localName === "simpleContent") {
254 6
                $isSimple = true;
255 49
                break;
256
            }
257 45
        }
258
259 135
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute("name")) : new ComplexType($schema, $node->getAttribute("name"));
260
261 135
        $type->setDoc(static::getDocumentation($node));
262 135
        if ($node->getAttribute("name")) {
263 135
            $schema->addType($type);
264 45
        }
265
266 135
        return $type;
267
    }
268
269
    /**
270
    * @param Closure|null $callback
271
    *
272
    * @return Closure
273
    */
274 135
    protected function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
275
    {
276 135
        $type = $this->loadComplexTypeBeforeCallbackCallback($schema, $node);
277
278 135
        return $this->makeCallbackCallback(
279 135
            $type,
280 135
            $node,
281
            function (
282
                DOMElement $node,
283
                DOMElement $childNode
284
            ) use(
285 135
                $schema,
286 135
                $type
287
            ) {
288 135
                $this->loadComplexTypeFromChildNode(
289 135
                    $type,
290 135
                    $node,
291 135
                    $childNode,
292 90
                    $schema
293 45
                );
294 135
            },
295 90
            $callback
296 45
        );
297
    }
298
299 135
    protected function loadComplexTypeFromChildNode(
300
        BaseComplexType $type,
301
        DOMElement $node,
302
        DOMElement $childNode,
303
        Schema $schema
304
    ) {
305
        $commonMethods = [
306
            [
307 135
                ['sequence', 'choice', 'all'],
308 135
                [$this, 'maybeLoadSequenceFromElementContainer'],
309
                [
310 135
                    $type,
311 135
                    $childNode,
312 45
                ],
313 45
            ],
314 45
        ];
315
        $methods = [
316 90
            'attribute' => [
317 135
                [$type, 'addAttributeFromAttributeOrRef'],
318
                [
319 135
                    $this,
320 135
                    $childNode,
321 135
                    $schema,
322 90
                    $node
323 45
                ]
324 45
            ],
325
            'attributeGroup' => [
326 45
                (AttributeGroup::class . '::findSomethingLikeThis'),
327
                [
328 135
                    $this,
329 135
                    $schema,
330 135
                    $node,
331 135
                    $childNode,
332 90
                    $type
333 45
                ]
334 45
            ],
335 45
        ];
336
        if (
337 90
            $type instanceof ComplexType
338 45
        ) {
339 135
            $methods['group'] = [
340 135
                [$this, 'addGroupAsElement'],
341
                [
342 135
                    $schema,
343 135
                    $node,
344 135
                    $childNode,
345 90
                    $type
346 45
                ]
347 45
            ];
348 45
        }
349
350 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
351 135
    }
352
353
    /**
354
    * @param Closure|null $callback
355
    *
356
    * @return Closure
357
    */
358 135
    protected function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
359
    {
360 135
        $type = new SimpleType($schema, $node->getAttribute("name"));
361 135
        $type->setDoc(static::getDocumentation($node));
362 135
        if ($node->getAttribute("name")) {
363 135
            $schema->addType($type);
364 45
        }
365
366 90
        static $methods = [
367
            'union' => 'loadUnion',
368
            'list' => 'loadList',
369 45
        ];
370
371 135
        return $this->makeCallbackCallback(
372 135
            $type,
373 135
            $node,
374
            function (
375
                DOMElement $node,
376
                DOMElement $childNode
377
            ) use (
378 135
                $methods,
379 135
                $type
380
            ) {
381 135
                $this->maybeCallMethod(
382 135
                    $methods,
383 135
                    $childNode->localName,
384 135
                    $childNode,
385 135
                    $type,
386 90
                    $childNode
387 45
                );
388 135
            },
389 90
            $callback
390 45
        );
391
    }
392
393 135
    protected function loadList(SimpleType $type, DOMElement $node)
394
    {
395 135
        if ($node->hasAttribute("itemType")) {
396
            /**
397
            * @var SimpleType $listType
398
            */
399 135
            $listType = $this->findSomeType($type, $node, 'itemType');
400 135
            $type->setList($listType);
401 45
        } else {
402
            $addCallback = function (SimpleType $list) use ($type) {
403 135
                $type->setList($list);
404 135
            };
405
406 135
            Type::loadTypeWithCallbackOnChildNodes(
407 135
                $this,
408 135
                $type->getSchema(),
409 135
                $node,
410 90
                $addCallback
411 45
            );
412
        }
413 135
    }
414
415 135
    protected function loadUnion(SimpleType $type, DOMElement $node)
416
    {
417 135
        if ($node->hasAttribute("memberTypes")) {
418 135
            $types = preg_split('/\s+/', $node->getAttribute("memberTypes"));
419 135
            foreach ($types as $typeName) {
420
                /**
421
                * @var SimpleType $unionType
422
                */
423 135
                $unionType = $this->findSomeTypeFromAttribute(
424 135
                    $type,
425 135
                    $node,
426 90
                    $typeName
427 45
                );
428 135
                $type->addUnion($unionType);
429 45
            }
430 45
        }
431
        $addCallback = function (SimpleType $unType) use ($type) {
432 135
            $type->addUnion($unType);
433 135
        };
434
435 135
        Type::loadTypeWithCallbackOnChildNodes(
436 135
            $this,
437 135
            $type->getSchema(),
438 135
            $node,
439 90
            $addCallback
440 45
        );
441 135
    }
442
443 135
    protected function loadExtensionChildNode(
444
        BaseComplexType $type,
445
        DOMElement $node,
446
        DOMElement $childNode
447
    ) {
448
        $commonMethods = [
449
            [
450 135
                ['sequence', 'choice', 'all'],
451 135
                [$this, 'maybeLoadSequenceFromElementContainer'],
452
                [
453 135
                    $type,
454 135
                    $childNode,
455 45
                ],
456 45
            ],
457 45
        ];
458
        $methods = [
459 90
            'attribute' => [
460 135
                [$type, 'addAttributeFromAttributeOrRef'],
461
                [
462 135
                    $this,
463 135
                    $childNode,
464 135
                    $type->getSchema(),
465 90
                    $node
466 45
                ]
467 45
            ],
468
            'attributeGroup' => [
469 45
                (AttributeGroup::class . '::findSomethingLikeThis'),
470
                [
471 135
                    $this,
472 135
                    $type->getSchema(),
473 135
                    $node,
474 135
                    $childNode,
475 90
                    $type
476 45
                ]
477 45
            ],
478 45
        ];
479
480 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
481 135
    }
482
483 135
    protected function loadExtension(BaseComplexType $type, DOMElement $node)
484
    {
485 135
        $extension = new Extension();
486 135
        $type->setExtension($extension);
487
488 135
        if ($node->hasAttribute("base")) {
489 135
            $this->findAndSetSomeBase(
490 135
                $type,
491 135
                $extension,
492 90
                $node
493 45
            );
494 45
        }
495
496 135
        $this->loadExtensionChildNodes($type, $node->childNodes, $node);
497 135
    }
498
499 135
    protected function loadExtensionChildNodes(
500
        BaseComplexType $type,
501
        DOMNodeList $childNodes,
502
        DOMElement $node
503
    ) {
504 135
        foreach ($childNodes as $childNode) {
505 135
            if ($childNode instanceof DOMElement) {
506 135
                $this->loadExtensionChildNode(
507 135
                    $type,
508 135
                    $node,
509 90
                    $childNode
510 45
                );
511 45
            }
512 45
        }
513 135
    }
514
515 135
    protected function loadRestriction(Type $type, DOMElement $node)
516
    {
517 135
        Restriction::loadRestriction($this, $type, $node);
518 135
    }
519
520
    /**
521
    * @return Closure
522
    */
523 135
    protected function loadElementDef(Schema $schema, DOMElement $node)
524
    {
525 135
        return $this->loadAttributeOrElementDef($schema, $node, false);
526
    }
527
528
    /**
529
    * @return Closure
530
    */
531 135
    protected function loadImport(Schema $schema, DOMElement $node)
532
    {
533 135
        $base = urldecode($node->ownerDocument->documentURI);
534 135
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation"));
535
536 135
        $namespace = $node->getAttribute("namespace");
537
538
        if (
539
            (
540 135
                isset(self::$globalSchemaInfo[$namespace]) &&
541 135
                Schema::hasLoadedFile(
542 135
                    $loadedFilesKey = self::$globalSchemaInfo[$namespace]
543 45
                )
544 45
            ) ||
545 9
            Schema::hasLoadedFile(
546 9
                $loadedFilesKey = $this->getNamespaceSpecificFileIndex(
547 9
                    $file,
548 6
                    $namespace
549 3
                )
550 3
            ) ||
551 47
            Schema::hasLoadedFile($loadedFilesKey = $file)
552 45
        ) {
553 135
            $schema->addSchema(Schema::getLoadedFile($loadedFilesKey));
554
555
            return function() {
556 135
            };
557
        }
558
559 3
        return $this->loadImportFresh($schema, $node, $file, $namespace);
560
    }
561
562
    /**
563
    * @param string $file
564
    * @param string $namespace
565
    *
566
    * @return Closure
567
    */
568 3
    protected function loadImportFresh(
569
        Schema $schema,
570
        DOMElement $node,
571
        $file,
572
        $namespace
573
    ) {
574 3
        if (! $namespace) {
575 3
            $newSchema = Schema::setLoadedFile($file, $schema);
576 1
        } else {
577
            $newSchema = Schema::setLoadedFile($file, new Schema());
578
            $newSchema->addSchema($this->getGlobalSchema());
579
        }
580
581 3
        $xml = $this->getDOM(isset($this->knownLocationSchemas[$file]) ? $this->knownLocationSchemas[$file] : $file);
582
583 3
        $callbacks = $this->schemaNode($newSchema, $xml->documentElement, $schema);
584
585 3
        if ($namespace) {
586
            $schema->addSchema($newSchema);
587
        }
588
589
590 3
        return function () use ($callbacks) {
591 3
            foreach ($callbacks as $callback) {
592 3
                $callback();
593 1
            }
594 3
        };
595
    }
596
}
597