001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002 *
003 * Copyright 2000-2013, Michael Meyling <mime@qedeq.org>.
004 *
005 * "Hilbert II" is free software; you can redistribute
006 * it and/or modify it under the terms of the GNU General Public
007 * License as published by the Free Software Foundation; either
008 * version 2 of the License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU General Public License for more details.
014 */
015 package org.qedeq.kernel.xml.parser;
016
017 import java.io.File;
018 import java.io.FileInputStream;
019 import java.io.IOException;
020 import java.io.InputStream;
021 import java.util.MissingResourceException;
022
023 import javax.xml.parsers.ParserConfigurationException;
024 import javax.xml.parsers.SAXParser;
025 import javax.xml.parsers.SAXParserFactory;
026
027 import org.qedeq.base.trace.Trace;
028 import org.qedeq.kernel.se.common.Plugin;
029 import org.qedeq.kernel.se.common.SourceFileException;
030 import org.qedeq.kernel.se.common.SourceFileExceptionList;
031 import org.qedeq.kernel.xml.common.XmlSyntaxException;
032 import org.qedeq.kernel.xml.handler.common.SaxDefaultHandler;
033 import org.qedeq.kernel.xml.handler.common.SimpleHandler;
034 import org.xml.sax.InputSource;
035 import org.xml.sax.SAXException;
036 import org.xml.sax.SAXNotRecognizedException;
037 import org.xml.sax.XMLReader;
038
039
040 /**
041 * Parser for XML files. This class uses features specific for Xerces.
042 *
043 * @author Michael Meyling
044 */
045 public final class SaxParser {
046
047 /** This class. */
048 private static final Class CLASS = SaxParser.class;
049
050 /** Namespaces feature id (http://xml.org/sax/features/namespaces). */
051 private static final String NAMESPACES_FEATURE_ID = "http://xml.org/sax/features/namespaces";
052
053 /** Validation feature id (http://xml.org/sax/features/validation). */
054 private static final String VALIDATION_FEATURE_ID = "http://xml.org/sax/features/validation";
055
056 /** Schema validation feature id (http://apache.org/xml/features/validation/schema). */
057 private static final String SCHEMA_VALIDATION_FEATURE_ID
058 = "http://apache.org/xml/features/validation/schema";
059
060 /** Schema full checking feature id
061 * (http://apache.org/xml/features/validation/schema-full-checking). */
062 protected static final String SCHEMA_FULL_CHECKING_FEATURE_ID
063 = "http://apache.org/xml/features/validation/schema-full-checking";
064
065 /** Handler which deals with the XML contents. */
066 private SaxDefaultHandler handler;
067
068 /** SAX parser. */
069 private XMLReader reader;
070
071 /** Simple handler for validation purpose only. */
072 private final SimpleHandler deflt;
073
074 /** Saved errors of parsing. */
075 private SourceFileExceptionList exceptionList;
076
077 /** Plugin we work for. */
078 private Plugin plugin;
079
080 /**
081 * Constructor.
082 *
083 * @param plugin We work for this plugin.
084 * @param handler Default handler for this application.
085 * @throws ParserConfigurationException Severe parser configuration problem.
086 * @throws SAXException Option not recognized or supported.
087 */
088 public SaxParser(final Plugin plugin, final SaxDefaultHandler handler)
089 throws ParserConfigurationException, SAXException {
090 super();
091
092 this.handler = handler;
093 this.deflt = new SimpleHandler();
094 this.plugin = plugin;
095
096 final String factoryImpl = System.getProperty("javax.xml.parsers.SAXParserFactory");
097 if (factoryImpl == null) {
098 System.setProperty("javax.xml.parsers.SAXParserFactory",
099 "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 }
|