1 package org.opentrafficsim.editor;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedHashSet;
9 import java.util.LinkedList;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Map.Entry;
13 import java.util.NavigableMap;
14 import java.util.NoSuchElementException;
15 import java.util.Objects;
16 import java.util.Optional;
17 import java.util.Set;
18 import java.util.SortedSet;
19 import java.util.TreeMap;
20 import java.util.TreeSet;
21 import java.util.function.Consumer;
22 import java.util.function.Function;
23 import java.util.function.Supplier;
24 import java.util.stream.Collectors;
25
26 import javax.xml.parsers.ParserConfigurationException;
27
28 import org.djutils.event.Event;
29 import org.djutils.event.EventListener;
30 import org.djutils.event.EventListenerMap;
31 import org.djutils.event.EventType;
32 import org.djutils.event.LocalEventProducer;
33 import org.djutils.event.reference.Reference;
34 import org.djutils.event.reference.ReferenceType;
35 import org.djutils.exceptions.Throw;
36 import org.djutils.immutablecollections.ImmutableArrayList;
37 import org.djutils.immutablecollections.ImmutableList;
38 import org.djutils.metadata.MetaData;
39 import org.djutils.metadata.ObjectDescriptor;
40 import org.opentrafficsim.base.logger.Logger;
41 import org.opentrafficsim.editor.DocumentReader.NodeAnnotation;
42 import org.opentrafficsim.editor.XsdTreeNodeUtil.LoadingIndices;
43 import org.opentrafficsim.editor.XsdTreeNodeUtil.Occurs;
44 import org.opentrafficsim.editor.decoration.validation.CoupledValidator;
45 import org.opentrafficsim.editor.decoration.validation.KeyValidator;
46 import org.opentrafficsim.editor.decoration.validation.KeyrefValidator;
47 import org.opentrafficsim.editor.decoration.validation.ValueValidator;
48 import org.opentrafficsim.editor.decoration.validation.ValueValidator.Whitespace;
49 import org.opentrafficsim.road.network.factory.xml.AdapterRegistry;
50 import org.opentrafficsim.swing.gui.OtsSimulationPanel;
51 import org.opentrafficsim.xml.bindings.ExpressionAdapter;
52 import org.w3c.dom.Document;
53 import org.w3c.dom.Element;
54 import org.w3c.dom.Node;
55 import org.w3c.dom.NodeList;
56 import org.xml.sax.SAXException;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public class XsdTreeNode extends LocalEventProducer
76 {
77
78
79 public static final EventType VALUE_CHANGED = new EventType("VALUECHANGED",
80 new MetaData("Value changed", "Value changed on node",
81 new ObjectDescriptor("Node", "Node with changed value", XsdTreeNode.class),
82 new ObjectDescriptor("Previous", "Previous node value", String.class)));
83
84
85 public static final EventType ATTRIBUTE_CHANGED = new EventType("ATTRIBUTECHANGED",
86 new MetaData("Attribute changed", "Attribute changed on node",
87 new ObjectDescriptor("Node", "Node with changed attribute value", XsdTreeNode.class),
88 new ObjectDescriptor("Attribute", "Name of the attribute", String.class),
89 new ObjectDescriptor("Previous", "Previous attribute value", String.class)));
90
91
92 public static final EventType OPTION_CHANGED = new EventType("OPTIONCHANGED",
93 new MetaData("Option changed", "Option changed on node",
94 new ObjectDescriptor("Node", "Node on which the event is called", XsdTreeNode.class),
95 new ObjectDescriptor("Selected", "Newly selected option node", XsdTreeNode.class),
96 new ObjectDescriptor("Previous", "Previously selected option node", XsdTreeNode.class)));
97
98
99 public static final EventType ACTIVATION_CHANGED = new EventType("ACTIVATIONCHANGED",
100 new MetaData("Activation changed", "Activation changed on node",
101 new ObjectDescriptor("Node", "Node with changed activation.", XsdTreeNode.class),
102 new ObjectDescriptor("Activation", "New activation state.", Boolean.class)));
103
104
105 public static final EventType MOVED = new EventType("MOVED",
106 new MetaData("Node moved", "Node moved", new ObjectDescriptor("Node", "Node that was moved.", XsdTreeNode.class),
107 new ObjectDescriptor("OldIndex", "Old index.", Integer.class),
108 new ObjectDescriptor("NewIndex", "New index.", Integer.class)));
109
110
111 private static final int MAX_OPTIONNAME_LENGTH = 64;
112
113
114 @SuppressWarnings("checkstyle:visibilitymodifier")
115 XsdTreeNode parent;
116
117
118 @SuppressWarnings("checkstyle:visibilitymodifier")
119 Node xsdNode;
120
121
122 private final ImmutableList<Node> hiddenNodes;
123
124
125
126
127
128 @SuppressWarnings("checkstyle:visibilitymodifier")
129 Node referringXsdNode;
130
131
132 private final Schema schema;
133
134
135 private int minOccurs = 0;
136
137
138 private int maxOccurs = -1;
139
140
141
142
143 private final String pathString;
144
145
146
147
148 @SuppressWarnings("checkstyle:visibilitymodifier")
149 XsdTreeNode choice;
150
151
152 @SuppressWarnings("checkstyle:visibilitymodifier")
153 List<XsdTreeNode> options;
154
155
156 @SuppressWarnings("checkstyle:visibilitymodifier")
157 XsdTreeNode selected;
158
159
160
161
162 @SuppressWarnings("checkstyle:visibilitymodifier")
163 List<XsdTreeNode> children;
164
165
166
167
168 private List<Node> attributeNodes;
169
170
171
172 private Map<Integer, Whitespace> attributeWhiteSpaces;
173
174
175 private List<String> attributeValues;
176
177
178
179
180 @SuppressWarnings("checkstyle:visibilitymodifier")
181 boolean active;
182
183
184
185
186
187
188 private boolean deactivated;
189
190
191 private Boolean isIdentifiable;
192
193
194 private int idIndex;
195
196
197 private Boolean isEditable;
198
199
200 private String value;
201
202
203 private Whitespace whiteSpace;
204
205
206
207
208
209 private boolean isIncluded;
210
211
212
213
214 private Function<XsdTreeNode, String> stringFunction;
215
216
217 private Map<String, Consumer<XsdTreeNode>> consumers = new LinkedHashMap<>();
218
219
220 private String description;
221
222
223 private Set<Function<XsdTreeNode, String>> nodeValidators = new LinkedHashSet<>();
224
225
226 private NavigableMap<ValueValidator, Object> valueValidators = new TreeMap<>();
227
228
229 private Map<String, SortedSet<ValueValidator>> attributeValidators = new LinkedHashMap<>();
230
231
232
233
234
235
236 private Map<String, Map<ValueValidator, Object>> attributeValidatorFields = new LinkedHashMap<>();
237
238
239 private Boolean isSelfValid = null;
240
241
242 private Boolean isValid = null;
243
244
245 private Boolean valueValid = null;
246
247
248 private String valueInvalidMessage = null;
249
250
251 private Boolean nodeValid = null;
252
253
254 private String nodeInvalidMessage = null;
255
256
257 private List<Boolean> attributeValid;
258
259
260 private List<String> attributeInvalidMessage;
261
262
263
264
265
266
267 protected XsdTreeNode(final Schema schema)
268 {
269 Throw.whenNull(schema, "XsdSchema may not be null.");
270 this.parent = null;
271 this.hiddenNodes = new ImmutableArrayList<>(Collections.emptyList());
272 this.schema = schema;
273 this.xsdNode = this.schema.getRoot();
274 this.referringXsdNode = null;
275 setOccurs();
276 this.pathString = buildPathLocation();
277 this.active = true;
278 this.isIncluded = false;
279 }
280
281
282
283
284
285
286
287 XsdTreeNode(final XsdTreeNode parent, final Node xsdNode, final ImmutableList<Node> hiddenNodes)
288 {
289 this(parent, xsdNode, hiddenNodes, null);
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329 XsdTreeNode(final XsdTreeNode parent, final Node xsdNode, final ImmutableList<Node> hiddenNodes,
330 final Node referringXsdNode)
331 {
332 Throw.whenNull(xsdNode, "Node may not be null.");
333 this.parent = parent;
334 this.xsdNode = xsdNode;
335 this.hiddenNodes = hiddenNodes;
336 this.referringXsdNode = referringXsdNode;
337 this.schema = parent.schema;
338 setOccurs();
339 this.active = this.minOccurs > 0;
340 this.pathString = buildPathLocation();
341 this.isIncluded = parent.isIncluded;
342 Node valueDefiningNode = XsdTreeNodeUtil.getValueDefiningNode(getRelevantNode(), this.schema);
343 if (valueDefiningNode != null)
344 {
345 this.whiteSpace = ValueValidator.getWhiteSpace(valueDefiningNode, this.schema);
346 }
347 }
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364 private void setOccurs()
365 {
366 Node node = this.choice != null ? this.choice.xsdNode : getRelevantNode();
367 this.minOccurs = Occurs.MIN.get(node);
368 this.maxOccurs = Occurs.MAX.get(node);
369
370
371
372 if (getNodeName().equals("xsd:all"))
373 {
374 int childCount = 0;
375 for (int i = 0; i < this.xsdNode.getChildNodes().getLength(); i++)
376 {
377 Node child = this.xsdNode.getChildNodes().item(i);
378 if (!child.getNodeName().equals("#text"))
379 {
380 childCount++;
381 }
382 }
383 this.maxOccurs *= childCount;
384 }
385 }
386
387
388
389
390
391 private String buildPathLocation()
392 {
393 List<XsdTreeNode> path = getPath();
394 StringBuilder pathStr = new StringBuilder(((XsdTreeNode) path.get(0)).getNodeName());
395 for (int i = 1; i < path.size(); i++)
396 {
397 String nodeString = ((XsdTreeNode) path.get(i)).getNodeName();
398
399 if ((!nodeString.equals("xsd:choice") && !nodeString.equals("xsd:all") && !nodeString.equals("xsd:sequence")
400 && !nodeString.equals("xi:include")) || i == path.size() - 1)
401 {
402 pathStr.append(".").append(nodeString);
403 }
404 }
405 return pathStr.toString();
406 }
407
408
409
410
411
412 public List<XsdTreeNode> getPath()
413 {
414 LinkedList<XsdTreeNode> deque = new LinkedList<>();
415 for (XsdTreeNode node = this; node != null; node = node.parent)
416 {
417 deque.addFirst(node);
418 }
419 return deque;
420 }
421
422
423
424
425
426 public XsdTreeNodeRoot getRoot()
427 {
428 return this.parent.getRoot();
429 }
430
431
432
433
434
435
436 private Node getRelevantNode()
437 {
438 return this.referringXsdNode == null ? this.xsdNode : this.referringXsdNode;
439 }
440
441
442
443
444
445 public boolean isSequence()
446 {
447 return this.xsdNode.getNodeName().equals("xsd:sequence");
448 }
449
450
451
452
453
454
455
456 public boolean isChoice()
457 {
458 return this.choice != null;
459 }
460
461
462
463
464
465 public List<XsdOption> getOptions()
466 {
467 List<XsdOption> out = new ArrayList<>();
468 if (this.choice != null)
469 {
470 for (XsdTreeNode node : this.choice.options)
471 {
472 out.add(new XsdOption(node, this.choice, node.equals(this.choice.selected)));
473 }
474 }
475 return out;
476 }
477
478
479
480
481
482 public void setOption(final XsdTreeNode node)
483 {
484 Throw.when(!isChoice(), IllegalStateException.class, "Setting option on node that is not (part of) a choice.");
485 Throw.when(!this.choice.options.contains(node) && !this.choice.equals(node), IllegalStateException.class,
486 "Setting option on node that does not have this option.");
487 XsdTreeNode previous = this.choice.selected == null ? this.choice : this.choice.selected;
488 if (node.equals(previous))
489 {
490 return;
491 }
492 this.choice.selected = node;
493 int index = removeOptionFromParent();
494 this.parent.children.add(index, node);
495 node.invalidate();
496 this.choice.options.forEach((n) -> n.fireEvent(XsdTreeNodeRoot.OPTION_CHANGED, new Object[] {n, node, previous}));
497 }
498
499
500
501
502
503 private int removeOptionFromParent()
504 {
505 int removeIndex = this.parent.children.indexOf(this);
506 if (removeIndex >= 0)
507 {
508 this.parent.children.remove(removeIndex);
509 return removeIndex;
510 }
511 for (XsdTreeNode node : this.choice.options)
512 {
513 removeIndex = this.parent.children.indexOf(node);
514 if (removeIndex >= 0)
515 {
516 this.parent.children.remove(removeIndex);
517 return removeIndex;
518 }
519 }
520 return this.parent.children.size();
521 }
522
523
524
525
526
527 public XsdTreeNode getOption()
528 {
529 return this.choice.selected;
530 }
531
532
533
534
535 void createOptions()
536 {
537 Throw.when(!this.xsdNode.getNodeName().equals("xsd:choice") && !this.xsdNode.getNodeName().equals("xsd:all"),
538 IllegalStateException.class, "Can only add options for a node of type xsd:choice or xsd:all.");
539 this.options = new ArrayList<>();
540 XsdTreeNodeUtil.addChildren(this.xsdNode, this.parent, this.options, this.hiddenNodes, this.schema, false, -1);
541 this.choice = this;
542 for (XsdTreeNode option : this.options)
543 {
544 option.minOccurs = this.minOccurs;
545 option.maxOccurs = this.maxOccurs;
546 if (this.minOccurs == 0)
547 {
548 option.active = false;
549 }
550 option.choice = this;
551 }
552 if (this.choice.xsdNode.getNodeName().equals("xsd:all"))
553 {
554 for (XsdTreeNode option : this.options)
555 {
556 XsdTreeNodeUtil.addXsdAllValidator(this.choice, option);
557 }
558 }
559 }
560
561
562
563
564
565
566 public boolean isSingleChoiceType()
567 {
568 boolean choiceFound = false;
569 for (int i = 0; i < this.xsdNode.getChildNodes().getLength(); i++)
570 {
571 Node child = this.xsdNode.getChildNodes().item(i);
572 String name = child.getNodeName();
573 if (name.equals("xsd:choice") && Occurs.MAX.get(child) == 1)
574 {
575 if (choiceFound)
576 {
577 return false;
578 }
579 choiceFound = true;
580 }
581 else if (!name.equals("#text"))
582 {
583 return false;
584 }
585 }
586 return choiceFound;
587 }
588
589
590
591
592
593
594
595 public int getChildCount()
596 {
597 if (!this.active)
598 {
599 return 0;
600 }
601 assureChildren();
602 return this.children.size();
603 }
604
605
606
607
608
609
610 public void setChild(final int index, final XsdTreeNode child)
611 {
612 if (index >= this.children.size())
613 {
614 this.children.add(child);
615 }
616 else
617 {
618 this.children.add(index, child);
619 }
620 child.parent = this;
621 child.invalidate();
622 }
623
624
625
626
627
628
629 public XsdTreeNode getChild(final int index)
630 {
631 assureChildren();
632 return this.children.get(index);
633 }
634
635
636
637
638
639
640 public boolean hasChild(final String name)
641 {
642 assureChildren();
643 for (XsdTreeNode child : this.children)
644 {
645 if (child.getNodeName().equals("xsd:sequence") || child.getNodeName().equals("xsd:choice")
646 || child.getNodeName().equals("xsd:all"))
647 {
648 return child.hasChild(name);
649 }
650 if (child.getNodeName().equals(name))
651 {
652 return true;
653 }
654 }
655 return false;
656 }
657
658
659
660
661
662
663
664
665 public XsdTreeNode getFirstChild(final String name)
666 {
667 assureChildren();
668 for (XsdTreeNode child : this.children)
669 {
670 if (child.getNodeName().equals("xsd:sequence") || child.getNodeName().equals("xsd:choice")
671 || child.getNodeName().equals("xsd:all"))
672 {
673 try
674 {
675 return child.getFirstChild(name);
676 }
677 catch (NoSuchElementException ex)
678 {
679
680 }
681 }
682 if (child.getNodeName().equals(name))
683 {
684 return child;
685 }
686 }
687 throw new NoSuchElementException("Node does not have a child named " + name);
688 }
689
690
691
692
693
694 public List<XsdTreeNode> getChildren()
695 {
696 assureChildren();
697 return new ArrayList<>(this.children);
698 }
699
700
701
702
703 protected void assureChildren()
704 {
705 if (this.children != null)
706 {
707 return;
708 }
709 if (!this.active)
710 {
711 return;
712 }
713 this.children = new ArrayList<>();
714 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
715 {
716 if (this.attributeValues == null || this.attributeValues.get(0) == null)
717 {
718 return;
719 }
720 File file = new File(this.attributeValues.get(0));
721 if (!file.isAbsolute())
722 {
723 file = new File(getRoot().getDirectory() + this.attributeValues.get(0));
724 }
725 if (!file.exists() && this.attributeValues.get(1) != null)
726 {
727 file = new File(this.attributeValues.get(1));
728 if (!file.isAbsolute())
729 {
730 file = new File(getRoot().getDirectory() + this.attributeValues.get(1));
731 }
732 }
733 if (file.exists())
734 {
735 Document document;
736 try
737 {
738 document = DocumentReader.open(file.toURI());
739 }
740 catch (SAXException | IOException | ParserConfigurationException exception)
741 {
742 return;
743 }
744 Node xsdIncludeNode = document.getFirstChild();
745 String nameXml = xsdIncludeNode.getNodeName().replace("ots:", "");
746
747
748 for (XsdTreeNode sibling : this.parent.children)
749 {
750 if (sibling.isRelevantNode(nameXml))
751 {
752 XsdTreeNode child =
753 new XsdTreeNode(this, sibling.xsdNode, sibling.hiddenNodes, sibling.referringXsdNode);
754 child.isIncluded = true;
755 this.children.add(child);
756 getRoot().fireEvent(XsdTreeNodeRoot.NODE_CREATED,
757 new Object[] {child, child.parent, child.parent.children.indexOf(child)});
758 child.loadXmlNodes(xsdIncludeNode);
759 return;
760 }
761 }
762 }
763 }
764 else if (!this.xsdNode.hasChildNodes())
765 {
766 return;
767 }
768 Map<Node, ImmutableList<Node>> relevantNodes = XsdTreeNodeUtil.getRelevantNodesWithChildren(this.xsdNode,
769 new ImmutableArrayList<>(Collections.emptyList()), this.schema);
770 for (Entry<Node, ImmutableList<Node>> entry : relevantNodes.entrySet())
771 {
772 XsdTreeNodeUtil.addChildren(entry.getKey(), this, this.children, entry.getValue(), this.schema, true, -1);
773 }
774 for (int index = 0; index < this.children.size(); index++)
775 {
776 XsdTreeNode child = this.children.get(index);
777 for (int occurs = 1; occurs < child.minOccurs; occurs++)
778 {
779 if (!child.isActive())
780 {
781 child.setActive();
782 }
783 child.add();
784 index++;
785 }
786 }
787 }
788
789
790
791
792
793 public XsdTreeNode getParent()
794 {
795 return this.parent;
796 }
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816 private synchronized void assureAttributesAndDescription()
817 {
818 if (this.attributeNodes != null)
819 {
820 return;
821 }
822 this.attributeNodes = new ArrayList<>();
823 this.attributeValues = new ArrayList<>();
824 this.attributeValid = new ArrayList<>();
825 this.attributeInvalidMessage = new ArrayList<>();
826 this.description = NodeAnnotation.DESCRIPTION.get(getRelevantNode()).orElse(null);
827 int descriptionSpecificity = this.description != null ? 0 : Integer.MIN_VALUE;
828 Node complexType =
829 (this.xsdNode.getNodeName().equals("xsd:complexType") || this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
830 ? this.xsdNode : DocumentReader.getChild(this.xsdNode, "xsd:complexType").orElse(null);
831 if (complexType != null && this.xsdNode.hasChildNodes())
832 {
833 findAttributesAndDescription(complexType, -1, descriptionSpecificity);
834 }
835 }
836
837
838
839
840
841
842
843
844
845 private void findAttributesAndDescription(final Node node, final int nodeSpecificity, final int descriptionSpecificity)
846 {
847 Optional<String> descript = NodeAnnotation.DESCRIPTION.get(node);
848 int updatedDescriptionSpecificity = descriptionSpecificity;
849 if (descript.isPresent() && descriptionSpecificity < nodeSpecificity)
850 {
851 updatedDescriptionSpecificity = nodeSpecificity;
852 this.description = descript.get();
853 }
854 for (int childIndex = 0; childIndex < node.getChildNodes().getLength(); childIndex++)
855 {
856 Node child = node.getChildNodes().item(childIndex);
857 if (child.getNodeName().equals("xsd:attribute"))
858 {
859 if (DocumentReader.getAttribute(child, "name").isPresent())
860 {
861 this.attributeNodes.add(child);
862 this.attributeValues.add(null);
863 this.attributeValid.add(null);
864 this.attributeInvalidMessage.add(null);
865
866
867
868 if (!node.equals(XiIncludeNode.XI_INCLUDE))
869 {
870 Whitespace attributeWhiteSpace = ValueValidator.getWhiteSpace(child, this.schema);
871 if (!attributeWhiteSpace.equals(Whitespace.NONE))
872 {
873 if (this.attributeWhiteSpaces == null)
874 {
875 this.attributeWhiteSpaces = new LinkedHashMap<>();
876 }
877 int attributeIndex = this.attributeNodes.size() - 1;
878 this.attributeWhiteSpaces.put(attributeIndex, attributeWhiteSpace);
879 }
880 }
881 }
882 else
883 {
884 Optional<String> ref = DocumentReader.getAttribute(child, "ref");
885 if (ref.isPresent())
886 {
887 throw new IllegalStateException(
888 "An xsd:attribute refers to type " + ref + " but attribute references are not supported.");
889 }
890 }
891 }
892 if (child.getNodeName().equals("xsd:complexContent") || child.getNodeName().equals("xsd:simpleContent"))
893 {
894 Optional<Node> extension = DocumentReader.getChild(child, "xsd:extension");
895 if (extension.isPresent())
896 {
897 findAttributesAndDescription(extension.get(), nodeSpecificity - 1, updatedDescriptionSpecificity);
898 String base = DocumentReader.getAttribute(extension.get(), "base").get();
899 Optional<Node> baseNode = this.schema.getType(base);
900 if (baseNode.isPresent())
901 {
902 findAttributesAndDescription(baseNode.get(), nodeSpecificity - 2, updatedDescriptionSpecificity);
903 }
904 }
905 Optional<Node> restriction = DocumentReader.getChild(child, "xsd:restriction");
906 if (restriction.isPresent())
907 {
908 String base = DocumentReader.getAttribute(restriction.get(), "base").get();
909 Optional<Node> baseNode = this.schema.getType(base);
910 if (baseNode.isPresent())
911 {
912 findAttributesAndDescription(baseNode.get(), nodeSpecificity - 2, descriptionSpecificity);
913 }
914 }
915 }
916 }
917 }
918
919
920
921
922
923 public int attributeCount()
924 {
925 assureAttributesAndDescription();
926 return this.attributeNodes.size();
927 }
928
929
930
931
932
933
934
935 public Node getAttributeNode(final int index)
936 {
937 assureAttributesAndDescription();
938 Objects.checkIndex(index, attributeCount());
939 return this.attributeNodes.get(index);
940 }
941
942
943
944
945
946
947
948 @SuppressWarnings("checkstyle:hiddenfield")
949 public void setAttributeValue(final int index, final String value)
950 {
951 Objects.checkIndex(index, attributeCount());
952 String previous = this.attributeValues.get(index);
953 Whitespace whitespace = this.attributeWhiteSpaces == null ? Whitespace.NONE
954 : this.attributeWhiteSpaces.getOrDefault(index, Whitespace.NONE);
955 String next = whitespace.parse(value);
956 if (!XsdTreeNodeUtil.valuesAreEqual(previous, next))
957 {
958 boolean isDefaultBoolean = false;
959 if ("xsd:boolean".equals(DocumentReader.getAttribute(this.attributeNodes.get(index), "type").orElse(null)))
960 {
961 Optional<String> defaultValue = getDefaultAttributeValue(index);
962 isDefaultBoolean = defaultValue.isPresent() && defaultValue.get().equals(next);
963 }
964 this.attributeValues.set(index, (next == null || next.isEmpty() || isDefaultBoolean) ? null : next);
965 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
966 {
967 removeChildren();
968 this.children = null;
969 assureChildren();
970 }
971 invalidate();
972 fireEvent(ATTRIBUTE_CHANGED, new Object[] {this, getAttributeNameByIndex(index), previous});
973 }
974 }
975
976
977
978
979
980
981
982 public Optional<String> getDefaultAttributeValue(final int index)
983 {
984 assureAttributesAndDescription();
985 Objects.checkIndex(index, attributeCount());
986 Optional<String> appInfoDefault =
987 DocumentReader.NodeAnnotation.APPINFO_DEFAULT_VALUE.get(this.attributeNodes.get(index));
988 if (appInfoDefault.isPresent())
989 {
990 return appInfoDefault;
991 }
992 return DocumentReader.getAttribute(this.attributeNodes.get(index), "default");
993 }
994
995
996
997
998
999
1000
1001 @SuppressWarnings("checkstyle:hiddenfield")
1002 public void setAttributeValue(final String name, final String value)
1003 {
1004 setAttributeValue(getAttributeIndexByName(name), value);
1005 }
1006
1007
1008
1009
1010
1011
1012
1013 public String getAttributeValue(final int index)
1014 {
1015 assureAttributesAndDescription();
1016 Objects.checkIndex(index, attributeCount());
1017 return this.attributeValues.get(index);
1018 }
1019
1020
1021
1022
1023
1024
1025
1026 public String getAttributeValue(final String attribute)
1027 {
1028 assureAttributesAndDescription();
1029 return this.attributeValues.get(getAttributeIndexByName(attribute));
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039 public String getAttributeValueOrDefault(final int index)
1040 {
1041 return getAttributeValue(index) != null ? getAttributeValue(index) : getDefaultAttributeValue(index).orElse(null);
1042 }
1043
1044
1045
1046
1047
1048
1049 public String getAttributeValueOrDefault(final String attribute)
1050 {
1051 return getAttributeValueOrDefault(getAttributeIndexByName(attribute));
1052 }
1053
1054
1055
1056
1057
1058
1059
1060 public int getAttributeIndexByName(final String attribute)
1061 {
1062 assureAttributesAndDescription();
1063 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
1064 {
1065 switch (attribute)
1066 {
1067 case "File":
1068 return 0;
1069 case "Fallback":
1070 return 1;
1071 default:
1072 throw new NoSuchElementException("Attribute " + attribute + " is not in node " + getNodeName() + ".");
1073 }
1074 }
1075 for (int index = 0; index < this.attributeNodes.size(); index++)
1076 {
1077 Node attr = this.attributeNodes.get(index);
1078 if (attribute.equals(DocumentReader.getAttribute(attr, "name").orElse(null)))
1079 {
1080 return index;
1081 }
1082 }
1083 throw new NoSuchElementException("Attribute " + attribute + " is not in node " + getNodeName() + ".");
1084 }
1085
1086
1087
1088
1089
1090
1091
1092 public String getAttributeNameByIndex(final int index)
1093 {
1094 Objects.checkIndex(index, attributeCount());
1095 return DocumentReader.getAttribute(this.attributeNodes.get(index), "name").get();
1096 }
1097
1098
1099
1100
1101
1102
1103 public boolean hasAttribute(final String attribute)
1104 {
1105 assureAttributesAndDescription();
1106 for (int index = 0; index < this.attributeNodes.size(); index++)
1107 {
1108 Node attr = this.attributeNodes.get(index);
1109 if (attribute.equals(DocumentReader.getAttribute(attr, "name").orElse(null)))
1110 {
1111 return true;
1112 }
1113 }
1114 return false;
1115 }
1116
1117
1118
1119
1120
1121
1122
1123 public boolean isActive()
1124 {
1125 return this.active;
1126 }
1127
1128
1129
1130
1131
1132 public void setActive()
1133 {
1134 if (!this.active)
1135 {
1136 this.active = true;
1137 if (this.deactivated)
1138 {
1139 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE) || this.isIncluded)
1140 {
1141
1142 for (XsdTreeNode child : this.children)
1143 {
1144 child.setActive();
1145 }
1146 }
1147 invalidate();
1148 fireEvent(new Event(XsdTreeNodeRoot.ACTIVATION_CHANGED, new Object[] {this, true}));
1149 return;
1150 }
1151 this.children = null;
1152 this.attributeNodes = null;
1153 this.isIdentifiable = null;
1154 this.isEditable = null;
1155 assureAttributesAndDescription();
1156 assureChildren();
1157 if (this.choice != null && this.choice.selected.equals(this))
1158 {
1159 this.choice.active = true;
1160 for (XsdTreeNode option : this.choice.options)
1161 {
1162 if (!option.equals(this))
1163 {
1164 option.setActive();
1165 }
1166 }
1167 }
1168 if (isEditable())
1169 {
1170 this.whiteSpace = ValueValidator.getWhiteSpace(getRelevantNode(), this.schema);
1171 }
1172 invalidate();
1173 fireEvent(new Event(XsdTreeNodeRoot.ACTIVATION_CHANGED, new Object[] {this, true}));
1174 }
1175 }
1176
1177
1178
1179
1180
1181 public void setInactive()
1182 {
1183 if (this.active)
1184 {
1185 this.deactivated = true;
1186 this.active = false;
1187 invalidate();
1188 fireEvent(new Event(XsdTreeNode.ACTIVATION_CHANGED, new Object[] {this, false}));
1189
1190 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE) || this.isIncluded)
1191 {
1192 for (XsdTreeNode child : this.children)
1193 {
1194 child.setInactive();
1195 }
1196 }
1197 }
1198 }
1199
1200
1201
1202
1203
1204 public boolean isIdentifiable()
1205 {
1206 if (!this.active)
1207 {
1208 return false;
1209 }
1210 if (this.isIdentifiable == null)
1211 {
1212 assureAttributesAndDescription();
1213 for (int index = 0; index < attributeCount(); index++)
1214 {
1215 Node node = this.attributeNodes.get(index);
1216 if ("Id".equals(DocumentReader.getAttribute(node, "name").orElse(null)))
1217 {
1218 this.isIdentifiable = true;
1219 this.idIndex = index;
1220 return true;
1221 }
1222 }
1223 this.isIdentifiable = false;
1224 }
1225 return this.isIdentifiable;
1226 }
1227
1228
1229
1230
1231
1232
1233 public void setId(final String id)
1234 {
1235 Throw.when(!isIdentifiable(), NoSuchElementException.class, "Node is non-identifiable.");
1236 setAttributeValue(this.idIndex, id);
1237 }
1238
1239
1240
1241
1242
1243
1244 public String getId()
1245 {
1246 Throw.when(!isIdentifiable(), NoSuchElementException.class, "Getting id from non-identifiable node.");
1247 return this.attributeValues.get(this.idIndex);
1248 }
1249
1250
1251
1252
1253
1254
1255 public boolean isEditable()
1256 {
1257 if (!this.active)
1258 {
1259 return false;
1260 }
1261 if (this.isEditable == null)
1262 {
1263 this.isEditable = XsdTreeNodeUtil.isEditable(this.xsdNode, this.schema);
1264 }
1265 return this.isEditable;
1266 }
1267
1268
1269
1270
1271
1272 public void setValue(final String value)
1273 {
1274 Throw.when(!isEditable(), IllegalStateException.class,
1275 "Node is not an xsd:simpleType or xsd:complexType with xsd:simpleContent, hence no value is allowed.");
1276 String previous = this.value;
1277 String next = this.whiteSpace.parse(value);
1278 if (!XsdTreeNodeUtil.valuesAreEqual(previous, next))
1279 {
1280 this.value = (next == null || next.isEmpty()) ? null : next;
1281 invalidate();
1282 fireEvent(new Event(VALUE_CHANGED, new Object[] {this, previous}));
1283 }
1284 }
1285
1286
1287
1288
1289
1290 public String getValue()
1291 {
1292 return this.value;
1293 }
1294
1295
1296
1297
1298
1299 public Optional<String> getDefaultValue()
1300 {
1301 Optional<String> appInfoDefault = DocumentReader.NodeAnnotation.APPINFO_DEFAULT_VALUE.get(getRelevantNode());
1302 if (appInfoDefault.isPresent())
1303 {
1304 return appInfoDefault;
1305 }
1306 return DocumentReader.getAttribute(getRelevantNode(), "default");
1307 }
1308
1309
1310
1311
1312
1313 public String getValueOrDefault()
1314 {
1315 return getValue() != null ? getValue() : getDefaultValue().orElse(null);
1316 }
1317
1318
1319
1320
1321
1322 public boolean isIncluded()
1323 {
1324 return this.isIncluded;
1325 }
1326
1327
1328
1329
1330
1331 public boolean isAddable()
1332 {
1333 return isActive() && (this.maxOccurs == -1 || (this.parent != null && siblingPositions().size() < this.maxOccurs));
1334 }
1335
1336
1337
1338
1339
1340
1341 public XsdTreeNode add()
1342 {
1343 if (this.choice != null)
1344 {
1345 int index = this.parent.children.indexOf(this) + 1;
1346 XsdTreeNode node = new XsdTreeNode(this.choice.parent, this.choice.xsdNode, this.choice.hiddenNodes,
1347 this.choice.referringXsdNode);
1348 getRoot().fireEvent(XsdTreeNodeRoot.NODE_CREATED,
1349 new Object[] {node, node.parent, node.parent.children.indexOf(this) + 1});
1350 node.createOptions();
1351 int indexSelected = this.choice.options.indexOf(this.choice.selected);
1352 XsdTreeNode selectedOption = node.options.get(indexSelected);
1353 node.choice.setOption(selectedOption);
1354 this.parent.children.remove(selectedOption);
1355 this.parent.children.add(index, selectedOption);
1356 node.options.get(indexSelected).setActive();
1357 return selectedOption;
1358 }
1359 else
1360 {
1361 int index = this.parent.children.indexOf(this) + 1;
1362 XsdTreeNode node = new XsdTreeNode(this.parent, this.xsdNode, this.hiddenNodes, this.referringXsdNode);
1363 this.parent.children.add(index, node);
1364 node.active = true;
1365 getRoot().fireEvent(XsdTreeNodeRoot.NODE_CREATED,
1366 new Object[] {node, node.parent, node.parent.children.indexOf(node)});
1367 return node;
1368 }
1369 }
1370
1371
1372
1373
1374
1375 public XsdTreeNode duplicate()
1376 {
1377 return duplicate(this.parent);
1378 }
1379
1380
1381
1382
1383
1384
1385 public XsdTreeNode duplicate(final XsdTreeNode newParent)
1386 {
1387
1388 XsdTreeNode copyNode = emptyCopy(newParent);
1389 copyNode.active = this.active;
1390 copyInto(copyNode);
1391 copyNode.invalidate();
1392 getRoot().fireEvent(XsdTreeNodeRoot.NODE_CREATED,
1393 new Object[] {copyNode, newParent, newParent.children.indexOf(copyNode)});
1394 invalidate();
1395 return copyNode;
1396 }
1397
1398
1399
1400
1401
1402 public XsdTreeNode emptyCopy()
1403 {
1404 return emptyCopy(this.parent);
1405 }
1406
1407
1408
1409
1410
1411
1412 private XsdTreeNode emptyCopy(final XsdTreeNode newParent)
1413 {
1414 int indexOfNode = this.parent.children.indexOf(this);
1415 if (newParent.equals(this.parent))
1416 {
1417 indexOfNode++;
1418 }
1419 XsdTreeNode copyNode = new XsdTreeNode(newParent, this.xsdNode, this.hiddenNodes, this.referringXsdNode);
1420 if (newParent.children == null)
1421 {
1422 newParent.children = new ArrayList<>();
1423 }
1424 newParent.children.add(indexOfNode, copyNode);
1425 copyNode.parent = newParent;
1426 return copyNode;
1427 }
1428
1429
1430
1431
1432
1433
1434
1435 public boolean canContain(final XsdTreeNode copied)
1436 {
1437 return this.xsdNode == copied.xsdNode || (this.referringXsdNode != null && copied.referringXsdNode != null
1438 && DocumentReader.getAttribute(this.referringXsdNode, "type").isPresent()
1439 && DocumentReader.getAttribute(this.referringXsdNode, "type").get()
1440 .equals(DocumentReader.getAttribute(copied.referringXsdNode, "type").orElse(null)));
1441 }
1442
1443
1444
1445
1446
1447 public void copyInto(final XsdTreeNode copyNode)
1448 {
1449 if (this.equals(copyNode))
1450 {
1451 return;
1452 }
1453 copyNode.value = this.value;
1454 copyNode.isIncluded = this.isIncluded;
1455
1456 if (this.choice != null)
1457 {
1458 XsdTreeNode choiceNode = new XsdTreeNode(copyNode.parent, this.choice.xsdNode, this.choice.hiddenNodes,
1459 this.choice.referringXsdNode);
1460 choiceNode.choice = choiceNode;
1461
1462 int selectedIndex = this.choice.options.indexOf(this);
1463 choiceNode.options = new ArrayList<>();
1464 XsdTreeNodeUtil.addChildren(this.choice.xsdNode, copyNode.parent, choiceNode.options, this.choice.hiddenNodes,
1465 this.schema, false, selectedIndex);
1466 choiceNode.options.add(selectedIndex, copyNode);
1467 choiceNode.selected = choiceNode.options.get(selectedIndex);
1468 if (this.choice.getNodeName().equals("xsd:all"))
1469 {
1470 XsdTreeNodeUtil.addXsdAllValidator(choiceNode, choiceNode);
1471 }
1472 for (int index = 0; index < choiceNode.options.size(); index++)
1473 {
1474 XsdTreeNode option = choiceNode.options.get(index);
1475 if (this.choice.getNodeName().equals("xsd:all"))
1476 {
1477 XsdTreeNodeUtil.addXsdAllValidator(choiceNode, option);
1478 }
1479 option.minOccurs = choiceNode.minOccurs;
1480 option.maxOccurs = choiceNode.maxOccurs;
1481 if (choiceNode.minOccurs > 0)
1482 {
1483 option.setActive();
1484 }
1485 option.active = this.choice.options.get(index).active;
1486 option.choice = choiceNode;
1487 }
1488 }
1489
1490 copyNode.assureAttributesAndDescription();
1491 for (int index = 0; index < attributeCount(); index++)
1492 {
1493 copyNode.attributeValues.set(index, this.attributeValues.get(index));
1494 }
1495
1496 if (copyNode.children != null)
1497 {
1498 for (int index = 0; index < copyNode.getChildCount(); index++)
1499 {
1500 XsdTreeNode child = copyNode.getChild(index);
1501 copyNode.children.remove(index);
1502 child.parent = null;
1503 getRoot().fireEvent(XsdTreeNodeRoot.NODE_REMOVED, new Object[] {child, copyNode, index});
1504 }
1505 }
1506 if (this.children != null)
1507 {
1508 for (int index = 0; index < this.children.size(); index++)
1509 {
1510 this.children.get(index).duplicate(copyNode);
1511 }
1512 }
1513 }
1514
1515
1516
1517
1518
1519
1520 public boolean isRemovable()
1521 {
1522 return isActive() && siblingPositions().size() > this.minOccurs;
1523 }
1524
1525
1526
1527
1528
1529
1530 public final void remove()
1531 {
1532 int numberOfTypeOrChoiceInParent = siblingPositions().size();
1533 if (this.minOccurs == 0 && numberOfTypeOrChoiceInParent == 1 && !this.isIncluded)
1534 {
1535 setInactive();
1536 return;
1537 }
1538 if (this.choice != null && this.choice.selected.equals(this))
1539 {
1540 for (XsdTreeNode option : this.choice.options)
1541 {
1542 if (!this.choice.selected.equals(this))
1543 {
1544 option.remove();
1545 }
1546 }
1547 }
1548 removeChildren();
1549 XsdTreeNode parentNode = this.parent;
1550 int index = this.parent.children.indexOf(this);
1551 this.parent.children.remove(this);
1552 XsdTreeNodeRoot root = getRoot();
1553 this.parent = null;
1554 root.fireEvent(XsdTreeNodeRoot.NODE_REMOVED, new Object[] {this, parentNode, index});
1555 }
1556
1557
1558
1559
1560 private void removeChildren()
1561 {
1562 if (this.children != null)
1563 {
1564
1565 for (XsdTreeNode child : new ArrayList<>(this.children))
1566 {
1567 child.remove();
1568 }
1569 }
1570 }
1571
1572
1573
1574
1575
1576 public boolean canMoveUp()
1577 {
1578 List<Integer> positions = siblingPositions();
1579 return !positions.isEmpty() && this.parent.children.indexOf(this) > positions.get(0);
1580 }
1581
1582
1583
1584
1585
1586 public boolean canMoveDown()
1587 {
1588 List<Integer> positions = siblingPositions();
1589
1590 return !positions.isEmpty() && this.parent.children.indexOf(this) < positions.get(positions.size() - 1);
1591 }
1592
1593
1594
1595
1596
1597
1598
1599
1600 private List<Integer> siblingPositions()
1601 {
1602 List<Integer> siblingPositions = new ArrayList<>();
1603 if (this.parent == null)
1604 {
1605 return siblingPositions;
1606 }
1607 if (this.choice != null)
1608 {
1609 for (int index = 0; index < this.parent.children.size(); index++)
1610 {
1611 for (XsdTreeNode option : this.choice.options)
1612 {
1613 if (XsdTreeNodeUtil.haveSameType(option, this.parent.children.get(index)))
1614 {
1615 siblingPositions.add(index);
1616 break;
1617 }
1618 }
1619 }
1620 }
1621 else
1622 {
1623 for (int index = 0; index < this.parent.children.size(); index++)
1624 {
1625 if (XsdTreeNodeUtil.haveSameType(this, this.parent.children.get(index)))
1626 {
1627 siblingPositions.add(index);
1628 }
1629 }
1630 }
1631 return siblingPositions;
1632 }
1633
1634
1635
1636
1637
1638
1639 public void move(final int down)
1640 {
1641 int oldIndex = this.parent.children.indexOf(this);
1642 this.parent.children.remove(this);
1643 int newIndex = oldIndex + down;
1644 this.parent.children.add(newIndex, this);
1645 fireEvent(MOVED, new Object[] {this, oldIndex, newIndex});
1646 }
1647
1648
1649
1650
1651
1652 public int minOccurs()
1653 {
1654 return this.minOccurs;
1655 }
1656
1657
1658
1659
1660
1661
1662 public int maxOccurs()
1663 {
1664 return this.maxOccurs;
1665 }
1666
1667
1668
1669
1670
1671
1672 public String getAttributeBaseType(final int index)
1673 {
1674 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
1675 {
1676 return "xsd:anyURI";
1677 }
1678 return ValueValidator.getBaseType(this.attributeNodes.get(index), this.schema);
1679 }
1680
1681
1682
1683
1684
1685
1686 public boolean isType(final String path)
1687 {
1688 boolean isType = getPathString().endsWith("." + path);
1689 if (isType)
1690 {
1691 return isType;
1692 }
1693 int dot = path.lastIndexOf(".");
1694 if (dot > -1)
1695 {
1696 if (this.parent == null)
1697 {
1698 return false;
1699 }
1700 isType = isType(path.substring(dot + 1)) && this.parent.isType(path.substring(0, dot));
1701 if (isType)
1702 {
1703 return isType;
1704 }
1705 }
1706 return this.schema.isType(this.xsdNode, path);
1707 }
1708
1709
1710
1711
1712
1713
1714
1715 public boolean hasExpression()
1716 {
1717 if (!this.active)
1718 {
1719 return false;
1720 }
1721 if (valueIsExpression())
1722 {
1723 return true;
1724 }
1725 for (int index = 0; index < attributeCount(); index++)
1726 {
1727 if (attributeIsExpression(index))
1728 {
1729 return true;
1730 }
1731 }
1732 if (this.children != null)
1733 {
1734 for (XsdTreeNode child : this.children)
1735 {
1736 if (child.hasExpression())
1737 {
1738 return true;
1739 }
1740 }
1741 }
1742 return false;
1743 }
1744
1745
1746
1747
1748
1749 public boolean valueIsExpression()
1750 {
1751 return this.value != null && this.value.startsWith("{") && this.value.endsWith("}");
1752 }
1753
1754
1755
1756
1757
1758
1759 public boolean idIsExpression()
1760 {
1761 Throw.when(!isIdentifiable(), NoSuchElementException.class, "Node is non-identifiable.");
1762 return attributeIsExpression(getAttributeIndexByName("Id"));
1763 }
1764
1765
1766
1767
1768
1769
1770
1771 public boolean attributeIsExpression(final int index)
1772 {
1773 Objects.checkIndex(index, attributeCount());
1774 String attributeValue = this.attributeValues.get(index);
1775 return attributeValue != null && attributeValue.startsWith("{") && attributeValue.endsWith("}");
1776 }
1777
1778
1779
1780
1781
1782
1783
1784
1785 public void addConsumer(final String menuItem, final Consumer<XsdTreeNode> consumer)
1786 {
1787 this.consumers.put(menuItem, consumer);
1788 }
1789
1790
1791
1792
1793
1794 public boolean hasConsumer()
1795 {
1796 return !this.consumers.isEmpty();
1797 }
1798
1799
1800
1801
1802
1803 public Set<String> getConsumerMenuItems()
1804 {
1805 return this.consumers.keySet();
1806 }
1807
1808
1809
1810
1811
1812
1813 public void consume(final String menuItem)
1814 {
1815 Throw.when(!this.consumers.containsKey(menuItem), IllegalArgumentException.class, "Unable to consume node for %s.",
1816 menuItem);
1817 this.consumers.get(menuItem).accept(this);
1818 }
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828 public String getNodeName()
1829 {
1830 Node node = getRelevantNode();
1831 Optional<String> ref = DocumentReader.getAttribute(node, "ref");
1832 if (ref.isPresent())
1833 {
1834 return ref.get().replace("ots:", "");
1835 }
1836 Optional<String> name = DocumentReader.getAttribute(node, "name");
1837 if (name.isPresent())
1838 {
1839 return name.get().replace("ots:", "");
1840 }
1841 return node.getNodeName().replace("ots:", "");
1842 }
1843
1844
1845
1846
1847
1848 public Optional<String> getDescription()
1849 {
1850 assureAttributesAndDescription();
1851 if (this.description == null && isChoice())
1852 {
1853 this.choice.assureAttributesAndDescription();
1854 return Optional.ofNullable(this.choice.description);
1855 }
1856 return Optional.ofNullable(this.description);
1857 }
1858
1859
1860
1861
1862
1863 public String getShortString()
1864 {
1865 if (this.options != null)
1866 {
1867
1868 return this.options.toString().toLowerCase();
1869 }
1870 if (this.xsdNode.getNodeName().equals("xsd:sequence"))
1871 {
1872
1873 StringBuilder stringBuilder = new StringBuilder();
1874 Node relevantNode = getRelevantNode();
1875 Optional<String> annotation = NodeAnnotation.APPINFO_NAME.get(relevantNode);
1876 if (annotation.isPresent())
1877 {
1878 stringBuilder.append(annotation.get());
1879 }
1880 else
1881 {
1882 stringBuilder.append("{");
1883
1884 Set<String> coveredTypes = new LinkedHashSet<>();
1885 String separator = "";
1886 boolean preActive = this.active;
1887 this.active = true;
1888 assureChildren();
1889 if (getChildCount() == 1)
1890 {
1891 stringBuilder.append(getChild(0).getShortString());
1892 }
1893 else
1894 {
1895 for (XsdTreeNode child : this.children)
1896 {
1897 if (!coveredTypes.contains(child.getPathString()) || child.xsdNode.getNodeName().equals("xsd:sequence")
1898 || child.xsdNode.getNodeName().equals("xsd:choice")
1899 || child.xsdNode.getNodeName().equals("xsd:all"))
1900 {
1901 stringBuilder.append(separator).append(child.getShortString());
1902 separator = "\u2009|\u2009";
1903 coveredTypes.add(child.getPathString());
1904 }
1905 }
1906 }
1907 this.active = preActive;
1908 stringBuilder.append("}");
1909 }
1910 if (stringBuilder.length() > MAX_OPTIONNAME_LENGTH)
1911 {
1912 return stringBuilder.substring(0, MAX_OPTIONNAME_LENGTH - 3) + "..}";
1913 }
1914 return stringBuilder.toString();
1915 }
1916 if (this.xsdNode.getNodeName().equals("xi:include"))
1917 {
1918 return "Include";
1919 }
1920 return OtsSimulationPanel.separatedName(getNodeName());
1921 }
1922
1923
1924
1925
1926
1927
1928
1929
1930 @SuppressWarnings("hiddenfield")
1931 public void setStringFunction(final Function<XsdTreeNode, String> stringFunction, final boolean overwrite)
1932 {
1933 if (this.stringFunction == null || overwrite)
1934 {
1935 this.stringFunction = stringFunction;
1936 }
1937 }
1938
1939
1940
1941
1942
1943
1944 public String getPathString()
1945 {
1946 return this.pathString;
1947 }
1948
1949
1950
1951
1952
1953 @Override
1954 public String toString()
1955 {
1956 StringBuilder string = new StringBuilder(getShortString());
1957 if (!this.active)
1958 {
1959 return string.toString();
1960 }
1961 if (isIdentifiable() && getId() != null && !getId().isEmpty())
1962 {
1963 string.append("\u2009").append(getId());
1964 }
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976 if (this.stringFunction != null)
1977 {
1978 string.append("\u2009(").append(this.stringFunction.apply(this)).append(")");
1979 }
1980 return string.toString();
1981 }
1982
1983
1984
1985
1986
1987
1988
1989
1990 public List<String> getIdRestrictions()
1991 {
1992 return getAttributeRestrictions(this.idIndex);
1993 }
1994
1995
1996
1997
1998
1999 public List<String> getValueRestrictions()
2000 {
2001 if ("ots:boolean".equals(DocumentReader.getAttribute(getRelevantNode(), "type").orElse(null)))
2002 {
2003 return List.of("true", "false");
2004 }
2005 List<String> valueOptions = getOptionsFromValidators(this.valueValidators);
2006 if (!valueOptions.isEmpty())
2007 {
2008 return valueOptions;
2009 }
2010 return XsdTreeNodeUtil.getOptionsFromRestrictions(ValueValidator.getRestrictions(this.xsdNode, this.schema));
2011 }
2012
2013
2014
2015
2016
2017
2018
2019 public List<String> getAttributeRestrictions(final int index)
2020 {
2021 Objects.checkIndex(index, attributeCount());
2022 if ("ots:boolean".equals(DocumentReader.getAttribute(this.attributeNodes.get(index), "type").orElse(null)))
2023 {
2024 return List.of("true", "false");
2025 }
2026 String field = getAttributeNameByIndex(index);
2027
2028 Map<ValueValidator, Object> map = new LinkedHashMap<>();
2029 this.attributeValidators.computeIfAbsent(field, (f) -> new TreeSet<>())
2030 .forEach((v) -> map.put(v, this.attributeValidatorFields.get(field).get(v)));
2031 List<String> valueOptions = getOptionsFromValidators(map);
2032
2033
2034 if (!valueOptions.isEmpty() || this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
2035 {
2036 return valueOptions;
2037 }
2038 return XsdTreeNodeUtil
2039 .getOptionsFromRestrictions(ValueValidator.getRestrictions(this.attributeNodes.get(index), this.schema));
2040 }
2041
2042
2043
2044
2045
2046
2047 private List<String> getOptionsFromValidators(final Map<ValueValidator, Object> validators)
2048 {
2049 List<String> intersection = null;
2050 for (Entry<ValueValidator, Object> entry : validators.entrySet())
2051 {
2052 ValueValidator validator = entry.getKey();
2053 Optional<List<String>> valueOptions = validator.getOptions(this, entry.getValue());
2054 if (valueOptions.isPresent())
2055 {
2056 intersection = intersection == null ? valueOptions.get()
2057 : intersection.stream().filter(valueOptions.get()::contains).collect(Collectors.toList());
2058 }
2059 }
2060 return intersection == null ? Collections.emptyList() : intersection;
2061 }
2062
2063
2064
2065
2066
2067
2068
2069 public Optional<XsdTreeNode> getCoupledNodeValue()
2070 {
2071 return Optional.ofNullable(getCoupledNode(this.valueValidators.keySet()));
2072 }
2073
2074
2075
2076
2077
2078
2079
2080 public Optional<XsdTreeNode> getCoupledNodeAttribute(final int index)
2081 {
2082 Objects.checkIndex(index, attributeCount());
2083 return getCoupledNodeAttribute(getAttributeNameByIndex(index));
2084 }
2085
2086
2087
2088
2089
2090
2091 public Optional<XsdTreeNode> getCoupledNodeAttribute(final String attribute)
2092 {
2093 if (this.attributeValidators.containsKey(attribute))
2094 {
2095 return Optional.ofNullable(getCoupledNode(this.attributeValidators.get(attribute)));
2096 }
2097 return Optional.empty();
2098 }
2099
2100
2101
2102
2103
2104
2105 private XsdTreeNode getCoupledNode(final Set<ValueValidator> validators)
2106 {
2107 for (ValueValidator validator : validators)
2108 {
2109 if (validator instanceof CoupledValidator coupledValidator)
2110 {
2111 coupledValidator.validate(this);
2112 return coupledValidator.getCoupledNode(this);
2113 }
2114 }
2115 return null;
2116 }
2117
2118
2119
2120
2121
2122
2123
2124
2125 public boolean isSelfValid()
2126 {
2127 if (this.isSelfValid == null)
2128 {
2129 if (!this.active)
2130 {
2131 this.isSelfValid = true;
2132 }
2133 else if (reportInvalidNode().isPresent() || reportInvalidValue().isPresent())
2134 {
2135 this.isSelfValid = false;
2136 }
2137 else
2138 {
2139 boolean attributesValid = true;
2140 for (int index = 0; index < attributeCount(); index++)
2141 {
2142 if (reportInvalidAttributeValue(index).isPresent())
2143 {
2144 attributesValid = false;
2145 break;
2146 }
2147 }
2148 this.isSelfValid = attributesValid;
2149 }
2150 }
2151 return this.isSelfValid;
2152 }
2153
2154
2155
2156
2157
2158
2159 public boolean isValid()
2160 {
2161 if (this.isValid == null)
2162 {
2163 if (!isActive())
2164 {
2165 this.isValid = true;
2166 }
2167 else if (!isSelfValid())
2168 {
2169 this.isValid = false;
2170 }
2171 else
2172 {
2173 boolean childrenValid = true;
2174 if (this.children != null)
2175 {
2176 for (XsdTreeNode child : this.children)
2177 {
2178 if (!child.isValid())
2179 {
2180 childrenValid = false;
2181 }
2182 }
2183 }
2184 this.isValid = childrenValid;
2185 }
2186 }
2187 return this.isValid;
2188 }
2189
2190
2191
2192
2193 public void invalidate()
2194 {
2195 this.isSelfValid = null;
2196 this.isValid = null;
2197 if (this.parent != null)
2198 {
2199 this.parent.invalidate();
2200 }
2201 this.valueValid = null;
2202 this.valueInvalidMessage = null;
2203 this.nodeValid = null;
2204 this.nodeInvalidMessage = null;
2205 assureAttributesAndDescription();
2206 Collections.fill(this.attributeValid, null);
2207 Collections.fill(this.attributeInvalidMessage, null);
2208 }
2209
2210
2211
2212
2213
2214 void invalidateAll()
2215 {
2216 if (this.children != null)
2217 {
2218 for (XsdTreeNode child : this.children)
2219 {
2220 child.invalidateAll();
2221 }
2222 }
2223 invalidate();
2224 }
2225
2226
2227
2228
2229
2230
2231 public void addNodeValidator(final Function<XsdTreeNode, String> validator)
2232 {
2233 this.nodeValidators.add(validator);
2234 }
2235
2236
2237
2238
2239
2240
2241
2242
2243 public void addValueValidator(final ValueValidator validator, final Object field)
2244 {
2245 Throw.when(validator instanceof CoupledValidator && coupledValidatorExists(this.valueValidators.keySet(), validator),
2246 IllegalStateException.class, "Adding %s to the node value of %s but a CoupledValidator already exists.",
2247 validator.getClass().getSimpleName(), getPathString());
2248 this.valueValidators.put(validator, field);
2249 }
2250
2251
2252
2253
2254
2255
2256
2257 public void addAttributeValidator(final String attribute, final ValueValidator validator)
2258 {
2259 addAttributeValidator(attribute, validator, null);
2260 }
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270 public void addAttributeValidator(final String attribute, final ValueValidator validator, final Object field)
2271 {
2272 Throw.when(
2273 validator instanceof CoupledValidator
2274 && coupledValidatorExists(this.attributeValidators.get(attribute), validator),
2275 IllegalStateException.class, "Adding %s to the attribute %s in %s but a CoupledValidator already exists.",
2276 validator.getClass().getSimpleName(), attribute, getPathString());
2277 this.attributeValidators.computeIfAbsent(attribute, (key) -> new TreeSet<>()).add(validator);
2278 this.attributeValidatorFields.computeIfAbsent(attribute, (key) -> new LinkedHashMap<>()).put(validator, field);
2279 }
2280
2281
2282
2283
2284
2285
2286
2287 private boolean coupledValidatorExists(final Set<ValueValidator> validators, final ValueValidator validator)
2288 {
2289 return validators != null
2290 && validators.stream().filter((v) -> v instanceof CoupledValidator && !v.equals(validator)).count() > 0;
2291 }
2292
2293
2294
2295
2296
2297
2298 public Optional<String> reportInvalidNode()
2299 {
2300 if (this.nodeValid == null)
2301 {
2302 for (Function<XsdTreeNode, String> validator : this.nodeValidators)
2303 {
2304 String message = validator.apply(this);
2305 if (message != null)
2306 {
2307 this.nodeInvalidMessage = message;
2308 this.nodeValid = false;
2309 return Optional.of(message);
2310 }
2311 }
2312 this.nodeValid = true;
2313 }
2314 return Optional.ofNullable(this.nodeInvalidMessage);
2315 }
2316
2317
2318
2319
2320
2321 public Optional<String> reportInvalidId()
2322 {
2323 if (!isActive())
2324 {
2325 return Optional.empty();
2326 }
2327 return isIdentifiable() ? reportInvalidAttributeValue(getAttributeIndexByName("Id")) : Optional.empty();
2328 }
2329
2330
2331
2332
2333
2334 public Optional<String> reportInvalidValue()
2335 {
2336 if (this.valueValid == null)
2337 {
2338 if (!isEditable() || !isActive())
2339 {
2340 this.valueInvalidMessage = null;
2341 this.valueValid = true;
2342 return Optional.empty();
2343 }
2344
2345 Supplier<String> getter = () -> getValueOrDefault();
2346 Consumer<String> setter = v -> this.value = v;
2347 Supplier<Optional<ExpressionAdapter<?, ?>>> adapterSupplier = () -> AdapterRegistry.getElementAdapter(getNodeName(),
2348 getRelevantNode(), (n) -> this.schema.getType(n).orElse(null));
2349 Supplier<Optional<String>> valeuValidationSupplier =
2350 () -> ValueValidator.reportInvalidValue(this.xsdNode, getValueOrDefault(), this.schema);
2351 Optional<String> message = ValueValidator.reportInvalidWithExpression(this, valueIsExpression(), getter, setter,
2352 adapterSupplier, this.valueValidators.navigableKeySet(), valeuValidationSupplier);
2353
2354 this.valueInvalidMessage = message.orElse(null);
2355 this.valueValid = message.isEmpty();
2356 }
2357 return Optional.ofNullable(this.valueInvalidMessage);
2358 }
2359
2360
2361
2362
2363
2364
2365
2366 public Optional<String> reportInvalidAttributeValue(final int index)
2367 {
2368 Objects.checkIndex(index, attributeCount());
2369 if (this.attributeValid.get(index) == null)
2370 {
2371 if (!isActive())
2372 {
2373 this.attributeInvalidMessage.set(index, null);
2374 this.attributeValid.set(index, true);
2375 return Optional.empty();
2376 }
2377 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
2378 {
2379 XsdTreeNode root = getPath().get(0);
2380 if (root instanceof XsdTreeNodeRoot)
2381 {
2382 Optional<String> message = ValueValidator.reportInvalidInclude(((XsdTreeNodeRoot) root).getDirectory(),
2383 this.attributeValues.get(0), this.attributeValues.get(1));
2384 this.attributeInvalidMessage.set(index, message.orElse(null));
2385 this.attributeValid.set(index, message.isEmpty());
2386 return message;
2387 }
2388 else
2389 {
2390
2391 this.attributeInvalidMessage.set(index, null);
2392 this.attributeValid.set(index, true);
2393 return Optional.empty();
2394 }
2395 }
2396
2397 Supplier<String> getter = () -> getAttributeValueOrDefault(index);
2398 Consumer<String> setter = v -> this.attributeValues.set(index, v);
2399 Supplier<Optional<ExpressionAdapter<?, ?>>> adapterSupplier =
2400 () -> AdapterRegistry.getAttributeAdapter(getNodeName(), getRelevantNode(), getAttributeNode(index),
2401 (n) -> this.schema.getType(n).orElse(null));
2402 SortedSet<ValueValidator> validatorSet =
2403 this.attributeValidators.computeIfAbsent(getAttributeNameByIndex(index), key -> new TreeSet<>());
2404 Supplier<Optional<String>> valueValidationSupplier = () -> ValueValidator
2405 .reportInvalidAttributeValue(getAttributeNode(index), getAttributeValueOrDefault(index), this.schema);
2406 Optional<String> message = ValueValidator.reportInvalidWithExpression(this, attributeIsExpression(index), getter,
2407 setter, adapterSupplier, validatorSet, valueValidationSupplier);
2408
2409 this.attributeInvalidMessage.set(index, message.orElse(null));
2410 this.attributeValid.set(index, message.isEmpty());
2411 return message;
2412 }
2413 return Optional.ofNullable(this.attributeInvalidMessage.get(index));
2414 }
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428 public void saveXmlNodes(final Document document, final Node xmlParent)
2429 {
2430 if (!this.active)
2431 {
2432 return;
2433 }
2434
2435
2436 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
2437 {
2438 Element element = document.createElement(getNodeName());
2439 xmlParent.appendChild(element);
2440 if (this.attributeValues != null && this.attributeValues.get(0) != null)
2441 {
2442 element.setAttribute("href", this.attributeValues.get(0));
2443 if (this.attributeValues.get(1) != null)
2444 {
2445 Element fallback = document.createElement("xi:fallback");
2446 element.appendChild(fallback);
2447 Element include = document.createElement("xi:include");
2448 fallback.appendChild(include);
2449 include.setAttribute("href", this.attributeValues.get(1));
2450 }
2451 }
2452 return;
2453 }
2454
2455
2456 if (this.xsdNode.getNodeName().equals("xsd:sequence"))
2457 {
2458 for (int index = 0; index < this.getChildCount(); index++)
2459 {
2460 this.children.get(index).saveXmlNodes(document, xmlParent);
2461 }
2462 return;
2463 }
2464
2465 Element element = document.createElement("ots:" + getNodeName());
2466 xmlParent.appendChild(element);
2467
2468 if (this.value != null && !this.value.isEmpty())
2469 {
2470 element.setTextContent(this.value);
2471 }
2472
2473 for (int index = 0; index < attributeCount(); index++)
2474 {
2475 String attributeValue = this.attributeValues.get(index);
2476 if (attributeValue != null && !attributeValue.isEmpty())
2477 {
2478 element.setAttribute(getAttributeNameByIndex(index), attributeValue);
2479 }
2480 }
2481
2482 for (int index = 0; index < this.getChildCount(); index++)
2483 {
2484 if (!this.children.get(index).isIncluded)
2485 {
2486 this.children.get(index).saveXmlNodes(document, element);
2487 }
2488 }
2489 }
2490
2491
2492
2493
2494
2495
2496 public void loadXmlNodes(final Node nodeXml)
2497 {
2498 setActive();
2499
2500 if (this.xsdNode.equals(XiIncludeNode.XI_INCLUDE))
2501 {
2502 assureAttributesAndDescription();
2503 setAttributeValue(0, DocumentReader.getAttribute(nodeXml, "href").orElse(null));
2504 Optional<Node> fallback = DocumentReader.getChild(nodeXml, "xi:fallback");
2505 if (fallback.isPresent())
2506 {
2507 Optional<Node> fallbackInclude = DocumentReader.getChild(fallback.get(), "xi:include");
2508 if (fallbackInclude.isPresent())
2509 {
2510 setAttributeValue(1, DocumentReader.getAttribute(fallbackInclude.get(), "href").orElse(null));
2511 }
2512 }
2513 return;
2514 }
2515
2516
2517 String candidateValue = "";
2518 if (nodeXml.getChildNodes() != null)
2519 {
2520 for (int indexXml = 0; indexXml < nodeXml.getChildNodes().getLength(); indexXml++)
2521 {
2522 if (nodeXml.getChildNodes().item(indexXml).getNodeName().equals("#text"))
2523 {
2524 String nodeValue = nodeXml.getChildNodes().item(indexXml).getNodeValue();
2525 if (!nodeValue.isBlank())
2526 {
2527 candidateValue += nodeValue;
2528 }
2529 }
2530 }
2531 }
2532 if (!candidateValue.isEmpty())
2533 {
2534 String previous = this.value;
2535 this.value = candidateValue;
2536 fireEvent(new Event(VALUE_CHANGED, new Object[] {this, previous}));
2537 }
2538
2539
2540 assureAttributesAndDescription();
2541 if (nodeXml.getAttributes() != null)
2542 {
2543 for (int index = 0; index < nodeXml.getAttributes().getLength(); index++)
2544 {
2545 Node attributeNode = nodeXml.getAttributes().item(index);
2546 switch (attributeNode.getNodeName())
2547 {
2548 case "xmlns:ots":
2549 case "xmlns:xi":
2550 case "xmlns:xsi":
2551 case "xsi:schemaLocation":
2552 continue;
2553 default:
2554 try
2555 {
2556 setAttributeValue(attributeNode.getNodeName(), attributeNode.getNodeValue());
2557 }
2558 catch (NoSuchElementException e)
2559 {
2560 Logger.ots().warn("Unable to load attribute {}=\"{}\" in {}.", attributeNode.getNodeName(),
2561 attributeNode.getNodeValue(), getShortString());
2562 }
2563 }
2564 }
2565 }
2566
2567
2568 assureChildren();
2569 if (nodeXml.getChildNodes() != null)
2570 {
2571 loadChildren(new LoadingIndices(0, 0), nodeXml.getChildNodes(), false);
2572
2573
2574 if (this.isIncluded)
2575 {
2576 int index = 0;
2577 while (index < this.children.size())
2578 {
2579 boolean relevantForAny = false;
2580 for (int indexXml = 0; indexXml < nodeXml.getChildNodes().getLength(); indexXml++)
2581 {
2582 String xmlName = nodeXml.getChildNodes().item(indexXml).getNodeName().replace("ots:", "");
2583 if (this.children.get(index).isRelevantNode(xmlName))
2584 {
2585 relevantForAny = true;
2586 break;
2587 }
2588 }
2589 if (!relevantForAny)
2590 {
2591
2592 this.children.remove(index);
2593 }
2594 else
2595 {
2596 index++;
2597 }
2598 }
2599 }
2600
2601 }
2602 invalidate();
2603 }
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642 protected void loadChildren(final LoadingIndices indices, final NodeList childrenXml, final boolean loadingSubSequence)
2643 {
2644 List<XsdTreeNode> loadedChildren = new ArrayList<>();
2645 int passes = 0;
2646 int maxPasses = 1;
2647 int loadedDuringPass = 0;
2648 Optional<Node> complexType = DocumentReader.getChild(this.xsdNode, "xsd:complexType");
2649 if (complexType.isPresent())
2650 {
2651 Optional<Node> sequence = DocumentReader.getChild(complexType.get(), "xsd:sequence");
2652 if (sequence.isPresent())
2653 {
2654 maxPasses = Occurs.MAX.get(sequence.get());
2655 }
2656 }
2657
2658 int xmlNodeIndex = indices.getXmlNode();
2659 int xsdTreeNodeIndex = indices.getXsdTreeNode();
2660 while (xmlNodeIndex < childrenXml.getLength())
2661 {
2662 Node childNodeXml = childrenXml.item(xmlNodeIndex);
2663 if (childNodeXml.getNodeName().equals("#text"))
2664 {
2665 xmlNodeIndex++;
2666 continue;
2667 }
2668
2669
2670 String nameXml = childNodeXml.getNodeName().replace("ots:", "");
2671 if (xsdTreeNodeIndex > 0 && this.children.get(xsdTreeNodeIndex - 1).isRelevantNode(nameXml))
2672 {
2673 if (xsdTreeNodeIndex >= this.children.size() || !this.children.get(xsdTreeNodeIndex).isRelevantNode(nameXml))
2674 {
2675 this.children.get(xsdTreeNodeIndex - 1).add();
2676 }
2677 }
2678 else
2679 {
2680 while (xsdTreeNodeIndex < this.children.size() && (!this.children.get(xsdTreeNodeIndex).isRelevantNode(nameXml)
2681 || loadedChildren.contains(this.children.get(xsdTreeNodeIndex))))
2682 {
2683 xsdTreeNodeIndex++;
2684 }
2685 if (xsdTreeNodeIndex >= this.children.size())
2686 {
2687 if (loadedDuringPass == 0)
2688 {
2689 Logger.ots().warn("Failing to load {}, it is not a valid node.", nameXml);
2690 xmlNodeIndex++;
2691 xsdTreeNodeIndex = 0;
2692 continue;
2693 }
2694 else
2695 {
2696 passes++;
2697 if (passes >= maxPasses)
2698 {
2699 if (!loadingSubSequence)
2700 {
2701 Logger.ots().warn("Failing to load {}, maximum number of passes reached.", nameXml);
2702 }
2703 indices.setXmlNode(xmlNodeIndex);
2704 return;
2705 }
2706 }
2707 xsdTreeNodeIndex = 0;
2708 loadedDuringPass = 0;
2709 continue;
2710 }
2711 }
2712
2713
2714 XsdTreeNode relevantChild = this.children.get(xsdTreeNodeIndex);
2715 if (relevantChild.choice == null)
2716 {
2717 if (relevantChild.getNodeName().equals("xsd:sequence"))
2718 {
2719 LoadingIndices sequenceIndices = new LoadingIndices(xmlNodeIndex, 0);
2720 relevantChild.loadChildren(sequenceIndices, childrenXml, true);
2721 loadedChildren.add(relevantChild);
2722 xmlNodeIndex = sequenceIndices.getXmlNode() - 1;
2723 }
2724 else
2725 {
2726 relevantChild.loadXmlNodes(childNodeXml);
2727 loadedChildren.add(relevantChild);
2728 }
2729 }
2730 else
2731 {
2732 boolean optionSet = false;
2733 for (XsdTreeNode option : relevantChild.choice.options)
2734 {
2735 if (option.xsdNode.getNodeName().equals("xsd:sequence"))
2736 {
2737 for (XsdTreeNode child : option.children)
2738 {
2739 if (child.isRelevantNode(nameXml))
2740 {
2741 relevantChild.choice.setOption(option);
2742 LoadingIndices optionIndices = new LoadingIndices(xmlNodeIndex, 0);
2743 option.loadChildren(optionIndices, childrenXml, true);
2744 loadedChildren.add(option);
2745 xmlNodeIndex = optionIndices.getXmlNode() - 1;
2746 optionSet = true;
2747 break;
2748 }
2749 }
2750 }
2751 if (option.getNodeName().equals(nameXml))
2752 {
2753 relevantChild.choice.setOption(option);
2754 option.loadXmlNodes(childNodeXml);
2755 optionSet = true;
2756 }
2757 if (optionSet)
2758 {
2759 for (XsdTreeNode otherOption : relevantChild.choice.options)
2760 {
2761 if (!otherOption.equals(option))
2762 {
2763 otherOption.setActive();
2764 }
2765 }
2766 break;
2767 }
2768 }
2769 }
2770 loadedDuringPass++;
2771 xsdTreeNodeIndex++;
2772 indices.setXsdTreeNode(xsdTreeNodeIndex);
2773 xmlNodeIndex++;
2774 }
2775 indices.setXmlNode(xmlNodeIndex);
2776 }
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792 private boolean isRelevantNode(final String nameXml)
2793 {
2794 if (this.getNodeName().equals(nameXml))
2795 {
2796 return true;
2797 }
2798 if (this.choice == null)
2799 {
2800 if (getNodeName().equals("xsd:sequence"))
2801 {
2802 this.active = true;
2803 assureChildren();
2804 for (XsdTreeNode child : this.children)
2805 {
2806 boolean relevant = child.isRelevantNode(nameXml);
2807 if (relevant)
2808 {
2809 return relevant;
2810 }
2811 }
2812 }
2813 return false;
2814 }
2815 if (this.choice.selected.equals(this))
2816 {
2817 for (XsdTreeNode option : this.choice.options)
2818 {
2819 if (option.xsdNode.getNodeName().equals("xsd:sequence"))
2820 {
2821 option.active = true;
2822 option.assureChildren();
2823 for (XsdTreeNode child : option.children)
2824 {
2825 boolean relevant = child.isRelevantNode(nameXml);
2826 if (relevant)
2827 {
2828 return relevant;
2829 }
2830 }
2831 }
2832 else if (!option.equals(this))
2833 {
2834 boolean relevant = option.isRelevantNode(nameXml);
2835 if (relevant)
2836 {
2837 return relevant;
2838 }
2839 }
2840 }
2841 }
2842 return false;
2843 }
2844
2845
2846
2847 @Override
2848 public boolean addListener(final EventListener listener, final EventType eventType, final ReferenceType referenceType)
2849 {
2850 boolean result = super.addListener(listener, eventType, referenceType);
2851 sortListeners(eventType);
2852 return result;
2853 }
2854
2855 @Override
2856 public boolean addListener(final EventListener listener, final EventType eventType)
2857 {
2858 boolean result = super.addListener(listener, eventType);
2859 sortListeners(eventType);
2860 return result;
2861 }
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871 private void sortListeners(final EventType eventType)
2872 {
2873 EventListenerMap map = getEventListenerMap();
2874 Reference<EventListener> undo = null;
2875 List<Reference<EventListener>> list = map.get(eventType);
2876 List<Reference<EventListener>> keys = new ArrayList<>();
2877 List<Reference<EventListener>> keyrefs = new ArrayList<>();
2878 List<Reference<EventListener>> coupled = new ArrayList<>();
2879 for (Reference<EventListener> listen : list)
2880 {
2881 if (listen.get() instanceof Undo)
2882 {
2883 undo = listen;
2884 }
2885 else if (listen.get() instanceof KeyValidator)
2886 {
2887 keys.add(listen);
2888 }
2889 else if (listen.get() instanceof KeyrefValidator)
2890 {
2891 keyrefs.add(listen);
2892 }
2893 else if (listen.get() instanceof CoupledValidator)
2894 {
2895 coupled.add(listen);
2896 }
2897 }
2898 list.removeAll(coupled);
2899 list.removeAll(keyrefs);
2900 list.removeAll(keys);
2901 list.addAll(0, coupled);
2902 list.addAll(0, keyrefs);
2903 list.addAll(0, keys);
2904 if (undo != null)
2905 {
2906 list.remove(undo);
2907 list.add(0, undo);
2908 }
2909 }
2910
2911 }