Completed
Pull Request — master (#18)
by SignpostMarv
03:40
created

SchemaReaderLoadAbstraction   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 508
Duplicated Lines 5.31 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 27
loc 508
ccs 288
cts 288
cp 1
rs 8.3396
wmc 44

22 Methods

Rating   Name   Duplication   Size   Complexity  
A loadAttributeOrElementDef() 0 17 2
A loadAttributeGroup() 0 3 1
A loadAttributeDef() 0 3 1
B loadSequenceNormaliseMax() 0 10 5
A loadExtensionChildNodes() 10 16 3
B loadComplexTypeBeforeCallbackCallback() 0 26 5
B loadUnion() 0 25 3
B loadSimpleType() 0 37 2
A loadRestriction() 0 3 1
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() 11 16 3
A loadElementDef() 0 3 1
A loadComplexType() 0 22 1
A addGroupAsElement() 0 18 1
A loadSequenceChildNodeLoadGroup() 0 10 1
A loadList() 0 18 2
B loadSequenceChildNodeLoadElement() 0 26 4
B loadSequenceChildNode() 0 38 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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 SchemaReaderFillAbstraction
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
        $limit = $node->childNodes->length;
106 135 View Code Duplication
        for ($i = 0; $i < $limit; $i += 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
            /**
108
            * @var DOMNode $childNode
109
            */
110 135
            $childNode = $node->childNodes->item($i);
111 135
            if ($childNode instanceof DOMElement) {
112 135
                $this->loadSequenceChildNode(
113 135
                    $elementContainer,
114 135
                    $node,
115 135
                    $childNode,
116 90
                    $max
117 45
                );
118 45
            }
119 45
        }
120 135
    }
121
122
    /**
123
    * @param int|null $max
124
    */
125 135
    protected function loadSequenceChildNode(
126
        ElementContainer $elementContainer,
127
        DOMElement $node,
128
        DOMElement $childNode,
129
        $max
130
    ) {
131
        $commonMethods = [
132
            [
133 135
                ['sequence', 'choice', 'all'],
134 135
                [$this, 'loadSequenceChildNodeLoadSequence'],
135
                [
136 135
                    $elementContainer,
137 135
                    $childNode,
138 135
                    $max,
139 45
                ],
140 45
            ],
141 45
        ];
142
        $methods = [
143
            'element' => [
144 135
                [$this, 'loadSequenceChildNodeLoadElement'],
145
                [
146 135
                    $elementContainer,
147 135
                    $node,
148 135
                    $childNode,
149 90
                    $max
150 45
                ]
151 45
            ],
152
            'group' => [
153 135
                [$this, 'loadSequenceChildNodeLoadGroup'],
154
                [
155 135
                    $elementContainer,
156 135
                    $node,
157 90
                    $childNode
158 45
                ]
159 45
            ],
160 45
        ];
161
162 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
163 135
    }
164
165
    /**
166
    * @param int|null $max
167
    */
168 135
    protected function loadSequenceChildNodeLoadSequence(
169
        ElementContainer $elementContainer,
170
        DOMElement $childNode,
171
        $max
172
    ) {
173 135
        $this->loadSequence($elementContainer, $childNode, $max);
174 135
    }
175
176
    /**
177
    * @param int|null $max
178
    */
179 135
    protected function loadSequenceChildNodeLoadElement(
180
        ElementContainer $elementContainer,
181
        DOMElement $node,
182
        DOMElement $childNode,
183
        $max
184
    ) {
185 135
        if ($childNode->hasAttribute("ref")) {
186
            /**
187
            * @var ElementDef $referencedElement
188
            */
189 135
            $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
190 135
            $element = ElementRef::loadElementRef(
191 135
                $referencedElement,
192 90
                $childNode
193 45
            );
194 45
        } else {
195 135
            $element = Element::loadElement(
196 135
                $this,
197 135
                $elementContainer->getSchema(),
198 90
                $childNode
199 45
            );
200
        }
201 135
        if (is_int($max) && (bool) $max) {
202 135
            $element->setMax($max);
203 45
        }
204 135
        $elementContainer->addElement($element);
205 135
    }
206
207 135
    protected function loadSequenceChildNodeLoadGroup(
208
        ElementContainer $elementContainer,
209
        DOMElement $node,
210
        DOMElement $childNode
211
    ) {
212 135
        $this->addGroupAsElement(
213 135
            $elementContainer->getSchema(),
214 135
            $node,
215 135
            $childNode,
216 90
            $elementContainer
217 45
        );
218 135
    }
219
220 135
    protected function addGroupAsElement(
221
        Schema $schema,
222
        DOMElement $node,
223
        DOMElement $childNode,
224
        ElementContainer $elementContainer
225
    ) {
226
        /**
227
        * @var Group $referencedGroup
228
        */
229 135
        $referencedGroup = $this->findSomething(
230 135
            'findGroup',
231 135
            $schema,
232 135
            $node,
233 135
            $childNode->getAttribute("ref")
234 45
        );
235
236 135
        $group = GroupRef::loadGroupRef($referencedGroup, $childNode);
237 135
        $elementContainer->addElement($group);
238 135
    }
239
240
    /**
241
    * @return Closure
242
    */
243 135
    protected function loadGroup(Schema $schema, DOMElement $node)
244
    {
245 135
        return Group::loadGroup($this, $schema, $node);
246
    }
247
248
    /**
249
    * @return BaseComplexType
250
    */
251 135
    protected function loadComplexTypeBeforeCallbackCallback(
252
        Schema $schema,
253
        DOMElement $node
254
    ) {
255 135
        $isSimple = false;
256
257 135
        $limit = $node->childNodes->length;
258 135
        for ($i = 0; $i < $limit; $i += 1) {
259
            /**
260
            * @var DOMNode $childNode
261
            */
262 135
            $childNode = $node->childNodes->item($i);
263 135
            if ($childNode->localName === "simpleContent") {
264 6
                $isSimple = true;
265 6
                break;
266
            }
267 45
        }
268
269 135
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute("name")) : new ComplexType($schema, $node->getAttribute("name"));
270
271 135
        $type->setDoc(static::getDocumentation($node));
272 135
        if ($node->getAttribute("name")) {
273 135
            $schema->addType($type);
274 45
        }
275
276 135
        return $type;
277
    }
278
279
    /**
280
    * @param Closure|null $callback
281
    *
282
    * @return Closure
283
    */
284 135
    protected function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
285
    {
286 135
        $type = $this->loadComplexTypeBeforeCallbackCallback($schema, $node);
287
288 135
        return $this->makeCallbackCallback(
289 135
            $type,
290 135
            $node,
291
            function (
292
                DOMElement $node,
293
                DOMElement $childNode
294
            ) use(
295 135
                $schema,
296 135
                $type
297
            ) {
298 135
                $this->loadComplexTypeFromChildNode(
299 135
                    $type,
300 135
                    $node,
301 135
                    $childNode,
302 90
                    $schema
303 45
                );
304 135
            },
305 90
            $callback
306 45
        );
307
    }
308
309 135
    protected function loadComplexTypeFromChildNode(
310
        BaseComplexType $type,
311
        DOMElement $node,
312
        DOMElement $childNode,
313
        Schema $schema
314
    ) {
315
        $commonMethods = [
316
            [
317 135
                ['sequence', 'choice', 'all'],
318 135
                [$this, 'maybeLoadSequenceFromElementContainer'],
319
                [
320 135
                    $type,
321 135
                    $childNode,
322 45
                ],
323 45
            ],
324 45
        ];
325
        $methods = [
326 90
            'attribute' => [
327 135
                [$type, 'addAttributeFromAttributeOrRef'],
328
                [
329 135
                    $this,
330 135
                    $childNode,
331 135
                    $schema,
332 90
                    $node
333 45
                ]
334 45
            ],
335
            'attributeGroup' => [
336 45
                (AttributeGroup::class . '::findSomethingLikeThis'),
337
                [
338 135
                    $this,
339 135
                    $schema,
340 135
                    $node,
341 135
                    $childNode,
342 90
                    $type
343 45
                ]
344 45
            ],
345 45
        ];
346
        if (
347 90
            $type instanceof ComplexType
348 45
        ) {
349 135
            $methods['group'] = [
350 135
                [$this, 'addGroupAsElement'],
351
                [
352 135
                    $schema,
353 135
                    $node,
354 135
                    $childNode,
355 90
                    $type
356 45
                ]
357 45
            ];
358 45
        }
359
360 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
361 135
    }
362
363
    /**
364
    * @param Closure|null $callback
365
    *
366
    * @return Closure
367
    */
368 135
    protected function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
369
    {
370 135
        $type = new SimpleType($schema, $node->getAttribute("name"));
371 135
        $type->setDoc(static::getDocumentation($node));
372 135
        if ($node->getAttribute("name")) {
373 135
            $schema->addType($type);
374 45
        }
375
376 90
        static $methods = [
377
            'union' => 'loadUnion',
378
            'list' => 'loadList',
379 45
        ];
380
381 135
        return $this->makeCallbackCallback(
382 135
            $type,
383 135
            $node,
384
            function (
385
                DOMElement $node,
386
                DOMElement $childNode
387
            ) use (
388 135
                $methods,
389 135
                $type
390
            ) {
391
                /**
392
                * @var string[]
393
                */
394 135
                $methods = $methods;
395
396 135
                $this->maybeCallMethod(
397 135
                    $methods,
398 135
                    $childNode->localName,
399 135
                    $childNode,
400 135
                    $type,
401 90
                    $childNode
402 45
                );
403 135
            },
404 90
            $callback
405 45
        );
406
    }
407
408 135
    protected function loadList(SimpleType $type, DOMElement $node)
409
    {
410 135
        if ($node->hasAttribute("itemType")) {
411
            /**
412
            * @var SimpleType $listType
413
            */
414 135
            $listType = $this->findSomeType($type, $node, 'itemType');
415 135
            $type->setList($listType);
416 45
        } else {
417
            $addCallback = function (SimpleType $list) use ($type) {
418 135
                $type->setList($list);
419 135
            };
420
421 135
            Type::loadTypeWithCallbackOnChildNodes(
422 135
                $this,
423 135
                $type->getSchema(),
424 135
                $node,
425 90
                $addCallback
426 45
            );
427
        }
428 135
    }
429
430 135
    protected function loadUnion(SimpleType $type, DOMElement $node)
431
    {
432 135
        if ($node->hasAttribute("memberTypes")) {
433 135
            $types = preg_split('/\s+/', $node->getAttribute("memberTypes"));
434 135
            foreach ($types as $typeName) {
435
                /**
436
                * @var SimpleType $unionType
437
                */
438 135
                $unionType = $this->findSomeTypeFromAttribute(
439 135
                    $type,
440 135
                    $node,
441 90
                    $typeName
442 45
                );
443 135
                $type->addUnion($unionType);
444 45
            }
445 45
        }
446 135
        $addCallback = function (SimpleType $unType) use ($type) {
447 135
            $type->addUnion($unType);
448 135
        };
449
450 135
        Type::loadTypeWithCallbackOnChildNodes(
451 135
            $this,
452 135
            $type->getSchema(),
453 135
            $node,
454 90
            $addCallback
455 45
        );
456 135
    }
457
458 135
    protected function loadExtensionChildNode(
459
        BaseComplexType $type,
460
        DOMElement $node,
461
        DOMElement $childNode
462
    ) {
463
        $commonMethods = [
464
            [
465 135
                ['sequence', 'choice', 'all'],
466 135
                [$this, 'maybeLoadSequenceFromElementContainer'],
467
                [
468 135
                    $type,
469 135
                    $childNode,
470 45
                ],
471 45
            ],
472 45
        ];
473
        $methods = [
474 90
            'attribute' => [
475 135
                [$type, 'addAttributeFromAttributeOrRef'],
476
                [
477 135
                    $this,
478 135
                    $childNode,
479 135
                    $type->getSchema(),
480 90
                    $node
481 45
                ]
482 45
            ],
483
            'attributeGroup' => [
484 45
                (AttributeGroup::class . '::findSomethingLikeThis'),
485
                [
486 135
                    $this,
487 135
                    $type->getSchema(),
488 135
                    $node,
489 135
                    $childNode,
490 90
                    $type
491 45
                ]
492 45
            ],
493 45
        ];
494
495 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
496 135
    }
497
498 135
    protected function loadExtension(BaseComplexType $type, DOMElement $node)
499
    {
500 135
        $extension = new Extension();
501 135
        $type->setExtension($extension);
502
503 135
        if ($node->hasAttribute("base")) {
504 135
            $this->findAndSetSomeBase(
505 135
                $type,
506 135
                $extension,
507 90
                $node
508 45
            );
509 45
        }
510
511 135
        $this->loadExtensionChildNodes($type, $node->childNodes, $node);
512 135
    }
513
514 135
    protected function loadExtensionChildNodes(
515
        BaseComplexType $type,
516
        DOMNodeList $childNodes,
517
        DOMElement $node
518
    ) {
519 135
        $limit = $childNodes->length;
520 135 View Code Duplication
        for ($i = 0; $i < $limit; $i += 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
521
            /**
522
            * @var DOMElement $childNode
523
            */
524 135
            $childNode = $childNodes->item($i);
525 135
            if ($childNode instanceof DOMElement) {
526 135
                $this->loadExtensionChildNode(
527 135
                    $type,
528 135
                    $node,
529 90
                    $childNode
530 45
                );
531 45
            }
532 45
        }
533 135
    }
534
535 135
    protected function loadRestriction(Type $type, DOMElement $node)
536
    {
537 135
        Restriction::loadRestriction($this, $type, $node);
538 135
    }
539
540
    /**
541
    * @return Closure
542
    */
543 135
    protected function loadElementDef(Schema $schema, DOMElement $node)
544
    {
545 135
        return $this->loadAttributeOrElementDef($schema, $node, false);
546
    }
547
}
548