1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xerces.internal.impl.dtd;
  23 
  24 import java.io.EOFException;
  25 import java.io.IOException;
  26 import java.io.StringReader;
  27 import java.util.Locale;
  28 
  29 import com.sun.org.apache.xerces.internal.impl.Constants;
  30 import com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl;
  31 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  32 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
  33 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  34 
  35 import com.sun.org.apache.xerces.internal.util.Status;
  36 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  37 import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler;
  38 
  39 import com.sun.org.apache.xerces.internal.xni.XNIException;
  40 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
  41 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarLoader;
  42 import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
  43 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  44 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  45 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  46 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  47 
  48 
  49 /**
  50  * The DTD loader. The loader knows how to build grammars from XMLInputSources.
  51  * It extends the DTD processor in order to do this; it's
  52  * a separate class because DTD processors don't need to know how
  53  * to talk to the outside world in their role as instance-document
  54  * helpers.
  55  * <p>
  56  * This component requires the following features and properties.  It
  57  * know ho to set them if no one else does:from the
  58  * <ul>
  59  *  <li>http://xml.org/sax/features/namespaces</li>
  60  *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
  61  *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
  62  *  <li>http://apache.org/xml/properties/internal/grammar-pool</li>
  63  *  <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
  64  * </ul>
  65  *
  66  * @xerces.internal
  67  *
  68  * @author Neil Graham, IBM
  69  * @author Michael Glavassevich, IBM
  70  *
  71  * @version $Id: XMLDTDLoader.java,v 1.6 2010-11-01 04:39:42 joehw Exp $
  72  */
  73 public class XMLDTDLoader
  74         extends XMLDTDProcessor
  75         implements XMLGrammarLoader {
  76 
  77     //
  78     // Constants
  79     //
  80 
  81     // feature identifiers
  82 
  83     /** Feature identifier: standard uri conformant feature. */
  84     protected static final String STANDARD_URI_CONFORMANT_FEATURE =
  85         Constants.XERCES_FEATURE_PREFIX + Constants.STANDARD_URI_CONFORMANT_FEATURE;
  86 
  87     /** Feature identifier: balance syntax trees. */
  88     protected static final String BALANCE_SYNTAX_TREES =
  89         Constants.XERCES_FEATURE_PREFIX + Constants.BALANCE_SYNTAX_TREES;
  90 
  91     // recognized features:
  92     private static final String[] LOADER_RECOGNIZED_FEATURES = {
  93         VALIDATION,
  94         WARN_ON_DUPLICATE_ATTDEF,
  95         WARN_ON_UNDECLARED_ELEMDEF,
  96         NOTIFY_CHAR_REFS,
  97         STANDARD_URI_CONFORMANT_FEATURE,
  98         BALANCE_SYNTAX_TREES
  99     };
 100 
 101     // property identifiers
 102 
 103     /** Property identifier: error handler. */
 104     protected static final String ERROR_HANDLER =
 105         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY;
 106 
 107     /** Property identifier: entity resolver. */
 108     public static final String ENTITY_RESOLVER =
 109         Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
 110 
 111     /** Property identifier: locale. */
 112     public static final String LOCALE =
 113         Constants.XERCES_PROPERTY_PREFIX + Constants.LOCALE_PROPERTY;
 114 
 115     /** Recognized properties. */
 116     private static final String[] LOADER_RECOGNIZED_PROPERTIES = {
 117         SYMBOL_TABLE,
 118         ERROR_REPORTER,
 119         ERROR_HANDLER,
 120         ENTITY_RESOLVER,
 121         GRAMMAR_POOL,
 122         DTD_VALIDATOR,
 123         LOCALE
 124     };
 125 
 126     // enforcing strict uri?
 127     private boolean fStrictURI = false;
 128 
 129     /** Controls whether the DTD grammar produces balanced syntax trees. */
 130     private boolean fBalanceSyntaxTrees = false;
 131 
 132     /** Entity resolver . */
 133     protected XMLEntityResolver fEntityResolver;
 134 
 135     // the scanner we use to actually read the DTD
 136     protected XMLDTDScannerImpl fDTDScanner;
 137 
 138     // the entity manager the scanner needs.
 139     protected XMLEntityManager fEntityManager;
 140 
 141     // what's our Locale?
 142     protected Locale fLocale;
 143 
 144     //
 145     // Constructors
 146     //
 147 
 148     /** Deny default construction; we need a SymtolTable! */
 149     public XMLDTDLoader() {
 150         this(new SymbolTable());
 151     } // <init>()
 152 
 153     public XMLDTDLoader(SymbolTable symbolTable) {
 154         this(symbolTable, null);
 155     } // init(SymbolTable)
 156 
 157     public XMLDTDLoader(SymbolTable symbolTable,
 158                 XMLGrammarPool grammarPool) {
 159         this(symbolTable, grammarPool, null, new XMLEntityManager());
 160     } // init(SymbolTable, XMLGrammarPool)
 161 
 162     XMLDTDLoader(SymbolTable symbolTable,
 163                 XMLGrammarPool grammarPool, XMLErrorReporter errorReporter,
 164                 XMLEntityResolver entityResolver) {
 165         fSymbolTable = symbolTable;
 166         fGrammarPool = grammarPool;
 167         if(errorReporter == null) {
 168             errorReporter = new XMLErrorReporter();
 169             errorReporter.setProperty(ERROR_HANDLER, new DefaultErrorHandler());
 170         }
 171         fErrorReporter = errorReporter;
 172         // Add XML message formatter if there isn't one.
 173         if (fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) {
 174             XMLMessageFormatter xmft = new XMLMessageFormatter();
 175             fErrorReporter.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft);
 176             fErrorReporter.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft);
 177         }
 178         fEntityResolver = entityResolver;
 179         if(fEntityResolver instanceof XMLEntityManager) {
 180             fEntityManager = (XMLEntityManager)fEntityResolver;
 181         } else {
 182             fEntityManager = new XMLEntityManager();
 183         }
 184         fEntityManager.setProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY, errorReporter);
 185         fDTDScanner = createDTDScanner(fSymbolTable, fErrorReporter, fEntityManager);
 186         fDTDScanner.setDTDHandler(this);
 187         fDTDScanner.setDTDContentModelHandler(this);
 188         reset();
 189     } // init(SymbolTable, XMLGrammarPool, XMLErrorReporter, XMLEntityResolver)
 190 
 191     // XMLGrammarLoader methods
 192 
 193     /**
 194      * Returns a list of feature identifiers that are recognized by
 195      * this component. This method may return null if no features
 196      * are recognized by this component.
 197      */
 198     public String[] getRecognizedFeatures() {
 199         return (String[])(LOADER_RECOGNIZED_FEATURES.clone());
 200     } // getRecognizedFeatures():String[]
 201 
 202     /**
 203      * Sets the state of a feature. This method is called by the component
 204      * manager any time after reset when a feature changes state.
 205      * <p>
 206      * <strong>Note:</strong> Components should silently ignore features
 207      * that do not affect the operation of the component.
 208      *
 209      * @param featureId The feature identifier.
 210      * @param state     The state of the feature.
 211      *
 212      * @throws SAXNotRecognizedException The component should not throw
 213      *                                   this exception.
 214      * @throws SAXNotSupportedException The component should not throw
 215      *                                  this exception.
 216      */
 217     public void setFeature(String featureId, boolean state)
 218             throws XMLConfigurationException {
 219         if (featureId.equals(VALIDATION)) {
 220             fValidation = state;
 221         }
 222         else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) {
 223             fWarnDuplicateAttdef = state;
 224         }
 225         else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) {
 226             fWarnOnUndeclaredElemdef = state;
 227         }
 228         else if (featureId.equals(NOTIFY_CHAR_REFS)) {
 229             fDTDScanner.setFeature(featureId, state);
 230         }
 231         else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) {
 232             fStrictURI = state;
 233         }
 234         else if (featureId.equals(BALANCE_SYNTAX_TREES)) {
 235             fBalanceSyntaxTrees = state;
 236         }
 237         else {
 238             throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId);
 239         }
 240     } // setFeature(String,boolean)
 241 
 242     /**
 243      * Returns a list of property identifiers that are recognized by
 244      * this component. This method may return null if no properties
 245      * are recognized by this component.
 246      */
 247     public String[] getRecognizedProperties() {
 248         return (String[])(LOADER_RECOGNIZED_PROPERTIES.clone());
 249     } // getRecognizedProperties():String[]
 250 
 251     /**
 252      * Returns the state of a property.
 253      *
 254      * @param propertyId The property identifier.
 255      *
 256      * @throws XMLConfigurationException Thrown on configuration error.
 257      */
 258     public Object getProperty(String propertyId)
 259             throws XMLConfigurationException {
 260         if (propertyId.equals(SYMBOL_TABLE)) {
 261             return fSymbolTable;
 262         }
 263         else if (propertyId.equals(ERROR_REPORTER)) {
 264             return fErrorReporter;
 265         }
 266         else if (propertyId.equals(ERROR_HANDLER)) {
 267             return fErrorReporter.getErrorHandler();
 268         }
 269         else if (propertyId.equals(ENTITY_RESOLVER)) {
 270             return fEntityResolver;
 271         }
 272         else if (propertyId.equals(LOCALE)) {
 273             return getLocale();
 274         }
 275         else if (propertyId.equals(GRAMMAR_POOL)) {
 276             return fGrammarPool;
 277         }
 278         else if (propertyId.equals(DTD_VALIDATOR)) {
 279             return fValidator;
 280         }
 281         throw new XMLConfigurationException(Status.NOT_RECOGNIZED, propertyId);
 282     } // getProperty(String):  Object
 283 
 284     /**
 285      * Sets the value of a property. This method is called by the component
 286      * manager any time after reset when a property changes value.
 287      * <p>
 288      * <strong>Note:</strong> Components should silently ignore properties
 289      * that do not affect the operation of the component.
 290      *
 291      * @param propertyId The property identifier.
 292      * @param value      The value of the property.
 293      *
 294      * @throws SAXNotRecognizedException The component should not throw
 295      *                                   this exception.
 296      * @throws SAXNotSupportedException The component should not throw
 297      *                                  this exception.
 298      */
 299     public void setProperty(String propertyId, Object value)
 300             throws XMLConfigurationException {
 301         if (propertyId.equals(SYMBOL_TABLE)) {
 302             fSymbolTable = (SymbolTable)value;
 303             fDTDScanner.setProperty(propertyId, value);
 304             fEntityManager.setProperty(propertyId, value);
 305         }
 306         else if(propertyId.equals(ERROR_REPORTER)) {
 307             fErrorReporter = (XMLErrorReporter)value;
 308             // Add XML message formatter if there isn't one.
 309             if (fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) {
 310                 XMLMessageFormatter xmft = new XMLMessageFormatter();
 311                 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft);
 312                 fErrorReporter.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft);
 313             }
 314             fDTDScanner.setProperty(propertyId, value);
 315             fEntityManager.setProperty(propertyId, value);
 316         }
 317         else if (propertyId.equals(ERROR_HANDLER)) {
 318             fErrorReporter.setProperty(propertyId, value);
 319         }
 320         else if (propertyId.equals(ENTITY_RESOLVER)) {
 321             fEntityResolver = (XMLEntityResolver)value;
 322             fEntityManager.setProperty(propertyId, value);
 323         }
 324         else if (propertyId.equals(LOCALE)) {
 325             setLocale((Locale) value);
 326         }
 327         else if(propertyId.equals(GRAMMAR_POOL)) {
 328             fGrammarPool = (XMLGrammarPool)value;
 329         }
 330         else {
 331             throw new XMLConfigurationException(Status.NOT_RECOGNIZED, propertyId);
 332         }
 333     } // setProperty(String,Object)
 334 
 335     /**
 336      * Returns the state of a feature.
 337      *
 338      * @param featureId The feature identifier.
 339      *
 340      * @throws XMLConfigurationException Thrown on configuration error.
 341      */
 342     public boolean getFeature(String featureId)
 343             throws XMLConfigurationException {
 344         if (featureId.equals(VALIDATION)) {
 345             return fValidation;
 346         }
 347         else if (featureId.equals(WARN_ON_DUPLICATE_ATTDEF)) {
 348             return fWarnDuplicateAttdef;
 349         }
 350         else if (featureId.equals(WARN_ON_UNDECLARED_ELEMDEF)) {
 351             return fWarnOnUndeclaredElemdef;
 352         }
 353         else if (featureId.equals(NOTIFY_CHAR_REFS)) {
 354             return fDTDScanner.getFeature(featureId);
 355         }
 356         else if (featureId.equals(STANDARD_URI_CONFORMANT_FEATURE)) {
 357             return fStrictURI;
 358         }
 359         else if (featureId.equals(BALANCE_SYNTAX_TREES)) {
 360             return fBalanceSyntaxTrees;
 361         }
 362         throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId);
 363     } //getFeature(String):  boolean
 364 
 365     /**
 366      * Set the locale to use for messages.
 367      *
 368      * @param locale The locale object to use for localization of messages.
 369      *
 370      * @exception XNIException Thrown if the parser does not support the
 371      *                         specified locale.
 372      */
 373     public void setLocale(Locale locale) {
 374         fLocale = locale;
 375         fErrorReporter.setLocale(locale);
 376     } // setLocale(Locale)
 377 
 378     /** Return the Locale the XMLGrammarLoader is using. */
 379     public Locale getLocale() {
 380         return fLocale;
 381     } // getLocale():  Locale
 382 
 383 
 384     /**
 385      * Sets the error handler.
 386      *
 387      * @param errorHandler The error handler.
 388      */
 389     public void setErrorHandler(XMLErrorHandler errorHandler) {
 390         fErrorReporter.setProperty(ERROR_HANDLER, errorHandler);
 391     } // setErrorHandler(XMLErrorHandler)
 392 
 393     /** Returns the registered error handler.  */
 394     public XMLErrorHandler getErrorHandler() {
 395         return fErrorReporter.getErrorHandler();
 396     } // getErrorHandler():  XMLErrorHandler
 397 
 398     /**
 399      * Sets the entity resolver.
 400      *
 401      * @param entityResolver The new entity resolver.
 402      */
 403     public void setEntityResolver(XMLEntityResolver entityResolver) {
 404         fEntityResolver = entityResolver;
 405         fEntityManager.setProperty(ENTITY_RESOLVER, entityResolver);
 406     } // setEntityResolver(XMLEntityResolver)
 407 
 408     /** Returns the registered entity resolver.  */
 409     public XMLEntityResolver getEntityResolver() {
 410         return fEntityResolver;
 411     } // getEntityResolver():  XMLEntityResolver
 412 
 413     /**
 414      * Returns a Grammar object by parsing the contents of the
 415      * entity pointed to by source.
 416      *
 417      * @param source        the location of the entity which forms
 418      *                          the starting point of the grammar to be constructed.
 419      * @throws IOException      When a problem is encountered reading the entity
 420      *          XNIException    When a condition arises (such as a FatalError) that requires parsing
 421      *                              of the entity be terminated.
 422      */
 423     public Grammar loadGrammar(XMLInputSource source)
 424             throws IOException, XNIException {
 425         reset();
 426         // First chance checking strict URI
 427         String eid = XMLEntityManager.expandSystemId(source.getSystemId(), source.getBaseSystemId(), fStrictURI);
 428         XMLDTDDescription desc = new XMLDTDDescription(source.getPublicId(), source.getSystemId(), source.getBaseSystemId(), eid, null);
 429         if (!fBalanceSyntaxTrees) {
 430             fDTDGrammar = new DTDGrammar(fSymbolTable, desc);
 431         }
 432         else {
 433             fDTDGrammar = new BalancedDTDGrammar(fSymbolTable, desc);
 434         }
 435         fGrammarBucket = new DTDGrammarBucket();
 436         fGrammarBucket.setStandalone(false);
 437         fGrammarBucket.setActiveGrammar(fDTDGrammar);
 438         // no reason to use grammar bucket's "put" method--we
 439         // know which grammar it is, and we don't know the root name anyway...
 440 
 441         // actually start the parsing!
 442         try {
 443             fDTDScanner.setInputSource(source);
 444             fDTDScanner.scanDTDExternalSubset(true);
 445         } catch (EOFException e) {
 446             // expected behaviour...
 447         }
 448         finally {
 449             // Close all streams opened by the parser.
 450             fEntityManager.closeReaders();
 451         }
 452         if(fDTDGrammar != null && fGrammarPool != null) {
 453             fGrammarPool.cacheGrammars(XMLDTDDescription.XML_DTD, new Grammar[] {fDTDGrammar});
 454         }
 455         return fDTDGrammar;
 456     } // loadGrammar(XMLInputSource):  Grammar
 457 
 458     /**
 459      * Parse a DTD internal and/or external subset and insert the content
 460      * into the existing DTD grammar owned by the given DTDValidator.
 461      */
 462     public void loadGrammarWithContext(XMLDTDValidator validator, String rootName,
 463             String publicId, String systemId, String baseSystemId, String internalSubset)
 464         throws IOException, XNIException {
 465         final DTDGrammarBucket grammarBucket = validator.getGrammarBucket();
 466         final DTDGrammar activeGrammar = grammarBucket.getActiveGrammar();
 467         if (activeGrammar != null && !activeGrammar.isImmutable()) {
 468             fGrammarBucket = grammarBucket;
 469             fEntityManager.setScannerVersion(getScannerVersion());
 470             reset();
 471             try {
 472                 // process internal subset
 473                 if (internalSubset != null) {
 474                     // To get the DTD scanner to end at the right place we have to fool
 475                     // it into thinking that it reached the end of the internal subset
 476                     // in a real document.
 477                     StringBuffer buffer = new StringBuffer(internalSubset.length() + 2);
 478                     buffer.append(internalSubset).append("]>");
 479                     XMLInputSource is = new XMLInputSource(null, baseSystemId,
 480                             null, new StringReader(buffer.toString()), null);
 481                     fEntityManager.startDocumentEntity(is);
 482                     fDTDScanner.scanDTDInternalSubset(true, false, systemId != null);
 483                 }
 484                 // process external subset
 485                 if (systemId != null) {
 486                     XMLDTDDescription desc = new XMLDTDDescription(publicId, systemId, baseSystemId, null, rootName);
 487                     XMLInputSource source = fEntityManager.resolveEntity(desc);
 488                     fDTDScanner.setInputSource(source);
 489                     fDTDScanner.scanDTDExternalSubset(true);
 490                 }
 491             }
 492             catch (EOFException e) {
 493                 // expected behaviour...
 494             }
 495             finally {
 496                 // Close all streams opened by the parser.
 497                 fEntityManager.closeReaders();
 498             }
 499         }
 500     } // loadGrammarWithContext(XMLDTDValidator, String, String, String, String, String)
 501 
 502     // reset all the components that we rely upon
 503     protected void reset() {
 504         super.reset();
 505         fDTDScanner.reset();
 506         fEntityManager.reset();
 507         fErrorReporter.setDocumentLocator(fEntityManager.getEntityScanner());
 508     }
 509 
 510     protected XMLDTDScannerImpl createDTDScanner(SymbolTable symbolTable,
 511             XMLErrorReporter errorReporter, XMLEntityManager entityManager) {
 512         return new XMLDTDScannerImpl(symbolTable, errorReporter, entityManager);
 513     } // createDTDScanner(SymbolTable, XMLErrorReporter, XMLEntityManager) : XMLDTDScannerImpl
 514 
 515     protected short getScannerVersion() {
 516         return Constants.XML_VERSION_1_0;
 517     } // getScannerVersion() : short
 518 
 519 } // class XMLDTDLoader