summaryrefslogtreecommitdiff
path: root/libjava/gnu/xml/util/XCat.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/gnu/xml/util/XCat.java')
-rw-r--r--libjava/gnu/xml/util/XCat.java1609
1 files changed, 0 insertions, 1609 deletions
diff --git a/libjava/gnu/xml/util/XCat.java b/libjava/gnu/xml/util/XCat.java
deleted file mode 100644
index 0f163387081..00000000000
--- a/libjava/gnu/xml/util/XCat.java
+++ /dev/null
@@ -1,1609 +0,0 @@
-/* XCat.java --
- Copyright (C) 2001 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-
-package gnu.xml.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.StringTokenizer;
-import java.util.Stack;
-import java.util.Vector;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXNotRecognizedException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.XMLReader;
-
-import org.xml.sax.ext.DefaultHandler2;
-import org.xml.sax.ext.EntityResolver2;
-
-import org.xml.sax.helpers.XMLReaderFactory;
-
-/**
- * Packages <a href=
- "http://www.oasis-open.org/committees/entity/spec-2001-08-06.html"
- >OASIS XML Catalogs</a>,
- * primarily for entity resolution by parsers.
- * That specification defines an XML syntax for mappings between
- * identifiers declared in DTDs (particularly PUBLIC identifiers) and
- * locations. SAX has always supported such mappings, but conventions for
- * an XML file syntax to maintain them have previously been lacking.
- *
- * <p> This has three main operational modes. The primary intended mode is
- * to create a resolver, then preloading it with one or more site-standard
- * catalogs before using it with one or more SAX parsers: <pre>
- * XCat catalog = new XCat ();
- * catalog.setErrorHandler (diagnosticErrorHandler);
- * catalog.loadCatalog ("file:/local/catalogs/catalog.cat");
- * catalog.loadCatalog ("http://shared/catalog.cat");
- * ...
- * catalog.disableLoading ();
- * parser1.setEntityResolver (catalog);
- * parser2.setEntityResolver (catalog);
- * ...</pre>
- *
- * <p>A second mode is to arrange that your application uses instances of
- * this class as its entity resolver, and automatically loads catalogs
- * referenced by <em>&lt;?oasis-xml-catalog...?&gt;</em> processing
- * instructions found before the DTD in documents it parses.
- * It would then discard the resolver after each parse.
- *
- * <p> A third mode applies catalogs in contexts other than entity
- * resolution for parsers.
- * The {@link #resolveURI resolveURI()} method supports resolving URIs
- * stored in XML application data, rather than inside DTDs.
- * Catalogs would be loaded as shown above, and the catalog could
- * be used concurrently for parser entity resolution and for
- * application URI resolution.
- * </p>
- *
- * <center><hr width='70%'></center>
- *
- * <p>Errors in catalogs implicitly loaded (during resolution) are ignored
- * beyond being reported through any <em>ErrorHandler</em> assigned using
- * {@link #setErrorHandler setErrorHandler()}. SAX exceptions
- * thrown from such a handler won't abort resolution, although throwing a
- * <em>RuntimeException</em> or <em>Error</em> will normally abort both
- * resolution and parsing. Useful diagnostic information is available to
- * any <em>ErrorHandler</em> used to report problems, or from any exception
- * thrown from an explicit {@link #loadCatalog loadCatalog()} invocation.
- * Applications can use that information as troubleshooting aids.
- *
- * <p>While this class requires <em>SAX2 Extensions 1.1</em> classes in
- * its class path, basic functionality does not require using a SAX2
- * parser that supports the extended entity resolution functionality.
- * See the original SAX1
- * {@link #resolveEntity(java.lang.String,java.lang.String) resolveEntity()}
- * method for a list of restrictions which apply when it is used with
- * older SAX parsers.
- *
- * @see EntityResolver2
- *
- * @author David Brownell
- */
-public class XCat implements EntityResolver2
-{
- private Catalog catalogs [];
- private boolean usingPublic = true;
- private boolean loadingPermitted = true;
- private boolean unified = true;
- private String parserClass;
- private ErrorHandler errorHandler;
-
- // private EntityResolver next; // chain to next if we fail...
-
- //
- // NOTE: This is a straightforward implementation, and if
- // there are lots of "nextCatalog" or "delegate*" entries
- // in use, two tweaks would be worth considering:
- //
- // - Centralize some sort of cache (key by URI) for individual
- // resolvers. That'd avoid multiple copies of a given catalog.
- //
- // - Have resolution track what catalogs (+modes) have been
- // searched. This would support loop detection.
- //
-
-
- /**
- * Initializes without preloading a catalog.
- * This API is convenient when you may want to arrange that catalogs
- * are automatically loaded when explicitly referenced in documents,
- * using the <em>oasis-xml-catalog</em> processing instruction.
- * In such cases you won't usually be able to preload catalogs.
- */
- public XCat () { }
-
- /**
- * Initializes, and preloads a catalog using the default SAX parser.
- * This API is convenient when you operate with one or more standard
- * catalogs.
- *
- * <p> This just delegates to {@link #loadCatalog loadCatalog()};
- * see it for exception information.
- *
- * @param uri absolute URI for the catalog file.
- */
- public XCat (String uri)
- throws SAXException, IOException
- { loadCatalog (uri); }
-
-
- /**
- * Loads an OASIS XML Catalog.
- * It is appended to the list of currently active catalogs, or
- * reloaded if a catalog with the same URI was already loaded.
- * Callers have control over what parser is used, how catalog parsing
- * errors are reported, and whether URIs will be resolved consistently.
- *
- * <p> The OASIS specification says that errors detected when loading
- * catalogs "must recover by ignoring the catalog entry file that
- * failed, and proceeding." In this API, that action can be the
- * responsibility of applications, when they explicitly load any
- * catalog using this method.
- *
- * <p>Note that catalogs referenced by this one will not be loaded
- * at this time. Catalogs referenced through <em>nextCatalog</em>
- * or <em>delegate*</em> elements are normally loaded only if needed.
- *
- * @see #setErrorHandler
- * @see #setParserClass
- * @see #setUnified
- *
- * @param uri absolute URI for the catalog file.
- *
- * @exception IOException As thrown by the parser, typically to
- * indicate problems reading data from that URI.
- * @exception SAXException As thrown by the parser, typically to
- * indicate problems parsing data from that URI. It may also
- * be thrown if the parser doesn't support necessary handlers.
- * @exception IllegalStateException When attempting to load a
- * catalog after loading has been {@link #disableLoading disabled},
- * such as after any entity or URI lookup has been performed.
- */
- public synchronized void loadCatalog (String uri)
- throws SAXException, IOException
- {
- Catalog catalog;
- int index = -1;
-
- if (!loadingPermitted)
- throw new IllegalStateException ();
-
- uri = normalizeURI (uri);
- if (catalogs != null) {
- // maybe just reload
- for (index = 0; index < catalogs.length; index++)
- if (uri.equals (catalogs [index].catalogURI))
- break;
- }
- catalog = loadCatalog (parserClass, errorHandler, uri, unified);
-
- // add to list of catalogs
- if (catalogs == null) {
- index = 0;
- catalogs = new Catalog [1];
- } else if (index == catalogs.length) {
- Catalog tmp [];
-
- tmp = new Catalog [index + 1];
- System.arraycopy (catalogs, 0, tmp, 0, index);
- catalogs = tmp;
- }
- catalogs [index] = catalog;
- }
-
-
- /**
- * "New Style" external entity resolution for parsers.
- * Calls to this method prevent explicit loading of additional catalogs
- * using {@link #loadCatalog loadCatalog()}.
- *
- * <p>This supports the full core catalog functionality for locating
- * (and relocating) parsed entities that have been declared in a
- * document's DTD.
- *
- * @param name Entity name, such as "dudley", "%nell", or "[dtd]".
- * @param publicId Either a normalized public ID, or null.
- * @param baseURI Absolute base URI associated with systemId.
- * @param systemId URI found in entity declaration (may be
- * relative to baseURI).
- *
- * @return Input source for accessing the external entity, or null
- * if no mapping was found. The input source may have opened
- * the stream, and will have a fully resolved URI.
- *
- * @see #getExternalSubset
- */
- public InputSource resolveEntity (
- String name, // UNUSED ... systemId is always non-null
- String publicId,
- String baseURI, // UNUSED ... it just lets sysId be relative
- String systemId
- ) throws SAXException, IOException
- {
- if (loadingPermitted)
- disableLoading ();
-
- try {
- // steps as found in OASIS XML catalog spec 7.1.2
- // steps 1, 8 involve looping over the list of catalogs
- for (int i = 0; i < catalogs.length; i++) {
- InputSource retval;
- retval = catalogs [i].resolve (usingPublic, publicId, systemId);
- if (retval != null)
- return retval;;
- }
- } catch (DoneDelegation x) {
- // done!
- }
- // step 9 involves returning "no match"
- return null;
- }
-
-
- /**
- * "New Style" parser callback to add an external subset.
- * For documents that don't include an external subset, this may
- * return one according to <em>doctype</em> catalog entries.
- * (This functionality is not a core part of the OASIS XML Catalog
- * specification, though it's presented in an appendix.)
- * If no such entry is defined, this returns null to indicate that
- * this document will not be modified to include such a subset.
- * Calls to this method prevent explicit loading of additional catalogs
- * using {@link #loadCatalog loadCatalog()}.
- *
- * <p><em>Warning:</em> That catalog functionality can be dangerous.
- * It can provide definitions of general entities, and thereby mask
- * certain well formedess errors.
- *
- * @param name Name of the document element, either as declared in
- * a DOCTYPE declaration or as observed in the text.
- * @param baseURI Document's base URI (absolute).
- *
- * @return Input source for accessing the external subset, or null
- * if no mapping was found. The input source may have opened
- * the stream, and will have a fully resolved URI.
- */
- public InputSource getExternalSubset (String name, String baseURI)
- throws SAXException, IOException
- {
- if (loadingPermitted)
- disableLoading ();
- try {
- for (int i = 0; i < catalogs.length; i++) {
- InputSource retval = catalogs [i].getExternalSubset (name);
- if (retval != null)
- return retval;
- }
- } catch (DoneDelegation x) {
- // done!
- }
- return null;
- }
-
-
- /**
- * "Old Style" external entity resolution for parsers.
- * This API provides only core functionality.
- * Calls to this method prevent explicit loading of additional catalogs
- * using {@link #loadCatalog loadCatalog()}.
- *
- * <p>The functional limitations of this interface include:</p><ul>
- *
- * <li>Since system IDs will be absolutized before the resolver
- * sees them, matching against relative URIs won't work.
- * This may affect <em>system</em>, <em>rewriteSystem</em>,
- * and <em>delegateSystem</em> catalog entries.
- *
- * <li>Because of that absolutization, documents declaring entities
- * with system IDs using URI schemes that the JVM does not recognize
- * may be unparsable. URI schemes such as <em>file:/</em>,
- * <em>http://</em>, <em>https://</em>, and <em>ftp://</em>
- * will usually work reliably.
- *
- * <li>Because missing external subsets can't be provided, the
- * <em>doctype</em> catalog entries will be ignored.
- * (The {@link #getExternalSubset getExternalSubset()} method is
- * a "New Style" resolution option.)
- *
- * </ul>
- *
- * <p>Applications can tell whether this limited functionality will be
- * used: if the feature flag associated with the {@link EntityResolver2}
- * interface is not <em>true</em>, the limitations apply. Applications
- * can't usually know whether a given document and catalog will trigger
- * those limitations. The issue can only be bypassed by operational
- * procedures such as not using catalogs or documents which involve
- * those features.
- *
- * @param publicId Either a normalized public ID, or null
- * @param systemId Always an absolute URI.
- *
- * @return Input source for accessing the external entity, or null
- * if no mapping was found. The input source may have opened
- * the stream, and will have a fully resolved URI.
- */
- final public InputSource resolveEntity (String publicId, String systemId)
- throws SAXException, IOException
- {
- return resolveEntity (null, publicId, null, systemId);
- }
-
-
- /**
- * Resolves a URI reference that's not defined to the DTD.
- * This is intended for use with URIs found in document text, such as
- * <em>xml-stylesheet</em> processing instructions and in attribute
- * values, where they are not recognized as URIs by XML parsers.
- * Calls to this method prevent explicit loading of additional catalogs
- * using {@link #loadCatalog loadCatalog()}.
- *
- * <p>This functionality is supported by the OASIS XML Catalog
- * specification, but will never be invoked by an XML parser.
- * It corresponds closely to functionality for mapping system
- * identifiers for entities declared in DTDs; closely enough that
- * this implementation's default behavior is that they be
- * identical, to minimize potential confusion.
- *
- * <p>This method could be useful when implementing the
- * {@link javax.xml.transform.URIResolver} interface, wrapping the
- * input source in a {@link javax.xml.transform.sax.SAXSource}.
- *
- * @see #isUnified
- * @see #setUnified
- *
- * @param baseURI The relevant base URI as specified by the XML Base
- * specification. This recognizes <em>xml:base</em> attributes
- * as overriding the actual (physical) base URI.
- * @param uri Either an absolute URI, or one relative to baseURI
- *
- * @return Input source for accessing the mapped URI, or null
- * if no mapping was found. The input source may have opened
- * the stream, and will have a fully resolved URI.
- */
- public InputSource resolveURI (String baseURI, String uri)
- throws SAXException, IOException
- {
- if (loadingPermitted)
- disableLoading ();
-
- // NOTE: baseURI isn't used here, but caller MUST have it,
- // and heuristics _might_ use it in the future ... plus,
- // it's symmetric with resolveEntity ().
-
- // steps 1, 6 involve looping
- try {
- for (int i = 0; i < catalogs.length; i++) {
- InputSource tmp = catalogs [i].resolveURI (uri);
- if (tmp != null)
- return tmp;
- }
- } catch (DoneDelegation x) {
- // done
- }
- // step 7 reports no match
- return null;
- }
-
-
- /**
- * Records that catalog loading is no longer permitted.
- * Loading is automatically disabled when lookups are performed,
- * and should be manually disabled when <em>startDTD()</em> (or
- * any other DTD declaration callback) is invoked, or at the latest
- * when the document root element is seen.
- */
- public synchronized void disableLoading ()
- {
- // NOTE: this method and loadCatalog() are synchronized
- // so that it's impossible to load (top level) catalogs
- // after lookups start. Likewise, deferred loading is also
- // synchronized (for "next" and delegated catalogs) to
- // ensure that parsers can share resolvers.
- loadingPermitted = false;
- }
-
-
- /**
- * Returns the error handler used to report catalog errors.
- * Null is returned if the parser's default error handling
- * will be used.
- *
- * @see #setErrorHandler
- */
- public ErrorHandler getErrorHandler ()
- { return errorHandler; }
-
- /**
- * Assigns the error handler used to report catalog errors.
- * These errors may come either from the SAX2 parser or
- * from the catalog parsing code driven by the parser.
- *
- * <p> If you're sharing the resolver between parsers, don't
- * change this once lookups have begun.
- *
- * @see #getErrorHandler
- *
- * @param parser The error handler, or null saying to use the default
- * (no diagnostics, and only fatal errors terminate loading).
- */
- public void setErrorHandler (ErrorHandler handler)
- { errorHandler = handler; }
-
-
- /**
- * Returns the name of the SAX2 parser class used to parse catalogs.
- * Null is returned if the system default is used.
- * @see #setParserClass
- */
- public String getParserClass ()
- { return parserClass; }
-
- /**
- * Names the SAX2 parser class used to parse catalogs.
- *
- * <p> If you're sharing the resolver between parsers, don't change
- * this once lookups have begun.
- *
- * <p> Note that in order to properly support the <em>xml:base</em>
- * attribute and relative URI resolution, the SAX parser used to parse
- * the catalog must provide a {@link Locator} and support the optional
- * declaration and lexical handlers.
- *
- * @see #getParserClass
- *
- * @param parser The parser class name, or null saying to use the
- * system default SAX2 parser.
- */
- public void setParserClass (String parser)
- { parserClass = parser; }
-
-
- /**
- * Returns true (the default) if all methods resolve
- * a given URI in the same way.
- * Returns false if calls resolving URIs as entities (such as
- * {@link #resolveEntity resolveEntity()}) use different catalog entries
- * than those resolving them as URIs ({@link #resolveURI resolveURI()}),
- * which will generally produce different results.
- *
- * <p>The OASIS XML Catalog specification defines two related schemes
- * to map URIs "as URIs" or "as system IDs".
- * URIs use <em>uri</em>, <em>rewriteURI</em>, and <em>delegateURI</em>
- * elements. System IDs do the same things with <em>systemId</em>,
- * <em>rewriteSystemId</em>, and <em>delegateSystemId</em>.
- * It's confusing and error prone to maintain two parallel copies of
- * such data. Accordingly, this class makes that behavior optional.
- * The <em>unified</em> interpretation of URI mappings is preferred,
- * since it prevents surprises where one URI gets mapped to different
- * contents depending on whether the reference happens to have come
- * from a DTD (or not).
- *
- * @see #setUnified
- */
- public boolean isUnified ()
- { return unified; }
-
- /**
- * Assigns the value of the flag returned by {@link #isUnified}.
- * Set it to false to be strictly conformant with the OASIS XML Catalog
- * specification. Set it to true to make all mappings for a given URI
- * give the same result, regardless of the reason for the mapping.
- *
- * <p>Don't change this once you've loaded the first catalog.
- *
- * @param value new flag setting
- */
- public void setUnified (boolean value)
- { unified = value; }
-
-
- /**
- * Returns true (the default) if a catalog's public identifier
- * mappings will be used.
- * When false is returned, such mappings are ignored except when
- * system IDs are discarded, such as for
- * entities using the <em>urn:publicid:</em> URI scheme in their
- * system identifiers. (See RFC 3151 for information about that
- * URI scheme. Using it in system identifiers may not work well
- * with many SAX parsers unless the <em>resolve-dtd-uris</em>
- * feature flag is set to false.)
- * @see #setUsingPublic
- */
- public boolean isUsingPublic ()
- { return usingPublic; }
-
- /**
- * Specifies which catalog search mode is used.
- * By default, public identifier mappings are able to override system
- * identifiers when both are available.
- * Applications may choose to ignore public
- * identifier mappings in such cases, so that system identifiers
- * declared in DTDs will only be overridden by an explicit catalog
- * match for that system ID.
- *
- * <p> If you're sharing the resolver between parsers, don't
- * change this once lookups have begun.
- * @see #isUsingPublic
- *
- * @param value true to always use public identifier mappings,
- * false to only use them for system ids using the <em>urn:publicid:</em>
- * URI scheme.
- */
- public void setUsingPublic (boolean value)
- { usingPublic = value; }
-
-
-
- // hmm, what's this do? :)
- private static Catalog loadCatalog (
- String parserClass,
- ErrorHandler eh,
- String uri,
- boolean unified
- ) throws SAXException, IOException
- {
- XMLReader parser;
- Loader loader;
- boolean doesIntern = false;
-
- if (parserClass == null)
- parser = XMLReaderFactory.createXMLReader ();
- else
- parser = XMLReaderFactory.createXMLReader (parserClass);
- if (eh != null)
- parser.setErrorHandler (eh);
- // resolve-dtd-entities is at default value (unrecognized == true)
-
- try {
- doesIntern = parser.getFeature (
- "http://xml.org/sax/features/string-interning");
- } catch (SAXNotRecognizedException e) { }
-
- loader = new Loader (doesIntern, eh, unified);
- loader.cat.parserClass = parserClass;
- loader.cat.catalogURI = uri;
-
- parser.setContentHandler (loader);
- parser.setProperty (
- "http://xml.org/sax/properties/declaration-handler",
- loader);
- parser.setProperty (
- "http://xml.org/sax/properties/lexical-handler",
- loader);
- parser.parse (uri);
-
- return loader.cat;
- }
-
- // perform one or both the normalizations for public ids
- private static String normalizePublicId (boolean full, String publicId)
- {
- if (publicId.startsWith ("urn:publicid:")) {
- StringBuffer buf = new StringBuffer ();
- char chars [] = publicId.toCharArray ();
-boolean hasbug = false;
-
- for (int i = 13; i < chars.length; i++) {
- switch (chars [i]) {
- case '+': buf.append (' '); continue;
- case ':': buf.append ("//"); continue;
- case ';': buf.append ("::"); continue;
- case '%':
-// FIXME unhex that char! meanwhile, warn and fallthrough ...
- hasbug = true;
- default: buf.append (chars [i]); continue;
- }
- }
- publicId = buf.toString ();
-if (hasbug)
-System.err.println ("nyet unhexing public id: " + publicId);
- full = true;
- }
-
- // SAX parsers do everything except that URN mapping, but
- // we can't trust other sources to normalize correctly
- if (full) {
- StringTokenizer tokens;
- String token;
-
- tokens = new StringTokenizer (publicId, " \r\n");
- publicId = null;
- while (tokens.hasMoreTokens ()) {
- if (publicId == null)
- publicId = tokens.nextToken ();
- else
- publicId += " " + tokens.nextToken ();
- }
- }
- return publicId;
- }
-
- private static boolean isUriExcluded (int c)
- { return c <= 0x20 || c >= 0x7f || "\"<>^`{|}".indexOf (c) != -1; }
-
- private static int hexNibble (int c)
- {
- if (c < 10)
- return c + '0';
- return ('a' - 10) + c;
- }
-
- // handles URIs with "excluded" characters
- private static String normalizeURI (String systemId)
- {
- int length = systemId.length ();
-
- for (int i = 0; i < length; i++) {
- char c = systemId.charAt (i);
-
- // escape non-ASCII plus "excluded" characters
- if (isUriExcluded (c)) {
- byte buf [];
- ByteArrayOutputStream out;
- int b;
-
- // a JVM that doesn't know UTF8 and 8859_1 is unusable!
- try {
- buf = systemId.getBytes ("UTF8");
- out = new ByteArrayOutputStream (buf.length + 10);
-
- for (i = 0; i < buf.length; i++) {
- b = buf [i] & 0x0ff;
- if (isUriExcluded (b)) {
- out.write ((int) '%');
- out.write (hexNibble (b >> 4));
- out.write (hexNibble (b & 0x0f));
- } else
- out.write (b);
- }
- return out.toString ("8859_1");
- } catch (IOException e) {
- throw new RuntimeException (
- "can't normalize URI: " + e.getMessage ());
- }
- }
- }
- return systemId;
- }
-
- // thrown to mark authoritative end of a search
- private static class DoneDelegation extends SAXException
- {
- DoneDelegation () { }
- }
-
-
- /**
- * Represents a OASIS XML Catalog, and encapsulates much of
- * the catalog functionality.
- */
- private static class Catalog
- {
- // loading infrastructure
- String catalogURI;
- ErrorHandler eh;
- boolean unified;
- String parserClass;
-
- // catalog data
- boolean hasPreference;
- boolean usingPublic;
-
- Hashtable publicIds;
- Hashtable publicDelegations;
-
- Hashtable systemIds;
- Hashtable systemRewrites;
- Hashtable systemDelegations;
-
- Hashtable uris;
- Hashtable uriRewrites;
- Hashtable uriDelegations;
-
- Hashtable doctypes;
-
- Vector next;
-
- // nonpublic!
- Catalog () { }
-
-
- // steps as found in OASIS XML catalog spec 7.1.2
- private InputSource locatePublicId (String publicId)
- throws SAXException, IOException
- {
- // 5. return (first) 'public' entry
- if (publicIds != null) {
- String retval = (String) publicIds.get (publicId);
- if (retval != null) {
- // IF the URI is accessible ...
- return new InputSource (retval);
- }
- }
-
- // 6. return delegatePublic catalog match [complex]
- if (publicDelegations != null)
- return checkDelegations (publicDelegations, publicId,
- publicId, null);
-
- return null;
- }
-
- // steps as found in OASIS XML catalog spec 7.1.2 or 7.2.2
- private InputSource mapURI (
- String uri,
- Hashtable ids,
- Hashtable rewrites,
- Hashtable delegations
- ) throws SAXException, IOException
- {
- // 7.1.2: 2. return (first) 'system' entry
- // 7.2.2: 2. return (first) 'uri' entry
- if (ids != null) {
- String retval = (String) ids.get (uri);
- if (retval != null) {
- // IF the URI is accessible ...
- return new InputSource (retval);
- }
- }
-
- // 7.1.2: 3. return 'rewriteSystem' entries
- // 7.2.2: 3. return 'rewriteURI' entries
- if (rewrites != null) {
- String prefix = null;
- String replace = null;
- int prefixLen = -1;
-
- for (Enumeration e = rewrites.keys ();
- e.hasMoreElements ();
- /* NOP */) {
- String temp = (String) e.nextElement ();
- int len = -1;
-
- if (!uri.startsWith (temp))
- continue;
- if (prefix != null
- && (len = temp.length ()) < prefixLen)
- continue;
- prefix = temp;
- prefixLen = len;
- replace = (String) rewrites.get (temp);
- }
- if (prefix != null) {
- StringBuffer buf = new StringBuffer (replace);
- buf.append (uri.substring (prefixLen));
- // IF the URI is accessible ...
- return new InputSource (buf.toString ());
- }
- }
-
- // 7.1.2: 4. return 'delegateSystem' catalog match [complex]
- // 7.2.2: 4. return 'delegateURI' catalog match [complex]
- if (delegations != null)
- return checkDelegations (delegations, uri, null, uri);
-
- return null;
- }
-
-
- /**
- * Returns a URI for an external entity.
- */
- public InputSource resolve (
- boolean usingPublic,
- String publicId,
- String systemId
- ) throws SAXException, IOException
- {
- boolean preferSystem;
- InputSource retval;
-
- if (hasPreference)
- preferSystem = !this.usingPublic;
- else
- preferSystem = !usingPublic;
-
- if (publicId != null)
- publicId = normalizePublicId (false, publicId);
-
- // behavior here matches section 7.1.1 of the oasis spec
- if (systemId != null) {
- if (systemId.startsWith ("urn:publicid:")) {
- String temp = normalizePublicId (true, systemId);
- if (publicId == null) {
- publicId = temp;
- systemId = null;
- } else if (!publicId.equals (temp)) {
- // error; ok to recover by:
- systemId = null;
- }
- } else
- systemId = normalizeURI (systemId);
- }
-
- if (systemId == null && publicId == null)
- return null;
-
- if (systemId != null) {
- retval = mapURI (systemId, systemIds, systemRewrites,
- systemDelegations);
- if (retval != null) {
- retval.setPublicId (publicId);
- return retval;
- }
- }
-
- if (publicId != null
- && !(systemId != null && preferSystem)) {
- retval = locatePublicId (publicId);
- if (retval != null) {
- retval.setPublicId (publicId);
- return retval;
- }
- }
-
- // 7. apply nextCatalog entries
- if (next != null) {
- int length = next.size ();
- for (int i = 0; i < length; i++) {
- Catalog n = getNext (i);
- retval = n.resolve (usingPublic, publicId, systemId);
- if (retval != null)
- return retval;
- }
- }
-
- return null;
- }
-
- /**
- * Maps one URI into another, for resources that are not defined
- * using XML external entity or notation syntax.
- */
- public InputSource resolveURI (String uri)
- throws SAXException, IOException
- {
- if (uri.startsWith ("urn:publicid:"))
- return resolve (true, normalizePublicId (true, uri), null);
-
- InputSource retval;
-
- uri = normalizeURI (uri);
-
- // 7.2.2 steps 2-4
- retval = mapURI (uri, uris, uriRewrites, uriDelegations);
- if (retval != null)
- return retval;
-
- // 7.2.2 step 5. apply nextCatalog entries
- if (next != null) {
- int length = next.size ();
- for (int i = 0; i < length; i++) {
- Catalog n = getNext (i);
- retval = n.resolveURI (uri);
- if (retval != null)
- return retval;
- }
- }
-
- return null;
- }
-
-
- /**
- * Finds the external subset associated with a given root element.
- */
- public InputSource getExternalSubset (String name)
- throws SAXException, IOException
- {
- if (doctypes != null) {
- String value = (String) doctypes.get (name);
- if (value != null) {
- // IF the URI is accessible ...
- return new InputSource (value);
- }
- }
- if (next != null) {
- int length = next.size ();
- for (int i = 0; i < length; i++) {
- Catalog n = getNext (i);
- if (n == null)
- continue;
- InputSource retval = n.getExternalSubset (name);
- if (retval != null)
- return retval;
- }
- }
- return null;
- }
-
- private synchronized Catalog getNext (int i)
- throws SAXException, IOException
- {
- Object obj;
-
- if (next == null || i < 0 || i >= next.size ())
- return null;
- obj = next.elementAt (i);
- if (obj instanceof Catalog)
- return (Catalog) obj;
-
- // ok, we deferred reading that catalog till now.
- // load and cache it.
- Catalog cat = null;
-
- try {
- cat = loadCatalog (parserClass, eh, (String) obj, unified);
- next.setElementAt (cat, i);
- } catch (SAXException e) {
- // must fail quietly, says the OASIS spec
- } catch (IOException e) {
- // same applies here
- }
- return cat;
- }
-
- private InputSource checkDelegations (
- Hashtable delegations,
- String id,
- String publicId, // only one of public/system
- String systemId // will be non-null...
- ) throws SAXException, IOException
- {
- Vector matches = null;
- int length = 0;
-
- // first, see if any prefixes match.
- for (Enumeration e = delegations.keys ();
- e.hasMoreElements ();
- /* NOP */) {
- String prefix = (String) e.nextElement ();
-
- if (!id.startsWith (prefix))
- continue;
- if (matches == null)
- matches = new Vector ();
-
- // maintain in longer->shorter sorted order
- // NOTE: assumes not many matches will fire!
- int index;
-
- for (index = 0; index < length; index++) {
- String temp = (String) matches.elementAt (index);
- if (prefix.length () > temp.length ()) {
- matches.insertElementAt (prefix, index);
- break;
- }
- }
- if (index == length)
- matches.addElement (prefix);
- length++;
- }
- if (matches == null)
- return null;
-
- // now we know the list of catalogs to replace our "top level"
- // list ... we use it here, rather than somehow going back and
- // restarting, since this helps avoid reading most catalogs.
- // this assumes stackspace won't be a problem.
- for (int i = 0; i < length; i++) {
- Catalog catalog = null;
- InputSource result;
-
- // get this catalog. we may not have read it yet.
- synchronized (delegations) {
- Object prefix = matches.elementAt (i);
- Object cat = delegations.get (prefix);
-
- if (cat instanceof Catalog)
- catalog = (Catalog) cat;
- else {
- try {
- // load and cache that catalog
- catalog = loadCatalog (parserClass, eh,
- (String) cat, unified);
- delegations.put (prefix, catalog);
- } catch (SAXException e) {
- // must ignore, says the OASIS spec
- } catch (IOException e) {
- // same applies here
- }
- }
- }
-
- // ignore failed loads, and proceed
- if (catalog == null)
- continue;
-
- // we have a catalog ... resolve!
- // usingPublic value can't matter, there's no choice
- result = catalog.resolve (true, publicId, systemId);
- if (result != null)
- return result;
- }
-
- // if there were no successes, the entire
- // lookup failed (all the way to top level)
- throw new DoneDelegation ();
- }
- }
-
-
- /** This is the namespace URI used for OASIS XML Catalogs. */
- private static final String catalogNamespace =
- "urn:oasis:names:tc:entity:xmlns:xml:catalog";
-
-
- /**
- * Loads/unmarshals one catalog.
- */
- private static class Loader extends DefaultHandler2
- {
- private boolean preInterned;
- private ErrorHandler handler;
- private boolean unified;
- private int ignoreDepth;
- private Locator locator;
- private boolean started;
- private Hashtable externals;
- private Stack bases;
-
- Catalog cat = new Catalog ();
-
-
- /**
- * Constructor.
- * @param flag true iff the parser already interns strings.
- * @param eh Errors and warnings are delegated to this.
- * @param unified true keeps one table for URI mappings;
- * false matches OASIS spec, storing mappings
- * for URIs and SYSTEM ids in parallel tables.
- */
- Loader (boolean flag, ErrorHandler eh, boolean unified)
- {
- preInterned = flag;
- handler = eh;
- this.unified = unified;
- cat.unified = unified;
- cat.eh = eh;
- }
-
-
- // strips out fragments
- private String nofrag (String uri)
- throws SAXException
- {
- if (uri.indexOf ('#') != -1) {
- warn ("URI with fragment: " + uri);
- uri = uri.substring (0, uri.indexOf ('#'));
- }
- return uri;
- }
-
- // absolutizes relative URIs
- private String absolutize (String uri)
- throws SAXException
- {
- // avoid creating URLs if they're already absolutized,
- // or if the URI is already using a known scheme
- if (uri.startsWith ("file:/")
- || uri.startsWith ("http:/")
- || uri.startsWith ("https:/")
- || uri.startsWith ("ftp:/")
- || uri.startsWith ("urn:")
- )
- return uri;
-
- // otherwise, let's hope the JDK handles this URI scheme.
- try {
- URL base = (URL) bases.peek ();
- return new URL (base, uri).toString ();
- } catch (Exception e) {
- fatal ("can't absolutize URI: " + uri);
- return null;
- }
- }
-
- // recoverable error
- private void error (String message)
- throws SAXException
- {
- if (handler == null)
- return;
- handler.error (new SAXParseException (message, locator));
- }
-
- // nonrecoverable error
- private void fatal (String message)
- throws SAXException
- {
- SAXParseException spe;
-
- spe = new SAXParseException (message, locator);
- if (handler != null)
- handler.fatalError (spe);
- throw spe;
- }
-
- // low severity problem
- private void warn (String message)
- throws SAXException
- {
- if (handler == null)
- return;
- handler.warning (new SAXParseException (message, locator));
- }
-
- // callbacks:
-
- public void setDocumentLocator (Locator l)
- { locator = l; }
-
- public void startDocument ()
- throws SAXException
- {
- if (locator == null)
- error ("no locator!");
- bases = new Stack ();
- String uri = locator.getSystemId ();
- try {
- bases.push (new URL (uri));
- } catch (IOException e) {
- fatal ("bad document base URI: " + uri);
- }
- }
-
- public void endDocument ()
- throws SAXException
- {
- try {
- if (!started)
- error ("not a catalog!");
- } finally {
- locator = null;
- handler = null;
- externals = null;
- bases = null;
- }
- }
-
- // XML Base support for external entities.
-
- // NOTE: expects parser is in default "resolve-dtd-uris" mode.
- public void externalEntityDecl (String name, String pub, String sys)
- throws SAXException
- {
- if (externals == null)
- externals = new Hashtable ();
- if (externals.get (name) == null)
- externals.put (name, pub);
- }
-
- public void startEntity (String name)
- throws SAXException
- {
- if (externals == null)
- return;
- String uri = (String) externals.get (name);
-
- // NOTE: breaks if an EntityResolver substitutes these URIs.
- // If toplevel loader supports one, must intercept calls...
- if (uri != null) {
- try {
- bases.push (new URL (uri));
- } catch (IOException e) {
- fatal ("entity '" + name + "', bad URI: " + uri);
- }
- }
- }
-
- public void endEntity (String name)
- {
- if (externals == null)
- return;
- String value = (String) externals.get (name);
-
- if (value != null)
- bases.pop ();
- }
-
- /**
- * Processes catalog elements, saving their data.
- */
- public void startElement (String namespace, String local,
- String qName, Attributes atts)
- throws SAXException
- {
- // must ignore non-catalog elements, and their contents
- if (ignoreDepth != 0 || !catalogNamespace.equals (namespace)) {
- ignoreDepth++;
- return;
- }
-
- // basic sanity checks
- if (!preInterned)
- local = local.intern ();
- if (!started) {
- started = true;
- if ("catalog" != local)
- fatal ("root element not 'catalog': " + local);
- }
-
- // Handle any xml:base attribute
- String xmlbase = atts.getValue ("xml:base");
-
- if (xmlbase != null) {
- URL base = (URL) bases.peek ();
- try {
- base = new URL (base, xmlbase);
- } catch (IOException e) {
- fatal ("can't resolve xml:base attribute: " + xmlbase);
- }
- bases.push (base);
- } else
- bases.push (bases.peek ());
-
- // fetch multi-element attributes, apply standard tweaks
- // values (uri, catalog, rewritePrefix) get normalized too,
- // as a precaution and since we may compare the values
- String catalog = atts.getValue ("catalog");
- if (catalog != null)
- catalog = normalizeURI (absolutize (catalog));
-
- String rewritePrefix = atts.getValue ("rewritePrefix");
- if (rewritePrefix != null)
- rewritePrefix = normalizeURI (absolutize (rewritePrefix));
-
- String systemIdStartString;
- systemIdStartString = atts.getValue ("systemIdStartString");
- if (systemIdStartString != null) {
- systemIdStartString = normalizeURI (systemIdStartString);
- // unmatchable <rewriteSystemId>, <delegateSystemId> elements
- if (systemIdStartString.startsWith ("urn:publicid:")) {
- error ("systemIdStartString is really a publicId!!");
- return;
- }
- }
-
- String uri = atts.getValue ("uri");
- if (uri != null)
- uri = normalizeURI (absolutize (uri));
-
- String uriStartString;
- uriStartString = atts.getValue ("uriStartString");
- if (uriStartString != null) {
- uriStartString = normalizeURI (uriStartString);
- // unmatchable <rewriteURI>, <delegateURI> elements
- if (uriStartString.startsWith ("urn:publicid:")) {
- error ("uriStartString is really a publicId!!");
- return;
- }
- }
-
- // strictly speaking "group" and "catalog" shouldn't nest
- // ... arbitrary restriction, no evident motivation
-
-// FIXME stack "prefer" settings (two elements only!) and use
-// them to populate different public mapping/delegation tables
-
- if ("catalog" == local || "group" == local) {
- String prefer = atts.getValue ("prefer");
-
- if (prefer != null && !"public".equals (prefer)) {
- if (!"system".equals (prefer)) {
- error ("in <" + local + " ... prefer='...'>, "
- + "assuming 'public'");
- prefer = "public";
- }
- }
- if (prefer != null) {
- if ("catalog" == local) {
- cat.hasPreference = true;
- cat.usingPublic = "public".equals (prefer);
- } else {
- if (!cat.hasPreference || cat.usingPublic
- != "public".equals (prefer)) {
-fatal ("<group prefer=...> case not handled");
- }
- }
- } else if ("group" == local && cat.hasPreference) {
-fatal ("<group prefer=...> case not handled");
- }
-
- //
- // PUBLIC ids: cleanly set up for id substitution
- //
- } else if ("public" == local) {
- String publicId = atts.getValue ("publicId");
- String value = null;
-
- if (publicId == null || uri == null) {
- error ("expecting <public publicId=... uri=.../>");
- return;
- }
- publicId = normalizePublicId (true, publicId);
- uri = nofrag (uri);
- if (cat.publicIds == null)
- cat.publicIds = new Hashtable ();
- else
- value = (String) cat.publicIds.get (publicId);
- if (value != null) {
- if (!value.equals (uri))
- warn ("ignoring <public...> entry for " + publicId);
- } else
- cat.publicIds.put (publicId, uri);
-
- } else if ("delegatePublic" == local) {
- String publicIdStartString;
- Object value = null;
-
- publicIdStartString = atts.getValue ("publicIdStartString");
- if (publicIdStartString == null || catalog == null) {
- error ("expecting <delegatePublic "
- + "publicIdStartString=... catalog=.../>");
- return;
- }
- publicIdStartString = normalizePublicId (true,
- publicIdStartString);
- if (cat.publicDelegations == null)
- cat.publicDelegations = new Hashtable ();
- else
- value = cat.publicDelegations.get (publicIdStartString);
- if (value != null) {
- if (!value.equals (catalog))
- warn ("ignoring <delegatePublic...> entry for "
- + uriStartString);
- } else
- cat.publicDelegations.put (publicIdStartString, catalog);
-
-
- //
- // SYSTEM ids: need substitution due to operational issues
- //
- } else if ("system" == local) {
- String systemId = atts.getValue ("systemId");
- String value = null;
-
- if (systemId == null || uri == null) {
- error ("expecting <system systemId=... uri=.../>");
- return;
- }
- systemId = normalizeURI (systemId);
- uri = nofrag (uri);
- if (systemId.startsWith ("urn:publicid:")) {
- error ("systemId is really a publicId!!");
- return;
- }
- if (cat.systemIds == null) {
- cat.systemIds = new Hashtable ();
- if (unified)
- cat.uris = cat.systemIds;
- } else
- value = (String) cat.systemIds.get (systemId);
- if (value != null) {
- if (!value.equals (uri))
- warn ("ignoring <system...> entry for " + systemId);
- } else
- cat.systemIds.put (systemId, uri);
-
- } else if ("rewriteSystem" == local) {
- String value = null;
-
- if (systemIdStartString == null || rewritePrefix == null
- || systemIdStartString.length () == 0
- || rewritePrefix.length () == 0
- ) {
- error ("expecting <rewriteSystem "
- + "systemIdStartString=... rewritePrefix=.../>");
- return;
- }
- if (cat.systemRewrites == null) {
- cat.systemRewrites = new Hashtable ();
- if (unified)
- cat.uriRewrites = cat.systemRewrites;
- } else
- value = (String) cat.systemRewrites.get (
- systemIdStartString);
- if (value != null) {
- if (!value.equals (rewritePrefix))
- warn ("ignoring <rewriteSystem...> entry for "
- + systemIdStartString);
- } else
- cat.systemRewrites.put (systemIdStartString,
- rewritePrefix);
-
- } else if ("delegateSystem" == local) {
- Object value = null;
-
- if (systemIdStartString == null || catalog == null) {
- error ("expecting <delegateSystem "
- + "systemIdStartString=... catalog=.../>");
- return;
- }
- if (cat.systemDelegations == null) {
- cat.systemDelegations = new Hashtable ();
- if (unified)
- cat.uriDelegations = cat.systemDelegations;
- } else
- value = cat.systemDelegations.get (systemIdStartString);
- if (value != null) {
- if (!value.equals (catalog))
- warn ("ignoring <delegateSystem...> entry for "
- + uriStartString);
- } else
- cat.systemDelegations.put (systemIdStartString, catalog);
-
-
- //
- // URI: just like "system" ID support, except that
- // fragment IDs are disallowed in "system" elements.
- //
- } else if ("uri" == local) {
- String name = atts.getValue ("name");
- String value = null;
-
- if (name == null || uri == null) {
- error ("expecting <uri name=... uri=.../>");
- return;
- }
- if (name.startsWith ("urn:publicid:")) {
- error ("name is really a publicId!!");
- return;
- }
- name = normalizeURI (name);
- if (cat.uris == null) {
- cat.uris = new Hashtable ();
- if (unified)
- cat.systemIds = cat.uris;
- } else
- value = (String) cat.uris.get (name);
- if (value != null) {
- if (!value.equals (uri))
- warn ("ignoring <uri...> entry for " + name);
- } else
- cat.uris.put (name, uri);
-
- } else if ("rewriteURI" == local) {
- String value = null;
-
- if (uriStartString == null || rewritePrefix == null
- || uriStartString.length () == 0
- || rewritePrefix.length () == 0
- ) {
- error ("expecting <rewriteURI "
- + "uriStartString=... rewritePrefix=.../>");
- return;
- }
- if (cat.uriRewrites == null) {
- cat.uriRewrites = new Hashtable ();
- if (unified)
- cat.systemRewrites = cat.uriRewrites;
- } else
- value = (String) cat.uriRewrites.get (uriStartString);
- if (value != null) {
- if (!value.equals (rewritePrefix))
- warn ("ignoring <rewriteURI...> entry for "
- + uriStartString);
- } else
- cat.uriRewrites.put (uriStartString, rewritePrefix);
-
- } else if ("delegateURI" == local) {
- Object value = null;
-
- if (uriStartString == null || catalog == null) {
- error ("expecting <delegateURI "
- + "uriStartString=... catalog=.../>");
- return;
- }
- if (cat.uriDelegations == null) {
- cat.uriDelegations = new Hashtable ();
- if (unified)
- cat.systemDelegations = cat.uriDelegations;
- } else
- value = cat.uriDelegations.get (uriStartString);
- if (value != null) {
- if (!value.equals (catalog))
- warn ("ignoring <delegateURI...> entry for "
- + uriStartString);
- } else
- cat.uriDelegations.put (uriStartString, catalog);
-
- //
- // NON-DELEGATING approach to modularity
- //
- } else if ("nextCatalog" == local) {
- if (catalog == null) {
- error ("expecting <nextCatalog catalog=.../>");
- return;
- }
- if (cat.next == null)
- cat.next = new Vector ();
- cat.next.addElement (catalog);
-
- //
- // EXTENSIONS from appendix E
- //
- } else if ("doctype" == local) {
- String name = atts.getValue ("name");
- String value = null;
-
- if (name == null || uri == null) {
- error ("expecting <doctype name=... uri=.../>");
- return;
- }
- name = normalizeURI (name);
- if (cat.doctypes == null)
- cat.doctypes = new Hashtable ();
- else
- value = (String) cat.doctypes.get (name);
- if (value != null) {
- if (!value.equals (uri))
- warn ("ignoring <doctype...> entry for "
- + uriStartString);
- } else
- cat.doctypes.put (name, uri);
-
-
- //
- // RESERVED ... ignore (like reserved attributes) but warn
- //
- } else {
- warn ("ignoring unknown catalog element: " + local);
- ignoreDepth++;
- }
- }
-
- public void endElement (String uri, String local, String qName)
- throws SAXException
- {
- if (ignoreDepth != 0)
- ignoreDepth--;
- else
- bases.pop ();
- }
- }
-}