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