1 /* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.xml.catalog; 26 27 import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.Reader; 31 import java.io.StringReader; 32 import java.net.URL; 33 import javax.xml.parsers.ParserConfigurationException; 34 import javax.xml.parsers.SAXParserFactory; 35 import javax.xml.transform.Source; 36 import javax.xml.transform.sax.SAXSource; 37 import org.w3c.dom.ls.LSInput; 38 import org.xml.sax.InputSource; 39 import org.xml.sax.SAXException; 40 import org.xml.sax.XMLReader; 41 42 /** 43 * Implements CatalogResolver. 44 * 45 * <p> 46 * This class implements a SAX EntityResolver, StAX XMLResolver, 47 * Schema Validation LSResourceResolver and Transform URIResolver. 48 * 49 * 50 * @since 9 51 */ 52 final class CatalogResolverImpl implements CatalogResolver { 53 Catalog catalog; 54 55 /** 56 * Construct an instance of the CatalogResolver from a Catalog. 57 * 58 * @param catalog A Catalog. 59 */ 60 public CatalogResolverImpl(Catalog catalog) { 61 this.catalog = catalog; 62 } 63 64 /* 65 Implements the EntityResolver interface 66 */ 67 @Override 68 public InputSource resolveEntity(String publicId, String systemId) { 69 //8150187: NPE expected if the system identifier is null for CatalogResolver 70 CatalogMessages.reportNPEOnNull("systemId", systemId); 71 72 //Normalize publicId and systemId 73 systemId = Normalizer.normalizeURI(Util.getNotNullOrEmpty(systemId)); 74 publicId = Normalizer.normalizePublicId(Normalizer.decodeURN(Util.getNotNullOrEmpty(publicId))); 75 76 //check whether systemId is an urn 77 if (systemId != null && systemId.startsWith(Util.URN)) { 78 systemId = Normalizer.decodeURN(systemId); 79 if (publicId != null && !publicId.equals(systemId)) { 80 systemId = null; 81 } else { 82 publicId = systemId; 83 systemId = null; 84 } 85 } 86 87 CatalogImpl c = (CatalogImpl)catalog; 88 String resolvedSystemId = Util.resolve(c, publicId, systemId); 89 90 if (resolvedSystemId != null) { 91 return new InputSource(resolvedSystemId); 92 } 93 94 GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve(); 95 switch (resolveType) { 96 case IGNORE: 97 return new InputSource(new StringReader("")); 98 case STRICT: 99 CatalogMessages.reportError(CatalogMessages.ERR_NO_MATCH, 100 new Object[]{publicId, systemId}); 101 } 102 103 //no action, allow the parser to continue 104 return null; 105 } 106 107 /* 108 Implements the URIResolver interface 109 */ 110 CatalogResolverImpl entityResolver; 111 112 @Override 113 public Source resolve(String href, String base) { 114 CatalogMessages.reportNPEOnNull("href", href); 115 116 href = Util.getNotNullOrEmpty(href); 117 base = Util.getNotNullOrEmpty(base); 118 119 String result = null; 120 CatalogImpl c = (CatalogImpl)catalog; 121 String uri = Normalizer.normalizeURI(href); 122 if (uri == null) { 123 return null; 124 } 125 126 //check whether uri is an urn 127 if (uri != null && uri.startsWith(Util.URN)) { 128 String publicId = Normalizer.decodeURN(uri); 129 if (publicId != null) { 130 result = Util.resolve(c, publicId, null); 131 } 132 } 133 134 //if no match with a public id, continue search for an URI 135 if (result == null) { 136 //remove fragment if any. 137 int hashPos = uri.indexOf("#"); 138 if (hashPos >= 0) { 139 uri = uri.substring(0, hashPos); 140 } 141 142 //search the current catalog 143 result = Util.resolve(c, null, uri); 144 } 145 146 //Report error or return the URI as is when no match is found 147 if (result == null) { 148 GroupEntry.ResolveType resolveType = c.getResolve(); 149 switch (resolveType) { 150 case IGNORE: 151 return new SAXSource(new InputSource(new StringReader(""))); 152 case STRICT: 153 CatalogMessages.reportError(CatalogMessages.ERR_NO_URI_MATCH, 154 new Object[]{href, base}); 155 } 156 try { 157 URL url = null; 158 159 if (base == null) { 160 url = new URL(uri); 161 result = url.toString(); 162 } else { 163 URL baseURL = new URL(base); 164 url = (href.length() == 0 ? baseURL : new URL(baseURL, uri)); 165 result = url.toString(); 166 } 167 } catch (java.net.MalformedURLException mue) { 168 CatalogMessages.reportError(CatalogMessages.ERR_CREATING_URI, 169 new Object[]{href, base}); 170 } 171 } 172 173 SAXSource source = new SAXSource(); 174 source.setInputSource(new InputSource(result)); 175 setEntityResolver(source); 176 return source; 177 } 178 179 /** 180 * Establish an entityResolver for newly resolved URIs. 181 * <p> 182 * This is called from the URIResolver to set an EntityResolver on the SAX 183 * parser to be used for new XML documents that are encountered as a result 184 * of the document() function, xsl:import, or xsl:include. This is done 185 * because the XSLT processor calls out to the SAXParserFactory itself to 186 * create a new SAXParser to parse the new document. The new parser does not 187 * automatically inherit the EntityResolver of the original (although 188 * arguably it should). Quote from JAXP specification on Class 189 * SAXTransformerFactory: 190 * <p> 191 * {@code If an application wants to set the ErrorHandler or EntityResolver 192 * for an XMLReader used during a transformation, it should use a URIResolver 193 * to return the SAXSource which provides (with getXMLReader) a reference to 194 * the XMLReader} 195 * 196 */ 197 private void setEntityResolver(SAXSource source) { 198 XMLReader reader = source.getXMLReader(); 199 if (reader == null) { 200 SAXParserFactory spFactory = new SAXParserFactoryImpl(); 201 spFactory.setNamespaceAware(true); 202 try { 203 reader = spFactory.newSAXParser().getXMLReader(); 204 } catch (ParserConfigurationException | SAXException ex) { 205 CatalogMessages.reportRunTimeError(CatalogMessages.ERR_PARSER_CONF, ex); 206 } 207 } 208 if (entityResolver != null) { 209 entityResolver = new CatalogResolverImpl(catalog); 210 } 211 reader.setEntityResolver(entityResolver); 212 source.setXMLReader(reader); 213 } 214 215 @Override 216 public InputStream resolveEntity(String publicId, String systemId, String baseUri, String namespace) { 217 InputSource is = resolveEntity(publicId, systemId); 218 219 if (is != null && !is.isEmpty()) { 220 221 try { 222 return new URL(is.getSystemId()).openStream(); 223 } catch (IOException ex) { 224 //considered as no mapping. 225 } 226 227 } 228 229 GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve(); 230 switch (resolveType) { 231 case IGNORE: 232 return null; 233 case STRICT: 234 CatalogMessages.reportError(CatalogMessages.ERR_NO_MATCH, 235 new Object[]{publicId, systemId}); 236 } 237 238 //no action, allow the parser to continue 239 return null; 240 } 241 242 @Override 243 public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { 244 InputSource is = resolveEntity(publicId, systemId); 245 246 if (is != null && !is.isEmpty()) { 247 return new LSInputImpl(is.getSystemId()); 248 } 249 250 GroupEntry.ResolveType resolveType = ((CatalogImpl) catalog).getResolve(); 251 switch (resolveType) { 252 case IGNORE: 253 return null; 254 case STRICT: 255 CatalogMessages.reportError(CatalogMessages.ERR_NO_MATCH, 256 new Object[]{publicId, systemId}); 257 } 258 259 //no action, allow the parser to continue 260 return null; 261 } 262 263 /** 264 * Implements LSInput. All that we need is the systemId since the Catalog 265 * has already resolved it. 266 */ 267 class LSInputImpl implements LSInput { 268 269 private String systemId; 270 271 public LSInputImpl(String systemId) { 272 this.systemId = systemId; 273 } 274 275 @Override 276 public Reader getCharacterStream() { 277 return null; 278 } 279 280 @Override 281 public void setCharacterStream(Reader characterStream) { 282 } 283 284 @Override 285 public InputStream getByteStream() { 286 return null; 287 } 288 289 @Override 290 public void setByteStream(InputStream byteStream) { 291 } 292 293 @Override 294 public String getStringData() { 295 return null; 296 } 297 298 @Override 299 public void setStringData(String stringData) { 300 } 301 302 @Override 303 public String getSystemId() { 304 return systemId; 305 } 306 307 @Override 308 public void setSystemId(String systemId) { 309 this.systemId = systemId; 310 } 311 312 @Override 313 public String getPublicId() { 314 return null; 315 } 316 317 @Override 318 public void setPublicId(String publicId) { 319 } 320 321 @Override 322 public String getBaseURI() { 323 return null; 324 } 325 326 @Override 327 public void setBaseURI(String baseURI) { 328 } 329 330 @Override 331 public String getEncoding() { 332 return null; 333 } 334 335 @Override 336 public void setEncoding(String encoding) { 337 } 338 339 @Override 340 public boolean getCertifiedText() { 341 return false; 342 } 343 344 @Override 345 public void setCertifiedText(boolean certifiedText) { 346 } 347 } 348 349 }