1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.sun.org.apache.xml.internal.resolver.tools; 19 20import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; 21import java.io.IOException; 22import java.io.InputStream; 23import java.net.URL; 24import java.net.MalformedURLException; 25 26import org.xml.sax.SAXException; 27import org.xml.sax.XMLReader; 28import org.xml.sax.InputSource; 29import org.xml.sax.EntityResolver; 30 31import javax.xml.transform.sax.SAXSource; 32import javax.xml.transform.Source; 33import javax.xml.transform.URIResolver; 34import javax.xml.transform.TransformerException; 35import javax.xml.parsers.ParserConfigurationException; 36import javax.xml.parsers.SAXParserFactory; 37 38import com.sun.org.apache.xml.internal.resolver.Catalog; 39import com.sun.org.apache.xml.internal.resolver.CatalogManager; 40import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; 41 42/** 43 * A SAX EntityResolver/JAXP URIResolver that uses catalogs. 44 * 45 * <p>This class implements both a SAX EntityResolver and a JAXP URIResolver. 46 * </p> 47 * 48 * <p>This resolver understands OASIS TR9401 catalogs, XCatalogs, and the 49 * current working draft of the OASIS Entity Resolution Technical 50 * Committee specification.</p> 51 * 52 * @see Catalog 53 * @see org.xml.sax.EntityResolver 54 * @see javax.xml.transform.URIResolver 55 * @deprecated The JDK internal Catalog API in package 56 * {@code com.sun.org.apache.xml.internal.resolver} 57 * is encapsulated in JDK 9. The entire implementation under the package is now 58 * deprecated and subject to removal in a future release. Users of the API 59 * should migrate to the {@linkplain javax.xml.catalog new public API}. 60 * <p> 61 * The new Catalog API is supported throughout the JDK XML Processors, which allows 62 * the use of Catalog by simply setting a path to a Catalog file as a property. 63 * 64 * @author Norman Walsh 65 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a> 66 * 67 * @version 1.0 68 */ 69@Deprecated(since="9", forRemoval=true) 70public class CatalogResolver implements EntityResolver, URIResolver { 71 /** Make the parser Namespace aware? */ 72 public boolean namespaceAware = true; 73 74 /** Make the parser validating? */ 75 public boolean validating = false; 76 77 /** The underlying catalog */ 78 private Catalog catalog = null; 79 80 /** The catalog manager */ 81 private CatalogManager catalogManager = CatalogManager.getStaticManager(); 82 83 /** Constructor */ 84 public CatalogResolver() { 85 initializeCatalogs(false); 86 } 87 88 /** Constructor */ 89 public CatalogResolver(boolean privateCatalog) { 90 initializeCatalogs(privateCatalog); 91 } 92 93 /** Constructor */ 94 public CatalogResolver(CatalogManager manager) { 95 catalogManager = manager; 96 initializeCatalogs(!catalogManager.getUseStaticCatalog()); 97 } 98 99 /** Initialize catalog */ 100 private void initializeCatalogs(boolean privateCatalog) { 101 catalog = catalogManager.getCatalog(); 102 } 103 104 /** Return the underlying catalog */ 105 public Catalog getCatalog() { 106 return catalog; 107 } 108 109 /** 110 * Implements the guts of the <code>resolveEntity</code> method 111 * for the SAX interface. 112 * 113 * <p>Presented with an optional public identifier and a system 114 * identifier, this function attempts to locate a mapping in the 115 * catalogs.</p> 116 * 117 * <p>If such a mapping is found, it is returned. If no mapping is 118 * found, null is returned.</p> 119 * 120 * @param publicId The public identifier for the entity in question. 121 * This may be null. 122 * 123 * @param systemId The system identifier for the entity in question. 124 * XML requires a system identifier on all external entities, so this 125 * value is always specified. 126 * 127 * @return The resolved identifier (a URI reference). 128 */ 129 public String getResolvedEntity (String publicId, String systemId) { 130 String resolved = null; 131 132 if (catalog == null) { 133 catalogManager.debug.message(1, "Catalog resolution attempted with null catalog; ignored"); 134 return null; 135 } 136 137 if (systemId != null) { 138 try { 139 resolved = catalog.resolveSystem(systemId); 140 } catch (MalformedURLException me) { 141 catalogManager.debug.message(1, "Malformed URL exception trying to resolve", 142 publicId); 143 resolved = null; 144 } catch (IOException ie) { 145 catalogManager.debug.message(1, "I/O exception trying to resolve", publicId); 146 resolved = null; 147 } 148 } 149 150 if (resolved == null) { 151 if (publicId != null) { 152 try { 153 resolved = catalog.resolvePublic(publicId, systemId); 154 } catch (MalformedURLException me) { 155 catalogManager.debug.message(1, "Malformed URL exception trying to resolve", 156 publicId); 157 } catch (IOException ie) { 158 catalogManager.debug.message(1, "I/O exception trying to resolve", publicId); 159 } 160 } 161 162 if (resolved != null) { 163 catalogManager.debug.message(2, "Resolved public", publicId, resolved); 164 } 165 } else { 166 catalogManager.debug.message(2, "Resolved system", systemId, resolved); 167 } 168 169 return resolved; 170 } 171 172 /** 173 * Implements the <code>resolveEntity</code> method 174 * for the SAX interface. 175 * 176 * <p>Presented with an optional public identifier and a system 177 * identifier, this function attempts to locate a mapping in the 178 * catalogs.</p> 179 * 180 * <p>If such a mapping is found, the resolver attempts to open 181 * the mapped value as an InputSource and return it. Exceptions are 182 * ignored and null is returned if the mapped value cannot be opened 183 * as an input source.</p> 184 * 185 * <p>If no mapping is found (or an error occurs attempting to open 186 * the mapped value as an input source), null is returned and the system 187 * will use the specified system identifier as if no entityResolver 188 * was specified.</p> 189 * 190 * @param publicId The public identifier for the entity in question. 191 * This may be null. 192 * 193 * @param systemId The system identifier for the entity in question. 194 * XML requires a system identifier on all external entities, so this 195 * value is always specified. 196 * 197 * @return An InputSource for the mapped identifier, or null. 198 */ 199 public InputSource resolveEntity (String publicId, String systemId) { 200 String resolved = getResolvedEntity(publicId, systemId); 201 202 if (resolved != null) { 203 try { 204 InputSource iSource = new InputSource(resolved); 205 iSource.setPublicId(publicId); 206 207 // Ideally this method would not attempt to open the 208 // InputStream, but there is a bug (in Xerces, at least) 209 // that causes the parser to mistakenly open the wrong 210 // system identifier if the returned InputSource does 211 // not have a byteStream. 212 // 213 // It could be argued that we still shouldn't do this here, 214 // but since the purpose of calling the entityResolver is 215 // almost certainly to open the input stream, it seems to 216 // do little harm. 217 // 218 URL url = new URL(resolved); 219 InputStream iStream = url.openStream(); 220 iSource.setByteStream(iStream); 221 222 return iSource; 223 } catch (Exception e) { 224 catalogManager.debug.message(1, 225 "Failed to create InputSource (" 226 + e.toString() 227 + ")", resolved); 228 return null; 229 } 230 } 231 232 return null; 233 } 234 235 /** JAXP URIResolver API */ 236 public Source resolve(String href, String base) 237 throws TransformerException { 238 239 String uri = href; 240 String fragment = null; 241 int hashPos = href.indexOf("#"); 242 if (hashPos >= 0) { 243 uri = href.substring(0, hashPos); 244 fragment = href.substring(hashPos+1); 245 } 246 247 String result = null; 248 249 try { 250 result = catalog.resolveURI(href); 251 } catch (Exception e) { 252 // nop; 253 } 254 255 if (result == null) { 256 try { 257 URL url = null; 258 259 if (base==null) { 260 url = new URL(uri); 261 result = url.toString(); 262 } else { 263 URL baseURL = new URL(base); 264 url = (href.length()==0 ? baseURL : new URL(baseURL, uri)); 265 result = url.toString(); 266 } 267 } catch (java.net.MalformedURLException mue) { 268 // try to make an absolute URI from the current base 269 String absBase = makeAbsolute(base); 270 if (!absBase.equals(base)) { 271 // don't bother if the absBase isn't different! 272 return resolve(href, absBase); 273 } else { 274 throw new TransformerException("Malformed URL " 275 + href + "(base " + base + ")", 276 mue); 277 } 278 } 279 } 280 281 catalogManager.debug.message(2, "Resolved URI", href, result); 282 283 SAXSource source = new SAXSource(); 284 source.setInputSource(new InputSource(result)); 285 setEntityResolver(source); 286 return source; 287 } 288 289 /** 290 * <p>Establish an entityResolver for newly resolved URIs.</p> 291 * 292 * <p>This is called from the URIResolver to set an EntityResolver 293 * on the SAX parser to be used for new XML documents that are 294 * encountered as a result of the document() function, xsl:import, 295 * or xsl:include. This is done because the XSLT processor calls 296 * out to the SAXParserFactory itself to create a new SAXParser to 297 * parse the new document. The new parser does not automatically 298 * inherit the EntityResolver of the original (although arguably 299 * it should). See below:</p> 300 * 301 * <tt>"If an application wants to set the ErrorHandler or 302 * EntityResolver for an XMLReader used during a transformation, 303 * it should use a URIResolver to return the SAXSource which 304 * provides (with getXMLReader) a reference to the XMLReader"</tt> 305 * 306 * <p>...quoted from page 118 of the Java API for XML 307 * Processing 1.1 specification</p> 308 * 309 */ 310 private void setEntityResolver(SAXSource source) throws TransformerException { 311 XMLReader reader = source.getXMLReader(); 312 if (reader == null) { 313 SAXParserFactory spFactory = catalogManager.useServicesMechanism() ? 314 SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); 315 spFactory.setNamespaceAware(true); 316 try { 317 reader = spFactory.newSAXParser().getXMLReader(); 318 } 319 catch (ParserConfigurationException ex) { 320 throw new TransformerException(ex); 321 } 322 catch (SAXException ex) { 323 throw new TransformerException(ex); 324 } 325 } 326 reader.setEntityResolver(this); 327 source.setXMLReader(reader); 328 } 329 330 /** Attempt to construct an absolute URI */ 331 private String makeAbsolute(String uri) { 332 if (uri == null) { 333 uri = ""; 334 } 335 336 try { 337 URL url = new URL(uri); 338 return url.toString(); 339 } catch (MalformedURLException mue) { 340 try { 341 URL fileURL = FileURL.makeURL(uri); 342 return fileURL.toString(); 343 } catch (MalformedURLException mue2) { 344 // bail 345 return uri; 346 } 347 } 348 } 349} 350