1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Nov 2017
   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 com.sun.org.apache.xerces.internal.impl.Constants;
  25 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  26 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  27 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  28 import com.sun.org.apache.xerces.internal.util.XMLChar;
  29 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  30 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  31 import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler;
  32 import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
  33 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  34 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  35 import com.sun.org.apache.xerces.internal.xni.XMLString;
  36 import com.sun.org.apache.xerces.internal.xni.XNIException;
  37 import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
  38 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
  39 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
  40 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  41 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  42 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  43 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDContentModelFilter;
  44 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDContentModelSource;
  45 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDFilter;
  46 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDSource;
  47 import java.util.ArrayList;
  48 import java.util.HashMap;
  49 import java.util.List;
  50 import java.util.Locale;
  51 import java.util.Map;
  52 import java.util.StringTokenizer;
  53 
  54 /**
  55  * The DTD processor. The processor implements a DTD
  56  * filter: receiving DTD events from the DTD scanner; validating
  57  * the content and structure; building a grammar, if applicable;
  58  * and notifying the DTDHandler of the information resulting from the
  59  * process.
  60  * <p>
  61  * This component requires the following features and properties from the
  62  * component manager that uses it:
  63  * <ul>
  64  *  <li>http://xml.org/sax/features/namespaces</li>
  65  *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
  66  *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
  67  *  <li>http://apache.org/xml/properties/internal/grammar-pool</li>
  68  *  <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
  69  * </ul>
  70  *
  71  * @xerces.internal
  72  *
  73  * @author Neil Graham, IBM
  74  *
  75  */
  76 public class XMLDTDProcessor
  77         implements XMLComponent, XMLDTDFilter, XMLDTDContentModelFilter {
  78 
  79     //
  80     // Constants
  81     //
  82 
  83     /** Top level scope (-1). */
  84     private static final int TOP_LEVEL_SCOPE = -1;
  85 
  86     // feature identifiers
  87 
  88     /** Feature identifier: validation. */
  89     protected static final String VALIDATION =
  90         Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
  91 
  92     /** Feature identifier: notify character references. */
  93     protected static final String NOTIFY_CHAR_REFS =
  94         Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_CHAR_REFS_FEATURE;
  95 
  96     /** Feature identifier: warn on duplicate attdef */
  97     protected static final String WARN_ON_DUPLICATE_ATTDEF =
  98         Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ATTDEF_FEATURE;
  99 
 100     /** Feature identifier: warn on undeclared element referenced in content model. */
 101     protected static final String WARN_ON_UNDECLARED_ELEMDEF =
 102         Constants.XERCES_FEATURE_PREFIX + Constants.WARN_ON_UNDECLARED_ELEMDEF_FEATURE;
 103 
 104         protected static final String PARSER_SETTINGS =
 105         Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
 106 
 107     // property identifiers
 108 
 109     /** Property identifier: symbol table. */
 110     protected static final String SYMBOL_TABLE =
 111         Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
 112 
 113     /** Property identifier: error reporter. */
 114     protected static final String ERROR_REPORTER =
 115         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
 116 
 117     /** Property identifier: grammar pool. */
 118     protected static final String GRAMMAR_POOL =
 119         Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
 120 
 121     /** Property identifier: validator . */
 122     protected static final String DTD_VALIDATOR =
 123         Constants.XERCES_PROPERTY_PREFIX + Constants.DTD_VALIDATOR_PROPERTY;
 124 
 125     // recognized features and properties
 126 
 127     /** Recognized features. */
 128     private static final String[] RECOGNIZED_FEATURES = {
 129         VALIDATION,
 130         WARN_ON_DUPLICATE_ATTDEF,
 131         WARN_ON_UNDECLARED_ELEMDEF,
 132         NOTIFY_CHAR_REFS,
 133     };
 134 
 135     /** Feature defaults. */
 136     private static final Boolean[] FEATURE_DEFAULTS = {
 137         null,
 138         Boolean.FALSE,
 139         Boolean.FALSE,
 140         null,
 141     };
 142 
 143     /** Recognized properties. */
 144     private static final String[] RECOGNIZED_PROPERTIES = {
 145         SYMBOL_TABLE,
 146         ERROR_REPORTER,
 147         GRAMMAR_POOL,
 148         DTD_VALIDATOR,
 149     };
 150 
 151     /** Property defaults. */
 152     private static final Object[] PROPERTY_DEFAULTS = {
 153         null,
 154         null,
 155         null,
 156         null,
 157     };
 158 
 159     // debugging
 160 
 161     //
 162     // Data
 163     //
 164 
 165     // features
 166 
 167     /** Validation. */
 168     protected boolean fValidation;
 169 
 170     /** Validation against only DTD */
 171     protected boolean fDTDValidation;
 172 
 173     /** warn on duplicate attribute definition, this feature works only when validation is true */
 174     protected boolean fWarnDuplicateAttdef;
 175 
 176     /** warn on undeclared element referenced in content model, this feature only works when valiation is true */
 177     protected boolean fWarnOnUndeclaredElemdef;
 178 
 179     // properties
 180 
 181     /** Symbol table. */
 182     protected SymbolTable fSymbolTable;
 183 
 184     /** Error reporter. */
 185     protected XMLErrorReporter fErrorReporter;
 186 
 187     /** Grammar bucket. */
 188     protected DTDGrammarBucket fGrammarBucket;
 189 
 190     // the validator to which we look for our grammar bucket (the
 191     // validator needs to hold the bucket so that it can initialize
 192     // the grammar with details like whether it's for a standalone document...
 193     protected XMLDTDValidator fValidator;
 194 
 195     // the grammar pool we'll try to add the grammar to:
 196     protected XMLGrammarPool fGrammarPool;
 197 
 198     // what's our Locale?
 199     protected Locale fLocale;
 200 
 201     // handlers
 202 
 203     /** DTD handler. */
 204     protected XMLDTDHandler fDTDHandler;
 205 
 206     /** DTD source. */
 207     protected XMLDTDSource fDTDSource;
 208 
 209     /** DTD content model handler. */
 210     protected XMLDTDContentModelHandler fDTDContentModelHandler;
 211 
 212     /** DTD content model source. */
 213     protected XMLDTDContentModelSource fDTDContentModelSource;
 214 
 215     // grammars
 216 
 217     /** DTD Grammar. */
 218     protected DTDGrammar fDTDGrammar;
 219 
 220     // state
 221 
 222     /** Perform validation. */
 223     private boolean fPerformValidation;
 224 
 225     /** True if in an ignore conditional section of the DTD. */
 226     protected boolean fInDTDIgnore;
 227 
 228     // information regarding the current element
 229 
 230     // validation states
 231 
 232     /** Mixed. */
 233     private boolean fMixed;
 234 
 235     // temporary variables
 236 
 237     /** Temporary entity declaration. */
 238     private final XMLEntityDecl fEntityDecl = new XMLEntityDecl();
 239 
 240     /** Notation declaration hash. */
 241     private final Map<String, String> fNDataDeclNotations = new HashMap<>();
 242 
 243     /** DTD element declaration name. */
 244     private String fDTDElementDeclName = null;
 245 
 246     /** Mixed element type "hash". */
 247     private final List<String> fMixedElementTypes = new ArrayList<>();
 248 
 249     /** Element declarations in DTD. */
 250     private final List<String> fDTDElementDecls = new ArrayList<>();
 251 
 252     // to check for duplicate ID or ANNOTATION attribute declare in
 253     // ATTLIST, and misc VCs
 254 
 255     /** ID attribute names. */
 256     private Map<String, String> fTableOfIDAttributeNames;
 257 
 258     /** NOTATION attribute names. */
 259     private Map<String, String> fTableOfNOTATIONAttributeNames;
 260 
 261     /** NOTATION enumeration values. */
 262     private Map<String, String> fNotationEnumVals;
 263 
 264     //
 265     // Constructors
 266     //
 267 
 268     /** Default constructor. */
 269     public XMLDTDProcessor() {
 270 
 271         // initialize data
 272 
 273     } // <init>()
 274 
 275     //
 276     // XMLComponent methods
 277     //
 278 
 279     /*
 280      * Resets the component. The component can query the component manager
 281      * about any features and properties that affect the operation of the
 282      * component.
 283      *
 284      * @param componentManager The component manager.
 285      *
 286      * @throws SAXException Thrown by component on finitialization error.
 287      *                      For example, if a feature or property is
 288      *                      required for the operation of the component, the
 289      *                      component manager may throw a
 290      *                      SAXNotRecognizedException or a
 291      *                      SAXNotSupportedException.
 292      */
 293     public void reset(XMLComponentManager componentManager) throws XMLConfigurationException {
 294 
 295         boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true);
 296 
 297         if (!parser_settings) {
 298             // parser settings have not been changed
 299             reset();
 300             return;
 301         }
 302 
 303         // sax features
 304         fValidation = componentManager.getFeature(VALIDATION, false);
 305 
 306         fDTDValidation =
 307                 !(componentManager
 308                     .getFeature(
 309                         Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE, false));
 310 
 311         // Xerces features
 312 
 313         fWarnDuplicateAttdef = componentManager.getFeature(WARN_ON_DUPLICATE_ATTDEF, false);
 314         fWarnOnUndeclaredElemdef = componentManager.getFeature(WARN_ON_UNDECLARED_ELEMDEF, false);
 315 
 316         // get needed components
 317         fErrorReporter =
 318             (XMLErrorReporter) componentManager.getProperty(
 319                 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY);
 320         fSymbolTable =
 321             (SymbolTable) componentManager.getProperty(
 322                 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY);
 323 
 324         fGrammarPool = (XMLGrammarPool) componentManager.getProperty(GRAMMAR_POOL, null);
 325 
 326         try {
 327             fValidator = (XMLDTDValidator) componentManager.getProperty(DTD_VALIDATOR, null);
 328         } catch (ClassCastException e) {
 329             fValidator = null;
 330         }
 331         // we get our grammarBucket from the validator...
 332         if (fValidator != null) {
 333             fGrammarBucket = fValidator.getGrammarBucket();
 334         } else {
 335             fGrammarBucket = null;
 336         }
 337         reset();
 338 
 339     } // reset(XMLComponentManager)
 340 
 341     protected void reset() {
 342         // clear grammars
 343         fDTDGrammar = null;
 344         // initialize state
 345         fInDTDIgnore = false;
 346 
 347         fNDataDeclNotations.clear();
 348 
 349         // datatype validators
 350         if (fValidation) {
 351 
 352             if (fNotationEnumVals == null) {
 353                 fNotationEnumVals = new HashMap<>();
 354             }
 355             fNotationEnumVals.clear();
 356 
 357             fTableOfIDAttributeNames = new HashMap<>();
 358             fTableOfNOTATIONAttributeNames = new HashMap<>();
 359         }
 360 
 361     }
 362     /**
 363      * Returns a list of feature identifiers that are recognized by
 364      * this component. This method may return null if no features
 365      * are recognized by this component.
 366      */
 367     public String[] getRecognizedFeatures() {
 368         return RECOGNIZED_FEATURES.clone();
 369     } // getRecognizedFeatures():String[]
 370 
 371     /**
 372      * Sets the state of a feature. This method is called by the component
 373      * manager any time after reset when a feature changes state.
 374      * <p>
 375      * <strong>Note:</strong> Components should silently ignore features
 376      * that do not affect the operation of the component.
 377      *
 378      * @param featureId The feature identifier.
 379      * @param state     The state of the feature.
 380      *
 381      * @throws SAXNotRecognizedException The component should not throw
 382      *                                   this exception.
 383      * @throws SAXNotSupportedException The component should not throw
 384      *                                  this exception.
 385      */
 386     public void setFeature(String featureId, boolean state)
 387             throws XMLConfigurationException {
 388     } // setFeature(String,boolean)
 389 
 390     /**
 391      * Returns a list of property identifiers that are recognized by
 392      * this component. This method may return null if no properties
 393      * are recognized by this component.
 394      */
 395     public String[] getRecognizedProperties() {
 396         return RECOGNIZED_PROPERTIES.clone();
 397     } // getRecognizedProperties():String[]
 398 
 399     /**
 400      * Sets the value of a property. This method is called by the component
 401      * manager any time after reset when a property changes value.
 402      * <p>
 403      * <strong>Note:</strong> Components should silently ignore properties
 404      * that do not affect the operation of the component.
 405      *
 406      * @param propertyId The property identifier.
 407      * @param value      The value of the property.
 408      *
 409      * @throws SAXNotRecognizedException The component should not throw
 410      *                                   this exception.
 411      * @throws SAXNotSupportedException The component should not throw
 412      *                                  this exception.
 413      */
 414     public void setProperty(String propertyId, Object value)
 415             throws XMLConfigurationException {
 416     } // setProperty(String,Object)
 417 
 418     /**
 419      * Returns the default state for a feature, or null if this
 420      * component does not want to report a default value for this
 421      * feature.
 422      *
 423      * @param featureId The feature identifier.
 424      *
 425      * @since Xerces 2.2.0
 426      */
 427     public Boolean getFeatureDefault(String featureId) {
 428         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
 429             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
 430                 return FEATURE_DEFAULTS[i];
 431             }
 432         }
 433         return null;
 434     } // getFeatureDefault(String):Boolean
 435 
 436     /**
 437      * Returns the default state for a property, or null if this
 438      * component does not want to report a default value for this
 439      * property.
 440      *
 441      * @param propertyId The property identifier.
 442      *
 443      * @since Xerces 2.2.0
 444      */
 445     public Object getPropertyDefault(String propertyId) {
 446         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
 447             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
 448                 return PROPERTY_DEFAULTS[i];
 449             }
 450         }
 451         return null;
 452     } // getPropertyDefault(String):Object
 453 
 454     //
 455     // XMLDTDSource methods
 456     //
 457 
 458     /**
 459      * Sets the DTD handler.
 460      *
 461      * @param dtdHandler The DTD handler.
 462      */
 463     public void setDTDHandler(XMLDTDHandler dtdHandler) {
 464         fDTDHandler = dtdHandler;
 465     } // setDTDHandler(XMLDTDHandler)
 466 
 467     /**
 468      * Returns the DTD handler.
 469      *
 470      * @return The DTD handler.
 471      */
 472     public XMLDTDHandler getDTDHandler() {
 473         return fDTDHandler;
 474     } // getDTDHandler():  XMLDTDHandler
 475 
 476     //
 477     // XMLDTDContentModelSource methods
 478     //
 479 
 480     /**
 481      * Sets the DTD content model handler.
 482      *
 483      * @param dtdContentModelHandler The DTD content model handler.
 484      */
 485     public void setDTDContentModelHandler(XMLDTDContentModelHandler dtdContentModelHandler) {
 486         fDTDContentModelHandler = dtdContentModelHandler;
 487     } // setDTDContentModelHandler(XMLDTDContentModelHandler)
 488 
 489     /**
 490      * Gets the DTD content model handler.
 491      *
 492      * @return dtdContentModelHandler The DTD content model handler.
 493      */
 494     public XMLDTDContentModelHandler getDTDContentModelHandler() {
 495         return fDTDContentModelHandler;
 496     } // getDTDContentModelHandler():  XMLDTDContentModelHandler
 497 
 498     //
 499     // XMLDTDContentModelHandler and XMLDTDHandler methods
 500     //
 501 
 502     /**
 503      * The start of the DTD external subset.
 504      *
 505      * @param augs Additional information that may include infoset
 506      *                      augmentations.
 507      *
 508      * @throws XNIException Thrown by handler to signal an error.
 509      */
 510     public void startExternalSubset(XMLResourceIdentifier identifier,
 511                                     Augmentations augs) throws XNIException {
 512         if(fDTDGrammar != null)
 513             fDTDGrammar.startExternalSubset(identifier, augs);
 514         if(fDTDHandler != null){
 515             fDTDHandler.startExternalSubset(identifier, augs);
 516         }
 517     }
 518 
 519     /**
 520      * The end of the DTD external subset.
 521      *
 522      * @param augs Additional information that may include infoset
 523      *                      augmentations.
 524      *
 525      * @throws XNIException Thrown by handler to signal an error.
 526      */
 527     public void endExternalSubset(Augmentations augs) throws XNIException {
 528         if(fDTDGrammar != null)
 529             fDTDGrammar.endExternalSubset(augs);
 530         if(fDTDHandler != null){
 531             fDTDHandler.endExternalSubset(augs);
 532         }
 533     }
 534 
 535     /**
 536      * Check standalone entity reference.
 537      * Made static to make common between the validator and loader.
 538      *
 539      * @param name
 540      *@param grammar    grammar to which entity belongs
 541      * @param tempEntityDecl    empty entity declaration to put results in
 542      * @param errorReporter     error reporter to send errors to
 543      *
 544      * @throws XNIException Thrown by application to signal an error.
 545      */
 546     protected static void checkStandaloneEntityRef(String name, DTDGrammar grammar,
 547                     XMLEntityDecl tempEntityDecl, XMLErrorReporter errorReporter) throws XNIException {
 548         // check VC: Standalone Document Declartion, entities references appear in the document.
 549         int entIndex = grammar.getEntityDeclIndex(name);
 550         if (entIndex > -1) {
 551             grammar.getEntityDecl(entIndex, tempEntityDecl);
 552             if (tempEntityDecl.inExternal) {
 553                 errorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
 554                                             "MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
 555                                             new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
 556             }
 557         }
 558     }
 559 
 560     /**
 561      * A comment.
 562      *
 563      * @param text The text in the comment.
 564      * @param augs   Additional information that may include infoset augmentations
 565      *
 566      * @throws XNIException Thrown by application to signal an error.
 567      */
 568     public void comment(XMLString text, Augmentations augs) throws XNIException {
 569 
 570         // call handlers
 571         if(fDTDGrammar != null)
 572             fDTDGrammar.comment(text, augs);
 573         if (fDTDHandler != null) {
 574             fDTDHandler.comment(text, augs);
 575         }
 576 
 577     } // comment(XMLString)
 578 
 579 
 580     /**
 581      * A processing instruction. Processing instructions consist of a
 582      * target name and, optionally, text data. The data is only meaningful
 583      * to the application.
 584      * <p>
 585      * Typically, a processing instruction's data will contain a series
 586      * of pseudo-attributes. These pseudo-attributes follow the form of
 587      * element attributes but are <strong>not</strong> parsed or presented
 588      * to the application as anything other than text. The application is
 589      * responsible for parsing the data.
 590      *
 591      * @param target The target.
 592      * @param data   The data or null if none specified.
 593      * @param augs   Additional information that may include infoset augmentations
 594      *
 595      * @throws XNIException Thrown by handler to signal an error.
 596      */
 597     public void processingInstruction(String target, XMLString data, Augmentations augs)
 598     throws XNIException {
 599 
 600         // call handlers
 601         if(fDTDGrammar != null)
 602             fDTDGrammar.processingInstruction(target, data, augs);
 603         if (fDTDHandler != null) {
 604             fDTDHandler.processingInstruction(target, data, augs);
 605         }
 606     } // processingInstruction(String,XMLString)
 607 
 608     //
 609     // XMLDTDHandler methods
 610     //
 611 
 612     /**
 613      * The start of the DTD.
 614      *
 615      * @param locator  The document locator, or null if the document
 616      *                 location cannot be reported during the parsing of
 617      *                 the document DTD. However, it is <em>strongly</em>
 618      *                 recommended that a locator be supplied that can
 619      *                 at least report the base system identifier of the
 620      *                 DTD.
 621      * @param augs Additional information that may include infoset
 622      *                      augmentations.
 623      *
 624      * @throws XNIException Thrown by handler to signal an error.
 625      */
 626     public void startDTD(XMLLocator locator, Augmentations augs) throws XNIException {
 627 
 628 
 629         // initialize state
 630         fNDataDeclNotations.clear();
 631         fDTDElementDecls.clear();
 632 
 633         // the grammar bucket's DTDGrammar will now be the
 634         // one we want, whether we're constructing it or not.
 635         // if we're not constructing it, then we should not have a reference
 636         // to it!
 637        if( !fGrammarBucket.getActiveGrammar().isImmutable()) {
 638             fDTDGrammar = fGrammarBucket.getActiveGrammar();
 639         }
 640 
 641         // call handlers
 642         if(fDTDGrammar != null )
 643             fDTDGrammar.startDTD(locator, augs);
 644         if (fDTDHandler != null) {
 645             fDTDHandler.startDTD(locator, augs);
 646         }
 647 
 648     } // startDTD(XMLLocator)
 649 
 650     /**
 651      * Characters within an IGNORE conditional section.
 652      *
 653      * @param text The ignored text.
 654      * @param augs Additional information that may include infoset
 655      *                      augmentations.
 656      *
 657      * @throws XNIException Thrown by handler to signal an error.
 658      */
 659     public void ignoredCharacters(XMLString text, Augmentations augs) throws XNIException {
 660 
 661         // ignored characters in DTD
 662         if(fDTDGrammar != null )
 663             fDTDGrammar.ignoredCharacters(text, augs);
 664         if (fDTDHandler != null) {
 665             fDTDHandler.ignoredCharacters(text, augs);
 666         }
 667     }
 668 
 669     /**
 670      * Notifies of the presence of a TextDecl line in an entity. If present,
 671      * this method will be called immediately following the startParameterEntity call.
 672      * <p>
 673      * <strong>Note:</strong> This method is only called for external
 674      * parameter entities referenced in the DTD.
 675      *
 676      * @param version  The XML version, or null if not specified.
 677      * @param encoding The IANA encoding name of the entity.
 678      * @param augs Additional information that may include infoset
 679      *                      augmentations.
 680      *
 681      * @throws XNIException Thrown by handler to signal an error.
 682      */
 683     public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {
 684 
 685         // call handlers
 686         if(fDTDGrammar != null )
 687             fDTDGrammar.textDecl(version, encoding, augs);
 688         if (fDTDHandler != null) {
 689             fDTDHandler.textDecl(version, encoding, augs);
 690         }
 691     }
 692 
 693     /**
 694      * This method notifies of the start of a parameter entity. The parameter
 695      * entity name start with a '%' character.
 696      *
 697      * @param name     The name of the parameter entity.
 698      * @param identifier The resource identifier.
 699      * @param encoding The auto-detected IANA encoding name of the entity
 700      *                 stream. This value will be null in those situations
 701      *                 where the entity encoding is not auto-detected (e.g.
 702      *                 internal parameter entities).
 703      * @param augs Additional information that may include infoset
 704      *                      augmentations.
 705      *
 706      * @throws XNIException Thrown by handler to signal an error.
 707      */
 708     public void startParameterEntity(String name,
 709                                      XMLResourceIdentifier identifier,
 710                                      String encoding,
 711                                      Augmentations augs) throws XNIException {
 712 
 713         if (fPerformValidation && fDTDGrammar != null &&
 714                 fGrammarBucket.getStandalone()) {
 715             checkStandaloneEntityRef(name, fDTDGrammar, fEntityDecl, fErrorReporter);
 716         }
 717         // call handlers
 718         if(fDTDGrammar != null )
 719             fDTDGrammar.startParameterEntity(name, identifier, encoding, augs);
 720         if (fDTDHandler != null) {
 721             fDTDHandler.startParameterEntity(name, identifier, encoding, augs);
 722         }
 723     }
 724 
 725     /**
 726      * This method notifies the end of a parameter entity. Parameter entity
 727      * names begin with a '%' character.
 728      *
 729      * @param name The name of the parameter entity.
 730      * @param augs Additional information that may include infoset
 731      *                      augmentations.
 732      *
 733      * @throws XNIException Thrown by handler to signal an error.
 734      */
 735     public void endParameterEntity(String name, Augmentations augs) throws XNIException {
 736 
 737         // call handlers
 738         if(fDTDGrammar != null )
 739             fDTDGrammar.endParameterEntity(name, augs);
 740         if (fDTDHandler != null) {
 741             fDTDHandler.endParameterEntity(name, augs);
 742         }
 743     }
 744 
 745     /**
 746      * An element declaration.
 747      *
 748      * @param name         The name of the element.
 749      * @param contentModel The element content model.
 750      * @param augs Additional information that may include infoset
 751      *                      augmentations.
 752      *
 753      * @throws XNIException Thrown by handler to signal an error.
 754      */
 755     public void elementDecl(String name, String contentModel, Augmentations augs)
 756     throws XNIException {
 757 
 758         //check VC: Unique Element Declaration
 759         if (fValidation) {
 760             if (fDTDElementDecls.contains(name)) {
 761                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 762                                            "MSG_ELEMENT_ALREADY_DECLARED",
 763                                            new Object[]{ name},
 764                                            XMLErrorReporter.SEVERITY_ERROR);
 765             }
 766             else {
 767                 fDTDElementDecls.add(name);
 768             }
 769         }
 770 
 771         // call handlers
 772         if(fDTDGrammar != null )
 773             fDTDGrammar.elementDecl(name, contentModel, augs);
 774         if (fDTDHandler != null) {
 775             fDTDHandler.elementDecl(name, contentModel, augs);
 776         }
 777 
 778     } // elementDecl(String,String)
 779 
 780     /**
 781      * The start of an attribute list.
 782      *
 783      * @param elementName The name of the element that this attribute
 784      *                    list is associated with.
 785      * @param augs Additional information that may include infoset
 786      *                      augmentations.
 787      *
 788      * @throws XNIException Thrown by handler to signal an error.
 789      */
 790     public void startAttlist(String elementName, Augmentations augs)
 791         throws XNIException {
 792 
 793         // call handlers
 794         if(fDTDGrammar != null )
 795             fDTDGrammar.startAttlist(elementName, augs);
 796         if (fDTDHandler != null) {
 797             fDTDHandler.startAttlist(elementName, augs);
 798         }
 799 
 800     } // startAttlist(String)
 801 
 802     /**
 803      * An attribute declaration.
 804      *
 805      * @param elementName   The name of the element that this attribute
 806      *                      is associated with.
 807      * @param attributeName The name of the attribute.
 808      * @param type          The attribute type. This value will be one of
 809      *                      the following: "CDATA", "ENTITY", "ENTITIES",
 810      *                      "ENUMERATION", "ID", "IDREF", "IDREFS",
 811      *                      "NMTOKEN", "NMTOKENS", or "NOTATION".
 812      * @param enumeration   If the type has the value "ENUMERATION" or
 813      *                      "NOTATION", this array holds the allowed attribute
 814      *                      values; otherwise, this array is null.
 815      * @param defaultType   The attribute default type. This value will be
 816      *                      one of the following: "#FIXED", "#IMPLIED",
 817      *                      "#REQUIRED", or null.
 818      * @param defaultValue  The attribute default value, or null if no
 819      *                      default value is specified.
 820      * @param nonNormalizedDefaultValue  The attribute default value with no normalization
 821      *                      performed, or null if no default value is specified.
 822      * @param augs Additional information that may include infoset
 823      *                      augmentations.
 824      *
 825      * @throws XNIException Thrown by handler to signal an error.
 826      */
 827     public void attributeDecl(String elementName, String attributeName,
 828                               String type, String[] enumeration,
 829                               String defaultType, XMLString defaultValue,
 830                               XMLString nonNormalizedDefaultValue, Augmentations augs) throws XNIException {
 831 
 832         if (type != XMLSymbols.fCDATASymbol && defaultValue != null) {
 833             normalizeDefaultAttrValue(defaultValue);
 834         }
 835 
 836         if (fValidation) {
 837 
 838                 boolean duplicateAttributeDef = false ;
 839 
 840                 //Get Grammar index to grammar array
 841                 DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar:fGrammarBucket.getActiveGrammar());
 842                 int elementIndex       = grammar.getElementDeclIndex( elementName);
 843                 if (grammar.getAttributeDeclIndex(elementIndex, attributeName) != -1) {
 844                     //more than one attribute definition is provided for the same attribute of a given element type.
 845                     duplicateAttributeDef = true ;
 846 
 847                     //this feature works only when validation is true.
 848                     if(fWarnDuplicateAttdef){
 849                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 850                                                  "MSG_DUPLICATE_ATTRIBUTE_DEFINITION",
 851                                                  new Object[]{ elementName, attributeName },
 852                                                  XMLErrorReporter.SEVERITY_WARNING );
 853                     }
 854                 }
 855 
 856 
 857             //
 858             // a) VC: One ID per Element Type, If duplicate ID attribute
 859             // b) VC: ID attribute Default. if there is a declareared attribute
 860             //        default for ID it should be of type #IMPLIED or #REQUIRED
 861             if (type == XMLSymbols.fIDSymbol) {
 862                 if (defaultValue != null && defaultValue.length != 0) {
 863                     if (defaultType == null ||
 864                         !(defaultType == XMLSymbols.fIMPLIEDSymbol ||
 865                           defaultType == XMLSymbols.fREQUIREDSymbol)) {
 866                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 867                                                    "IDDefaultTypeInvalid",
 868                                                    new Object[]{ attributeName},
 869                                                    XMLErrorReporter.SEVERITY_ERROR);
 870                     }
 871                 }
 872 
 873                 if (!fTableOfIDAttributeNames.containsKey(elementName)) {
 874                     fTableOfIDAttributeNames.put(elementName, attributeName);
 875                 }
 876                 else {
 877                         //we should not report an error, when there is duplicate attribute definition for given element type
 878                         //according to XML 1.0 spec, When more than one definition is provided for the same attribute of a given
 879                         //element type, the first declaration is binding and later declaration are *ignored*. So processor should
 880                         //ignore the second declarations, however an application would be warned of the duplicate attribute defintion
 881                         // if http://apache.org/xml/features/validation/warn-on-duplicate-attdef feature is set to true,
 882                         // one typical case where this could be a  problem, when any XML file
 883                         // provide the ID type information through internal subset so that it is available to the parser which read
 884                         //only internal subset. Now that attribute declaration(ID Type) can again be part of external parsed entity
 885                         //referenced. At that time if parser doesn't make this distinction it will throw an error for VC One ID per
 886                         //Element Type, which (second defintion) actually should be ignored. Application behavior may differ on the
 887                         //basis of error or warning thrown. - nb.
 888 
 889                         if(!duplicateAttributeDef){
 890                                 String previousIDAttributeName = fTableOfIDAttributeNames.get( elementName );//rule a)
 891                                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 892                                                "MSG_MORE_THAN_ONE_ID_ATTRIBUTE",
 893                                                new Object[]{ elementName, previousIDAttributeName, attributeName},
 894                                                XMLErrorReporter.SEVERITY_ERROR);
 895                         }
 896                 }
 897             }
 898 
 899             //
 900             //  VC: One Notation Per Element Type, should check if there is a
 901             //      duplicate NOTATION attribute
 902 
 903             if (type == XMLSymbols.fNOTATIONSymbol) {
 904                 // VC: Notation Attributes: all notation names in the
 905                 //     (attribute) declaration must be declared.
 906                 for (int i=0; i<enumeration.length; i++) {
 907                     fNotationEnumVals.put(enumeration[i], attributeName);
 908                 }
 909 
 910                 if (fTableOfNOTATIONAttributeNames.containsKey( elementName ) == false) {
 911                     fTableOfNOTATIONAttributeNames.put( elementName, attributeName);
 912                 }
 913                 else {
 914                     //we should not report an error, when there is duplicate attribute definition for given element type
 915                     //according to XML 1.0 spec, When more than one definition is provided for the same attribute of a given
 916                     //element type, the first declaration is binding and later declaration are *ignored*. So processor should
 917                     //ignore the second declarations, however an application would be warned of the duplicate attribute definition
 918                     // if http://apache.org/xml/features/validation/warn-on-duplicate-attdef feature is set to true,
 919                     // Application behavior may differ on the basis of error or warning thrown. - nb.
 920 
 921                         if(!duplicateAttributeDef){
 922 
 923                                 String previousNOTATIONAttributeName = fTableOfNOTATIONAttributeNames.get( elementName );
 924                                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 925                                                "MSG_MORE_THAN_ONE_NOTATION_ATTRIBUTE",
 926                                                new Object[]{ elementName, previousNOTATIONAttributeName, attributeName},
 927                                                XMLErrorReporter.SEVERITY_ERROR);
 928                          }
 929                 }
 930             }
 931 
 932             // VC: No Duplicate Tokens
 933             // XML 1.0 SE Errata - E2
 934             if (type == XMLSymbols.fENUMERATIONSymbol || type == XMLSymbols.fNOTATIONSymbol) {
 935                 outer:
 936                     for (int i = 0; i < enumeration.length; ++i) {
 937                         for (int j = i + 1; j < enumeration.length; ++j) {
 938                             if (enumeration[i].equals(enumeration[j])) {
 939                                 // Only report the first uniqueness violation. There could be others,
 940                                 // but additional overhead would be incurred tracking unique tokens
 941                                 // that have already been encountered. -- mrglavas
 942                                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 943                                                type == XMLSymbols.fENUMERATIONSymbol
 944                                                    ? "MSG_DISTINCT_TOKENS_IN_ENUMERATION"
 945                                                    : "MSG_DISTINCT_NOTATION_IN_ENUMERATION",
 946                                                new Object[]{ elementName, enumeration[i], attributeName },
 947                                                XMLErrorReporter.SEVERITY_ERROR);
 948                                 break outer;
 949                             }
 950                         }
 951                     }
 952             }
 953 
 954             // VC: Attribute Default Legal
 955             boolean ok = true;
 956             if (defaultValue != null &&
 957                 (defaultType == null ||
 958                  (defaultType != null && defaultType == XMLSymbols.fFIXEDSymbol))) {
 959 
 960                 String value = defaultValue.toString();
 961                 if (type == XMLSymbols.fNMTOKENSSymbol ||
 962                     type == XMLSymbols.fENTITIESSymbol ||
 963                     type == XMLSymbols.fIDREFSSymbol) {
 964 
 965                     StringTokenizer tokenizer = new StringTokenizer(value," ");
 966                     if (tokenizer.hasMoreTokens()) {
 967                         while (true) {
 968                             String nmtoken = tokenizer.nextToken();
 969                             if (type == XMLSymbols.fNMTOKENSSymbol) {
 970                                 if (!isValidNmtoken(nmtoken)) {
 971                                     ok = false;
 972                                     break;
 973                                 }
 974                             }
 975                             else if (type == XMLSymbols.fENTITIESSymbol ||
 976                                      type == XMLSymbols.fIDREFSSymbol) {
 977                                 if (!isValidName(nmtoken)) {
 978                                     ok = false;
 979                                     break;
 980                                 }
 981                             }
 982                             if (!tokenizer.hasMoreTokens()) {
 983                                 break;
 984                             }
 985                         }
 986                     }
 987 
 988                 }
 989                 else {
 990                     if (type == XMLSymbols.fENTITYSymbol ||
 991                         type == XMLSymbols.fIDSymbol ||
 992                         type == XMLSymbols.fIDREFSymbol ||
 993                         type == XMLSymbols.fNOTATIONSymbol) {
 994 
 995                         if (!isValidName(value)) {
 996                             ok = false;
 997                         }
 998 
 999                     }
1000                     else if (type == XMLSymbols.fNMTOKENSymbol ||
1001                              type == XMLSymbols.fENUMERATIONSymbol) {
1002 
1003                         if (!isValidNmtoken(value)) {
1004                             ok = false;
1005                         }
1006                     }
1007 
1008                     if (type == XMLSymbols.fNOTATIONSymbol ||
1009                         type == XMLSymbols.fENUMERATIONSymbol) {
1010                         ok = false;
1011                         for (int i=0; i<enumeration.length; i++) {
1012                             if (defaultValue.equals(enumeration[i])) {
1013                                 ok = true;
1014                             }
1015                         }
1016                     }
1017 
1018                 }
1019                 if (!ok) {
1020                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1021                                                "MSG_ATT_DEFAULT_INVALID",
1022                                                new Object[]{attributeName, value},
1023                                                XMLErrorReporter.SEVERITY_ERROR);
1024                 }
1025             }
1026         }
1027 
1028         // call handlers
1029         if(fDTDGrammar != null)
1030             fDTDGrammar.attributeDecl(elementName, attributeName,
1031                                   type, enumeration,
1032                                   defaultType, defaultValue, nonNormalizedDefaultValue, augs);
1033         if (fDTDHandler != null) {
1034             fDTDHandler.attributeDecl(elementName, attributeName,
1035                                       type, enumeration,
1036                                       defaultType, defaultValue, nonNormalizedDefaultValue, augs);
1037         }
1038 
1039     } // attributeDecl(String,String,String,String[],String,XMLString, XMLString, Augmentations)
1040 
1041     /**
1042      * The end of an attribute list.
1043      *
1044      * @param augs Additional information that may include infoset
1045      *                      augmentations.
1046      *
1047      * @throws XNIException Thrown by handler to signal an error.
1048      */
1049     public void endAttlist(Augmentations augs) throws XNIException {
1050 
1051         // call handlers
1052         if(fDTDGrammar != null)
1053             fDTDGrammar.endAttlist(augs);
1054         if (fDTDHandler != null) {
1055             fDTDHandler.endAttlist(augs);
1056         }
1057 
1058     } // endAttlist()
1059 
1060     /**
1061      * An internal entity declaration.
1062      *
1063      * @param name The name of the entity. Parameter entity names start with
1064      *             '%', whereas the name of a general entity is just the
1065      *             entity name.
1066      * @param text The value of the entity.
1067      * @param nonNormalizedText The non-normalized value of the entity. This
1068      *             value contains the same sequence of characters that was in
1069      *             the internal entity declaration, without any entity
1070      *             references expanded.
1071      * @param augs Additional information that may include infoset
1072      *                      augmentations.
1073      *
1074      * @throws XNIException Thrown by handler to signal an error.
1075      */
1076     public void internalEntityDecl(String name, XMLString text,
1077                                    XMLString nonNormalizedText,
1078                                    Augmentations augs) throws XNIException {
1079 
1080         DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar: fGrammarBucket.getActiveGrammar());
1081         int index = grammar.getEntityDeclIndex(name) ;
1082 
1083         //If the same entity is declared more than once, the first declaration
1084         //encountered is binding, SAX requires only effective(first) declaration
1085         //to be reported to the application
1086 
1087         //REVISIT: Does it make sense to pass duplicate Entity information across
1088         //the pipeline -- nb?
1089 
1090         //its a new entity and hasn't been declared.
1091         if(index == -1){
1092             //store internal entity declaration in grammar
1093             if(fDTDGrammar != null)
1094                 fDTDGrammar.internalEntityDecl(name, text, nonNormalizedText, augs);
1095             // call handlers
1096             if (fDTDHandler != null) {
1097                 fDTDHandler.internalEntityDecl(name, text, nonNormalizedText, augs);
1098             }
1099         }
1100 
1101     } // internalEntityDecl(String,XMLString,XMLString)
1102 
1103 
1104     /**
1105      * An external entity declaration.
1106      *
1107      * @param name     The name of the entity. Parameter entity names start
1108      *                 with '%', whereas the name of a general entity is just
1109      *                 the entity name.
1110      * @param identifier    An object containing all location information
1111      *                      pertinent to this external entity.
1112      * @param augs Additional information that may include infoset
1113      *                      augmentations.
1114      *
1115      * @throws XNIException Thrown by handler to signal an error.
1116      */
1117     public void externalEntityDecl(String name, XMLResourceIdentifier identifier,
1118                                    Augmentations augs) throws XNIException {
1119 
1120         DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar:  fGrammarBucket.getActiveGrammar());
1121         int index = grammar.getEntityDeclIndex(name) ;
1122 
1123         //If the same entity is declared more than once, the first declaration
1124         //encountered is binding, SAX requires only effective(first) declaration
1125         //to be reported to the application
1126 
1127         //REVISIT: Does it make sense to pass duplicate entity information across
1128         //the pipeline -- nb?
1129 
1130         //its a new entity and hasn't been declared.
1131         if(index == -1){
1132             //store external entity declaration in grammar
1133             if(fDTDGrammar != null)
1134                 fDTDGrammar.externalEntityDecl(name, identifier, augs);
1135             // call handlers
1136             if (fDTDHandler != null) {
1137                 fDTDHandler.externalEntityDecl(name, identifier, augs);
1138             }
1139         }
1140 
1141     } // externalEntityDecl(String,XMLResourceIdentifier, Augmentations)
1142 
1143     /**
1144      * An unparsed entity declaration.
1145      *
1146      * @param name     The name of the entity.
1147      * @param identifier    An object containing all location information
1148      *                      pertinent to this entity.
1149      * @param notation The name of the notation.
1150      * @param augs Additional information that may include infoset
1151      *                      augmentations.
1152      *
1153      * @throws XNIException Thrown by handler to signal an error.
1154      */
1155     public void unparsedEntityDecl(String name, XMLResourceIdentifier identifier,
1156                                    String notation,
1157                                    Augmentations augs) throws XNIException {
1158 
1159         // VC: Notation declared,  in the production of NDataDecl
1160         if (fValidation) {
1161             fNDataDeclNotations.put(name, notation);
1162         }
1163 
1164         // call handlers
1165         if(fDTDGrammar != null)
1166             fDTDGrammar.unparsedEntityDecl(name, identifier, notation, augs);
1167         if (fDTDHandler != null) {
1168             fDTDHandler.unparsedEntityDecl(name, identifier, notation, augs);
1169         }
1170 
1171     } // unparsedEntityDecl(String,XMLResourceIdentifier,String,Augmentations)
1172 
1173     /**
1174      * A notation declaration
1175      *
1176      * @param name     The name of the notation.
1177      * @param identifier    An object containing all location information
1178      *                      pertinent to this notation.
1179      * @param augs Additional information that may include infoset
1180      *                      augmentations.
1181      *
1182      * @throws XNIException Thrown by handler to signal an error.
1183      */
1184     public void notationDecl(String name, XMLResourceIdentifier identifier,
1185                              Augmentations augs) throws XNIException {
1186 
1187         // VC: Unique Notation Name
1188         if (fValidation) {
1189             DTDGrammar grammar = (fDTDGrammar != null ? fDTDGrammar : fGrammarBucket.getActiveGrammar());
1190             if (grammar.getNotationDeclIndex(name) != -1) {
1191                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1192                                            "UniqueNotationName",
1193                                            new Object[]{name},
1194                                            XMLErrorReporter.SEVERITY_ERROR);
1195             }
1196         }
1197 
1198         // call handlers
1199         if(fDTDGrammar != null)
1200             fDTDGrammar.notationDecl(name, identifier, augs);
1201         if (fDTDHandler != null) {
1202             fDTDHandler.notationDecl(name, identifier, augs);
1203         }
1204 
1205     } // notationDecl(String,XMLResourceIdentifier, Augmentations)
1206 
1207     /**
1208      * The start of a conditional section.
1209      *
1210      * @param type The type of the conditional section. This value will
1211      *             either be CONDITIONAL_INCLUDE or CONDITIONAL_IGNORE.
1212      * @param augs Additional information that may include infoset
1213      *                      augmentations.
1214      *
1215      * @throws XNIException Thrown by handler to signal an error.
1216      *
1217      * @see #CONDITIONAL_INCLUDE
1218      * @see #CONDITIONAL_IGNORE
1219      */
1220     public void startConditional(short type, Augmentations augs) throws XNIException {
1221 
1222         // set state
1223         fInDTDIgnore = type == XMLDTDHandler.CONDITIONAL_IGNORE;
1224 
1225         // call handlers
1226         if(fDTDGrammar != null)
1227             fDTDGrammar.startConditional(type, augs);
1228         if (fDTDHandler != null) {
1229             fDTDHandler.startConditional(type, augs);
1230         }
1231 
1232     } // startConditional(short)
1233 
1234     /**
1235      * The end of a conditional section.
1236      *
1237      * @param augs Additional information that may include infoset
1238      *                      augmentations.
1239      *
1240      * @throws XNIException Thrown by handler to signal an error.
1241      */
1242     public void endConditional(Augmentations augs) throws XNIException {
1243 
1244         // set state
1245         fInDTDIgnore = false;
1246 
1247         // call handlers
1248         if(fDTDGrammar != null)
1249             fDTDGrammar.endConditional(augs);
1250         if (fDTDHandler != null) {
1251             fDTDHandler.endConditional(augs);
1252         }
1253 
1254     } // endConditional()
1255 
1256     /**
1257      * The end of the DTD.
1258      *
1259      * @param augs Additional information that may include infoset
1260      *                      augmentations.
1261      *
1262      * @throws XNIException Thrown by handler to signal an error.
1263      */
1264     public void endDTD(Augmentations augs) throws XNIException {
1265 
1266 
1267         // save grammar
1268         if(fDTDGrammar != null) {
1269             fDTDGrammar.endDTD(augs);
1270             if(fGrammarPool != null)
1271                 fGrammarPool.cacheGrammars(XMLGrammarDescription.XML_DTD, new Grammar[] {fDTDGrammar});
1272         }
1273         if (fValidation) {
1274             DTDGrammar grammar = (fDTDGrammar != null? fDTDGrammar: fGrammarBucket.getActiveGrammar());
1275 
1276             // VC: Notation Declared. for external entity declaration [Production 76].
1277             for (Map.Entry<String, String> entry : fNDataDeclNotations.entrySet()) {
1278                 String notation = entry.getValue();
1279                 if (grammar.getNotationDeclIndex(notation) == -1) {
1280                     String entity = entry.getKey();
1281                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1282                             "MSG_NOTATION_NOT_DECLARED_FOR_UNPARSED_ENTITYDECL",
1283                             new Object[]{entity, notation},
1284                             XMLErrorReporter.SEVERITY_ERROR);
1285                 }
1286             }
1287 
1288             for (Map.Entry<String, String> entry : fNotationEnumVals.entrySet()) {
1289                 String notation = entry.getKey();
1290                 if (grammar.getNotationDeclIndex(notation) == -1) {
1291                     String attributeName = entry.getValue();
1292                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1293                             "MSG_NOTATION_NOT_DECLARED_FOR_NOTATIONTYPE_ATTRIBUTE",
1294                             new Object[]{attributeName, notation},
1295                             XMLErrorReporter.SEVERITY_ERROR);
1296                 }
1297             }
1298 
1299             // VC: No Notation on Empty Element
1300             // An attribute of type NOTATION must not be declared on an element declared EMPTY.
1301             for (Map.Entry<String, String> entry : fTableOfNOTATIONAttributeNames.entrySet()) {
1302                 String elementName = entry.getKey();
1303                 int elementIndex = grammar.getElementDeclIndex(elementName);
1304                 if (grammar.getContentSpecType(elementIndex) == XMLElementDecl.TYPE_EMPTY) {
1305                     String attributeName = entry.getValue();
1306                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1307                                                "NoNotationOnEmptyElement",
1308                                                new Object[]{elementName, attributeName},
1309                                                XMLErrorReporter.SEVERITY_ERROR);
1310                 }
1311             }
1312 
1313             // should be safe to release these references
1314             fTableOfIDAttributeNames = null;
1315             fTableOfNOTATIONAttributeNames = null;
1316 
1317             // check whether each element referenced in a content model is declared
1318             if (fWarnOnUndeclaredElemdef) {
1319                 checkDeclaredElements(grammar);
1320             }
1321         }
1322 
1323         // call handlers
1324         if (fDTDHandler != null) {
1325             fDTDHandler.endDTD(augs);
1326         }
1327 
1328     } // endDTD()
1329 
1330     // sets the XMLDTDSource of this handler
1331     public void setDTDSource(XMLDTDSource source ) {
1332         fDTDSource = source;
1333     } // setDTDSource(XMLDTDSource)
1334 
1335     // returns the XMLDTDSource of this handler
1336     public XMLDTDSource getDTDSource() {
1337         return fDTDSource;
1338     } // getDTDSource():  XMLDTDSource
1339 
1340     //
1341     // XMLDTDContentModelHandler methods
1342     //
1343 
1344     // sets the XMLContentModelDTDSource of this handler
1345     public void setDTDContentModelSource(XMLDTDContentModelSource source ) {
1346         fDTDContentModelSource = source;
1347     } // setDTDContentModelSource(XMLDTDContentModelSource)
1348 
1349     // returns the XMLDTDSource of this handler
1350     public XMLDTDContentModelSource getDTDContentModelSource() {
1351         return fDTDContentModelSource;
1352     } // getDTDContentModelSource():  XMLDTDContentModelSource
1353 
1354 
1355     /**
1356      * The start of a content model. Depending on the type of the content
1357      * model, specific methods may be called between the call to the
1358      * startContentModel method and the call to the endContentModel method.
1359      *
1360      * @param elementName The name of the element.
1361      * @param augs Additional information that may include infoset
1362      *                      augmentations.
1363      *
1364      * @throws XNIException Thrown by handler to signal an error.
1365      */
1366     public void startContentModel(String elementName, Augmentations augs)
1367         throws XNIException {
1368 
1369         if (fValidation) {
1370             fDTDElementDeclName = elementName;
1371             fMixedElementTypes.clear();
1372         }
1373 
1374         // call handlers
1375         if(fDTDGrammar != null)
1376             fDTDGrammar.startContentModel(elementName, augs);
1377         if (fDTDContentModelHandler != null) {
1378             fDTDContentModelHandler.startContentModel(elementName, augs);
1379         }
1380 
1381     } // startContentModel(String)
1382 
1383     /**
1384      * A content model of ANY.
1385      *
1386      * @param augs Additional information that may include infoset
1387      *                      augmentations.
1388      *
1389      * @throws XNIException Thrown by handler to signal an error.
1390      *
1391      * @see #empty
1392      * @see #startGroup
1393      */
1394     public void any(Augmentations augs) throws XNIException {
1395         if(fDTDGrammar != null)
1396             fDTDGrammar.any(augs);
1397         if (fDTDContentModelHandler != null) {
1398             fDTDContentModelHandler.any(augs);
1399         }
1400     } // any()
1401 
1402     /**
1403      * A content model of EMPTY.
1404      *
1405      * @param augs Additional information that may include infoset
1406      *                      augmentations.
1407      *
1408      * @throws XNIException Thrown by handler to signal an error.
1409      *
1410      * @see #any
1411      * @see #startGroup
1412      */
1413     public void empty(Augmentations augs) throws XNIException {
1414         if(fDTDGrammar != null)
1415             fDTDGrammar.empty(augs);
1416         if (fDTDContentModelHandler != null) {
1417             fDTDContentModelHandler.empty(augs);
1418         }
1419     } // empty()
1420 
1421     /**
1422      * A start of either a mixed or children content model. A mixed
1423      * content model will immediately be followed by a call to the
1424      * <code>pcdata()</code> method. A children content model will
1425      * contain additional groups and/or elements.
1426      *
1427      * @param augs Additional information that may include infoset
1428      *                      augmentations.
1429      *
1430      * @throws XNIException Thrown by handler to signal an error.
1431      *
1432      * @see #any
1433      * @see #empty
1434      */
1435     public void startGroup(Augmentations augs) throws XNIException {
1436 
1437         fMixed = false;
1438         // call handlers
1439         if(fDTDGrammar != null)
1440             fDTDGrammar.startGroup(augs);
1441         if (fDTDContentModelHandler != null) {
1442             fDTDContentModelHandler.startGroup(augs);
1443         }
1444 
1445     } // startGroup()
1446 
1447     /**
1448      * The appearance of "#PCDATA" within a group signifying a
1449      * mixed content model. This method will be the first called
1450      * following the content model's <code>startGroup()</code>.
1451      *
1452      * @param augs Additional information that may include infoset
1453      *                      augmentations.
1454      *
1455      * @throws XNIException Thrown by handler to signal an error.
1456      *
1457      * @see #startGroup
1458      */
1459     public void pcdata(Augmentations augs) {
1460         fMixed = true;
1461         if(fDTDGrammar != null)
1462             fDTDGrammar.pcdata(augs);
1463         if (fDTDContentModelHandler != null) {
1464             fDTDContentModelHandler.pcdata(augs);
1465         }
1466     } // pcdata()
1467 
1468     /**
1469      * A referenced element in a mixed or children content model.
1470      *
1471      * @param elementName The name of the referenced element.
1472      * @param augs Additional information that may include infoset
1473      *                      augmentations.
1474      *
1475      * @throws XNIException Thrown by handler to signal an error.
1476      */
1477     public void element(String elementName, Augmentations augs) throws XNIException {
1478 
1479         // check VC: No duplicate Types, in a single mixed-content declaration
1480         if (fMixed && fValidation) {
1481             if (fMixedElementTypes.contains(elementName)) {
1482                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1483                                            "DuplicateTypeInMixedContent",
1484                                            new Object[]{fDTDElementDeclName, elementName},
1485                                            XMLErrorReporter.SEVERITY_ERROR);
1486             }
1487             else {
1488                 fMixedElementTypes.add(elementName);
1489             }
1490         }
1491 
1492         // call handlers
1493         if(fDTDGrammar != null)
1494             fDTDGrammar.element(elementName, augs);
1495         if (fDTDContentModelHandler != null) {
1496             fDTDContentModelHandler.element(elementName, augs);
1497         }
1498 
1499     } // childrenElement(String)
1500 
1501     /**
1502      * The separator between choices or sequences of a mixed or children
1503      * content model.
1504      *
1505      * @param separator The type of children separator.
1506      * @param augs Additional information that may include infoset
1507      *                      augmentations.
1508      *
1509      * @throws XNIException Thrown by handler to signal an error.
1510      *
1511      * @see #SEPARATOR_CHOICE
1512      * @see #SEPARATOR_SEQUENCE
1513      */
1514     public void separator(short separator, Augmentations augs)
1515         throws XNIException {
1516 
1517         // call handlers
1518         if(fDTDGrammar != null)
1519             fDTDGrammar.separator(separator, augs);
1520         if (fDTDContentModelHandler != null) {
1521             fDTDContentModelHandler.separator(separator, augs);
1522         }
1523 
1524     } // separator(short)
1525 
1526     /**
1527      * The occurrence count for a child in a children content model or
1528      * for the mixed content model group.
1529      *
1530      * @param occurrence The occurrence count for the last element
1531      *                   or group.
1532      * @param augs Additional information that may include infoset
1533      *                      augmentations.
1534      *
1535      * @throws XNIException Thrown by handler to signal an error.
1536      *
1537      * @see #OCCURS_ZERO_OR_ONE
1538      * @see #OCCURS_ZERO_OR_MORE
1539      * @see #OCCURS_ONE_OR_MORE
1540      */
1541     public void occurrence(short occurrence, Augmentations augs)
1542         throws XNIException {
1543 
1544         // call handlers
1545         if(fDTDGrammar != null)
1546             fDTDGrammar.occurrence(occurrence, augs);
1547         if (fDTDContentModelHandler != null) {
1548             fDTDContentModelHandler.occurrence(occurrence, augs);
1549         }
1550 
1551     } // occurrence(short)
1552 
1553     /**
1554      * The end of a group for mixed or children content models.
1555      *
1556      * @param augs Additional information that may include infoset
1557      *                      augmentations.
1558      *
1559      * @throws XNIException Thrown by handler to signal an error.
1560      */
1561     public void endGroup(Augmentations augs) throws XNIException {
1562 
1563         // call handlers
1564         if(fDTDGrammar != null)
1565             fDTDGrammar.endGroup(augs);
1566         if (fDTDContentModelHandler != null) {
1567             fDTDContentModelHandler.endGroup(augs);
1568         }
1569 
1570     } // endGroup()
1571 
1572     /**
1573      * The end of a content model.
1574      *
1575      * @param augs Additional information that may include infoset
1576      *                      augmentations.
1577      *
1578      * @throws XNIException Thrown by handler to signal an error.
1579      */
1580     public void endContentModel(Augmentations augs) throws XNIException {
1581 
1582         // call handlers
1583         if(fDTDGrammar != null)
1584             fDTDGrammar.endContentModel(augs);
1585         if (fDTDContentModelHandler != null) {
1586             fDTDContentModelHandler.endContentModel(augs);
1587         }
1588 
1589     } // endContentModel()
1590 
1591     //
1592     // Private methods
1593     //
1594 
1595     /**
1596      * Normalize the attribute value of a non CDATA default attribute
1597      * collapsing sequences of space characters (x20)
1598      *
1599      * @param value The value to normalize
1600      * @return Whether the value was changed or not.
1601      */
1602     private boolean normalizeDefaultAttrValue(XMLString value) {
1603 
1604         boolean skipSpace = true; // skip leading spaces
1605         int current = value.offset;
1606         int end = value.offset + value.length;
1607         for (int i = value.offset; i < end; i++) {
1608             if (value.ch[i] == ' ') {
1609                 if (!skipSpace) {
1610                     // take the first whitespace as a space and skip the others
1611                     value.ch[current++] = ' ';
1612                     skipSpace = true;
1613                 }
1614                 else {
1615                     // just skip it.
1616                 }
1617             }
1618             else {
1619                 // simply shift non space chars if needed
1620                 if (current != i) {
1621                     value.ch[current] = value.ch[i];
1622                 }
1623                 current++;
1624                 skipSpace = false;
1625             }
1626         }
1627         if (current != end) {
1628             if (skipSpace) {
1629                 // if we finished on a space trim it
1630                 current--;
1631             }
1632             // set the new value length
1633             value.length = current - value.offset;
1634             return true;
1635         }
1636         return false;
1637     }
1638 
1639     protected boolean isValidNmtoken(String nmtoken) {
1640         return XMLChar.isValidNmtoken(nmtoken);
1641     } // isValidNmtoken(String):  boolean
1642 
1643     protected boolean isValidName(String name) {
1644         return XMLChar.isValidName(name);
1645     } // isValidName(String):  boolean
1646 
1647     /**
1648      * Checks that all elements referenced in content models have
1649      * been declared. This method calls out to the error handler
1650      * to indicate warnings.
1651      */
1652     private void checkDeclaredElements(DTDGrammar grammar) {
1653         int elementIndex = grammar.getFirstElementDeclIndex();
1654         XMLContentSpec contentSpec = new XMLContentSpec();
1655         while (elementIndex >= 0) {
1656             int type = grammar.getContentSpecType(elementIndex);
1657             if (type == XMLElementDecl.TYPE_CHILDREN || type == XMLElementDecl.TYPE_MIXED) {
1658                 checkDeclaredElements(grammar,
1659                         elementIndex,
1660                         grammar.getContentSpecIndex(elementIndex),
1661                         contentSpec);
1662             }
1663             elementIndex = grammar.getNextElementDeclIndex(elementIndex);
1664         }
1665     }
1666 
1667     /**
1668      * Does a recursive (if necessary) check on the specified element's
1669      * content spec to make sure that all children refer to declared
1670      * elements.
1671      */
1672     private void checkDeclaredElements(DTDGrammar grammar, int elementIndex,
1673             int contentSpecIndex, XMLContentSpec contentSpec) {
1674         grammar.getContentSpec(contentSpecIndex, contentSpec);
1675         if (contentSpec.type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
1676             String value = (String) contentSpec.value;
1677             if (value != null && grammar.getElementDeclIndex(value) == -1) {
1678                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1679                         "UndeclaredElementInContentSpec",
1680                         new Object[]{grammar.getElementDeclName(elementIndex).rawname, value},
1681                         XMLErrorReporter.SEVERITY_WARNING);
1682             }
1683         }
1684         // It's not a leaf, so we have to recurse its left and maybe right
1685         // nodes. Save both values before we recurse and trash the node.
1686         else if ((contentSpec.type == XMLContentSpec.CONTENTSPECNODE_CHOICE)
1687                 || (contentSpec.type == XMLContentSpec.CONTENTSPECNODE_SEQ)) {
1688             final int leftNode = ((int[])contentSpec.value)[0];
1689             final int rightNode = ((int[])contentSpec.otherValue)[0];
1690             //  Recurse on both children.
1691             checkDeclaredElements(grammar, elementIndex, leftNode, contentSpec);
1692             checkDeclaredElements(grammar, elementIndex, rightNode, contentSpec);
1693         }
1694         else if (contentSpec.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_MORE
1695                 || contentSpec.type == XMLContentSpec.CONTENTSPECNODE_ZERO_OR_ONE
1696                 || contentSpec.type == XMLContentSpec.CONTENTSPECNODE_ONE_OR_MORE) {
1697             final int leftNode = ((int[])contentSpec.value)[0];
1698             checkDeclaredElements(grammar, elementIndex, leftNode, contentSpec);
1699         }
1700     }
1701 
1702 } // class XMLDTDProcessor