1 package org.opentrafficsim.editor;
2
3 import java.rmi.RemoteException;
4 import java.util.ArrayList;
5 import java.util.LinkedHashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.regex.Pattern;
9
10 import org.djutils.event.Event;
11 import org.djutils.event.EventListener;
12 import org.djutils.exceptions.Throw;
13 import org.djutils.immutablecollections.Immutable;
14 import org.djutils.immutablecollections.ImmutableArrayList;
15 import org.djutils.immutablecollections.ImmutableList;
16 import org.opentrafficsim.editor.decoration.validation.XsdAllValidator;
17 import org.w3c.dom.Node;
18
19
20
21
22
23
24
25
26
27 public final class XsdTreeNodeUtil
28 {
29
30
31 private static final Pattern UPPER_PATTERN = Pattern.compile("(?=\\p{Lu})(?<=\\p{Ll})|(?=\\p{Lu}\\p{Ll})");
32
33
34 private static final Map<String, XsdAllValidator> XSD_ALL_VALIDATORS = new LinkedHashMap<>();
35
36
37
38
39 private XsdTreeNodeUtil()
40 {
41
42 }
43
44
45
46
47
48
49 public static void addXsdAllValidator(final XsdTreeNode shared, final XsdTreeNode node)
50 {
51 String path = shared.getPathString();
52 XsdAllValidator validator = XSD_ALL_VALIDATORS.computeIfAbsent(path, (p) -> new XsdAllValidator(node.getRoot()));
53 node.addNodeValidator(validator);
54 validator.addNode(node);
55 }
56
57
58
59
60
61
62
63 static int getOccurs(final Node node, final String attribute)
64 {
65 String occurs = DocumentReader.getAttribute(node, attribute);
66 if (occurs == null)
67 {
68 return 1;
69 }
70 if ("unbounded".equals(occurs))
71 {
72 return -1;
73 }
74 return Integer.valueOf(occurs);
75 }
76
77
78
79
80
81
82
83
84
85
86
87
88
89 static void addChildren(final Node node, final XsdTreeNode parentNode, final List<XsdTreeNode> children,
90 final ImmutableList<Node> hiddenNodes, final Schema schema, final boolean flattenSequence, final int skip)
91 {
92 int skipIndex = skip;
93 XsdTreeNode root = parentNode.getRoot();
94 for (int childIndex = 0; childIndex < node.getChildNodes().getLength(); childIndex++)
95 {
96 Node child = node.getChildNodes().item(childIndex);
97 switch (child.getNodeName())
98 {
99 case "xsd:element":
100 if (children.size() == skipIndex)
101 {
102 skipIndex = -1;
103 break;
104 }
105 XsdTreeNode element;
106 String ref = DocumentReader.getAttribute(child, "ref");
107 String type = DocumentReader.getAttribute(child, "type");
108 if (ref != null)
109 {
110 element = new XsdTreeNode(parentNode, XsdTreeNodeUtil.ref(child, ref, schema),
111 XsdTreeNodeUtil.append(hiddenNodes, node), child);
112 }
113 else if (type != null)
114 {
115 Node typedNode = XsdTreeNodeUtil.type(child, type, schema);
116 if (typedNode == null)
117 {
118
119 element = new XsdTreeNode(parentNode, child, XsdTreeNodeUtil.append(hiddenNodes, node));
120 }
121 else
122 {
123 element = new XsdTreeNode(parentNode, typedNode, XsdTreeNodeUtil.append(hiddenNodes, node), child);
124 }
125 }
126 else
127 {
128 element = new XsdTreeNode(parentNode, child, XsdTreeNodeUtil.append(hiddenNodes, node));
129 }
130 children.add(element);
131 root.fireEvent(XsdTreeNodeRoot.NODE_CREATED,
132 new Object[] {element, parentNode, parentNode.children.indexOf(element)});
133 break;
134 case "xsd:sequence":
135 if (children.size() == skipIndex)
136 {
137 skipIndex = -1;
138 break;
139 }
140 if (flattenSequence)
141 {
142 addChildren(child, parentNode, children, XsdTreeNodeUtil.append(hiddenNodes, node), schema,
143 flattenSequence, -1);
144 }
145 else
146 {
147
148 XsdTreeNode sequence = new XsdTreeNode(parentNode, child, XsdTreeNodeUtil.append(hiddenNodes, node));
149 children.add(sequence);
150 root.fireEvent(XsdTreeNodeRoot.NODE_CREATED,
151 new Object[] {sequence, parentNode, parentNode.children.indexOf(sequence)});
152 }
153 break;
154 case "xsd:choice":
155 case "xsd:all":
156 if (children.size() == skipIndex)
157 {
158 skipIndex = -1;
159 break;
160 }
161 XsdTreeNode choice = new XsdTreeNode(parentNode, child, XsdTreeNodeUtil.append(hiddenNodes, node));
162 root.fireEvent(XsdTreeNodeRoot.NODE_CREATED,
163 new Object[] {choice, parentNode, parentNode.children.indexOf(choice)});
164 choice.createOptions();
165
166
167
168
169
170
171 children.add(choice);
172 choice.setOption(choice.options.get(0));
173 break;
174 case "xsd:extension":
175 if (children.size() == skipIndex)
176 {
177 skipIndex = -1;
178 break;
179 }
180 XsdTreeNode extension = new XsdTreeNode(parentNode, child, XsdTreeNodeUtil.append(hiddenNodes, node));
181 root.fireEvent(XsdTreeNodeRoot.NODE_CREATED,
182 new Object[] {extension, parentNode, parentNode.children.indexOf(extension)});
183 children.add(extension);
184 break;
185 case "xsd:attribute":
186 case "xsd:annotation":
187 case "xsd:simpleType":
188 case "xsd:restriction":
189 case "xsd:simpleContent":
190 case "xsd:union":
191 case "#text":
192
193 break;
194 default:
195 System.out.println("Ignoring a " + child.getNodeName());
196 }
197 }
198 }
199
200
201
202
203
204
205
206 static ImmutableList<Node> append(final ImmutableList<Node> hiddenNodes, final Node node)
207 {
208 List<Node> list = new ArrayList<>(hiddenNodes.size() + 1);
209 list.addAll(hiddenNodes.toCollection());
210 list.add(node);
211 return new ImmutableArrayList<>(list, Immutable.WRAP);
212 }
213
214
215
216
217
218
219 static List<String> getOptionsFromRestrictions(final List<Node> restrictions)
220 {
221 List<String> options = new ArrayList<>();
222 for (Node restriction : restrictions)
223 {
224 List<Node> enumerations = DocumentReader.getChildren(restriction, "xsd:enumeration");
225 for (Node enumeration : enumerations)
226 {
227 options.add(DocumentReader.getAttribute(enumeration, "value"));
228 }
229 }
230 return options;
231 }
232
233
234
235
236
237
238
239 protected static void fireCreatedEventOnExistingNodes(final XsdTreeNode node, final EventListener listener)
240 throws RemoteException
241 {
242 List<XsdTreeNode> subNodes = node.children == null ? new ArrayList<>() : new ArrayList<>(node.children);
243
244 if (node.choice != null && node.choice.selected.equals(node))
245 {
246 subNodes.add(node.choice);
247 subNodes.addAll(node.choice.options);
248 subNodes.remove(node);
249 }
250 for (XsdTreeNode child : subNodes)
251 {
252 fireCreatedEventOnExistingNodes(child, listener);
253 }
254 Event event = new Event(XsdTreeNodeRoot.NODE_CREATED, new Object[] {node, node.getParent(), subNodes.indexOf(node)});
255 listener.notify(event);
256 }
257
258
259
260
261
262
263
264 static int resolveInsertion(final int insertIndex, final int removeIndex)
265 {
266 int tmp = insertIndex < 0 ? removeIndex : insertIndex;
267 return tmp < removeIndex ? tmp : removeIndex;
268 }
269
270
271
272
273
274
275
276
277
278
279
280
281 static Map<Node, ImmutableList<Node>> getRelevantNodesWithChildren(final Node node, final ImmutableList<Node> hiddenNodes,
282 final Schema schema)
283 {
284 Node complexType =
285 node.getNodeName().equals("xsd:complexType") ? node : DocumentReader.getChild(node, "xsd:complexType");
286 if (complexType != null)
287 {
288 Node sequence = DocumentReader.getChild(complexType, "xsd:sequence");
289 if (sequence != null)
290 {
291 return Map.of(sequence, append(hiddenNodes, complexType));
292 }
293 Node complexContent = DocumentReader.getChild(complexType, "xsd:complexContent");
294 if (complexContent != null)
295 {
296 Node extension = DocumentReader.getChild(complexContent, "xsd:extension");
297 if (extension != null)
298 {
299 ImmutableList<Node> hiddenExtension = append(append(hiddenNodes, complexType), complexContent);
300 LinkedHashMap<Node, ImmutableList<Node>> elements = new LinkedHashMap<>();
301 String base = DocumentReader.getAttribute(extension, "base");
302 if (base != null)
303 {
304 Node baseNode = schema.getType(base);
305 if (baseNode != null)
306 {
307 elements.putAll(getRelevantNodesWithChildren(baseNode, append(hiddenExtension, extension), schema));
308 }
309 }
310 elements.put(extension, hiddenExtension);
311 return elements;
312 }
313 }
314 return Map.of(complexType, hiddenNodes);
315 }
316 return Map.of(node, hiddenNodes);
317 }
318
319
320
321
322
323
324
325
326 static boolean haveSameType(final XsdTreeNode node1, final XsdTreeNode node2)
327 {
328 return (node1.referringXsdNode != null && node1.referringXsdNode.equals(node2.referringXsdNode))
329 || (node1.referringXsdNode == null && node1.xsdNode.equals(node2.xsdNode));
330 }
331
332
333
334
335
336
337
338
339
340 static Node ref(final Node node, final String ref, final Schema schema)
341 {
342 if (ref.equals("xi:include"))
343 {
344 return XiIncludeNode.XI_INCLUDE;
345 }
346 Node refNode = schema.getElement(ref);
347 Throw.when(refNode == null, RuntimeException.class, "Unable to load ref for %s from XSD schema.", ref);
348 return refNode;
349 }
350
351
352
353
354
355
356
357
358
359 static Node type(final Node node, final String type, final Schema schema)
360 {
361 if (type.startsWith("xsd:"))
362 {
363 return null;
364 }
365 Node typeNode = schema.getType(type);
366 Throw.when(typeNode == null, RuntimeException.class, "Unable to load type for %s from XSD schema.", type);
367 return typeNode;
368 }
369
370
371
372
373
374
375 static String separatedName(final String name)
376 {
377 String[] parts = UPPER_PATTERN.split(name);
378 if (parts.length == 1)
379 {
380 return parts[0];
381 }
382 String separator = "";
383 StringBuilder stringBuilder = new StringBuilder();
384 for (String part : parts)
385 {
386 stringBuilder.append(separator).append(part);
387 separator = " ";
388 }
389 return stringBuilder.toString();
390 }
391
392
393
394
395
396
397
398 public static boolean valuesAreEqual(final String value1, final String value2)
399 {
400 boolean value1Empty = value1 == null || value1.isEmpty();
401 boolean value2Empty = value2 == null || value2.isEmpty();
402 return (value1Empty && value2Empty) || (value1 != null && value1.equals(value2));
403 }
404
405
406
407
408
409 protected static final class LoadingIndices
410 {
411
412 private int xmlNode;
413
414
415 private int xsdTreeNode;
416
417
418
419
420
421
422 public LoadingIndices(final int xmlNode, final int xsdTreeNode)
423 {
424 this.xmlNode = xmlNode;
425 this.xsdTreeNode = xsdTreeNode;
426 }
427
428
429
430
431
432 public int getXmlNode()
433 {
434 return this.xmlNode;
435 }
436
437
438
439
440
441 public void setXmlNode(final int xmlNode)
442 {
443 this.xmlNode = xmlNode;
444 }
445
446
447
448
449
450 public int getXsdTreeNode()
451 {
452 return this.xsdTreeNode;
453 }
454
455
456
457
458
459 public void setXsdTreeNode(final int xsdTreeNode)
460 {
461 this.xsdTreeNode = xsdTreeNode;
462 }
463 }
464
465 }