1 package org.opentrafficsim.editor;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.URI;
6 import java.util.ArrayList;
7
8 import javax.xml.parsers.DocumentBuilder;
9 import javax.xml.parsers.DocumentBuilderFactory;
10 import javax.xml.parsers.ParserConfigurationException;
11
12 import org.w3c.dom.Document;
13 import org.w3c.dom.Node;
14 import org.xml.sax.SAXException;
15
16 /**
17 * Utility class to read XSD or XML from URI. There are also methods to obtain certain information from a node.
18 * <p>
19 * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
21 * </p>
22 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
23 */
24 public final class DocumentReader
25 {
26
27 /**
28 * Private constructor.
29 */
30 private DocumentReader()
31 {
32
33 }
34
35 /**
36 * Opens an XSD or XML file.
37 * @param file file.
38 * @return document, i.e. the root of the XSD file.
39 * @throws SAXException exception
40 * @throws IOException exception
41 * @throws ParserConfigurationException exception
42 */
43 public static Document open(final URI file) throws SAXException, IOException, ParserConfigurationException
44 {
45 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
46 // dbf.setXIncludeAware(true);
47 dbf.setIgnoringComments(true);
48 dbf.setIgnoringElementContentWhitespace(true);
49 DocumentBuilder db = dbf.newDocumentBuilder();
50 Document doc = db.parse(new File(file));
51 return doc;
52 }
53
54 /**
55 * Returns an annotation value. These are defined as below, for either xsd:appinfo or xsd:documentation. All space-like
56 * characters are replaced by blanks, and consecutive blanks are removed.
57 *
58 * <pre>
59 * <xsd:sequence>
60 * <xsd:annotation>
61 * <xsd:appinfo source="name">annotates the sequence</xsd:appinfo>
62 * </xsd:annotation>
63 * </xsd:sequence>
64 * </pre>
65 *
66 * @param node node, either xsd:element or xsd:attribute.
67 * @param element either "xsd:documentation" or "xsd:appinfo".
68 * @param source name that the source attribute of the annotation should have.
69 * @return annotation value, {@code null} if not found.
70 */
71 public static String getAnnotation(final Node node, final String element, final String source)
72 {
73 for (Node child : DocumentReader.getChildren(node, "xsd:annotation"))
74 {
75 for (Node annotation : DocumentReader.getChildren(child, element))
76 {
77 String appInfoSource = DocumentReader.getAttribute(annotation, "source");
78 if (appInfoSource != null && appInfoSource.equals(source))
79 {
80 StringBuilder str = new StringBuilder();
81 for (int appIndex = 0; appIndex < annotation.getChildNodes().getLength(); appIndex++)
82 {
83 Node appInfo = annotation.getChildNodes().item(appIndex);
84 if (appInfo.getNodeName().equals("#text"))
85 {
86 str.append(appInfo.getNodeValue());
87 }
88 }
89 // tabs, line break, etc. to blanks, then remove consecutive blanks, then trailing/leading blanks
90 return str.toString().replaceAll("\\s", " ").replaceAll("\\s{2,}", " ").trim();
91 }
92 }
93 }
94 return null;
95 }
96
97 /**
98 * Returns the attribute of a node. This is short for:
99 *
100 * <pre>
101 * String value = node.hasAttributes() && node.getAttributes().getNamedItem(name) != null
102 * ? node.getAttributes().getNamedItem(name).getNodeValue() : null;
103 * </pre>
104 *
105 * @param node node.
106 * @param name attribute name.
107 * @return value of the attribute in the node.
108 */
109 public static String getAttribute(final Node node, final String name)
110 {
111 return node.hasAttributes() && node.getAttributes().getNamedItem(name) != null
112 ? node.getAttributes().getNamedItem(name).getNodeValue() : null;
113 }
114
115 /**
116 * Returns a child node of specified type. It should be a type of which there may be only one.
117 * @param node
118 * @param type child type, e.g. xsd:complexType.
119 * @return child node of specified type.
120 */
121 public static Node getChild(final Node node, final String type)
122 {
123 if (node.hasChildNodes())
124 {
125 for (int childIndex = 0; childIndex < node.getChildNodes().getLength(); childIndex++)
126 {
127 Node child = node.getChildNodes().item(childIndex);
128 if (child.getNodeName().equals(type))
129 {
130 return child;
131 }
132 }
133 }
134 return null;
135 }
136
137 /**
138 * Returns child nodes of specified type.
139 * @param node
140 * @param type child type, e.g. xsd:field.
141 * @return child nodes of specified type.
142 */
143 public static ArrayList<Node> getChildren(final Node node, final String type)
144 {
145 ArrayList<Node> children = new ArrayList<>();
146 if (node.hasChildNodes())
147 {
148 for (int childIndex = 0; childIndex < node.getChildNodes().getLength(); childIndex++)
149 {
150 Node child = node.getChildNodes().item(childIndex);
151 if (child.getNodeName().equals(type))
152 {
153 children.add(child);
154 }
155 }
156 }
157 return children;
158 }
159
160 }