From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/classpath/gnu/xml/util/XCat.java | 1611 ++++++++++++++++++++++++++++++ 1 file changed, 1611 insertions(+) create mode 100644 libjava/classpath/gnu/xml/util/XCat.java (limited to 'libjava/classpath/gnu/xml/util/XCat.java') diff --git a/libjava/classpath/gnu/xml/util/XCat.java b/libjava/classpath/gnu/xml/util/XCat.java new file mode 100644 index 000000000..ea23ad682 --- /dev/null +++ b/libjava/classpath/gnu/xml/util/XCat.java @@ -0,0 +1,1611 @@ +/* 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 gnu.java.lang.CPStringBuilder; + +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 OASIS XML Catalogs, + * 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. + * + *

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:

+ *      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);
+ *      ...
+ * + *

A second mode is to arrange that your application uses instances of + * this class as its entity resolver, and automatically loads catalogs + * referenced by <?oasis-xml-catalog...?> processing + * instructions found before the DTD in documents it parses. + * It would then discard the resolver after each parse. + * + *

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. + *

+ * + *

+ * + *

Errors in catalogs implicitly loaded (during resolution) are ignored + * beyond being reported through any ErrorHandler assigned using + * {@link #setErrorHandler setErrorHandler()}. SAX exceptions + * thrown from such a handler won't abort resolution, although throwing a + * RuntimeException or Error will normally abort both + * resolution and parsing. Useful diagnostic information is available to + * any ErrorHandler used to report problems, or from any exception + * thrown from an explicit {@link #loadCatalog loadCatalog()} invocation. + * Applications can use that information as troubleshooting aids. + * + *

While this class requires SAX2 Extensions 1.1 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 oasis-xml-catalog 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. + * + *

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. + * + *

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. + * + *

Note that catalogs referenced by this one will not be loaded + * at this time. Catalogs referenced through nextCatalog + * or delegate* 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()}. + * + *

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 doctype 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()}. + * + *

Warning: 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()}. + * + *

The functional limitations of this interface include:

+ * + *

Applications can tell whether this limited functionality will be + * used: if the feature flag associated with the {@link EntityResolver2} + * interface is not true, 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 + * xml-stylesheet 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()}. + * + *

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. + * + *

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 xml:base 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 startDTD() (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. + * + *

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. + * + *

If you're sharing the resolver between parsers, don't change + * this once lookups have begun. + * + *

Note that in order to properly support the xml:base + * 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. + * + *

The OASIS XML Catalog specification defines two related schemes + * to map URIs "as URIs" or "as system IDs". + * URIs use uri, rewriteURI, and delegateURI + * elements. System IDs do the same things with systemId, + * rewriteSystemId, and delegateSystemId. + * It's confusing and error prone to maintain two parallel copies of + * such data. Accordingly, this class makes that behavior optional. + * The unified 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. + * + *

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 urn:publicid: 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 resolve-dtd-uris + * 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. + * + *

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 urn:publicid: + * 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:")) { + CPStringBuilder buf = new CPStringBuilder (); + 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) { + CPStringBuilder buf = new CPStringBuilder (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 , 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 , 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 (" case not handled"); + } + } + } else if ("group" == local && cat.hasPreference) { +fatal (" 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 "); + 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 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 "); + 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 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 "); + 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 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 "); + 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 entry for " + + systemIdStartString); + } else + cat.systemRewrites.put (systemIdStartString, + rewritePrefix); + + } else if ("delegateSystem" == local) { + Object value = null; + + if (systemIdStartString == null || catalog == null) { + error ("expecting "); + 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 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 "); + 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 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 "); + 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 entry for " + + uriStartString); + } else + cat.uriRewrites.put (uriStartString, rewritePrefix); + + } else if ("delegateURI" == local) { + Object value = null; + + if (uriStartString == null || catalog == null) { + error ("expecting "); + 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 entry for " + + uriStartString); + } else + cat.uriDelegations.put (uriStartString, catalog); + + // + // NON-DELEGATING approach to modularity + // + } else if ("nextCatalog" == local) { + if (catalog == null) { + error ("expecting "); + 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 "); + 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 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 (); + } + } +} -- cgit v1.2.3