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 org.apache.xerces.parsers; 19 20 import java.io.IOException; 21 import java.util.ArrayList; 22 import java.util.HashMap; 23 import java.util.Locale; 24 25 import org.apache.xerces.impl.Constants; 26 import org.apache.xerces.util.ParserConfigurationSettings; 27 import org.apache.xerces.util.SymbolTable; 28 import org.apache.xerces.xni.XMLDTDContentModelHandler; 29 import org.apache.xerces.xni.XMLDTDHandler; 30 import org.apache.xerces.xni.XMLDocumentHandler; 31 import org.apache.xerces.xni.XNIException; 32 import org.apache.xerces.xni.parser.XMLComponent; 33 import org.apache.xerces.xni.parser.XMLComponentManager; 34 import org.apache.xerces.xni.parser.XMLConfigurationException; 35 import org.apache.xerces.xni.parser.XMLDocumentSource; 36 import org.apache.xerces.xni.parser.XMLEntityResolver; 37 import org.apache.xerces.xni.parser.XMLErrorHandler; 38 import org.apache.xerces.xni.parser.XMLInputSource; 39 import org.apache.xerces.xni.parser.XMLParserConfiguration; 40 41 /** 42 * A very basic parser configuration. This configuration class can 43 * be used as a base class for custom parser configurations. The 44 * basic parser configuration creates the symbol table (if not 45 * specified at construction time) and manages all of the recognized 46 * features and properties. 47 * <p> 48 * The basic parser configuration does <strong>not</strong> mandate 49 * any particular pipeline configuration or the use of specific 50 * components except for the symbol table. If even this is too much 51 * for a basic parser configuration, the programmer can create a new 52 * configuration class that implements the 53 * <code>XMLParserConfiguration</code> interface. 54 * <p> 55 * Subclasses of the basic parser configuration can add their own 56 * recognized features and properties by calling the 57 * <code>addRecognizedFeature</code> and 58 * <code>addRecognizedProperty</code> methods, respectively. 59 * <p> 60 * The basic parser configuration assumes that the configuration 61 * will be made up of various parser components that implement the 62 * <code>XMLComponent</code> interface. If subclasses of this 63 * configuration create their own components for use in the 64 * parser configuration, then each component should be added to 65 * the list of components by calling the <code>addComponent</code> 66 * method. The basic parser configuration will make sure to call 67 * the <code>reset</code> method of each registered component 68 * before parsing an instance document. 69 * <p> 70 * This class recognizes the following features and properties: 71 * <ul> 72 * <li>Features 73 * <ul> 74 * <li>http://xml.org/sax/features/validation</li> 75 * <li>http://xml.org/sax/features/namespaces</li> 76 * <li>http://xml.org/sax/features/external-general-entities</li> 77 * <li>http://xml.org/sax/features/external-parameter-entities</li> 78 * </ul> 79 * <li>Properties 80 * <ul> 81 * <li>http://xml.org/sax/properties/xml-string</li> 82 * <li>http://apache.org/xml/properties/internal/symbol-table</li> 83 * <li>http://apache.org/xml/properties/internal/error-handler</li> 84 * <li>http://apache.org/xml/properties/internal/entity-resolver</li> 85 * </ul> 86 * </ul> 87 * 88 * @author Arnaud Le Hors, IBM 89 * @author Andy Clark, IBM 90 * 91 * @version $Id: BasicParserConfiguration.java 447239 2006-09-18 05:08:26Z mrglavas $ 92 */ 93 public abstract class BasicParserConfiguration 94 extends ParserConfigurationSettings 95 implements XMLParserConfiguration { 96 97 // 98 // Constants 99 // 100 101 // feature identifiers 102 103 /** Feature identifier: validation. */ 104 protected static final String VALIDATION = 105 Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE; 106 107 /** Feature identifier: namespaces. */ 108 protected static final String NAMESPACES = 109 Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE; 110 111 /** Feature identifier: external general entities. */ 112 protected static final String EXTERNAL_GENERAL_ENTITIES = 113 Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE; 114 115 /** Feature identifier: external parameter entities. */ 116 protected static final String EXTERNAL_PARAMETER_ENTITIES = 117 Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE; 118 119 // property identifiers 120 121 /** Property identifier: xml string. */ 122 protected static final String XML_STRING = 123 Constants.SAX_PROPERTY_PREFIX + Constants.XML_STRING_PROPERTY; 124 125 /** Property identifier: symbol table. */ 126 protected static final String SYMBOL_TABLE = 127 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; 128 129 /** Property identifier: error handler. */ 130 protected static final String ERROR_HANDLER = 131 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY; 132 133 /** Property identifier: entity resolver. */ 134 protected static final String ENTITY_RESOLVER = 135 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; 136 137 // 138 // Data 139 // 140 141 // components (non-configurable) 142 143 /** Symbol table. */ 144 protected SymbolTable fSymbolTable; 145 146 147 // data 148 149 /** Locale. */ 150 protected Locale fLocale; 151 152 /** Components. */ 153 protected ArrayList fComponents; 154 155 // handlers 156 157 /** The document handler. */ 158 protected XMLDocumentHandler fDocumentHandler; 159 160 /** The DTD handler. */ 161 protected XMLDTDHandler fDTDHandler; 162 163 /** The DTD content model handler. */ 164 protected XMLDTDContentModelHandler fDTDContentModelHandler; 165 166 /** Last component in the document pipeline */ 167 protected XMLDocumentSource fLastComponent; 168 169 // 170 // Constructors 171 // 172 173 /** Default Constructor. */ 174 protected BasicParserConfiguration() { 175 this(null, null); 176 } // <init>() 177 178 /** 179 * Constructs a parser configuration using the specified symbol table. 180 * 181 * @param symbolTable The symbol table to use. 182 */ 183 protected BasicParserConfiguration(SymbolTable symbolTable) { 184 this(symbolTable, null); 185 } // <init>(SymbolTable) 186 187 /** 188 * Constructs a parser configuration using the specified symbol table 189 * and parent settings. 190 * 191 * @param symbolTable The symbol table to use. 192 * @param parentSettings The parent settings. 193 */ 194 protected BasicParserConfiguration(SymbolTable symbolTable, 195 XMLComponentManager parentSettings) { 196 super(parentSettings); 197 198 // create a vector to hold all the components in use 199 fComponents = new ArrayList(); 200 201 // create storage for recognized features and properties 202 fRecognizedFeatures = new ArrayList(); 203 fRecognizedProperties = new ArrayList(); 204 205 // create table for features and properties 206 fFeatures = new HashMap(); 207 fProperties = new HashMap(); 208 209 // add default recognized features 210 final String[] recognizedFeatures = { 211 PARSER_SETTINGS, 212 VALIDATION, 213 NAMESPACES, 214 EXTERNAL_GENERAL_ENTITIES, 215 EXTERNAL_PARAMETER_ENTITIES, 216 }; 217 addRecognizedFeatures(recognizedFeatures); 218 fFeatures.put(PARSER_SETTINGS, Boolean.TRUE); 219 // set state for default features 220 fFeatures.put(VALIDATION, Boolean.FALSE); 221 fFeatures.put(NAMESPACES, Boolean.TRUE); 222 fFeatures.put(EXTERNAL_GENERAL_ENTITIES, Boolean.TRUE); 223 fFeatures.put(EXTERNAL_PARAMETER_ENTITIES, Boolean.TRUE); 224 225 // add default recognized properties 226 final String[] recognizedProperties = { 227 XML_STRING, 228 SYMBOL_TABLE, 229 ERROR_HANDLER, 230 ENTITY_RESOLVER, 231 }; 232 addRecognizedProperties(recognizedProperties); 233 234 if (symbolTable == null) { 235 symbolTable = new SymbolTable(); 236 } 237 fSymbolTable = symbolTable; 238 fProperties.put(SYMBOL_TABLE, fSymbolTable); 239 240 } // <init>(SymbolTable) 241 242 /** 243 * Adds a component to the parser configuration. This method will 244 * also add all of the component's recognized features and properties 245 * to the list of default recognized features and properties. 246 * 247 * @param component The component to add. 248 */ 249 protected void addComponent(XMLComponent component) { 250 251 // don't add a component more than once 252 if (fComponents.contains(component)) { 253 return; 254 } 255 fComponents.add(component); 256 257 // register component's recognized features 258 String[] recognizedFeatures = component.getRecognizedFeatures(); 259 addRecognizedFeatures(recognizedFeatures); 260 261 // register component's recognized properties 262 String[] recognizedProperties = component.getRecognizedProperties(); 263 addRecognizedProperties(recognizedProperties); 264 265 // set default values 266 if (recognizedFeatures != null) { 267 for (int i = 0; i < recognizedFeatures.length; i++) { 268 String featureId = recognizedFeatures[i]; 269 Boolean state = component.getFeatureDefault(featureId); 270 if (state != null) { 271 super.setFeature(featureId, state.booleanValue()); 272 } 273 } 274 } 275 if (recognizedProperties != null) { 276 for (int i = 0; i < recognizedProperties.length; i++) { 277 String propertyId = recognizedProperties[i]; 278 Object value = component.getPropertyDefault(propertyId); 279 if (value != null) { 280 super.setProperty(propertyId, value); 281 } 282 } 283 } 284 285 } // addComponent(XMLComponent) 286 287 // 288 // XMLParserConfiguration methods 289 // 290 291 /** 292 * Parse an XML document. 293 * <p> 294 * The parser can use this method to instruct this configuration 295 * to begin parsing an XML document from any valid input source 296 * (a character stream, a byte stream, or a URI). 297 * <p> 298 * Parsers may not invoke this method while a parse is in progress. 299 * Once a parse is complete, the parser may then parse another XML 300 * document. 301 * <p> 302 * This method is synchronous: it will not return until parsing 303 * has ended. If a client application wants to terminate 304 * parsing early, it should throw an exception. 305 * 306 * @param inputSource The input source for the top-level of the 307 * XML document. 308 * 309 * @exception XNIException Any XNI exception, possibly wrapping 310 * another exception. 311 * @exception IOException An IO exception from the parser, possibly 312 * from a byte stream or character stream 313 * supplied by the parser. 314 */ 315 public abstract void parse(XMLInputSource inputSource) 316 throws XNIException, IOException; 317 318 /** 319 * Sets the document handler on the last component in the pipeline 320 * to receive information about the document. 321 * 322 * @param documentHandler The document handler. 323 */ 324 public void setDocumentHandler(XMLDocumentHandler documentHandler) { 325 fDocumentHandler = documentHandler; 326 if (fLastComponent != null) { 327 fLastComponent.setDocumentHandler(fDocumentHandler); 328 if (fDocumentHandler !=null){ 329 fDocumentHandler.setDocumentSource(fLastComponent); 330 } 331 } 332 } // setDocumentHandler(XMLDocumentHandler) 333 334 /** Returns the registered document handler. */ 335 public XMLDocumentHandler getDocumentHandler() { 336 return fDocumentHandler; 337 } // getDocumentHandler():XMLDocumentHandler 338 339 /** 340 * Sets the DTD handler. 341 * 342 * @param dtdHandler The DTD handler. 343 */ 344 public void setDTDHandler(XMLDTDHandler dtdHandler) { 345 fDTDHandler = dtdHandler; 346 } // setDTDHandler(XMLDTDHandler) 347 348 /** Returns the registered DTD handler. */ 349 public XMLDTDHandler getDTDHandler() { 350 return fDTDHandler; 351 } // getDTDHandler():XMLDTDHandler 352 353 /** 354 * Sets the DTD content model handler. 355 * 356 * @param handler The DTD content model handler. 357 */ 358 public void setDTDContentModelHandler(XMLDTDContentModelHandler handler) { 359 fDTDContentModelHandler = handler; 360 } // setDTDContentModelHandler(XMLDTDContentModelHandler) 361 362 /** Returns the registered DTD content model handler. */ 363 public XMLDTDContentModelHandler getDTDContentModelHandler() { 364 return fDTDContentModelHandler; 365 } // getDTDContentModelHandler():XMLDTDContentModelHandler 366 367 /** 368 * Sets the resolver used to resolve external entities. The EntityResolver 369 * interface supports resolution of public and system identifiers. 370 * 371 * @param resolver The new entity resolver. Passing a null value will 372 * uninstall the currently installed resolver. 373 */ 374 public void setEntityResolver(XMLEntityResolver resolver) { 375 // REVISIT: Should this be a property? 376 fProperties.put(ENTITY_RESOLVER, resolver); 377 } // setEntityResolver(XMLEntityResolver) 378 379 /** 380 * Return the current entity resolver. 381 * 382 * @return The current entity resolver, or null if none 383 * has been registered. 384 * @see #setEntityResolver 385 */ 386 public XMLEntityResolver getEntityResolver() { 387 // REVISIT: Should this be a property? 388 return (XMLEntityResolver)fProperties.get(ENTITY_RESOLVER); 389 } // getEntityResolver():XMLEntityResolver 390 391 /** 392 * Allow an application to register an error event handler. 393 * 394 * <p>If the application does not register an error handler, all 395 * error events reported by the SAX parser will be silently 396 * ignored; however, normal processing may not continue. It is 397 * highly recommended that all SAX applications implement an 398 * error handler to avoid unexpected bugs.</p> 399 * 400 * <p>Applications may register a new or different handler in the 401 * middle of a parse, and the SAX parser must begin using the new 402 * handler immediately.</p> 403 * 404 * @param errorHandler The error handler. 405 * @exception java.lang.NullPointerException If the handler 406 * argument is null. 407 * @see #getErrorHandler 408 */ 409 public void setErrorHandler(XMLErrorHandler errorHandler) { 410 // REVISIT: Should this be a property? 411 fProperties.put(ERROR_HANDLER, errorHandler); 412 } // setErrorHandler(XMLErrorHandler) 413 414 /** 415 * Return the current error handler. 416 * 417 * @return The current error handler, or null if none 418 * has been registered. 419 * @see #setErrorHandler 420 */ 421 public XMLErrorHandler getErrorHandler() { 422 // REVISIT: Should this be a property? 423 return (XMLErrorHandler)fProperties.get(ERROR_HANDLER); 424 } // getErrorHandler():XMLErrorHandler 425 426 /** 427 * Set the state of a feature. 428 * 429 * Set the state of any feature in a SAX2 parser. The parser 430 * might not recognize the feature, and if it does recognize 431 * it, it might not be able to fulfill the request. 432 * 433 * @param featureId The unique identifier (URI) of the feature. 434 * @param state The requested state of the feature (true or false). 435 * 436 * @exception org.apache.xerces.xni.parser.XMLConfigurationException If the 437 * requested feature is not known. 438 */ 439 public void setFeature(String featureId, boolean state) 440 throws XMLConfigurationException { 441 442 // forward to every component 443 int count = fComponents.size(); 444 for (int i = 0; i < count; i++) { 445 XMLComponent c = (XMLComponent) fComponents.get(i); 446 c.setFeature(featureId, state); 447 } 448 // save state if noone "objects" 449 super.setFeature(featureId, state); 450 451 } // setFeature(String,boolean) 452 453 /** 454 * setProperty 455 * 456 * @param propertyId 457 * @param value 458 */ 459 public void setProperty(String propertyId, Object value) 460 throws XMLConfigurationException { 461 462 // forward to every component 463 int count = fComponents.size(); 464 for (int i = 0; i < count; i++) { 465 XMLComponent c = (XMLComponent) fComponents.get(i); 466 c.setProperty(propertyId, value); 467 } 468 469 // store value if noone "objects" 470 super.setProperty(propertyId, value); 471 472 } // setProperty(String,Object) 473 474 /** 475 * Set the locale to use for messages. 476 * 477 * @param locale The locale object to use for localization of messages. 478 * 479 * @exception XNIException Thrown if the parser does not support the 480 * specified locale. 481 */ 482 public void setLocale(Locale locale) throws XNIException { 483 fLocale = locale; 484 } // setLocale(Locale) 485 486 /** Returns the locale. */ 487 public Locale getLocale() { 488 return fLocale; 489 } // getLocale():Locale 490 491 // 492 // Protected methods 493 // 494 495 /** 496 * reset all components before parsing and namespace context 497 */ 498 protected void reset() throws XNIException { 499 500 // reset every component 501 int count = fComponents.size(); 502 for (int i = 0; i < count; i++) { 503 XMLComponent c = (XMLComponent) fComponents.get(i); 504 c.reset(this); 505 } 506 507 } // reset() 508 509 /** 510 * Check a property. If the property is known and supported, this method 511 * simply returns. Otherwise, the appropriate exception is thrown. 512 * 513 * @param propertyId The unique identifier (URI) of the property 514 * being set. 515 * @exception org.apache.xerces.xni.parser.XMLConfigurationException If the 516 * requested feature is not known or supported. 517 */ 518 protected void checkProperty(String propertyId) 519 throws XMLConfigurationException { 520 521 // special cases 522 if (propertyId.startsWith(Constants.SAX_PROPERTY_PREFIX)) { 523 final int suffixLength = propertyId.length() - Constants.SAX_PROPERTY_PREFIX.length(); 524 525 // 526 // http://xml.org/sax/properties/xml-string 527 // Value type: String 528 // Access: read-only 529 // Get the literal string of characters associated with the 530 // current event. If the parser recognises and supports this 531 // property but is not currently parsing text, it should return 532 // null (this is a good way to check for availability before the 533 // parse begins). 534 // 535 if (suffixLength == Constants.XML_STRING_PROPERTY.length() && 536 propertyId.endsWith(Constants.XML_STRING_PROPERTY)) { 537 // REVISIT - we should probably ask xml-dev for a precise 538 // definition of what this is actually supposed to return, and 539 // in exactly which circumstances. 540 short type = XMLConfigurationException.NOT_SUPPORTED; 541 throw new XMLConfigurationException(type, propertyId); 542 } 543 } 544 545 // check property 546 super.checkProperty(propertyId); 547 548 } // checkProperty(String) 549 550 551 /** 552 * Check a feature. If feature is know and supported, this method simply 553 * returns. Otherwise, the appropriate exception is thrown. 554 * 555 * @param featureId The unique identifier (URI) of the feature. 556 * 557 * @throws XMLConfigurationException Thrown for configuration error. 558 * In general, components should 559 * only throw this exception if 560 * it is <strong>really</strong> 561 * a critical error. 562 */ 563 protected void checkFeature(String featureId) 564 throws XMLConfigurationException { 565 566 // 567 // Xerces Features 568 // 569 if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { 570 final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length(); 571 572 // 573 // special performance feature: no one by component manager is allowed to set it 574 // 575 if (suffixLength == Constants.PARSER_SETTINGS.length() && 576 featureId.endsWith(Constants.PARSER_SETTINGS)) { 577 short type = XMLConfigurationException.NOT_SUPPORTED; 578 throw new XMLConfigurationException(type, featureId); 579 } 580 } 581 582 super.checkFeature(featureId); 583 584 } // checkFeature(String) 585 586 587 } // class BasicParserConfiguration