SchemaReader::findSomethingLikeAttributeGroup()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 4
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GoetasWebservices\XML\XSDReader;
6
7
use Closure;
8
use DOMDocument;
9
use DOMElement;
10
use DOMNode;
11
use GoetasWebservices\XML\XSDReader\Documentation\DocumentationReader;
12
use GoetasWebservices\XML\XSDReader\Documentation\StandardDocumentationReader;
13
use GoetasWebservices\XML\XSDReader\Exception\IOException;
14
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
15
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
16
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
17
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
18
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
19
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
21
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
22
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
23
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
24
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
25
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
26
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
27
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
28
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
29
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
30
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
31
use GoetasWebservices\XML\XSDReader\Schema\Item;
32
use GoetasWebservices\XML\XSDReader\Schema\Schema;
33
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
34
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
35
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
36
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
37
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
38
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
39
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
40
41
class SchemaReader
42
{
43
    public const XSD_NS = 'http://www.w3.org/2001/XMLSchema';
44
45
    public const XML_NS = 'http://www.w3.org/XML/1998/namespace';
46
47
    /**
48
     * @var DocumentationReader
49
     */
50
    private $documentationReader;
51
52
    /**
53
     * @var Schema[]
54
     */
55
    private $loadedFiles = array();
56
57
    /**
58
     * @var Schema[][]
59
     */
60
    private $loadedSchemas = array();
61
62
    /**
63
     * @var string[]
64
     */
65
    protected $knownLocationSchemas = [
66
        'http://www.w3.org/2001/xml.xsd' => (
67
            __DIR__.'/Resources/xml.xsd'
68
        ),
69
        'http://www.w3.org/2001/XMLSchema.xsd' => (
70
            __DIR__.'/Resources/XMLSchema.xsd'
71
        ),
72
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
73
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
74
        ),
75
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
76
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
77
        ),
78
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
79
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
80
        ),
81
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
82
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
83
        ),
84
    ];
85
86
    /**
87
     * @var string[]
88
     */
89
    protected static $globalSchemaInfo = array(
90
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
91
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
92
    );
93
94 2
    private function extractErrorMessage(): \Exception
95
    {
96 2
        $errors = array();
97
98 2
        foreach (libxml_get_errors() as $error) {
99 1
            $errors[] = sprintf("Error[%s] code %s: %s in '%s' at position %s:%s", $error->level, $error->code, trim($error->message), $error->file, $error->line, $error->column);
100
        }
101 2
        $e = new \Exception(implode('; ', $errors));
102 2
        libxml_use_internal_errors(false);
103
104 2
        return $e;
105
    }
106
107 69
    public function __construct(DocumentationReader $documentationReader = null)
108
    {
109 69
        if (null === $documentationReader) {
110 69
            $documentationReader = new StandardDocumentationReader();
111
        }
112 69
        $this->documentationReader = $documentationReader;
113 69
    }
114
115 1
    public function addKnownSchemaLocation(string $remote, string $local): void
116
    {
117 1
        $this->knownLocationSchemas[$remote] = $local;
118 1
    }
119
120 58
    private function loadAttributeGroup(
121
        Schema $schema,
122
        DOMElement $node
123
    ): Closure {
124 58
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
125 58
        $attGroup->setDoc($this->getDocumentation($node));
126 58
        $schema->addAttributeGroup($attGroup);
127
128 58
        return function () use ($schema, $node, $attGroup): void {
129 58
            SchemaReader::againstDOMNodeList(
130 58
                $node,
131 58
                function (
132
                    DOMElement $node,
133
                    DOMElement $childNode
134
                ) use (
135 58
                    $schema,
136 58
                    $attGroup
137
                ): void {
138 58
                    switch ($childNode->localName) {
139 58
                        case 'attribute':
140 58
                            $attribute = $this->getAttributeFromAttributeOrRef(
141 58
                                $childNode,
142 58
                                $schema,
143 58
                                $node
144
                            );
145 58
                            $attGroup->addAttribute($attribute);
146 58
                            break;
147 58
                        case 'attributeGroup':
148 1
                            $this->findSomethingLikeAttributeGroup(
149 1
                                $schema,
150 1
                                $node,
151 1
                                $childNode,
152 1
                                $attGroup
153
                            );
154 1
                            break;
155
                    }
156 58
                }
157
            );
158 58
        };
159
    }
160
161 58
    private function getAttributeFromAttributeOrRef(
162
        DOMElement $childNode,
163
        Schema $schema,
164
        DOMElement $node
165
    ): AttributeItem {
166 58
        if ($childNode->hasAttribute('ref')) {
167 58
            $attribute = $this->findAttributeItem($schema, $node, $childNode->getAttribute('ref'));
168
        } else {
169
            /**
170
             * @var Attribute
171
             */
172 58
            $attribute = $this->loadAttribute($schema, $childNode);
173
        }
174
175 58
        return $attribute;
176
    }
177
178 58
    private function loadAttribute(
179
        Schema $schema,
180
        DOMElement $node
181
    ): Attribute {
182 58
        $attribute = new Attribute($schema, $node->getAttribute('name'));
183 58
        $attribute->setDoc($this->getDocumentation($node));
184 58
        $this->fillItem($attribute, $node);
185
186 58
        if ($node->hasAttribute('nillable')) {
187 1
            $attribute->setNil($node->getAttribute('nillable') == 'true');
188
        }
189 58
        if ($node->hasAttribute('form')) {
190 1
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
191
        }
192 58
        if ($node->hasAttribute('use')) {
193 58
            $attribute->setUse($node->getAttribute('use'));
194
        }
195
196 58
        return $attribute;
197
    }
198
199 58
    private function loadAttributeOrElementDef(
200
        Schema $schema,
201
        DOMElement $node,
202
        bool $attributeDef
203
    ): Closure {
204 58
        $name = $node->getAttribute('name');
205 58
        if ($attributeDef) {
206 58
            $attribute = new AttributeDef($schema, $name);
207 58
            $schema->addAttribute($attribute);
208
        } else {
209 58
            $attribute = new ElementDef($schema, $name);
210 58
            $schema->addElement($attribute);
211
        }
212
213 58
        return function () use ($attribute, $node): void {
214 58
            $this->fillItem($attribute, $node);
215 58
        };
216
    }
217
218 58
    private function loadAttributeDef(Schema $schema, DOMElement $node): Closure
219
    {
220 58
        return $this->loadAttributeOrElementDef($schema, $node, true);
221
    }
222
223 58
    private function getDocumentation(DOMElement $node): string
224
    {
225 58
        return $this->documentationReader->get($node);
226
    }
227
228
    /**
229
     * @return Closure[]
230
     */
231 58
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null): array
232
    {
233 58
        $this->setSchemaThingsFromNode($schema, $node, $parent);
234 58
        $functions = array();
235
236 58
        self::againstDOMNodeList(
237 58
            $node,
238 58
            function (
239
                DOMElement $node,
240
                DOMElement $childNode
241
            ) use (
242 58
                $schema,
243 58
                &$functions
244
            ): void {
245 58
                $callback = null;
246
247 58
                switch ($childNode->localName) {
248 58
                    case 'attributeGroup':
249 58
                        $callback = $this->loadAttributeGroup($schema, $childNode);
250 58
                        break;
251 58
                    case 'include':
252 58
                    case 'import':
253 58
                        $callback = $this->loadImport($schema, $childNode);
254 58
                        break;
255 58
                    case 'element':
256 58
                        $callback = $this->loadElementDef($schema, $childNode);
257 58
                        break;
258 58
                    case 'attribute':
259 58
                        $callback = $this->loadAttributeDef($schema, $childNode);
260 58
                        break;
261 58
                    case 'group':
262 58
                        $callback = $this->loadGroup($schema, $childNode);
263 58
                        break;
264 58
                    case 'complexType':
265 58
                        $callback = $this->loadComplexType($schema, $childNode);
266 58
                        break;
267 58
                    case 'simpleType':
268 58
                        $callback = $this->loadSimpleType($schema, $childNode);
269 58
                        break;
270
                }
271
272 58
                if ($callback instanceof Closure) {
273 58
                    $functions[] = $callback;
274
                }
275 58
            }
276
        );
277
278 58
        return $functions;
279
    }
280
281 58
    private function loadGroupRef(Group $referenced, DOMElement $node): GroupRef
282
    {
283 58
        $ref = new GroupRef($referenced);
284 58
        $ref->setDoc($this->getDocumentation($node));
285
286 58
        self::maybeSetMax($ref, $node);
287 58
        self::maybeSetMin($ref, $node);
288
289 58
        return $ref;
290
    }
291
292 58
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node): void
293
    {
294 58
        if ($node->hasAttribute('maxOccurs')) {
295 58
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
296
        }
297 58
    }
298
299 58
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node): void
300
    {
301 58
        if ($node->hasAttribute('minOccurs')) {
302 58
            $ref->setMin((int) $node->getAttribute('minOccurs'));
303 58
            if ($ref->getMin() > $ref->getMax()) {
304 58
                $ref->setMax($ref->getMin());
305
            }
306
        }
307 58
    }
308
309 58
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, int $max = null): void
310
    {
311
        $max =
312
            (
313 58
                (is_int($max) && (bool) $max) ||
314 58
                $node->getAttribute('maxOccurs') == 'unbounded' ||
315 58
                $node->getAttribute('maxOccurs') > 1
316
            )
317 58
                ? 2
318 58
                : null;
319
320 58
        self::againstDOMNodeList(
321 58
            $node,
322 58
            function (
323
                DOMElement $node,
324
                DOMElement $childNode
325
            ) use (
326 58
                $elementContainer,
327 58
                $max
328
            ): void {
329 58
                $this->loadSequenceChildNode(
330 58
                    $elementContainer,
331 58
                    $node,
332 58
                    $childNode,
333 58
                    $max
334
                );
335 58
            }
336
        );
337 58
    }
338
339 58
    private function loadSequenceChildNode(
340
        ElementContainer $elementContainer,
341
        DOMElement $node,
342
        DOMElement $childNode,
343
        ? int $max
344
    ): void {
345 58
        switch ($childNode->localName) {
346 58
            case 'sequence':
347 58
            case 'choice':
348 58
            case 'all':
349 58
                $this->loadSequence(
350 58
                    $elementContainer,
351 58
                    $childNode,
352 58
                    $max
353
                );
354 58
                break;
355 58
            case 'element':
356 58
                $this->loadSequenceChildNodeLoadElement(
357 58
                    $elementContainer,
358 58
                    $node,
359 58
                    $childNode,
360 58
                    $max
361
                );
362 58
                break;
363 58
            case 'group':
364 58
                $this->addGroupAsElement(
365 58
                    $elementContainer->getSchema(),
366 58
                    $node,
367 58
                    $childNode,
368 58
                    $elementContainer
369
                );
370 58
                break;
371
        }
372 58
    }
373
374 58
    private function loadSequenceChildNodeLoadElement(
375
        ElementContainer $elementContainer,
376
        DOMElement $node,
377
        DOMElement $childNode,
378
        ? int $max
379
    ): void {
380 58
        if ($childNode->hasAttribute('ref')) {
381 58
            $element = new ElementRef(
382 58
                $this->findElement($elementContainer->getSchema(), $node, $childNode->getAttribute('ref'))
383
            );
384 58
            $element->setDoc($this->getDocumentation($childNode));
385
386 58
            self::maybeSetMax($element, $childNode);
387 58
            self::maybeSetMin($element, $childNode);
388 58
            if ($childNode->hasAttribute('nillable')) {
389
                $element->setNil($childNode->getAttribute('nillable') == 'true');
390
            }
391 58
            if ($childNode->hasAttribute('form')) {
392 58
                $element->setQualified($childNode->getAttribute('form') == 'qualified');
393
            }
394
        } else {
395 58
            $element = $this->loadElement(
396 58
                $elementContainer->getSchema(),
397 58
                $childNode
398
            );
399
        }
400 58
        if ($max > 1) {
401
            /*
402
            * although one might think the typecast is not needed with $max being `? int $max` after passing > 1,
403
            * phpstan@a4f89fa still thinks it's possibly null.
404
            * see https://github.com/phpstan/phpstan/issues/577 for related issue
405
            */
406 58
            $element->setMax((int) $max);
407
        }
408 58
        $elementContainer->addElement($element);
409 58
    }
410
411 58
    private function addGroupAsElement(
412
        Schema $schema,
413
        DOMElement $node,
414
        DOMElement $childNode,
415
        ElementContainer $elementContainer
416
    ): void {
417 58
        $referencedGroup = $this->findGroup(
418 58
            $schema,
419 58
            $node,
420 58
            $childNode->getAttribute('ref')
421
        );
422
423 58
        $group = $this->loadGroupRef($referencedGroup, $childNode);
424 58
        $elementContainer->addElement($group);
425 58
    }
426
427 58
    private function loadGroup(Schema $schema, DOMElement $node): Closure
428
    {
429 58
        $group = new Group($schema, $node->getAttribute('name'));
430 58
        $group->setDoc($this->getDocumentation($node));
431 58
        $groupOriginal = $group;
432
433 58
        if ($node->hasAttribute('maxOccurs') || $node->hasAttribute('maxOccurs')) {
434 1
            $group = new GroupRef($group);
435
436 1
            if ($node->hasAttribute('maxOccurs')) {
437 1
                self::maybeSetMax($group, $node);
438
            }
439 1
            if ($node->hasAttribute('minOccurs')) {
440 1
                self::maybeSetMin($group, $node);
441
            }
442
        }
443
444 58
        $schema->addGroup($group);
445
446 58
        return function () use ($groupOriginal, $node): void {
447 58
            static::againstDOMNodeList(
448 58
                $node,
449 58
                function (DOMelement $node, DOMElement $childNode) use ($groupOriginal): void {
450 58
                    switch ($childNode->localName) {
451 58
                        case 'sequence':
452 58
                        case 'choice':
453 58
                        case 'all':
454 58
                            $this->loadSequence($groupOriginal, $childNode);
455 58
                            break;
456
                    }
457 58
                }
458
            );
459 58
        };
460
    }
461
462 58
    private function loadComplexType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
463
    {
464
        /**
465
         * @var bool
466
         */
467 58
        $isSimple = false;
468
469 58
        self::againstDOMNodeList(
470 58
            $node,
471 58
            function (
472
                DOMElement $node,
473
                DOMElement $childNode
474
            ) use (
475 58
                &$isSimple
476
            ): void {
477 58
                if ($isSimple) {
478 1
                    return;
479
                }
480 58
                if ($childNode->localName === 'simpleContent') {
481 2
                    $isSimple = true;
482
                }
483 58
            }
484
        );
485
486 58
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
487
488 58
        $type->setDoc($this->getDocumentation($node));
489 58
        if ($node->getAttribute('name')) {
490 58
            $schema->addType($type);
491
        }
492
493 58
        return function () use ($type, $node, $schema, $callback): void {
494 58
            $this->fillTypeNode($type, $node, true);
495
496 58
            self::againstDOMNodeList(
497 58
                $node,
498 58
                function (
499
                    DOMElement $node,
500
                    DOMElement $childNode
501
                ) use (
502 58
                    $schema,
503 58
                    $type
504
                ): void {
505 58
                    $this->loadComplexTypeFromChildNode(
506 58
                        $type,
507 58
                        $node,
508 58
                        $childNode,
509 58
                        $schema
510
                    );
511 58
                }
512
            );
513
514 58
            if ($callback instanceof Closure) {
515 58
                call_user_func($callback, $type);
516
            }
517 58
        };
518
    }
519
520 58
    private function loadComplexTypeFromChildNode(
521
        BaseComplexType $type,
522
        DOMElement $node,
523
        DOMElement $childNode,
524
        Schema $schema
525
    ): void {
526 58
        switch ($childNode->localName) {
527 58
            case 'sequence':
528 58
            case 'choice':
529 58
            case 'all':
530 58
                if ($type instanceof ElementContainer) {
531 58
                    $this->loadSequence(
532 58
                        $type,
533 58
                        $childNode
534
                    );
535
                }
536 58
                break;
537 58
            case 'attribute':
538 58
                $this->addAttributeFromAttributeOrRef(
539 58
                    $type,
540 58
                    $childNode,
541 58
                    $schema,
542 58
                    $node
543
                );
544 58
                break;
545 58
            case 'attributeGroup':
546 2
                $this->findSomethingLikeAttributeGroup(
547 2
                    $schema,
548 2
                    $node,
549 2
                    $childNode,
550 2
                    $type
551
                );
552 2
                break;
553 58
            case 'group':
554
                if (
555 1
                    $type instanceof ComplexType
556
                ) {
557 1
                    $this->addGroupAsElement(
558 1
                        $schema,
559 1
                        $node,
560 1
                        $childNode,
561 1
                        $type
562
                    );
563
                }
564 1
                break;
565
        }
566 58
    }
567
568 58
    private function loadSimpleType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
569
    {
570 58
        $type = new SimpleType($schema, $node->getAttribute('name'));
571 58
        $type->setDoc($this->getDocumentation($node));
572 58
        if ($node->getAttribute('name')) {
573 58
            $schema->addType($type);
574
        }
575
576 58
        return function () use ($type, $node, $callback): void {
577 58
            $this->fillTypeNode($type, $node, true);
578
579 58
            self::againstDOMNodeList(
580 58
                $node,
581 58
                function (DOMElement $node, DOMElement $childNode) use ($type): void {
582 58
                    switch ($childNode->localName) {
583 58
                        case 'union':
584 58
                            $this->loadUnion($type, $childNode);
585 58
                            break;
586 58
                        case 'list':
587 58
                            $this->loadList($type, $childNode);
588 58
                            break;
589
                    }
590 58
                }
591
            );
592
593 58
            if ($callback instanceof Closure) {
594 58
                call_user_func($callback, $type);
595
            }
596 58
        };
597
    }
598
599 58
    private function loadList(SimpleType $type, DOMElement $node): void
600
    {
601 58
        if ($node->hasAttribute('itemType')) {
602
            /**
603
             * @var SimpleType
604
             */
605 58
            $listType = $this->findSomeType($type, $node, 'itemType');
606 58
            $type->setList($listType);
607
        } else {
608 58
            self::againstDOMNodeList(
609 58
                $node,
610 58
                function (
611
                    DOMElement $node,
612
                    DOMElement $childNode
613
                ) use (
614 58
                    $type
615
                ): void {
616 58
                    $this->loadTypeWithCallback(
617 58
                        $type->getSchema(),
618 58
                        $childNode,
619 58
                        function (SimpleType $list) use ($type): void {
620 58
                            $type->setList($list);
621 58
                        }
622
                    );
623 58
                }
624
            );
625
        }
626 58
    }
627
628 58
    private function findSomeType(
629
        SchemaItem $fromThis,
630
        DOMElement $node,
631
        string $attributeName
632
    ): SchemaItem {
633 58
        return $this->findSomeTypeFromAttribute(
634 58
            $fromThis,
635 58
            $node,
636 58
            $node->getAttribute($attributeName)
637
        );
638
    }
639
640 58
    private function findSomeTypeFromAttribute(
641
        SchemaItem $fromThis,
642
        DOMElement $node,
643
        string $attributeName
644
    ): SchemaItem {
645 58
        $out = $this->findType(
646 58
            $fromThis->getSchema(),
647 58
            $node,
648 58
            $attributeName
649
        );
650
651 58
        return $out;
652
    }
653
654 58
    private function loadUnion(SimpleType $type, DOMElement $node): void
655
    {
656 58
        if ($node->hasAttribute('memberTypes')) {
657 58
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
658 58
            foreach ($types as $typeName) {
659
                /**
660
                 * @var SimpleType
661
                 */
662 58
                $unionType = $this->findSomeTypeFromAttribute(
663 58
                    $type,
664 58
                    $node,
665 58
                    $typeName
666
                );
667 58
                $type->addUnion($unionType);
668
            }
669
        }
670 58
        self::againstDOMNodeList(
671 58
            $node,
672 58
            function (
673
                DOMElement $node,
674
                DOMElement $childNode
675
            ) use (
676 58
                $type
677
            ): void {
678 58
                $this->loadTypeWithCallback(
679 58
                    $type->getSchema(),
680 58
                    $childNode,
681 58
                    function (SimpleType $unType) use ($type): void {
682 58
                        $type->addUnion($unType);
683 58
                    }
684
                );
685 58
            }
686
        );
687 58
    }
688
689 58
    private function fillTypeNode(Type $type, DOMElement $node, bool $checkAbstract = false): void
690
    {
691 58
        if ($checkAbstract) {
692 58
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
693
        }
694
695 58
        self::againstDOMNodeList(
696 58
            $node,
697 58
            function (DOMElement $node, DOMElement $childNode) use ($type): void {
698 58
                switch ($childNode->localName) {
699 58
                    case 'restriction':
700 58
                        $this->loadRestriction($type, $childNode);
701 58
                        break;
702 58
                    case 'extension':
703 58
                        if ($type instanceof BaseComplexType) {
704 58
                            $this->loadExtension($type, $childNode);
705
                        }
706 58
                        break;
707 58
                    case 'simpleContent':
708 58
                    case 'complexContent':
709 58
                        $this->fillTypeNode($type, $childNode);
710 58
                        break;
711
                }
712 58
            }
713
        );
714 58
    }
715
716 58
    private function loadExtension(BaseComplexType $type, DOMElement $node): void
717
    {
718 58
        $extension = new Extension();
719 58
        $type->setExtension($extension);
720
721 58
        if ($node->hasAttribute('base')) {
722 58
            $this->findAndSetSomeBase(
723 58
                $type,
724 58
                $extension,
725 58
                $node
726
            );
727
        }
728 58
        $this->loadExtensionChildNodes($type, $node);
729 58
    }
730
731 58
    private function findAndSetSomeBase(
732
        Type $type,
733
        Base $setBaseOnThis,
734
        DOMElement $node
735
    ): void {
736
        /**
737
         * @var Type
738
         */
739 58
        $parent = $this->findSomeType($type, $node, 'base');
740 58
        $setBaseOnThis->setBase($parent);
741 58
    }
742
743 58
    private function loadExtensionChildNodes(
744
        BaseComplexType $type,
745
        DOMElement $node
746
    ): void {
747 58
        self::againstDOMNodeList(
748 58
            $node,
749 58
            function (
750
                DOMElement $node,
751
                DOMElement $childNode
752
            ) use (
753 58
                $type
754
            ): void {
755 58
                switch ($childNode->localName) {
756 58
                    case 'sequence':
757 58
                    case 'choice':
758 58
                    case 'all':
759 58
                        if ($type instanceof ElementContainer) {
760 58
                            $this->loadSequence(
761 58
                                $type,
762 58
                                $childNode
763
                            );
764
                        }
765 58
                        break;
766 58
                    case 'attribute':
767 58
                        $this->addAttributeFromAttributeOrRef(
768 58
                            $type,
769 58
                            $childNode,
770 58
                            $type->getSchema(),
771 58
                            $node
772
                        );
773 58
                        break;
774 58
                    case 'attributeGroup':
775 58
                        $this->findSomethingLikeAttributeGroup(
776 58
                            $type->getSchema(),
777 58
                            $node,
778 58
                            $childNode,
779 58
                            $type
780
                        );
781 58
                        break;
782
                }
783 58
            }
784
        );
785 58
    }
786
787 58
    private function loadRestriction(Type $type, DOMElement $node): void
788
    {
789 58
        $restriction = new Restriction();
790 58
        $type->setRestriction($restriction);
791 58
        if ($node->hasAttribute('base')) {
792 58
            $this->findAndSetSomeBase($type, $restriction, $node);
793
        } else {
794 58
            self::againstDOMNodeList(
795 58
                $node,
796 58
                function (
797
                    DOMElement $node,
798
                    DOMElement $childNode
799
                ) use (
800 58
                    $type,
801 58
                    $restriction
802
                ): void {
803 58
                    $this->loadTypeWithCallback(
804 58
                        $type->getSchema(),
805 58
                        $childNode,
806 58
                        function (Type $restType) use ($restriction): void {
807 58
                            $restriction->setBase($restType);
808 58
                        }
809
                    );
810 58
                }
811
            );
812
        }
813 58
        self::againstDOMNodeList(
814 58
            $node,
815 58
            function (
816
                DOMElement $node,
817
                DOMElement $childNode
818
            ) use (
819 58
                $restriction
820
            ): void {
821
                if (
822 58
                in_array(
823 58
                    $childNode->localName,
824
                    [
825 58
                        'enumeration',
826
                        'pattern',
827
                        'length',
828
                        'minLength',
829
                        'maxLength',
830
                        'minInclusive',
831
                        'maxInclusive',
832
                        'minExclusive',
833
                        'maxExclusive',
834
                        'fractionDigits',
835
                        'totalDigits',
836
                        'whiteSpace',
837
                    ],
838 58
                    true
839
                )
840
                ) {
841 58
                    $restriction->addCheck(
842 58
                        $childNode->localName,
843
                        [
844 58
                            'value' => $childNode->getAttribute('value'),
845 58
                            'doc' => $this->getDocumentation($childNode),
846
                        ]
847
                    );
848
                }
849 58
            }
850
        );
851 58
    }
852
853
    /**
854
     * @return mixed[]
855
     */
856 58
    private static function splitParts(DOMElement $node, string $typeName): array
857
    {
858 58
        $prefix = null;
859 58
        $name = $typeName;
860 58
        if (strpos($typeName, ':') !== false) {
861 58
            list($prefix, $name) = explode(':', $typeName);
862
        }
863
864 58
        $namespace = $node->lookupNamespaceUri($prefix ?: '');
865
866
        return array(
867 58
            $name,
868 58
            $namespace,
869 58
            $prefix,
870
        );
871
    }
872
873 58
    private function findAttributeItem(Schema $schema, DOMElement $node, string $typeName): AttributeItem
874
    {
875 58
        list($name, $namespace) = static::splitParts($node, $typeName);
876
877
        /**
878
         * @var string|null $namespace
879
         */
880 58
        $namespace = $namespace ?: $schema->getTargetNamespace();
881
882
        try {
883
            /**
884
             * @var AttributeItem $out
885
             */
886 58
            $out = $schema->findAttribute((string) $name, $namespace);
887
888 58
            return $out;
889
        } catch (TypeNotFoundException $e) {
890
            throw new TypeException(
891
                sprintf(
892
                    "Can't find %s named {%s}#%s, at line %d in %s ",
893
                    'attribute',
894
                    $namespace,
895
                    $name,
896
                    $node->getLineNo(),
897
                    $node->ownerDocument->documentURI
898
                ),
899
                0,
900
                $e
901
            );
902
        }
903
    }
904
905 58
    private function findAttributeGroup(Schema $schema, DOMElement $node, string $typeName): AttributeGroup
906
    {
907 58
        list($name, $namespace) = static::splitParts($node, $typeName);
908
909
        /**
910
         * @var string|null $namespace
911
         */
912 58
        $namespace = $namespace ?: $schema->getTargetNamespace();
913
914
        try {
915
            /**
916
             * @var AttributeGroup $out
917
             */
918 58
            $out = $schema->findAttributeGroup((string) $name, $namespace);
919
920 58
            return $out;
921
        } catch (TypeNotFoundException $e) {
922
            throw new TypeException(
923
                sprintf(
924
                    "Can't find %s named {%s}#%s, at line %d in %s ",
925
                    'attributegroup',
926
                    $namespace,
927
                    $name,
928
                    $node->getLineNo(),
929
                    $node->ownerDocument->documentURI
930
                ),
931
                0,
932
                $e
933
            );
934
        }
935
    }
936
937 58
    private function findElement(Schema $schema, DOMElement $node, string $typeName): ElementDef
938
    {
939 58
        list($name, $namespace) = static::splitParts($node, $typeName);
940
941
        /**
942
         * @var string|null $namespace
943
         */
944 58
        $namespace = $namespace ?: $schema->getTargetNamespace();
945
946
        try {
947 58
            return $schema->findElement((string) $name, $namespace);
948
        } catch (TypeNotFoundException $e) {
949
            throw new TypeException(
950
                sprintf(
951
                    "Can't find %s named {%s}#%s, at line %d in %s ",
952
                    'element',
953
                    $namespace,
954
                    $name,
955
                    $node->getLineNo(),
956
                    $node->ownerDocument->documentURI
957
                ),
958
                0,
959
                $e
960
            );
961
        }
962
    }
963
964 58
    private function findGroup(Schema $schema, DOMElement $node, string $typeName): Group
965
    {
966 58
        list($name, $namespace) = static::splitParts($node, $typeName);
967
968
        /**
969
         * @var string|null $namespace
970
         */
971 58
        $namespace = $namespace ?: $schema->getTargetNamespace();
972
973
        try {
974
            /**
975
             * @var Group $out
976
             */
977 58
            $out = $schema->findGroup((string) $name, $namespace);
978
979 58
            return $out;
980
        } catch (TypeNotFoundException $e) {
981
            throw new TypeException(
982
                sprintf(
983
                    "Can't find %s named {%s}#%s, at line %d in %s ",
984
                    'group',
985
                    $namespace,
986
                    $name,
987
                    $node->getLineNo(),
988
                    $node->ownerDocument->documentURI
989
                ),
990
                0,
991
                $e
992
            );
993
        }
994
    }
995
996 58
    private function findType(Schema $schema, DOMElement $node, string $typeName): SchemaItem
997
    {
998 58
        list($name, $namespace) = static::splitParts($node, $typeName);
999
1000
        /**
1001
         * @var string|null $namespace
1002
         */
1003 58
        $namespace = $namespace ?: $schema->getTargetNamespace();
1004
1005
        try {
1006
            /**
1007
             * @var SchemaItem $out
1008
             */
1009 58
            $out = $schema->findType((string) $name, $namespace);
1010
1011 58
            return $out;
1012
        } catch (TypeNotFoundException $e) {
1013
            throw new TypeException(
1014
                sprintf(
1015
                    "Can't find %s named {%s}#%s, at line %d in %s ",
1016
                    'type',
1017
                    $namespace,
1018
                    $name,
1019
                    $node->getLineNo(),
1020
                    $node->ownerDocument->documentURI
1021
                ),
1022
                0,
1023
                $e
1024
            );
1025
        }
1026
    }
1027
1028
    /**
1029
     * @return Closure
1030
     */
1031 58
    private function loadElementDef(Schema $schema, DOMElement $node): Closure
1032
    {
1033 58
        return $this->loadAttributeOrElementDef($schema, $node, false);
1034
    }
1035
1036 58
    private function fillItem(Item $element, DOMElement $node): void
1037
    {
1038
        /**
1039
         * @var bool
1040
         */
1041 58
        $skip = false;
1042 58
        self::againstDOMNodeList(
1043 58
            $node,
1044 58
            function (
1045
                DOMElement $node,
1046
                DOMElement $childNode
1047
            ) use (
1048 58
                $element,
1049 58
                &$skip
1050
            ): void {
1051
                if (
1052 58
                    !$skip &&
1053 58
                    in_array(
1054 58
                        $childNode->localName,
1055
                        [
1056 58
                            'complexType',
1057
                            'simpleType',
1058
                        ],
1059 58
                        true
1060
                    )
1061
                ) {
1062 58
                    $this->loadTypeWithCallback(
1063 58
                        $element->getSchema(),
1064 58
                        $childNode,
1065 58
                        function (Type $type) use ($element): void {
1066 58
                            $element->setType($type);
1067 58
                        }
1068
                    );
1069 58
                    $skip = true;
1070
                }
1071 58
            }
1072
        );
1073 58
        if ($skip) {
1074 58
            return;
1075
        }
1076 58
        $this->fillItemNonLocalType($element, $node);
1077 58
    }
1078
1079 58
    private function fillItemNonLocalType(Item $element, DOMElement $node): void
1080
    {
1081 58
        if ($node->getAttribute('type')) {
1082
            /**
1083
             * @var Type
1084
             */
1085 58
            $type = $this->findSomeType($element, $node, 'type');
1086
        } else {
1087
            /**
1088
             * @var Type
1089
             */
1090 58
            $type = $this->findSomeTypeFromAttribute(
1091 58
                $element,
1092 58
                $node,
1093 58
                ($node->lookupPrefix(self::XSD_NS).':anyType')
1094
            );
1095
        }
1096
1097 58
        $element->setType($type);
1098 58
    }
1099
1100 58
    private function loadImport(
1101
        Schema $schema,
1102
        DOMElement $node
1103
    ): Closure {
1104 58
        $namespace = $node->getAttribute('namespace');
1105 58
        $schemaLocation = $node->getAttribute('schemaLocation');
1106
1107
        // postpone schema loading
1108 58
        if ($namespace && !$schemaLocation && !isset(self::$globalSchemaInfo[$namespace])) {
1109 2
            return function () use ($schema, $namespace): void {
1110 2
                if (!empty($this->loadedSchemas[$namespace])) {
1111 2
                    foreach ($this->loadedSchemas[$namespace] as $s) {
1112 2
                        $schema->addSchema($s, $namespace);
1113
                    }
1114
                }
1115 2
            };
1116 58
        } elseif ($namespace && !$schemaLocation && !empty($this->loadedSchemas[$namespace])) {
1117 1
            foreach ($this->loadedSchemas[$namespace] as $s) {
1118 1
                $schema->addSchema($s, $namespace);
1119
            }
1120
        }
1121
1122 58
        $base = urldecode($node->ownerDocument->documentURI);
1123 58
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
1124
1125 58
        if (isset($this->loadedFiles[$file])) {
1126 58
            $schema->addSchema($this->loadedFiles[$file]);
1127
1128 58
            return function (): void {
1129 58
            };
1130
        }
1131
1132 2
        return $this->loadImportFresh($namespace, $schema, $file);
1133
    }
1134
1135 2
    private function createOrUseSchemaForNs(
1136
        Schema $schema,
1137
        string $namespace
1138
    ): Schema {
1139 2
        if (('' !== trim($namespace))) {
1140 1
            $newSchema = new Schema();
1141 1
            $newSchema->addSchema($this->getGlobalSchema());
1142 1
            $schema->addSchema($newSchema);
1143
        } else {
1144 1
            $newSchema = $schema;
1145
        }
1146
1147 2
        return $newSchema;
1148
    }
1149
1150
    private function loadImportFresh(
1151
        string $namespace,
1152
        Schema $schema,
1153
        string $file
1154
    ): Closure {
1155 2
        return function () use ($namespace, $schema, $file): void {
1156 2
            $dom = $this->getDOM(
1157 2
                isset($this->knownLocationSchemas[$file])
1158 1
                    ? $this->knownLocationSchemas[$file]
1159 2
                    : $file
1160
            );
1161
1162 2
            $schemaNew = $this->createOrUseSchemaForNs($schema, $namespace);
1163
1164 2
            $this->setLoadedFile($file, $schemaNew);
1165
1166 2
            $callbacks = $this->schemaNode($schemaNew, $dom->documentElement, $schema);
1167
1168 2
            foreach ($callbacks as $callback) {
1169 2
                $callback();
1170
            }
1171 2
        };
1172
    }
1173
1174
    /**
1175
     * @var Schema|null
1176
     */
1177
    protected $globalSchema;
1178
1179
    /**
1180
     * @return Schema
1181
     */
1182 58
    public function getGlobalSchema(): Schema
1183
    {
1184 58
        if (!($this->globalSchema instanceof Schema)) {
1185 58
            $callbacks = array();
1186 58
            $globalSchemas = array();
1187
            /**
1188
             * @var string $namespace
1189
             */
1190 58
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
1191 58
                $this->setLoadedFile(
1192 58
                    $uri,
1193 58
                    $globalSchemas[$namespace] = $schema = new Schema()
1194
                );
1195 58
                $this->setLoadedSchema($namespace, $schema);
1196 58
                if ($namespace === self::XSD_NS) {
1197 58
                    $this->globalSchema = $schema;
1198
                }
1199 58
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1200 58
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1201
            }
1202
1203 58
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anySimpleType'));
1204 58
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anyType'));
1205
1206 58
            $globalSchemas[(string) static::XML_NS]->addSchema(
1207 58
                $globalSchemas[(string) static::XSD_NS],
1208 58
                (string) static::XSD_NS
1209
            );
1210 58
            $globalSchemas[(string) static::XSD_NS]->addSchema(
1211 58
                $globalSchemas[(string) static::XML_NS],
1212 58
                (string) static::XML_NS
1213
            );
1214
1215
            /**
1216
             * @var Closure
1217
             */
1218 58
            foreach ($callbacks as $callback) {
1219 58
                $callback();
1220
            }
1221
        }
1222
1223
        /**
1224
         * @var Schema
1225
         */
1226 58
        $out = $this->globalSchema;
1227
1228 58
        if (!($out instanceof Schema)) {
1229
            throw new TypeException('Globa schema not discoverd');
1230
        }
1231
1232 58
        return $out;
1233
    }
1234
1235
    /**
1236
     * @param DOMElement[] $nodes
1237
     */
1238 1
    public function readNodes(array $nodes, string $file = null): Schema
1239
    {
1240 1
        $rootSchema = new Schema();
1241 1
        $rootSchema->addSchema($this->getGlobalSchema());
1242
1243 1
        if ($file !== null) {
1244 1
            $this->setLoadedFile($file, $rootSchema);
1245
        }
1246
1247 1
        $all = array();
1248 1
        foreach ($nodes as $k => $node) {
1249 1
            if (($node instanceof \DOMElement) && $node->namespaceURI === self::XSD_NS && $node->localName == 'schema') {
1250 1
                $holderSchema = new Schema();
1251 1
                $holderSchema->addSchema($this->getGlobalSchema());
1252
1253 1
                $this->setLoadedSchemaFromElement($node, $holderSchema);
1254
1255 1
                $rootSchema->addSchema($holderSchema);
1256
1257 1
                $callbacks = $this->schemaNode($holderSchema, $node);
1258 1
                $all = array_merge($callbacks, $all);
1259
            }
1260
        }
1261
1262 1
        foreach ($all as $callback) {
1263 1
            call_user_func($callback);
1264
        }
1265
1266 1
        return $rootSchema;
1267
    }
1268
1269 57
    public function readNode(DOMElement $node, string $file = null): Schema
1270
    {
1271 57
        $rootSchema = new Schema();
1272 57
        $rootSchema->addSchema($this->getGlobalSchema());
1273
1274 57
        if ($file !== null) {
1275 56
            $this->setLoadedFile($file, $rootSchema);
1276
        }
1277
1278 57
        $this->setLoadedSchemaFromElement($node, $rootSchema);
1279
1280 57
        $callbacks = $this->schemaNode($rootSchema, $node);
1281
1282 57
        foreach ($callbacks as $callback) {
1283 51
            call_user_func($callback);
1284
        }
1285
1286 57
        return $rootSchema;
1287
    }
1288
1289
    /**
1290
     * @throws IOException
1291
     */
1292 56
    public function readString(string $content, string $file = 'schema.xsd'): Schema
1293
    {
1294 56
        $xml = new DOMDocument('1.0', 'UTF-8');
1295 56
        libxml_use_internal_errors(true);
1296 56
        if (!@$xml->loadXML($content)) {
1297 1
            throw new IOException("Can't load the schema", 0, $this->extractErrorMessage());
1298
        }
1299 55
        libxml_use_internal_errors(false);
1300 55
        $xml->documentURI = $file;
1301
1302 55
        return $this->readNode($xml->documentElement, $file);
1303
    }
1304
1305
    /**
1306
     * @throws IOException
1307
     */
1308 2
    public function readFile(string $file): Schema
1309
    {
1310 2
        $xml = $this->getDOM($file);
1311
1312 1
        return $this->readNode($xml->documentElement, $file);
1313
    }
1314
1315
    /**
1316
     * @throws IOException
1317
     */
1318 59
    private function getDOM(string $file): DOMDocument
1319
    {
1320 59
        $xml = new DOMDocument('1.0', 'UTF-8');
1321 59
        libxml_use_internal_errors(true);
1322 59
        if (!@$xml->load($file)) {
1323 1
            libxml_use_internal_errors(false);
1324 1
            throw new IOException("Can't load the file '$file'", 0, $this->extractErrorMessage());
1325
        }
1326 58
        libxml_use_internal_errors(false);
1327
1328 58
        return $xml;
1329
    }
1330
1331 58
    private static function againstDOMNodeList(
1332
        DOMElement $node,
1333
        Closure $againstNodeList
1334
    ): void {
1335 58
        $limit = $node->childNodes->length;
1336 58
        for ($i = 0; $i < $limit; $i += 1) {
1337
            /**
1338
             * @var DOMNode
1339
             */
1340 58
            $childNode = $node->childNodes->item($i);
1341
1342 58
            if ($childNode instanceof DOMElement) {
1343 58
                $againstNodeList(
1344 58
                    $node,
1345 58
                    $childNode
1346
                );
1347
            }
1348
        }
1349 58
    }
1350
1351 58
    private function loadTypeWithCallback(
1352
        Schema $schema,
1353
        DOMElement $childNode,
1354
        Closure $callback
1355
    ): void {
1356
        /**
1357
         * @var Closure|null $func
1358
         */
1359 58
        $func = null;
1360
1361 58
        switch ($childNode->localName) {
1362 58
            case 'complexType':
1363 58
                $func = $this->loadComplexType($schema, $childNode, $callback);
1364 58
                break;
1365 58
            case 'simpleType':
1366 58
                $func = $this->loadSimpleType($schema, $childNode, $callback);
1367 58
                break;
1368
        }
1369
1370 58
        if ($func instanceof Closure) {
1371 58
            call_user_func($func);
1372
        }
1373 58
    }
1374
1375 58
    private function loadElement(
1376
        Schema $schema,
1377
        DOMElement $node
1378
    ): Element {
1379 58
        $element = new Element($schema, $node->getAttribute('name'));
1380 58
        $element->setDoc($this->getDocumentation($node));
1381
1382 58
        $this->fillItem($element, $node);
1383
1384 58
        self::maybeSetMax($element, $node);
1385 58
        self::maybeSetMin($element, $node);
1386
1387 58
        $xp = new \DOMXPath($node->ownerDocument);
1388 58
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1389
1390 58
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1391 58
            $element->setMin(0);
1392
        }
1393
1394 58
        if ($node->hasAttribute('nillable')) {
1395 3
            $element->setNil($node->getAttribute('nillable') == 'true');
1396
        }
1397 58
        if ($node->hasAttribute('form')) {
1398 3
            $element->setQualified($node->getAttribute('form') == 'qualified');
1399
        }
1400
1401 58
        return $element;
1402
    }
1403
1404 58
    private function addAttributeFromAttributeOrRef(
1405
        BaseComplexType $type,
1406
        DOMElement $childNode,
1407
        Schema $schema,
1408
        DOMElement $node
1409
    ): void {
1410 58
        $attribute = $this->getAttributeFromAttributeOrRef(
1411 58
            $childNode,
1412 58
            $schema,
1413 58
            $node
1414
        );
1415
1416 58
        $type->addAttribute($attribute);
1417 58
    }
1418
1419 58
    private function findSomethingLikeAttributeGroup(
1420
        Schema $schema,
1421
        DOMElement $node,
1422
        DOMElement $childNode,
1423
        AttributeContainer $addToThis
1424
    ): void {
1425 58
        $attribute = $this->findAttributeGroup($schema, $node, $childNode->getAttribute('ref'));
1426 58
        $addToThis->addAttribute($attribute);
1427 58
    }
1428
1429 58
    private function setLoadedFile(string $key, Schema $schema): void
1430
    {
1431 58
        $this->loadedFiles[$key] = $schema;
1432 58
    }
1433
1434 58
    private function setLoadedSchemaFromElement(DOMElement $node, Schema $schema): void
1435
    {
1436 58
        if ($node->hasAttribute('targetNamespace')) {
1437 58
            $this->setLoadedSchema($node->getAttribute('targetNamespace'), $schema);
1438
        }
1439 58
    }
1440
1441 58
    private function setLoadedSchema(string $namespace, Schema $schema): void
1442
    {
1443 58
        if (!isset($this->loadedSchemas[$namespace])) {
1444 58
            $this->loadedSchemas[$namespace] = array();
1445
        }
1446 58
        if (!in_array($schema, $this->loadedSchemas[$namespace], true)) {
1447 58
            $this->loadedSchemas[$namespace][] = $schema;
1448
        }
1449 58
    }
1450
1451 58
    private function setSchemaThingsFromNode(
1452
        Schema $schema,
1453
        DOMElement $node,
1454
        Schema $parent = null
1455
    ): void {
1456 58
        $schema->setDoc($this->getDocumentation($node));
1457
1458 58
        if ($node->hasAttribute('targetNamespace')) {
1459 58
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1460
        } elseif ($parent instanceof Schema) {
1461
            $schema->setTargetNamespace($parent->getTargetNamespace());
1462
        }
1463 58
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1464 58
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1465 58
        $schema->setDoc($this->getDocumentation($node));
1466 58
    }
1467
}
1468