View Javadoc

1   /* This file is part of the project "Hilbert II" - http://www.qedeq.org" target="alexandria_uri">http://www.qedeq.org
2    *
3    * Copyright 2000-2014,  Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU General Public License for more details.
14   */
15  package org.qedeq.kernel.xml.parser;
16  
17  import java.io.File;
18  import java.io.FileInputStream;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.MissingResourceException;
22  
23  import javax.xml.parsers.ParserConfigurationException;
24  import javax.xml.parsers.SAXParser;
25  import javax.xml.parsers.SAXParserFactory;
26  
27  import org.qedeq.base.trace.Trace;
28  import org.qedeq.kernel.se.common.ModuleService;
29  import org.qedeq.kernel.se.common.SourceFileException;
30  import org.qedeq.kernel.se.common.SourceFileExceptionList;
31  import org.qedeq.kernel.xml.common.XmlSyntaxException;
32  import org.qedeq.kernel.xml.handler.common.SaxDefaultHandler;
33  import org.qedeq.kernel.xml.handler.common.SimpleHandler;
34  import org.xml.sax.InputSource;
35  import org.xml.sax.SAXException;
36  import org.xml.sax.SAXNotRecognizedException;
37  import org.xml.sax.XMLReader;
38  
39  
40  /**
41   * Parser for XML files. This class uses features specific for Xerces.
42   *
43   * @author Michael Meyling
44   */
45  public final class SaxParser {
46  
47      /** This class. */
48      private static final Class CLASS = SaxParser.class;
49  
50      /** Namespaces feature id (http://xml.org/sax/features/namespaces)." target="alexandria_uri">http://xml.org/sax/features/namespaces). */
51      private static final String NAMESPACES_FEATURE_ID = "http://xml.org/sax/features/namespaces";
52  
53      /** Validation feature id (http://xml.org/sax/features/validation)." target="alexandria_uri">http://xml.org/sax/features/validation). */
54      private static final String VALIDATION_FEATURE_ID = "http://xml.org/sax/features/validation";
55  
56      /** Schema validation feature id (http://apache.org/xml/features/validation/schema)." target="alexandria_uri">http://apache.org/xml/features/validation/schema). */
57      private static final String SCHEMA_VALIDATION_FEATURE_ID
58          = "http://apache.org/xml/features/validation/schema";
59  
60      /** Schema full checking feature id
61       * (http://apache.org/xml/features/validation/schema-full-checking). */
62      protected static final String SCHEMA_FULL_CHECKING_FEATURE_ID
63          = "http://apache.org/xml/features/validation/schema-full-checking";
64  
65      /** Handler which deals with the XML contents. */
66      private SaxDefaultHandler handler;
67  
68      /** SAX parser. */
69      private XMLReader reader;
70  
71      /** Simple handler for validation purpose only. */
72      private final SimpleHandler deflt;
73  
74      /** Saved errors of parsing. */
75      private SourceFileExceptionList exceptionList;
76  
77      /** Plugin we work for. */
78      private ModuleService plugin;
79  
80      /**
81       * Constructor.
82       *
83       * @param   plugin    We work for this plugin.
84       * @param   handler   Default handler for this application.
85       * @throws  ParserConfigurationException    Severe parser configuration problem.
86       * @throws  SAXException                    Option not recognized or supported.
87       */
88      public SaxParser(final ModuleService plugin, final SaxDefaultHandler handler)
89              throws ParserConfigurationException, SAXException {
90          super();
91  
92          this.handler = handler;
93          this.deflt = new SimpleHandler();
94          this.plugin = plugin;
95  
96          final String factoryImpl = System.getProperty("javax.xml.parsers.SAXParserFactory");
97          if (factoryImpl == null) {
98              System.setProperty("javax.xml.parsers.SAXParserFactory",
99                  "org.apache.xerces.jaxp.SAXParserFactoryImpl");
100         }
101         SAXParserFactory factory = SAXParserFactory.newInstance();
102         factory.setNamespaceAware(true);
103         factory.setValidating(true);
104 
105         factory.setFeature(NAMESPACES_FEATURE_ID, true);
106         factory.setFeature(VALIDATION_FEATURE_ID, true);
107 
108         try {
109             factory.setFeature(SCHEMA_VALIDATION_FEATURE_ID, true);
110         } catch (SAXNotRecognizedException e) {
111             Trace.trace(CLASS, this, "constructor", e);
112             // ignore
113         }
114         try {
115             factory.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, true);
116         } catch (SAXNotRecognizedException e) {
117             Trace.trace(CLASS, this, "constructor", e);
118             // ignore
119         }
120 
121         final SAXParser parser = factory.newSAXParser();
122         if (!parser.isNamespaceAware()) {
123             throw new ParserConfigurationException(
124                 "Current XML parser doesn't support namespaces.");
125         }
126         if (!parser.isValidating()) {
127             throw new ParserConfigurationException(
128                 "Current XML parser doesn't support schema validation.");
129         }
130 
131         reader = parser.getXMLReader();
132         reader.setEntityResolver(new SaxEntityResolver(handler));
133 
134         // set parser features
135         reader.setFeature(NAMESPACES_FEATURE_ID, true);
136         reader.setFeature(VALIDATION_FEATURE_ID, true);
137         try {
138             reader.setFeature(SCHEMA_VALIDATION_FEATURE_ID, true);
139         } catch (SAXNotRecognizedException e) {
140             Trace.trace(CLASS, this, "constructor", e);
141             // ignore
142         }
143         try {
144             reader.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, true);
145         } catch (SAXNotRecognizedException e) {
146             Trace.trace(CLASS, this, "constructor", e);
147             // ignore
148         }
149 
150     }
151 
152     /**
153      * Parse input source.
154      * @param   in              Parse data from this file source.
155      * @param   validateOnly    validate with {@link #deflt} or parse with {@link #handler}.
156      * @param   original        Original URL for the file. If this is <code>null</code> same as
157      *                          file name.
158      *
159      * @throws  SourceFileExceptionList    Loading failed.
160      */
161     private void parse(final File in, final boolean validateOnly, final String original)
162             throws SourceFileExceptionList {
163         final String method = "parse(URL, boolean, InputStream)";
164         InputStream stream = null;
165         exceptionList = new SourceFileExceptionList();
166         try {
167             stream = new FileInputStream(in);
168             final InputSource input = new InputSource(stream);
169             reader.setErrorHandler(new SaxErrorHandler(plugin, original, exceptionList));
170             handler.setUrl(original);
171             deflt.setUrl(original);
172             if (validateOnly) {
173                 try {
174                     reader.setContentHandler(deflt);
175                     reader.parse(input);
176                 } catch (MissingResourceException ex) {
177                     throw new SAXException("For " + ex.getClassName() + " we searched for value"
178                         + " of " + ex.getKey(), ex);
179                 }
180             } else {
181                 handler.setExceptionList(exceptionList);
182                 reader.setContentHandler(handler);
183                 reader.parse(input);
184             }
185         } catch (SAXException e) {
186             if (exceptionList.size() <= 0) {    // do we have already exceptions?
187                 // no, we must add this one
188                 final XmlSyntaxException xml = XmlSyntaxException.createBySAXException(e);
189                 exceptionList.add(new SourceFileException(plugin, xml, handler.createSourceArea(), null));
190             }
191             throw exceptionList;
192         } catch (IOException e) {
193             final XmlSyntaxException xml = XmlSyntaxException.createByIOException(e);
194             exceptionList.add(new SourceFileException(plugin, xml, handler.createSourceArea(), null));
195             throw exceptionList;
196         } finally {
197             if (stream != null) {
198                 try {
199                     stream.close();
200                 } catch (Exception e) {
201                     Trace.trace(CLASS, this, method, e);
202                 }
203             }
204         }
205         if (exceptionList.size() > 0) {
206             throw exceptionList;
207         }
208     }
209 
210     /**
211      * Parses XML file.
212      *
213      * @param   fileName        File name.
214      * @param   original        Original URL for the file. If this is <code>null</code> same as
215      *                          file name.
216      * @throws  SourceFileExceptionList    Loading failed.
217      */
218     public final void parse(final String fileName, final String original)
219             throws SourceFileExceptionList {
220         final File file = new File(fileName);
221         parse(file.getAbsoluteFile(), original);
222     }
223 
224     /**
225      * Parses the XML file.
226      *
227      * @param   file            File to parse.
228      * @param   original        Original URL for the file. If this is <code>null</code> same as
229      *                          file.
230      * @throws  SourceFileExceptionList    Loading failed.
231      */
232     public final void parse(final File file, final String original) throws SourceFileExceptionList {
233         String org = original;
234         if (org == null) {
235             org = "" + file;
236         }
237         parse(file, true, org);
238         parse(file, false, org);
239     }
240 
241     /**
242      * Get errors that occurred during last parsing.
243      *
244      * @return  List with collected Exceptions.
245      */
246     public SourceFileExceptionList getExceptionList() {
247         return exceptionList;
248     }
249 
250     /**
251      * Get encoding of XML document. This value is set during parsing the document.
252      *
253      * @return  Encoding. Maybe <code>null</code>.
254      */
255     public String getEncoding() {
256         return deflt.getEncoding();
257     }
258 
259 }