001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002 *
003 * Copyright 2000-2011, 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.DefaultSourceFileExceptionList;
029 import org.qedeq.kernel.se.common.Plugin;
030 import org.qedeq.kernel.se.common.SourceFileException;
031 import org.qedeq.kernel.se.common.SourceFileExceptionList;
032 import org.qedeq.kernel.xml.common.XmlSyntaxException;
033 import org.qedeq.kernel.xml.handler.common.SaxDefaultHandler;
034 import org.qedeq.kernel.xml.handler.common.SimpleHandler;
035 import org.xml.sax.InputSource;
036 import org.xml.sax.SAXException;
037 import org.xml.sax.SAXNotRecognizedException;
038 import org.xml.sax.XMLReader;
039
040
041 /**
042 * Parser for XML files. This class uses features specific for Xerces.
043 *
044 * @author Michael Meyling
045 */
046 public final class SaxParser {
047
048 /** This class. */
049 private static final Class CLASS = SaxParser.class;
050
051 /** Namespaces feature id (http://xml.org/sax/features/namespaces). */
052 private static final String NAMESPACES_FEATURE_ID = "http://xml.org/sax/features/namespaces";
053
054 /** Validation feature id (http://xml.org/sax/features/validation). */
055 private static final String VALIDATION_FEATURE_ID = "http://xml.org/sax/features/validation";
056
057 /** Schema validation feature id (http://apache.org/xml/features/validation/schema). */
058 private static final String SCHEMA_VALIDATION_FEATURE_ID
059 = "http://apache.org/xml/features/validation/schema";
060
061 /** Schema full checking feature id
062 * (http://apache.org/xml/features/validation/schema-full-checking). */
063 protected static final String SCHEMA_FULL_CHECKING_FEATURE_ID
064 = "http://apache.org/xml/features/validation/schema-full-checking";
065
066 /** Handler which deals with the XML contents. */
067 private SaxDefaultHandler handler;
068
069 /** SAX parser. */
070 private XMLReader reader;
071
072 /** Simple handler for validation purpose only. */
073 private final SimpleHandler deflt;
074
075 /** Saved errors of parsing. */
076 private DefaultSourceFileExceptionList exceptionList;
077
078 /** Plugin we work for. */
079 private Plugin plugin;
080
081 /**
082 * Constructor.
083 *
084 * @param plugin We work for this plugin.
085 * @param handler Default handler for this application.
086 * @throws ParserConfigurationException Severe parser configuration problem.
087 * @throws SAXException Option not recognized or supported.
088 */
089 public SaxParser(final Plugin plugin, final SaxDefaultHandler handler)
090 throws ParserConfigurationException, SAXException {
091 super();
092
093 this.handler = handler;
094 this.deflt = new SimpleHandler();
095 this.plugin = plugin;
096
097 final String factoryImpl = System.getProperty("javax.xml.parsers.SAXParserFactory");
098 if (factoryImpl == null) {
099 System.setProperty("javax.xml.parsers.SAXParserFactory",
100 "org.apache.xerces.jaxp.SAXParserFactoryImpl");
101 }
102 SAXParserFactory factory = SAXParserFactory.newInstance();
103 factory.setNamespaceAware(true);
104 factory.setValidating(true);
105
106 factory.setFeature(NAMESPACES_FEATURE_ID, true);
107 factory.setFeature(VALIDATION_FEATURE_ID, true);
108
109 try {
110 factory.setFeature(SCHEMA_VALIDATION_FEATURE_ID, true);
111 } catch (SAXNotRecognizedException e) {
112 Trace.trace(CLASS, this, "constructor", e);
113 // ignore
114 }
115 try {
116 factory.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, true);
117 } catch (SAXNotRecognizedException e) {
118 Trace.trace(CLASS, this, "constructor", e);
119 // ignore
120 }
121
122 final SAXParser parser = factory.newSAXParser();
123 if (!parser.isNamespaceAware()) {
124 throw new ParserConfigurationException(
125 "Current XML parser doesn't support namespaces.");
126 }
127 if (!parser.isValidating()) {
128 throw new ParserConfigurationException(
129 "Current XML parser doesn't support schema validation.");
130 }
131
132 reader = parser.getXMLReader();
133 reader.setEntityResolver(new SaxEntityResolver(handler));
134
135 // set parser features
136 reader.setFeature(NAMESPACES_FEATURE_ID, true);
137 reader.setFeature(VALIDATION_FEATURE_ID, true);
138 try {
139 reader.setFeature(SCHEMA_VALIDATION_FEATURE_ID, true);
140 } catch (SAXNotRecognizedException e) {
141 Trace.trace(CLASS, this, "constructor", e);
142 // ignore
143 }
144 try {
145 reader.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, true);
146 } catch (SAXNotRecognizedException e) {
147 Trace.trace(CLASS, this, "constructor", e);
148 // ignore
149 }
150
151 }
152
153 /**
154 * Parse input source.
155 * @param in Parse data from this file source.
156 * @param validateOnly validate with {@link #deflt} or parse with {@link #handler}.
157 * @param original Original URL for the file. If this is <code>null</code> same as
158 * file name.
159 *
160 * @throws SourceFileExceptionList Loading failed.
161 */
162 private void parse(final File in, final boolean validateOnly, final String original)
163 throws SourceFileExceptionList {
164 final String method = "parse(URL, boolean, InputStream)";
165 InputStream stream = null;
166 exceptionList = new DefaultSourceFileExceptionList();
167 try {
168 stream = new FileInputStream(in);
169 final InputSource input = new InputSource(stream);
170 reader.setErrorHandler(new SaxErrorHandler(plugin, original, exceptionList));
171 handler.setUrl(original);
172 deflt.setUrl(original);
173 if (validateOnly) {
174 try {
175 reader.setContentHandler(deflt);
176 reader.parse(input);
177 } catch (MissingResourceException ex) {
178 throw new SAXException("For " + ex.getClassName() + " we searched for value"
179 + " of " + ex.getKey(), ex);
180 }
181 } else {
182 handler.setExceptionList(exceptionList);
183 reader.setContentHandler(handler);
184 reader.parse(input);
185 }
186 } catch (SAXException e) {
187 if (exceptionList.size() <= 0) { // do we have already exceptions?
188 // no, we must add this one
189 final XmlSyntaxException xml = XmlSyntaxException.createBySAXException(e);
190 exceptionList.add(new SourceFileException(plugin, xml, handler.createSourceArea(), null));
191 }
192 throw exceptionList;
193 } catch (IOException e) {
194 final XmlSyntaxException xml = XmlSyntaxException.createByIOException(e);
195 exceptionList.add(new SourceFileException(plugin, xml, handler.createSourceArea(), null));
196 throw exceptionList;
197 } finally {
198 if (stream != null) {
199 try {
200 stream.close();
201 } catch (Exception e) {
202 Trace.trace(CLASS, this, method, e);
203 }
204 }
205 }
206 if (exceptionList.size() > 0) {
207 throw exceptionList;
208 }
209 }
210
211 /**
212 * Parses XML file.
213 *
214 * @param fileName File name.
215 * @param original Original URL for the file. If this is <code>null</code> same as
216 * file name.
217 * @throws SourceFileExceptionList Loading failed.
218 */
219 public final void parse(final String fileName, final String original)
220 throws SourceFileExceptionList {
221 final File file = new File(fileName);
222 parse(file.getAbsoluteFile(), original);
223 }
224
225 /**
226 * Parses the XML file.
227 *
228 * @param file File to parse.
229 * @param original Original URL for the file. If this is <code>null</code> same as
230 * file.
231 * @throws SourceFileExceptionList Loading failed.
232 */
233 public final void parse(final File file, final String original) throws SourceFileExceptionList {
234 String org = original;
235 if (org == null) {
236 org = "" + file;
237 }
238 parse(file, true, org);
239 parse(file, false, org);
240 }
241
242 /**
243 * Get errors that occurred during last parsing.
244 *
245 * @return List with collected Exceptions.
246 */
247 public DefaultSourceFileExceptionList getExceptionList() {
248 return exceptionList;
249 }
250
251 /**
252 * Get encoding of XML document. This value is set during parsing the document.
253 *
254 * @return Encoding. Maybe <code>null</code>.
255 */
256 public String getEncoding() {
257 return deflt.getEncoding();
258 }
259
260 }
|