View Javadoc
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 URI; file.
38       * @return Document; 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       * &lt;xsd:sequence&gt;
60       *   &lt;xsd:annotation&gt;
61       *     &lt;xsd:appinfo source="name"&gt;annotates the sequence&lt;/xsd:appinfo&gt;
62       *   &lt;/xsd:annotation&gt;
63       * &lt;/xsd:sequence&gt;
64       * </pre>
65       * 
66       * @param node Node; node, either xsd:element or xsd:attribute.
67       * @param element String; either "xsd:documentation" or "xsd:appinfo".
68       * @param source String; name that the source attribute of the annotation should have.
69       * @return String; 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() &amp;&amp; node.getAttributes().getNamedItem(name) != null
102      *         ? node.getAttributes().getNamedItem(name).getNodeValue() : null;
103      * </pre>
104      * 
105      * @param node Node; node.
106      * @param name String; attribute name.
107      * @return String; 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 Node node;
118      * @param type String; child type, e.g. xsd:complexType.
119      * @return Node; 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 Node node;
140      * @param type String; child type, e.g. xsd:field.
141      * @return ArayList&lt;Node&gt;; 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 }