1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 // ResolvingParser.java - An interface for reading catalog files 6 7 /* 8 * Copyright 2001-2004 The Apache Software Foundation or its licensors, 9 * as applicable. 10 * 11 * Licensed under the Apache License, Version 2.0 (the "License"); 12 * you may not use this file except in compliance with the License. 13 * You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, software 18 * distributed under the License is distributed on an "AS IS" BASIS, 19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 * See the License for the specific language governing permissions and 21 * limitations under the License. 22 */ 23 24 package com.sun.org.apache.xml.internal.resolver.tools; 25 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.net.URL; 29 import java.net.MalformedURLException; 30 import java.util.Locale; 31 32 import org.xml.sax.Parser; 33 import org.xml.sax.InputSource; 34 import org.xml.sax.Locator; 35 import org.xml.sax.ErrorHandler; 36 import org.xml.sax.DTDHandler; 37 import org.xml.sax.DocumentHandler; 38 import org.xml.sax.AttributeList; 39 import org.xml.sax.EntityResolver; 40 import org.xml.sax.SAXException; 41 42 import javax.xml.parsers.SAXParserFactory; 43 import javax.xml.parsers.SAXParser; 44 45 import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; 46 import com.sun.org.apache.xml.internal.resolver.Catalog; 47 import com.sun.org.apache.xml.internal.resolver.CatalogManager; 48 import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; 49 50 /** 51 * A SAX Parser that performs catalog-based entity resolution. 52 * 53 * <p>This class implements a SAX Parser that performs entity resolution 54 * using the CatalogResolver. The actual, underlying parser is obtained 55 * from a SAXParserFactory.</p> 56 * </p> 57 * 58 * @deprecated This interface has been replaced by the 59 * {@link com.sun.org.apache.xml.internal.resolver.tools.ResolvingXMLReader} for SAX2. 60 * @see CatalogResolver 61 * @see org.xml.sax.Parser 62 * 63 * @author Norman Walsh 64 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a> 65 * 66 * @version 1.0 67 */ 68 public class ResolvingParser 69 implements Parser, DTDHandler, DocumentHandler, EntityResolver { 70 /** Make the parser Namespace aware? */ 71 public static boolean namespaceAware = true; 72 73 /** Make the parser validating? */ 74 public static boolean validating = false; 75 76 /** Suppress explanatory message? 77 * 78 * @see #parse(InputSource) 79 */ 80 public static boolean suppressExplanation = false; 81 82 /** The underlying parser. */ 83 private SAXParser saxParser = null; 84 85 /** The underlying reader. */ 86 private Parser parser = null; 87 88 /** The underlying DocumentHandler. */ 89 private DocumentHandler documentHandler = null; 90 91 /** The underlying DTDHandler. */ 92 private DTDHandler dtdHandler = null; 93 94 /** The manager for the underlying resolver. */ 95 private CatalogManager catalogManager = CatalogManager.getStaticManager(); 96 97 /** The underlying catalog resolver. */ 98 private CatalogResolver catalogResolver = null; 99 100 /** A separate resolver for oasis-xml-pi catalogs. */ 101 private CatalogResolver piCatalogResolver = null; 102 103 /** Are we in the prolog? Is an oasis-xml-catalog PI valid now? */ 104 private boolean allowXMLCatalogPI = false; 105 106 /** Has an oasis-xml-catalog PI been seen? */ 107 private boolean oasisXMLCatalogPI = false; 108 109 /** The base URI of the input document, if known. */ 110 private URL baseURL = null; 111 112 /** Constructor. */ 113 public ResolvingParser() { 114 initParser(); 115 } 116 117 /** Constructor. */ 118 public ResolvingParser(CatalogManager manager) { 119 catalogManager = manager; 120 initParser(); 121 } 122 123 /** Initialize the parser. */ 124 private void initParser() { 125 catalogResolver = new CatalogResolver(catalogManager); 126 SAXParserFactory spf = catalogManager.useServicesMechanism() ? 127 SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); 128 spf.setNamespaceAware(namespaceAware); 129 spf.setValidating(validating); 130 131 try { 132 saxParser = spf.newSAXParser(); 133 parser = saxParser.getParser(); 134 documentHandler = null; 135 dtdHandler = null; 136 } catch (Exception ex) { 137 ex.printStackTrace(); 138 } 139 } 140 141 /** Return the Catalog being used. */ 142 public Catalog getCatalog() { 143 return catalogResolver.getCatalog(); 144 } 145 146 /** 147 * SAX Parser API. 148 * 149 * <p>Note that the JAXP 1.1ea2 parser crashes with an InternalError if 150 * it encounters a system identifier that appears to be a relative URI 151 * that begins with a slash. For example, the declaration:</p> 152 * 153 * <pre> 154 * <!DOCTYPE book SYSTEM "/path/to/dtd/on/my/system/docbookx.dtd"> 155 * </pre> 156 * 157 * <p>would cause such an error. As a convenience, this method catches 158 * that error and prints an explanation. (Unfortunately, it's not possible 159 * to identify the particular system identifier that causes the problem.) 160 * </p> 161 * 162 * <p>The underlying error is forwarded after printing the explanatory 163 * message. The message is only every printed once and if 164 * <code>suppressExplanation</code> is set to <code>false</code> before 165 * parsing, it will never be printed.</p> 166 */ 167 public void parse(InputSource input) 168 throws IOException, 169 SAXException { 170 setupParse(input.getSystemId()); 171 try { 172 parser.parse(input); 173 } catch (InternalError ie) { 174 explain(input.getSystemId()); 175 throw ie; 176 } 177 } 178 179 /** SAX Parser API. 180 * 181 * @see #parse(InputSource) 182 */ 183 public void parse(String systemId) 184 throws IOException, 185 SAXException { 186 setupParse(systemId); 187 try { 188 parser.parse(systemId); 189 } catch (InternalError ie) { 190 explain(systemId); 191 throw ie; 192 } 193 } 194 195 /** SAX Parser API. */ 196 public void setDocumentHandler(DocumentHandler handler) { 197 documentHandler = handler; 198 } 199 200 /** SAX Parser API. */ 201 public void setDTDHandler(DTDHandler handler) { 202 dtdHandler = handler; 203 } 204 205 /** 206 * SAX Parser API. 207 * 208 * <p>The purpose of this class is to implement an entity resolver. 209 * Attempting to set a different one is pointless (and ignored).</p> 210 */ 211 public void setEntityResolver(EntityResolver resolver) { 212 // nop 213 } 214 215 /** SAX Parser API. */ 216 public void setErrorHandler(ErrorHandler handler) { 217 parser.setErrorHandler(handler); 218 } 219 220 /** SAX Parser API. */ 221 public void setLocale(Locale locale) throws SAXException { 222 parser.setLocale(locale); 223 } 224 225 /** SAX DocumentHandler API. */ 226 public void characters(char[] ch, int start, int length) 227 throws SAXException { 228 if (documentHandler != null) { 229 documentHandler.characters(ch,start,length); 230 } 231 } 232 233 /** SAX DocumentHandler API. */ 234 public void endDocument() throws SAXException { 235 if (documentHandler != null) { 236 documentHandler.endDocument(); 237 } 238 } 239 240 /** SAX DocumentHandler API. */ 241 public void endElement(String name) throws SAXException { 242 if (documentHandler != null) { 243 documentHandler.endElement(name); 244 } 245 } 246 247 /** SAX DocumentHandler API. */ 248 public void ignorableWhitespace(char[] ch, int start, int length) 249 throws SAXException { 250 if (documentHandler != null) { 251 documentHandler.ignorableWhitespace(ch,start,length); 252 } 253 } 254 255 /** SAX DocumentHandler API. */ 256 public void processingInstruction(String target, String pidata) 257 throws SAXException { 258 259 if (target.equals("oasis-xml-catalog")) { 260 URL catalog = null; 261 String data = pidata; 262 263 int pos = data.indexOf("catalog="); 264 if (pos >= 0) { 265 data = data.substring(pos+8); 266 if (data.length() > 1) { 267 String quote = data.substring(0,1); 268 data = data.substring(1); 269 pos = data.indexOf(quote); 270 if (pos >= 0) { 271 data = data.substring(0, pos); 272 try { 273 if (baseURL != null) { 274 catalog = new URL(baseURL, data); 275 } else { 276 catalog = new URL(data); 277 } 278 } catch (MalformedURLException mue) { 279 // nevermind 280 } 281 } 282 } 283 } 284 285 if (allowXMLCatalogPI) { 286 if (catalogManager.getAllowOasisXMLCatalogPI()) { 287 catalogManager.debug.message(4,"oasis-xml-catalog PI", pidata); 288 289 if (catalog != null) { 290 try { 291 catalogManager.debug.message(4,"oasis-xml-catalog", catalog.toString()); 292 oasisXMLCatalogPI = true; 293 294 if (piCatalogResolver == null) { 295 piCatalogResolver = new CatalogResolver(true); 296 } 297 298 piCatalogResolver.getCatalog().parseCatalog(catalog.toString()); 299 } catch (Exception e) { 300 catalogManager.debug.message(3, "Exception parsing oasis-xml-catalog: " 301 + catalog.toString()); 302 } 303 } else { 304 catalogManager.debug.message(3, "PI oasis-xml-catalog unparseable: " + pidata); 305 } 306 } else { 307 catalogManager.debug.message(4,"PI oasis-xml-catalog ignored: " + pidata); 308 } 309 } else { 310 catalogManager.debug.message(3, "PI oasis-xml-catalog occurred in an invalid place: " 311 + pidata); 312 } 313 } else { 314 if (documentHandler != null) { 315 documentHandler.processingInstruction(target, pidata); 316 } 317 } 318 } 319 320 /** SAX DocumentHandler API. */ 321 public void setDocumentLocator(Locator locator) { 322 if (documentHandler != null) { 323 documentHandler.setDocumentLocator(locator); 324 } 325 } 326 327 /** SAX DocumentHandler API. */ 328 public void startDocument() throws SAXException { 329 if (documentHandler != null) { 330 documentHandler.startDocument(); 331 } 332 } 333 334 /** SAX DocumentHandler API. */ 335 public void startElement(String name, AttributeList atts) 336 throws SAXException { 337 allowXMLCatalogPI = false; 338 if (documentHandler != null) { 339 documentHandler.startElement(name,atts); 340 } 341 } 342 343 /** SAX DTDHandler API. */ 344 public void notationDecl (String name, String publicId, String systemId) 345 throws SAXException { 346 allowXMLCatalogPI = false; 347 if (dtdHandler != null) { 348 dtdHandler.notationDecl(name,publicId,systemId); 349 } 350 } 351 352 /** SAX DTDHandler API. */ 353 public void unparsedEntityDecl (String name, 354 String publicId, 355 String systemId, 356 String notationName) 357 throws SAXException { 358 allowXMLCatalogPI = false; 359 if (dtdHandler != null) { 360 dtdHandler.unparsedEntityDecl (name, publicId, systemId, notationName); 361 } 362 } 363 364 /** 365 * Implements the <code>resolveEntity</code> method 366 * for the SAX interface, using an underlying CatalogResolver 367 * to do the real work. 368 */ 369 public InputSource resolveEntity (String publicId, String systemId) { 370 allowXMLCatalogPI = false; 371 String resolved = catalogResolver.getResolvedEntity(publicId, systemId); 372 373 if (resolved == null && piCatalogResolver != null) { 374 resolved = piCatalogResolver.getResolvedEntity(publicId, systemId); 375 } 376 377 if (resolved != null) { 378 try { 379 InputSource iSource = new InputSource(resolved); 380 iSource.setPublicId(publicId); 381 382 // Ideally this method would not attempt to open the 383 // InputStream, but there is a bug (in Xerces, at least) 384 // that causes the parser to mistakenly open the wrong 385 // system identifier if the returned InputSource does 386 // not have a byteStream. 387 // 388 // It could be argued that we still shouldn't do this here, 389 // but since the purpose of calling the entityResolver is 390 // almost certainly to open the input stream, it seems to 391 // do little harm. 392 // 393 URL url = new URL(resolved); 394 InputStream iStream = url.openStream(); 395 iSource.setByteStream(iStream); 396 397 return iSource; 398 } catch (Exception e) { 399 catalogManager.debug.message(1, "Failed to create InputSource", resolved); 400 return null; 401 } 402 } else { 403 return null; 404 } 405 } 406 407 /** Setup for parsing. */ 408 private void setupParse(String systemId) { 409 allowXMLCatalogPI = true; 410 parser.setEntityResolver(this); 411 parser.setDocumentHandler(this); 412 parser.setDTDHandler(this); 413 414 URL cwd = null; 415 416 try { 417 cwd = FileURL.makeURL("basename"); 418 } catch (MalformedURLException mue) { 419 cwd = null; 420 } 421 422 try { 423 baseURL = new URL(systemId); 424 } catch (MalformedURLException mue) { 425 if (cwd != null) { 426 try { 427 baseURL = new URL(cwd, systemId); 428 } catch (MalformedURLException mue2) { 429 // give up 430 baseURL = null; 431 } 432 } else { 433 // give up 434 baseURL = null; 435 } 436 } 437 } 438 439 /** Provide one possible explanation for an InternalError. */ 440 private void explain(String systemId) { 441 if (!suppressExplanation) { 442 System.out.println("Parser probably encountered bad URI in " + systemId); 443 System.out.println("For example, replace '/some/uri' with 'file:/some/uri'."); 444 } 445 } 446 }