001: // SAXCatalogReader.java - Read XML Catalog files
002:
003: /*
004: * Copyright 2001-2004 The Apache Software Foundation or its licensors,
005: * as applicable.
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: package com.sun.org.apache.xml.internal.resolver.readers;
021:
022: import java.util.Hashtable;
023: import java.io.IOException;
024: import java.io.FileNotFoundException;
025: import java.io.InputStream;
026: import java.net.URL;
027: import java.net.URLConnection;
028: import java.net.MalformedURLException;
029: import java.net.UnknownHostException;
030:
031: import javax.xml.parsers.ParserConfigurationException;
032: import javax.xml.parsers.SAXParserFactory;
033: import javax.xml.parsers.SAXParser;
034:
035: import org.xml.sax.AttributeList;
036: import org.xml.sax.Attributes;
037: import org.xml.sax.ContentHandler;
038: import org.xml.sax.DocumentHandler;
039: import org.xml.sax.EntityResolver;
040: import org.xml.sax.InputSource;
041: import org.xml.sax.Locator;
042: import org.xml.sax.Parser;
043: import org.xml.sax.SAXException;
044:
045: import com.sun.org.apache.xml.internal.resolver.Catalog;
046: import com.sun.org.apache.xml.internal.resolver.CatalogManager;
047: import com.sun.org.apache.xml.internal.resolver.CatalogException;
048: import com.sun.org.apache.xml.internal.resolver.readers.CatalogReader;
049: import com.sun.org.apache.xml.internal.resolver.helpers.Debug;
050:
051: /**
052: * A SAX-based CatalogReader.
053: *
054: * <p>This class is used to read XML Catalogs using the SAX. This reader
055: * has an advantage over the DOM-based reader in that it functions on
056: * the stream of SAX events. It has the disadvantage
057: * that it cannot look around in the tree.</p>
058: *
059: * <p>Since the choice of CatalogReaders (in the InputStream case) can only
060: * be made on the basis of MIME type, the following problem occurs: only
061: * one CatalogReader can exist for all XML mime types. In order to get
062: * around this problem, the SAXCatalogReader relies on a set of external
063: * CatalogParsers to actually build the catalog.</p>
064: *
065: * <p>The selection of CatalogParsers is made on the basis of the QName
066: * of the root element of the document.</p>
067: *
068: * @see Catalog
069: * @see CatalogReader
070: * @see SAXCatalogReader
071: * @see TextCatalogReader
072: * @see DOMCatalogParser
073: *
074: * @author Norman Walsh
075: * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
076: *
077: * @version 1.0
078: */
079: public class SAXCatalogReader implements CatalogReader, ContentHandler,
080: DocumentHandler {
081: /** The SAX Parser Factory */
082: protected SAXParserFactory parserFactory = null;
083:
084: /** The SAX Parser Class */
085: protected String parserClass = null;
086:
087: /**
088: * Mapping table from QNames to CatalogParser classes.
089: *
090: * <p>Each key in this hash table has the form "elementname"
091: * or "{namespaceuri}elementname". The former is used if the
092: * namespace URI is null.</p>
093: */
094: protected Hashtable namespaceMap = new Hashtable();
095:
096: /** The parser in use for the current catalog. */
097: private SAXCatalogParser saxParser = null;
098:
099: /** Set if something goes horribly wrong. It allows the class to
100: * ignore the rest of the events that are received.
101: */
102: private boolean abandonHope = false;
103:
104: /** The Catalog that we're working for. */
105: private Catalog catalog;
106:
107: /** Set the XML SAX Parser Factory.
108: */
109: public void setParserFactory(SAXParserFactory parserFactory) {
110: this .parserFactory = parserFactory;
111: }
112:
113: /** Set the XML SAX Parser Class
114: */
115: public void setParserClass(String parserClass) {
116: this .parserClass = parserClass;
117: }
118:
119: /** Get the parser factory currently in use. */
120: public SAXParserFactory getParserFactory() {
121: return parserFactory;
122: }
123:
124: /** Get the parser class currently in use. */
125: public String getParserClass() {
126: return parserClass;
127: }
128:
129: /** The debug class to use for this reader.
130: *
131: * This is a bit of a hack. Anyway, whenever we read for a catalog,
132: * we extract the debug object
133: * from the catalog's manager so that we can use it to print messages.
134: *
135: * In production, we don't really expect any messages so it doesn't
136: * really matter. But it's still a bit of a hack.
137: */
138: protected Debug debug = CatalogManager.getStaticManager().debug;
139:
140: /** The constructor */
141: public SAXCatalogReader() {
142: parserFactory = null;
143: parserClass = null;
144: }
145:
146: /** The constructor */
147: public SAXCatalogReader(SAXParserFactory parserFactory) {
148: this .parserFactory = parserFactory;
149: }
150:
151: /** The constructor */
152: public SAXCatalogReader(String parserClass) {
153: this .parserClass = parserClass;
154: }
155:
156: /** Set the SAXCatalogParser class for the given namespace/root
157: * element type.
158: */
159: public void setCatalogParser(String namespaceURI,
160: String rootElement, String parserClass) {
161: if (namespaceURI == null) {
162: namespaceMap.put(rootElement, parserClass);
163: } else {
164: namespaceMap.put("{" + namespaceURI + "}" + rootElement,
165: parserClass);
166: }
167: }
168:
169: /** Get the SAXCatalogParser class for the given namespace/root
170: * element type.
171: */
172: public String getCatalogParser(String namespaceURI,
173: String rootElement) {
174: if (namespaceURI == null) {
175: return (String) namespaceMap.get(rootElement);
176: } else {
177: return (String) namespaceMap.get("{" + namespaceURI + "}"
178: + rootElement);
179: }
180: }
181:
182: /**
183: * Parse an XML Catalog file.
184: *
185: * @param catalog The catalog to which this catalog file belongs
186: * @param fileUrl The URL or filename of the catalog file to process
187: *
188: * @throws MalformedURLException Improper fileUrl
189: * @throws IOException Error reading catalog file
190: */
191: public void readCatalog(Catalog catalog, String fileUrl)
192: throws MalformedURLException, IOException, CatalogException {
193:
194: URL url = null;
195:
196: try {
197: url = new URL(fileUrl);
198: } catch (MalformedURLException e) {
199: url = new URL("file:///" + fileUrl);
200: }
201:
202: debug = catalog.getCatalogManager().debug;
203:
204: try {
205: URLConnection urlCon = url.openConnection();
206: readCatalog(catalog, urlCon.getInputStream());
207: } catch (FileNotFoundException e) {
208: catalog.getCatalogManager().debug.message(1,
209: "Failed to load catalog, file not found", url
210: .toString());
211: }
212: }
213:
214: /**
215: * Parse an XML Catalog stream.
216: *
217: * @param catalog The catalog to which this catalog file belongs
218: * @param is The input stream from which the catalog will be read
219: *
220: * @throws MalformedURLException Improper fileUrl
221: * @throws IOException Error reading catalog file
222: * @throws CatalogException A Catalog exception
223: */
224: public void readCatalog(Catalog catalog, InputStream is)
225: throws IOException, CatalogException {
226:
227: // Create an instance of the parser
228: if (parserFactory == null && parserClass == null) {
229: debug
230: .message(1,
231: "Cannot read SAX catalog without a parser");
232: throw new CatalogException(CatalogException.UNPARSEABLE);
233: }
234:
235: debug = catalog.getCatalogManager().debug;
236: EntityResolver bResolver = catalog.getCatalogManager()
237: .getBootstrapResolver();
238:
239: this .catalog = catalog;
240:
241: try {
242: if (parserFactory != null) {
243: SAXParser parser = parserFactory.newSAXParser();
244: SAXParserHandler spHandler = new SAXParserHandler();
245: spHandler.setContentHandler(this );
246: if (bResolver != null) {
247: spHandler.setEntityResolver(bResolver);
248: }
249: parser.parse(new InputSource(is), spHandler);
250: } else {
251: Parser parser = (Parser) Class.forName(parserClass)
252: .newInstance();
253: parser.setDocumentHandler(this );
254: if (bResolver != null) {
255: parser.setEntityResolver(bResolver);
256: }
257: parser.parse(new InputSource(is));
258: }
259: } catch (ClassNotFoundException cnfe) {
260: throw new CatalogException(CatalogException.UNPARSEABLE);
261: } catch (IllegalAccessException iae) {
262: throw new CatalogException(CatalogException.UNPARSEABLE);
263: } catch (InstantiationException ie) {
264: throw new CatalogException(CatalogException.UNPARSEABLE);
265: } catch (ParserConfigurationException pce) {
266: throw new CatalogException(CatalogException.UNKNOWN_FORMAT);
267: } catch (SAXException se) {
268: Exception e = se.getException();
269: // FIXME: there must be a better way
270: UnknownHostException uhe = new UnknownHostException();
271: FileNotFoundException fnfe = new FileNotFoundException();
272: if (e != null) {
273: if (e.getClass() == uhe.getClass()) {
274: throw new CatalogException(
275: CatalogException.PARSE_FAILED, e.toString());
276: } else if (e.getClass() == fnfe.getClass()) {
277: throw new CatalogException(
278: CatalogException.PARSE_FAILED, e.toString());
279: }
280: }
281: throw new CatalogException(se);
282: }
283: }
284:
285: // ----------------------------------------------------------------------
286: // Implement the SAX ContentHandler interface
287:
288: /** The SAX <code>setDocumentLocator</code> method. Does nothing. */
289: public void setDocumentLocator(Locator locator) {
290: if (saxParser != null) {
291: saxParser.setDocumentLocator(locator);
292: }
293: }
294:
295: /** The SAX <code>startDocument</code> method. Does nothing. */
296: public void startDocument() throws SAXException {
297: saxParser = null;
298: abandonHope = false;
299: return;
300: }
301:
302: /** The SAX <code>endDocument</code> method. Does nothing. */
303: public void endDocument() throws SAXException {
304: if (saxParser != null) {
305: saxParser.endDocument();
306: }
307: }
308:
309: /**
310: * The SAX <code>startElement</code> method.
311: *
312: * <p>The catalog parser is selected based on the namespace of the
313: * first element encountered in the catalog.</p>
314: */
315: public void startElement(String name, AttributeList atts)
316: throws SAXException {
317:
318: if (abandonHope) {
319: return;
320: }
321:
322: if (saxParser == null) {
323: String prefix = "";
324: if (name.indexOf(':') > 0) {
325: prefix = name.substring(0, name.indexOf(':'));
326: }
327:
328: String localName = name;
329: if (localName.indexOf(':') > 0) {
330: localName = localName
331: .substring(localName.indexOf(':') + 1);
332: }
333:
334: String namespaceURI = null;
335: if (prefix.equals("")) {
336: namespaceURI = atts.getValue("xmlns");
337: } else {
338: namespaceURI = atts.getValue("xmlns:" + prefix);
339: }
340:
341: String saxParserClass = getCatalogParser(namespaceURI,
342: localName);
343:
344: if (saxParserClass == null) {
345: abandonHope = true;
346: if (namespaceURI == null) {
347: debug.message(2, "No Catalog parser for " + name);
348: } else {
349: debug.message(2, "No Catalog parser for " + "{"
350: + namespaceURI + "}" + name);
351: }
352: return;
353: }
354:
355: try {
356: saxParser = (SAXCatalogParser) Class.forName(
357: saxParserClass).newInstance();
358:
359: saxParser.setCatalog(catalog);
360: saxParser.startDocument();
361: saxParser.startElement(name, atts);
362: } catch (ClassNotFoundException cnfe) {
363: saxParser = null;
364: abandonHope = true;
365: debug.message(2, cnfe.toString());
366: } catch (InstantiationException ie) {
367: saxParser = null;
368: abandonHope = true;
369: debug.message(2, ie.toString());
370: } catch (IllegalAccessException iae) {
371: saxParser = null;
372: abandonHope = true;
373: debug.message(2, iae.toString());
374: } catch (ClassCastException cce) {
375: saxParser = null;
376: abandonHope = true;
377: debug.message(2, cce.toString());
378: }
379: } else {
380: saxParser.startElement(name, atts);
381: }
382: }
383:
384: /**
385: * The SAX2 <code>startElement</code> method.
386: *
387: * <p>The catalog parser is selected based on the namespace of the
388: * first element encountered in the catalog.</p>
389: */
390: public void startElement(String namespaceURI, String localName,
391: String qName, Attributes atts) throws SAXException {
392:
393: if (abandonHope) {
394: return;
395: }
396:
397: if (saxParser == null) {
398: String saxParserClass = getCatalogParser(namespaceURI,
399: localName);
400:
401: if (saxParserClass == null) {
402: abandonHope = true;
403: if (namespaceURI == null) {
404: debug.message(2, "No Catalog parser for "
405: + localName);
406: } else {
407: debug.message(2, "No Catalog parser for " + "{"
408: + namespaceURI + "}" + localName);
409: }
410: return;
411: }
412:
413: try {
414: saxParser = (SAXCatalogParser) Class.forName(
415: saxParserClass).newInstance();
416:
417: saxParser.setCatalog(catalog);
418: saxParser.startDocument();
419: saxParser.startElement(namespaceURI, localName, qName,
420: atts);
421: } catch (ClassNotFoundException cnfe) {
422: saxParser = null;
423: abandonHope = true;
424: debug.message(2, cnfe.toString());
425: } catch (InstantiationException ie) {
426: saxParser = null;
427: abandonHope = true;
428: debug.message(2, ie.toString());
429: } catch (IllegalAccessException iae) {
430: saxParser = null;
431: abandonHope = true;
432: debug.message(2, iae.toString());
433: } catch (ClassCastException cce) {
434: saxParser = null;
435: abandonHope = true;
436: debug.message(2, cce.toString());
437: }
438: } else {
439: saxParser
440: .startElement(namespaceURI, localName, qName, atts);
441: }
442: }
443:
444: /** The SAX <code>endElement</code> method. Does nothing. */
445: public void endElement(String name) throws SAXException {
446: if (saxParser != null) {
447: saxParser.endElement(name);
448: }
449: }
450:
451: /** The SAX2 <code>endElement</code> method. Does nothing. */
452: public void endElement(String namespaceURI, String localName,
453: String qName) throws SAXException {
454: if (saxParser != null) {
455: saxParser.endElement(namespaceURI, localName, qName);
456: }
457: }
458:
459: /** The SAX <code>characters</code> method. Does nothing. */
460: public void characters(char ch[], int start, int length)
461: throws SAXException {
462: if (saxParser != null) {
463: saxParser.characters(ch, start, length);
464: }
465: }
466:
467: /** The SAX <code>ignorableWhitespace</code> method. Does nothing. */
468: public void ignorableWhitespace(char ch[], int start, int length)
469: throws SAXException {
470: if (saxParser != null) {
471: saxParser.ignorableWhitespace(ch, start, length);
472: }
473: }
474:
475: /** The SAX <code>processingInstruction</code> method. Does nothing. */
476: public void processingInstruction(String target, String data)
477: throws SAXException {
478: if (saxParser != null) {
479: saxParser.processingInstruction(target, data);
480: }
481: }
482:
483: /** The SAX <code>startPrefixMapping</code> method. Does nothing. */
484: public void startPrefixMapping(String prefix, String uri)
485: throws SAXException {
486: if (saxParser != null) {
487: saxParser.startPrefixMapping(prefix, uri);
488: }
489: }
490:
491: /** The SAX <code>endPrefixMapping</code> method. Does nothing. */
492: public void endPrefixMapping(String prefix) throws SAXException {
493: if (saxParser != null) {
494: saxParser.endPrefixMapping(prefix);
495: }
496: }
497:
498: /** The SAX <code>skippedentity</code> method. Does nothing. */
499: public void skippedEntity(String name) throws SAXException {
500: if (saxParser != null) {
501: saxParser.skippedEntity(name);
502: }
503: }
504: }
|