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