diff --git a/java/src/manifest.resolver b/java/src/manifest.resolver new file mode 100644 index 0000000..e6c6b14 --- /dev/null +++ b/java/src/manifest.resolver @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Created-By: 1.2.2 (Sun Microsystems Inc.) +Main-Class: org.apache.xml.resolver.Catalog + +Name: org/apache/xml/resolver +Comment: xml-commons Resolver code +Implementation-Title: org.apache.xml.resolver.Catalog +Implementation-Version: 1.0 +Implementation-Vendor: Apache Software Foundation +Implementation-URL: http://xml.apache.org/commons/ + diff --git a/java/src/org/apache/xml/resolver/Catalog.java b/java/src/org/apache/xml/resolver/Catalog.java new file mode 100644 index 0000000..875643c --- /dev/null +++ b/java/src/org/apache/xml/resolver/Catalog.java @@ -0,0 +1,2071 @@ +// Catalog.java - Represents OASIS Open Catalog files. + +/* + * 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; + +import java.lang.Integer; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.io.File; +import java.io.DataInputStream; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import java.net.URL; +import java.net.MalformedURLException; +import org.apache.xml.resolver.CatalogManager; +import org.apache.xml.resolver.helpers.Debug; +import org.apache.xml.resolver.helpers.PublicId; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.readers.SAXCatalogReader; +import org.apache.xml.resolver.readers.TR9401CatalogReader; +import org.apache.xml.resolver.readers.OASISXMLCatalogReader; +import javax.xml.parsers.SAXParserFactory; + +/** + *

Represents OASIS Open Catalog files.

+ * + *

This class implements the semantics of OASIS Open Catalog files + * (defined by + * OASIS Technical + * Resolution 9401:1997 (Amendment 2 to TR 9401)).

+ * + *

The primary purpose of the Catalog is to associate resources in the + * document with local system identifiers. Some entities + * (document types, XML entities, and notations) have names and all of them + * can have either public or system identifiers or both. (In XML, only a + * notation can have a public identifier without a system identifier, but + * the methods implemented in this class obey the Catalog semantics + * from the SGML + * days when system identifiers were optional.)

+ * + *

The system identifiers returned by the resolution methods in this + * class are valid, i.e. usable by, and in fact constructed by, the + * java.net.URL class. Unfortunately, this class seems to behave in + * somewhat non-standard ways and the system identifiers returned may + * not be directly usable in a browser or filesystem context. + * + *

This class recognizes all of the Catalog entries defined in + * TR9401:1997:

+ * + * + * + *

Note that BASE entries are treated as described by RFC2396. In + * particular, this has the counter-intuitive property that after a BASE + * entry identifing "http://example.com/a/b/c" as the base URI, + * the relative URI "foo" is resolved to the absolute URI + * "http://example.com/a/b/foo". You must provide the trailing slash if + * you do not want the final component of the path to be discarded as a + * filename would in a URI for a resource: "http://example.com/a/b/c/". + *

+ * + *

Note that subordinate catalogs (all catalogs except the first, + * including CATALOG and DELEGATE* catalogs) are only loaded if and when + * they are required.

+ * + *

This class relies on classes which implement the CatalogReader + * interface to actually load catalog files. This allows the catalog + * semantics to be implemented for TR9401 text-based catalogs, XML + * catalogs, or any number of other storage formats.

+ * + *

Additional catalogs may also be loaded with the + * {@link #parseCatalog} method.

+ * + * + * + *

Change Log:

+ *
+ *
2.0
+ *

Rewrite to use CatalogReaders.

+ *
1.1
+ *

Allow quoted components in xml.catalog.files + * so that URLs containing colons can be used on Unix. + * The string passed to xml.catalog.files can now have the form:

+ *
+ * unquoted-path-with-no-sep-chars:"double-quoted path with or without sep chars":'single-quoted path with or without sep chars'
+ * 
+ *

(Where ":" is the separater character in this example.)

+ *

If an unquoted path contains an embedded double or single quote + * character, no special processig is performed on that character. No + * path can contain separater characters, double, and single quotes + * simultaneously.

+ *

Fix bug in calculation of BASE entries: if + * a catalog contains multiple BASE entries, each is relative to the preceding + * base, not the default base URI of the catalog.

+ *
+ *
1.0.1
+ *

Fixed a bug in the calculation of the list of subordinate catalogs. + * This bug caused an infinite loop where parsing would alternately process + * two catalogs indefinitely.

+ *
+ *
+ * + * @see CatalogReader + * @see CatalogEntry + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + * + *

Derived from public domain code originally published by Arbortext, + * Inc.

+ */ +public class Catalog { + /** The BASE Catalog Entry type */ + public static final int BASE = CatalogEntry.addEntryType("BASE", 1); + + /** The CATALOG Catalog Entry type */ + public static final int CATALOG = CatalogEntry.addEntryType("CATALOG", 1); + + /** The DOCUMENT Catalog Entry type */ + public static final int DOCUMENT = CatalogEntry.addEntryType("DOCUMENT", 1); + + /** The OVERRIDE Catalog Entry type */ + public static final int OVERRIDE = CatalogEntry.addEntryType("OVERRIDE", 1); + + /** The SGMLDECL Catalog Entry type */ + public static final int SGMLDECL = CatalogEntry.addEntryType("SGMLDECL", 1); + + /** The DELEGATE_PUBLIC Catalog Entry type */ + public static final int DELEGATE_PUBLIC = CatalogEntry.addEntryType("DELEGATE_PUBLIC", 2); + + /** The DELEGATE_SYSTEM Catalog Entry type */ + public static final int DELEGATE_SYSTEM = CatalogEntry.addEntryType("DELEGATE_SYSTEM", 2); + + /** The DELEGATE_URI Catalog Entry type */ + public static final int DELEGATE_URI = CatalogEntry.addEntryType("DELEGATE_URI", 2); + + /** The DOCTYPE Catalog Entry type */ + public static final int DOCTYPE = CatalogEntry.addEntryType("DOCTYPE", 2); + + /** The DTDDECL Catalog Entry type */ + public static final int DTDDECL = CatalogEntry.addEntryType("DTDDECL", 2); + + /** The ENTITY Catalog Entry type */ + public static final int ENTITY = CatalogEntry.addEntryType("ENTITY", 2); + + /** The LINKTYPE Catalog Entry type */ + public static final int LINKTYPE = CatalogEntry.addEntryType("LINKTYPE", 2); + + /** The NOTATION Catalog Entry type */ + public static final int NOTATION = CatalogEntry.addEntryType("NOTATION", 2); + + /** The PUBLIC Catalog Entry type */ + public static final int PUBLIC = CatalogEntry.addEntryType("PUBLIC", 2); + + /** The SYSTEM Catalog Entry type */ + public static final int SYSTEM = CatalogEntry.addEntryType("SYSTEM", 2); + + /** The URI Catalog Entry type */ + public static final int URI = CatalogEntry.addEntryType("URI", 2); + + /** The REWRITE_SYSTEM Catalog Entry type */ + public static final int REWRITE_SYSTEM = CatalogEntry.addEntryType("REWRITE_SYSTEM", 2); + + /** The REWRITE_URI Catalog Entry type */ + public static final int REWRITE_URI = CatalogEntry.addEntryType("REWRITE_URI", 2); + + /** + * The base URI for relative system identifiers in the catalog. + * This may be changed by BASE entries in the catalog. + */ + protected URL base; + + /** The base URI of the Catalog file currently being parsed. */ + protected URL catalogCwd; + + /** The catalog entries currently known to the system. */ + protected Vector catalogEntries = new Vector(); + + /** The default initial override setting. */ + protected boolean default_override = true; + + /** + *

A vector of catalog files to be loaded.

+ * + *

This list is initially established by + * loadSystemCatalogs when + * it parses the system catalog list, but CATALOG entries may + * contribute to it during the course of parsing.

+ * + * @see #loadSystemCatalogs + * @see #localCatalogFiles + */ + protected Vector catalogFiles = new Vector(); + + /** + *

A vector of catalog files constructed during processing of + * CATALOG entries in the current catalog.

+ * + *

This two-level system is actually necessary to correctly implement + * the semantics of the CATALOG entry. If one catalog file includes + * another with a CATALOG entry, the included catalog logically + * occurs at the end of the including catalog, and after any + * preceding CATALOG entries. In other words, the CATALOG entry + * cannot insert anything into the middle of a catalog file.

+ * + *

When processing reaches the end of each catalog files, any + * elements on this vector are added to the front of the + * catalogFiles vector.

+ * + * @see #catalogFiles + */ + protected Vector localCatalogFiles = new Vector(); + + /** + *

A vector of Catalogs.

+ * + *

The semantics of Catalog resolution are such that each + * catalog is effectively a list of Catalogs (in other words, + * a recursive list of Catalog instances).

+ * + *

Catalogs that are processed as the result of CATALOG or + * DELEGATE* entries are subordinate to the catalog that contained + * them, but they may in turn have subordinate catalogs.

+ * + *

Catalogs are only loaded when they are needed, so this vector + * initially contains a list of Catalog filenames (URLs). If, during + * processing, one of these catalogs has to be loaded, the resulting + * Catalog object is placed in the vector, effectively caching it + * for the next query.

+ */ + protected Vector catalogs = new Vector(); + + /** + *

A vector of DELEGATE* Catalog entries constructed during + * processing of the Catalog.

+ * + *

This two-level system has two purposes; first, it allows + * us to sort the DELEGATE* entries by the length of the partial + * public identifier so that a linear search encounters them in + * the correct order and second, it puts them all at the end of + * the Catalog.

+ * + *

When processing reaches the end of each catalog file, any + * elements on this vector are added to the end of the + * catalogEntries vector. This assures that matching + * PUBLIC keywords are encountered before DELEGATE* entries.

+ */ + protected Vector localDelegate = new Vector(); + + /** + *

A hash of CatalogReaders.

+ * + *

This hash maps MIME types to elements in the readerArr + * vector. This allows the Catalog to quickly locate the reader + * for a particular MIME type.

+ */ + protected Hashtable readerMap = new Hashtable(); + + /** + *

A vector of CatalogReaders.

+ * + *

This vector contains all of the readers in the order that they + * were added. In the event that a catalog is read from a file, where + * the MIME type is unknown, each reader is attempted in turn until + * one succeeds.

+ */ + protected Vector readerArr = new Vector(); + + /** + *

Setup readers.

+ */ + public void setupReaders() { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + spf.setValidating(false); + + SAXCatalogReader saxReader = new SAXCatalogReader(spf); + + saxReader.setCatalogParser(null, "XMLCatalog", + "org.apache.xml.resolver.readers.XCatalogReader"); + + saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, + "catalog", + "org.apache.xml.resolver.readers.OASISXMLCatalogReader"); + + addReader("application/xml", saxReader); + + TR9401CatalogReader textReader = new TR9401CatalogReader(); + addReader("text/plain", textReader); + } + + /** + *

Add a new CatalogReader to the Catalog.

+ * + *

This method allows you to add a new CatalogReader to the + * catalog. The reader will be associated with the specified mimeType. + * You can only have one reader per mimeType.

+ * + *

In the absence of a mimeType (e.g., when reading a catalog + * directly from a file on the local system), the readers are attempted + * in the order that you add them to the Catalog.

+ * + *

Note that subordinate catalogs (created by CATALOG or + * DELEGATE* entries) get a copy of the set of readers present in + * the primary catalog when they are created. Readers added subsequently + * will not be available. For this reason, it is best to add all + * of the readers before the first call to parse a catalog.

+ * + * @param mimeType The MIME type associated with this reader. + * @param reader The CatalogReader to use. + */ + public void addReader(String mimeType, CatalogReader reader) { + if (readerMap.contains(mimeType)) { + Integer pos = (Integer) readerMap.get(mimeType); + readerArr.set(pos.intValue(), reader); + } else { + readerArr.add(reader); + Integer pos = new Integer(readerArr.size()-1); + readerMap.put(mimeType, pos); + } + } + + /** + *

Copies the reader list from the current Catalog to a new Catalog

+ * + *

This method is used internally when constructing a new catalog. + * It copies the current reader associations over to the new catalog. + *

+ * + * @param newCatalog The new Catalog. + */ + protected void copyReaders(Catalog newCatalog) { + // Have to copy the readers in the right order...convert hash to arr + Vector mapArr = new Vector(readerMap.size()); + + // Pad the mapArr out to the right length + for (int count = 0; count < readerMap.size(); count++) { + mapArr.add(null); + } + + Enumeration enum = readerMap.keys(); + while (enum.hasMoreElements()) { + String mimeType = (String) enum.nextElement(); + Integer pos = (Integer) readerMap.get(mimeType); + mapArr.set(pos.intValue(), mimeType); + } + + for (int count = 0; count < mapArr.size(); count++) { + String mimeType = (String) mapArr.get(count); + Integer pos = (Integer) readerMap.get(mimeType); + newCatalog.addReader(mimeType, + (CatalogReader) + readerArr.get(pos.intValue())); + } + } + + /** + *

Create a new Catalog object.

+ * + *

This method constructs a new instance of the running Catalog + * class (which might be a subtype of org.apache.xml.resolver.Catalog). + *

+ * + *

N.B. All Catalog subtypes should call newCatalog() to construct + * a new Catalog. Do not simply use "new Subclass()" since that will + * confuse future subclasses.

+ */ + protected Catalog newCatalog() { + String catalogClass = this.getClass().getName(); + + try { + Catalog c = (Catalog) (Class.forName(catalogClass).newInstance()); + copyReaders(c); + return c; + } catch (ClassNotFoundException cnfe) { + Debug.message(1, "Class Not Found Exception: " + catalogClass); + } catch (IllegalAccessException iae) { + Debug.message(1, "Illegal Access Exception: " + catalogClass); + } catch (InstantiationException ie) { + Debug.message(1, "Instantiation Exception: " + catalogClass); + } catch (ClassCastException cce) { + Debug.message(1, "Class Cast Exception: " + catalogClass); + } catch (Exception e) { + Debug.message(1, "Other Exception: " + catalogClass); + } + + Catalog c = new Catalog(); + copyReaders(c); + return c; + } + + /** + *

Constructs an empty Catalog.

+ * + *

The constructor interrogates the relevant system properties + * and initializes the catalog data structures.

+ */ + public Catalog() { + default_override = CatalogManager.preferPublic(); + } + + /** + *

Returns the current base URI.

+ */ + public String getCurrentBase() { + return base.toString(); + } + + /** + *

Returns the default override setting associated with this + * catalog.

+ * + *

All catalog files loaded by this catalog will have the + * initial override setting specified by this default.

+ */ + public String getDefaultOverride() { + if (default_override) { + return "yes"; + } else { + return "no"; + } + } + + /** + *

Load the system catalog files.

+ * + *

The method adds all of the + * catalogs specified in the xml.catalog.files property + * to the Catalog list.

+ * + * @throws MalformedURLException One of the system catalogs is + * identified with a filename that is not a valid URL. + * @throws IOException One of the system catalogs cannot be read. + */ + public void loadSystemCatalogs() + throws MalformedURLException, IOException { + Vector catalogs = CatalogManager.catalogFiles(); + if (catalogs != null) { + for (int count = 0; count < catalogs.size(); count++) { + catalogFiles.addElement(catalogs.elementAt(count)); + } + } + + if (catalogFiles.size() > 0) { + // This is a little odd. The parseCatalog() method expects + // a filename, but it adds that name to the end of the + // catalogFiles vector, and then processes that vector. + // This allows the system to handle CATALOG entries + // correctly. + // + // In this init case, we take the last element off the + // catalogFiles vector and pass it to parseCatalog. This + // will "do the right thing" in the init case, and allow + // parseCatalog() to do the right thing in the non-init + // case. Honest. + // + String catfile = (String) catalogFiles.lastElement(); + catalogFiles.removeElement(catfile); + parseCatalog(catfile); + } + } + + /** + *

Parse a catalog file, augmenting internal data structures

+ * + * @param fileName The filename of the catalog file to process + * + * @throws MalformedURLException The fileName cannot be turned into + * a valid URL. + * @throws IOException Error reading catalog file. + */ + public synchronized void parseCatalog(String fileName) + throws MalformedURLException, IOException { + + Debug.message(4, "Parse catalog: " + fileName); + + // Put the file into the list of catalogs to process... + // In all cases except the case when initCatalog() is the + // caller, this will be the only catalog initially in the list... + catalogFiles.addElement(fileName); + + // Now process all the pending catalogs... + parsePendingCatalogs(); + } + + /** + *

Parse a catalog file, augmenting internal data structures

+ * + *

Catalogs retrieved over the net may have an associated MIME type. + * The MIME type can be used to select an appropriate reader.

+ * + * @param mimeType The MIME type of the catalog file. + * @param is The InputStream from which the catalog should be read + * + * @throws CatalogException Failed to load catalog + * mimeType. + * @throws IOException Error reading catalog file. + */ + public synchronized void parseCatalog(String mimeType, InputStream is) + throws IOException, CatalogException { + + Debug.message(4, "Parse " + mimeType + " catalog on input stream"); + + CatalogReader reader = null; + + if (readerMap.containsKey(mimeType)) { + int arrayPos = ((Integer) readerMap.get(mimeType)).intValue(); + reader = (CatalogReader) readerArr.get(arrayPos); + } + + if (reader == null) { + String msg = "No CatalogReader for MIME type: " + mimeType; + Debug.message(2, msg); + throw new CatalogException(CatalogException.UNPARSEABLE, msg); + } + + reader.readCatalog(this, is); + + // Now process all the pending catalogs... + parsePendingCatalogs(); + } + + /** + *

Parse all of the pending catalogs.

+ * + *

Catalogs may refer to other catalogs, this method parses + * all of the currently pending catalog files.

+ */ + protected synchronized void parsePendingCatalogs() + throws MalformedURLException, IOException { + + if (!localCatalogFiles.isEmpty()) { + // Move all the localCatalogFiles into the front of + // the catalogFiles queue + Vector newQueue = new Vector(); + Enumeration q = localCatalogFiles.elements(); + while (q.hasMoreElements()) { + newQueue.addElement(q.nextElement()); + } + + // Put the rest of the catalogs on the end of the new list + for (int curCat = 0; curCat < catalogFiles.size(); curCat++) { + String catfile = (String) catalogFiles.elementAt(curCat); + newQueue.addElement(catfile); + } + + catalogFiles = newQueue; + localCatalogFiles.clear(); + } + + // Suppose there are no catalog files to process, but the + // single catalog already parsed included some delegate + // entries? Make sure they don't get lost. + if (catalogFiles.isEmpty() && !localDelegate.isEmpty()) { + Enumeration e = localDelegate.elements(); + while (e.hasMoreElements()) { + catalogEntries.addElement(e.nextElement()); + } + localDelegate.clear(); + } + + // Now process all the files on the catalogFiles vector. This + // vector can grow during processing if CATALOG entries are + // encountered in the catalog + while (!catalogFiles.isEmpty()) { + String catfile = (String) catalogFiles.elementAt(0); + try { + catalogFiles.remove(0); + } catch (ArrayIndexOutOfBoundsException e) { + // can't happen + } + + if (catalogEntries.size() == 0 && catalogs.size() == 0) { + // We haven't parsed any catalogs yet, let this + // catalog be the first... + try { + parseCatalogFile(catfile); + } catch (CatalogException ce) { + System.out.println("FIXME: " + ce.toString()); + } + } else { + // This is a subordinate catalog. We save its name, + // but don't bother to load it unless it's necessary. + catalogs.addElement(catfile); + } + + if (!localCatalogFiles.isEmpty()) { + // Move all the localCatalogFiles into the front of + // the catalogFiles queue + Vector newQueue = new Vector(); + Enumeration q = localCatalogFiles.elements(); + while (q.hasMoreElements()) { + newQueue.addElement(q.nextElement()); + } + + // Put the rest of the catalogs on the end of the new list + for (int curCat = 0; curCat < catalogFiles.size(); curCat++) { + catfile = (String) catalogFiles.elementAt(curCat); + newQueue.addElement(catfile); + } + + catalogFiles = newQueue; + localCatalogFiles.clear(); + } + + if (!localDelegate.isEmpty()) { + Enumeration e = localDelegate.elements(); + while (e.hasMoreElements()) { + catalogEntries.addElement(e.nextElement()); + } + localDelegate.clear(); + } + } + + // We've parsed them all, reinit the vector... + catalogFiles.clear(); + } + + /** + *

Parse a single catalog file, augmenting internal data structures

+ * + * @param fileName The filename of the catalog file to process + * + * @throws MalformedURLException The fileName cannot be turned into + * a valid URL. + * @throws IOException Error reading catalog file. + */ + protected synchronized void parseCatalogFile(String fileName) + throws MalformedURLException, IOException, CatalogException { + + CatalogEntry entry; + + // The base-base is the cwd. If the catalog file is specified + // with a relative path, this assures that it gets resolved + // properly... + try { + // tack on a basename because URLs point to files not dirs + String userdir = fixSlashes(System.getProperty("user.dir")); + catalogCwd = new URL("file:///" + userdir + "/basename"); + } catch (MalformedURLException e) { + String userdir = fixSlashes(System.getProperty("user.dir")); + Debug.message(1, "Malformed URL on cwd", userdir); + catalogCwd = null; + } + + // The initial base URI is the location of the catalog file + try { + base = new URL(catalogCwd, fixSlashes(fileName)); + } catch (MalformedURLException e) { + try { + base = new URL("file:///" + fixSlashes(fileName)); + } catch (MalformedURLException e2) { + Debug.message(1, "Malformed URL on catalog filename", + fixSlashes(fileName)); + base = null; + } + } + + Debug.message(2, "Loading catalog", fileName); + Debug.message(4, "Default BASE", base.toString()); + + fileName = base.toString(); + + DataInputStream inStream = null; + boolean parsed = false; + boolean notFound = false; + + for (int count = 0; !parsed && count < readerArr.size(); count++) { + CatalogReader reader = (CatalogReader) readerArr.get(count); + + try { + notFound = false; + inStream = new DataInputStream(base.openStream()); + } catch (FileNotFoundException fnfe) { + // No catalog; give up! + notFound = true; + break; + } + + try { + reader.readCatalog(this, inStream); + parsed = true; + } catch (CatalogException ce) { + if (ce.getExceptionType() == CatalogException.PARSE_FAILED) { + // give up! + break; + } else { + // try again! + } + } + + try { + inStream.close(); + } catch (IOException e) { + //nop + } + } + + if (!parsed) { + if (notFound) { + Debug.message(3, "Catalog does not exist", fileName); + } else { + Debug.message(1, "Failed to parse catalog", fileName); + } + } + } + + /** + *

Cleanup and process a Catalog entry.

+ * + *

This method processes each Catalog entry, changing mapped + * relative system identifiers into absolute ones (based on the current + * base URI), and maintaining other information about the current + * catalog.

+ * + * @param entry The CatalogEntry to process. + */ + public void addEntry(CatalogEntry entry) { + int type = entry.getEntryType(); + + if (type == BASE) { + String value = entry.getEntryArg(0); + URL newbase = null; + + Debug.message(5, "BASE CUR", base.toString()); + Debug.message(4, "BASE STR", value); + + try { + value = fixSlashes(value); + newbase = new URL(base, value); + } catch (MalformedURLException e) { + try { + newbase = new URL("file:///" + value); + } catch (MalformedURLException e2) { + Debug.message(1, "Malformed URL on base", value); + newbase = null; + } + } + + if (newbase != null) { + base = newbase; + } + + Debug.message(5, "BASE NEW", base.toString()); + } else if (type == CATALOG) { + String fsi = makeAbsolute(entry.getEntryArg(0)); + + Debug.message(4, "CATALOG", fsi); + + localCatalogFiles.addElement(fsi); + } else if (type == PUBLIC) { + String publicid = PublicId.normalize(entry.getEntryArg(0)); + String systemid = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(0, publicid); + entry.setEntryArg(1, systemid); + + Debug.message(4, "PUBLIC", publicid, systemid); + + catalogEntries.addElement(entry); + } else if (type == SYSTEM) { + String systemid = normalizeURI(entry.getEntryArg(0)); + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(1, fsi); + + Debug.message(4, "SYSTEM", systemid, fsi); + + catalogEntries.addElement(entry); + } else if (type == URI) { + String uri = normalizeURI(entry.getEntryArg(0)); + String altURI = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(1, altURI); + + Debug.message(4, "URI", uri, altURI); + + catalogEntries.addElement(entry); + } else if (type == DOCUMENT) { + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(0))); + entry.setEntryArg(0, fsi); + + Debug.message(4, "DOCUMENT", fsi); + + catalogEntries.addElement(entry); + } else if (type == OVERRIDE) { + Debug.message(4, "OVERRIDE", entry.getEntryArg(0)); + + catalogEntries.addElement(entry); + } else if (type == SGMLDECL) { + // meaningless in XML + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(0))); + entry.setEntryArg(0, fsi); + + Debug.message(4, "SGMLDECL", fsi); + + catalogEntries.addElement(entry); + } else if (type == DELEGATE_PUBLIC) { + String ppi = PublicId.normalize(entry.getEntryArg(0)); + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(0, ppi); + entry.setEntryArg(1, fsi); + + Debug.message(4, "DELEGATE_PUBLIC", ppi, fsi); + + addDelegate(entry); + } else if (type == DELEGATE_SYSTEM) { + String psi = normalizeURI(entry.getEntryArg(0)); + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(0, psi); + entry.setEntryArg(1, fsi); + + Debug.message(4, "DELEGATE_SYSTEM", psi, fsi); + + addDelegate(entry); + } else if (type == DELEGATE_URI) { + String pui = normalizeURI(entry.getEntryArg(0)); + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(0, pui); + entry.setEntryArg(1, fsi); + + Debug.message(4, "DELEGATE_URI", pui, fsi); + + addDelegate(entry); + } else if (type == REWRITE_SYSTEM) { + String psi = normalizeURI(entry.getEntryArg(0)); + String rpx = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(0, psi); + entry.setEntryArg(1, rpx); + + Debug.message(4, "REWRITE_SYSTEM", psi, rpx); + + catalogEntries.addElement(entry); + } else if (type == REWRITE_URI) { + String pui = normalizeURI(entry.getEntryArg(0)); + String upx = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(0, pui); + entry.setEntryArg(1, upx); + + Debug.message(4, "REWRITE_URI", pui, upx); + + catalogEntries.addElement(entry); + } else if (type == DOCTYPE) { + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + entry.setEntryArg(1, fsi); + + Debug.message(4, "DOCTYPE", entry.getEntryArg(0), fsi); + + catalogEntries.addElement(entry); + } else if (type == DTDDECL) { + // meaningless in XML + String fpi = PublicId.normalize(entry.getEntryArg(0)); + entry.setEntryArg(0, fpi); + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + entry.setEntryArg(1, fsi); + + Debug.message(4, "DTDDECL", fpi, fsi); + + catalogEntries.addElement(entry); + } else if (type == ENTITY) { + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + entry.setEntryArg(1, fsi); + + Debug.message(4, "ENTITY", entry.getEntryArg(0), fsi); + + catalogEntries.addElement(entry); + } else if (type == LINKTYPE) { + // meaningless in XML + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + entry.setEntryArg(1, fsi); + + Debug.message(4, "LINKTYPE", entry.getEntryArg(0), fsi); + + catalogEntries.addElement(entry); + } else if (type == NOTATION) { + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + entry.setEntryArg(1, fsi); + + Debug.message(4, "NOTATION", entry.getEntryArg(0), fsi); + + catalogEntries.addElement(entry); + } else { + catalogEntries.addElement(entry); + } + } + + /** + *

Handle unknown CatalogEntry types.

+ * + *

This method exists to allow subclasses to deal with unknown + * entry types.

+ */ + public void unknownEntry(Vector strings) { + if (strings != null && strings.size() > 0) { + String keyword = (String) strings.elementAt(0); + Debug.message(2, "Unrecognized token parsing catalog", keyword); + } + } + + /** + *

Parse all subordinate catalogs.

+ * + *

This method recursively parses all of the subordinate catalogs. + * If this method does not throw an exception, you can be confident that + * no subsequent call to any resolve*() method will either, with two + * possible exceptions:

+ * + *
    + *
  1. Delegated catalogs are re-parsed each time they are needed + * (because a variable list of them may be needed in each case, + * depending on the length of the matching partial public identifier).

    + *

    But they are parsed by this method, so as long as they don't + * change or disappear while the program is running, they shouldn't + * generate errors later if they don't generate errors now.

    + *
  2. If you add new catalogs with parseCatalog, they + * won't be loaded until they are needed or until you call + * parseAllCatalogs again.

    + *
+ * + *

On the other hand, if you don't call this method, you may + * successfully parse documents without having to load all possible + * catalogs.

+ * + * @throws MalformedURLException The filename (URL) for a + * subordinate or delegated catalog is not a valid URL. + * @throws IOException Error reading some subordinate or delegated + * catalog file. + */ + public void parseAllCatalogs() + throws MalformedURLException, IOException { + + // Parse all the subordinate catalogs + for (int catPos = 0; catPos < catalogs.size(); catPos++) { + Catalog c = null; + + try { + c = (Catalog) catalogs.elementAt(catPos); + } catch (ClassCastException e) { + String catfile = (String) catalogs.elementAt(catPos); + c = newCatalog(); + + c.parseCatalog(catfile); + catalogs.setElementAt(c, catPos); + c.parseAllCatalogs(); + } + } + + // Parse all the DELEGATE catalogs + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == DELEGATE_PUBLIC + || e.getEntryType() == DELEGATE_SYSTEM + || e.getEntryType() == DELEGATE_URI) { + Catalog dcat = newCatalog(); + dcat.parseCatalog(e.getEntryArg(1)); + } + } + } + + + /** + *

Return the applicable DOCTYPE system identifier.

+ * + * @param entityName The name of the entity (element) for which + * a doctype is required. + * @param publicId The nominal public identifier for the doctype + * (as provided in the source document). + * @param systemId The nominal system identifier for the doctype + * (as provided in the source document). + * + * @return The system identifier to use for the doctype. + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveDoctype(String entityName, + String publicId, + String systemId) + throws MalformedURLException, IOException { + String resolved = null; + + Debug.message(3, "resolveDoctype(" + +entityName+","+publicId+","+systemId+")"); + + systemId = normalizeURI(systemId); + + if (publicId != null && publicId.startsWith("urn:publicid:")) { + publicId = PublicId.decodeURN(publicId); + } + + if (systemId != null && systemId.startsWith("urn:publicid:")) { + systemId = PublicId.decodeURN(systemId); + if (publicId != null && !publicId.equals(systemId)) { + Debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier"); + systemId = null; + } else { + publicId = systemId; + systemId = null; + } + } + + if (systemId != null) { + // If there's a SYSTEM entry in this catalog, use it + resolved = resolveLocalSystem(systemId); + if (resolved != null) { + return resolved; + } + } + + if (publicId != null) { + // If there's a PUBLIC entry in this catalog, use it + resolved = resolveLocalPublic(DOCTYPE, + entityName, + publicId, + systemId); + if (resolved != null) { + return resolved; + } + } + + // If there's a DOCTYPE entry in this catalog, use it + boolean over = default_override; + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == OVERRIDE) { + over = e.getEntryArg(0).equalsIgnoreCase("YES"); + continue; + } + + if (e.getEntryType() == DOCTYPE + && e.getEntryArg(0).equals(entityName)) { + if (over || systemId == null) { + return e.getEntryArg(1); + } + } + } + + // Otherwise, look in the subordinate catalogs + return resolveSubordinateCatalogs(DOCTYPE, + entityName, + publicId, + systemId); + } + + /** + *

Return the applicable DOCUMENT entry.

+ * + * @return The system identifier to use for the doctype. + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveDocument() + throws MalformedURLException, IOException { + // If there's a DOCUMENT entry, return it + + Debug.message(3, "resolveDocument"); + + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == DOCUMENT) { + return e.getEntryArg(1); //FIXME check this + } + } + + return resolveSubordinateCatalogs(DOCUMENT, + null, null, null); + } + + /** + *

Return the applicable ENTITY system identifier.

+ * + * @param entityName The name of the entity for which + * a system identifier is required. + * @param publicId The nominal public identifier for the entity + * (as provided in the source document). + * @param systemId The nominal system identifier for the entity + * (as provided in the source document). + * + * @return The system identifier to use for the entity. + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveEntity(String entityName, + String publicId, + String systemId) + throws MalformedURLException, IOException { + String resolved = null; + + Debug.message(3, "resolveEntity(" + +entityName+","+publicId+","+systemId+")"); + + systemId = normalizeURI(systemId); + + if (publicId != null && publicId.startsWith("urn:publicid:")) { + publicId = PublicId.decodeURN(publicId); + } + + if (systemId != null && systemId.startsWith("urn:publicid:")) { + systemId = PublicId.decodeURN(systemId); + if (publicId != null && !publicId.equals(systemId)) { + Debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier"); + systemId = null; + } else { + publicId = systemId; + systemId = null; + } + } + + if (systemId != null) { + // If there's a SYSTEM entry in this catalog, use it + resolved = resolveLocalSystem(systemId); + if (resolved != null) { + return resolved; + } + } + + if (publicId != null) { + // If there's a PUBLIC entry in this catalog, use it + resolved = resolveLocalPublic(ENTITY, + entityName, + publicId, + systemId); + if (resolved != null) { + return resolved; + } + } + + // If there's a ENTITY entry in this catalog, use it + boolean over = default_override; + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == OVERRIDE) { + over = e.getEntryArg(0).equalsIgnoreCase("YES"); + continue; + } + + if (e.getEntryType() == ENTITY + && e.getEntryArg(0).equals(entityName)) { + if (over || systemId == null) { + return e.getEntryArg(1); + } + } + } + + // Otherwise, look in the subordinate catalogs + return resolveSubordinateCatalogs(ENTITY, + entityName, + publicId, + systemId); + } + + /** + *

Return the applicable NOTATION system identifier.

+ * + * @param notationName The name of the notation for which + * a doctype is required. + * @param publicId The nominal public identifier for the notation + * (as provided in the source document). + * @param systemId The nominal system identifier for the notation + * (as provided in the source document). + * + * @return The system identifier to use for the notation. + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveNotation(String notationName, + String publicId, + String systemId) + throws MalformedURLException, IOException { + String resolved = null; + + Debug.message(3, "resolveNotation(" + +notationName+","+publicId+","+systemId+")"); + + systemId = normalizeURI(systemId); + + if (publicId != null && publicId.startsWith("urn:publicid:")) { + publicId = PublicId.decodeURN(publicId); + } + + if (systemId != null && systemId.startsWith("urn:publicid:")) { + systemId = PublicId.decodeURN(systemId); + if (publicId != null && !publicId.equals(systemId)) { + Debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier"); + systemId = null; + } else { + publicId = systemId; + systemId = null; + } + } + + if (systemId != null) { + // If there's a SYSTEM entry in this catalog, use it + resolved = resolveLocalSystem(systemId); + if (resolved != null) { + return resolved; + } + } + + if (publicId != null) { + // If there's a PUBLIC entry in this catalog, use it + resolved = resolveLocalPublic(NOTATION, + notationName, + publicId, + systemId); + if (resolved != null) { + return resolved; + } + } + + // If there's a NOTATION entry in this catalog, use it + boolean over = default_override; + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == OVERRIDE) { + over = e.getEntryArg(0).equalsIgnoreCase("YES"); + continue; + } + + if (e.getEntryType() == NOTATION + && e.getEntryArg(0).equals(notationName)) { + if (over || systemId == null) { + return e.getEntryArg(1); + } + } + } + + // Otherwise, look in the subordinate catalogs + return resolveSubordinateCatalogs(NOTATION, + notationName, + publicId, + systemId); + } + + /** + *

Return the applicable PUBLIC or SYSTEM identifier.

+ * + *

This method searches the Catalog and returns the system + * identifier specified for the given system or + * public identifiers. If + * no appropriate PUBLIC or SYSTEM entry is found in the Catalog, + * null is returned.

+ * + * @param publicId The public identifier to locate in the catalog. + * Public identifiers are normalized before comparison. + * @param systemId The nominal system identifier for the entity + * in question (as provided in the source document). + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + * + * @return The system identifier to use. + * Note that the nominal system identifier is not returned if a + * match is not found in the catalog, instead null is returned + * to indicate that no match was found. + */ + public String resolvePublic(String publicId, String systemId) + throws MalformedURLException, IOException { + + Debug.message(3, "resolvePublic("+publicId+","+systemId+")"); + + systemId = normalizeURI(systemId); + + if (publicId != null && publicId.startsWith("urn:publicid:")) { + publicId = PublicId.decodeURN(publicId); + } + + if (systemId != null && systemId.startsWith("urn:publicid:")) { + systemId = PublicId.decodeURN(systemId); + if (publicId != null && !publicId.equals(systemId)) { + Debug.message(1, "urn:publicid: system identifier differs from public identifier; using public identifier"); + systemId = null; + } else { + publicId = systemId; + systemId = null; + } + } + + // If there's a SYSTEM entry in this catalog, use it + if (systemId != null) { + String resolved = resolveLocalSystem(systemId); + if (resolved != null) { + return resolved; + } + } + + // If there's a PUBLIC entry in this catalog, use it + String resolved = resolveLocalPublic(PUBLIC, + null, + publicId, + systemId); + if (resolved != null) { + return resolved; + } + + // Otherwise, look in the subordinate catalogs + return resolveSubordinateCatalogs(PUBLIC, + null, + publicId, + systemId); + } + + /** + *

Return the applicable PUBLIC or SYSTEM identifier

+ * + *

This method searches the Catalog and returns the system + * identifier specified for the given system or public identifiers. + * If no appropriate PUBLIC or SYSTEM entry is found in the Catalog, + * delegated Catalogs are interrogated.

+ * + *

There are four possible cases:

+ * + * + * + * @param entityType The CatalogEntry type for which this query is + * being conducted. This is necessary in order to do the approprate + * query on a delegated catalog. + * @param entityName The name of the entity being searched for, if + * appropriate. + * @param publicId The public identifier of the entity in question. + * @param systemId The nominal system identifier for the entity + * in question (as provided in the source document). + * + * @throws MalformedURLException The formal system identifier of a + * delegated catalog cannot be turned into a valid URL. + * @throws IOException Error reading delegated catalog file. + * + * @return The system identifier to use. + * Note that the nominal system identifier is not returned if a + * match is not found in the catalog, instead null is returned + * to indicate that no match was found. + */ + protected synchronized String resolveLocalPublic(int entityType, + String entityName, + String publicId, + String systemId) + throws MalformedURLException, IOException { + + // Always normalize the public identifier before attempting a match + publicId = PublicId.normalize(publicId); + + // If there's a SYSTEM entry in this catalog, use it + if (systemId != null) { + String resolved = resolveLocalSystem(systemId); + if (resolved != null) { + return resolved; + } + } + + // If there's a PUBLIC entry in this catalog, use it + boolean over = default_override; + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == OVERRIDE) { + over = e.getEntryArg(0).equalsIgnoreCase("YES"); + continue; + } + + if (e.getEntryType() == PUBLIC + && e.getEntryArg(0).equals(publicId)) { + if (over || systemId == null) { + return e.getEntryArg(1); + } + } + } + + // If there's a DELEGATE_PUBLIC entry in this catalog, use it + over = default_override; + enum = catalogEntries.elements(); + Vector delCats = new Vector(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == OVERRIDE) { + over = e.getEntryArg(0).equalsIgnoreCase("YES"); + continue; + } + + if (e.getEntryType() == DELEGATE_PUBLIC + && (over || systemId == null)) { + String p = (String) e.getEntryArg(0); + if (p.length() <= publicId.length() + && p.equals(publicId.substring(0, p.length()))) { + // delegate this match to the other catalog + + delCats.addElement(e.getEntryArg(1)); + } + } + } + + if (delCats.size() > 0) { + Enumeration enumCats = delCats.elements(); + + if (Debug.getDebug() > 1) { + Debug.message(2, "Switching to delegated catalog(s):"); + while (enumCats.hasMoreElements()) { + String delegatedCatalog = (String) enumCats.nextElement(); + Debug.message(2, "\t" + delegatedCatalog); + } + } + + Catalog dcat = newCatalog(); + + enumCats = delCats.elements(); + while (enumCats.hasMoreElements()) { + String delegatedCatalog = (String) enumCats.nextElement(); + dcat.parseCatalog(delegatedCatalog); + } + + return dcat.resolvePublic(publicId, null); + } + + // Nada! + return null; + } + + /** + *

Return the applicable SYSTEM system identifier

+ * + *

If a SYSTEM entry exists in the Catalog + * for the system ID specified, return the mapped value.

+ * + *

On Windows-based operating systems, the comparison between + * the system identifier provided and the SYSTEM entries in the + * Catalog is case-insensitive.

+ * + * @param systemId The system ID to locate in the catalog. + * + * @return The resolved system identifier. + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveSystem(String systemId) + throws MalformedURLException, IOException { + + Debug.message(3, "resolveSystem("+systemId+")"); + + systemId = normalizeURI(systemId); + + if (systemId != null && systemId.startsWith("urn:publicid:")) { + systemId = PublicId.decodeURN(systemId); + return resolvePublic(systemId, null); + } + + // If there's a SYSTEM entry in this catalog, use it + if (systemId != null) { + String resolved = resolveLocalSystem(systemId); + if (resolved != null) { + return resolved; + } + } + + // Otherwise, look in the subordinate catalogs + return resolveSubordinateCatalogs(SYSTEM, + null, + null, + systemId); + } + + /** + *

Return the applicable SYSTEM system identifier in this + * catalog.

+ * + *

If a SYSTEM entry exists in the catalog file + * for the system ID specified, return the mapped value.

+ * + * @param systemId The system ID to locate in the catalog + * + * @return The mapped system identifier or null + */ + protected String resolveLocalSystem(String systemId) + throws MalformedURLException, IOException { + + String osname = System.getProperty("os.name"); + boolean windows = (osname.indexOf("Windows") >= 0); + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == SYSTEM + && (e.getEntryArg(0).equals(systemId) + || (windows + && e.getEntryArg(0).equalsIgnoreCase(systemId)))) { + return e.getEntryArg(1); + } + } + + // If there's a REWRITE_SYSTEM entry in this catalog, use it + enum = catalogEntries.elements(); + String startString = null; + String prefix = null; + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + + if (e.getEntryType() == REWRITE_SYSTEM) { + String p = (String) e.getEntryArg(0); + if (p.length() <= systemId.length() + && p.equals(systemId.substring(0, p.length()))) { + // Is this the longest prefix? + if (startString == null + || p.length() > startString.length()) { + startString = p; + prefix = e.getEntryArg(1); + } + } + } + + if (prefix != null) { + // return the systemId with the new prefix + return prefix + systemId.substring(startString.length()); + } + } + + // If there's a DELEGATE_SYSTEM entry in this catalog, use it + enum = catalogEntries.elements(); + Vector delCats = new Vector(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + + if (e.getEntryType() == DELEGATE_SYSTEM) { + String p = (String) e.getEntryArg(0); + if (p.length() <= systemId.length() + && p.equals(systemId.substring(0, p.length()))) { + // delegate this match to the other catalog + + delCats.addElement(e.getEntryArg(1)); + } + } + } + + if (delCats.size() > 0) { + Enumeration enumCats = delCats.elements(); + + if (Debug.getDebug() > 1) { + Debug.message(2, "Switching to delegated catalog(s):"); + while (enumCats.hasMoreElements()) { + String delegatedCatalog = (String) enumCats.nextElement(); + Debug.message(2, "\t" + delegatedCatalog); + } + } + + Catalog dcat = newCatalog(); + + enumCats = delCats.elements(); + while (enumCats.hasMoreElements()) { + String delegatedCatalog = (String) enumCats.nextElement(); + dcat.parseCatalog(delegatedCatalog); + } + + return dcat.resolveSystem(systemId); + } + + return null; + } + + /** + *

Return the applicable URI

+ * + *

If a URI entry exists in the Catalog + * for the URI specified, return the mapped value.

+ * + *

URI comparison is case sensitive.

+ * + * @param uri The URI to locate in the catalog. + * + * @return The resolved URI. + * + * @throws MalformedURLException The system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveURI(String uri) + throws MalformedURLException, IOException { + + Debug.message(3, "resolveURI("+uri+")"); + + uri = normalizeURI(uri); + + if (uri != null && uri.startsWith("urn:publicid:")) { + uri = PublicId.decodeURN(uri); + return resolvePublic(uri, null); + } + + // If there's a URI entry in this catalog, use it + if (uri != null) { + String resolved = resolveLocalURI(uri); + if (resolved != null) { + return resolved; + } + } + + // Otherwise, look in the subordinate catalogs + return resolveSubordinateCatalogs(URI, + null, + null, + uri); + } + + /** + *

Return the applicable URI in this + * catalog.

+ * + *

If a URI entry exists in the catalog file + * for the URI specified, return the mapped value.

+ * + * @param uri The URI to locate in the catalog + * + * @return The mapped URI or null + */ + protected String resolveLocalURI(String uri) + throws MalformedURLException, IOException { + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == URI + && (e.getEntryArg(0).equals(uri))) { + return e.getEntryArg(1); + } + } + + // If there's a REWRITE_URI entry in this catalog, use it + enum = catalogEntries.elements(); + String startString = null; + String prefix = null; + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + + if (e.getEntryType() == REWRITE_URI) { + String p = (String) e.getEntryArg(0); + if (p.length() <= uri.length() + && p.equals(uri.substring(0, p.length()))) { + // Is this the longest prefix? + if (startString == null + || p.length() > startString.length()) { + startString = p; + prefix = e.getEntryArg(1); + } + } + } + + if (prefix != null) { + // return the systemId with the new prefix + return prefix + uri.substring(startString.length()); + } + } + + // If there's a DELEGATE_URI entry in this catalog, use it + enum = catalogEntries.elements(); + Vector delCats = new Vector(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + + if (e.getEntryType() == DELEGATE_URI) { + String p = (String) e.getEntryArg(0); + if (p.length() <= uri.length() + && p.equals(uri.substring(0, p.length()))) { + // delegate this match to the other catalog + + delCats.addElement(e.getEntryArg(1)); + } + } + } + + if (delCats.size() > 0) { + Enumeration enumCats = delCats.elements(); + + if (Debug.getDebug() > 1) { + Debug.message(2, "Switching to delegated catalog(s):"); + while (enumCats.hasMoreElements()) { + String delegatedCatalog = (String) enumCats.nextElement(); + Debug.message(2, "\t" + delegatedCatalog); + } + } + + Catalog dcat = newCatalog(); + + enumCats = delCats.elements(); + while (enumCats.hasMoreElements()) { + String delegatedCatalog = (String) enumCats.nextElement(); + dcat.parseCatalog(delegatedCatalog); + } + + return dcat.resolveURI(uri); + } + + return null; + } + + /** + *

Search the subordinate catalogs, in order, looking for a + * match.

+ * + *

This method searches the Catalog and returns the system + * identifier specified for the given entity type with the given + * name, public, and system identifiers. In some contexts, these + * may be null.

+ * + * @param entityType The CatalogEntry type for which this query is + * being conducted. This is necessary in order to do the approprate + * query on a subordinate catalog. + * @param entityName The name of the entity being searched for, if + * appropriate. + * @param publicId The public identifier of the entity in question + * (as provided in the source document). + * @param systemId The nominal system identifier for the entity + * in question (as provided in the source document). This parameter is + * overloaded for the URI entry type. + * + * @throws MalformedURLException The formal system identifier of a + * delegated catalog cannot be turned into a valid URL. + * @throws IOException Error reading delegated catalog file. + * + * @return The system identifier to use. + * Note that the nominal system identifier is not returned if a + * match is not found in the catalog, instead null is returned + * to indicate that no match was found. + */ + protected synchronized String resolveSubordinateCatalogs(int entityType, + String entityName, + String publicId, + String systemId) + throws MalformedURLException, IOException { + + for (int catPos = 0; catPos < catalogs.size(); catPos++) { + Catalog c = null; + + try { + c = (Catalog) catalogs.elementAt(catPos); + } catch (ClassCastException e) { + String catfile = (String) catalogs.elementAt(catPos); + c = newCatalog(); + + try { + c.parseCatalog(catfile); + } catch (MalformedURLException mue) { + Debug.message(1, "Malformed Catalog URL", catfile); + } catch (FileNotFoundException fnfe) { + Debug.message(1, "Failed to load catalog, file not found", + catfile); + } catch (IOException ioe) { + Debug.message(1, "Failed to load catalog, I/O error", catfile); + } + + catalogs.setElementAt(c, catPos); + } + + String resolved = null; + + // Ok, now what are we supposed to call here? + if (entityType == DOCTYPE) { + resolved = c.resolveDoctype(entityName, + publicId, + systemId); + } else if (entityType == DOCUMENT) { + resolved = c.resolveDocument(); + } else if (entityType == ENTITY) { + resolved = c.resolveEntity(entityName, + publicId, + systemId); + } else if (entityType == NOTATION) { + resolved = c.resolveNotation(entityName, + publicId, + systemId); + } else if (entityType == PUBLIC) { + resolved = c.resolvePublic(publicId, systemId); + } else if (entityType == SYSTEM) { + resolved = c.resolveSystem(systemId); + } else if (entityType == URI) { + resolved = c.resolveURI(systemId); + } + + if (resolved != null) { + return resolved; + } + } + + return null; + } + + // ----------------------------------------------------------------- + + /** + *

Replace backslashes with forward slashes. (URLs always use + * forward slashes.)

+ * + * @param sysid The input system identifier. + * @return The same system identifier with backslashes turned into + * forward slashes. + */ + protected String fixSlashes (String sysid) { + return sysid.replace('\\', '/'); + } + + /** + *

Construct an absolute URI from a relative one, using the current + * base URI.

+ * + * @param sysid The (possibly relative) system identifier + * @return The system identifier made absolute with respect to the + * current {@link #base}. + */ + protected String makeAbsolute(String sysid) { + URL local = null; + + sysid = fixSlashes(sysid); + + try { + local = new URL(base, sysid); + } catch (MalformedURLException e) { + Debug.message(1, "Malformed URL on system identifier", sysid); + } + + if (local != null) { + return local.toString(); + } else { + return sysid; + } + } + + /** + *

Perform character normalization on a URI reference.

+ * + * @param uriref The URI reference + * @return The normalized URI reference. + */ + protected String normalizeURI(String uriref) { + String newRef = ""; + byte[] bytes; + + if (uriref == null) { + return null; + } + + try { + bytes = uriref.getBytes("UTF-8"); + } catch (UnsupportedEncodingException uee) { + // this can't happen + Debug.message(1, "UTF-8 is an unsupported encoding!?"); + return uriref; + } + + for (int count = 0; count < bytes.length; count++) { + int ch = bytes[count] & 0xFF; + + if ((ch <= 0x20) // ctrl + || (ch > 0x7F) // high ascii + || (ch == 0x22) // " + || (ch == 0x3C) // < + || (ch == 0x3E) // > + || (ch == 0x5C) // \ + || (ch == 0x5E) // ^ + || (ch == 0x60) // ` + || (ch == 0x7B) // { + || (ch == 0x7C) // | + || (ch == 0x7D) // } + || (ch == 0x7F)) { + newRef += encodedByte(ch); + } else { + newRef += (char) bytes[count]; + } + } + + return newRef; + } + + /** + *

Perform %-encoding on a single byte.

+ * + * @param b The 8-bit integer that represents th byte. (Bytes are signed + but encoding needs to look at the bytes unsigned.) + * @return The %-encoded string for the byte in question. + */ + protected String encodedByte (int b) { + String hex = Integer.toHexString(b).toUpperCase(); + if (hex.length() < 2) { + return "%0" + hex; + } else { + return "%" + hex; + } + } + + // ----------------------------------------------------------------- + + /** + *

Add to the current list of delegated catalogs.

+ * + *

This method always constructs the {@link #localDelegate} + * vector so that it is ordered by length of partial + * public identifier.

+ * + * @param entry The DELEGATE catalog entry + */ + protected void addDelegate(CatalogEntry entry) { + int pos = 0; + String partial = entry.getEntryArg(0); + + Enumeration local = localDelegate.elements(); + while (local.hasMoreElements()) { + CatalogEntry dpe = (CatalogEntry) local.nextElement(); + String dp = dpe.getEntryArg(0); + if (dp.equals(partial)) { + // we already have this prefix + return; + } + if (dp.length() > partial.length()) { + pos++; + } + if (dp.length() < partial.length()) { + break; + } + } + + // now insert partial into the vector at [pos] + if (localDelegate.size() == 0) { + localDelegate.addElement(entry); + } else { + localDelegate.insertElementAt(entry, pos); + } + } +} + diff --git a/java/src/org/apache/xml/resolver/CatalogEntry.java b/java/src/org/apache/xml/resolver/CatalogEntry.java new file mode 100644 index 0000000..a7340ab --- /dev/null +++ b/java/src/org/apache/xml/resolver/CatalogEntry.java @@ -0,0 +1,289 @@ +// CatalogEntry.java - Represents Catalog entries + +/* + * 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; + +import java.util.Hashtable; +import java.util.Vector; + +/** + *

Represents a Catalog entry.

+ * + *

Instances of this class represent individual entries + * in a Catalog.

+ * + *

Each catalog entry has a unique name and is associated with + * an arbitrary number of arguments (all strings). For example, the + * TR9401 catalog entry "PUBLIC" has two arguments, a public identifier + * and a system identifier. Each entry has a unique numeric type, + * assigned automatically when the entry type is created.

+ * + *

The number and type of catalog entries is maintained + * statically. Catalog classes, or their subclasses, can add + * new entry types, but all Catalog objects share the same global pool + * of types.

+ * + *

Initially there are no valid entries.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class CatalogEntry { + /** The nextEntry is the ordinal number of the next entry type. */ + protected static int nextEntry = 0; + + /** + * The entryTypes vector maps catalog entry names + * (e.g., 'BASE' or 'SYSTEM') to their type (1, 2, etc.). + * Names are case sensitive. + */ + protected static Hashtable entryTypes = new Hashtable(); + + /** The entryTypes vector maps catalog entry types to the + number of arguments they're required to have. */ + protected static Vector entryArgs = new Vector(); + + /** + *

Adds a new catalog entry type.

+ * + * @param name The name of the catalog entry type. This must be + * unique among all types and is case-sensitive. (Adding a duplicate + * name effectively replaces the old type with the new type.) + * @param numArgs The number of arguments that this entry type + * is required to have. There is no provision for variable numbers + * of arguments. + * @return The type for the new entry. + */ + public static int addEntryType(String name, int numArgs) { + entryTypes.put(name, new Integer(nextEntry)); + entryArgs.add(nextEntry, new Integer(numArgs)); + nextEntry++; + + return nextEntry-1; + } + + /** + *

Lookup an entry type

+ * + * @param name The name of the catalog entry type. + * @return The type of the catalog entry with the specified name. + * @throws InvalidCatalogEntryTypeException if no entry has the + * specified name. + */ + public static int getEntryType(String name) + throws CatalogException { + if (!entryTypes.containsKey(name)) { + throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE); + } + + Integer iType = (Integer) entryTypes.get(name); + + if (iType == null) { + throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE); + } + + return iType.intValue(); + } + + /** + *

Find out how many arguments an entry is required to have.

+ * + * @param name The name of the catalog entry type. + * @return The number of arguments that entry type is required to have. + * @throws InvalidCatalogEntryTypeException if no entry has the + * specified name. + */ + public static int getEntryArgCount(String name) + throws CatalogException { + return getEntryArgCount(getEntryType(name)); + } + + /** + *

Find out how many arguments an entry is required to have.

+ * + * @param type A valid catalog entry type. + * @return The number of arguments that entry type is required to have. + * @throws InvalidCatalogEntryTypeException if the type is invalid. + */ + public static int getEntryArgCount(int type) + throws CatalogException { + try { + Integer iArgs = (Integer) entryArgs.get(type); + return iArgs.intValue(); + } catch (ArrayIndexOutOfBoundsException e) { + throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE); + } + } + + /** The entry type of this entry */ + protected int entryType = 0; + + /** The arguments associated with this entry */ + protected Vector args = null; + + /** + *

Null constructor; something for subclasses to call.

+ */ + public CatalogEntry() {} + + /** + *

Construct a catalog entry of the specified type.

+ * + * @param name The name of the entry type + * @param args A String Vector of arguments + * @throws InvalidCatalogEntryTypeException if no such entry type + * exists. + * @throws InvalidCatalogEntryException if the wrong number of arguments + * is passed. + */ + public CatalogEntry(String name, Vector args) + throws CatalogException { + Integer iType = (Integer) entryTypes.get(name); + + if (iType == null) { + throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE); + } + + int type = iType.intValue(); + + try { + Integer iArgs = (Integer) entryArgs.get(type); + if (iArgs.intValue() != args.size()) { + throw new CatalogException(CatalogException.INVALID_ENTRY); + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE); + } + + entryType = type; + this.args = args; + } + + /** + *

Construct a catalog entry of the specified type.

+ * + * @param name The name of the entry type + * @param args A String Vector of arguments + * @throws InvalidCatalogEntryTypeException if no such entry type + * exists. + * @throws InvalidCatalogEntryException if the wrong number of arguments + * is passed. + */ + public CatalogEntry(int type, Vector args) + throws CatalogException { + try { + Integer iArgs = (Integer) entryArgs.get(type); + if (iArgs.intValue() != args.size()) { + throw new CatalogException(CatalogException.INVALID_ENTRY); + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE); + } + + entryType = type; + this.args = args; + } + + /** + *

Get the entry type.

+ * + * @return The entry type of the CatalogEntry + */ + public int getEntryType() { + return entryType; + } + + /** + *

Get an entry argument.

+ * + * @param argNum The argument number (arguments are numbered from 0). + * @return The specified argument or null if an invalid argNum is + * provided. + */ + public String getEntryArg(int argNum) { + try { + String arg = (String) args.get(argNum); + return arg; + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } + + /** + *

Set an entry argument.

+ * + *

Catalogs sometimes need to adjust the catlog entry parameters, + * for example to make a relative URI absolute with respect to the + * current base URI. But in general, this function should only be + * called shortly after object creation to do some sort of cleanup. + * Catalog entries should not mutate over time.

+ * + * @param argNum The argument number (arguments are numbered from 0). + * @throws ArrayIndexOutOfBoundsException if an invalid argument + * number is provided. + */ + public void setEntryArg(int argNum, String newspec) + throws ArrayIndexOutOfBoundsException { + args.set(argNum, newspec); + } +} diff --git a/java/src/org/apache/xml/resolver/CatalogException.java b/java/src/org/apache/xml/resolver/CatalogException.java new file mode 100644 index 0000000..27170e6 --- /dev/null +++ b/java/src/org/apache/xml/resolver/CatalogException.java @@ -0,0 +1,203 @@ +// CatalogException.java - Catalog exception + +/* + * 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; + +/** + *

Signal Catalog exception.

+ * + *

This exception is thrown if an error occurs loading a + * catalog file.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class CatalogException extends Exception { + /** A wrapper around another exception */ + public static final int WRAPPER = 1; + /** An invalid entry */ + public static final int INVALID_ENTRY = 2; + /** An invalid entry type */ + public static final int INVALID_ENTRY_TYPE = 3; + /** Could not instantiate an XML parser */ + public static final int NO_XML_PARSER = 4; + /** Unknown XML format */ + public static final int UNKNOWN_FORMAT = 5; + /** Unparseable XML catalog (not XML)*/ + public static final int UNPARSEABLE = 6; + /** XML but parse failed */ + public static final int PARSE_FAILED = 7; + + /** + * The embedded exception if tunnelling, or null. + */ + private Exception exception = null; + private int exceptionType = 0; + + /** + * Create a new CatalogException. + * + * @param type The exception type + * @param message The error or warning message. + */ + public CatalogException (int type, String message) { + super(message); + this.exceptionType = type; + this.exception = null; + } + + /** + * Create a new CatalogException. + * + * @param type The exception type + */ + public CatalogException (int type) { + super("Catalog Exception " + type); + this.exceptionType = type; + this.exception = null; + } + + /** + * Create a new CatalogException wrapping an existing exception. + * + *

The existing exception will be embedded in the new + * one, and its message will become the default message for + * the CatalogException.

+ * + * @param e The exception to be wrapped in a CatalogException. + */ + public CatalogException (Exception e) { + super(); + this.exceptionType = WRAPPER; + this.exception = e; + } + + /** + * Create a new CatalogException from an existing exception. + * + *

The existing exception will be embedded in the new + * one, but the new exception will have its own message.

+ * + * @param message The detail message. + * @param e The exception to be wrapped in a CatalogException. + */ + public CatalogException (String message, Exception e) { + super(message); + this.exceptionType = WRAPPER; + this.exception = e; + } + + /** + * Return a detail message for this exception. + * + *

If there is an embedded exception, and if the CatalogException + * has no detail message of its own, this method will return + * the detail message from the embedded exception.

+ * + * @return The error or warning message. + */ + public String getMessage () + { + String message = super.getMessage(); + + if (message == null && exception != null) { + return exception.getMessage(); + } else { + return message; + } + } + + /** + * Return the embedded exception, if any. + * + * @return The embedded exception, or null if there is none. + */ + public Exception getException () + { + return exception; + } + + /** + * Return the exception type + * + * @return The exception type + */ + public int getExceptionType () + { + return exceptionType; + } + + /** + * Override toString to pick up any embedded exception. + * + * @return A string representation of this exception. + */ + public String toString () + { + if (exception != null) { + return exception.toString(); + } else { + return super.toString(); + } + } +} diff --git a/java/src/org/apache/xml/resolver/CatalogManager.java b/java/src/org/apache/xml/resolver/CatalogManager.java new file mode 100644 index 0000000..67063ce --- /dev/null +++ b/java/src/org/apache/xml/resolver/CatalogManager.java @@ -0,0 +1,463 @@ +// CatalogManager.java - Access CatalogManager.properties + +/* + * 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; + +import java.lang.ClassNotFoundException; +import java.lang.IllegalAccessException; +import java.lang.InstantiationException; +import java.lang.SecurityException; +import java.lang.ClassCastException; +import java.net.URL; +import java.net.MalformedURLException; + +import java.io.*; +import java.util.*; + +/** + *

CatalogManager provides an interface to the catalog properties.

+ * + *

Properties can come from two places: from system properties or + * from a CatalogManager.properties file. This class provides a transparent + * interface to both, with system properties preferred over property file values.

+ * + *

The following table summarizes the properties:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
System PropertyCatalogManager.properties
Property
Description
xml.catalog.ignoreMissing If true, a missing CatalogManager.properties file or missing properties + * within that file will not generate warning messages. See also the + * ignoreMissingProperties method.
xml.catalog.filescatalogsThe semicolon-delimited list of catalog files.
 relative-catalogsIf false, relative catalog URIs are made absolute with respect to the base URI of + * the CatalogManager.properties file. This setting only applies to catalog + * URIs obtained from the catalogs property in the + * CatalogManager.properties file
xml.catalog.verbosityverbosityIf non-zero, the Catalog classes will print informative and debugging messages. + * The higher the number, the more messages.
xml.catalog.preferpreferWhich identifier is preferred, "public" or "system"?
xml.catalog.staticCatalogstatic-catalogShould a single catalog be constructed for all parsing, or should a different + * catalog be created for each parser?
xml.catalog.allowPIallow-oasis-xml-catalog-piIf the source document contains "oasis-xml-catalog" processing instructions, + * should they be used?
xml.catalog.classNamecatalog-class-nameIf you're using the convenience classes + * org.apache.xml.resolver.tools.*), this setting + * allows you to specify an alternate class name to use for the underlying + * catalog.
+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ + +public class CatalogManager { + private static String pFiles = "xml.catalog.files"; + private static String pVerbosity = "xml.catalog.verbosity"; + private static String pPrefer = "xml.catalog.prefer"; + private static String pStatic = "xml.catalog.staticCatalog"; + private static String pAllowPI = "xml.catalog.allowPI"; + private static String pClassname = "xml.catalog.className"; + private static String pIgnoreMissing = "xml.catalog.ignoreMissing"; + + /** Flag to ignore missing property files and/or properties */ + private static boolean ignoreMissingProperties + = (System.getProperty(pIgnoreMissing) != null + || System.getProperty(pFiles) != null); + + /** Holds the resources after they are loaded from the file. */ + private static ResourceBundle resources; + + /** The name of the CatalogManager properties file. */ + private static String propertyFile = "CatalogManager.properties"; + + /** The location of the propertyFile */ + private static URL propertyFileURI = null; + + /** Default catalog files list. */ + private static String defaultCatalogFiles = "./xcatalog"; + + /** Default verbosity level if there is no property setting for it. */ + private static int defaultVerbosity = 1; + + /** Default preference setting. */ + private static boolean defaultPreferPublic = true; + + /** Default setting of the static catalog flag. */ + private static boolean defaultStaticCatalog = true; + + /** Default setting of the oasisXMLCatalogPI flag. */ + private static boolean defaultOasisXMLCatalogPI = true; + + /** Default setting of the relativeCatalogs flag. */ + private static boolean defaultRelativeCatalogs = true; + + /** + *

Load the properties from the propertyFile and build the + * resources from it.

+ */ + private synchronized static void readProperties() { + try { + propertyFileURI = CatalogManager.class.getResource("/"+propertyFile); + InputStream in = + CatalogManager.class.getResourceAsStream("/"+propertyFile); + if (in==null) { + if (!ignoreMissingProperties) { + System.err.println("Cannot find "+propertyFile); + } + return; + } + resources = new PropertyResourceBundle(in); + } catch (MissingResourceException mre) { + if (!ignoreMissingProperties) { + System.err.println("Cannot read "+propertyFile); + } + } catch (java.io.IOException e) { + if (!ignoreMissingProperties) { + System.err.println("Failure trying to read "+propertyFile); + } + } + } + + /** + *

Tell the CatalogManager how to handle missing properties

+ * + *

If ignore is true, missing or unreadable property files will + * not be reported. Otherwise, a message will be sent to System.err. + *

+ */ + public static void ignoreMissingProperties(boolean ignore) { + ignoreMissingProperties = ignore; + } + + /** + *

Obtain the verbosity setting from the properties.

+ * + * @returns The verbosity level from the propertyFile or the + * defaultVerbosity. + */ + public static int verbosity () { + String verbStr = System.getProperty(pVerbosity); + + if (verbStr == null) { + if (resources==null) readProperties(); + if (resources==null) return defaultVerbosity; + try { + verbStr = resources.getString("verbosity"); + } catch (MissingResourceException e) { + return defaultVerbosity; + } + } + + try { + int verb = Integer.parseInt(verbStr.trim()); + return verb; + } catch (Exception e) { + System.err.println("Cannot parse verbosity: \"" + verbStr + "\""); + return defaultVerbosity; + } + } + + /** + *

Obtain the relativeCatalogs setting from the properties.

+ * + *

This property is used when the catalogFiles property is + * interrogated. If true, then relative catalog entry file names + * are returned. If false, relative catalog entry file names are + * made absolute with respect to the properties file before returning + * them.

+ * + *

This property only applies when the catalog files + * come from a properties file. If they come from a system property or + * the default list, they are never considered relative. (What would + * they be relative to?)

+ * + *

In the properties, a value of 'yes', 'true', or '1' is considered + * true, anything else is false.

+ * + * @returns The relativeCatalogs setting from the propertyFile or the + * defaultRelativeCatalogs. + */ + public static boolean relativeCatalogs () { + if (resources==null) readProperties(); + + if (resources==null) return defaultRelativeCatalogs; + + try { + String allow = resources.getString("relative-catalogs"); + return (allow.equalsIgnoreCase("true") + || allow.equalsIgnoreCase("yes") + || allow.equalsIgnoreCase("1")); + } catch (MissingResourceException e) { + return defaultRelativeCatalogs; + } + } + + /** + *

Obtain the list of catalog files from the properties.

+ * + *

Note that the list of catalog files is always a semicolon + * delimited list, even on Unix systems where a colon delimited list + * might be considered more natural.

+ * + * @returns A vector of the catalog file names or null if no catalogs + * are available in the properties. + */ + public static Vector catalogFiles () { + String catalogList = System.getProperty(pFiles); + boolean fromPropertiesFile = false; + + if (catalogList == null) { + if (resources == null) readProperties(); + if (resources != null) { + try { + catalogList = resources.getString("catalogs"); + fromPropertiesFile = true; + } catch (MissingResourceException e) { + System.err.println(propertyFile + ": catalogs not found."); + catalogList = null; + } + } + } + + if (catalogList == null) { + catalogList = defaultCatalogFiles; + } + + StringTokenizer files = new StringTokenizer(catalogList, ";"); + Vector catalogs = new Vector(); + while (files.hasMoreTokens()) { + String catalogFile = files.nextToken(); + URL absURI = null; + + if (fromPropertiesFile && !relativeCatalogs()) { + try { + absURI = new URL(propertyFileURI, catalogFile); + catalogFile = absURI.toString(); + } catch (MalformedURLException mue) { + absURI = null; + } + } + + catalogs.add(catalogFile); + } + return catalogs; + } + + /** + *

Obtain the preferPublic setting from the properties.

+ * + *

In the properties, a value of 'public' is true, + * anything else is false.

+ * + * @returns True if prefer is public or the + * defaultPreferSetting. + */ + public static boolean preferPublic () { + String prefer = System.getProperty(pPrefer); + + if (prefer == null) { + if (resources==null) readProperties(); + if (resources==null) return defaultPreferPublic; + try { + prefer = resources.getString("prefer"); + } catch (MissingResourceException e) { + return defaultPreferPublic; + } + } + + if (prefer == null) { + return defaultPreferPublic; + } + + return (prefer.equalsIgnoreCase("public")); + } + + /** + *

Obtain the static-catalog setting from the properties.

+ * + *

In the properties, a value of 'yes', 'true', or '1' is considered + * true, anything else is false.

+ * + * @returns The static-catalog setting from the propertyFile or the + * defaultStaticCatalog. + */ + public static boolean staticCatalog () { + String staticCatalog = System.getProperty(pStatic); + + if (staticCatalog == null) { + if (resources==null) readProperties(); + if (resources==null) return defaultStaticCatalog; + try { + staticCatalog = resources.getString("static-catalog"); + } catch (MissingResourceException e) { + return defaultStaticCatalog; + } + } + + if (staticCatalog == null) { + return defaultStaticCatalog; + } + + return (staticCatalog.equalsIgnoreCase("true") + || staticCatalog.equalsIgnoreCase("yes") + || staticCatalog.equalsIgnoreCase("1")); + } + + /** + *

Obtain the oasisXMLCatalogPI setting from the properties.

+ * + *

In the properties, a value of 'yes', 'true', or '1' is considered + * true, anything else is false.

+ * + * @returns The oasisXMLCatalogPI setting from the propertyFile or the + * defaultOasisXMLCatalogPI. + */ + public static boolean allowOasisXMLCatalogPI () { + String allow = System.getProperty(pAllowPI); + + if (allow == null) { + if (resources==null) readProperties(); + if (resources==null) return defaultOasisXMLCatalogPI; + try { + allow = resources.getString("allow-oasis-xml-catalog-pi"); + } catch (MissingResourceException e) { + return defaultOasisXMLCatalogPI; + } + } + + if (allow == null) { + return defaultOasisXMLCatalogPI; + } + + return (allow.equalsIgnoreCase("true") + || allow.equalsIgnoreCase("yes") + || allow.equalsIgnoreCase("1")); + } + + /** + *

Obtain the Catalog class name setting from the properties.

+ * + */ + public static String catalogClassName () { + String className = System.getProperty(pClassname); + + if (className == null) { + if (resources==null) readProperties(); + if (resources==null) return null; + try { + return resources.getString("catalog-class-name"); + } catch (MissingResourceException e) { + return null; + } + } + + return className; + } +} diff --git a/java/src/org/apache/xml/resolver/LICENSE b/java/src/org/apache/xml/resolver/LICENSE new file mode 100644 index 0000000..558c10d --- /dev/null +++ b/java/src/org/apache/xml/resolver/LICENSE @@ -0,0 +1,57 @@ +/* + * 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 + * . + */ + diff --git a/java/src/org/apache/xml/resolver/Resolver.java b/java/src/org/apache/xml/resolver/Resolver.java new file mode 100644 index 0000000..d9445f7 --- /dev/null +++ b/java/src/org/apache/xml/resolver/Resolver.java @@ -0,0 +1,746 @@ +// Resolver.java - Represents an extension of OASIS Open Catalog files. + +/* + * 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; + +import java.lang.Integer; +import java.io.IOException; +import java.io.File; +import java.io.InputStream; +import java.io.DataInputStream; +import java.io.PrintWriter; +import java.io.FileWriter; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.FileNotFoundException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import java.net.URL; +import java.net.URLConnection; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.readers.SAXCatalogReader; +import org.apache.xml.resolver.readers.OASISXMLCatalogReader; +import org.apache.xml.resolver.readers.ExtendedXMLCatalogReader; +import org.apache.xml.resolver.readers.TR9401CatalogReader; +import org.apache.xml.resolver.readers.TextCatalogReader; +import org.apache.xml.resolver.helpers.Debug; +import org.xml.sax.SAXException; +import org.xml.sax.EntityResolver; +import javax.xml.parsers.SAXParserFactory; + +/** + *

An extension to OASIS Open Catalog files, this class supports + * suffix-based matching and an external RFC2483 resolver.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class Resolver extends Catalog { + /** + *

The URISUFFIX Catalog Entry type.

+ * + *

URI suffix entries match URIs that end in a specified suffix.

+ */ + public static final int URISUFFIX = CatalogEntry.addEntryType("URISUFFIX", 2); + + /** + *

The SYSTEMSUFFIX Catalog Entry type.

+ * + *

System suffix entries match system identifiers that end in a + * specified suffix.

+ */ + public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType("SYSTEMSUFFIX", 2); + + /** + *

The RESOLVER Catalog Entry type.

+ * + *

A hook for providing support for web-based backup resolvers.

+ */ + public static final int RESOLVER = CatalogEntry.addEntryType("RESOLVER", 1); + + /** + *

The SYSTEMREVERSE Catalog Entry type.

+ * + *

This is a bit of a hack. There's no actual SYSTEMREVERSE entry, + * but this entry type is used to indicate that a reverse lookup is + * being performed. (This allows the Resolver to implement + * RFC2483 I2N and I2NS.) + */ + public static final int SYSTEMREVERSE + = CatalogEntry.addEntryType("SYSTEMREVERSE", 1); + + /** + *

Setup readers.

+ */ + public void setupReaders() { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + spf.setValidating(false); + + SAXCatalogReader saxReader = new SAXCatalogReader(spf); + + saxReader.setCatalogParser(null, "XMLCatalog", + "org.apache.xml.resolver.readers.XCatalogReader"); + + saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, + "catalog", + "org.apache.xml.resolver.readers.ExtendedXMLCatalogReader"); + + addReader("application/xml", saxReader); + + TR9401CatalogReader textReader = new TR9401CatalogReader(); + addReader("text/plain", textReader); + } + + /** + *

Cleanup and process a Catalog entry.

+ * + *

This method processes each Catalog entry, changing mapped + * relative system identifiers into absolute ones (based on the current + * base URI), and maintaining other information about the current + * catalog.

+ * + * @param entry The CatalogEntry to process. + */ + public void addEntry(CatalogEntry entry) { + int type = entry.getEntryType(); + + if (type == URISUFFIX) { + String suffix = normalizeURI(entry.getEntryArg(0)); + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(1, fsi); + + Debug.message(4, "URISUFFIX", suffix, fsi); + } else if (type == SYSTEMSUFFIX) { + String suffix = normalizeURI(entry.getEntryArg(0)); + String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); + + entry.setEntryArg(1, fsi); + + Debug.message(4, "SYSTEMSUFFIX", suffix, fsi); + } + + super.addEntry(entry); + } + + /** + *

Return the applicable URI

+ * + *

If a URI entry exists in the Catalog + * for the URI specified, return the mapped value.

+ * + *

In the Resolver (as opposed to the Catalog) class, if the + * URI isn't found by the usual algorithm, URISUFFIX entries are + * considered.

+ * + *

URI comparison is case sensitive.

+ * + * @param uri The URI to locate in the catalog. + * + * @return The resolved URI. + * + * @throws MalformedURLException The system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveURI(String uri) + throws MalformedURLException, IOException { + + String resolved = super.resolveURI(uri); + if (resolved != null) { + return resolved; + } + + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == RESOLVER) { + resolved = resolveExternalSystem(uri, e.getEntryArg(0)); + if (resolved != null) { + return resolved; + } + } else if (e.getEntryType() == URISUFFIX) { + String suffix = e.getEntryArg(0); + String result = e.getEntryArg(1); + + if (suffix.length() <= uri.length() + && uri.substring(uri.length()-suffix.length()).equals(suffix)) { + return result; + } + } + } + + // Otherwise, look in the subordinate catalogs + return resolveSubordinateCatalogs(Catalog.URI, + null, + null, + uri); + } + + /** + *

Return the applicable SYSTEM system identifier, resorting + * to external RESOLVERs if necessary.

+ * + *

If a SYSTEM entry exists in the Catalog + * for the system ID specified, return the mapped value.

+ * + *

In the Resolver (as opposed to the Catalog) class, if the + * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are + * considered.

+ * + *

On Windows-based operating systems, the comparison between + * the system identifier provided and the SYSTEM entries in the + * Catalog is case-insensitive.

+ * + * @param systemId The system ID to locate in the catalog. + * + * @return The system identifier to use for systemId. + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public String resolveSystem(String systemId) + throws MalformedURLException, IOException { + + String resolved = super.resolveSystem(systemId); + if (resolved != null) { + return resolved; + } + + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == RESOLVER) { + resolved = resolveExternalSystem(systemId, e.getEntryArg(0)); + if (resolved != null) { + return resolved; + } + } else if (e.getEntryType() == SYSTEMSUFFIX) { + String suffix = e.getEntryArg(0); + String result = e.getEntryArg(1); + + if (suffix.length() <= systemId.length() + && systemId.substring(systemId.length()-suffix.length()).equals(suffix)) { + return result; + } + } + } + + return resolveSubordinateCatalogs(Catalog.SYSTEM, + null, + null, + systemId); + } + + /** + *

Return the applicable PUBLIC or SYSTEM identifier, resorting + * to external resolvers if necessary.

+ * + *

This method searches the Catalog and returns the system + * identifier specified for the given system or + * public identifiers. If + * no appropriate PUBLIC or SYSTEM entry is found in the Catalog, + * null is returned.

+ * + *

Note that a system or public identifier in the current catalog + * (or subordinate catalogs) will be used in preference to an + * external resolver. Further, if a systemId is present, the external + * resolver(s) will be queried for that before the publicId.

+ * + * @param publicId The public identifier to locate in the catalog. + * Public identifiers are normalized before comparison. + * @param systemId The nominal system identifier for the entity + * in question (as provided in the source document). + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + * + * @return The system identifier to use. + * Note that the nominal system identifier is not returned if a + * match is not found in the catalog, instead null is returned + * to indicate that no match was found. + */ + public String resolvePublic(String publicId, String systemId) + throws MalformedURLException, IOException { + + String resolved = super.resolvePublic(publicId, systemId); + if (resolved != null) { + return resolved; + } + + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == RESOLVER) { + if (systemId != null) { + resolved = resolveExternalSystem(systemId, + e.getEntryArg(0)); + if (resolved != null) { + return resolved; + } + } + resolved = resolveExternalPublic(publicId, e.getEntryArg(0)); + if (resolved != null) { + return resolved; + } + } + } + + return resolveSubordinateCatalogs(Catalog.PUBLIC, + null, + publicId, + systemId); + } + + /** + *

Query an external RFC2483 resolver for a system identifier.

+ * + * @param systemId The system ID to locate. + * @param resolver The name of the resolver to use. + * + * @return The system identifier to use for the systemId. + */ + protected String resolveExternalSystem(String systemId, String resolver) + throws MalformedURLException, IOException { + Resolver r = queryResolver(resolver, "i2l", systemId, null); + if (r != null) { + return r.resolveSystem(systemId); + } else { + return null; + } + } + + /** + *

Query an external RFC2483 resolver for a public identifier.

+ * + * @param publicId The system ID to locate. + * @param resolver The name of the resolver to use. + * + * @return The system identifier to use for the systemId. + */ + protected String resolveExternalPublic(String publicId, String resolver) + throws MalformedURLException, IOException { + Resolver r = queryResolver(resolver, "fpi2l", publicId, null); + if (r != null) { + return r.resolvePublic(publicId, null); + } else { + return null; + } + } + + /** + *

Query an external RFC2483 resolver.

+ * + * @param resolver The URL of the RFC2483 resolver. + * @param command The command to send the resolver. + * @param arg1 The first argument to the resolver. + * @param arg2 The second argument to the resolver, usually null. + * + * @return The Resolver constructed. + */ + protected Resolver queryResolver(String resolver, + String command, + String arg1, + String arg2) { + InputStream iStream = null; + String RFC2483 = resolver + "?command=" + command + + "&format=tr9401&uri=" + arg1 + + "&uri2=" + arg2; + String line = null; + + try { + URL url = new URL(RFC2483); + + URLConnection urlCon = url.openConnection(); + + urlCon.setUseCaches(false); + + Resolver r = (Resolver) newCatalog(); + + String cType = urlCon.getContentType(); + + // I don't care about the character set or subtype + if (cType.indexOf(";") > 0) { + cType = cType.substring(0, cType.indexOf(";")); + } + + r.parseCatalog(cType, urlCon.getInputStream()); + + return r; + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.UNPARSEABLE) { + Debug.message(1, "Unparseable catalog: " + RFC2483); + } else if (cex.getExceptionType() + == CatalogException.UNKNOWN_FORMAT) { + Debug.message(1, "Unknown catalog format: " + RFC2483); + } + return null; + } catch (MalformedURLException mue) { + Debug.message(1, "Malformed resolver URL: " + RFC2483); + return null; + } catch (IOException ie) { + Debug.message(1, "I/O Exception opening resolver: " + RFC2483); + return null; + } + } + + /** + *

Append two vectors, returning the result.

+ * + * @param vec The first vector + * @param appvec The vector to be appended + * @return The vector vec, with appvec's elements appended to it + */ + private Vector appendVector(Vector vec, Vector appvec) { + if (appvec != null) { + for (int count = 0; count < appvec.size(); count++) { + vec.addElement(appvec.elementAt(count)); + } + } + return vec; + } + + /** + *

Find the URNs for a given system identifier in all catalogs.

+ * + * @param systemId The system ID to locate. + * + * @return A vector of URNs that map to the systemId. + */ + public Vector resolveAllSystemReverse(String systemId) + throws MalformedURLException, IOException { + Vector resolved = new Vector(); + + // If there's a SYSTEM entry in this catalog, use it + if (systemId != null) { + Vector localResolved = resolveLocalSystemReverse(systemId); + resolved = appendVector(resolved, localResolved); + } + + // Otherwise, look in the subordinate catalogs + Vector subResolved = resolveAllSubordinateCatalogs(SYSTEMREVERSE, + null, + null, + systemId); + + return appendVector(resolved, subResolved); + } + + /** + *

Find the URN for a given system identifier.

+ * + * @param systemId The system ID to locate. + * + * @return A (single) URN that maps to the systemId. + */ + public String resolveSystemReverse(String systemId) + throws MalformedURLException, IOException { + Vector resolved = resolveAllSystemReverse(systemId); + if (resolved != null && resolved.size() > 0) { + return (String) resolved.elementAt(0); + } else { + return null; + } + } + + /** + *

Return the applicable SYSTEM system identifiers

+ * + *

If one or more SYSTEM entries exists in the Catalog + * for the system ID specified, return the mapped values.

+ * + *

The caller is responsible for doing any necessary + * normalization of the system identifier before calling + * this method. For example, a relative system identifier in + * a document might be converted to an absolute system identifier + * before attempting to resolve it.

+ * + *

Note that this function will force all subordinate catalogs + * to be loaded.

+ * + *

On Windows-based operating systems, the comparison between + * the system identifier provided and the SYSTEM entries in the + * Catalog is case-insensitive.

+ * + * @param systemId The system ID to locate in the catalog. + * + * @return The system identifier to use for the notation. + * + * @throws MalformedURLException The formal system identifier of a + * subordinate catalog cannot be turned into a valid URL. + * @throws IOException Error reading subordinate catalog file. + */ + public Vector resolveAllSystem(String systemId) + throws MalformedURLException, IOException { + Vector resolutions = new Vector(); + + // If there are SYSTEM entries in this catalog, start with them + if (systemId != null) { + Vector localResolutions = resolveAllLocalSystem(systemId); + resolutions = appendVector(resolutions, localResolutions); + } + + // Then look in the subordinate catalogs + Vector subResolutions = resolveAllSubordinateCatalogs(SYSTEM, + null, + null, + systemId); + resolutions = appendVector(resolutions, subResolutions); + + if (resolutions.size() > 0) { + return resolutions; + } else { + return null; + } + } + + /** + *

Return all applicable SYSTEM system identifiers in this + * catalog.

+ * + *

If one or more SYSTEM entries exists in the catalog file + * for the system ID specified, return the mapped values.

+ * + * @param systemId The system ID to locate in the catalog + * + * @return A vector of the mapped system identifiers or null + */ + private Vector resolveAllLocalSystem(String systemId) { + Vector map = new Vector(); + String osname = System.getProperty("os.name"); + boolean windows = (osname.indexOf("Windows") >= 0); + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == SYSTEM + && (e.getEntryArg(0).equals(systemId) + || (windows + && e.getEntryArg(0).equalsIgnoreCase(systemId)))) { + map.addElement(e.getEntryArg(1)); + } + } + if (map.size() == 0) { + return null; + } else { + return map; + } + } + + /** + *

Find the URNs for a given system identifier in the current catalog.

+ * + * @param systemId The system ID to locate. + * + * @return A vector of URNs that map to the systemId. + */ + private Vector resolveLocalSystemReverse(String systemId) { + Vector map = new Vector(); + String osname = System.getProperty("os.name"); + boolean windows = (osname.indexOf("Windows") >= 0); + Enumeration enum = catalogEntries.elements(); + while (enum.hasMoreElements()) { + CatalogEntry e = (CatalogEntry) enum.nextElement(); + if (e.getEntryType() == SYSTEM + && (e.getEntryArg(1).equals(systemId) + || (windows + && e.getEntryArg(1).equalsIgnoreCase(systemId)))) { + map.addElement(e.getEntryArg(0)); + } + } + if (map.size() == 0) { + return null; + } else { + return map; + } + } + + /** + *

Search the subordinate catalogs, in order, looking for all + * match.

+ * + *

This method searches the Catalog and returns all of the system + * identifiers specified for the given entity type with the given + * name, public, and system identifiers. In some contexts, these + * may be null.

+ * + * @param entityType The CatalogEntry type for which this query is + * being conducted. This is necessary in order to do the approprate + * query on a subordinate catalog. + * @param entityName The name of the entity being searched for, if + * appropriate. + * @param publicId The public identifier of the entity in question + * (as provided in the source document). + * @param systemId The nominal system identifier for the entity + * in question (as provided in the source document). + * + * @throws MalformedURLException The formal system identifier of a + * delegated catalog cannot be turned into a valid URL. + * @throws IOException Error reading delegated catalog file. + * + * @return The system identifier to use. + * Note that the nominal system identifier is not returned if a + * match is not found in the catalog, instead null is returned + * to indicate that no match was found. + */ + private synchronized Vector resolveAllSubordinateCatalogs(int entityType, + String entityName, + String publicId, + String systemId) + throws MalformedURLException, IOException { + + Vector resolutions = new Vector(); + + for (int catPos = 0; catPos < catalogs.size(); catPos++) { + Resolver c = null; + + try { + c = (Resolver) catalogs.elementAt(catPos); + } catch (ClassCastException e) { + String catfile = (String) catalogs.elementAt(catPos); + c = (Resolver) newCatalog(); + + try { + c.parseCatalog(catfile); + } catch (MalformedURLException mue) { + Debug.message(1, "Malformed Catalog URL", catfile); + } catch (FileNotFoundException fnfe) { + Debug.message(1, "Failed to load catalog, file not found", + catfile); + } catch (IOException ioe) { + Debug.message(1, "Failed to load catalog, I/O error", catfile); + } + + catalogs.setElementAt(c, catPos); + } + + String resolved = null; + + // Ok, now what are we supposed to call here? + if (entityType == DOCTYPE) { + resolved = c.resolveDoctype(entityName, + publicId, + systemId); + if (resolved != null) { + // Only find one DOCTYPE resolution + resolutions.addElement(resolved); + return resolutions; + } + } else if (entityType == DOCUMENT) { + resolved = c.resolveDocument(); + if (resolved != null) { + // Only find one DOCUMENT resolution + resolutions.addElement(resolved); + return resolutions; + } + } else if (entityType == ENTITY) { + resolved = c.resolveEntity(entityName, + publicId, + systemId); + if (resolved != null) { + // Only find one ENTITY resolution + resolutions.addElement(resolved); + return resolutions; + } + } else if (entityType == NOTATION) { + resolved = c.resolveNotation(entityName, + publicId, + systemId); + if (resolved != null) { + // Only find one NOTATION resolution + resolutions.addElement(resolved); + return resolutions; + } + } else if (entityType == PUBLIC) { + resolved = c.resolvePublic(publicId, systemId); + if (resolved != null) { + // Only find one PUBLIC resolution + resolutions.addElement(resolved); + return resolutions; + } + } else if (entityType == SYSTEM) { + Vector localResolutions = c.resolveAllSystem(systemId); + resolutions = appendVector(resolutions, localResolutions); + break; + } else if (entityType == SYSTEMREVERSE) { + Vector localResolutions = c.resolveAllSystemReverse(systemId); + resolutions = appendVector(resolutions, localResolutions); + } + } + + if (resolutions != null) { + return resolutions; + } else { + return null; + } + } +} + + + + diff --git a/java/src/org/apache/xml/resolver/apps/XParseError.java b/java/src/org/apache/xml/resolver/apps/XParseError.java new file mode 100644 index 0000000..7a18839 --- /dev/null +++ b/java/src/org/apache/xml/resolver/apps/XParseError.java @@ -0,0 +1,200 @@ +// XParseError.java - An error handler for xparse + +/* + * 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.apps; + +import java.io.IOException; +import java.net.URL; +import java.net.MalformedURLException; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +/** + *

An ErrorHandler for xparse.

+ * + *

This class is just the error handler for xparse.

+ * + * @see xparse + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class XParseError implements ErrorHandler { + /** Show errors? */ + private boolean showErrors = true; + + /** Show warnings? */ + private boolean showWarnings = false; + + /** How many messages should be presented? */ + private int maxMessages = 10; + + /** The number of fatal errors seen so far. */ + private int fatalCount = 0; + + /** The number of errors seen so far. */ + private int errorCount = 0; + + /** The number of warnings seen so far. */ + private int warningCount = 0; + + /** The base URI of the running application. */ + private String baseURI = ""; + + /** Constructor */ + public XParseError(boolean errors, boolean warnings) { + showErrors = errors; + showWarnings = warnings; + + String dir = System.getProperty("user.dir"); + String file = ""; + + if (dir.endsWith("/")) { + file = "file:" + dir + "file"; + } else { + file = "file:" + dir + "/" + file; + } + + try { + URL url = new URL(file); + baseURI = url.toString(); + } catch (MalformedURLException mue) { + // nop; + } + } + + /** Return the error count */ + public int getErrorCount() { + return errorCount; + } + + /** Return the fatal error count */ + public int getFatalCount() { + return fatalCount; + } + + /** Return the warning count */ + public int getWarningCount() { + return warningCount; + } + + /** Return the number of messages to display */ + public int getMaxMessages() { + return maxMessages; + } + + /** Set the number of messages to display */ + public void setMaxMessages(int max) { + maxMessages = max; + } + + /** SAX2 API */ + public void error(SAXParseException exception) { + if (showErrors) { + if (errorCount+warningCount < maxMessages) { + message("Error", exception); + } + errorCount++; + } + } + + /** SAX2 API */ + public void fatalError(SAXParseException exception) { + if (showErrors) { + if (errorCount+warningCount < maxMessages) { + message("Fatal error", exception); + } + errorCount++; + fatalCount++; + } + } + + /** SAX2 API */ + public void warning(SAXParseException exception) { + if (showWarnings) { + if ((errorCount+warningCount < maxMessages)) { + message("Warning", exception); + } + warningCount++; + } + } + + /** Display a message to the user */ + private void message(String type, SAXParseException exception) { + String filename = exception.getSystemId(); + if (filename.startsWith(baseURI)) { + filename = filename.substring(baseURI.length()); + } + + System.out.print(type + + ":" + + filename + + ":" + + exception.getLineNumber()); + + if (exception.getColumnNumber() > 0) { + System.out.print(":" + exception.getColumnNumber()); + } + + System.out.println(":" + exception.getMessage()); + } +} diff --git a/java/src/org/apache/xml/resolver/apps/package.html b/java/src/org/apache/xml/resolver/apps/package.html new file mode 100644 index 0000000..a04bf36 --- /dev/null +++ b/java/src/org/apache/xml/resolver/apps/package.html @@ -0,0 +1,12 @@ + + +Apache XML Commons Resolver Applications package + + +

Apache XML Commons Resolver Applications package

+ +

This package provides a few simple command-line tools for exploring +the facilities of the resolver.

+ + + diff --git a/java/src/org/apache/xml/resolver/apps/resolver.java b/java/src/org/apache/xml/resolver/apps/resolver.java new file mode 100644 index 0000000..73e86d7 --- /dev/null +++ b/java/src/org/apache/xml/resolver/apps/resolver.java @@ -0,0 +1,338 @@ +// resolver.java - A simple command-line test tool for the resolver + +/* + * 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.apps; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.net.MalformedURLException; +import java.util.Vector; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import javax.xml.parsers.*; +import javax.xml.transform.*; +import javax.xml.transform.sax.SAXSource; + +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.tools.CatalogResolver; +import org.apache.xml.resolver.helpers.Debug; +import org.apache.xml.resolver.tools.CatalogResolver; + +/** + *

A simple command-line resolver.

+ * + *

This class implements a simple command-line resolver. It takes + * some parameters and passes them through the resolver, printing the + * result. + *

+ * + *

Usage: resolver [options] keyword

+ * + *

Where options are:

+ * + *
+ *
-c catalogfile
+ *
Load a particular catalog file.
+ *
-n name
+ *
Sets the name.
+ *
-p publicId
+ *
Sets the public identifier.
+ *
-s systemId
+ *
Sets the system identifier.
+ *
-a
+ *
Absolute system URI.
+ *
-u uri
+ *
Sets the URI.
+ *
-d integer
+ *
Set the debug level.
+ *
+ * + *

And keyword is one of: doctype, document, entity, notation, public, + * system, or uri.

+ * + *

The process ends with error-level 1, if there errors.

+ * + * @see org.apache.xml.resolver.tools.ResolvingParser + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 */ +public class resolver { + /** The main entry point */ + public static void main (String[] args) + throws FileNotFoundException, IOException { + + int debug = 0; + Vector catalogFiles = new Vector(); + int resType = 0; + String resTypeStr = null; + String name = null; + String publicId = null; + String systemId = null; + String uri = null; + boolean absoluteSystem = false; + + for (int i=0; i= 0) { + Debug.setDebug(debug); + } + } catch (Exception e) { + // nop + } + continue; + } + + resTypeStr = args[i]; + } + + if (resTypeStr == null) { + usage(); + } + + if (resTypeStr.equalsIgnoreCase("doctype")) { + resType = Catalog.DOCTYPE; + if (publicId == null && systemId == null) { + System.out.println("DOCTYPE requires public or system identifier."); + usage(); + } + } else if (resTypeStr.equalsIgnoreCase("document")) { + resType = Catalog.DOCUMENT; + } else if (resTypeStr.equalsIgnoreCase("entity")) { + resType = Catalog.ENTITY; + if (publicId == null && systemId == null && name == null) { + System.out.println("ENTITY requires name or public or system identifier."); + usage(); + } + } else if (resTypeStr.equalsIgnoreCase("notation")) { + resType = Catalog.NOTATION; + if (publicId == null && systemId == null && name == null) { + System.out.println("NOTATION requires name or public or system identifier."); + usage(); + } + } else if (resTypeStr.equalsIgnoreCase("public")) { + resType = Catalog.PUBLIC; + if (publicId == null) { + System.out.println("PUBLIC requires public identifier."); + usage(); + } + } else if (resTypeStr.equalsIgnoreCase("system")) { + resType = Catalog.SYSTEM; + if (systemId == null) { + System.out.println("SYSTEM requires system identifier."); + usage(); + } + } else if (resTypeStr.equalsIgnoreCase("uri")) { + resType = Catalog.URI; + if (uri == null) { + System.out.println("URI requires a uri."); + usage(); + } + } else { + System.out.println(resTypeStr + " is not a recognized keyword."); + usage(); + } + + if (absoluteSystem) { + URL base = null; + URL sysid = null; + + // Calculate the appropriate BASE URI + try { + // tack on a basename because URLs point to files not dirs + String userdir = System.getProperty("user.dir"); + userdir.replace('\\', '/'); + base = new URL("file:///" + userdir + "/basename"); + } catch (MalformedURLException e) { + String userdir = System.getProperty("user.dir"); + userdir.replace('\\', '/'); + Debug.message(1, "Malformed URL on cwd", userdir); + base = null; + } + + try { + sysid = new URL(base, systemId); + systemId = sysid.toString(); + } catch (MalformedURLException e) { + try { + sysid = new URL("file:///" + systemId); + } catch (MalformedURLException e2) { + Debug.message(1, "Malformed URL on system id", systemId); + } + } + } + + CatalogResolver catalogResolver = new CatalogResolver(); + Catalog resolver = catalogResolver.getCatalog(); + + for (int count = 0; count < catalogFiles.size(); count++) { + String file = (String) catalogFiles.elementAt(count); + resolver.parseCatalog(file); + } + String result = null; + + if (resType == Catalog.DOCTYPE) { + System.out.println("Resolve DOCTYPE (name, publicid, systemid):"); + if (name != null) { System.out.println(" name: " + name); } + if (publicId != null) { System.out.println(" public id: " + publicId); } + if (systemId != null) { System.out.println(" system id: " + systemId); } + if (uri != null) { System.out.println(" uri: " + uri); } + result = resolver.resolveDoctype(name, publicId, systemId); + } else if (resType == Catalog.DOCUMENT) { + System.out.println("Resolve DOCUMENT ():"); + result = resolver.resolveDocument(); + } else if (resType == Catalog.ENTITY) { + System.out.println("Resolve ENTITY (name, publicid, systemid):"); + if (name != null) { System.out.println(" name: " + name); } + if (publicId != null) { System.out.println(" public id: " + publicId); } + if (systemId != null) { System.out.println(" system id: " + systemId); } + result = resolver.resolveEntity(name, publicId, systemId); + } else if (resType == Catalog.NOTATION) { + System.out.println("Resolve NOTATION (name, publicid, systemid):"); + if (name != null) { System.out.println(" name: " + name); } + if (publicId != null) { System.out.println(" public id: " + publicId); } + if (systemId != null) { System.out.println(" system id: " + systemId); } + result = resolver.resolveNotation(name, publicId, systemId); + } else if (resType == Catalog.PUBLIC) { + System.out.println("Resolve PUBLIC (publicid, systemid):"); + if (publicId != null) { System.out.println(" public id: " + publicId); } + if (systemId != null) { System.out.println(" system id: " + systemId); } + result = resolver.resolvePublic(publicId, systemId); + } else if (resType == Catalog.SYSTEM) { + System.out.println("Resolve SYSTEM (systemid):"); + if (systemId != null) { System.out.println(" system id: " + systemId); } + result = resolver.resolveSystem(systemId); + } else if (resType == Catalog.URI) { + System.out.println("Resolve URI (uri):"); + if (uri != null) { System.out.println(" uri: " + uri); } + result = resolver.resolveURI(uri); + } else { + System.out.println("resType is wrong!? This can't happen!"); + usage(); + } + + System.out.println("Result: " + result); + } + + public static void usage() { + System.out.println("Usage: resolver [options] keyword"); + System.out.println(""); + System.out.println("Where:"); + System.out.println(""); + System.out.println("-c catalogfile Loads a particular catalog file."); + System.out.println("-n name Sets the name."); + System.out.println("-p publicId Sets the public identifier."); + System.out.println("-s systemId Sets the system identifier."); + System.out.println("-a Makes the system URI absolute before resolution"); + System.out.println("-u uri Sets the URI."); + System.out.println("-d integer Set the debug level."); + System.out.println("keyword Identifies the type of resolution to perform:"); + System.out.println(" doctype, document, entity, notation, public, system,"); + System.out.println(" or uri."); + + System.exit(1); + } +} + + diff --git a/java/src/org/apache/xml/resolver/apps/xparse.java b/java/src/org/apache/xml/resolver/apps/xparse.java new file mode 100644 index 0000000..d1a09b2 --- /dev/null +++ b/java/src/org/apache/xml/resolver/apps/xparse.java @@ -0,0 +1,300 @@ +// xparse.java - A simple command-line XML parser + +/* + * 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.apps; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Vector; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import javax.xml.parsers.*; + +import org.apache.xml.resolver.tools.ResolvingParser; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.helpers.Debug; + +/** + *

A simple command-line XML parsing application.

+ * + *

This class implements a simple command-line XML Parser. It's + * just a little wrapper around the JAXP Parser with support for + * catalogs. + *

+ * + *

Usage: xparse [options] document.xml

+ * + *

Where:

+ * + *
+ *
-c catalogfile
+ *
Load a particular catalog file.
+ *
-w
+ *
Perform a well-formed parse, not a validating parse.
+ *
-v (the default)
+ *
Perform a validating parse.
+ *
-n
+ *
Perform a namespace-ignorant parse.
+ *
-N (the default)
+ *
Perform a namespace-aware parse.
+ *
-d integer
+ *
Set the debug level. Warnings are shown if the debug level + * is > 2.
+ *
-E integer
+ *
Set the maximum number of errors to display.
+ *
+ * + *

The process ends with error-level 1, if there errors.

+ * + * @deprecated This interface has been replaced by the + * {@link org.apache.xml.resolver.tools.ResolvingXMLReader} for SAX2. + * + * @see org.apache.xml.resolver.tools.ResolvingParser + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class xparse +{ + /** The main entry point */ + public static void main (String[] args) + throws FileNotFoundException, IOException { + + String xmlfile = null; + int debug = 0; + int maxErrs = 10; + boolean nsAware = true; + boolean validating = true; + boolean showWarnings = (debug > 2); + boolean showErrors = true; + Vector catalogFiles = new Vector(); + + for (int i=0; i= 0) { + Debug.setDebug(debug); + showWarnings = (debug > 2); + } + } catch (Exception e) { + // nop + } + continue; + } + + if (args[i].equals("-E")) { + ++i; + String errstr = args[i]; + try { + int errs = Integer.parseInt(errstr); + if (errs >= 0) { + maxErrs = errs; + } + } catch (Exception e) { + // nop + } + continue; + } + + xmlfile = args[i]; + } + + if (xmlfile == null) { + System.out.println("Usage: org.apache.xml.resolver.apps.xparse [opts] xmlfile"); + System.exit(1); + } + + ResolvingParser.validating = validating; + ResolvingParser.namespaceAware = nsAware; + ResolvingParser reader = new ResolvingParser(); + Catalog catalog = reader.getCatalog(); + + for (int count = 0; count < catalogFiles.size(); count++) { + String file = (String) catalogFiles.elementAt(count); + catalog.parseCatalog(file); + } + + XParseError xpe = new XParseError(showErrors, showWarnings); + xpe.setMaxMessages(maxErrs); + reader.setErrorHandler(xpe); + + String parseType = validating ? "validating" : "well-formed"; + String nsType = nsAware ? "namespace-aware" : "namespace-ignorant"; + if (maxErrs > 0) { + System.out.println("Attempting " + + parseType + + ", " + + nsType + + " parse"); + } + + Date startTime = new Date(); + + try { + reader.parse(xmlfile); + } catch (SAXException sx) { + System.out.println("SAX Exception: " + sx); + } catch (Exception e) { + e.printStackTrace(); + } + + Date endTime = new Date(); + + long millisec = endTime.getTime() - startTime.getTime(); + long secs = 0; + long mins = 0; + long hours = 0; + + if (millisec > 1000) { + secs = millisec / 1000; + millisec = millisec % 1000; + } + + if (secs > 60) { + mins = secs / 60; + secs = secs % 60; + } + + if (mins > 60) { + hours = mins / 60; + mins = mins % 60; + } + + if (maxErrs > 0) { + System.out.print("Parse "); + if (xpe.getFatalCount() > 0) { + System.out.print("failed "); + } else { + System.out.print("succeeded "); + System.out.print("("); + if (hours > 0) { + System.out.print(hours + ":"); + } + if (hours > 0 || mins > 0) { + System.out.print(mins + ":"); + } + System.out.print(secs + "." + millisec); + System.out.print(") "); + } + System.out.print("with "); + + int errCount = xpe.getErrorCount(); + int warnCount = xpe.getWarningCount(); + + if (errCount > 0) { + System.out.print(errCount + " error"); + System.out.print(errCount > 1 ? "s" : ""); + System.out.print(" and "); + } else { + System.out.print("no errors and "); + } + + if (warnCount > 0) { + System.out.print(warnCount + " warning"); + System.out.print(warnCount > 1 ? "s" : ""); + System.out.print("."); + } else { + System.out.print("no warnings."); + } + + System.out.println(""); + } + + if (xpe.getErrorCount() > 0) { + System.exit(1); + } + } +} + + diff --git a/java/src/org/apache/xml/resolver/apps/xread.java b/java/src/org/apache/xml/resolver/apps/xread.java new file mode 100644 index 0000000..4434601 --- /dev/null +++ b/java/src/org/apache/xml/resolver/apps/xread.java @@ -0,0 +1,303 @@ +// xread.java - A simple command-line XML parser + +/* + * 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.apps; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Vector; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import javax.xml.parsers.*; + +import org.apache.xml.resolver.tools.ResolvingXMLReader; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.helpers.Debug; + +/** + *

A simple command-line XML parsing application.

+ * + *

This class implements a simple command-line XML Parser. It's + * just a little wrapper around the JAXP XMLReader with support for + * catalogs. + *

+ * + *

Usage: xread [options] document.xml

+ * + *

Where:

+ * + *
+ *
-c catalogfile
+ *
Load a particular catalog file.
+ *
-w
+ *
Perform a well-formed parse, not a validating parse.
+ *
-v (the default)
+ *
Perform a validating parse.
+ *
-n
+ *
Perform a namespace-ignorant parse.
+ *
-N (the default)
+ *
Perform a namespace-aware parse.
+ *
-d integer
+ *
Set the debug level. Warnings are shown if the debug level + * is > 2.
+ *
-E integer
+ *
Set the maximum number of errors to display.
+ *
+ * + *

The process ends with error-level 1, if there errors.

+ * + * @see org.apache.xml.resolver.tools.ResolvingXMLReader + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class xread +{ + /** The main entry point */ + public static void main (String[] args) + throws FileNotFoundException, IOException { + + String xmlfile = null; + int debug = 0; + int maxErrs = 10; + boolean nsAware = true; + boolean validating = true; + boolean showWarnings = (debug > 2); + boolean showErrors = true; + Vector catalogFiles = new Vector(); + + for (int i=0; i= 0) { + Debug.setDebug(debug); + showWarnings = (debug > 2); + } + } catch (Exception e) { + // nop + } + continue; + } + + if (args[i].equals("-E")) { + ++i; + String errstr = args[i]; + try { + int errs = Integer.parseInt(errstr); + if (errs >= 0) { + maxErrs = errs; + } + } catch (Exception e) { + // nop + } + continue; + } + + xmlfile = args[i]; + } + + if (xmlfile == null) { + System.out.println("Usage: org.apache.xml.resolver.apps.xread [opts] xmlfile"); + System.exit(1); + } + + ResolvingXMLReader reader = new ResolvingXMLReader(); + + try { + reader.setFeature("http://xml.org/sax/features/namespaces", nsAware); + reader.setFeature("http://xml.org/sax/features/validation", validating); + } catch (SAXException e) { + // nop; + } + + Catalog catalog = reader.getCatalog(); + + for (int count = 0; count < catalogFiles.size(); count++) { + String file = (String) catalogFiles.elementAt(count); + catalog.parseCatalog(file); + } + + XParseError xpe = new XParseError(showErrors, showWarnings); + xpe.setMaxMessages(maxErrs); + reader.setErrorHandler(xpe); + + String parseType = validating ? "validating" : "well-formed"; + String nsType = nsAware ? "namespace-aware" : "namespace-ignorant"; + if (maxErrs > 0) { + System.out.println("Attempting " + + parseType + + ", " + + nsType + + " parse"); + } + + Date startTime = new Date(); + + try { + reader.parse(xmlfile); + } catch (SAXException sx) { + System.out.println("SAX Exception: " + sx); + } catch (Exception e) { + e.printStackTrace(); + } + + Date endTime = new Date(); + + long millisec = endTime.getTime() - startTime.getTime(); + long secs = 0; + long mins = 0; + long hours = 0; + + if (millisec > 1000) { + secs = millisec / 1000; + millisec = millisec % 1000; + } + + if (secs > 60) { + mins = secs / 60; + secs = secs % 60; + } + + if (mins > 60) { + hours = mins / 60; + mins = mins % 60; + } + + if (maxErrs > 0) { + System.out.print("Parse "); + if (xpe.getFatalCount() > 0) { + System.out.print("failed "); + } else { + System.out.print("succeeded "); + System.out.print("("); + if (hours > 0) { + System.out.print(hours + ":"); + } + if (hours > 0 || mins > 0) { + System.out.print(mins + ":"); + } + System.out.print(secs + "." + millisec); + System.out.print(") "); + } + System.out.print("with "); + + int errCount = xpe.getErrorCount(); + int warnCount = xpe.getWarningCount(); + + if (errCount > 0) { + System.out.print(errCount + " error"); + System.out.print(errCount > 1 ? "s" : ""); + System.out.print(" and "); + } else { + System.out.print("no errors and "); + } + + if (warnCount > 0) { + System.out.print(warnCount + " warning"); + System.out.print(warnCount > 1 ? "s" : ""); + System.out.print("."); + } else { + System.out.print("no warnings."); + } + + System.out.println(""); + } + + if (xpe.getErrorCount() > 0) { + System.exit(1); + } + } +} + + diff --git a/java/src/org/apache/xml/resolver/helpers/Debug.java b/java/src/org/apache/xml/resolver/helpers/Debug.java new file mode 100644 index 0000000..d9fb536 --- /dev/null +++ b/java/src/org/apache/xml/resolver/helpers/Debug.java @@ -0,0 +1,145 @@ +// Debug.java - Print debug messages + +/* + * 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 org.apache.xml.resolver.CatalogManager; + +/** + *

Static debugging/messaging class for Catalogs.

+ * + *

This class defines a set of static methods that can be called + * to produce debugging messages. Messages have an associated "debug + * level" and messages below the current setting are not displayed.

+ * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class Debug { + /** The internal debug level. */ + protected static int debug = CatalogManager.verbosity(); + + /** Set the debug level for future messages. */ + public static void setDebug(int newDebug) { + debug = newDebug; + } + + /** Get the current debug level. */ + public static int getDebug() { + return debug; + } + + /** + *

Print debug message (if the debug level is high enough).

+ * + *

Prints "the message"

+ * + * @param level The debug level of this message. This message + * will only be + * displayed if the current debug level is at least equal to this + * value. + * @param message The text of the message. + */ + public static void message(int level, String message) { + if (debug >= level) { + System.out.println(message); + } + } + + /** + *

Print debug message (if the debug level is high enough).

+ * + *

Prints "the message: spec"

+ * + * @param level The debug level of this message. This message + * will only be + * displayed if the current debug level is at least equal to this + * value. + * @param message The text of the message. + * @param spec An argument to the message. + */ + public static void message(int level, String message, String spec) { + if (debug >= level) { + System.out.println(message + ": " + spec); + } + } + + /** + *

Print debug message (if the debug level is high enough).

+ * + *

Prints "the message: spec1" and "spec2" indented on the next line.

+ * + * @param level The debug level of this message. This message + * will only be + * displayed if the current debug level is at least equal to this + * value. + * @param message The text of the message. + * @param spec1 An argument to the message. + * @param spec2 Another argument to the message. + */ + public static void message(int level, String message, + String spec1, String spec2) { + if (debug >= level) { + System.out.println(message + ": " + spec1); + System.out.println("\t" + spec2); + } + } +} diff --git a/java/src/org/apache/xml/resolver/helpers/Namespaces.java b/java/src/org/apache/xml/resolver/helpers/Namespaces.java new file mode 100644 index 0000000..ffd2fba --- /dev/null +++ b/java/src/org/apache/xml/resolver/helpers/Namespaces.java @@ -0,0 +1,151 @@ +// Namespaces.java - Analyze namespace nodes in a DOM tree + +/* + * 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 org.w3c.dom.*; + +/** + *

Static Namespace query methods.

+ * + *

This class defines a set of static methods that can be called + * to analyze the namespace properties of DOM nodes.

+ * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class Namespaces { + /** + *

Returns the "prefix" part of a QName or the empty string (not + * null) if the name has no prefix.

+ * + * @param element The QName of an element. + * @return The prefix part of the element name. + */ + public static String getPrefix(Element element) { + String name = element.getTagName(); + String prefix = ""; + + if (name.indexOf(':') > 0) { + prefix = name.substring(0, name.indexOf(':')); + } + + return prefix; + } + + /** + *

Returns the "localname" part of a QName, which is the whole + * name if it has no prefix.

+ * + * @param element The QName of an element. + * @return The local part of a QName. + */ + public static String getLocalName(Element element) { + String name = element.getTagName(); + + if (name.indexOf(':') > 0) { + name = name.substring(name.indexOf(':')+1); + } + + return name; + } + + /** + *

Returns the namespace URI for the specified prefix at the + * specified context node.

+ * + * @param node The context node. + * @param prefix The prefix. + * @return The namespace URI associated with the prefix, or + * null if no namespace declaration exists for the prefix. + */ + public static String getNamespaceURI(Node node, String prefix) { + if (node == null || node.getNodeType() != Node.ELEMENT_NODE) { + return null; + } + + if (prefix.equals("")) { + if (((Element) node).hasAttribute("xmlns")) { + return ((Element) node).getAttribute("xmlns"); + } + } else { + String nsattr = "xmlns:" + prefix; + if (((Element) node).hasAttribute(nsattr)) { + return ((Element) node).getAttribute(nsattr); + } + } + + return getNamespaceURI(node.getParentNode(), prefix); + } + + /** + *

Returns the namespace URI for the namespace to which the + * element belongs.

+ * + * @param element The element. + * @return The namespace URI associated with the namespace of the + * element, or null if no namespace declaration exists for it. + */ + public static String getNamespaceURI(Element element) { + String prefix = getPrefix(element); + return getNamespaceURI(element, prefix); + } +} diff --git a/java/src/org/apache/xml/resolver/helpers/PublicId.java b/java/src/org/apache/xml/resolver/helpers/PublicId.java new file mode 100644 index 0000000..b964d90 --- /dev/null +++ b/java/src/org/apache/xml/resolver/helpers/PublicId.java @@ -0,0 +1,198 @@ +// PublicId.java - Information about public identifiers + +/* + * 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; + +/** + *

Static methods for dealing with public identifiers.

+ * + *

This class defines a set of static methods that can be called + * to handle public identifiers.

+ * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public abstract class PublicId { + protected PublicId() { } + + /** + *

Normalize a public identifier.

+ * + *

Public identifiers must be normalized according to the following + * rules before comparisons between them can be made:

+ * + *
    + *
  • Whitespace characters are normalized to spaces (e.g., line feeds, + * tabs, etc. become spaces).
  • + *
  • Leading and trailing whitespace is removed.
  • + *
  • Multiple internal whitespaces are normalized to a single + * space.
  • + *
+ * + *

This method is declared static so that other classes + * can use it directly.

+ * + * @param publicId The unnormalized public identifier. + * + * @return The normalized identifier. + */ + public static String normalize(String publicId) { + String normal = publicId.replace('\t', ' '); + normal = normal.replace('\r', ' '); + normal = normal.replace('\n', ' '); + normal = normal.trim(); + + int pos; + + while ((pos = normal.indexOf(" ")) >= 0) { + normal = normal.substring(0, pos) + normal.substring(pos+1); + } + + return normal; + } + + /** + *

Encode a public identifier as a "publicid" URN.

+ * + *

This method is declared static so that other classes + * can use it directly.

+ * + * @param publicId The unnormalized public identifier. + * + * @return The normalized identifier. + */ + public static String encodeURN(String publicId) { + String urn = PublicId.normalize(publicId); + + urn = PublicId.stringReplace(urn, "%", "%25"); + urn = PublicId.stringReplace(urn, ";", "%3B"); + urn = PublicId.stringReplace(urn, "'", "%27"); + urn = PublicId.stringReplace(urn, "?", "%3F"); + urn = PublicId.stringReplace(urn, "#", "%23"); + urn = PublicId.stringReplace(urn, "+", "%2B"); + urn = PublicId.stringReplace(urn, " ", "+"); + urn = PublicId.stringReplace(urn, "::", ";"); + urn = PublicId.stringReplace(urn, ":", "%3A"); + urn = PublicId.stringReplace(urn, "//", ":"); + urn = PublicId.stringReplace(urn, "/", "%2F"); + + return "urn:publicid:" + urn; + } + + /** + *

Decode a "publicid" URN into a public identifier.

+ * + *

This method is declared static so that other classes + * can use it directly.

+ * + * @param publicId The unnormalized public identifier. + * + * @return The normalized identifier. + */ + public static String decodeURN(String urn) { + String publicId = ""; + + if (urn.startsWith("urn:publicid:")) { + publicId = urn.substring(13); + } else { + return urn; + } + + publicId = PublicId.stringReplace(publicId, "%2F", "/"); + publicId = PublicId.stringReplace(publicId, ":", "//"); + publicId = PublicId.stringReplace(publicId, "%3A", ":"); + publicId = PublicId.stringReplace(publicId, ";", "::"); + publicId = PublicId.stringReplace(publicId, "+", " "); + publicId = PublicId.stringReplace(publicId, "%2B", "+"); + publicId = PublicId.stringReplace(publicId, "%23", "#"); + publicId = PublicId.stringReplace(publicId, "%3F", "?"); + publicId = PublicId.stringReplace(publicId, "%27", "'"); + publicId = PublicId.stringReplace(publicId, "%3B", ";"); + publicId = PublicId.stringReplace(publicId, "%25", "%"); + + return publicId; + } + + /** + *

Replace one string with another.

+ * + */ + private static String stringReplace(String str, + String oldStr, + String newStr) { + + String result = ""; + int pos = str.indexOf(oldStr); + + // System.out.println(str + ": " + oldStr + " => " + newStr); + + while (pos >= 0) { + // System.out.println(str + " (" + pos + ")"); + result += str.substring(0, pos); + result += newStr; + str = str.substring(pos+1); + + pos = str.indexOf(oldStr); + } + + return result + str; + } +} diff --git a/java/src/org/apache/xml/resolver/helpers/package.html b/java/src/org/apache/xml/resolver/helpers/package.html new file mode 100644 index 0000000..a0ad685 --- /dev/null +++ b/java/src/org/apache/xml/resolver/helpers/package.html @@ -0,0 +1,12 @@ + + +Apache XML Commons Resolver Helpers package + + +

Apache XML Commons Resolver Helpers package

+ +

This package provides a collection of static helper functions used +across a number of different classes.

+ + + diff --git a/java/src/org/apache/xml/resolver/overview.html b/java/src/org/apache/xml/resolver/overview.html new file mode 100644 index 0000000..0f5d2c6 --- /dev/null +++ b/java/src/org/apache/xml/resolver/overview.html @@ -0,0 +1,41 @@ + + +Apache XML Common Resolver + + +

The Apache XML Commons Resolver classes implement Catalog-based entity and +URI resolution.

+ +

Overview

+ +

The resolver class implements the full semantics of OASIS +Technical +Resolution 9401:1997 (Amendment 2 to TR 9401) catalogs and +the 06 Aug 2001 Committee Specification of OASIS +XML +Catalogs.

+ +

It also +includes a framework of classes designed to read catalog files in +a number of formats: +

+ +
    +
  1. The plain-text flavor described by TR9401. +
  2. The XCatalog +XML format defined by John Cowan +
  3. The XML Catalog format defined by the OASIS +Entity Resolution +Technical Committee. +
+ +

Quick Start

+ +

The tools +classes provide "out-of-the-box" SAX1 Parser, SAX2 XMLReader, +and JAXP URIResolvers. If you're just interested in hooking a +parser or URI resolver into your application, this is the place to start. +

+ + + diff --git a/java/src/org/apache/xml/resolver/package.html b/java/src/org/apache/xml/resolver/package.html new file mode 100644 index 0000000..9e80df3 --- /dev/null +++ b/java/src/org/apache/xml/resolver/package.html @@ -0,0 +1,34 @@ + + +Apache XML Commons Resolver package + + +

Apache XML Commons Resolver package

+ +

The classes in this package implement the full semantics of +OASIS Technical +Resolution 9401:1997 (Amendment 2 to TR 9401) catalogs. Also included +are a framework of classes designed to read catalog files in +a number of formats. +

+ +

The principal classes in this package are:

+ +
    +
  • Catalog, implements the +semantics of OASIS Catalogs. +
  • Resolver, demonstrates how the semantics +of OASIS Catalogs can be extended with these classes. +
  • CatalogManager, interfaces with the +Catalog properties file (that controls the default values of certain +properties). +
  • TR9401CatalogReader, reads +plain text TR9401 catalog files +
  • OASISXMLCatalogReader, reads +OASIS XML Catalogs (as defined in the 2001-08-01 committee draft) +
  • XCatalogReader, reads John Cowan's +XCatalog files +
+ + + diff --git a/java/src/org/apache/xml/resolver/readers/CatalogReader.java b/java/src/org/apache/xml/resolver/readers/CatalogReader.java new file mode 100644 index 0000000..63d85ea --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/CatalogReader.java @@ -0,0 +1,118 @@ +// CatalogReader.java - An interface for reading catalog files + +/* + * 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.readers; + +import java.io.IOException; +import java.net.MalformedURLException; +import org.apache.xml.resolver.CatalogException; + +import java.io.InputStream; +import org.apache.xml.resolver.Catalog; + +/** + *

The CatalogReader interface.

+ * + *

The Catalog class requires that classes implement this interface + * in order to be used to read catalogs. Examples of CatalogReaders + * include the TextCatalogReader, the SAXCatalogReader, and the + * DOMCatalogReader.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public interface CatalogReader { + /** + *

Read a catalog from a file

+ * + *

This class reads a catalog from a URL.

+ * + * @param catalog The catalog for which this reader is called. + * @param fileUrl The URL of a document to be read. + * @throws MalformedURLException if the specified URL cannot be + * turned into a URL object. + * @throws IOException if the URL cannot be read. + * @throws UnknownCatalogFormatException if the catalog format is + * not recognized. + * @throws UnparseableCatalogException if the catalog cannot be parsed. + * (For example, if it is supposed to be XML and isn't well-formed.) + */ + public void readCatalog(Catalog catalog, String fileUrl) + throws MalformedURLException, IOException, CatalogException; + + /** + *

Read a catalog from an input stream

+ * + *

This class reads a catalog from an input stream.

+ * + * @param catalog The catalog for which this reader is called. + * @param is The input stream that is to be read. + * @throws IOException if the URL cannot be read. + * @throws UnknownCatalogFormatException if the catalog format is + * not recognized. + * @throws UnparseableCatalogException if the catalog cannot be parsed. + * (For example, if it is supposed to be XML and isn't well-formed.) + */ + public void readCatalog(Catalog catalog, InputStream is) + throws IOException, CatalogException; +} diff --git a/java/src/org/apache/xml/resolver/readers/DOMCatalogParser.java b/java/src/org/apache/xml/resolver/readers/DOMCatalogParser.java new file mode 100644 index 0000000..3095eea --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/DOMCatalogParser.java @@ -0,0 +1,90 @@ +// DOMCatalogParser.java - An interface for reading catalog files + +/* + * 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.readers; + +import org.apache.xml.resolver.Catalog; +import org.w3c.dom.Node; + +/** + *

The DOMCatalogParser interface.

+ * + *

This interface must be implemented in order for a class to + * participate as a parser for the DOMCatalogReader. + * + * @see Catalog + * @see DOMCatalogReader + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public interface DOMCatalogParser { + /** + *

Parse a DOM node as a catalog entry

+ * + *

This method is expected to analyze the specified node and + * construct appropriate catalog entry(ies) from it.

+ * + * @param catalog The catalog for which this node is being considered. + * @param node The DOM Node from the catalog. + */ + public void parseCatalogEntry(Catalog catalog, Node node); +} diff --git a/java/src/org/apache/xml/resolver/readers/DOMCatalogReader.java b/java/src/org/apache/xml/resolver/readers/DOMCatalogReader.java new file mode 100644 index 0000000..9a67bd2 --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/DOMCatalogReader.java @@ -0,0 +1,288 @@ +// DOMCatalogReader.java - Read XML Catalog files + +/* + * 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.readers; + +import java.lang.Integer; +import java.util.Vector; +import java.util.Hashtable; +import java.util.Enumeration; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogEntry; +import org.apache.xml.resolver.CatalogException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.helpers.Debug; +import org.apache.xml.resolver.helpers.PublicId; +import org.apache.xml.resolver.helpers.Namespaces; + +import org.xml.sax.SAXException; +import org.w3c.dom.*; + +/** + *

A DOM-based CatalogReader

+ * + *

This class is used to read XML Catalogs using the DOM. This reader + * has an advantage over the SAX-based reader that it can analyze the + * DOM tree rather than simply a series of SAX events. It has the disadvantage + * that it requires all of the code necessary to build and walk a DOM + * tree.

+ * + *

Since the choice of CatalogReaders (in the InputStream case) can only + * be made on the basis of MIME type, the following problem occurs: only + * one CatalogReader can exist for all XML mime types. In order to get + * around this problem, the DOMCatalogReader relies on a set of external + * CatalogParsers to actually build the catalog.

+ * + *

The selection of CatalogParsers is made on the basis of the QName + * of the root element of the document.

+ * + *

This class requires the Java API for XML Parsing.

+ * + * @see Catalog + * @see CatalogReader + * @see SAXCatalogReader + * @see TextCatalogReader + * @see DOMCatalogParser + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class DOMCatalogReader implements CatalogReader { + /** + *

Mapping table from QNames to CatalogParser classes.

+ * + *

Each key in this hash table has the form "elementname" + * or "{namespaceuri}elementname". The former is used if the + * namespace URI is null.

+ */ + protected Hashtable namespaceMap = new Hashtable(); + + /** + *

Add a new parser to the reader.

+ * + *

This method associates the specified parserClass with the + * namespaceURI/rootElement names specified.

+ * + * @param namespaceURI The namespace URI. Not the prefix. + * @param rootElement The name of the root element. + * @param parserClass The name of the parserClass to instantiate + * for this kind of catalog. + */ + public void setCatalogParser(String namespaceURI, + String rootElement, + String parserClass) { + if (namespaceURI == null) { + namespaceMap.put(rootElement, parserClass); + } else { + namespaceMap.put("{"+namespaceURI+"}"+rootElement, parserClass); + } + } + + /** + *

Get the name of the parser class for a given catalog type.

+ * + *

This method returns the parserClass associated with the + * namespaceURI/rootElement names specified.

+ * + * @param namespaceURI The namespace URI. Not the prefix. + * @param rootElement The name of the root element. + * @return The parser class. + */ + public String getCatalogParser(String namespaceURI, + String rootElement) { + if (namespaceURI == null) { + return (String) namespaceMap.get(rootElement); + } else { + return (String) namespaceMap.get("{"+namespaceURI+"}"+rootElement); + } + } + + /** + *

Null constructor; something for subclasses to call.

+ */ + public DOMCatalogReader() { } + + /** + *

Read a catalog from an input stream

+ * + *

This class reads a catalog from an input stream:

+ * + *
    + *
  • Based on the QName of the root element, it determines which + * parser to instantiate for this catalog.
  • + *
  • It constructs a DOM Document from the catalog and
  • + *
  • For each child of the root node, it calls the parser's + * parseCatalogEntry method. This method is expected to make + * appropriate calls back into the catalog to add entries for the + * entries in the catalog. It is free to do this in whatever manner + * is appropriate (perhaps using just the node passed in, perhaps + * wandering arbitrarily throughout the tree).
  • + *
+ * + * @param catalog The catalog for which this reader is called. + * @param is The input stream that is to be read. + * @throws IOException if the URL cannot be read. + * @throws UnknownCatalogFormatException if the catalog format is + * not recognized. + * @throws UnparseableCatalogException if the catalog cannot be parsed. + * (For example, if it is supposed to be XML and isn't well-formed or + * if the parser class cannot be instantiated.) + */ + public void readCatalog(Catalog catalog, InputStream is) + throws IOException, CatalogException { + + DocumentBuilderFactory factory = null; + DocumentBuilder builder = null; + + factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + factory.setValidating(false); + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException pce) { + throw new CatalogException(CatalogException.UNPARSEABLE); + } + + Document doc = null; + + try { + doc = builder.parse(is); + } catch (SAXException se) { + throw new CatalogException(CatalogException.UNKNOWN_FORMAT); + } + + Element root = doc.getDocumentElement(); + + String namespaceURI = Namespaces.getNamespaceURI(root); + String localName = Namespaces.getLocalName(root); + + String domParserClass = getCatalogParser(namespaceURI, + localName); + + if (domParserClass == null) { + if (namespaceURI == null) { + Debug.message(1, "No Catalog parser for " + localName); + } else { + Debug.message(1, "No Catalog parser for " + + "{" + namespaceURI + "}" + + localName); + } + return; + } + + DOMCatalogParser domParser = null; + + try { + domParser = (DOMCatalogParser) Class.forName(domParserClass).newInstance(); + } catch (ClassNotFoundException cnfe) { + Debug.message(1, "Cannot load XML Catalog Parser class", domParserClass); + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (InstantiationException ie) { + Debug.message(1, "Cannot instantiate XML Catalog Parser class", domParserClass); + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (IllegalAccessException iae) { + Debug.message(1, "Cannot access XML Catalog Parser class", domParserClass); + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (ClassCastException cce ) { + Debug.message(1, "Cannot cast XML Catalog Parser class", domParserClass); + throw new CatalogException(CatalogException.UNPARSEABLE); + } + + Node node = root.getFirstChild(); + while (node != null) { + domParser.parseCatalogEntry(catalog, node); + node = node.getNextSibling(); + } + } + + /** + *

Read the catalog behind the specified URL.

+ * + * @see #readCatalog(Catalog, InputStream) + * + * @param catalog The catalog for which we are reading. + * @param fileUrl The URL of the document that should be read. + * + * @throws MalformedURLException if the specified URL cannot be + * turned into a URL object. + * @throws IOException if the URL cannot be read. + * @throws UnknownCatalogFormatException if the catalog format is + * not recognized. + * @throws UnparseableCatalogException if the catalog cannot be parsed. + * (For example, if it is supposed to be XML and isn't well-formed.) + */ + public void readCatalog(Catalog catalog, String fileUrl) + throws MalformedURLException, IOException, CatalogException { + URL url = new URL(fileUrl); + URLConnection urlCon = url.openConnection(); + readCatalog(catalog, urlCon.getInputStream()); + } +} diff --git a/java/src/org/apache/xml/resolver/readers/ExtendedXMLCatalogReader.java b/java/src/org/apache/xml/resolver/readers/ExtendedXMLCatalogReader.java new file mode 100644 index 0000000..86e0d28 --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/ExtendedXMLCatalogReader.java @@ -0,0 +1,235 @@ +// ExtendedXMLCatalogReader.java - Read XML Catalog files + +/* + * 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.readers; + +import java.lang.Integer; +import java.util.Stack; +import java.util.Vector; +import java.util.Enumeration; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.Resolver; +import org.apache.xml.resolver.CatalogEntry; +import org.apache.xml.resolver.CatalogException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.helpers.PublicId; +import org.apache.xml.resolver.helpers.Debug; + +import org.xml.sax.*; +import org.w3c.dom.*; + +/** + *

Parse Extended OASIS Entity Resolution Technical Committee + * XML Catalog files.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class ExtendedXMLCatalogReader extends OASISXMLCatalogReader { + /** The namespace name of extended catalog elements */ + public static final String extendedNamespaceName = "http://nwalsh.com/xcatalog/1.0"; + + /** + *

The SAX startElement method recognizes elements + * from the plain catalog format and instantiates CatalogEntry + * objects for them.

+ * + * @param namespaceURI The namespace name of the element. + * @param localName The local name of the element. + * @param qName The QName of the element. + * @param atts The list of attributes on the element. + * + * @see CatalogEntry + */ + public void startElement (String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + + // Check before calling the super because super will report our + // namespace as an extension namespace, but that doesn't count + // for this element. + boolean inExtension = inExtensionNamespace(); + + super.startElement(namespaceURI, localName, qName, atts); + + int entryType = -1; + Vector entryArgs = new Vector(); + + if (namespaceURI != null && extendedNamespaceName.equals(namespaceURI) + && !inExtension) { + // This is an Extended XML Catalog entry + + if (atts.getValue("xml:base") != null) { + String baseURI = atts.getValue("xml:base"); + entryType = Catalog.BASE; + entryArgs.add(baseURI); + baseURIStack.push(baseURI); + + Debug.message(4, "xml:base", baseURI); + + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry (base)", localName); + } + } + + entryType = -1; + entryArgs = new Vector(); + } else { + baseURIStack.push(baseURIStack.peek()); + } + + if (localName.equals("uriSuffix")) { + if (checkAttributes(atts, "suffix", "uri")) { + entryType = Resolver.URISUFFIX; + entryArgs.add(atts.getValue("suffix")); + entryArgs.add(atts.getValue("uri")); + + Debug.message(4, "uriSuffix", + atts.getValue("suffix"), + atts.getValue("uri")); + } + } else if (localName.equals("systemSuffix")) { + if (checkAttributes(atts, "suffix", "uri")) { + entryType = Resolver.SYSTEMSUFFIX; + entryArgs.add(atts.getValue("suffix")); + entryArgs.add(atts.getValue("uri")); + + Debug.message(4, "systemSuffix", + atts.getValue("suffix"), + atts.getValue("uri")); + } + } else { + // This is equivalent to an invalid catalog entry type + Debug.message(1, "Invalid catalog entry type", localName); + } + + if (entryType >= 0) { + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry", localName); + } + } + } + } + } + + /**

The SAX endElement method does nothing.

*/ + public void endElement (String namespaceURI, + String localName, + String qName) + throws SAXException { + + super.endElement(namespaceURI, localName, qName); + + // Check after popping the stack so we don't erroneously think we + // are our own extension namespace... + boolean inExtension = inExtensionNamespace(); + + int entryType = -1; + Vector entryArgs = new Vector(); + + if (namespaceURI != null + && (extendedNamespaceName.equals(namespaceURI)) + && !inExtension) { + + String popURI = (String) baseURIStack.pop(); + String baseURI = (String) baseURIStack.peek(); + + if (!baseURI.equals(popURI)) { + entryType = catalog.BASE; + entryArgs.add(baseURI); + + Debug.message(4, "(reset) xml:base", baseURI); + + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry (rbase)", localName); + } + } + } + } + } +} diff --git a/java/src/org/apache/xml/resolver/readers/OASISXMLCatalogReader.java b/java/src/org/apache/xml/resolver/readers/OASISXMLCatalogReader.java new file mode 100644 index 0000000..5314c4d --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/OASISXMLCatalogReader.java @@ -0,0 +1,565 @@ +// OASISXMLCatalogReader.java - Read XML Catalog files + +/* + * 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.readers; + +import java.lang.Integer; +import java.util.Stack; +import java.util.Vector; +import java.util.Enumeration; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogEntry; +import org.apache.xml.resolver.CatalogException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.helpers.PublicId; +import org.apache.xml.resolver.helpers.Debug; + +import org.xml.sax.*; +import org.w3c.dom.*; + +/** + *

Parse OASIS Entity Resolution Technical Committee + * XML Catalog files.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalogParser { + /** The catalog object needs to be stored by the object so that + * SAX callbacks can use it. + */ + protected Catalog catalog = null; + + /** The namespace name of OASIS ERTC catalogs */ + public static final String namespaceName = "urn:oasis:names:tc:entity:xmlns:xml:catalog"; + + /** The namespace name of OASIS ERTC TR9401 catalog extension */ + public static final String tr9401NamespaceName = "urn:oasis:names:tc:entity:xmlns:tr9401:catalog"; + + protected Stack baseURIStack = new Stack(); + protected Stack overrideStack = new Stack(); + protected Stack namespaceStack = new Stack(); + + /** Set the current catalog. */ + public void setCatalog (Catalog catalog) { + this.catalog = catalog; + } + + /** Get the current catalog. */ + public Catalog getCatalog () { + return catalog; + } + + /** + *

Are we in an extension namespace?

+ * + * @returns true if the current stack of open namespaces includes + * an extension namespace. + */ + protected boolean inExtensionNamespace() { + boolean inExtension = false; + + Enumeration elements = namespaceStack.elements(); + while (!inExtension && elements.hasMoreElements()) { + String ns = (String) elements.nextElement(); + if (ns == null) { + inExtension = true; + } else { + inExtension = (!ns.equals(tr9401NamespaceName) + && !ns.equals(namespaceName)); + } + } + + return inExtension; + } + + // ---------------------------------------------------------------------- + // Implement the SAX ContentHandler interface + + /**

The SAX setDocumentLocator method does nothing.

*/ + public void setDocumentLocator (Locator locator) { + return; + } + + /**

The SAX startDocument method does nothing.

*/ + public void startDocument () + throws SAXException { + baseURIStack.push(catalog.getCurrentBase()); + overrideStack.push(catalog.getDefaultOverride()); + return; + } + + /**

The SAX endDocument method does nothing.

*/ + public void endDocument () + throws SAXException { + return; + } + + /** + *

The SAX startElement method recognizes elements + * from the plain catalog format and instantiates CatalogEntry + * objects for them.

+ * + * @param namespaceURI The namespace name of the element. + * @param localName The local name of the element. + * @param qName The QName of the element. + * @param atts The list of attributes on the element. + * + * @see CatalogEntry + */ + public void startElement (String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + + int entryType = -1; + Vector entryArgs = new Vector(); + + namespaceStack.push(namespaceURI); + + boolean inExtension = inExtensionNamespace(); + + if (namespaceURI != null && namespaceName.equals(namespaceURI) + && !inExtension) { + // This is an XML Catalog entry + + if (atts.getValue("xml:base") != null) { + String baseURI = atts.getValue("xml:base"); + entryType = Catalog.BASE; + entryArgs.add(baseURI); + baseURIStack.push(baseURI); + + Debug.message(4, "xml:base", baseURI); + + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry (base)", localName); + } + } + + entryType = -1; + entryArgs = new Vector(); + + } else { + baseURIStack.push(baseURIStack.peek()); + } + + if ((localName.equals("catalog") || localName.equals("group")) + && atts.getValue("prefer") != null) { + String override = atts.getValue("prefer"); + + if (override.equals("public")) { + override = "yes"; + } else if (override.equals("system")) { + override = "no"; + } else { + Debug.message(1, + "Invalid prefer: must be 'system' or 'public'", + localName); + override = catalog.getDefaultOverride(); + } + + entryType = Catalog.OVERRIDE; + entryArgs.add(override); + overrideStack.push(override); + + Debug.message(4, "override", override); + + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry (override)", localName); + } + } + + entryType = -1; + entryArgs = new Vector(); + + } else { + overrideStack.push(overrideStack.peek()); + } + + if (localName.equals("delegatePublic")) { + if (checkAttributes(atts, "publicIdStartString", "catalog")) { + entryType = Catalog.DELEGATE_PUBLIC; + entryArgs.add(atts.getValue("publicIdStartString")); + entryArgs.add(atts.getValue("catalog")); + + Debug.message(4, "delegatePublic", + PublicId.normalize(atts.getValue("publicIdStartString")), + atts.getValue("catalog")); + } + } else if (localName.equals("delegateSystem")) { + if (checkAttributes(atts, "systemIdStartString", "catalog")) { + entryType = Catalog.DELEGATE_SYSTEM; + entryArgs.add(atts.getValue("systemIdStartString")); + entryArgs.add(atts.getValue("catalog")); + + Debug.message(4, "delegateSystem", + atts.getValue("systemIdStartString"), + atts.getValue("catalog")); + } + } else if (localName.equals("delegateURI")) { + if (checkAttributes(atts, "uriStartString", "catalog")) { + entryType = Catalog.DELEGATE_URI; + entryArgs.add(atts.getValue("uriStartString")); + entryArgs.add(atts.getValue("catalog")); + + Debug.message(4, "delegateURI", + atts.getValue("uriStartString"), + atts.getValue("catalog")); + } + } else if (localName.equals("rewriteSystem")) { + if (checkAttributes(atts, "systemIdStartString", "rewritePrefix")) { + entryType = Catalog.REWRITE_SYSTEM; + entryArgs.add(atts.getValue("systemIdStartString")); + entryArgs.add(atts.getValue("rewritePrefix")); + + Debug.message(4, "rewriteSystem", + atts.getValue("systemIdStartString"), + atts.getValue("rewritePrefix")); + } + } else if (localName.equals("rewriteURI")) { + if (checkAttributes(atts, "uriStartString", "rewritePrefix")) { + entryType = Catalog.REWRITE_URI; + entryArgs.add(atts.getValue("uriStartString")); + entryArgs.add(atts.getValue("rewritePrefix")); + + Debug.message(4, "rewriteURI", + atts.getValue("uriStartString"), + atts.getValue("rewritePrefix")); + } + } else if (localName.equals("nextCatalog")) { + if (checkAttributes(atts, "catalog")) { + entryType = Catalog.CATALOG; + entryArgs.add(atts.getValue("catalog")); + + Debug.message(4, "nextCatalog", atts.getValue("catalog")); + } + } else if (localName.equals("public")) { + if (checkAttributes(atts, "publicId", "uri")) { + entryType = Catalog.PUBLIC; + entryArgs.add(atts.getValue("publicId")); + entryArgs.add(atts.getValue("uri")); + + Debug.message(4, "public", + PublicId.normalize(atts.getValue("publicId")), + atts.getValue("uri")); + } + } else if (localName.equals("system")) { + if (checkAttributes(atts, "systemId", "uri")) { + entryType = Catalog.SYSTEM; + entryArgs.add(atts.getValue("systemId")); + entryArgs.add(atts.getValue("uri")); + + Debug.message(4, "system", + atts.getValue("systemId"), + atts.getValue("uri")); + } + } else if (localName.equals("uri")) { + if (checkAttributes(atts, "name", "uri")) { + entryType = Catalog.URI; + entryArgs.add(atts.getValue("name")); + entryArgs.add(atts.getValue("uri")); + + Debug.message(4, "uri", + atts.getValue("name"), + atts.getValue("uri")); + } + } else if (localName.equals("catalog")) { + // nop, start of catalog + } else if (localName.equals("group")) { + // nop, a group + } else { + // This is equivalent to an invalid catalog entry type + Debug.message(1, "Invalid catalog entry type", localName); + } + + if (entryType >= 0) { + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry", localName); + } + } + } + } + + if (namespaceURI != null && tr9401NamespaceName.equals(namespaceURI) + && !inExtension) { + // This is a TR9401 Catalog entry + + if (atts.getValue("xml:base") != null) { + String baseURI = atts.getValue("xml:base"); + entryType = Catalog.BASE; + entryArgs.add(baseURI); + baseURIStack.push(baseURI); + + Debug.message(4, "xml:base", baseURI); + + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry (base)", localName); + } + } + + entryType = -1; + entryArgs = new Vector(); + + } else { + baseURIStack.push(baseURIStack.peek()); + } + + if (localName.equals("doctype")) { + entryType = catalog.DOCTYPE; + entryArgs.add(atts.getValue("name")); + entryArgs.add(atts.getValue("uri")); + } else if (localName.equals("document")) { + entryType = catalog.DOCUMENT; + entryArgs.add(atts.getValue("uri")); + } else if (localName.equals("dtddecl")) { + entryType = catalog.DTDDECL; + entryArgs.add(atts.getValue("publicId")); + entryArgs.add(atts.getValue("uri")); + } else if (localName.equals("entity")) { + entryType = Catalog.ENTITY; + entryArgs.add(atts.getValue("name")); + entryArgs.add(atts.getValue("uri")); + } else if (localName.equals("linktype")) { + entryType = Catalog.LINKTYPE; + entryArgs.add(atts.getValue("name")); + entryArgs.add(atts.getValue("uri")); + } else if (localName.equals("notation")) { + entryType = Catalog.NOTATION; + entryArgs.add(atts.getValue("name")); + entryArgs.add(atts.getValue("uri")); + } else if (localName.equals("sgmldecl")) { + entryType = Catalog.SGMLDECL; + entryArgs.add(atts.getValue("uri")); + } else { + // This is equivalent to an invalid catalog entry type + Debug.message(1, "Invalid catalog entry type", localName); + } + + if (entryType >= 0) { + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry", localName); + } + } + } + } + } + + public boolean checkAttributes (Attributes atts, String attName) { + if (atts.getValue(attName) == null) { + Debug.message(1, "Error: required attribute " + attName + " missing."); + return false; + } else { + return true; + } + } + + public boolean checkAttributes (Attributes atts, + String attName1, + String attName2) { + return checkAttributes(atts, attName1) + && checkAttributes(atts, attName2); + } + + /**

The SAX endElement method does nothing.

*/ + public void endElement (String namespaceURI, + String localName, + String qName) + throws SAXException { + + int entryType = -1; + Vector entryArgs = new Vector(); + + boolean inExtension = inExtensionNamespace(); + + if (namespaceURI != null + && !inExtension + && (namespaceName.equals(namespaceURI) + || tr9401NamespaceName.equals(namespaceURI))) { + + String popURI = (String) baseURIStack.pop(); + String baseURI = (String) baseURIStack.peek(); + + if (!baseURI.equals(popURI)) { + entryType = catalog.BASE; + entryArgs.add(baseURI); + + Debug.message(4, "(reset) xml:base", baseURI); + + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry (rbase)", localName); + } + } + } + } + + if (namespaceURI != null && namespaceName.equals(namespaceURI) + && !inExtension) { + if (localName.equals("catalog") || localName.equals("group")) { + String popOverride = (String) overrideStack.pop(); + String override = (String) overrideStack.peek(); + + if (!override.equals(popOverride)) { + entryType = catalog.OVERRIDE; + entryArgs.add(override); + overrideStack.push(override); + + Debug.message(4, "(reset) override", override); + + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry (roverride)", localName); + } + } + } + } + } + + namespaceStack.pop(); + + return; + } + + /**

The SAX characters method does nothing.

*/ + public void characters (char ch[], int start, int length) + throws SAXException { + return; + } + + /**

The SAX ignorableWhitespace method does nothing.

*/ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException { + return; + } + + /**

The SAX processingInstruction method does nothing.

*/ + public void processingInstruction (String target, String data) + throws SAXException { + return; + } + + /**

The SAX skippedEntity method does nothing.

*/ + public void skippedEntity (String name) + throws SAXException { + return; + } + + /**

The SAX startPrefixMapping method does nothing.

*/ + public void startPrefixMapping(String prefix, String uri) + throws SAXException { + return; + } + + /**

The SAX endPrefixMapping method does nothing.

*/ + public void endPrefixMapping(String prefix) + throws SAXException { + return; + } + +} diff --git a/java/src/org/apache/xml/resolver/readers/SAXCatalogParser.java b/java/src/org/apache/xml/resolver/readers/SAXCatalogParser.java new file mode 100644 index 0000000..abec686 --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/SAXCatalogParser.java @@ -0,0 +1,82 @@ +// SAXCatalogParser.java - An interface for reading catalog files + +/* + * 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.readers; + +import org.apache.xml.resolver.Catalog; +import org.xml.sax.*; + +/** + *

The SAXCatalogParser interface.

+ * + *

This interface must be implemented in order for a class to + * participate as a parser for the SAXCatalogReader. + * + * @see Catalog + * @see SAXCatalogReader + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public interface SAXCatalogParser extends ContentHandler, DocumentHandler { + /** Set the Catalog for which parsing is being performed. */ + public void setCatalog(Catalog catalog); +} diff --git a/java/src/org/apache/xml/resolver/readers/SAXCatalogReader.java b/java/src/org/apache/xml/resolver/readers/SAXCatalogReader.java new file mode 100644 index 0000000..ff2a471 --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/SAXCatalogReader.java @@ -0,0 +1,515 @@ +// SAXCatalogReader.java - Read XML Catalog files + +/* + * 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.readers; + +import java.lang.Integer; +import java.util.Vector; +import java.util.Hashtable; +import java.util.Enumeration; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import java.net.UnknownHostException; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogEntry; +import org.apache.xml.resolver.CatalogException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.helpers.Debug; +import org.apache.xml.resolver.helpers.PublicId; +import org.apache.xml.resolver.helpers.Namespaces; + +import org.xml.sax.*; + +import javax.xml.parsers.*; + +/** + *

A SAX-based CatalogReader

+ * + *

This class is used to read XML Catalogs using the SAX. This reader + * has an advantage over the DOM-based reader in that it functions on + * the stream of SAX events. It has the disadvantage + * that it cannot look around in the tree.

+ * + *

Since the choice of CatalogReaders (in the InputStream case) can only + * be made on the basis of MIME type, the following problem occurs: only + * one CatalogReader can exist for all XML mime types. In order to get + * around this problem, the SAXCatalogReader relies on a set of external + * CatalogParsers to actually build the catalog.

+ * + *

The selection of CatalogParsers is made on the basis of the QName + * of the root element of the document.

+ * + * @see Catalog + * @see CatalogReader + * @see SAXCatalogReader + * @see TextCatalogReader + * @see DOMCatalogParser + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class SAXCatalogReader implements CatalogReader, ContentHandler, DocumentHandler { + /** The SAX Parser Factory */ + protected SAXParserFactory parserFactory = null; + + /** The SAX Parser Class */ + protected String parserClass = null; + + /** + *

Mapping table from QNames to CatalogParser classes.

+ * + *

Each key in this hash table has the form "elementname" + * or "{namespaceuri}elementname". The former is used if the + * namespace URI is null.

+ */ + protected Hashtable namespaceMap = new Hashtable(); + + /** The parser in use for the current catalog. */ + private SAXCatalogParser saxParser = null; + + /** Set if something goes horribly wrong. It allows the class to + * ignore the rest of the events that are received. + */ + private boolean abandonHope = false; + + /** The Catalog that we're working for. */ + private Catalog catalog; + + /** Set the XML SAX Parser Factory. + */ + public void setParserFactory(SAXParserFactory parserFactory) { + this.parserFactory = parserFactory; + } + + /** Set the XML SAX Parser Class + */ + public void setParserClass(String parserClass) { + this.parserClass = parserClass; + } + + /** Get the parser factory currently in use. */ + public SAXParserFactory getParserFactory() { + return parserFactory; + } + + /** Get the parser class currently in use. */ + public String getParserClass() { + return parserClass; + } + + /** Set the SAXCatalogParser class for the given namespace/root + * element type. + */ + public void setCatalogParser(String namespaceURI, + String rootElement, + String parserClass) { + if (namespaceURI == null) { + namespaceMap.put(rootElement, parserClass); + } else { + namespaceMap.put("{"+namespaceURI+"}"+rootElement, parserClass); + } + } + + /** Get the SAXCatalogParser class for the given namespace/root + * element type. + */ + public String getCatalogParser(String namespaceURI, + String rootElement) { + if (namespaceURI == null) { + return (String) namespaceMap.get(rootElement); + } else { + return (String) namespaceMap.get("{"+namespaceURI+"}"+rootElement); + } + } + + /** The constructor */ + public SAXCatalogReader() { + parserFactory = null; + parserClass = null; + } + + /** The constructor */ + public SAXCatalogReader(SAXParserFactory parserFactory) { + this.parserFactory = parserFactory; + } + + /** The constructor */ + public SAXCatalogReader(String parserClass) { + this.parserClass = parserClass; + } + + /** + *

Parse an XML Catalog file.

+ * + * @param catalog The catalog to which this catalog file belongs + * @param fileUrl The URL or filename of the catalog file to process + * + * @throws MalformedURLException Improper fileUrl + * @throws IOException Error reading catalog file + */ + public void readCatalog(Catalog catalog, String fileUrl) + throws MalformedURLException, IOException, + CatalogException { + + URL url = null; + + try { + url = new URL(fileUrl); + } catch (MalformedURLException e) { + url = new URL("file:///" + fileUrl); + } + + try { + URLConnection urlCon = url.openConnection(); + readCatalog(catalog, urlCon.getInputStream()); + } catch (FileNotFoundException e) { + Debug.message(1, "Failed to load catalog, file not found", + url.toString()); + } + } + + /** + *

Parse an XML Catalog stream.

+ * + * @param catalog The catalog to which this catalog file belongs + * @param is The input stream from which the catalog will be read + * + * @throws MalformedURLException Improper fileUrl + * @throws IOException Error reading catalog file + * @throws CatalogException A Catalog exception + */ + public void readCatalog(Catalog catalog, InputStream is) + throws IOException, CatalogException { + + // Create an instance of the parser + if (parserFactory == null && parserClass == null) { + Debug.message(1, "Cannot read SAX catalog without a parser"); + throw new CatalogException(CatalogException.UNPARSEABLE); + } + + this.catalog = catalog; + + try { + if (parserFactory != null) { + SAXParser parser = parserFactory.newSAXParser(); + SAXParserHandler spHandler = new SAXParserHandler(); + spHandler.setContentHandler(this); + parser.parse(new InputSource(is), spHandler); + } else { + Parser parser = (Parser) Class.forName(parserClass).newInstance(); + parser.setDocumentHandler(this); + parser.parse(new InputSource(is)); + } + } catch (ClassNotFoundException cnfe) { + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (IllegalAccessException iae) { + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (InstantiationException ie) { + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (ParserConfigurationException pce) { + throw new CatalogException(CatalogException.UNKNOWN_FORMAT); + } catch (SAXException se) { + Exception e = se.getException(); + // FIXME: there must be a better way + UnknownHostException uhe = new UnknownHostException(); + FileNotFoundException fnfe = new FileNotFoundException(); + if (e != null) { + if (e.getClass() == uhe.getClass()) { + throw new CatalogException(CatalogException.PARSE_FAILED, + e.toString()); + } else if (e.getClass() == fnfe.getClass()) { + throw new CatalogException(CatalogException.PARSE_FAILED, + e.toString()); + } + } + throw new CatalogException(se); + } + } + + // ---------------------------------------------------------------------- + // Implement the SAX ContentHandler interface + + /**

The SAX setDocumentLocator method. Does nothing.

*/ + public void setDocumentLocator (Locator locator) { + if (saxParser != null) { + saxParser.setDocumentLocator(locator); + } + } + + /**

The SAX startDocument method. Does nothing.

*/ + public void startDocument () throws SAXException { + saxParser = null; + abandonHope = false; + return; + } + + /**

The SAX endDocument method. Does nothing.

*/ + public void endDocument ()throws SAXException { + if (saxParser != null) { + saxParser.endDocument(); + } + } + + /** + *

The SAX startElement method.

+ * + *

The catalog parser is selected based on the namespace of the + * first element encountered in the catalog.

+ */ + public void startElement (String name, + AttributeList atts) + throws SAXException { + + if (abandonHope) { + return; + } + + if (saxParser == null) { + String prefix = ""; + if (name.indexOf(':') > 0) { + prefix = name.substring(0, name.indexOf(':')); + } + + String localName = name; + if (localName.indexOf(':') > 0) { + localName = localName.substring(localName.indexOf(':')+1); + } + + String namespaceURI = null; + if (prefix.equals("")) { + namespaceURI = atts.getValue("xmlns"); + } else { + namespaceURI = atts.getValue("xmlns:" + prefix); + } + + String saxParserClass = getCatalogParser(namespaceURI, + localName); + + if (saxParserClass == null) { + abandonHope = true; + if (namespaceURI == null) { + Debug.message(2, "No Catalog parser for " + name); + } else { + Debug.message(2, "No Catalog parser for " + + "{" + namespaceURI + "}" + + name); + } + return; + } + + try { + saxParser = (SAXCatalogParser) + Class.forName(saxParserClass).newInstance(); + + saxParser.setCatalog(catalog); + saxParser.startDocument(); + saxParser.startElement(name, atts); + } catch (ClassNotFoundException cnfe) { + saxParser = null; + abandonHope = true; + Debug.message(2, cnfe.toString()); + } catch (InstantiationException ie) { + saxParser = null; + abandonHope = true; + Debug.message(2, ie.toString()); + } catch (IllegalAccessException iae) { + saxParser = null; + abandonHope = true; + Debug.message(2, iae.toString()); + } catch (ClassCastException cce ) { + saxParser = null; + abandonHope = true; + Debug.message(2, cce.toString()); + } + } else { + saxParser.startElement(name, atts); + } + } + + /** + *

The SAX2 startElement method.

+ * + *

The catalog parser is selected based on the namespace of the + * first element encountered in the catalog.

+ */ + public void startElement (String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + + if (abandonHope) { + return; + } + + if (saxParser == null) { + String saxParserClass = getCatalogParser(namespaceURI, + localName); + + if (saxParserClass == null) { + abandonHope = true; + if (namespaceURI == null) { + Debug.message(2, "No Catalog parser for " + localName); + } else { + Debug.message(2, "No Catalog parser for " + + "{" + namespaceURI + "}" + + localName); + } + return; + } + + try { + saxParser = (SAXCatalogParser) + Class.forName(saxParserClass).newInstance(); + + saxParser.setCatalog(catalog); + saxParser.startDocument(); + saxParser.startElement(namespaceURI, localName, qName, atts); + } catch (ClassNotFoundException cnfe) { + saxParser = null; + abandonHope = true; + Debug.message(2, cnfe.toString()); + } catch (InstantiationException ie) { + saxParser = null; + abandonHope = true; + Debug.message(2, ie.toString()); + } catch (IllegalAccessException iae) { + saxParser = null; + abandonHope = true; + Debug.message(2, iae.toString()); + } catch (ClassCastException cce ) { + saxParser = null; + abandonHope = true; + Debug.message(2, cce.toString()); + } + } else { + saxParser.startElement(namespaceURI, localName, qName, atts); + } + } + + /**

The SAX endElement method. Does nothing.

*/ + public void endElement (String name) throws SAXException { + if (saxParser != null) { + saxParser.endElement(name); + } + } + + /**

The SAX2 endElement method. Does nothing.

*/ + public void endElement (String namespaceURI, + String localName, + String qName) throws SAXException { + if (saxParser != null) { + saxParser.endElement(namespaceURI, localName, qName); + } + } + + /**

The SAX characters method. Does nothing.

*/ + public void characters (char ch[], int start, int length) + throws SAXException { + if (saxParser != null) { + saxParser.characters(ch, start, length); + } + } + + /**

The SAX ignorableWhitespace method. Does nothing.

*/ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException { + if (saxParser != null) { + saxParser.ignorableWhitespace(ch, start, length); + } + } + + /**

The SAX processingInstruction method. Does nothing.

*/ + public void processingInstruction (String target, String data) + throws SAXException { + if (saxParser != null) { + saxParser.processingInstruction(target, data); + } + } + + /**

The SAX startPrefixMapping method. Does nothing.

*/ + public void startPrefixMapping (String prefix, String uri) + throws SAXException { + if (saxParser != null) { + saxParser.startPrefixMapping (prefix, uri); + } + } + + /**

The SAX endPrefixMapping method. Does nothing.

*/ + public void endPrefixMapping (String prefix) + throws SAXException { + if (saxParser != null) { + saxParser.endPrefixMapping (prefix); + } + } + + /**

The SAX skippedentity method. Does nothing.

*/ + public void skippedEntity (String name) + throws SAXException { + if (saxParser != null) { + saxParser.skippedEntity(name); + } + } +} diff --git a/java/src/org/apache/xml/resolver/readers/SAXParserHandler.java b/java/src/org/apache/xml/resolver/readers/SAXParserHandler.java new file mode 100644 index 0000000..8e6dbcf --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/SAXParserHandler.java @@ -0,0 +1,187 @@ +// SAXParserHandler.java - An entity-resolving DefaultHandler + +/* + * 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.readers; + +import java.io.IOException; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +/** + *

An entity-resolving DefaultHandler

+ * + *

This class provides a SAXParser DefaultHandler that performs + * entity resolution. + *

+ * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * @version 1.0 + */ +public class SAXParserHandler extends DefaultHandler { + private EntityResolver er = null; + private ContentHandler ch = null; + + public SAXParserHandler() { + super(); + } + + public void setEntityResolver(EntityResolver er) { + this.er = er; + } + + public void setContentHandler(ContentHandler ch) { + this.ch = ch; + } + + // Entity Resolver + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException { + + if (er != null) { + try { + return er.resolveEntity(publicId, systemId); + } catch (IOException e) { + System.out.println("resolveEntity threw IOException!"); + return null; + } + } else { + return null; + } + } + + // Content Handler + public void characters(char[] ch, int start, int length) + throws SAXException { + if (this.ch != null) { + this.ch.characters(ch, start, length); + } + } + + public void endDocument() + throws SAXException { + if (ch != null) { + ch.endDocument(); + } + } + + public void endElement(String namespaceURI, String localName, String qName) + throws SAXException { + if (ch != null) { + ch.endElement(namespaceURI, localName, qName); + } + } + + public void endPrefixMapping(String prefix) + throws SAXException { + if (ch != null) { + ch.endPrefixMapping(prefix); + } + } + + public void ignorableWhitespace(char[] ch, int start, int length) + throws SAXException { + if (this.ch != null) { + this.ch.ignorableWhitespace(ch, start, length); + } + } + + public void processingInstruction(String target, String data) + throws SAXException { + if (ch != null) { + ch.processingInstruction(target, data); + } + } + + public void setDocumentLocator(Locator locator) { + if (ch != null) { + ch.setDocumentLocator(locator); + } + } + + public void skippedEntity(String name) + throws SAXException { + if (ch != null) { + ch.skippedEntity(name); + } + } + + public void startDocument() + throws SAXException { + if (ch != null) { + ch.startDocument(); + } + } + + public void startElement(String namespaceURI, String localName, + String qName, Attributes atts) + throws SAXException { + if (ch != null) { + ch.startElement(namespaceURI, localName, qName, atts); + } + } + + public void startPrefixMapping(String prefix, String uri) + throws SAXException { + if (ch != null) { + ch.startPrefixMapping(prefix, uri); + } + } +} diff --git a/java/src/org/apache/xml/resolver/readers/TR9401CatalogReader.java b/java/src/org/apache/xml/resolver/readers/TR9401CatalogReader.java new file mode 100644 index 0000000..67ad4ee --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/TR9401CatalogReader.java @@ -0,0 +1,179 @@ +// TR9401CatalogReader.java - Read OASIS Catalog files + +/* + * 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.readers; + +import java.lang.Integer; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import java.util.Vector; +import java.util.Stack; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogEntry; +import org.apache.xml.resolver.CatalogException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.helpers.Debug; +import org.apache.xml.resolver.helpers.PublicId; + +/** + *

Parses OASIS Open Catalog files.

+ * + *

This class reads OASIS Open Catalog files, returning a stream + * of tokens.

+ * + *

This code interrogates the following non-standard system properties:

+ * + *
+ *
xml.catalog.debug
+ *

Sets the debug level. A value of 0 is assumed if the + * property is not set or is not a number.

+ *
+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class TR9401CatalogReader extends TextCatalogReader { + + /** + *

Start parsing an OASIS TR9401 Open Catalog file. The file is + * actually read and parsed + * as needed by nextEntry.

+ * + *

In a TR9401 Catalog the 'DELEGATE' entry delegates public + * identifiers. There is no delegate entry for system identifiers + * or URIs.

+ * + * @param fileUrl The URL or filename of the catalog file to process + * + * @throws MalformedURLException Improper fileUrl + * @throws IOException Error reading catalog file + */ + public void readCatalog(Catalog catalog, InputStream is) + throws MalformedURLException, IOException { + + catfile = is; + + if (catfile == null) { + return; + } + + Vector unknownEntry = null; + + while (true) { + String token = nextToken(); + + if (token == null) { + if (unknownEntry != null) { + catalog.unknownEntry(unknownEntry); + unknownEntry = null; + } + catfile.close(); + catfile = null; + return; + } + + String entryToken = null; + if (caseSensitive) { + entryToken = token; + } else { + entryToken = token.toUpperCase(); + } + + if (entryToken.equals("DELEGATE")) { + entryToken = "DELEGATE_PUBLIC"; + } + + try { + int type = CatalogEntry.getEntryType(entryToken); + int numArgs = CatalogEntry.getEntryArgCount(type); + Vector args = new Vector(); + + if (unknownEntry != null) { + catalog.unknownEntry(unknownEntry); + unknownEntry = null; + } + + for (int count = 0; count < numArgs; count++) { + args.addElement(nextToken()); + } + + catalog.addEntry(new CatalogEntry(entryToken, args)); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + if (unknownEntry == null) { + unknownEntry = new Vector(); + } + unknownEntry.addElement(token); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry", token); + unknownEntry = null; + } + } + } + } +} diff --git a/java/src/org/apache/xml/resolver/readers/TextCatalogReader.java b/java/src/org/apache/xml/resolver/readers/TextCatalogReader.java new file mode 100644 index 0000000..8f69f82 --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/TextCatalogReader.java @@ -0,0 +1,328 @@ +// TextCatalogReader.java - Read text/plain Catalog files + +/* + * 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.readers; + +import java.lang.Integer; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import java.util.Vector; +import java.util.Stack; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogEntry; +import org.apache.xml.resolver.CatalogException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.helpers.Debug; +import org.apache.xml.resolver.helpers.PublicId; + +/** + *

Parses plain text Catalog files.

+ * + *

This class reads plain text Open Catalog files.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class TextCatalogReader implements CatalogReader { + /** The input stream used to read the catalog */ + protected InputStream catfile = null; + + /** + * Character lookahead stack. Reading a catalog sometimes requires + * up to two characters of lookahead. + */ + protected int[] stack = new int[3]; + + /** + * Token stack. Recognizing an unexpected catalog entry requires + * the ability to "push back" a token. + */ + protected Stack tokenStack = new Stack(); + + /** The current position on the lookahead stack */ + protected int top = -1; + + /** Are keywords in the catalog case sensitive? */ + protected boolean caseSensitive = false; + + /** + *

Construct a CatalogReader object.

+ */ + public TextCatalogReader() { } + + public void setCaseSensitive(boolean isCaseSensitive) { + caseSensitive = isCaseSensitive; + } + + public boolean getCaseSensitive() { + return caseSensitive; + } + + /** + *

Start parsing a text catalog file. The file is + * actually read and parsed + * as needed by nextEntry.

+ * + * @param fileUrl The URL or filename of the catalog file to process + * + * @throws MalformedURLException Improper fileUrl + * @throws IOException Error reading catalog file + */ + public void readCatalog(Catalog catalog, String fileUrl) + throws MalformedURLException, IOException { + URL catURL = null; + + try { + catURL = new URL(fileUrl); + } catch (MalformedURLException e) { + catURL = new URL("file:///" + fileUrl); + } + + URLConnection urlCon = catURL.openConnection(); + try { + readCatalog(catalog, urlCon.getInputStream()); + } catch (FileNotFoundException e) { + Debug.message(1, "Failed to load catalog, file not found", + catURL.toString()); + } + } + + public void readCatalog(Catalog catalog, InputStream is) + throws MalformedURLException, IOException { + + catfile = is; + + if (catfile == null) { + return; + } + + Vector unknownEntry = null; + + while (true) { + String token = nextToken(); + + if (token == null) { + if (unknownEntry != null) { + catalog.unknownEntry(unknownEntry); + unknownEntry = null; + } + catfile.close(); + catfile = null; + return; + } + + String entryToken = null; + if (caseSensitive) { + entryToken = token; + } else { + entryToken = token.toUpperCase(); + } + + try { + int type = CatalogEntry.getEntryType(entryToken); + int numArgs = CatalogEntry.getEntryArgCount(type); + Vector args = new Vector(); + + if (unknownEntry != null) { + catalog.unknownEntry(unknownEntry); + unknownEntry = null; + } + + for (int count = 0; count < numArgs; count++) { + args.addElement(nextToken()); + } + + catalog.addEntry(new CatalogEntry(entryToken, args)); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + if (unknownEntry == null) { + unknownEntry = new Vector(); + } + unknownEntry.addElement(token); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry", token); + unknownEntry = null; + } + } + } + } + + /** + *

The destructor.

+ * + *

Makes sure the catalog file is closed.

+ */ + protected void finalize() { + if (catfile != null) { + try { + catfile.close(); + } catch (IOException e) { + // whatever... + } + } + catfile = null; + } + + // ----------------------------------------------------------------- + + /** + *

Return the next token in the catalog file.

+ * + * @return The Catalog file token from the input stream. + * @throws IOException If an error occurs reading from the stream. + */ + protected String nextToken() throws IOException { + String token = ""; + int ch, nextch; + + if (!tokenStack.empty()) { + return (String) tokenStack.pop(); + } + + // Skip over leading whitespace and comments + while (true) { + // skip leading whitespace + ch = catfile.read(); + while (ch <= ' ') { // all ctrls are whitespace + ch = catfile.read(); + if (ch < 0) { + return null; + } + } + + // now 'ch' is the current char from the file + nextch = catfile.read(); + if (nextch < 0) { + return null; + } + + if (ch == '-' && nextch == '-') { + // we've found a comment, skip it... + ch = ' '; + nextch = nextChar(); + while (ch != '-' || nextch != '-') { + ch = nextch; + nextch = nextChar(); + } + + // Ok, we've found the end of the comment, + // loop back to the top and start again... + } else { + stack[++top] = nextch; + stack[++top] = ch; + break; + } + } + + ch = nextChar(); + if (ch == '"' || ch == '\'') { + int quote = ch; + while ((ch = nextChar()) != quote) { + char[] chararr = new char[1]; + chararr[0] = (char) ch; + String s = new String(chararr); + token = token.concat(s); + } + return token; + } else { + // return the next whitespace or comment delimited + // string + while (ch > ' ') { + nextch = nextChar(); + if (ch == '-' && nextch == '-') { + stack[++top] = ch; + stack[++top] = nextch; + return token; + } else { + char[] chararr = new char[1]; + chararr[0] = (char) ch; + String s = new String(chararr); + token = token.concat(s); + ch = nextch; + } + } + return token; + } + } + + /** + *

Return the next logical character from the input stream.

+ * + * @return The next (logical) character from the input stream. The + * character may be buffered from a previous lookahead. + * + * @throws IOException If an error occurs reading from the stream. + */ + protected int nextChar() throws IOException { + if (top < 0) { + return catfile.read(); + } else { + return stack[top--]; + } + } +} diff --git a/java/src/org/apache/xml/resolver/readers/XCatalogReader.java b/java/src/org/apache/xml/resolver/readers/XCatalogReader.java new file mode 100644 index 0000000..e396b37 --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/XCatalogReader.java @@ -0,0 +1,234 @@ +// XCatalogReader.java - Read XML Catalog files + +/* + * 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.readers; + +import java.lang.Integer; +import java.util.Vector; +import java.util.Enumeration; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogEntry; +import org.apache.xml.resolver.CatalogException; +import org.apache.xml.resolver.readers.CatalogReader; +import org.apache.xml.resolver.helpers.PublicId; +import org.apache.xml.resolver.helpers.Debug; + +import org.xml.sax.*; + +import javax.xml.parsers.*; + +/** + *

Parse "xcatalog" XML Catalog files, this is the XML Catalog format + * developed by John Cowan and supported by Apache.

+ * + * @see Catalog + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class XCatalogReader extends SAXCatalogReader implements SAXCatalogParser { + /** The catalog object needs to be stored by the object so that + * SAX callbacks can use it. + */ + protected Catalog catalog = null; + + /** Set the current catalog. */ + public void setCatalog (Catalog catalog) { + this.catalog = catalog; + } + + /** Get the current catalog. */ + public Catalog getCatalog () { + return catalog; + } + + /** The constructor */ + public XCatalogReader(SAXParserFactory parserFactory) { + super(parserFactory); + } + + // ---------------------------------------------------------------------- + // Implement the SAX DocumentHandler interface + + /**

The SAX setDocumentLocator method does nothing.

*/ + public void setDocumentLocator (Locator locator) { + return; + } + + /**

The SAX startDocument method does nothing.

*/ + public void startDocument () + throws SAXException { + return; + } + + /**

The SAX endDocument method does nothing.

*/ + public void endDocument () + throws SAXException { + return; + } + + /** + *

The SAX startElement method recognizes elements + * from the plain catalog format and instantiates CatalogEntry + * objects for them.

+ * + * @param namespaceURI The namespace name of the element. + * @param localName The local name of the element. + * @param qName The QName of the element. + * @param atts The list of attributes on the element. + * + * @see CatalogEntry + */ + public void startElement (String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + + int entryType = -1; + Vector entryArgs = new Vector(); + + if (localName.equals("Base")) { + entryType = catalog.BASE; + entryArgs.add(atts.getValue("HRef")); + + Debug.message(4, "Base", atts.getValue("HRef")); + } else if (localName.equals("Delegate")) { + entryType = catalog.DELEGATE_PUBLIC; + entryArgs.add(atts.getValue("PublicId")); + entryArgs.add(atts.getValue("HRef")); + + Debug.message(4, "Delegate", + PublicId.normalize(atts.getValue("PublicId")), + atts.getValue("HRef")); + } else if (localName.equals("Extend")) { + entryType = catalog.CATALOG; + entryArgs.add(atts.getValue("HRef")); + + Debug.message(4, "Extend", atts.getValue("HRef")); + } else if (localName.equals("Map")) { + entryType = catalog.PUBLIC; + entryArgs.add(atts.getValue("PublicId")); + entryArgs.add(atts.getValue("HRef")); + + Debug.message(4, "Map", + PublicId.normalize(atts.getValue("PublicId")), + atts.getValue("HRef")); + } else if (localName.equals("Remap")) { + entryType = catalog.SYSTEM; + entryArgs.add(atts.getValue("SystemId")); + entryArgs.add(atts.getValue("HRef")); + + Debug.message(4, "Remap", + atts.getValue("SystemId"), + atts.getValue("HRef")); + } else if (localName.equals("XMLCatalog")) { + // nop, start of catalog + } else { + // This is equivalent to an invalid catalog entry type + Debug.message(1, "Invalid catalog entry type", localName); + } + + if (entryType >= 0) { + try { + CatalogEntry ce = new CatalogEntry(entryType, entryArgs); + catalog.addEntry(ce); + } catch (CatalogException cex) { + if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { + Debug.message(1, "Invalid catalog entry type", localName); + } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { + Debug.message(1, "Invalid catalog entry", localName); + } + } + } + } + + /**

The SAX endElement method does nothing.

*/ + public void endElement (String namespaceURI, + String localName, + String qName) + throws SAXException { + return; + } + + /**

The SAX characters method does nothing.

*/ + public void characters (char ch[], int start, int length) + throws SAXException { + return; + } + + /**

The SAX ignorableWhitespace method does nothing.

*/ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException { + return; + } + + /**

The SAX processingInstruction method does nothing.

*/ + public void processingInstruction (String target, String data) + throws SAXException { + return; + } +} diff --git a/java/src/org/apache/xml/resolver/readers/package.html b/java/src/org/apache/xml/resolver/readers/package.html new file mode 100644 index 0000000..e79bf6d --- /dev/null +++ b/java/src/org/apache/xml/resolver/readers/package.html @@ -0,0 +1,12 @@ + + +Apache XML Commons Resolver Readers package + + +

Apache XML Commons Resolver Readers package

+ +

This package provides the set of classes that read catalog files +and extract catalog entries from them.

+ + + diff --git a/java/src/org/apache/xml/resolver/tools/CatalogResolver.java b/java/src/org/apache/xml/resolver/tools/CatalogResolver.java new file mode 100644 index 0000000..5934f43 --- /dev/null +++ b/java/src/org/apache/xml/resolver/tools/CatalogResolver.java @@ -0,0 +1,371 @@ +// CatalogResolver.java - A SAX EntityResolver/JAXP URI Resolver + +/* + * 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.tools; + +import java.lang.ClassNotFoundException; +import java.lang.ClassCastException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.MalformedURLException; + +import org.xml.sax.*; +import org.xml.sax.helpers.*; + +import javax.xml.parsers.*; +import javax.xml.transform.*; +import javax.xml.transform.sax.SAXSource; + +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogManager; +import org.apache.xml.resolver.helpers.Debug; + +/** + *

A SAX EntityResolver/JAXP URIResolver that uses catalogs.

+ * + *

This class implements both a SAX EntityResolver and a JAXP URIResolver. + *

+ * + *

This resolver understands OASIS TR9401 catalogs, XCatalogs, and the + * current working draft of the OASIS Entity Resolution Technical + * Committee specification.

+ * + * @see Catalog + * @see org.xml.sax.EntityResolver + * @see javax.xml.transform.URIResolver + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class CatalogResolver implements EntityResolver, URIResolver { + /** Make the parser Namespace aware? */ + public boolean namespaceAware = true; + + /** Make the parser validating? */ + public boolean validating = false; + + /** The underlying catalog */ + private static Catalog staticCatalog = null; + private Catalog catalog = null; + + /** Constructor */ + public CatalogResolver() { + initializeCatalogs(false); + } + + /** Constructor */ + public CatalogResolver(boolean privateCatalog) { + initializeCatalogs(privateCatalog); + } + + /** Initialize catalog */ + private void initializeCatalogs(boolean privateCatalog) { + catalog = staticCatalog; + + if (privateCatalog || catalog == null) { + try { + String catalogClassName = CatalogManager.catalogClassName(); + if (catalogClassName == null) { + catalog = new Catalog(); + } else { + try { + catalog = (Catalog) Class.forName(catalogClassName).newInstance(); + } catch (ClassNotFoundException cnfe) { + Debug.message(1,"Catalog class named '" + + catalogClassName + + "' could not be found. Using default."); + catalog = new Catalog(); + } catch (ClassCastException cnfe) { + Debug.message(1,"Class named '" + + catalogClassName + + "' is not a Catalog. Using default."); + catalog = new Catalog(); + } + } + + catalog.setupReaders(); + + if (!privateCatalog) { + catalog.loadSystemCatalogs(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + if (!privateCatalog && catalog != null && CatalogManager.staticCatalog()) { + staticCatalog = catalog; + } + } + + /** Return the underlying catalog */ + public Catalog getCatalog() { + return catalog; + } + + /** + *

Implements the guts of the resolveEntity method + * for the SAX interface.

+ * + *

Presented with an optional public identifier and a system + * identifier, this function attempts to locate a mapping in the + * catalogs.

+ * + *

If such a mapping is found, it is returned. If no mapping is + * found, null is returned.

+ * + * @param publicId The public identifier for the entity in question. + * This may be null. + * + * @param systemId The system identifier for the entity in question. + * XML requires a system identifier on all external entities, so this + * value is always specified. + * + * @return The resolved identifier (a URI reference). + */ + public String getResolvedEntity (String publicId, String systemId) { + String resolved = null; + + if (catalog == null) { + Debug.message(1, "Catalog resolution attempted with null catalog; ignored"); + return null; + } + + if (systemId != null) { + try { + resolved = catalog.resolveSystem(systemId); + } catch (MalformedURLException me) { + Debug.message(1, "Malformed URL exception trying to resolve", + publicId); + resolved = null; + } catch (IOException ie) { + Debug.message(1, "I/O exception trying to resolve", publicId); + resolved = null; + } + } + + if (resolved == null) { + if (publicId != null) { + try { + resolved = catalog.resolvePublic(publicId, systemId); + } catch (MalformedURLException me) { + Debug.message(1, "Malformed URL exception trying to resolve", + publicId); + } catch (IOException ie) { + Debug.message(1, "I/O exception trying to resolve", publicId); + } + } + + if (resolved != null) { + Debug.message(2, "Resolved public", publicId, resolved); + } + } else { + Debug.message(2, "Resolved system", systemId, resolved); + } + + return resolved; + } + + /** + *

Implements the resolveEntity method + * for the SAX interface.

+ * + *

Presented with an optional public identifier and a system + * identifier, this function attempts to locate a mapping in the + * catalogs.

+ * + *

If such a mapping is found, the resolver attempts to open + * the mapped value as an InputSource and return it. Exceptions are + * ignored and null is returned if the mapped value cannot be opened + * as an input source.

+ * + *

If no mapping is found (or an error occurs attempting to open + * the mapped value as an input source), null is returned and the system + * will use the specified system identifier as if no entityResolver + * was specified.

+ * + * @param publicId The public identifier for the entity in question. + * This may be null. + * + * @param systemId The system identifier for the entity in question. + * XML requires a system identifier on all external entities, so this + * value is always specified. + * + * @return An InputSource for the mapped identifier, or null. + */ + public InputSource resolveEntity (String publicId, String systemId) { + String resolved = getResolvedEntity(publicId, systemId); + + 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) { + Debug.message(1, "Failed to create InputSource", resolved); + return null; + } + } + + return null; + } + + /** JAXP URIResolver 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; + + try { + result = catalog.resolveURI(href); + } catch (Exception e) { + // nop; + } + + 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); + } + } + } + + // if (!href.equals(result)) { + Debug.message(2, "Resolved URI", href, result); + // } + + 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/tools/ResolvingParser.java b/java/src/org/apache/xml/resolver/tools/ResolvingParser.java new file mode 100644 index 0000000..64661b0 --- /dev/null +++ b/java/src/org/apache/xml/resolver/tools/ResolvingParser.java @@ -0,0 +1,459 @@ +// ResolvingParser.java - An interface for reading catalog files + +/* + * 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.tools; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.MalformedURLException; +import java.util.Locale; + +import org.xml.sax.*; + +import javax.xml.parsers.*; + +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogManager; +import org.apache.xml.resolver.helpers.Debug; + +/** + *

A SAX Parser that performs catalog-based entity resolution.

+ * + *

This class implements a SAX Parser that performs entity resolution + * using the CatalogResolver. The actual, underlying parser is obtained + * from a SAXParserFactory.

+ *

+ * + * @deprecated This interface has been replaced by the + * {@link org.apache.xml.resolver.tools.ResolvingXMLReader} for SAX2. + * @see CatalogResolver + * @see org.xml.sax.Parser + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class ResolvingParser + implements Parser, DTDHandler, DocumentHandler, EntityResolver { + /** Make the parser Namespace aware? */ + public static boolean namespaceAware = true; + + /** Make the parser validating? */ + public static boolean validating = false; + + /** Suppress explanatory message? + * + * @see #parse(InputSource) + */ + public static boolean suppressExplanation = false; + + /** The underlying parser */ + private SAXParser saxParser = null; + + /** The underlying reader */ + private Parser parser = null; + + /** The underlying DocumentHandler */ + private DocumentHandler documentHandler = null; + + /** The underlying DTDHandler */ + private DTDHandler dtdHandler = null; + + /** The underlying catalog resolver */ + private CatalogResolver catalogResolver = new CatalogResolver(); + + /** A separate resolver for oasis-xml-pi catalogs */ + private CatalogResolver piCatalogResolver = null; + + /** Are we in the prolog? Is an oasis-xml-catalog PI valid now? */ + private boolean allowXMLCatalogPI = false; + + /** Has an oasis-xml-catalog PI been seen? */ + private boolean oasisXMLCatalogPI = false; + + /** The base URI of the input document, if known. */ + private URL baseURL = null; + + /** Constructor */ + public ResolvingParser() { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(namespaceAware); + spf.setValidating(validating); + + try { + saxParser = spf.newSAXParser(); + parser = saxParser.getParser(); + documentHandler = null; + dtdHandler = null; + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** Return the Catalog being used */ + public Catalog getCatalog() { + return catalogResolver.getCatalog(); + } + + /** + *

SAX Parser API

+ * + *

Note that the JAXP 1.1ea2 parser crashes with an InternalError if + * it encounters a system identifier that appears to be a relative URI + * that begins with a slash. For example, the declaration:

+ * + *
+   * <!DOCTYPE book SYSTEM "/path/to/dtd/on/my/system/docbookx.dtd">
+   * 
+ * + *

would cause such an error. As a convenience, this method catches + * that error and prints an explanation. (Unfortunately, it's not possible + * to identify the particular system identifier that causes the problem.) + *

+ * + *

The underlying error is forwarded after printing the explanatory + * message. The message is only every printed once and if + * suppressExplanation is set to false before + * parsing, it will never be printed.

+ */ + public void parse(InputSource input) + throws IOException, + SAXException { + setupParse(input.getSystemId()); + try { + parser.parse(input); + } catch (InternalError ie) { + explain(input.getSystemId()); + throw ie; + } + } + + /** SAX Parser API + * + * @see #parse(InputSource) + */ + public void parse(String systemId) + throws IOException, + SAXException { + setupParse(systemId); + try { + parser.parse(systemId); + } catch (InternalError ie) { + explain(systemId); + throw ie; + } + } + + /** SAX Parser API */ + public void setDocumentHandler(DocumentHandler handler) { + documentHandler = handler; + } + + /** SAX Parser API */ + public void setDTDHandler(DTDHandler handler) { + dtdHandler = handler; + } + + /** + *

SAX Parser API

+ * + *

The purpose of this class is to implement an entity resolver. + * Attempting to set a different one is pointless (and ignored).

+ */ + public void setEntityResolver(EntityResolver resolver) { + // nop + } + + /** SAX Parser API */ + public void setErrorHandler(ErrorHandler handler) { + parser.setErrorHandler(handler); + } + + /** SAX Parser API */ + public void setLocale(Locale locale) throws SAXException { + parser.setLocale(locale); + } + + /** SAX DocumentHandler API */ + public void characters(char[] ch, int start, int length) + throws SAXException { + if (documentHandler != null) { + documentHandler.characters(ch,start,length); + } + } + + /** SAX DocumentHandler API */ + public void endDocument() throws SAXException { + if (documentHandler != null) { + documentHandler.endDocument(); + } + } + + /** SAX DocumentHandler API */ + public void endElement(String name) throws SAXException { + if (documentHandler != null) { + documentHandler.endElement(name); + } + } + + /** SAX DocumentHandler API */ + public void ignorableWhitespace(char[] ch, int start, int length) + throws SAXException { + if (documentHandler != null) { + documentHandler.ignorableWhitespace(ch,start,length); + } + } + + /** SAX DocumentHandler API */ + public void processingInstruction(String target, String pidata) + throws SAXException { + + if (target.equals("oasis-xml-catalog")) { + URL catalog = null; + String data = pidata; + + int pos = data.indexOf("catalog="); + if (pos >= 0) { + data = data.substring(pos+8); + if (data.length() > 1) { + String quote = data.substring(0,1); + data = data.substring(1); + pos = data.indexOf(quote); + if (pos >= 0) { + data = data.substring(0, pos); + try { + if (baseURL != null) { + catalog = new URL(baseURL, data); + } else { + catalog = new URL(data); + } + } catch (MalformedURLException mue) { + // nevermind + } + } + } + } + + if (allowXMLCatalogPI) { + if (CatalogManager.allowOasisXMLCatalogPI()) { + Debug.message(4,"oasis-xml-catalog PI", pidata); + + if (catalog != null) { + try { + Debug.message(4,"oasis-xml-catalog", catalog.toString()); + oasisXMLCatalogPI = true; + + if (piCatalogResolver == null) { + piCatalogResolver = new CatalogResolver(true); + } + + piCatalogResolver.getCatalog().parseCatalog(catalog.toString()); + } catch (Exception e) { + Debug.message(3, "Exception parsing oasis-xml-catalog: " + + catalog.toString()); + } + } else { + Debug.message(3, "PI oasis-xml-catalog unparseable: " + pidata); + } + } else { + Debug.message(4,"PI oasis-xml-catalog ignored: " + pidata); + } + } else { + Debug.message(3, "PI oasis-xml-catalog occurred in an invalid place: " + + pidata); + } + } else { + if (documentHandler != null) { + documentHandler.processingInstruction(target, pidata); + } + } + } + + /** SAX DocumentHandler API */ + public void setDocumentLocator(Locator locator) { + if (documentHandler != null) { + documentHandler.setDocumentLocator(locator); + } + } + + /** SAX DocumentHandler API */ + public void startDocument() throws SAXException { + if (documentHandler != null) { + documentHandler.startDocument(); + } + } + + /** SAX DocumentHandler API */ + public void startElement(String name, AttributeList atts) + throws SAXException { + allowXMLCatalogPI = false; + if (documentHandler != null) { + documentHandler.startElement(name,atts); + } + } + + /** SAX DTDHandler API */ + public void notationDecl (String name, String publicId, String systemId) + throws SAXException { + allowXMLCatalogPI = false; + if (dtdHandler != null) { + dtdHandler.notationDecl(name,publicId,systemId); + } + } + + /** SAX DTDHandler API */ + public void unparsedEntityDecl (String name, + String publicId, + String systemId, + String notationName) + throws SAXException { + allowXMLCatalogPI = false; + if (dtdHandler != null) { + dtdHandler.unparsedEntityDecl (name, publicId, systemId, notationName); + } + } + + /** + *

Implements the resolveEntity method + * for the SAX interface, using an underlying CatalogResolver + * to do the real work.

+ */ + public InputSource resolveEntity (String publicId, String systemId) { + allowXMLCatalogPI = false; + String resolved = catalogResolver.getResolvedEntity(publicId, systemId); + + if (resolved == null && piCatalogResolver != null) { + resolved = piCatalogResolver.getResolvedEntity(publicId, systemId); + } + + 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) { + Debug.message(1, "Failed to create InputSource", resolved); + return null; + } + } else { + return null; + } + } + + /** Setup for parsing */ + private void setupParse(String systemId) { + allowXMLCatalogPI = true; + parser.setEntityResolver(this); + parser.setDocumentHandler(this); + parser.setDTDHandler(this); + + String userdir = System.getProperty("user.dir"); + URL cwd = null; + + userdir.replace('\\', '/'); + try { + cwd = new URL("file:///" + userdir + "/basename"); + } catch (MalformedURLException mue) { + cwd = null; + } + + try { + baseURL = new URL(systemId); + } catch (MalformedURLException mue) { + if (cwd != null) { + try { + baseURL = new URL(cwd, systemId); + } catch (MalformedURLException mue2) { + // give up + baseURL = null; + } + } else { + // give up + baseURL = null; + } + } + } + + /** Provide one possible explanation for an InternalError */ + private void explain(String systemId) { + if (!suppressExplanation) { + System.out.println("Parser probably encountered bad URI in " + systemId); + System.out.println("For example, replace '/some/uri' with 'file:/some/uri'."); + } + } +} + diff --git a/java/src/org/apache/xml/resolver/tools/ResolvingXMLFilter.java b/java/src/org/apache/xml/resolver/tools/ResolvingXMLFilter.java new file mode 100644 index 0000000..e908e2d --- /dev/null +++ b/java/src/org/apache/xml/resolver/tools/ResolvingXMLFilter.java @@ -0,0 +1,370 @@ +// ResolvingXMLFilter.java - An XMLFilter that performs catalog resolution + +/* + * 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.tools; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import org.xml.sax.*; +import org.xml.sax.helpers.XMLFilterImpl; + +import javax.xml.parsers.*; + +import org.apache.xml.resolver.Catalog; +import org.apache.xml.resolver.CatalogManager; +import org.apache.xml.resolver.helpers.Debug; + +/** + *

A SAX XMLFilter that performs catalog-based entity resolution.

+ * + *

This class implements a SAX XMLFilter that performs entity resolution + * using the CatalogResolver. The actual, underlying parser is obtained + * from a SAXParserFactory.

+ *

+ * + * @see CatalogResolver + * @see org.xml.sax.XMLFilter + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class ResolvingXMLFilter extends XMLFilterImpl { + /** Suppress explanatory message? + * + * @see #parse(InputSource) + */ + public static boolean suppressExplanation = false; + + /** The underlying catalog resolver */ + private CatalogResolver catalogResolver = new CatalogResolver(); + + /** A separate resolver for oasis-xml-pi catalogs */ + private CatalogResolver piCatalogResolver = null; + + /** Are we in the prolog? Is an oasis-xml-catalog PI valid now? */ + private boolean allowXMLCatalogPI = false; + + /** Has an oasis-xml-catalog PI been seen? */ + private boolean oasisXMLCatalogPI = false; + + /** The base URI of the input document, if known. */ + private URL baseURL = null; + + /** Construct an empty XML Filter with no parent */ + public ResolvingXMLFilter() { + super(); + } + + /** Construct an XML filter with the specified parent */ + public ResolvingXMLFilter(XMLReader parent) { + super(parent); + } + + /** + *

Provide accessto the underlying Catalog

+ */ + public Catalog getCatalog() { + return catalogResolver.getCatalog(); + } + + /** + *

SAX XMLReader API

+ * + *

Note that the JAXP 1.1ea2 parser crashes with an InternalError if + * it encounters a system identifier that appears to be a relative URI + * that begins with a slash. For example, the declaration:

+ * + *
+   * <!DOCTYPE book SYSTEM "/path/to/dtd/on/my/system/docbookx.dtd">
+   * 
+ * + *

would cause such an error. As a convenience, this method catches + * that error and prints an explanation. (Unfortunately, it's not possible + * to identify the particular system identifier that causes the problem.) + *

+ * + *

The underlying error is forwarded after printing the explanatory + * message. The message is only every printed once and if + * suppressExplanation is set to false before + * parsing, it will never be printed.

+ */ + public void parse(InputSource input) + throws IOException, SAXException { + allowXMLCatalogPI = true; + + setupBaseURI(input.getSystemId()); + + try { + super.parse(input); + } catch (InternalError ie) { + explain(input.getSystemId()); + throw ie; + } + } + + /** SAX XMLReader API + * + * @see #parse(InputSource) + */ + public void parse(String systemId) + throws IOException, SAXException { + allowXMLCatalogPI = true; + + setupBaseURI(systemId); + + try { + super.parse(systemId); + } catch (InternalError ie) { + explain(systemId); + throw ie; + } + } + + /** + *

Implements the resolveEntity method + * for the SAX interface, using an underlying CatalogResolver + * to do the real work.

+ */ + public InputSource resolveEntity (String publicId, String systemId) { + allowXMLCatalogPI = false; + String resolved = catalogResolver.getResolvedEntity(publicId, systemId); + + if (resolved == null && piCatalogResolver != null) { + resolved = piCatalogResolver.getResolvedEntity(publicId, systemId); + } + + 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) { + Debug.message(1, "Failed to create InputSource", resolved); + return null; + } + } else { + return null; + } + } + + /** SAX DTDHandler API + * + *

Captured here only to detect the end of the prolog so that + * we can ignore subsequent oasis-xml-catalog PIs. Otherwise + * the events are just passed through.

+ */ + public void notationDecl (String name, String publicId, String systemId) + throws SAXException { + allowXMLCatalogPI = false; + super.notationDecl(name,publicId,systemId); + } + + /** SAX DTDHandler API + * + *

Captured here only to detect the end of the prolog so that + * we can ignore subsequent oasis-xml-catalog PIs. Otherwise + * the events are just passed through.

+ */ + public void unparsedEntityDecl (String name, + String publicId, + String systemId, + String notationName) + throws SAXException { + allowXMLCatalogPI = false; + super.unparsedEntityDecl (name, publicId, systemId, notationName); + } + + /** SAX ContentHandler API + * + *

Captured here only to detect the end of the prolog so that + * we can ignore subsequent oasis-xml-catalog PIs. Otherwise + * the events are just passed through.

+ */ + public void startElement (String uri, String localName, String qName, + Attributes atts) + throws SAXException { + allowXMLCatalogPI = false; + super.startElement(uri,localName,qName,atts); + } + + /** SAX ContentHandler API + * + *

Detect and use the oasis-xml-catalog PI if it occurs.

+ */ + public void processingInstruction(String target, String pidata) + throws SAXException { + if (target.equals("oasis-xml-catalog")) { + URL catalog = null; + String data = pidata; + + int pos = data.indexOf("catalog="); + if (pos >= 0) { + data = data.substring(pos+8); + if (data.length() > 1) { + String quote = data.substring(0,1); + data = data.substring(1); + pos = data.indexOf(quote); + if (pos >= 0) { + data = data.substring(0, pos); + try { + if (baseURL != null) { + catalog = new URL(baseURL, data); + } else { + catalog = new URL(data); + } + } catch (MalformedURLException mue) { + // nevermind + } + } + } + } + + if (allowXMLCatalogPI) { + if (CatalogManager.allowOasisXMLCatalogPI()) { + Debug.message(4,"oasis-xml-catalog PI", pidata); + + if (catalog != null) { + try { + Debug.message(4,"oasis-xml-catalog", catalog.toString()); + oasisXMLCatalogPI = true; + + if (piCatalogResolver == null) { + piCatalogResolver = new CatalogResolver(true); + } + + piCatalogResolver.getCatalog().parseCatalog(catalog.toString()); + } catch (Exception e) { + Debug.message(3, "Exception parsing oasis-xml-catalog: " + + catalog.toString()); + } + } else { + Debug.message(3, "PI oasis-xml-catalog unparseable: " + pidata); + } + } else { + Debug.message(4,"PI oasis-xml-catalog ignored: " + pidata); + } + } else { + Debug.message(3, "PI oasis-xml-catalog occurred in an invalid place: " + + pidata); + } + } else { + super.processingInstruction(target, pidata); + } + } + + /** Save the base URI of the document being parsed. */ + private void setupBaseURI(String systemId) { + String userdir = System.getProperty("user.dir"); + URL cwd = null; + + userdir.replace('\\', '/'); + try { + cwd = new URL("file:///" + userdir + "/basename"); + } catch (MalformedURLException mue) { + cwd = null; + } + + try { + baseURL = new URL(systemId); + } catch (MalformedURLException mue) { + if (cwd != null) { + try { + baseURL = new URL(cwd, systemId); + } catch (MalformedURLException mue2) { + // give up + baseURL = null; + } + } else { + // give up + baseURL = null; + } + } + } + + /** Provide one possible explanation for an InternalError */ + private void explain(String systemId) { + if (!suppressExplanation) { + System.out.println("XMLReader probably encountered bad URI in " + systemId); + System.out.println("For example, replace '/some/uri' with 'file:/some/uri'."); + } + suppressExplanation = true; + } +} + diff --git a/java/src/org/apache/xml/resolver/tools/ResolvingXMLReader.java b/java/src/org/apache/xml/resolver/tools/ResolvingXMLReader.java new file mode 100644 index 0000000..b613bd3 --- /dev/null +++ b/java/src/org/apache/xml/resolver/tools/ResolvingXMLReader.java @@ -0,0 +1,107 @@ +// ResolvingXMLReader.java - An XMLReader that performs catalog resolution + +/* + * 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.tools; + +import java.io.IOException; +import java.net.URL; + +import org.xml.sax.*; +import org.xml.sax.helpers.XMLFilterImpl; + +import javax.xml.parsers.*; + +import org.apache.xml.resolver.*; + +/** + *

A SAX XMLReader that performs catalog-based entity resolution.

+ * + *

This class implements a SAX XMLReader that performs entity resolution + * using the CatalogResolver. The actual, underlying parser is obtained + * from a SAXParserFactory.

+ *

+ * + * @see CatalogResolver + * @see org.xml.sax.XMLReader + * + * @author Norman Walsh + * Norman.Walsh@Sun.COM + * + * @version 1.0 + */ +public class ResolvingXMLReader extends ResolvingXMLFilter { + /** + *

Construct a new reader from the JAXP factory

+ * + *

In order to do its job, a ResolvingXMLReader must in fact be + * a filter. So the only difference between this code and the filter + * code is that a zero-argument constructor builds a new reader.

+ */ + public ResolvingXMLReader() { + super(); + SAXParserFactory spf = SAXParserFactory.newInstance(); + try { + SAXParser parser = spf.newSAXParser(); + setParent(parser.getXMLReader()); + } catch (Exception ex) { + ex.printStackTrace(); + } + } +} + diff --git a/java/src/org/apache/xml/resolver/tools/package.html b/java/src/org/apache/xml/resolver/tools/package.html new file mode 100644 index 0000000..82eb95e --- /dev/null +++ b/java/src/org/apache/xml/resolver/tools/package.html @@ -0,0 +1,47 @@ + + +Apache XML Commons Resolver Tools package + + +

Apache XML Commons Resolver Tools package

+ +

This package implements several tool classes for catalog-based +entity resolution:

+ +
+
org.apache.xml.resolver.tools.CatalogResolver
+
This class provides a SAX EntityResolver and a JAXP URIResolver. +
+ +
org.apache.xml.resolver.tools.ResolvingXMLReader
+
This class provides a SAX2 XMLReader with catalog support. +The parser uses a CatalogResolver to provide entity resolution. +
+ +
org.apache.xml.resolver.tools.ResolvingParser
+
This class provides a SAX1 Parser with catalog support. +The parser uses a CatalogResolver to provide entity resolution. +
+
+ +

Typically, these classes can be used in other applications. For example, +running the Saxon +XSLT processor with the arguments:

+ +
+    -x org.apache.xml.resolver.tools.ResolvingXMLReader
+    -y org.apache.xml.resolver.tools.ResolvingXMLReader
+    -r org.apache.xml.resolver.tools.CatalogResolver
+
+ +

will provide catalog-based entity resolution for all of the entities +and stylesheets that you use. (The CatalogResolver can only be used +with Saxon 6.1 or later.)

+ +

Requirements

+ +

In order to use these classes, you must have the JAXP 1.1 jar files +in your CLASSPATH.

+ + +