1
2
3
4
5
6
7
8 package nl.tudelft.simulation.xml;
9
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.net.URL;
13
14 import nl.tudelft.simulation.language.io.URLResource;
15 import nl.tudelft.simulation.logger.Logger;
16
17 import org.apache.xerces.parsers.DOMParser;
18 import org.jdom.Element;
19 import org.jdom.input.DOMBuilder;
20 import org.w3c.dom.Document;
21 import org.xml.sax.ErrorHandler;
22 import org.xml.sax.InputSource;
23 import org.xml.sax.SAXException;
24 import org.xml.sax.SAXParseException;
25 import org.xml.sax.XMLReader;
26 import org.xml.sax.ext.DefaultHandler2;
27 import org.xml.sax.ext.EntityResolver2;
28 import org.xml.sax.helpers.XMLReaderFactory;
29
30 /***
31 * <br>
32 * (c) copyright 2002-2005-2004 <a href="http://www.simulation.tudelft.nl">Delft
33 * University of Technology </a>, the Netherlands. <br>
34 * See for project information <a href="http://www.simulation.tudelft.nl">
35 * www.simulation.tudelft.nl </a> <br>
36 * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser
37 * General Public License (LGPL) </a>, no warranty.
38 *
39 * @version Jun 27, 2004 <br>
40 * @author <a
41 * href="mailto:a.verbraeck@tbm.tudelft.nl">Alexander
42 * Verbraeck </a>
43 */
44 public abstract class AbstractXMLParser
45 {
46 /*** schema validation on/off */
47 private boolean validateSchema;
48
49 /*** the URL of the file to parse */
50 private URL url;
51
52 /*** the URL of the schema file */
53 private URL schema;
54
55 /*** the namespace of the schema */
56 private String schemaNamespace;
57
58 /***
59 * Parses an XML file and validates it against a schema XSD. Explicitly call
60 * parse() from the extended class, after other initialization tasks have
61 * been carried out. The parse() method will, after schema validation and
62 * xml-file validation, call the parse(Element) method that has been
63 * declared in the extended class.
64 *
65 * @param url the URL of the XML file to read.
66 * @param schema the URL of the XSD schema file to validate against.
67 * @param schemaNamespace the namespace of the schema
68 * @param validateSchema whether to validate the schema file.
69 */
70 public AbstractXMLParser(final URL url, final URL schema,
71 final String schemaNamespace, final boolean validateSchema)
72 {
73 this.validateSchema = validateSchema;
74 this.url = url;
75 this.schema = schema;
76 this.schemaNamespace = schemaNamespace;
77 }
78
79 /***
80 * This method carries out the task of parsing an XML file and validates it
81 * against a schema XSD. dsol-xml now contains the 2001 definitions of the
82 * schema files: XMLSchema.xsd, XMLSchema.dtd, xml.xsd, and datatypes.dtd to
83 * validate against. When no network connection is available, these
84 * definitions can help for validation when <code>validation</code> is
85 * true.
86 *
87 * @return the JDOM root element of the XML file
88 * @throws Exception on failure
89 */
90 protected Element parse() throws Exception
91 {
92 if (this.validateSchema)
93 {
94
95 XMLReader parser = XMLReaderFactory
96 .createXMLReader("org.apache.xerces.parsers.SAXParser");
97 parser.setErrorHandler(new ValidHandler());
98
99 parser.setFeature("http://xml.org/sax/features/validation", true);
100 parser.setFeature(
101 "http://apache.org/xml/features/validation/schema", true);
102
103 parser.setEntityResolver(new RelativePathResolver());
104 parser.setErrorHandler(new ValidHandler());
105 parser.parse(this.schema.toExternalForm());
106 }
107
108 DOMParser domParser = new DOMParser();
109 domParser.setFeature("http://xml.org/sax/features/validation", true);
110 domParser.setFeature(
111 "http://apache.org/xml/features/validation/schema", true);
112 domParser
113 .setProperty(
114 "http://apache.org/xml/properties/schema/external-schemaLocation",
115 this.schemaNamespace + " "
116 + this.schema.toExternalForm());
117 domParser.setEntityResolver(new RelativePathResolver());
118 domParser.setErrorHandler(new ValidHandler());
119
120 domParser.parse(this.url.toExternalForm());
121 Document document = domParser.getDocument();
122 DOMBuilder builder = new DOMBuilder();
123 org.jdom.Document jdomDocument = builder.build(document);
124 Element xmlRootElement = jdomDocument.getRootElement();
125 return xmlRootElement;
126 }
127
128 /***
129 * The actual parsing method to implement, based on the availability of the
130 * entire JDOM tree, starting from the root element.
131 *
132 * @param xmlRootElement the root element
133 * @throws Exception on failure
134 */
135 protected abstract void parse(final Element xmlRootElement)
136 throws Exception;
137
138 /***
139 * The ValidHandler ErrorHandler gives a more extensive explanation about
140 * errors that occur during parsing. If needed, this inner class can be
141 * overridden to provide other forms of error handling. <br>
142 * <br>
143 * (c) copyright 2002-2005-2004 <a
144 * href="http://www.simulation.tudelft.nl">Delft University of Technology
145 * </a>, the Netherlands. <br>
146 * See for project information <a href="http://www.simulation.tudelft.nl">
147 * www.simulation.tudelft.nl </a> <br>
148 * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser
149 * General Public License (LGPL) </a>, no warranty.
150 *
151 * @version Jun 27, 2004 <br>
152 * @author <a
153 * href="mailto:a.verbraeck@tbm.tudelft.nl">Alexander
154 * Verbraeck </a>
155 */
156 private static class ValidHandler implements ErrorHandler
157 {
158 /***
159 * format the exception with line number, column number, etc.
160 *
161 * @param exception the excpetion to format
162 * @return the String
163 */
164 private String formatError(final SAXParseException exception)
165 {
166 return "SAXParseException: \nsystemId=" + exception.getSystemId()
167 + "\npublicId=" + exception.getSystemId() + "\nMessage="
168 + exception.getMessage() + "\nline,col="
169 + exception.getLineNumber() + ","
170 + exception.getColumnNumber();
171 }
172
173 /***
174 * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
175 */
176 public void warning(final SAXParseException exception)
177 {
178
179 Logger.warning(this, formatError(exception), exception);
180 }
181
182 /***
183 * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
184 */
185 public void error(final SAXParseException e) throws SAXException
186 {
187 throw new SAXException(formatError(e));
188 }
189
190 /***
191 * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
192 */
193 public void fatalError(final SAXParseException e) throws SAXException
194 {
195 throw new SAXException(formatError(e));
196 }
197 }
198
199 /***
200 * The relative path resolver takes care of resolving xsd-files or
201 * xml-files, that are in the current classpath. Example use is the
202 * <code>include</code> tag in the schema file, that can now point to a
203 * relative path. <br>
204 * <br>
205 * (c) copyright 2002-2005-2004 <a
206 * href="http://www.simulation.tudelft.nl">Delft University of Technology
207 * </a>, the Netherlands. <br>
208 * See for project information <a href="http://www.simulation.tudelft.nl">
209 * www.simulation.tudelft.nl </a> <br>
210 * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser
211 * General Public License (LGPL) </a>, no warranty.
212 *
213 * @version Jun 27, 2004 <br>
214 * @author <a
215 * href="mailto:a.verbraeck@tbm.tudelft.nl">Alexander
216 * Verbraeck </a>
217 */
218 private static class RelativePathResolver implements EntityResolver2
219 {
220 /*** the fallback defaultHandler2 */
221 private DefaultHandler2 defaultHandler2 = new DefaultHandler2();
222
223 /***
224 * @see org.xml.sax.ext.EntityResolver2#getExternalSubset(java.lang.String,
225 * java.lang.String)
226 */
227 public InputSource getExternalSubset(final String name,
228 final String baseURI) throws SAXException, IOException
229 {
230 return this.defaultHandler2.getExternalSubset(name, baseURI);
231 }
232
233 /***
234 * @see org.xml.sax.ext.EntityResolver2#resolveEntity(java.lang.String,
235 * java.lang.String, java.lang.String, java.lang.String)
236 */
237 public InputSource resolveEntity(final String name,
238 final String publicId, final String baseURI,
239 final String systemId) throws SAXException, IOException
240 {
241 return this.defaultHandler2.resolveEntity(name, publicId, baseURI,
242 systemId);
243 }
244
245 /***
246 * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
247 * java.lang.String)
248 */
249 public InputSource resolveEntity(final String publicId,
250 final String systemId) throws SAXException, IOException
251 {
252 URL url;
253 if (systemId.startsWith("file:"))
254 {
255 url = URLResource.getResource(systemId.substring(5));
256 } else
257 {
258 url = URLResource.getResource(systemId);
259 }
260 InputStream localStream = URLResource.getResourceAsStream(url
261 .toExternalForm());
262 if (localStream != null)
263 {
264 return new InputSource(localStream);
265 }
266 return this.defaultHandler2.resolveEntity(publicId, systemId);
267 }
268 }
269 }