View Javadoc

1   /*
2    * @(#)AbstractXMLParser.java Jun 27, 2004 Copyright (c) 2002-2005, 2004 Delft
3    * University of Technology Jaffalaan 5, 2628 BX Delft, the Netherlands. All
4    * rights reserved. This software is proprietary information of Delft University
5    * of Technology The code is published under the Lesser General Public License
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              // create the parser to validate the schema file with SAX
95              XMLReader parser = XMLReaderFactory
96                      .createXMLReader("org.apache.xerces.parsers.SAXParser");
97              parser.setErrorHandler(new ValidHandler());
98              // turns on Schema Validation with Xerces
99              parser.setFeature("http://xml.org/sax/features/validation", true);
100             parser.setFeature(
101                     "http://apache.org/xml/features/validation/schema", true);
102             // fatal error handler
103             parser.setEntityResolver(new RelativePathResolver());
104             parser.setErrorHandler(new ValidHandler());
105             parser.parse(this.schema.toExternalForm());
106         }
107         // build the DOM document for the content -- validation on
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         // System.exit(0);
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             // ignore, but log
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 }