001: /*
002: * XmlParserImpl.java
003: *
004: * Copyright (C) 2000-2003 Peter Graves
005: * $Id: XmlParserImpl.java,v 1.7 2003/06/25 18:30:58 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import java.io.FileInputStream;
025: import java.io.FileNotFoundException;
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.io.Reader;
029: import java.util.Stack;
030: import javax.swing.tree.DefaultMutableTreeNode;
031: import javax.swing.tree.DefaultTreeModel;
032: import javax.swing.tree.TreeModel;
033: import org.xml.sax.Attributes;
034: import org.xml.sax.ContentHandler;
035: import org.xml.sax.EntityResolver;
036: import org.xml.sax.InputSource;
037: import org.xml.sax.Locator;
038: import org.xml.sax.SAXException;
039: import org.xml.sax.SAXNotRecognizedException;
040: import org.xml.sax.SAXNotSupportedException;
041: import org.xml.sax.SAXParseException;
042: import org.xml.sax.XMLReader;
043: import org.xml.sax.helpers.DefaultHandler;
044: import org.xml.sax.helpers.XMLReaderFactory;
045:
046: public final class XmlParserImpl extends DefaultHandler implements
047: Runnable, ContentHandler, EntityResolver {
048: private static final String VALIDATION = "http://xml.org/sax/features/validation";
049:
050: private String parserClassName;
051: private boolean aelfred;
052: private final Buffer buffer;
053: private Reader reader;
054: private XMLReader xmlReader;
055: private TreeModel treeModel;
056: private Stack stack;
057: private Exception exception;
058: private DefaultMutableTreeNode current;
059: private Locator locator;
060: private FastStringBuffer output;
061:
062: public XmlParserImpl(Buffer buffer) {
063: Debug.assertTrue(buffer != null);
064: this .buffer = buffer;
065: }
066:
067: public boolean initialize() {
068: String className = Editor.preferences().getStringProperty(
069: "org.xml.sax.driver");
070: if (className == null)
071: className = System.getProperty("org.xml.sax.driver");
072: if (className != null) {
073: try {
074: xmlReader = XMLReaderFactory.createXMLReader(className);
075: } catch (Exception e) {
076: Log.debug(e);
077: }
078: }
079: if (xmlReader == null)
080: xmlReader = Utilities.getDefaultXMLReader();
081: if (xmlReader == null) {
082: parserClassName = null;
083: aelfred = false;
084: Log.error("no parser found");
085: } else {
086: parserClassName = xmlReader.getClass().getName();
087: if (parserClassName
088: .equals("org.armedbear.j.aelfred.SAXDriver"))
089: aelfred = true;
090: else
091: aelfred = false;
092: }
093: return xmlReader != null;
094: }
095:
096: public String getParserClassName() {
097: return parserClassName;
098: }
099:
100: public void setReader(Reader reader) {
101: this .reader = reader;
102: }
103:
104: public boolean enableValidation(boolean enable) {
105: if (xmlReader == null) {
106: Debug.bug();
107: return false;
108: }
109: try {
110: xmlReader.setFeature(VALIDATION, enable);
111: } catch (SAXNotRecognizedException e) {
112: Log.error(e);
113: return false;
114: } catch (SAXNotSupportedException e) {
115: Log.error(e);
116: return false;
117: }
118: return true;
119: }
120:
121: private boolean isValidating() {
122: if (xmlReader == null) {
123: Debug.bug();
124: return false;
125: }
126: try {
127: return xmlReader.getFeature(VALIDATION);
128: } catch (SAXNotRecognizedException e) {
129: } catch (SAXNotSupportedException e) {
130: }
131: return false;
132: }
133:
134: public Exception getException() {
135: return exception;
136: }
137:
138: public String getOutput() {
139: return output != null ? output.toString() : "";
140: }
141:
142: public void run() {
143: if (xmlReader == null) {
144: Debug.bug();
145: initialize();
146: }
147:
148: if (xmlReader == null) {
149: Log.error("no XML reader available");
150: return;
151: }
152:
153: exception = null;
154: output = new FastStringBuffer();
155:
156: final boolean validating = isValidating();
157:
158: output.append("Using ");
159: output.append(xmlReader.getClass().getName());
160: output.append(" (");
161: if (!validating)
162: output.append("not ");
163: output.append("validating)\n");
164:
165: // Parser must be associated with a buffer.
166: if (buffer == null) {
167: Debug.bug();
168: return;
169: }
170:
171: InputSource inputSource = null;
172: final File file = buffer.getFile();
173: if (reader != null) {
174: inputSource = new InputSource(reader);
175: } else if (file != null) {
176: try {
177: InputStream inputStream = file.getInputStream();
178: if (inputStream != null) {
179: inputSource = new InputSource(inputStream);
180: String encoding = file.getEncoding();
181: if (encoding != null) {
182: inputSource.setEncoding(encoding);
183: Log.debug("parser encoding is " + encoding);
184: }
185: }
186: } catch (IOException e) {
187: Log.error(e);
188: }
189: }
190: if (inputSource == null)
191: return;
192: if (file != null) {
193: if (file.isRemote())
194: inputSource.setSystemId(file.netPath());
195: else
196: inputSource.setSystemId("file://".concat(file
197: .canonicalPath()));
198: }
199: treeModel = null;
200: stack = new Stack();
201:
202: if (xmlReader != null) {
203: xmlReader.setContentHandler(this );
204: xmlReader.setErrorHandler(this );
205: xmlReader.setEntityResolver(this );
206: long start = System.currentTimeMillis();
207: try {
208: xmlReader.parse(inputSource);
209: } catch (Exception e) {
210: exception = e;
211: }
212: long elapsed = System.currentTimeMillis() - start;
213: output.append('\n');
214: output.append(validating ? "Validation" : "Parsing");
215: output.append(" finished (");
216: output.append(elapsed);
217: output.append(" ms)");
218: }
219: }
220:
221: public InputSource resolveEntity(String publicId, String systemId) {
222: if (systemId == null)
223: return null;
224: if (Platform.isPlatformWindows()
225: && systemId.startsWith("file://")) {
226: // Strip "file://" prefix.
227: String filename = systemId.substring(7);
228: // Make sure there's a colon after the drive letter.
229: if (filename.length() > 2 && filename.charAt(1) == '/')
230: filename = filename.substring(0, 1) + ':'
231: + filename.substring(1);
232: // Make sure the slashes are pointing the right way.
233: filename = File.normalize(filename);
234: try {
235: return new InputSource(new FileInputStream(filename));
236: } catch (FileNotFoundException e) {
237: }
238: // FileNotFoundException was thrown.
239: if (filename.length() > 3 && filename.charAt(1) == ':'
240: && filename.charAt(2) == '\\') {
241: // Try relative to buffer's directory.
242: File file = File.getInstance(buffer.getFile()
243: .getParentFile(), filename.substring(3));
244: try {
245: return new InputSource(new FileInputStream(file
246: .canonicalPath()));
247: } catch (Exception e) {
248: }
249: }
250: }
251: if (aelfred) {
252: // There's no way to tell aelfred about new system identifiers, so
253: // URLs relative to the new entity won't be resolved correctly if
254: // we return an input stream here. Don't use caching if we're
255: // using aelfred!
256: Log.debug("using aelfred - cache not supported");
257: return null;
258: }
259: if (!buffer.getBooleanProperty(Property.ENABLE_CACHE)) {
260: Log.debug("cache disabled");
261: return null;
262: }
263: if (systemId.startsWith("http://")) {
264: Cache cache = Cache.getCache();
265: if (cache != null) {
266: Log.debug("checking cache for ".concat(systemId));
267: File file = cache.get(systemId);
268: if (file == null) {
269: Log.debug("caching ".concat(systemId));
270: file = cache.put(systemId);
271: }
272: if (file != null) {
273: try {
274: Log.debug("returning input stream from cache");
275: InputSource inputSource = new InputSource(file
276: .getInputStream());
277: inputSource.setSystemId(systemId);
278: return inputSource;
279: } catch (Exception e) {
280: Log.error(e);
281: }
282: }
283: }
284: }
285: return null;
286: }
287:
288: public void setDocumentLocator(Locator locator) {
289: this .locator = locator;
290: }
291:
292: public void startElement(String uri, String localName,
293: String qName, Attributes attributes) throws SAXException {
294: int lineNumber = 0;
295: int columnNumber = 0;
296: if (locator != null) {
297: lineNumber = locator.getLineNumber();
298: columnNumber = locator.getColumnNumber();
299: }
300: DefaultMutableTreeNode node = new DefaultMutableTreeNode(
301: new XmlTreeElement(localName, attributes, lineNumber,
302: columnNumber));
303: if (treeModel == null) {
304: treeModel = new DefaultTreeModel(node);
305: } else {
306: Debug.assertTrue(current != null);
307: current.insert(node, current.getChildCount());
308: stack.push(current);
309: }
310: current = node;
311: }
312:
313: public void endElement(String uri, String localName, String qName) {
314: if (stack.empty())
315: current = null;
316: else
317: current = (DefaultMutableTreeNode) stack.pop();
318: }
319:
320: public void warning(SAXParseException e) throws SAXException {
321: appendMessage("Warning", e);
322: }
323:
324: public void error(SAXParseException e) throws SAXException {
325: appendMessage("Error", e);
326: }
327:
328: public void fatalError(SAXParseException e) throws SAXException {
329: appendMessage("Fatal error", e);
330: }
331:
332: private void appendMessage(String what, SAXParseException e) {
333: FastStringBuffer sb = new FastStringBuffer();
334: final String systemId = e.getSystemId();
335: final int lineNumber = e.getLineNumber();
336: if (systemId.startsWith("file://")) {
337: sb.append(systemId.substring(7));
338: sb.append(':');
339: sb.append(lineNumber);
340: } else if (systemId.startsWith("file:")) {
341: sb.append(systemId.substring(5));
342: sb.append(':');
343: sb.append(lineNumber);
344: } else {
345: sb.append(systemId);
346: sb.append(" line ");
347: sb.append(lineNumber);
348: }
349: sb.append(": ");
350: sb.append(what);
351: sb.append(": ");
352: sb.append(e.getMessage());
353: sb.append('\n');
354: output.append(sb.toString());
355: }
356:
357: public TreeModel getTreeModel() {
358: return treeModel;
359: }
360: }
|