/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.org.apache.xml.internal.resolver.tools; import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.MalformedURLException; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.InputSource; import org.xml.sax.EntityResolver; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.Source; import javax.xml.transform.URIResolver; import javax.xml.transform.TransformerException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import com.sun.org.apache.xml.internal.resolver.Catalog; import com.sun.org.apache.xml.internal.resolver.CatalogManager; import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; /** * A SAX EntityResolver/JAXP URIResolver that uses catalogs. * *

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

* *

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

* * @see Catalog * @see org.xml.sax.EntityResolver * @see javax.xml.transform.URIResolver * @deprecated The JDK internal Catalog API in package * {@code com.sun.org.apache.xml.internal.resolver} * is encapsulated in JDK 9. The entire implementation under the package is now * deprecated and subject to removal in a future release. Users of the API * should migrate to the {@linkplain javax.xml.catalog new public API}. *

* The new Catalog API is supported throughout the JDK XML Processors, which allows * the use of Catalog by simply setting a path to a Catalog file as a property. * * @author Norman Walsh * Norman.Walsh@Sun.COM * * @version 1.0 */ @Deprecated(since="9", forRemoval=true) public class CatalogResolver implements EntityResolver, URIResolver { /** Make the parser Namespace aware? */ public boolean namespaceAware = true; /** Make the parser validating? */ public boolean validating = false; /** The underlying catalog */ private Catalog catalog = null; /** The catalog manager */ private CatalogManager catalogManager = CatalogManager.getStaticManager(); /** Constructor */ public CatalogResolver() { initializeCatalogs(false); } /** Constructor */ public CatalogResolver(boolean privateCatalog) { initializeCatalogs(privateCatalog); } /** Constructor */ public CatalogResolver(CatalogManager manager) { catalogManager = manager; initializeCatalogs(!catalogManager.getUseStaticCatalog()); } /** Initialize catalog */ private void initializeCatalogs(boolean privateCatalog) { catalog = catalogManager.getCatalog(); } /** Return the underlying catalog */ public Catalog getCatalog() { return catalog; } /** * Implements the guts of the resolveEntity method * for the SAX interface. * *

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

* *

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

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

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

* *

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

* *

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

* * @param publicId The public identifier for the entity in question. * This may be null. * * @param systemId The system identifier for the entity in question. * XML requires a system identifier on all external entities, so this * value is always specified. * * @return An InputSource for the mapped identifier, or null. */ public InputSource resolveEntity (String publicId, String systemId) { String resolved = getResolvedEntity(publicId, systemId); if (resolved != null) { try { InputSource iSource = new InputSource(resolved); iSource.setPublicId(publicId); // Ideally this method would not attempt to open the // InputStream, but there is a bug (in Xerces, at least) // that causes the parser to mistakenly open the wrong // system identifier if the returned InputSource does // not have a byteStream. // // It could be argued that we still shouldn't do this here, // but since the purpose of calling the entityResolver is // almost certainly to open the input stream, it seems to // do little harm. // URL url = new URL(resolved); InputStream iStream = url.openStream(); iSource.setByteStream(iStream); return iSource; } catch (Exception e) { catalogManager.debug.message(1, "Failed to create InputSource (" + e.toString() + ")", resolved); return null; } } return null; } /** JAXP URIResolver API */ public Source resolve(String href, String base) throws TransformerException { String uri = href; String fragment = null; int hashPos = href.indexOf("#"); if (hashPos >= 0) { uri = href.substring(0, hashPos); fragment = href.substring(hashPos+1); } String result = null; try { result = catalog.resolveURI(href); } catch (Exception e) { // nop; } if (result == null) { try { URL url = null; if (base==null) { url = new URL(uri); result = url.toString(); } else { URL baseURL = new URL(base); url = (href.length()==0 ? baseURL : new URL(baseURL, uri)); result = url.toString(); } } catch (java.net.MalformedURLException mue) { // try to make an absolute URI from the current base String absBase = makeAbsolute(base); if (!absBase.equals(base)) { // don't bother if the absBase isn't different! return resolve(href, absBase); } else { throw new TransformerException("Malformed URL " + href + "(base " + base + ")", mue); } } } catalogManager.debug.message(2, "Resolved URI", href, result); SAXSource source = new SAXSource(); source.setInputSource(new InputSource(result)); setEntityResolver(source); return source; } /** *

Establish an entityResolver for newly resolved URIs.

* *

This is called from the URIResolver to set an EntityResolver * on the SAX parser to be used for new XML documents that are * encountered as a result of the document() function, xsl:import, * or xsl:include. This is done because the XSLT processor calls * out to the SAXParserFactory itself to create a new SAXParser to * parse the new document. The new parser does not automatically * inherit the EntityResolver of the original (although arguably * it should). See below:

* * "If an application wants to set the ErrorHandler or * EntityResolver for an XMLReader used during a transformation, * it should use a URIResolver to return the SAXSource which * provides (with getXMLReader) a reference to the XMLReader" * *

...quoted from page 118 of the Java API for XML * Processing 1.1 specification

* */ private void setEntityResolver(SAXSource source) throws TransformerException { XMLReader reader = source.getXMLReader(); if (reader == null) { SAXParserFactory spFactory = catalogManager.useServicesMechanism() ? SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); spFactory.setNamespaceAware(true); try { reader = spFactory.newSAXParser().getXMLReader(); } catch (ParserConfigurationException ex) { throw new TransformerException(ex); } catch (SAXException ex) { throw new TransformerException(ex); } } reader.setEntityResolver(this); source.setXMLReader(reader); } /** Attempt to construct an absolute URI */ private String makeAbsolute(String uri) { if (uri == null) { uri = ""; } try { URL url = new URL(uri); return url.toString(); } catch (MalformedURLException mue) { try { URL fileURL = FileURL.makeURL(uri); return fileURL.toString(); } catch (MalformedURLException mue2) { // bail return uri; } } } }