--- old/src/java.xml/share/classes/javax/xml/catalog/CatalogResolverImpl.java 2016-08-26 10:24:36.412491833 -0700 +++ new/src/java.xml/share/classes/javax/xml/catalog/CatalogResolverImpl.java 2016-08-26 10:24:36.332487834 -0700 @@ -24,15 +24,27 @@ */ package javax.xml.catalog; +import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; import java.io.StringReader; -import java.util.Iterator; +import java.net.URL; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Source; +import javax.xml.transform.sax.SAXSource; +import org.w3c.dom.ls.LSInput; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; /** - * A SAX EntityResolver/JAXP URIResolver that uses catalogs. + * Implements CatalogResolver. * *

- * This class implements both a SAX EntityResolver and a JAXP URIResolver. + * This class implements a SAX EntityResolver, StAX XMLResolver, + * Schema Validation LSResourceResolver and Transform URIResolver. * * * @since 9 @@ -49,9 +61,14 @@ this.catalog = catalog; } + /* + Implements the EntityResolver interface + */ @Override public InputSource resolveEntity(String publicId, String systemId) { + //8150187: NPE expected if the system identifier is null for CatalogResolver CatalogMessages.reportNPEOnNull("systemId", systemId); + //Normalize publicId and systemId systemId = Normalizer.normalizeURI(Util.getNotNullOrEmpty(systemId)); publicId = Normalizer.normalizePublicId(Normalizer.decodeURN(Util.getNotNullOrEmpty(publicId))); @@ -87,4 +104,242 @@ return null; } + /* + Implements the URIResolver interface + */ + CatalogResolverImpl entityResolver; + + @Override + public Source resolve(String href, String base) { + CatalogMessages.reportNPEOnNull("href", href); + + href = Util.getNotNullOrEmpty(href); + base = Util.getNotNullOrEmpty(base); + + String result = null; + CatalogImpl c = (CatalogImpl)catalog; + String uri = Normalizer.normalizeURI(href); + //check whether uri is an urn + if (uri != null && uri.startsWith(Util.URN)) { + String publicId = Normalizer.decodeURN(uri); + if (publicId != null) { + result = Util.resolve(c, publicId, null); + } + } + + //if no match with a public id, continue search for an URI + if (result == null) { + //remove fragment if any. + int hashPos = uri.indexOf("#"); + if (hashPos >= 0) { + uri = uri.substring(0, hashPos); + } + + //search the current catalog + result = Util.resolve(c, null, uri); + } + + //Report error or return the URI as is when no match is found + if (result == null) { + GroupEntry.ResolveType resolveType = c.getResolve(); + switch (resolveType) { + case IGNORE: + return new SAXSource(new InputSource(new StringReader(""))); + case STRICT: + CatalogMessages.reportError(CatalogMessages.ERR_NO_URI_MATCH, + new Object[]{href, base}); + } + 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) { + CatalogMessages.reportError(CatalogMessages.ERR_CREATING_URI, + new Object[]{href, base}); + } + } + + 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). Quote from JAXP specification on Class + * SAXTransformerFactory: + *

+ * {@code 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} + * + */ + private void setEntityResolver(SAXSource source) { + XMLReader reader = source.getXMLReader(); + if (reader == null) { + SAXParserFactory spFactory = new SAXParserFactoryImpl(); + spFactory.setNamespaceAware(true); + try { + reader = spFactory.newSAXParser().getXMLReader(); + } catch (ParserConfigurationException | SAXException ex) { + CatalogMessages.reportRunTimeError(CatalogMessages.ERR_PARSER_CONF, ex); + } + } + if (entityResolver != null) { + entityResolver = new CatalogResolverImpl(catalog); + } + reader.setEntityResolver(entityResolver); + source.setXMLReader(reader); + } + + @Override + public InputStream resolveEntity(String publicId, String systemId, String baseUri, String namespace) { + InputSource is = resolveEntity(publicId, systemId); + + if (is != null && !is.isEmpty()) { + + try { + return new URL(is.getSystemId()).openStream(); + } catch (IOException ex) { + //considered as no mapping. + } + + } + + GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve(); + switch (resolveType) { + case IGNORE: + return null; + case STRICT: + CatalogMessages.reportError(CatalogMessages.ERR_NO_MATCH, + new Object[]{publicId, systemId}); + } + + //no action, allow the parser to continue + return null; + } + + @Override + public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { + InputSource is = resolveEntity(publicId, systemId); + + if (is != null && !is.isEmpty()) { + return new LSInputImpl(is.getSystemId()); + } + + GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve(); + switch (resolveType) { + case IGNORE: + return null; + case STRICT: + CatalogMessages.reportError(CatalogMessages.ERR_NO_MATCH, + new Object[]{publicId, systemId}); + } + + //no action, allow the parser to continue + return null; + } + + /** + * Implements LSInput. All that we need is the systemId since the Catalog + * has already resolved it. + */ + class LSInputImpl implements LSInput { + + private String systemId; + + public LSInputImpl(String systemId) { + this.systemId = systemId; + } + + @Override + public Reader getCharacterStream() { + return null; + } + + @Override + public void setCharacterStream(Reader characterStream) { + } + + @Override + public InputStream getByteStream() { + return null; + } + + @Override + public void setByteStream(InputStream byteStream) { + } + + @Override + public String getStringData() { + return null; + } + + @Override + public void setStringData(String stringData) { + } + + @Override + public String getSystemId() { + return systemId; + } + + @Override + public void setSystemId(String systemId) { + this.systemId = systemId; + } + + @Override + public String getPublicId() { + return null; + } + + @Override + public void setPublicId(String publicId) { + } + + @Override + public String getBaseURI() { + return null; + } + + @Override + public void setBaseURI(String baseURI) { + } + + @Override + public String getEncoding() { + return null; + } + + @Override + public void setEncoding(String encoding) { + } + + @Override + public boolean getCertifiedText() { + return false; + } + + @Override + public void setCertifiedText(boolean certifiedText) { + } + } + }