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