From 21fc7e9d1a2cc3e73bca4b4c3357f16d7b1484a7 Mon Sep 17 00:00:00 2001 From: ndw Date: Tue, 22 Apr 2003 14:51:09 +0000 Subject: [PATCH] Added a bootstrap resolver for use when reading XML Catalog files (so the DTD for catalogs can be resolved to the DTD stored in the Jar file, for example). git-svn-id: https://svn.apache.org/repos/asf/xml/commons/trunk@226059 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/xml/resolver/CatalogManager.java | 23 +- .../resolver/helpers/BootstrapResolver.java | 249 ++++++++++++++++++ .../resolver/readers/SAXCatalogReader.java | 26 +- 3 files changed, 292 insertions(+), 6 deletions(-) create mode 100644 java/src/org/apache/xml/resolver/helpers/BootstrapResolver.java diff --git a/java/src/org/apache/xml/resolver/CatalogManager.java b/java/src/org/apache/xml/resolver/CatalogManager.java index 24fda75..a4cbfab 100644 --- a/java/src/org/apache/xml/resolver/CatalogManager.java +++ b/java/src/org/apache/xml/resolver/CatalogManager.java @@ -59,13 +59,19 @@ package org.apache.xml.resolver; +import java.io.InputStream; + import java.net.URL; import java.net.MalformedURLException; -import java.io.*; -import java.util.*; +import java.util.MissingResourceException; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.StringTokenizer; +import java.util.Vector; import org.apache.xml.resolver.helpers.Debug; +import org.apache.xml.resolver.helpers.BootstrapResolver; import org.apache.xml.resolver.Catalog; /** @@ -167,6 +173,9 @@ public class CatalogManager { /** A static CatalogManager instance for sharing */ private static CatalogManager staticManager = new CatalogManager(); + /** The bootstrap resolver to use when loading XML Catalogs. */ + private BootstrapResolver bResolver = new BootstrapResolver(); + /** Flag to ignore missing property files and/or properties */ private boolean ignoreMissingProperties = (System.getProperty(pIgnoreMissing) != null @@ -255,6 +264,16 @@ public class CatalogManager { // to avoid it. } + /** Set the bootstrap resolver.*/ + public void setBootstrapResolver(BootstrapResolver resolver) { + bResolver = resolver; + } + + /** Get the bootstrap resolver.*/ + public BootstrapResolver getBootstrapResolver() { + return bResolver; + } + /** * Load the properties from the propertyFile and build the * resources from it. diff --git a/java/src/org/apache/xml/resolver/helpers/BootstrapResolver.java b/java/src/org/apache/xml/resolver/helpers/BootstrapResolver.java new file mode 100644 index 0000000..e90cf0a --- /dev/null +++ b/java/src/org/apache/xml/resolver/helpers/BootstrapResolver.java @@ -0,0 +1,249 @@ +// BootstrapResolver.java - Resolve entities and URIs internally + +/* + * The Apache Software License, Version 1.1 + * + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Xalan" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation and was + * originally based on software copyright (c) 2001, International + * Business Machines Corporation., http://www.ibm.com. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.xml.resolver.helpers; + +import java.util.Hashtable; +import java.net.URL; +import java.net.MalformedURLException; +import java.io.InputStream; + +import javax.xml.transform.URIResolver; +import javax.xml.transform.Source; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.TransformerException; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +/** + * A simple bootstrapping resolver. + * + *

This class is used as the entity resolver when reading XML Catalogs. + * It searches for the OASIS XML Catalog DTD, Relax NG Grammar and W3C XML Schema + * as resources (e.g., in the resolver jar file).

+ * + *

If you have your own DTDs or schemas, you can extend this class and + * set the BootstrapResolver in your CatalogManager.

+ * + * @see CatalogManager + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class BootstrapResolver implements EntityResolver, URIResolver { + /** URI of the W3C XML Schema for OASIS XML Catalog files. */ + public static final String xmlCatalogXSD = "http://www.oasis-open.org/committees/entity/release/1.0/catalog.xsd"; + + /** URI of the RELAX NG Grammar for OASIS XML Catalog files. */ + public static final String xmlCatalogRNG = "http://www.oasis-open.org/committees/entity/release/1.0/catalog.rng"; + + /** Public identifier for OASIS XML Catalog files. */ + public static final String xmlCatalogPubId = "-//OASIS//DTD XML Catalogs V1.0//EN"; + + /** System identifier for OASIS XML Catalog files. */ + public static final String xmlCatalogSysId = "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"; + + /** Private hash used for public identifiers. */ + private Hashtable publicMap = new Hashtable(); + + /** Private hash used for system identifiers. */ + private Hashtable systemMap = new Hashtable(); + + /** Private hash used for URIs. */ + private Hashtable uriMap = new Hashtable(); + + /** Constructor. */ + public BootstrapResolver() { + URL url = this.getClass().getResource("/org/apache/xml/resolver/etc/catalog.dtd"); + if (url != null) { + publicMap.put(xmlCatalogPubId, url.toString()); + systemMap.put(xmlCatalogSysId, url.toString()); + } + + url = this.getClass().getResource("/org/apache/xml/resolver/etc/catalog.rng"); + if (url != null) { + uriMap.put(xmlCatalogRNG, url.toString()); + } + + url = this.getClass().getResource("/org/apache/xml/resolver/etc/catalog.xsd"); + if (url != null) { + uriMap.put(xmlCatalogXSD, url.toString()); + } + } + + /** SAX resolveEntity API. */ + public InputSource resolveEntity (String publicId, String systemId) { + String resolved = null; + + if (systemId != null && systemMap.containsKey(systemId)) { + resolved = (String) systemMap.get(systemId); + } else if (publicId != null && publicMap.containsKey(publicId)) { + resolved = (String) publicMap.get(publicId); + } + + if (resolved != null) { + try { + InputSource iSource = new InputSource(resolved); + iSource.setPublicId(publicId); + + // Ideally this method would not attempt to open the + // InputStream, but there is a bug (in Xerces, at least) + // that causes the parser to mistakenly open the wrong + // system identifier if the returned InputSource does + // not have a byteStream. + // + // It could be argued that we still shouldn't do this here, + // but since the purpose of calling the entityResolver is + // almost certainly to open the input stream, it seems to + // do little harm. + // + URL url = new URL(resolved); + InputStream iStream = url.openStream(); + iSource.setByteStream(iStream); + + return iSource; + } catch (Exception e) { + // FIXME: silently fail? + return null; + } + } + + return null; + } + + /** Transformer resolve API. */ + public Source resolve(String href, String base) + throws TransformerException { + + String uri = href; + String fragment = null; + int hashPos = href.indexOf("#"); + if (hashPos >= 0) { + uri = href.substring(0, hashPos); + fragment = href.substring(hashPos+1); + } + + String result = null; + if (href != null && uriMap.containsKey(href)) { + result = (String) uriMap.get(href); + } + + if (result == null) { + try { + URL url = null; + + if (base==null) { + url = new URL(uri); + result = url.toString(); + } else { + URL baseURL = new URL(base); + url = (href.length()==0 ? baseURL : new URL(baseURL, uri)); + result = url.toString(); + } + } catch (java.net.MalformedURLException mue) { + // try to make an absolute URI from the current base + String absBase = makeAbsolute(base); + if (!absBase.equals(base)) { + // don't bother if the absBase isn't different! + return resolve(href, absBase); + } else { + throw new TransformerException("Malformed URL " + + href + "(base " + base + ")", + mue); + } + } + } + + SAXSource source = new SAXSource(); + source.setInputSource(new InputSource(result)); + return source; + } + + /** Attempt to construct an absolute URI */ + private String makeAbsolute(String uri) { + if (uri == null) { + uri = ""; + } + + try { + URL url = new URL(uri); + return url.toString(); + } catch (MalformedURLException mue) { + String dir = System.getProperty("user.dir"); + String file = ""; + + if (dir.endsWith("/")) { + file = "file://" + dir + uri; + } else { + file = "file://" + dir + "/" + uri; + } + + try { + URL fileURL = new URL(file); + return fileURL.toString(); + } catch (MalformedURLException mue2) { + // bail + return uri; + } + } + } +} diff --git a/java/src/org/apache/xml/resolver/readers/SAXCatalogReader.java b/java/src/org/apache/xml/resolver/readers/SAXCatalogReader.java index 22406f0..77c980b 100644 --- a/java/src/org/apache/xml/resolver/readers/SAXCatalogReader.java +++ b/java/src/org/apache/xml/resolver/readers/SAXCatalogReader.java @@ -67,16 +67,27 @@ import java.net.URL; import java.net.URLConnection; import java.net.MalformedURLException; import java.net.UnknownHostException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.parsers.SAXParser; + +import org.xml.sax.AttributeList; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.DocumentHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.Parser; +import org.xml.sax.SAXException; + import org.apache.xml.resolver.Catalog; import org.apache.xml.resolver.CatalogManager; import org.apache.xml.resolver.CatalogException; import org.apache.xml.resolver.readers.CatalogReader; import org.apache.xml.resolver.helpers.Debug; -import org.xml.sax.*; - -import javax.xml.parsers.*; - /** * A SAX-based CatalogReader. * @@ -258,6 +269,7 @@ public class SAXCatalogReader implements CatalogReader, ContentHandler, Document } debug = catalog.getCatalogManager().debug; + EntityResolver bResolver = catalog.getCatalogManager().getBootstrapResolver(); this.catalog = catalog; @@ -266,10 +278,16 @@ public class SAXCatalogReader implements CatalogReader, ContentHandler, Document SAXParser parser = parserFactory.newSAXParser(); SAXParserHandler spHandler = new SAXParserHandler(); spHandler.setContentHandler(this); + if (bResolver != null) { + spHandler.setEntityResolver(bResolver); + } parser.parse(new InputSource(is), spHandler); } else { Parser parser = (Parser) Class.forName(parserClass).newInstance(); parser.setDocumentHandler(this); + if (bResolver != null) { + parser.setEntityResolver(bResolver); + } parser.parse(new InputSource(is)); } } catch (ClassNotFoundException cnfe) {