/* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.org.apache.xml.internal.resolver; import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; import com.sun.org.apache.xerces.internal.utils.SecuritySupport; import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; import com.sun.org.apache.xml.internal.resolver.helpers.PublicId; import com.sun.org.apache.xml.internal.resolver.readers.CatalogReader; import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader; import com.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader; import com.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader; import java.io.DataInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Vector; 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 * @deprecated The JDK internal Catalog API in package * {@code com.sun.org.apache.xml.internal.resolver} * is encapsulated in JDK 9. The entire implementation under the package is now * deprecated and subject to removal in a future release. Users of the API * should migrate to the {@linkplain javax.xml.catalog new public API}. *

* The new Catalog API is supported throughout the JDK XML Processors, which allows * the use of Catalog by simply setting a path to a Catalog file as a property. * * @author Norman Walsh * Norman.Walsh@Sun.COM * * @version 1.0 * *

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

*/ @Deprecated(since="9", forRemoval=true) 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 SYSTEM_SUFFIX Catalog Entry type. */ public static final int SYSTEM_SUFFIX = CatalogEntry.addEntryType("SYSTEM_SUFFIX", 2); /** The URI_SUFFIX Catalog Entry type. */ public static final int URI_SUFFIX = CatalogEntry.addEntryType("URI_SUFFIX", 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; /** The catalog manager in use for this instance. */ protected CatalogManager catalogManager = CatalogManager.getStaticManager(); /** * 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 Map readerMap = new HashMap<>(); /** * 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(); /** * Constructs an empty Catalog. * *

The constructor interrogates the relevant system properties * using the default (static) CatalogManager * and initializes the catalog data structures.

*/ public Catalog() { // nop; } /** * Constructs an empty Catalog with a specific CatalogManager. * *

The constructor interrogates the relevant system properties * using the specified Catalog Manager * and initializes the catalog data structures.

*/ public Catalog(CatalogManager manager) { catalogManager = manager; } /** * Return the CatalogManager used by this catalog. * */ public CatalogManager getCatalogManager() { return catalogManager; } /** * Establish the CatalogManager used by this catalog. * */ public void setCatalogManager(CatalogManager manager) { catalogManager = manager; } /** * Setup readers. */ public void setupReaders() { SAXParserFactory spf = catalogManager.useServicesMechanism() ? SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); spf.setNamespaceAware(true); spf.setValidating(false); SAXCatalogReader saxReader = new SAXCatalogReader(spf); saxReader.setCatalogParser(null, "XMLCatalog", "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader"); saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, "catalog", "com.sun.org.apache.xml.internal.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.containsKey(mimeType)) { Integer pos = readerMap.get(mimeType); readerArr.set(pos, reader); } else { readerArr.add(reader); Integer pos = 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); } for (Map.Entry entry : readerMap.entrySet()) { mapArr.set(entry.getValue().intValue(), entry.getKey()); } for (int count = 0; count < mapArr.size(); count++) { String mimeType = (String) mapArr.get(count); Integer pos = 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 com.sun.org.apache.xml.internal.resolver.Catalog). * All new catalogs are managed by the same CatalogManager. *

* *

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()); c.setCatalogManager(catalogManager); copyReaders(c); return c; } catch (ClassNotFoundException cnfe) { catalogManager.debug.message(1, "Class Not Found Exception: " + catalogClass); } catch (IllegalAccessException iae) { catalogManager.debug.message(1, "Illegal Access Exception: " + catalogClass); } catch (InstantiationException ie) { catalogManager.debug.message(1, "Instantiation Exception: " + catalogClass); } catch (ClassCastException cce) { catalogManager.debug.message(1, "Class Cast Exception: " + catalogClass); } catch (Exception e) { catalogManager.debug.message(1, "Other Exception: " + catalogClass); } Catalog c = new Catalog(); c.setCatalogManager(catalogManager); copyReaders(c); return c; } /** * 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.getCatalogFiles(); 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 { default_override = catalogManager.getPreferPublic(); catalogManager.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 { default_override = catalogManager.getPreferPublic(); catalogManager.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; catalogManager.debug.message(2, msg); throw new CatalogException(CatalogException.UNPARSEABLE, msg); } reader.readCatalog(this, is); // Now process all the pending catalogs... parsePendingCatalogs(); } /** * Parse a catalog document, augmenting internal data structures. * *

This method supports catalog files stored in jar files: e.g., * jar:file:///path/to/filename.jar!/path/to/catalog.xml". That URI * doesn't survive transmogrification through the URI processing that * the parseCatalog(String) performs and passing it as an input stream * doesn't set the base URI appropriately.

* *

Written by Stefan Wachter (2002-09-26)

* * @param aUrl The URL of the catalog document to process * * @throws IOException Error reading catalog file. */ public synchronized void parseCatalog(URL aUrl) throws IOException { catalogCwd = aUrl; base = aUrl; default_override = catalogManager.getPreferPublic(); catalogManager.debug.message(4, "Parse catalog: " + aUrl.toString()); DataInputStream inStream = null; boolean parsed = false; for (int count = 0; !parsed && count < readerArr.size(); count++) { CatalogReader reader = (CatalogReader) readerArr.get(count); try { inStream = new DataInputStream(aUrl.openStream()); } catch (FileNotFoundException fnfe) { // No catalog; give up! 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) 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 catalogCwd = FileURL.makeURL("basename"); } catch (MalformedURLException e) { catalogManager.debug.message(1, "Malformed URL on cwd", "user.dir"); 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) { catalogManager.debug.message(1, "Malformed URL on catalog filename", fixSlashes(fileName)); base = null; } } catalogManager.debug.message(2, "Loading catalog", fileName); catalogManager.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) { catalogManager.debug.message(3, "Catalog does not exist", fileName); } else { catalogManager.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; if (base == null) { catalogManager.debug.message(5, "BASE CUR", "null"); } else { catalogManager.debug.message(5, "BASE CUR", base.toString()); } catalogManager.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) { catalogManager.debug.message(1, "Malformed URL on base", value); newbase = null; } } if (newbase != null) { base = newbase; } catalogManager.debug.message(5, "BASE NEW", base.toString()); } else if (type == CATALOG) { String fsi = makeAbsolute(entry.getEntryArg(0)); catalogManager.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); catalogManager.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); catalogManager.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); catalogManager.debug.message(4, "URI", uri, altURI); catalogEntries.addElement(entry); } else if (type == DOCUMENT) { String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(0))); entry.setEntryArg(0, fsi); catalogManager.debug.message(4, "DOCUMENT", fsi); catalogEntries.addElement(entry); } else if (type == OVERRIDE) { catalogManager.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); catalogManager.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); catalogManager.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); catalogManager.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); catalogManager.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); catalogManager.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); catalogManager.debug.message(4, "REWRITE_URI", pui, upx); catalogEntries.addElement(entry); } else if (type == SYSTEM_SUFFIX) { String pui = normalizeURI(entry.getEntryArg(0)); String upx = makeAbsolute(normalizeURI(entry.getEntryArg(1))); entry.setEntryArg(0, pui); entry.setEntryArg(1, upx); catalogManager.debug.message(4, "SYSTEM_SUFFIX", pui, upx); catalogEntries.addElement(entry); } else if (type == URI_SUFFIX) { String pui = normalizeURI(entry.getEntryArg(0)); String upx = makeAbsolute(normalizeURI(entry.getEntryArg(1))); entry.setEntryArg(0, pui); entry.setEntryArg(1, upx); catalogManager.debug.message(4, "URI_SUFFIX", pui, upx); catalogEntries.addElement(entry); } else if (type == DOCTYPE) { String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); entry.setEntryArg(1, fsi); catalogManager.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); catalogManager.debug.message(4, "DTDDECL", fpi, fsi); catalogEntries.addElement(entry); } else if (type == ENTITY) { String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1))); entry.setEntryArg(1, fsi); catalogManager.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); catalogManager.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); catalogManager.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); catalogManager.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 en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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; catalogManager.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)) { catalogManager.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 en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 catalogManager.debug.message(3, "resolveDocument"); Enumeration en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == DOCUMENT) { return e.getEntryArg(0); } } 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; catalogManager.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)) { catalogManager.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 en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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; catalogManager.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)) { catalogManager.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 en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 { catalogManager.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)) { catalogManager.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 en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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; en = catalogEntries.elements(); Vector delCats = new Vector(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 enCats = delCats.elements(); if (catalogManager.debug.getDebug() > 1) { catalogManager.debug.message(2, "Switching to delegated catalog(s):"); while (enCats.hasMoreElements()) { String delegatedCatalog = (String) enCats.nextElement(); catalogManager.debug.message(2, "\t" + delegatedCatalog); } } Catalog dcat = newCatalog(); enCats = delCats.elements(); while (enCats.hasMoreElements()) { String delegatedCatalog = (String) enCats.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 { catalogManager.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 = SecuritySupport.getSystemProperty("os.name"); boolean windows = (osname.indexOf("Windows") >= 0); Enumeration en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 en = catalogEntries.elements(); String startString = null; String prefix = null; while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 SYSTEM_SUFFIX entry in this catalog, use it en = catalogEntries.elements(); String suffixString = null; String suffixURI = null; while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == SYSTEM_SUFFIX) { String p = (String) e.getEntryArg(0); if (p.length() <= systemId.length() && systemId.endsWith(p)) { // Is this the longest prefix? if (suffixString == null || p.length() > suffixString.length()) { suffixString = p; suffixURI = e.getEntryArg(1); } } } } if (suffixURI != null) { // return the systemId for the suffix return suffixURI; } // If there's a DELEGATE_SYSTEM entry in this catalog, use it en = catalogEntries.elements(); Vector delCats = new Vector(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 enCats = delCats.elements(); if (catalogManager.debug.getDebug() > 1) { catalogManager.debug.message(2, "Switching to delegated catalog(s):"); while (enCats.hasMoreElements()) { String delegatedCatalog = (String) enCats.nextElement(); catalogManager.debug.message(2, "\t" + delegatedCatalog); } } Catalog dcat = newCatalog(); enCats = delCats.elements(); while (enCats.hasMoreElements()) { String delegatedCatalog = (String) enCats.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 { catalogManager.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 en = catalogEntries.elements(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 en = catalogEntries.elements(); String startString = null; String prefix = null; while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 uri with the new prefix return prefix + uri.substring(startString.length()); } // If there's a URI_SUFFIX entry in this catalog, use it en = catalogEntries.elements(); String suffixString = null; String suffixURI = null; while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.nextElement(); if (e.getEntryType() == URI_SUFFIX) { String p = (String) e.getEntryArg(0); if (p.length() <= uri.length() && uri.endsWith(p)) { // Is this the longest prefix? if (suffixString == null || p.length() > suffixString.length()) { suffixString = p; suffixURI = e.getEntryArg(1); } } } } if (suffixURI != null) { // return the uri for the suffix return suffixURI; } // If there's a DELEGATE_URI entry in this catalog, use it en = catalogEntries.elements(); Vector delCats = new Vector(); while (en.hasMoreElements()) { CatalogEntry e = (CatalogEntry) en.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 enCats = delCats.elements(); if (catalogManager.debug.getDebug() > 1) { catalogManager.debug.message(2, "Switching to delegated catalog(s):"); while (enCats.hasMoreElements()) { String delegatedCatalog = (String) enCats.nextElement(); catalogManager.debug.message(2, "\t" + delegatedCatalog); } } Catalog dcat = newCatalog(); enCats = delCats.elements(); while (enCats.hasMoreElements()) { String delegatedCatalog = (String) enCats.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) { catalogManager.debug.message(1, "Malformed Catalog URL", catfile); } catch (FileNotFoundException fnfe) { catalogManager.debug.message(1, "Failed to load catalog, file not found", catfile); } catch (IOException ioe) { catalogManager.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) { catalogManager.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) { if (uriref == null) { return null; } final int length = uriref.length(); for (int i = 0; i < length; ++i) { char c = uriref.charAt(i); if ((c <= 0x20) // ctrl || (c > 0x7F) // high ascii || (c == 0x22) // " || (c == 0x3C) // < || (c == 0x3E) // > || (c == 0x5C) // \ || (c == 0x5E) // ^ || (c == 0x60) // ` || (c == 0x7B) // { || (c == 0x7C) // | || (c == 0x7D) // } || (c == 0x7F)) { return normalizeURI(uriref, i); } } return uriref; } /** * Perform character normalization on a URI reference. * * @param uriref The URI reference * @param index The index of the first character which requires escaping. * @return The normalized URI reference. */ private String normalizeURI(String uriref, int index) { final StringBuilder buffer = new StringBuilder(); for (int i = 0; i < index; ++i) { buffer.append(uriref.charAt(i)); } final byte[] bytes; try { bytes = uriref.substring(index).getBytes("UTF-8"); } catch (UnsupportedEncodingException uee) { // this can't happen catalogManager.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)) { writeEncodedByte(ch, buffer); } else { buffer.append((char) bytes[count]); } } return buffer.toString(); } /** * Perform %-encoding on a single byte. * * @param b The 8-bit integer that represents the 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) { StringBuilder buffer = new StringBuilder(3); writeEncodedByte(b, buffer); return buffer.toString(); } /** * Perform %-encoding on a single byte. * * @param b The 8-bit integer that represents the byte. (Bytes are signed * but encoding needs to look at the bytes unsigned.) * @param buffer The target for the %-encoded string for the byte in question. */ private void writeEncodedByte(int b, StringBuilder buffer) { String hex = Integer.toHexString(b).toUpperCase(Locale.ENGLISH); if (hex.length() < 2) { buffer.append("%0"); buffer.append(hex); } else { buffer.append('%'); buffer.append(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); } } }