diff options
Diffstat (limited to 'libjava/gnu/xml/util/XCat.java')
| -rw-r--r-- | libjava/gnu/xml/util/XCat.java | 1609 |
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><?oasis-xml-catalog...?></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 (); - } - } -} |
