1 package org.opentrafficsim.editor;
2
3 import java.io.IOException;
4 import java.net.URI;
5 import java.net.URISyntaxException;
6 import java.util.ArrayList;
7 import java.util.Iterator;
8 import java.util.LinkedHashMap;
9 import java.util.LinkedHashSet;
10 import java.util.LinkedList;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Optional;
15 import java.util.Queue;
16 import java.util.Set;
17
18 import javax.xml.parsers.ParserConfigurationException;
19
20 import org.djutils.exceptions.Throw;
21 import org.opentrafficsim.base.OtsRuntimeException;
22 import org.opentrafficsim.base.logger.Logger;
23 import org.w3c.dom.Document;
24 import org.w3c.dom.Node;
25 import org.xml.sax.SAXException;
26
27
28
29
30
31
32
33
34
35
36
37
38
39 public class Schema
40 {
41
42
43 private Node root;
44
45
46 private final Set<String> readFiles = new LinkedHashSet<>();
47
48
49 private final Map<String, Node> types = new LinkedHashMap<>();
50
51
52 private final Map<String, Set<String>> extendedTypes = new LinkedHashMap<>();
53
54
55
56
57
58 private final Map<String, Set<String>> referredTypes = new LinkedHashMap<>();
59
60
61 private final Map<String, Node> elements = new LinkedHashMap<>();
62
63
64 private final Map<String, Set<String>> referredElements = new LinkedHashMap<>();
65
66
67 private final Map<String, String> documentation = new LinkedHashMap<>();
68
69
70 private final Map<String, Node> keys = new LinkedHashMap<>();
71
72
73 private final Map<String, Node> keyrefs = new LinkedHashMap<>();
74
75
76 private final Map<String, Node> uniques = new LinkedHashMap<>();
77
78
79 private final Queue<RecursionElement> queue = new LinkedList<>();
80
81
82 private boolean blockLoop = false;
83
84
85
86
87
88 public Schema(final Document document)
89 {
90 this.readFiles.add(document.getDocumentURI());
91
92 queue("", document, true);
93 while (!this.queue.isEmpty())
94 {
95 RecursionElement next = this.queue.poll();
96 read(next.path(), next.node(), next.extendPath());
97 }
98
99
100 for (Entry<String, Node> entry : this.elements.entrySet())
101 {
102 Optional<String> referredTypeName = DocumentReader.getAttribute(entry.getValue(), "type");
103 if (referredTypeName.isPresent() && !referredTypeName.get().startsWith("xsd:"))
104 {
105 Node referredType = getType(referredTypeName.get()).get();
106 entry.setValue(referredType);
107 }
108 }
109
110
111 Set<String> allTypes = new LinkedHashSet<>(this.types.keySet());
112 for (String str : this.extendedTypes.keySet())
113 {
114 allTypes.removeIf((val) -> val.startsWith(str));
115 }
116 for (String str : this.referredTypes.keySet())
117 {
118 allTypes.removeIf((val) -> val.startsWith(str));
119 }
120 if (!allTypes.isEmpty())
121 {
122 Logger.ots().trace("{} types are defined but never extended or referred to.", allTypes.size());
123
124 }
125
126 Set<String> allElements = new LinkedHashSet<>(this.elements.keySet());
127 for (String str : this.referredElements.keySet())
128 {
129 allElements.removeIf((val) -> val.startsWith(str));
130 }
131 for (String str : this.types.keySet())
132 {
133 allElements.removeIf((val) -> val.startsWith(str));
134 }
135 allElements.removeIf((path) -> path.startsWith("Ots"));
136 if (!allElements.isEmpty())
137 {
138 Logger.ots().trace("{} elements are defined but never referred to, nor are they a type.", allElements.size());
139
140 }
141
142 checkKeys();
143 checkKeyrefs();
144 checkUniques();
145
146
147
148
149
150 Logger.ots().trace("Root found as '{}'.", DocumentReader.getAttribute(this.getRoot(), "name").get());
151 Logger.ots().trace("Read {} files.", this.readFiles.size());
152 Logger.ots().trace("Read {} elements.", this.elements.size());
153 Logger.ots().trace("Read {} types.", this.types.size());
154 Logger.ots().trace("Read {} extended types.", this.extendedTypes.size());
155 Logger.ots().trace("Read {} documentations.", this.documentation.size());
156 Logger.ots().trace("Read {} keys.", this.keys.size());
157 Logger.ots().trace("Read {} keyrefs.", this.keyrefs.size());
158 Logger.ots().trace("Read {} uniques.", this.uniques.size());
159 for (String type : this.extendedTypes.keySet())
160 {
161 if (!this.types.containsKey(type) && !type.startsWith("xsd:"))
162 {
163 Logger.ots().trace("Type '{}' is extended but was not found.", type);
164 }
165 }
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 private void read(final String path, final Node node, final boolean extendPath)
194 {
195 if (recursion(path))
196 {
197 Logger.ots().trace("Recursion found at {}, further expansion is halted.", path);
198 return;
199 }
200
201 if (node.getNodeName().equals("xsd:extension"))
202 {
203 String base = DocumentReader.getAttribute(node, "base").get().replace("ots:", "");
204 if (!base.startsWith("xsd:"))
205 {
206 Optional<Node> baseNode = getType(base);
207 if (baseNode.isEmpty())
208 {
209 if (this.blockLoop)
210 {
211 return;
212 }
213 this.blockLoop = true;
214
215 queue(path, node, false);
216 }
217 else
218 {
219 queue(path, baseNode.get(), false);
220 }
221 }
222 if (this.extendedTypes.containsKey(base) && this.extendedTypes.get(base).contains(path))
223 {
224
225
226 return;
227 }
228 this.extendedTypes.computeIfAbsent(base, (key) -> new LinkedHashSet<String>()).add(path);
229 }
230 this.blockLoop = false;
231
232 String nextPath = path;
233 Node nextNode = node;
234 if (node.getNodeName().equals("xsd:element") && node.hasAttributes())
235 {
236
237 Optional<String> name = DocumentReader.getAttribute(node, "name");
238 if (name.isPresent())
239 {
240 if (name.get().equals("Ots"))
241 {
242 this.root = node;
243 }
244 nextPath = extendPath ? (nextPath.isEmpty() ? name.get() : nextPath + "." + name) : nextPath;
245 this.elements.put(nextPath, node);
246 }
247 String ref = DocumentReader.getAttribute(node, "ref").orElse(null);
248 if (ref != null)
249 {
250 ref = ref.replace("ots:", "");
251 nextNode = getElement(ref).get();
252
253
254
255
256
257 if (DocumentReader.getAttribute(nextNode, "type").isPresent())
258 {
259 element(path, nextNode);
260 }
261 nextPath = extendPath ? (nextPath.isEmpty() ? ref : nextPath + "." + ref) : nextPath;
262 this.elements.put(nextPath, nextNode);
263 Throw.whenNull(nextNode, "Element %s refers to a type that was not loaded in first pass.", nextPath);
264 }
265 }
266
267 Optional<String> name = DocumentReader.getAttribute(nextNode, "name");
268
269 String nodeName = nextNode.getNodeName();
270 if (name.isPresent() && (nodeName.equals("xsd:complexType") || nodeName.equals("xsd:simpleType")))
271 {
272 this.types.put(name.get(), nextNode);
273 nextPath = extendPath ? (nextPath.isEmpty() ? name.get() : nextPath + "." + name.get()) : nextPath;
274 }
275
276 if (!nextNode.hasChildNodes())
277 {
278 return;
279 }
280 for (int childIndex = 0; childIndex < nextNode.getChildNodes().getLength(); childIndex++)
281 {
282 Node child = nextNode.getChildNodes().item(childIndex);
283 switch (child.getNodeName())
284 {
285 case "#text":
286 break;
287 case "xsd:include":
288 include(nextPath, child);
289 break;
290 case "xsd:element":
291 element(nextPath, child);
292 break;
293 case "xsd:attribute":
294 attribute(nextPath, child);
295 break;
296 case "xsd:documentation":
297 documentation(nextPath, child);
298 break;
299 case "xsd:union":
300 union(nextPath, child);
301 break;
302 case "xsd:key":
303 if (nextPath.startsWith("Ots"))
304 {
305 this.keys.put(nextPath + "." + DocumentReader.getAttribute(child, "name").get(), child);
306 }
307 break;
308 case "xsd:keyref":
309 if (nextPath.startsWith("Ots"))
310 {
311 this.keyrefs.put(nextPath + "." + DocumentReader.getAttribute(child, "name").get(), child);
312 }
313 break;
314 case "xsd:unique":
315 if (nextPath.startsWith("Ots"))
316 {
317 this.uniques.put(nextPath + "." + DocumentReader.getAttribute(child, "name").get(), child);
318 }
319 break;
320 default:
321 read(nextPath, child, true);
322 }
323 }
324 }
325
326
327
328
329
330
331
332 private boolean recursion(final String path)
333 {
334 StringBuffer sub = (new StringBuffer(path)).reverse();
335 int fromIndex = 0;
336 while (fromIndex < sub.length())
337 {
338 int dot = sub.indexOf(".", fromIndex);
339 if (dot < 0)
340 {
341 return false;
342 }
343 int toIndex = 2 * (dot + 1);
344 if (toIndex > sub.length())
345 {
346 return false;
347 }
348 if (sub.substring(0, dot + 1).equals(sub.substring(dot + 1, toIndex)))
349 {
350 return true;
351 }
352 fromIndex = dot + 1;
353 }
354 return false;
355 }
356
357
358
359
360
361
362
363
364
365 private void queue(final String path, final Node node, final boolean extendPath)
366 {
367 this.queue.add(new RecursionElement(path, node, extendPath));
368 }
369
370
371
372
373
374
375 private void include(final String path, final Node node)
376 {
377 String schemaLocation = DocumentReader.getAttribute(node, "schemaLocation").get();
378 String schemaPath = folder(node) + schemaLocation;
379 if (!this.readFiles.add(schemaPath))
380 {
381 return;
382 }
383 try
384 {
385 read(path, DocumentReader.open(new URI(schemaPath)), true);
386 }
387 catch (SAXException | IOException | ParserConfigurationException | URISyntaxException e)
388 {
389 throw new OtsRuntimeException("Unable to find resource " + folder(node) + schemaLocation);
390 }
391 }
392
393
394
395
396
397
398 private String folder(final Node node)
399 {
400 String uri = node.getBaseURI();
401 if (uri == null)
402 {
403 return "";
404 }
405 int a = uri.lastIndexOf("\\");
406 int b = uri.lastIndexOf("/");
407 return uri.substring(0, (a > b ? a : b) + 1);
408 }
409
410
411
412
413
414
415 private void element(final String path, final Node node)
416 {
417 if (DocumentReader.getAttribute(node, "ref").isPresent())
418 {
419 ref(path, node);
420 return;
421 }
422 String type = DocumentReader.getAttribute(node, "type").orElse(null);
423 if (type != null && !type.startsWith("xsd:"))
424 {
425 type = type.replace("ots:", "");
426 this.referredTypes.computeIfAbsent(type, (key) -> new LinkedHashSet<>())
427 .add(path.isEmpty() ? DocumentReader.getAttribute(node, "name").get()
428 : path + "." + DocumentReader.getAttribute(node, "name").get());
429 Optional<Node> referred = getType(type);
430 if (referred.isEmpty())
431 {
432 queue(path, node, true);
433 return;
434 }
435 else
436 {
437 queue(path + "." + DocumentReader.getAttribute(node, "name").get(), referred.get(), false);
438 }
439 }
440 read(path, node, true);
441 }
442
443
444
445
446
447
448
449
450 private void ref(final String path, final Node node)
451 {
452 String ref = DocumentReader.getAttribute(node, "ref").get().replace("ots:", "");
453 if (ref.equals("xi:include"))
454 {
455 return;
456 }
457 this.referredElements.computeIfAbsent(ref, (key) -> new LinkedHashSet<>()).add(path);
458 Optional<Node> refNode = getElement(ref);
459 if (refNode.isEmpty())
460 {
461 queue(path, node, true);
462 }
463 else
464 {
465 read(path, node, true);
466 }
467 }
468
469
470
471
472
473
474 private void attribute(final String path, final Node node)
475 {
476 String type = DocumentReader.getAttribute(node, "type").orElse(null);
477 if (type != null)
478 {
479 type = DocumentReader.getAttribute(node, "type").get().replace("ots:", "");
480 String name = DocumentReader.getAttribute(node, "name").get();
481 this.referredTypes.computeIfAbsent(type, (key) -> new LinkedHashSet<>()).add(path + "." + name);
482 }
483 read(path, node, true);
484 }
485
486
487
488
489
490
491 private void documentation(final String path, final Node node)
492 {
493 Optional<Node> doc = DocumentReader.getChild(node, "#text");
494 if (doc.isPresent())
495 {
496 this.documentation.put(path, doc.get().getNodeValue().trim().replaceAll("\r\n", " ").replaceAll("\n", " ")
497 .replaceAll("\r", " ").replace(" ", ""));
498 }
499 }
500
501
502
503
504
505
506 private void union(final String path, final Node node)
507 {
508
509
510
511
512 for (String type : DocumentReader.getAttribute(node, "memberTypes").get().split(" "))
513 {
514 this.referredTypes.computeIfAbsent(type.replace("ots:", ""), (key) -> new LinkedHashSet<>()).add(path);
515 }
516 read(path, node, true);
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530
531 private record RecursionElement(String path, Node node, boolean extendPath)
532 {
533 }
534
535
536
537
538
539 private void checkKeys()
540 {
541 checkKeyOrUniques("Key", this.keys);
542 }
543
544
545
546
547
548 private void checkUniques()
549 {
550 checkKeyOrUniques("Unique", this.uniques);
551 }
552
553
554
555
556
557
558
559 private void checkKeyOrUniques(final String label, final Map<String, Node> map)
560 {
561 for (String fullPath : map.keySet())
562 {
563 Node node = map.get(fullPath);
564 String context = fullPath.substring(0, fullPath.lastIndexOf("."));
565 String element = DocumentReader.getAttribute(node, "name").get();
566 for (String selector : getXpath(node).split("\\|"))
567 {
568 String path;
569 Node selected = null;
570 if (!selector.startsWith(".//"))
571 {
572 path = context + "." + selector.replace("/", ".");
573 selected = getElement(path).orElse(null);
574 }
575 else
576 {
577
578 path = context + selector.replace(".//", ".{...}").replace("/", ".");
579 for (Entry<String, Node> entry : this.elements.entrySet())
580 {
581 String elementPath = entry.getKey();
582 if (elementPath.startsWith(context) && elementPath.endsWith(selector.substring(3).replace("/", ".")))
583 {
584 selected = entry.getValue();
585 break;
586 }
587 }
588 }
589 if (selected == null)
590 {
591 Logger.ots().trace("{} {} ({}) not found among elements.", label, element, path);
592 }
593 else
594 {
595 for (Node field : DocumentReader.getChildren(node, "xsd:field"))
596 {
597 String xpathFieldString = DocumentReader.getAttribute(field, "xpath").get();
598 boolean found = false;
599 for (String xpathField : xpathFieldString.split("\\|"))
600 {
601 if (xpathField.startsWith("@"))
602 {
603 xpathField = xpathField.substring(1);
604 if (hasElementAttribute(selected, xpathField))
605 {
606 found = true;
607 }
608 }
609 else
610 {
611 Logger.ots().trace("Field {} in {} {} not checked.", xpathField, label.toLowerCase(), element);
612 }
613 }
614 if (!found)
615 {
616 Logger.ots().trace("{} {} ({}) points to non existing field {}.", label, element, path,
617 xpathFieldString);
618 }
619 }
620 }
621 }
622 }
623 }
624
625
626
627
628
629 private void checkKeyrefs()
630 {
631 for (String fullPath : this.keyrefs.keySet())
632 {
633 Node node = this.keyrefs.get(fullPath);
634 String keyref = DocumentReader.getAttribute(node, "name").get();
635 String keyName = DocumentReader.getAttribute(node, "refer").get().replace("ots:", "");
636 Node key = null;
637 boolean keyFound = false;
638 Iterator<Node> iterator = this.keys.values().iterator();
639 while (!keyFound && iterator.hasNext())
640 {
641 key = iterator.next();
642 keyFound = keyName.equals(DocumentReader.getAttribute(key, "name").orElse(null));
643 }
644 if (!keyFound)
645 {
646 Logger.ots().trace("Keyref {} refers to non existing key {}.", keyref,
647 DocumentReader.getAttribute(node, "refer").orElse(null));
648 }
649 String context = fullPath.substring(0, fullPath.lastIndexOf("."));
650 List<Node> elementList = getSelectedElements(context, node);
651 if (elementList.isEmpty())
652 {
653 Logger.ots().trace("Keyref {} ({}) not found among elements.", keyref, getXpath(node));
654 }
655 else
656 {
657 for (int i = 0; i < elementList.size(); i++)
658 {
659 Node selected = elementList.get(i);
660 for (Node field : DocumentReader.getChildren(node, "xsd:field"))
661 {
662 String xpathFieldString = DocumentReader.getAttribute(field, "xpath").get();
663 String[] xpathFieldValues = xpathFieldString.split("\\|");
664 String xpathField = xpathFieldValues.length == 1 ? xpathFieldValues[0] : xpathFieldValues[i];
665 if (!followXPath(selected, xpathField))
666 {
667 Logger.ots().trace("Keyref {} ({}) points to non existing field {}.", keyref,
668 getXpath(node).split("\\|")[i], xpathField);
669 }
670 }
671 }
672 }
673 }
674 }
675
676
677
678
679
680
681
682 private boolean followXPath(final Node selected, final String xpath)
683 {
684 if (xpath.startsWith("@"))
685 {
686 String xpathField = xpath.substring(1);
687 return hasElementAttribute(selected, xpathField);
688 }
689 String selectedName = selected.getNodeName();
690 if (xpath.equals(".") && (selectedName.equals("xsd:simpleType") || selectedName.equals("xsd:element")))
691 {
692 return true;
693 }
694 int index = xpath.indexOf("/");
695 String name = index < 0 ? xpath : xpath.substring(0, index);
696 String remainder = index < 0 ? null : xpath.substring(index);
697 name = name.replace("ots:", "");
698 boolean found = false;
699 if (name.equals(DocumentReader.getAttribute(selected, "name").orElse(null)))
700 {
701 found = true;
702 }
703 else if (selectedName.equals("xsd:complexType") || selectedName.equals("xsd:sequence")
704 || selectedName.equals("xsd:choice") || selectedName.equals("xsd:all") || selectedName.equals("xsd:element"))
705 {
706 for (int i = 0; i < selected.getChildNodes().getLength() && !found; i++)
707 {
708 Node child = selected.getChildNodes().item(i);
709 found = followXPath(child, name);
710 }
711 }
712 if (found && remainder != null)
713 {
714 for (int i = 0; i < selected.getChildNodes().getLength() && !found; i++)
715 {
716 Node child = selected.getChildNodes().item(i);
717 if (selectedName.equals("xsd:simpleType") || selectedName.equals("xsd:complexType")
718 || selectedName.equals("xsd:element"))
719 {
720 found = followXPath(child, remainder);
721 }
722 }
723 }
724 return found;
725 }
726
727
728
729
730
731
732
733
734 private List<Node> getSelectedElements(final String context, final Node node)
735 {
736 List<Node> nodes = new ArrayList<>();
737 for (String selector : getXpath(node).split("\\|"))
738 {
739 Node selected = null;
740 if (!selector.startsWith(".//"))
741 {
742 selected = getElement(context + "." + selector.replace("/", ".")).orElse(null);
743 }
744 else
745 {
746
747 for (Entry<String, Node> entry : this.elements.entrySet())
748 {
749 String elementPath = entry.getKey();
750 if (elementPath.startsWith(context) && elementPath.endsWith(selector.replace(".//", "").replace("/", ".")))
751 {
752 selected = entry.getValue();
753 break;
754 }
755 }
756 }
757 if (selected != null)
758 {
759 nodes.add(selected);
760 }
761 else
762 {
763 for (Entry<String, Node> entry : this.elements.entrySet())
764 {
765 if (!entry.getKey().startsWith("Ots.")
766 && entry.getKey().endsWith(selector.replace(".//", "").replace("/", ".")))
767 {
768 nodes.add(entry.getValue());
769 break;
770 }
771 else if (isType(entry.getValue(), selector.replace(".//", "").replace("/", ".")))
772 {
773 nodes.add(entry.getValue());
774 break;
775 }
776 }
777 }
778 }
779 return nodes;
780 }
781
782
783
784
785
786
787 private String getXpath(final Node node)
788 {
789 Node child = DocumentReader.getChild(node, "xsd:selector").get();
790 String xpath = DocumentReader.getAttribute(child, "xpath").get();
791 xpath = xpath.replace("ots:", "");
792 return xpath;
793 }
794
795
796
797
798
799
800
801
802
803
804 private boolean hasElementAttribute(final Node node, final String name)
805 {
806 if (node.getNodeName().equals("xsd:complexType"))
807 {
808
809 return hasElementAttribute(node, name, null);
810 }
811
812 return hasElementAttribute(node, name, "xsd:complexType");
813 }
814
815
816
817
818
819
820
821
822
823
824
825 private boolean hasElementAttribute(final Node node, final String name, final String viaType)
826 {
827 Node via = viaType == null ? node : DocumentReader.getChild(node, viaType).get();
828 for (int childIndex = 0; childIndex < via.getChildNodes().getLength(); childIndex++)
829 {
830 Node child = via.getChildNodes().item(childIndex);
831 String childName = DocumentReader.getAttribute(child, "name").orElse(null);
832 String childNodeName = child.getNodeName();
833 if (childNodeName.equals("xsd:attribute") && name.equals(childName)
834 || childNodeName.equals("xsd:sequence") && hasElementAttribute(child, name, null))
835 {
836 return true;
837 }
838 if (childNodeName.equals("xsd:complexContent") || childNodeName.equals("xsd:simpleContent"))
839 {
840 Optional<Node> extension = DocumentReader.getChild(child, "xsd:extension");
841 if (extension.isPresent())
842 {
843 Optional<String> base = DocumentReader.getAttribute(extension.get(), "base");
844 if (base.isPresent())
845 {
846 Optional<Node> baseNode = getType(base.get());
847
848 if (baseNode.isPresent() && hasElementAttribute(baseNode.get(), name, null))
849 {
850 return true;
851 }
852 }
853 if (hasElementAttribute(extension.get(), name, null))
854 {
855 return true;
856 }
857 }
858 }
859 }
860 return false;
861 }
862
863
864
865
866
867 public Node getRoot()
868 {
869 return this.root;
870 }
871
872
873
874
875
876
877 public Optional<Node> getElement(final String path)
878 {
879 return Optional.ofNullable(this.elements.get(path.replace("ots:", "")));
880 }
881
882
883
884
885
886
887 public Optional<Node> getType(final String base)
888 {
889 return Optional.ofNullable(this.types.get(base.replace("ots:", "")));
890 }
891
892
893
894
895
896 public Map<String, Node> keys()
897 {
898 return new LinkedHashMap<>(this.keys);
899 }
900
901
902
903
904
905 public Map<String, Node> keyrefs()
906 {
907 return new LinkedHashMap<>(this.keyrefs);
908 }
909
910
911
912
913
914 public Map<String, Node> uniques()
915 {
916 return new LinkedHashMap<>(this.uniques);
917 }
918
919
920
921
922
923
924
925 public boolean isType(final Node node, final String path)
926 {
927 Optional<String> name = DocumentReader.getAttribute(node, "name");
928 if (name.isPresent() && path.equals(name.get()))
929 {
930 return true;
931 }
932 Node nodeUse = node;
933 if (nodeUse.getNodeName().equals("xsd:element"))
934 {
935 nodeUse = DocumentReader.getChild(node, "xsd:complexType").orElse(null);
936 if (nodeUse == null)
937 {
938 nodeUse = DocumentReader.getChild(node, "xsd:simpleType").orElse(null);
939 if (nodeUse == null)
940 {
941 return false;
942 }
943 }
944 }
945 for (int childIndex = 0; childIndex < nodeUse.getChildNodes().getLength(); childIndex++)
946 {
947 Node child = nodeUse.getChildNodes().item(childIndex);
948 if (child.getNodeName().equals("xsd:complexContent") || child.getNodeName().equals("xsd:simpleContent"))
949 {
950 String base = null;
951 Optional<Node> extension = DocumentReader.getChild(child, "xsd:extension");
952 if (extension.isPresent())
953 {
954 base = DocumentReader.getAttribute(extension.get(), "base").orElse(null);
955 }
956 Optional<Node> restriction = DocumentReader.getChild(child, "xsd:restriction");
957 if (restriction.isPresent())
958 {
959 base = DocumentReader.getAttribute(restriction.get(), "base").orElse(null);
960 }
961 if (base == null)
962 {
963 return false;
964 }
965 if (base.endsWith(path))
966 {
967 return true;
968 }
969 if (!base.startsWith("xsd:"))
970 {
971 Optional<Node> baseNode = getType(base);
972 if (baseNode.isPresent() && !baseNode.equals(nodeUse))
973 {
974 return isType(baseNode.get(), path);
975 }
976 }
977 }
978 }
979 return false;
980 }
981
982 }