1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 // SAXCatalogReader.java - Read XML 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.readers; 25 26 import com.sun.org.apache.xml.internal.resolver.Catalog; 27 import com.sun.org.apache.xml.internal.resolver.CatalogException; 28 import com.sun.org.apache.xml.internal.resolver.CatalogManager; 29 import com.sun.org.apache.xml.internal.resolver.helpers.Debug; 30 import java.io.FileNotFoundException; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.net.MalformedURLException; 34 import java.net.URL; 35 import java.net.URLConnection; 36 import java.net.UnknownHostException; 37 import java.util.Hashtable; 38 import javax.xml.parsers.ParserConfigurationException; 39 import javax.xml.parsers.SAXParser; 40 import javax.xml.parsers.SAXParserFactory; 41 import org.xml.sax.AttributeList; 42 import org.xml.sax.Attributes; 43 import org.xml.sax.ContentHandler; 44 import org.xml.sax.DocumentHandler; 45 import org.xml.sax.EntityResolver; 46 import org.xml.sax.InputSource; 47 import org.xml.sax.Locator; 48 import org.xml.sax.Parser; 49 import org.xml.sax.SAXException; 50 import sun.reflect.misc.ReflectUtil; 51 52 /** 53 * A SAX-based CatalogReader. 54 * 55 * <p>This class is used to read XML Catalogs using the SAX. This reader 56 * has an advantage over the DOM-based reader in that it functions on 57 * the stream of SAX events. It has the disadvantage 58 * that it cannot look around in the tree.</p> 59 * 60 * <p>Since the choice of CatalogReaders (in the InputStream case) can only 61 * be made on the basis of MIME type, the following problem occurs: only 62 * one CatalogReader can exist for all XML mime types. In order to get 63 * around this problem, the SAXCatalogReader relies on a set of external 64 * CatalogParsers to actually build the catalog.</p> 65 * 66 * <p>The selection of CatalogParsers is made on the basis of the QName 67 * of the root element of the document.</p> 68 * 69 * @see Catalog 70 * @see CatalogReader 71 * @see SAXCatalogReader 72 * @see TextCatalogReader 73 * @see DOMCatalogParser 74 * 75 * @author Norman Walsh 76 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a> 77 * 78 */ 79 public class SAXCatalogReader implements CatalogReader, ContentHandler, DocumentHandler { 80 /** The SAX Parser Factory */ 81 protected SAXParserFactory parserFactory = null; 82 83 /** The SAX Parser Class */ 84 protected String parserClass = null; 85 86 /** 87 * Mapping table from QNames to CatalogParser classes. 88 * 89 * <p>Each key in this hash table has the form "elementname" 90 * or "{namespaceuri}elementname". The former is used if the 91 * namespace URI is null.</p> 92 */ 93 protected Hashtable namespaceMap = new Hashtable(); 94 95 /** The parser in use for the current catalog. */ 96 private SAXCatalogParser saxParser = null; 97 98 /** Set if something goes horribly wrong. It allows the class to 99 * ignore the rest of the events that are received. 100 */ 101 private boolean abandonHope = false; 102 103 /** The Catalog that we're working for. */ 104 private Catalog catalog; 105 106 /** Set the XML SAX Parser Factory. 107 */ 108 public void setParserFactory(SAXParserFactory parserFactory) { 109 this.parserFactory = parserFactory; 110 } 111 112 /** Set the XML SAX Parser Class 113 */ 114 public void setParserClass(String parserClass) { 115 this.parserClass = parserClass; 116 } 117 118 /** Get the parser factory currently in use. */ 119 public SAXParserFactory getParserFactory() { 120 return parserFactory; 121 } 122 123 /** Get the parser class currently in use. */ 124 public String getParserClass() { 125 return parserClass; 126 } 127 128 /** The debug class to use for this reader. 129 * 130 * This is a bit of a hack. Anyway, whenever we read for a catalog, 131 * we extract the debug object 132 * from the catalog's manager so that we can use it to print messages. 133 * 134 * In production, we don't really expect any messages so it doesn't 135 * really matter. But it's still a bit of a hack. 136 */ 137 protected Debug debug = CatalogManager.getStaticManager().debug; 138 139 /** The constructor */ 140 public SAXCatalogReader() { 141 parserFactory = null; 142 parserClass = null; 143 } 144 145 /** The constructor */ 146 public SAXCatalogReader(SAXParserFactory parserFactory) { 147 this.parserFactory = parserFactory; 148 } 149 150 /** The constructor */ 151 public SAXCatalogReader(String parserClass) { 152 this.parserClass = parserClass; 153 } 154 155 /** Set the SAXCatalogParser class for the given namespace/root 156 * element type. 157 */ 158 public void setCatalogParser(String namespaceURI, 159 String rootElement, 160 String parserClass) { 161 if (namespaceURI == null) { 162 namespaceMap.put(rootElement, parserClass); 163 } else { 164 namespaceMap.put("{"+namespaceURI+"}"+rootElement, parserClass); 165 } 166 } 167 168 /** Get the SAXCatalogParser class for the given namespace/root 169 * element type. 170 */ 171 public String getCatalogParser(String namespaceURI, 172 String rootElement) { 173 if (namespaceURI == null) { 174 return (String) namespaceMap.get(rootElement); 175 } else { 176 return (String) namespaceMap.get("{"+namespaceURI+"}"+rootElement); 177 } 178 } 179 180 /** 181 * Parse an XML Catalog file. 182 * 183 * @param catalog The catalog to which this catalog file belongs 184 * @param fileUrl The URL or filename of the catalog file to process 185 * 186 * @throws MalformedURLException Improper fileUrl 187 * @throws IOException Error reading catalog file 188 */ 189 public void readCatalog(Catalog catalog, String fileUrl) 190 throws MalformedURLException, IOException, 191 CatalogException { 192 193 URL url = null; 194 195 try { 196 url = new URL(fileUrl); 197 } catch (MalformedURLException e) { 198 url = new URL("file:///" + fileUrl); 199 } 200 201 debug = catalog.getCatalogManager().debug; 202 203 try { 204 URLConnection urlCon = url.openConnection(); 205 readCatalog(catalog, urlCon.getInputStream()); 206 } catch (FileNotFoundException e) { 207 catalog.getCatalogManager().debug.message(1, "Failed to load catalog, file not found", 208 url.toString()); 209 } 210 } 211 212 /** 213 * Parse an XML Catalog stream. 214 * 215 * @param catalog The catalog to which this catalog file belongs 216 * @param is The input stream from which the catalog will be read 217 * 218 * @throws MalformedURLException Improper fileUrl 219 * @throws IOException Error reading catalog file 220 * @throws CatalogException A Catalog exception 221 */ 222 public void readCatalog(Catalog catalog, InputStream is) 223 throws IOException, CatalogException { 224 225 // Create an instance of the parser 226 if (parserFactory == null && parserClass == null) { 227 debug.message(1, "Cannot read SAX catalog without a parser"); 228 throw new CatalogException(CatalogException.UNPARSEABLE); 229 } 230 231 debug = catalog.getCatalogManager().debug; 232 EntityResolver bResolver = catalog.getCatalogManager().getBootstrapResolver(); 233 234 this.catalog = catalog; 235 236 try { 237 if (parserFactory != null) { 238 SAXParser parser = parserFactory.newSAXParser(); 239 SAXParserHandler spHandler = new SAXParserHandler(); 240 spHandler.setContentHandler(this); 241 if (bResolver != null) { 242 spHandler.setEntityResolver(bResolver); 243 } 244 parser.parse(new InputSource(is), spHandler); 245 } else { 246 Parser parser = (Parser) ReflectUtil.forName(parserClass).newInstance(); 247 parser.setDocumentHandler(this); 248 if (bResolver != null) { 249 parser.setEntityResolver(bResolver); 250 } 251 parser.parse(new InputSource(is)); 252 } 253 } catch (ClassNotFoundException cnfe) { 254 throw new CatalogException(CatalogException.UNPARSEABLE); 255 } catch (IllegalAccessException iae) { 256 throw new CatalogException(CatalogException.UNPARSEABLE); 257 } catch (InstantiationException ie) { 258 throw new CatalogException(CatalogException.UNPARSEABLE); 259 } catch (ParserConfigurationException pce) { 260 throw new CatalogException(CatalogException.UNKNOWN_FORMAT); 261 } catch (SAXException se) { 262 Exception e = se.getException(); 263 // FIXME: there must be a better way 264 UnknownHostException uhe = new UnknownHostException(); 265 FileNotFoundException fnfe = new FileNotFoundException(); 266 if (e != null) { 267 if (e.getClass() == uhe.getClass()) { 268 throw new CatalogException(CatalogException.PARSE_FAILED, 269 e.toString()); 270 } else if (e.getClass() == fnfe.getClass()) { 271 throw new CatalogException(CatalogException.PARSE_FAILED, 272 e.toString()); 273 } 274 } 275 throw new CatalogException(se); 276 } 277 } 278 279 // ---------------------------------------------------------------------- 280 // Implement the SAX ContentHandler interface 281 282 /** The SAX <code>setDocumentLocator</code> method. Does nothing. */ 283 public void setDocumentLocator (Locator locator) { 284 if (saxParser != null) { 285 saxParser.setDocumentLocator(locator); 286 } 287 } 288 289 /** The SAX <code>startDocument</code> method. Does nothing. */ 290 public void startDocument () throws SAXException { 291 saxParser = null; 292 abandonHope = false; 293 return; 294 } 295 296 /** The SAX <code>endDocument</code> method. Does nothing. */ 297 public void endDocument ()throws SAXException { 298 if (saxParser != null) { 299 saxParser.endDocument(); 300 } 301 } 302 303 /** 304 * The SAX <code>startElement</code> method. 305 * 306 * <p>The catalog parser is selected based on the namespace of the 307 * first element encountered in the catalog.</p> 308 */ 309 public void startElement (String name, 310 AttributeList atts) 311 throws SAXException { 312 313 if (abandonHope) { 314 return; 315 } 316 317 if (saxParser == null) { 318 String prefix = ""; 319 if (name.indexOf(':') > 0) { 320 prefix = name.substring(0, name.indexOf(':')); 321 } 322 323 String localName = name; 324 if (localName.indexOf(':') > 0) { 325 localName = localName.substring(localName.indexOf(':')+1); 326 } 327 328 String namespaceURI = null; 329 if (prefix.equals("")) { 330 namespaceURI = atts.getValue("xmlns"); 331 } else { 332 namespaceURI = atts.getValue("xmlns:" + prefix); 333 } 334 335 String saxParserClass = getCatalogParser(namespaceURI, 336 localName); 337 338 if (saxParserClass == null) { 339 abandonHope = true; 340 if (namespaceURI == null) { 341 debug.message(2, "No Catalog parser for " + name); 342 } else { 343 debug.message(2, "No Catalog parser for " 344 + "{" + namespaceURI + "}" 345 + name); 346 } 347 return; 348 } 349 350 try { 351 saxParser = (SAXCatalogParser) 352 ReflectUtil.forName(saxParserClass).newInstance(); 353 354 saxParser.setCatalog(catalog); 355 saxParser.startDocument(); 356 saxParser.startElement(name, atts); 357 } catch (ClassNotFoundException cnfe) { 358 saxParser = null; 359 abandonHope = true; 360 debug.message(2, cnfe.toString()); 361 } catch (InstantiationException ie) { 362 saxParser = null; 363 abandonHope = true; 364 debug.message(2, ie.toString()); 365 } catch (IllegalAccessException iae) { 366 saxParser = null; 367 abandonHope = true; 368 debug.message(2, iae.toString()); 369 } catch (ClassCastException cce ) { 370 saxParser = null; 371 abandonHope = true; 372 debug.message(2, cce.toString()); 373 } 374 } else { 375 saxParser.startElement(name, atts); 376 } 377 } 378 379 /** 380 * The SAX2 <code>startElement</code> method. 381 * 382 * <p>The catalog parser is selected based on the namespace of the 383 * first element encountered in the catalog.</p> 384 */ 385 public void startElement (String namespaceURI, 386 String localName, 387 String qName, 388 Attributes atts) 389 throws SAXException { 390 391 if (abandonHope) { 392 return; 393 } 394 395 if (saxParser == null) { 396 String saxParserClass = getCatalogParser(namespaceURI, 397 localName); 398 399 if (saxParserClass == null) { 400 abandonHope = true; 401 if (namespaceURI == null) { 402 debug.message(2, "No Catalog parser for " + localName); 403 } else { 404 debug.message(2, "No Catalog parser for " 405 + "{" + namespaceURI + "}" 406 + localName); 407 } 408 return; 409 } 410 411 try { 412 saxParser = (SAXCatalogParser) 413 ReflectUtil.forName(saxParserClass).newInstance(); 414 415 saxParser.setCatalog(catalog); 416 saxParser.startDocument(); 417 saxParser.startElement(namespaceURI, localName, qName, atts); 418 } catch (ClassNotFoundException cnfe) { 419 saxParser = null; 420 abandonHope = true; 421 debug.message(2, cnfe.toString()); 422 } catch (InstantiationException ie) { 423 saxParser = null; 424 abandonHope = true; 425 debug.message(2, ie.toString()); 426 } catch (IllegalAccessException iae) { 427 saxParser = null; 428 abandonHope = true; 429 debug.message(2, iae.toString()); 430 } catch (ClassCastException cce ) { 431 saxParser = null; 432 abandonHope = true; 433 debug.message(2, cce.toString()); 434 } 435 } else { 436 saxParser.startElement(namespaceURI, localName, qName, atts); 437 } 438 } 439 440 /** The SAX <code>endElement</code> method. Does nothing. */ 441 public void endElement (String name) throws SAXException { 442 if (saxParser != null) { 443 saxParser.endElement(name); 444 } 445 } 446 447 /** The SAX2 <code>endElement</code> method. Does nothing. */ 448 public void endElement (String namespaceURI, 449 String localName, 450 String qName) throws SAXException { 451 if (saxParser != null) { 452 saxParser.endElement(namespaceURI, localName, qName); 453 } 454 } 455 456 /** The SAX <code>characters</code> method. Does nothing. */ 457 public void characters (char ch[], int start, int length) 458 throws SAXException { 459 if (saxParser != null) { 460 saxParser.characters(ch, start, length); 461 } 462 } 463 464 /** The SAX <code>ignorableWhitespace</code> method. Does nothing. */ 465 public void ignorableWhitespace (char ch[], int start, int length) 466 throws SAXException { 467 if (saxParser != null) { 468 saxParser.ignorableWhitespace(ch, start, length); 469 } 470 } 471 472 /** The SAX <code>processingInstruction</code> method. Does nothing. */ 473 public void processingInstruction (String target, String data) 474 throws SAXException { 475 if (saxParser != null) { 476 saxParser.processingInstruction(target, data); 477 } 478 } 479 480 /** The SAX <code>startPrefixMapping</code> method. Does nothing. */ 481 public void startPrefixMapping (String prefix, String uri) 482 throws SAXException { 483 if (saxParser != null) { 484 saxParser.startPrefixMapping (prefix, uri); 485 } 486 } 487 488 /** The SAX <code>endPrefixMapping</code> method. Does nothing. */ 489 public void endPrefixMapping (String prefix) 490 throws SAXException { 491 if (saxParser != null) { 492 saxParser.endPrefixMapping (prefix); 493 } 494 } 495 496 /** The SAX <code>skippedentity</code> method. Does nothing. */ 497 public void skippedEntity (String name) 498 throws SAXException { 499 if (saxParser != null) { 500 saxParser.skippedEntity(name); 501 } 502 } 503 }