001: // DOMCatalogReader.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.InputStream;
025: import java.net.URL;
026: import java.net.URLConnection;
027: import java.net.MalformedURLException;
028:
029: import javax.xml.parsers.DocumentBuilderFactory;
030: import javax.xml.parsers.DocumentBuilder;
031: import javax.xml.parsers.ParserConfigurationException;
032:
033: import com.sun.org.apache.xml.internal.resolver.Catalog;
034: import com.sun.org.apache.xml.internal.resolver.CatalogException;
035: import com.sun.org.apache.xml.internal.resolver.readers.CatalogReader;
036: import com.sun.org.apache.xml.internal.resolver.helpers.Namespaces;
037:
038: import org.xml.sax.SAXException;
039: import org.w3c.dom.*;
040:
041: /**
042: * A DOM-based CatalogReader.
043: *
044: * <p>This class is used to read XML Catalogs using the DOM. This reader
045: * has an advantage over the SAX-based reader that it can analyze the
046: * DOM tree rather than simply a series of SAX events. It has the disadvantage
047: * that it requires all of the code necessary to build and walk a DOM
048: * tree.</p>
049: *
050: * <p>Since the choice of CatalogReaders (in the InputStream case) can only
051: * be made on the basis of MIME type, the following problem occurs: only
052: * one CatalogReader can exist for all XML mime types. In order to get
053: * around this problem, the DOMCatalogReader relies on a set of external
054: * CatalogParsers to actually build the catalog.</p>
055: *
056: * <p>The selection of CatalogParsers is made on the basis of the QName
057: * of the root element of the document.</p>
058: *
059: * <p>This class requires the <a href="http://java.sun.com/aboutJava/communityprocess/final/jsr005/index.html">Java API for XML Parsing</a>.</p>
060: *
061: * @see Catalog
062: * @see CatalogReader
063: * @see SAXCatalogReader
064: * @see TextCatalogReader
065: * @see DOMCatalogParser
066: *
067: * @author Norman Walsh
068: * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
069: *
070: * @version 1.0
071: */
072: public class DOMCatalogReader implements CatalogReader {
073: /**
074: * Mapping table from QNames to CatalogParser classes.
075: *
076: * <p>Each key in this hash table has the form "elementname"
077: * or "{namespaceuri}elementname". The former is used if the
078: * namespace URI is null.</p>
079: */
080: protected Hashtable namespaceMap = new Hashtable();
081:
082: /**
083: * Add a new parser to the reader.
084: *
085: * <p>This method associates the specified parserClass with the
086: * namespaceURI/rootElement names specified.</p>
087: *
088: * @param namespaceURI The namespace URI. <em>Not</em> the prefix.
089: * @param rootElement The name of the root element.
090: * @param parserClass The name of the parserClass to instantiate
091: * for this kind of catalog.
092: */
093: public void setCatalogParser(String namespaceURI,
094: String rootElement, String parserClass) {
095: if (namespaceURI == null) {
096: namespaceMap.put(rootElement, parserClass);
097: } else {
098: namespaceMap.put("{" + namespaceURI + "}" + rootElement,
099: parserClass);
100: }
101: }
102:
103: /**
104: * Get the name of the parser class for a given catalog type.
105: *
106: * <p>This method returns the parserClass associated with the
107: * namespaceURI/rootElement names specified.</p>
108: *
109: * @param namespaceURI The namespace URI. <em>Not</em> the prefix.
110: * @param rootElement The name of the root element.
111: * @return The parser class.
112: */
113: public String getCatalogParser(String namespaceURI,
114: String rootElement) {
115: if (namespaceURI == null) {
116: return (String) namespaceMap.get(rootElement);
117: } else {
118: return (String) namespaceMap.get("{" + namespaceURI + "}"
119: + rootElement);
120: }
121: }
122:
123: /**
124: * Null constructor; something for subclasses to call.
125: */
126: public DOMCatalogReader() {
127: }
128:
129: /**
130: * Read a catalog from an input stream.
131: *
132: * <p>This class reads a catalog from an input stream:</p>
133: *
134: * <ul>
135: * <li>Based on the QName of the root element, it determines which
136: * parser to instantiate for this catalog.</li>
137: * <li>It constructs a DOM Document from the catalog and</li>
138: * <li>For each child of the root node, it calls the parser's
139: * parseCatalogEntry method. This method is expected to make
140: * appropriate calls back into the catalog to add entries for the
141: * entries in the catalog. It is free to do this in whatever manner
142: * is appropriate (perhaps using just the node passed in, perhaps
143: * wandering arbitrarily throughout the tree).</li>
144: * </ul>
145: *
146: * @param catalog The catalog for which this reader is called.
147: * @param is The input stream that is to be read.
148: * @throws IOException if the URL cannot be read.
149: * @throws UnknownCatalogFormatException if the catalog format is
150: * not recognized.
151: * @throws UnparseableCatalogException if the catalog cannot be parsed.
152: * (For example, if it is supposed to be XML and isn't well-formed or
153: * if the parser class cannot be instantiated.)
154: */
155: public void readCatalog(Catalog catalog, InputStream is)
156: throws IOException, CatalogException {
157:
158: DocumentBuilderFactory factory = null;
159: DocumentBuilder builder = null;
160:
161: factory = DocumentBuilderFactory.newInstance();
162: factory.setNamespaceAware(false);
163: factory.setValidating(false);
164: try {
165: builder = factory.newDocumentBuilder();
166: } catch (ParserConfigurationException pce) {
167: throw new CatalogException(CatalogException.UNPARSEABLE);
168: }
169:
170: Document doc = null;
171:
172: try {
173: doc = builder.parse(is);
174: } catch (SAXException se) {
175: throw new CatalogException(CatalogException.UNKNOWN_FORMAT);
176: }
177:
178: Element root = doc.getDocumentElement();
179:
180: String namespaceURI = Namespaces.getNamespaceURI(root);
181: String localName = Namespaces.getLocalName(root);
182:
183: String domParserClass = getCatalogParser(namespaceURI,
184: localName);
185:
186: if (domParserClass == null) {
187: if (namespaceURI == null) {
188: catalog.getCatalogManager().debug.message(1,
189: "No Catalog parser for " + localName);
190: } else {
191: catalog.getCatalogManager().debug.message(1,
192: "No Catalog parser for " + "{" + namespaceURI
193: + "}" + localName);
194: }
195: return;
196: }
197:
198: DOMCatalogParser domParser = null;
199:
200: try {
201: domParser = (DOMCatalogParser) Class
202: .forName(domParserClass).newInstance();
203: } catch (ClassNotFoundException cnfe) {
204: catalog.getCatalogManager().debug.message(1,
205: "Cannot load XML Catalog Parser class",
206: domParserClass);
207: throw new CatalogException(CatalogException.UNPARSEABLE);
208: } catch (InstantiationException ie) {
209: catalog.getCatalogManager().debug.message(1,
210: "Cannot instantiate XML Catalog Parser class",
211: domParserClass);
212: throw new CatalogException(CatalogException.UNPARSEABLE);
213: } catch (IllegalAccessException iae) {
214: catalog.getCatalogManager().debug.message(1,
215: "Cannot access XML Catalog Parser class",
216: domParserClass);
217: throw new CatalogException(CatalogException.UNPARSEABLE);
218: } catch (ClassCastException cce) {
219: catalog.getCatalogManager().debug.message(1,
220: "Cannot cast XML Catalog Parser class",
221: domParserClass);
222: throw new CatalogException(CatalogException.UNPARSEABLE);
223: }
224:
225: Node node = root.getFirstChild();
226: while (node != null) {
227: domParser.parseCatalogEntry(catalog, node);
228: node = node.getNextSibling();
229: }
230: }
231:
232: /**
233: * Read the catalog behind the specified URL.
234: *
235: * @see #readCatalog(Catalog, InputStream)
236: *
237: * @param catalog The catalog for which we are reading.
238: * @param fileUrl The URL of the document that should be read.
239: *
240: * @throws MalformedURLException if the specified URL cannot be
241: * turned into a URL object.
242: * @throws IOException if the URL cannot be read.
243: * @throws UnknownCatalogFormatException if the catalog format is
244: * not recognized.
245: * @throws UnparseableCatalogException if the catalog cannot be parsed.
246: * (For example, if it is supposed to be XML and isn't well-formed.)
247: */
248: public void readCatalog(Catalog catalog, String fileUrl)
249: throws MalformedURLException, IOException, CatalogException {
250: URL url = new URL(fileUrl);
251: URLConnection urlCon = url.openConnection();
252: readCatalog(catalog, urlCon.getInputStream());
253: }
254: }
|