001: // ResolvingParser.java - An interface for reading 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.tools;
021:
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.net.URL;
025: import java.net.MalformedURLException;
026: import java.util.Locale;
027:
028: import org.xml.sax.Parser;
029: import org.xml.sax.InputSource;
030: import org.xml.sax.Locator;
031: import org.xml.sax.ErrorHandler;
032: import org.xml.sax.DTDHandler;
033: import org.xml.sax.DocumentHandler;
034: import org.xml.sax.AttributeList;
035: import org.xml.sax.EntityResolver;
036: import org.xml.sax.SAXException;
037:
038: import javax.xml.parsers.SAXParserFactory;
039: import javax.xml.parsers.SAXParser;
040:
041: import com.sun.org.apache.xml.internal.resolver.Catalog;
042: import com.sun.org.apache.xml.internal.resolver.CatalogManager;
043: import com.sun.org.apache.xml.internal.resolver.helpers.FileURL;
044:
045: /**
046: * A SAX Parser that performs catalog-based entity resolution.
047: *
048: * <p>This class implements a SAX Parser that performs entity resolution
049: * using the CatalogResolver. The actual, underlying parser is obtained
050: * from a SAXParserFactory.</p>
051: * </p>
052: *
053: * @deprecated This interface has been replaced by the
054: * {@link com.sun.org.apache.xml.internal.resolver.tools.ResolvingXMLReader} for SAX2.
055: * @see CatalogResolver
056: * @see org.xml.sax.Parser
057: *
058: * @author Norman Walsh
059: * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
060: *
061: * @version 1.0
062: */
063: public class ResolvingParser implements Parser, DTDHandler,
064: DocumentHandler, EntityResolver {
065: /** Make the parser Namespace aware? */
066: public static boolean namespaceAware = true;
067:
068: /** Make the parser validating? */
069: public static boolean validating = false;
070:
071: /** Suppress explanatory message?
072: *
073: * @see #parse(InputSource)
074: */
075: public static boolean suppressExplanation = false;
076:
077: /** The underlying parser. */
078: private SAXParser saxParser = null;
079:
080: /** The underlying reader. */
081: private Parser parser = null;
082:
083: /** The underlying DocumentHandler. */
084: private DocumentHandler documentHandler = null;
085:
086: /** The underlying DTDHandler. */
087: private DTDHandler dtdHandler = null;
088:
089: /** The manager for the underlying resolver. */
090: private CatalogManager catalogManager = CatalogManager
091: .getStaticManager();
092:
093: /** The underlying catalog resolver. */
094: private CatalogResolver catalogResolver = null;
095:
096: /** A separate resolver for oasis-xml-pi catalogs. */
097: private CatalogResolver piCatalogResolver = null;
098:
099: /** Are we in the prolog? Is an oasis-xml-catalog PI valid now? */
100: private boolean allowXMLCatalogPI = false;
101:
102: /** Has an oasis-xml-catalog PI been seen? */
103: private boolean oasisXMLCatalogPI = false;
104:
105: /** The base URI of the input document, if known. */
106: private URL baseURL = null;
107:
108: /** Constructor. */
109: public ResolvingParser() {
110: initParser();
111: }
112:
113: /** Constructor. */
114: public ResolvingParser(CatalogManager manager) {
115: catalogManager = manager;
116: initParser();
117: }
118:
119: /** Initialize the parser. */
120: private void initParser() {
121: catalogResolver = new CatalogResolver(catalogManager);
122:
123: SAXParserFactory spf = SAXParserFactory.newInstance();
124: spf.setNamespaceAware(namespaceAware);
125: spf.setValidating(validating);
126:
127: try {
128: saxParser = spf.newSAXParser();
129: parser = saxParser.getParser();
130: documentHandler = null;
131: dtdHandler = null;
132: } catch (Exception ex) {
133: ex.printStackTrace();
134: }
135: }
136:
137: /** Return the Catalog being used. */
138: public Catalog getCatalog() {
139: return catalogResolver.getCatalog();
140: }
141:
142: /**
143: * SAX Parser API.
144: *
145: * <p>Note that the JAXP 1.1ea2 parser crashes with an InternalError if
146: * it encounters a system identifier that appears to be a relative URI
147: * that begins with a slash. For example, the declaration:</p>
148: *
149: * <pre>
150: * <!DOCTYPE book SYSTEM "/path/to/dtd/on/my/system/docbookx.dtd">
151: * </pre>
152: *
153: * <p>would cause such an error. As a convenience, this method catches
154: * that error and prints an explanation. (Unfortunately, it's not possible
155: * to identify the particular system identifier that causes the problem.)
156: * </p>
157: *
158: * <p>The underlying error is forwarded after printing the explanatory
159: * message. The message is only every printed once and if
160: * <code>suppressExplanation</code> is set to <code>false</code> before
161: * parsing, it will never be printed.</p>
162: */
163: public void parse(InputSource input) throws IOException,
164: SAXException {
165: setupParse(input.getSystemId());
166: try {
167: parser.parse(input);
168: } catch (InternalError ie) {
169: explain(input.getSystemId());
170: throw ie;
171: }
172: }
173:
174: /** SAX Parser API.
175: *
176: * @see #parse(InputSource)
177: */
178: public void parse(String systemId) throws IOException, SAXException {
179: setupParse(systemId);
180: try {
181: parser.parse(systemId);
182: } catch (InternalError ie) {
183: explain(systemId);
184: throw ie;
185: }
186: }
187:
188: /** SAX Parser API. */
189: public void setDocumentHandler(DocumentHandler handler) {
190: documentHandler = handler;
191: }
192:
193: /** SAX Parser API. */
194: public void setDTDHandler(DTDHandler handler) {
195: dtdHandler = handler;
196: }
197:
198: /**
199: * SAX Parser API.
200: *
201: * <p>The purpose of this class is to implement an entity resolver.
202: * Attempting to set a different one is pointless (and ignored).</p>
203: */
204: public void setEntityResolver(EntityResolver resolver) {
205: // nop
206: }
207:
208: /** SAX Parser API. */
209: public void setErrorHandler(ErrorHandler handler) {
210: parser.setErrorHandler(handler);
211: }
212:
213: /** SAX Parser API. */
214: public void setLocale(Locale locale) throws SAXException {
215: parser.setLocale(locale);
216: }
217:
218: /** SAX DocumentHandler API. */
219: public void characters(char[] ch, int start, int length)
220: throws SAXException {
221: if (documentHandler != null) {
222: documentHandler.characters(ch, start, length);
223: }
224: }
225:
226: /** SAX DocumentHandler API. */
227: public void endDocument() throws SAXException {
228: if (documentHandler != null) {
229: documentHandler.endDocument();
230: }
231: }
232:
233: /** SAX DocumentHandler API. */
234: public void endElement(String name) throws SAXException {
235: if (documentHandler != null) {
236: documentHandler.endElement(name);
237: }
238: }
239:
240: /** SAX DocumentHandler API. */
241: public void ignorableWhitespace(char[] ch, int start, int length)
242: throws SAXException {
243: if (documentHandler != null) {
244: documentHandler.ignorableWhitespace(ch, start, length);
245: }
246: }
247:
248: /** SAX DocumentHandler API. */
249: public void processingInstruction(String target, String pidata)
250: throws SAXException {
251:
252: if (target.equals("oasis-xml-catalog")) {
253: URL catalog = null;
254: String data = pidata;
255:
256: int pos = data.indexOf("catalog=");
257: if (pos >= 0) {
258: data = data.substring(pos + 8);
259: if (data.length() > 1) {
260: String quote = data.substring(0, 1);
261: data = data.substring(1);
262: pos = data.indexOf(quote);
263: if (pos >= 0) {
264: data = data.substring(0, pos);
265: try {
266: if (baseURL != null) {
267: catalog = new URL(baseURL, data);
268: } else {
269: catalog = new URL(data);
270: }
271: } catch (MalformedURLException mue) {
272: // nevermind
273: }
274: }
275: }
276: }
277:
278: if (allowXMLCatalogPI) {
279: if (catalogManager.getAllowOasisXMLCatalogPI()) {
280: catalogManager.debug.message(4,
281: "oasis-xml-catalog PI", pidata);
282:
283: if (catalog != null) {
284: try {
285: catalogManager.debug.message(4,
286: "oasis-xml-catalog", catalog
287: .toString());
288: oasisXMLCatalogPI = true;
289:
290: if (piCatalogResolver == null) {
291: piCatalogResolver = new CatalogResolver(
292: true);
293: }
294:
295: piCatalogResolver.getCatalog()
296: .parseCatalog(catalog.toString());
297: } catch (Exception e) {
298: catalogManager.debug.message(3,
299: "Exception parsing oasis-xml-catalog: "
300: + catalog.toString());
301: }
302: } else {
303: catalogManager.debug.message(3,
304: "PI oasis-xml-catalog unparseable: "
305: + pidata);
306: }
307: } else {
308: catalogManager.debug.message(4,
309: "PI oasis-xml-catalog ignored: " + pidata);
310: }
311: } else {
312: catalogManager.debug.message(3,
313: "PI oasis-xml-catalog occurred in an invalid place: "
314: + pidata);
315: }
316: } else {
317: if (documentHandler != null) {
318: documentHandler.processingInstruction(target, pidata);
319: }
320: }
321: }
322:
323: /** SAX DocumentHandler API. */
324: public void setDocumentLocator(Locator locator) {
325: if (documentHandler != null) {
326: documentHandler.setDocumentLocator(locator);
327: }
328: }
329:
330: /** SAX DocumentHandler API. */
331: public void startDocument() throws SAXException {
332: if (documentHandler != null) {
333: documentHandler.startDocument();
334: }
335: }
336:
337: /** SAX DocumentHandler API. */
338: public void startElement(String name, AttributeList atts)
339: throws SAXException {
340: allowXMLCatalogPI = false;
341: if (documentHandler != null) {
342: documentHandler.startElement(name, atts);
343: }
344: }
345:
346: /** SAX DTDHandler API. */
347: public void notationDecl(String name, String publicId,
348: String systemId) throws SAXException {
349: allowXMLCatalogPI = false;
350: if (dtdHandler != null) {
351: dtdHandler.notationDecl(name, publicId, systemId);
352: }
353: }
354:
355: /** SAX DTDHandler API. */
356: public void unparsedEntityDecl(String name, String publicId,
357: String systemId, String notationName) throws SAXException {
358: allowXMLCatalogPI = false;
359: if (dtdHandler != null) {
360: dtdHandler.unparsedEntityDecl(name, publicId, systemId,
361: notationName);
362: }
363: }
364:
365: /**
366: * Implements the <code>resolveEntity</code> method
367: * for the SAX interface, using an underlying CatalogResolver
368: * to do the real work.
369: */
370: public InputSource resolveEntity(String publicId, String systemId) {
371: allowXMLCatalogPI = false;
372: String resolved = catalogResolver.getResolvedEntity(publicId,
373: systemId);
374:
375: if (resolved == null && piCatalogResolver != null) {
376: resolved = piCatalogResolver.getResolvedEntity(publicId,
377: systemId);
378: }
379:
380: if (resolved != null) {
381: try {
382: InputSource iSource = new InputSource(resolved);
383: iSource.setPublicId(publicId);
384:
385: // Ideally this method would not attempt to open the
386: // InputStream, but there is a bug (in Xerces, at least)
387: // that causes the parser to mistakenly open the wrong
388: // system identifier if the returned InputSource does
389: // not have a byteStream.
390: //
391: // It could be argued that we still shouldn't do this here,
392: // but since the purpose of calling the entityResolver is
393: // almost certainly to open the input stream, it seems to
394: // do little harm.
395: //
396: URL url = new URL(resolved);
397: InputStream iStream = url.openStream();
398: iSource.setByteStream(iStream);
399:
400: return iSource;
401: } catch (Exception e) {
402: catalogManager.debug.message(1,
403: "Failed to create InputSource", resolved);
404: return null;
405: }
406: } else {
407: return null;
408: }
409: }
410:
411: /** Setup for parsing. */
412: private void setupParse(String systemId) {
413: allowXMLCatalogPI = true;
414: parser.setEntityResolver(this );
415: parser.setDocumentHandler(this );
416: parser.setDTDHandler(this );
417:
418: URL cwd = null;
419:
420: try {
421: cwd = FileURL.makeURL("basename");
422: } catch (MalformedURLException mue) {
423: cwd = null;
424: }
425:
426: try {
427: baseURL = new URL(systemId);
428: } catch (MalformedURLException mue) {
429: if (cwd != null) {
430: try {
431: baseURL = new URL(cwd, systemId);
432: } catch (MalformedURLException mue2) {
433: // give up
434: baseURL = null;
435: }
436: } else {
437: // give up
438: baseURL = null;
439: }
440: }
441: }
442:
443: /** Provide one possible explanation for an InternalError. */
444: private void explain(String systemId) {
445: if (!suppressExplanation) {
446: System.out
447: .println("Parser probably encountered bad URI in "
448: + systemId);
449: System.out
450: .println("For example, replace '/some/uri' with 'file:/some/uri'.");
451: }
452: }
453: }
|