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
+ *
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) {