1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 1999-2005 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl.dtd;
  22 
  23 import com.sun.org.apache.xerces.internal.impl.Constants;
  24 import com.sun.org.apache.xerces.internal.impl.RevalidationHandler;
  25 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
  26 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  27 import com.sun.org.apache.xerces.internal.impl.dtd.models.ContentModelValidator;
  28 import com.sun.org.apache.xerces.internal.impl.dv.DTDDVFactory;
  29 import com.sun.org.apache.xerces.internal.impl.dv.DatatypeValidator;
  30 import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
  31 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  32 import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
  33 import com.sun.org.apache.xerces.internal.impl.validation.ValidationState;
  34 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  35 import com.sun.org.apache.xerces.internal.util.XMLChar;
  36 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  37 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  38 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  39 import com.sun.org.apache.xerces.internal.xni.QName;
  40 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  41 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
  42 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  43 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  44 import com.sun.org.apache.xerces.internal.xni.XMLString;
  45 import com.sun.org.apache.xerces.internal.xni.XNIException;
  46 import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
  47 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
  48 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
  49 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  50 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  51 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  52 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
  53 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
  54 
  55 /**
  56  * The DTD validator. The validator implements a document
  57  * filter: receiving document events from the scanner; validating
  58  * the content and structure; augmenting the InfoSet, if applicable;
  59  * and notifying the parser of the information resulting from the
  60  * validation process.
  61  * <p> Formerly, this component also handled DTD events and grammar construction.
  62  * To facilitate the development of a meaningful DTD grammar caching/preparsing
  63  * framework, this functionality has been moved into the XMLDTDLoader
  64  * class.  Therefore, this class no longer implements the DTDFilter
  65  * or DTDContentModelFilter interfaces.
  66  * <p>
  67  * This component requires the following features and properties from the
  68  * component manager that uses it:
  69  * <ul>
  70  *  <li>http://xml.org/sax/features/namespaces</li>
  71  *  <li>http://xml.org/sax/features/validation</li>
  72  *  <li>http://apache.org/xml/features/validation/dynamic</li>
  73  *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
  74  *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
  75  *  <li>http://apache.org/xml/properties/internal/grammar-pool</li>
  76  *  <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
  77  * </ul>
  78  *
  79  * @xerces.internal
  80  *
  81  * @author Eric Ye, IBM
  82  * @author Andy Clark, IBM
  83  * @author Jeffrey Rodriguez IBM
  84  * @author Neil Graham, IBM
  85  *
  86  * @version $Id: XMLDTDValidator.java,v 1.8 2010-11-01 04:39:42 joehw Exp $
  87  */
  88 public class XMLDTDValidator
  89         implements XMLComponent, XMLDocumentFilter, XMLDTDValidatorFilter, RevalidationHandler {
  90 
  91     //
  92     // Constants
  93     //
  94 
  95     /** Symbol: "&lt;&lt;datatypes>>". */
  96 
  97     /** Top level scope (-1). */
  98     private static final int TOP_LEVEL_SCOPE = -1;
  99 
 100     // feature identifiers
 101 
 102     /** Feature identifier: namespaces. */
 103     protected static final String NAMESPACES =
 104         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
 105 
 106     /** Feature identifier: validation. */
 107     protected static final String VALIDATION =
 108         Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
 109 
 110     /** Feature identifier: dynamic validation. */
 111     protected static final String DYNAMIC_VALIDATION =
 112         Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE;
 113 
 114     /** Feature identifier: balance syntax trees. */
 115     protected static final String BALANCE_SYNTAX_TREES =
 116         Constants.XERCES_FEATURE_PREFIX + Constants.BALANCE_SYNTAX_TREES;
 117 
 118     /** Feature identifier: warn on duplicate attdef */
 119     protected static final String WARN_ON_DUPLICATE_ATTDEF =
 120         Constants.XERCES_FEATURE_PREFIX + Constants.WARN_ON_DUPLICATE_ATTDEF_FEATURE;
 121 
 122         protected static final String PARSER_SETTINGS =
 123                 Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
 124 
 125 
 126 
 127     // property identifiers
 128 
 129     /** Property identifier: symbol table. */
 130     protected static final String SYMBOL_TABLE =
 131         Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
 132 
 133     /** Property identifier: error reporter. */
 134     protected static final String ERROR_REPORTER =
 135         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
 136 
 137     /** Property identifier: grammar pool. */
 138     protected static final String GRAMMAR_POOL =
 139         Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
 140 
 141     /** Property identifier: datatype validator factory. */
 142     protected static final String DATATYPE_VALIDATOR_FACTORY =
 143         Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY;
 144 
 145     // property identifier:  ValidationManager
 146     protected static final String VALIDATION_MANAGER =
 147         Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
 148 
 149     // recognized features and properties
 150 
 151     /** Recognized features. */
 152     private static final String[] RECOGNIZED_FEATURES = {
 153         NAMESPACES,
 154         VALIDATION,
 155         DYNAMIC_VALIDATION,
 156         BALANCE_SYNTAX_TREES
 157     };
 158 
 159     /** Feature defaults. */
 160     private static final Boolean[] FEATURE_DEFAULTS = {
 161         null,
 162         null,
 163         Boolean.FALSE,
 164         Boolean.FALSE,
 165     };
 166 
 167     /** Recognized properties. */
 168     private static final String[] RECOGNIZED_PROPERTIES = {
 169         SYMBOL_TABLE,
 170         ERROR_REPORTER,
 171         GRAMMAR_POOL,
 172         DATATYPE_VALIDATOR_FACTORY,
 173         VALIDATION_MANAGER
 174     };
 175 
 176     /** Property defaults. */
 177     private static final Object[] PROPERTY_DEFAULTS = {
 178         null,
 179         null,
 180         null,
 181         null,
 182         null,
 183     };
 184 
 185     // debugging
 186 
 187     /** Compile to true to debug attributes. */
 188     private static final boolean DEBUG_ATTRIBUTES = false;
 189 
 190     /** Compile to true to debug element children. */
 191     private static final boolean DEBUG_ELEMENT_CHILDREN = false;
 192 
 193     //
 194     // Data
 195     //
 196 
 197     // updated during reset
 198     protected ValidationManager fValidationManager = null;
 199 
 200     // validation state
 201     protected final ValidationState fValidationState = new ValidationState();
 202 
 203     // features
 204 
 205     /** Namespaces. */
 206     protected boolean fNamespaces;
 207 
 208     /** Validation. */
 209     protected boolean fValidation;
 210 
 211     /** Validation against only DTD */
 212     protected boolean fDTDValidation;
 213 
 214     /**
 215      * Dynamic validation. This state of this feature is only useful when
 216      * the validation feature is set to <code>true</code>.
 217      */
 218     protected boolean fDynamicValidation;
 219 
 220     /** Controls whether the DTD grammar produces balanced syntax trees. */
 221     protected boolean fBalanceSyntaxTrees;
 222 
 223     /** warn on duplicate attribute definition, this feature works only when validation is true */
 224     protected boolean fWarnDuplicateAttdef;
 225 
 226     // properties
 227 
 228     /** Symbol table. */
 229     protected SymbolTable fSymbolTable;
 230 
 231     /** Error reporter. */
 232     protected XMLErrorReporter fErrorReporter;
 233 
 234     // the grammar pool
 235     protected XMLGrammarPool fGrammarPool;
 236 
 237     /** Grammar bucket. */
 238     protected DTDGrammarBucket fGrammarBucket;
 239 
 240     /* location of the document as passed in from startDocument call */
 241     protected XMLLocator fDocLocation;
 242 
 243     /** Namespace support. */
 244     protected NamespaceContext fNamespaceContext = null;
 245 
 246     /** Datatype validator factory. */
 247     protected DTDDVFactory fDatatypeValidatorFactory;
 248 
 249     // handlers
 250 
 251     /** Document handler. */
 252     protected XMLDocumentHandler fDocumentHandler;
 253 
 254     protected XMLDocumentSource fDocumentSource;
 255     // grammars
 256 
 257     /** DTD Grammar. */
 258     protected DTDGrammar fDTDGrammar;
 259 
 260     // state
 261 
 262     /** True if seen DOCTYPE declaration. */
 263     protected boolean fSeenDoctypeDecl = false;
 264 
 265     /** Perform validation. */
 266     private boolean fPerformValidation;
 267 
 268     /** Schema type: None, DTD, Schema */
 269     private String fSchemaType;
 270 
 271     // information regarding the current element
 272 
 273     /** Current element name. */
 274     private final QName fCurrentElement = new QName();
 275 
 276     /** Current element index. */
 277     private int fCurrentElementIndex = -1;
 278 
 279     /** Current content spec type. */
 280     private int fCurrentContentSpecType = -1;
 281 
 282     /** The root element name. */
 283     private final QName fRootElement = new QName();
 284 
 285     private boolean fInCDATASection = false;
 286     // element stack
 287 
 288     /** Element index stack. */
 289     private int[] fElementIndexStack = new int[8];
 290 
 291     /** Content spec type stack. */
 292     private int[] fContentSpecTypeStack = new int[8];
 293 
 294     /** Element name stack. */
 295     private QName[] fElementQNamePartsStack = new QName[8];
 296 
 297     // children list and offset stack
 298 
 299     /**
 300      * Element children. This data structure is a growing stack that
 301      * holds the children of elements from the root to the current
 302      * element depth. This structure never gets "deeper" than the
 303      * deepest element. Space is re-used once each element is closed.
 304      * <p>
 305      * <strong>Note:</strong> This is much more efficient use of memory
 306      * than creating new arrays for each element depth.
 307      * <p>
 308      * <strong>Note:</strong> The use of this data structure is for
 309      * validation "on the way out". If the validation model changes to
 310      * "on the way in", then this data structure is not needed.
 311      */
 312     private QName[] fElementChildren = new QName[32];
 313 
 314     /** Element children count. */
 315     private int fElementChildrenLength = 0;
 316 
 317     /**
 318      * Element children offset stack. This stack refers to offsets
 319      * into the <code>fElementChildren</code> array.
 320      * @see #fElementChildren
 321      */
 322     private int[] fElementChildrenOffsetStack = new int[32];
 323 
 324     /** Element depth. */
 325     private int fElementDepth = -1;
 326 
 327     // validation states
 328 
 329     /** True if seen the root element. */
 330     private boolean fSeenRootElement = false;
 331 
 332     /** True if inside of element content. */
 333     private boolean fInElementContent = false;
 334 
 335     // temporary variables
 336 
 337     /** Temporary element declaration. */
 338     private XMLElementDecl fTempElementDecl = new XMLElementDecl();
 339 
 340     /** Temporary atribute declaration. */
 341     private final XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl();
 342 
 343     /** Temporary entity declaration. */
 344     private final XMLEntityDecl fEntityDecl = new XMLEntityDecl();
 345 
 346     /** Temporary qualified name. */
 347     private final QName fTempQName = new QName();
 348 
 349     /** Temporary string buffers. */
 350     private final StringBuffer fBuffer = new StringBuffer();
 351 
 352     // symbols: general
 353 
 354     // attribute validators
 355 
 356     /** Datatype validator: ID. */
 357     protected DatatypeValidator fValID;
 358 
 359     /** Datatype validator: IDREF. */
 360     protected DatatypeValidator fValIDRef;
 361 
 362     /** Datatype validator: IDREFS. */
 363     protected DatatypeValidator fValIDRefs;
 364 
 365     /** Datatype validator: ENTITY. */
 366     protected DatatypeValidator fValENTITY;
 367 
 368     /** Datatype validator: ENTITIES. */
 369     protected DatatypeValidator fValENTITIES;
 370 
 371     /** Datatype validator: NMTOKEN. */
 372     protected DatatypeValidator fValNMTOKEN;
 373 
 374     /** Datatype validator: NMTOKENS. */
 375     protected DatatypeValidator fValNMTOKENS;
 376 
 377     /** Datatype validator: NOTATION. */
 378     protected DatatypeValidator fValNOTATION;
 379 
 380     // to check for duplicate ID or ANNOTATION attribute declare in
 381     // ATTLIST, and misc VCs
 382 
 383     //
 384     // Constructors
 385     //
 386 
 387     /** Default constructor. */
 388     public XMLDTDValidator() {
 389 
 390         // initialize data
 391         for (int i = 0; i < fElementQNamePartsStack.length; i++) {
 392             fElementQNamePartsStack[i] = new QName();
 393         }
 394         fGrammarBucket = new DTDGrammarBucket();
 395 
 396     } // <init>()
 397 
 398     DTDGrammarBucket getGrammarBucket() {
 399         return fGrammarBucket;
 400     } // getGrammarBucket():  DTDGrammarBucket
 401 
 402     //
 403     // XMLComponent methods
 404     //
 405 
 406     /*
 407      * Resets the component. The component can query the component manager
 408      * about any features and properties that affect the operation of the
 409      * component.
 410      *
 411      * @param componentManager The component manager.
 412      *
 413      * @throws SAXException Thrown by component on finitialization error.
 414      *                      For example, if a feature or property is
 415      *                      required for the operation of the component, the
 416      *                      component manager may throw a
 417      *                      SAXNotRecognizedException or a
 418      *                      SAXNotSupportedException.
 419      */
 420     public void reset(XMLComponentManager componentManager)
 421     throws XMLConfigurationException {
 422 
 423         // clear grammars
 424         fDTDGrammar = null;
 425         fSeenDoctypeDecl = false;
 426         fInCDATASection = false;
 427         // initialize state
 428         fSeenRootElement = false;
 429         fInElementContent = false;
 430         fCurrentElementIndex = -1;
 431         fCurrentContentSpecType = -1;
 432 
 433         fRootElement.clear();
 434 
 435                 fValidationState.resetIDTables();
 436 
 437                 fGrammarBucket.clear();
 438                 fElementDepth = -1;
 439                 fElementChildrenLength = 0;
 440 
 441         boolean parser_settings = componentManager.getFeature(PARSER_SETTINGS, true);
 442 
 443         if (!parser_settings){
 444                 // parser settings have not been changed
 445                         fValidationManager.addValidationState(fValidationState);
 446                 return;
 447         }
 448 
 449         // sax features
 450         fNamespaces = componentManager.getFeature(NAMESPACES, true);
 451         fValidation = componentManager.getFeature(VALIDATION, false);
 452         fDTDValidation = !(componentManager.getFeature(Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE, false));
 453 
 454         // Xerces features
 455         fDynamicValidation = componentManager.getFeature(DYNAMIC_VALIDATION, false);
 456         fBalanceSyntaxTrees = componentManager.getFeature(BALANCE_SYNTAX_TREES, false);
 457         fWarnDuplicateAttdef = componentManager.getFeature(WARN_ON_DUPLICATE_ATTDEF, false);
 458 
 459         fSchemaType = (String)componentManager.getProperty (Constants.JAXP_PROPERTY_PREFIX
 460             + Constants.SCHEMA_LANGUAGE, null);
 461 
 462         fValidationManager= (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER);
 463         fValidationManager.addValidationState(fValidationState);
 464         fValidationState.setUsingNamespaces(fNamespaces);
 465 
 466         // get needed components
 467         fErrorReporter = (XMLErrorReporter)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.ERROR_REPORTER_PROPERTY);
 468         fSymbolTable = (SymbolTable)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.SYMBOL_TABLE_PROPERTY);
 469         fGrammarPool= (XMLGrammarPool)componentManager.getProperty(GRAMMAR_POOL, null);
 470 
 471         fDatatypeValidatorFactory = (DTDDVFactory)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY);
 472                 init();
 473 
 474     } // reset(XMLComponentManager)
 475 
 476     /**
 477      * Returns a list of feature identifiers that are recognized by
 478      * this component. This method may return null if no features
 479      * are recognized by this component.
 480      */
 481     public String[] getRecognizedFeatures() {
 482         return (String[])(RECOGNIZED_FEATURES.clone());
 483     } // getRecognizedFeatures():String[]
 484 
 485     /**
 486      * Sets the state of a feature. This method is called by the component
 487      * manager any time after reset when a feature changes state.
 488      * <p>
 489      * <strong>Note:</strong> Components should silently ignore features
 490      * that do not affect the operation of the component.
 491      *
 492      * @param featureId The feature identifier.
 493      * @param state     The state of the feature.
 494      *
 495      * @throws SAXNotRecognizedException The component should not throw
 496      *                                   this exception.
 497      * @throws SAXNotSupportedException The component should not throw
 498      *                                  this exception.
 499      */
 500     public void setFeature(String featureId, boolean state)
 501     throws XMLConfigurationException {
 502     } // setFeature(String,boolean)
 503 
 504     /**
 505      * Returns a list of property identifiers that are recognized by
 506      * this component. This method may return null if no properties
 507      * are recognized by this component.
 508      */
 509     public String[] getRecognizedProperties() {
 510         return (String[])(RECOGNIZED_PROPERTIES.clone());
 511     } // getRecognizedProperties():String[]
 512 
 513     /**
 514      * Sets the value of a property. This method is called by the component
 515      * manager any time after reset when a property changes value.
 516      * <p>
 517      * <strong>Note:</strong> Components should silently ignore properties
 518      * that do not affect the operation of the component.
 519      *
 520      * @param propertyId The property identifier.
 521      * @param value      The value of the property.
 522      *
 523      * @throws SAXNotRecognizedException The component should not throw
 524      *                                   this exception.
 525      * @throws SAXNotSupportedException The component should not throw
 526      *                                  this exception.
 527      */
 528     public void setProperty(String propertyId, Object value)
 529     throws XMLConfigurationException {
 530     } // setProperty(String,Object)
 531 
 532     /**
 533      * Returns the default state for a feature, or null if this
 534      * component does not want to report a default value for this
 535      * feature.
 536      *
 537      * @param featureId The feature identifier.
 538      *
 539      * @since Xerces 2.2.0
 540      */
 541     public Boolean getFeatureDefault(String featureId) {
 542         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
 543             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
 544                 return FEATURE_DEFAULTS[i];
 545             }
 546         }
 547         return null;
 548     } // getFeatureDefault(String):Boolean
 549 
 550     /**
 551      * Returns the default state for a property, or null if this
 552      * component does not want to report a default value for this
 553      * property.
 554      *
 555      * @param propertyId The property identifier.
 556      *
 557      * @since Xerces 2.2.0
 558      */
 559     public Object getPropertyDefault(String propertyId) {
 560         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
 561             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
 562                 return PROPERTY_DEFAULTS[i];
 563             }
 564         }
 565         return null;
 566     } // getPropertyDefault(String):Object
 567 
 568     //
 569     // XMLDocumentSource methods
 570     //
 571 
 572     /** Sets the document handler to receive information about the document. */
 573     public void setDocumentHandler(XMLDocumentHandler documentHandler) {
 574         fDocumentHandler = documentHandler;
 575     } // setDocumentHandler(XMLDocumentHandler)
 576 
 577     /** Returns the document handler */
 578     public XMLDocumentHandler getDocumentHandler() {
 579         return fDocumentHandler;
 580     } // getDocumentHandler():  XMLDocumentHandler
 581 
 582 
 583     //
 584     // XMLDocumentHandler methods
 585     //
 586 
 587     /** Sets the document source */
 588     public void setDocumentSource(XMLDocumentSource source){
 589         fDocumentSource = source;
 590     } // setDocumentSource
 591 
 592     /** Returns the document source */
 593     public XMLDocumentSource getDocumentSource (){
 594         return fDocumentSource;
 595     } // getDocumentSource
 596 
 597     /**
 598      * The start of the document.
 599      *
 600      * @param locator The system identifier of the entity if the entity
 601      *                 is external, null otherwise.
 602      * @param encoding The auto-detected IANA encoding name of the entity
 603      *                 stream. This value will be null in those situations
 604      *                 where the entity encoding is not auto-detected (e.g.
 605      *                 internal entities or a document entity that is
 606      *                 parsed from a java.io.Reader).
 607      * @param namespaceContext
 608      *                 The namespace context in effect at the
 609      *                 start of this document.
 610      *                 This object represents the current context.
 611      *                 Implementors of this class are responsible
 612      *                 for copying the namespace bindings from the
 613      *                 the current context (and its parent contexts)
 614      *                 if that information is important.
 615      * @param augs   Additional information that may include infoset augmentations
 616      *
 617      * @throws XNIException Thrown by handler to signal an error.
 618      */
 619     public void startDocument(XMLLocator locator, String encoding,
 620                               NamespaceContext namespaceContext, Augmentations augs)
 621     throws XNIException {
 622 
 623         // call handlers
 624         // get initial grammars
 625         if (fGrammarPool != null) {
 626             Grammar [] grammars = fGrammarPool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_DTD);
 627             final int length = (grammars != null) ? grammars.length : 0;
 628             for (int i = 0; i < length; ++i) {
 629                 fGrammarBucket.putGrammar((DTDGrammar)grammars[i]);
 630             }
 631         }
 632         fDocLocation = locator;
 633         fNamespaceContext = namespaceContext;
 634 
 635         if (fDocumentHandler != null) {
 636             fDocumentHandler.startDocument(locator, encoding, namespaceContext, augs);
 637         }
 638 
 639     } // startDocument(XMLLocator,String)
 640 
 641     /**
 642      * Notifies of the presence of an XMLDecl line in the document. If
 643      * present, this method will be called immediately following the
 644      * startDocument call.
 645      *
 646      * @param version    The XML version.
 647      * @param encoding   The IANA encoding name of the document, or null if
 648      *                   not specified.
 649      * @param standalone The standalone value, or null if not specified.
 650      * @param augs   Additional information that may include infoset augmentations
 651      *
 652      * @throws XNIException Thrown by handler to signal an error.
 653      */
 654     public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
 655     throws XNIException {
 656 
 657         // save standalone state
 658         fGrammarBucket.setStandalone(standalone != null && standalone.equals("yes"));
 659 
 660         // call handlers
 661         if (fDocumentHandler != null) {
 662             fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
 663         }
 664 
 665     } // xmlDecl(String,String,String)
 666 
 667     /**
 668      * Notifies of the presence of the DOCTYPE line in the document.
 669      *
 670      * @param rootElement The name of the root element.
 671      * @param publicId    The public identifier if an external DTD or null
 672      *                    if the external DTD is specified using SYSTEM.
 673      * @param systemId    The system identifier if an external DTD, null
 674      *                    otherwise.
 675      * @param augs   Additional information that may include infoset augmentations
 676      *
 677      * @throws XNIException Thrown by handler to signal an error.
 678      */
 679     public void doctypeDecl(String rootElement, String publicId, String systemId,
 680                             Augmentations augs)
 681     throws XNIException {
 682 
 683         // save root element state
 684         fSeenDoctypeDecl = true;
 685         fRootElement.setValues(null, rootElement, rootElement, null);
 686         // find or create grammar:
 687         String eid = null;
 688         try {
 689             eid = XMLEntityManager.expandSystemId(systemId, fDocLocation.getExpandedSystemId(), false);
 690         } catch (java.io.IOException e) {
 691         }
 692         XMLDTDDescription grammarDesc = new XMLDTDDescription(publicId, systemId, fDocLocation.getExpandedSystemId(), eid, rootElement);
 693         fDTDGrammar = fGrammarBucket.getGrammar(grammarDesc);
 694         if(fDTDGrammar == null) {
 695             // give grammar pool a chance...
 696             //
 697             // Do not bother checking the pool if no public or system identifier was provided.
 698             // Since so many different DTDs have roots in common, using only a root name as the
 699             // key may cause an unexpected grammar to be retrieved from the grammar pool. This scenario
 700             // would occur when an ExternalSubsetResolver has been queried and the
 701             // XMLInputSource returned contains an input stream but no external identifier.
 702             // This can never happen when the instance document specified a DOCTYPE. -- mrglavas
 703             if (fGrammarPool != null && (systemId != null || publicId != null)) {
 704                 fDTDGrammar = (DTDGrammar)fGrammarPool.retrieveGrammar(grammarDesc);
 705             }
 706         }
 707         if(fDTDGrammar == null) {
 708             // we'll have to create it...
 709             if (!fBalanceSyntaxTrees) {
 710                 fDTDGrammar = new DTDGrammar(fSymbolTable, grammarDesc);
 711             }
 712             else {
 713                 fDTDGrammar = new BalancedDTDGrammar(fSymbolTable, grammarDesc);
 714             }
 715         } else {
 716             // we've found a cached one;so let's make sure not to read
 717             // any external subset!
 718             fValidationManager.setCachedDTD(true);
 719         }
 720         fGrammarBucket.setActiveGrammar(fDTDGrammar);
 721 
 722         // call handlers
 723         if (fDocumentHandler != null) {
 724             fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
 725         }
 726 
 727     } // doctypeDecl(String,String,String, Augmentations)
 728 
 729 
 730     /**
 731      * The start of an element.
 732      *
 733      * @param element    The name of the element.
 734      * @param attributes The element attributes.
 735      * @param augs   Additional information that may include infoset augmentations
 736      *
 737      * @throws XNIException Thrown by handler to signal an error.
 738      */
 739     public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
 740     throws XNIException {
 741 
 742         handleStartElement(element, attributes, augs);
 743         // call handlers
 744         if (fDocumentHandler != null) {
 745             fDocumentHandler.startElement(element, attributes, augs);
 746 
 747         }
 748 
 749     } // startElement(QName,XMLAttributes)
 750 
 751     /**
 752      * An empty element.
 753      *
 754      * @param element    The name of the element.
 755      * @param attributes The element attributes.
 756      * @param augs   Additional information that may include infoset augmentations
 757      *
 758      * @throws XNIException Thrown by handler to signal an error.
 759      */
 760     public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
 761     throws XNIException {
 762 
 763         boolean removed = handleStartElement(element, attributes, augs);
 764 
 765         if (fDocumentHandler !=null) {
 766             fDocumentHandler.emptyElement(element, attributes, augs);
 767         }
 768         if (!removed) {
 769             handleEndElement(element, augs, true);
 770         }
 771 
 772 
 773     } // emptyElement(QName,XMLAttributes)
 774 
 775     /**
 776      * Character content.
 777      *
 778      * @param text The content.
 779      *
 780      * @param augs   Additional information that may include infoset augmentations
 781      *
 782      * @throws XNIException Thrown by handler to signal an error.
 783      */
 784     public void characters(XMLString text, Augmentations augs) throws XNIException {
 785 
 786         boolean callNextCharacters = true;
 787 
 788         // REVISIT: [Q] Is there a more efficient way of doing this?
 789         //          Perhaps if the scanner told us so we don't have to
 790         //          look at the characters again. -Ac
 791         boolean allWhiteSpace = true;
 792         for (int i=text.offset; i< text.offset+text.length; i++) {
 793             if (!isSpace(text.ch[i])) {
 794                 allWhiteSpace = false;
 795                 break;
 796             }
 797         }
 798         // call the ignoreableWhiteSpace callback
 799         // never call ignorableWhitespace if we are in cdata section
 800         if (fInElementContent && allWhiteSpace && !fInCDATASection) {
 801             if (fDocumentHandler != null) {
 802                 fDocumentHandler.ignorableWhitespace(text, augs);
 803                 callNextCharacters = false;
 804             }
 805         }
 806 
 807         // validate
 808         if (fPerformValidation) {
 809             if (fInElementContent) {
 810                 if (fGrammarBucket.getStandalone() &&
 811                     fDTDGrammar.getElementDeclIsExternal(fCurrentElementIndex)) {
 812                     if (allWhiteSpace) {
 813                         fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
 814                                                     "MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE",
 815                                                     null, XMLErrorReporter.SEVERITY_ERROR);
 816                     }
 817                 }
 818                 if (!allWhiteSpace) {
 819                     charDataInContent();
 820                 }
 821 
 822                 // For E15.2
 823                 if (augs != null && augs.getItem(Constants.CHAR_REF_PROBABLE_WS) == Boolean.TRUE) {
 824                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 825                                                "MSG_CONTENT_INVALID_SPECIFIED",
 826                                                new Object[]{ fCurrentElement.rawname,
 827                                                    fDTDGrammar.getContentSpecAsString(fElementDepth),
 828                                                    "character reference"},
 829                                                XMLErrorReporter.SEVERITY_ERROR);
 830                 }
 831             }
 832 
 833             if (fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
 834                 charDataInContent();
 835             }
 836         }
 837 
 838         // call handlers
 839         if (callNextCharacters && fDocumentHandler != null) {
 840             fDocumentHandler.characters(text, augs);
 841         }
 842 
 843     } // characters(XMLString)
 844 
 845 
 846 
 847     /**
 848      * Ignorable whitespace. For this method to be called, the document
 849      * source must have some way of determining that the text containing
 850      * only whitespace characters should be considered ignorable. For
 851      * example, the validator can determine if a length of whitespace
 852      * characters in the document are ignorable based on the element
 853      * content model.
 854      *
 855      * @param text The ignorable whitespace.
 856      * @param augs   Additional information that may include infoset augmentations
 857      *
 858      * @throws XNIException Thrown by handler to signal an error.
 859      */
 860     public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
 861 
 862         // call handlers
 863         if (fDocumentHandler != null) {
 864             fDocumentHandler.ignorableWhitespace(text, augs);
 865         }
 866 
 867     } // ignorableWhitespace(XMLString)
 868 
 869     /**
 870      * The end of an element.
 871      *
 872      * @param element The name of the element.
 873      * @param augs   Additional information that may include infoset augmentations
 874      *
 875      * @throws XNIException Thrown by handler to signal an error.
 876      */
 877     public void endElement(QName element, Augmentations augs) throws XNIException {
 878 
 879         handleEndElement(element,  augs, false);
 880 
 881     } // endElement(QName)
 882 
 883     /**
 884      * The start of a CDATA section.
 885      * @param augs   Additional information that may include infoset augmentations
 886      *
 887      * @throws XNIException Thrown by handler to signal an error.
 888      */
 889     public void startCDATA(Augmentations augs) throws XNIException {
 890 
 891         if (fPerformValidation && fInElementContent) {
 892             charDataInContent();
 893         }
 894         fInCDATASection = true;
 895         // call handlers
 896         if (fDocumentHandler != null) {
 897             fDocumentHandler.startCDATA(augs);
 898         }
 899 
 900     } // startCDATA()
 901 
 902     /**
 903      * The end of a CDATA section.
 904      * @param augs   Additional information that may include infoset augmentations
 905      *
 906      * @throws XNIException Thrown by handler to signal an error.
 907      */
 908     public void endCDATA(Augmentations augs) throws XNIException {
 909 
 910         fInCDATASection = false;
 911         // call handlers
 912         if (fDocumentHandler != null) {
 913             fDocumentHandler.endCDATA(augs);
 914         }
 915 
 916     } // endCDATA()
 917 
 918     /**
 919      * The end of the document.
 920      * @param augs   Additional information that may include infoset augmentations
 921      *
 922      * @throws XNIException Thrown by handler to signal an error.
 923      */
 924     public void endDocument(Augmentations augs) throws XNIException {
 925 
 926         // call handlers
 927         if (fDocumentHandler != null) {
 928             fDocumentHandler.endDocument(augs);
 929         }
 930 
 931     } // endDocument()
 932 
 933     /**
 934      * A comment.
 935      *
 936      * @param text The text in the comment.
 937      * @param augs   Additional information that may include infoset augmentations
 938      *
 939      * @throws XNIException Thrown by application to signal an error.
 940      */
 941     public void comment(XMLString text, Augmentations augs) throws XNIException {
 942         // fixes E15.1
 943         if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) {
 944             fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
 945             if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
 946                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 947                                                "MSG_CONTENT_INVALID_SPECIFIED",
 948                                                new Object[]{ fCurrentElement.rawname,
 949                                                              "EMPTY",
 950                                                              "comment"},
 951                                                XMLErrorReporter.SEVERITY_ERROR);
 952             }
 953         }
 954         // call handlers
 955         if (fDocumentHandler != null) {
 956             fDocumentHandler.comment(text, augs);
 957         }
 958 
 959     } // comment(XMLString)
 960 
 961 
 962     /**
 963      * A processing instruction. Processing instructions consist of a
 964      * target name and, optionally, text data. The data is only meaningful
 965      * to the application.
 966      * <p>
 967      * Typically, a processing instruction's data will contain a series
 968      * of pseudo-attributes. These pseudo-attributes follow the form of
 969      * element attributes but are <strong>not</strong> parsed or presented
 970      * to the application as anything other than text. The application is
 971      * responsible for parsing the data.
 972      *
 973      * @param target The target.
 974      * @param data   The data or null if none specified.
 975      * @param augs   Additional information that may include infoset augmentations
 976      *
 977      * @throws XNIException Thrown by handler to signal an error.
 978      */
 979     public void processingInstruction(String target, XMLString data, Augmentations augs)
 980     throws XNIException {
 981 
 982         // fixes E15.1
 983         if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) {
 984             fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
 985             if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
 986                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 987                                                "MSG_CONTENT_INVALID_SPECIFIED",
 988                                                new Object[]{ fCurrentElement.rawname,
 989                                                              "EMPTY",
 990                                                              "processing instruction"},
 991                                                XMLErrorReporter.SEVERITY_ERROR);
 992             }
 993         }
 994         // call handlers
 995         if (fDocumentHandler != null) {
 996             fDocumentHandler.processingInstruction(target, data, augs);
 997         }
 998     } // processingInstruction(String,XMLString)
 999 
1000     /**
1001      * This method notifies the start of a general entity.
1002      * <p>
1003      * <strong>Note:</strong> This method is not called for entity references
1004      * appearing as part of attribute values.
1005      *
1006      * @param name     The name of the general entity.
1007      * @param identifier The resource identifier.
1008      * @param encoding The auto-detected IANA encoding name of the entity
1009      *                 stream. This value will be null in those situations
1010      *                 where the entity encoding is not auto-detected (e.g.
1011      *                 internal entities or a document entity that is
1012      *                 parsed from a java.io.Reader).
1013      * @param augs     Additional information that may include infoset augmentations
1014      *
1015      * @exception XNIException Thrown by handler to signal an error.
1016      */
1017     public void startGeneralEntity(String name,
1018                                    XMLResourceIdentifier identifier,
1019                                    String encoding,
1020                                    Augmentations augs) throws XNIException {
1021         if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) {
1022             fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
1023             // fixes E15.1
1024             if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
1025                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1026                                            "MSG_CONTENT_INVALID_SPECIFIED",
1027                                            new Object[]{ fCurrentElement.rawname,
1028                                                          "EMPTY", "ENTITY"},
1029                                            XMLErrorReporter.SEVERITY_ERROR);
1030             }
1031             if (fGrammarBucket.getStandalone()) {
1032                 XMLDTDLoader.checkStandaloneEntityRef(name, fDTDGrammar, fEntityDecl, fErrorReporter);
1033             }
1034         }
1035         if (fDocumentHandler != null) {
1036             fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
1037         }
1038     }
1039 
1040     /**
1041      * This method notifies the end of a general entity.
1042      * <p>
1043      * <strong>Note:</strong> This method is not called for entity references
1044      * appearing as part of attribute values.
1045      *
1046      * @param name   The name of the entity.
1047      * @param augs   Additional information that may include infoset augmentations
1048      *
1049      * @exception XNIException
1050      *                   Thrown by handler to signal an error.
1051      */
1052     public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
1053         // call handlers
1054         if (fDocumentHandler != null) {
1055             fDocumentHandler.endGeneralEntity(name, augs);
1056         }
1057     } // endEntity(String)
1058 
1059     /**
1060      * Notifies of the presence of a TextDecl line in an entity. If present,
1061      * this method will be called immediately following the startParameterEntity call.
1062      * <p>
1063      * <strong>Note:</strong> This method is only called for external
1064      * parameter entities referenced in the DTD.
1065      *
1066      * @param version  The XML version, or null if not specified.
1067      * @param encoding The IANA encoding name of the entity.
1068      * @param augs Additional information that may include infoset
1069      *                      augmentations.
1070      *
1071      * @throws XNIException Thrown by handler to signal an error.
1072      */
1073     public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {
1074 
1075         // call handlers
1076         if (fDocumentHandler != null) {
1077             fDocumentHandler.textDecl(version, encoding, augs);
1078         }
1079     }
1080 
1081 
1082     public final boolean hasGrammar(){
1083 
1084         return (fDTDGrammar != null);
1085     }
1086 
1087     public final boolean validate(){
1088         // Do validation if all of the following are true:
1089         // 1. The JAXP Schema Language property is not XML Schema
1090         //    REVISIT: since only DTD and Schema are supported at this time,
1091         //             such checking is sufficient. but if more schema types
1092         //             are introduced in the future, we'll need to change it
1093         //             to something like
1094         //             (fSchemaType == null || fSchemaType == NS_XML_DTD)
1095         // 2. One of the following is true (validation features)
1096         // 2.1 Dynamic validation is off, and validation is on
1097         // 2.2 Dynamic validation is on, and DOCTYPE was seen
1098         // 3 Xerces schema validation feature is off, or DOCTYPE was seen.
1099         return (fSchemaType != Constants.NS_XMLSCHEMA) &&
1100                (!fDynamicValidation && fValidation ||
1101                 fDynamicValidation && fSeenDoctypeDecl) &&
1102                (fDTDValidation || fSeenDoctypeDecl);
1103     }
1104 
1105             //REVISIT:we can convert into functions.. adding default attribute values.. and one validating.
1106 
1107     /** Add default attributes and validate. */
1108     protected void addDTDDefaultAttrsAndValidate(QName elementName, int elementIndex,
1109                                                XMLAttributes attributes)
1110     throws XNIException {
1111 
1112         // is there anything to do?
1113         if (elementIndex == -1 || fDTDGrammar == null) {
1114             return;
1115         }
1116 
1117         //
1118         // Check after all specified attrs are scanned
1119         // (1) report error for REQUIRED attrs that are missing (V_TAGc)
1120         // (2) add default attrs (FIXED and NOT_FIXED)
1121         //
1122         int attlistIndex = fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
1123 
1124         while (attlistIndex != -1) {
1125 
1126             fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttDecl);
1127 
1128             if (DEBUG_ATTRIBUTES) {
1129                 if (fTempAttDecl != null) {
1130                     XMLElementDecl elementDecl = new XMLElementDecl();
1131                     fDTDGrammar.getElementDecl(elementIndex, elementDecl);
1132                     System.out.println("element: "+(elementDecl.name.localpart));
1133                     System.out.println("attlistIndex " + attlistIndex + "\n"+
1134                                        "attName : '"+(fTempAttDecl.name.localpart) + "'\n"
1135                                        + "attType : "+fTempAttDecl.simpleType.type + "\n"
1136                                        + "attDefaultType : "+fTempAttDecl.simpleType.defaultType + "\n"
1137                                        + "attDefaultValue : '"+fTempAttDecl.simpleType.defaultValue + "'\n"
1138                                        + attributes.getLength() +"\n"
1139                                       );
1140                 }
1141             }
1142             String attPrefix = fTempAttDecl.name.prefix;
1143             String attLocalpart = fTempAttDecl.name.localpart;
1144             String attRawName = fTempAttDecl.name.rawname;
1145             String attType = getAttributeTypeName(fTempAttDecl);
1146             int attDefaultType =fTempAttDecl.simpleType.defaultType;
1147             String attValue = null;
1148 
1149             if (fTempAttDecl.simpleType.defaultValue != null) {
1150                 attValue = fTempAttDecl.simpleType.defaultValue;
1151             }
1152 
1153             boolean specified = false;
1154             boolean required = attDefaultType == XMLSimpleType.DEFAULT_TYPE_REQUIRED;
1155             boolean cdata = attType == XMLSymbols.fCDATASymbol;
1156 
1157             if (!cdata || required || attValue != null) {
1158                 int attrCount = attributes.getLength();
1159                 for (int i = 0; i < attrCount; i++) {
1160                     if (attributes.getQName(i) == attRawName) {
1161                         specified = true;
1162                         break;
1163                     }
1164                 }
1165             }
1166 
1167             if (!specified) {
1168                 if (required) {
1169                     if (fPerformValidation) {
1170                         Object[] args = {elementName.localpart, attRawName};
1171                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1172                                                    "MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED", args,
1173                                                    XMLErrorReporter.SEVERITY_ERROR);
1174                     }
1175                 }
1176                 else if (attValue != null) {
1177                     if (fPerformValidation && fGrammarBucket.getStandalone()) {
1178                         if (fDTDGrammar.getAttributeDeclIsExternal(attlistIndex)) {
1179 
1180                             Object[] args = { elementName.localpart, attRawName};
1181                             fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1182                                                        "MSG_DEFAULTED_ATTRIBUTE_NOT_SPECIFIED", args,
1183                                                        XMLErrorReporter.SEVERITY_ERROR);
1184                         }
1185                     }
1186 
1187                     // add namespace information
1188                     if (fNamespaces) {
1189                         int index = attRawName.indexOf(':');
1190                         if (index != -1) {
1191                             attPrefix = attRawName.substring(0, index);
1192                             attPrefix = fSymbolTable.addSymbol(attPrefix);
1193                             attLocalpart = attRawName.substring(index + 1);
1194                             attLocalpart = fSymbolTable.addSymbol(attLocalpart);
1195                         }
1196                     }
1197 
1198                     // add attribute
1199                     fTempQName.setValues(attPrefix, attLocalpart, attRawName, fTempAttDecl.name.uri);
1200                     int newAttr = attributes.addAttribute(fTempQName, attType, attValue);
1201                 }
1202             }
1203             // get next att decl in the Grammar for this element
1204             attlistIndex = fDTDGrammar.getNextAttributeDeclIndex(attlistIndex);
1205         }
1206 
1207         // now iterate through the expanded attributes for
1208         // 1. if every attribute seen is declared in the DTD
1209         // 2. check if the VC: default_fixed holds
1210         // 3. validate every attribute.
1211         int attrCount = attributes.getLength();
1212         for (int i = 0; i < attrCount; i++) {
1213             String attrRawName = attributes.getQName(i);
1214             boolean declared = false;
1215             if (fPerformValidation) {
1216                 if (fGrammarBucket.getStandalone()) {
1217                     // check VC: Standalone Document Declaration, entities
1218                     // references appear in the document.
1219                     // REVISIT: this can be combined to a single check in
1220                     // startEntity if we add one more argument in
1221                     // startEnity, inAttrValue
1222                     String nonNormalizedValue = attributes.getNonNormalizedValue(i);
1223                     if (nonNormalizedValue != null) {
1224                         String entityName = getExternalEntityRefInAttrValue(nonNormalizedValue);
1225                         if (entityName != null) {
1226                             fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1227                                                        "MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
1228                                                        new Object[]{entityName},
1229                                                        XMLErrorReporter.SEVERITY_ERROR);
1230                         }
1231                     }
1232                 }
1233             }
1234             int attDefIndex = -1;
1235             int position =
1236             fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
1237             while (position != -1) {
1238                 fDTDGrammar.getAttributeDecl(position, fTempAttDecl);
1239                 if (fTempAttDecl.name.rawname == attrRawName) {
1240                     // found the match att decl,
1241                     attDefIndex = position;
1242                     declared = true;
1243                     break;
1244                 }
1245                 position = fDTDGrammar.getNextAttributeDeclIndex(position);
1246             }
1247             if (!declared) {
1248                 if (fPerformValidation) {
1249                     // REVISIT - cache the elem/attr tuple so that we only
1250                     // give this error once for each unique occurrence
1251                     Object[] args = { elementName.rawname, attrRawName};
1252 
1253                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1254                                                "MSG_ATTRIBUTE_NOT_DECLARED",
1255                                                args,XMLErrorReporter.SEVERITY_ERROR);
1256                 }
1257                 continue;
1258             }
1259             // attribute is declared
1260 
1261             // fTempAttDecl should have the right value set now, so
1262             // the following is not needed
1263             // fGrammar.getAttributeDecl(attDefIndex,fTempAttDecl);
1264 
1265             String type = getAttributeTypeName(fTempAttDecl);
1266             attributes.setType(i, type);
1267             attributes.getAugmentations(i).putItem(Constants.ATTRIBUTE_DECLARED, Boolean.TRUE);
1268 
1269             boolean changedByNormalization = false;
1270             String oldValue = attributes.getValue(i);
1271             String attrValue = oldValue;
1272             if (attributes.isSpecified(i) && type != XMLSymbols.fCDATASymbol) {
1273                 changedByNormalization = normalizeAttrValue(attributes, i);
1274                 attrValue = attributes.getValue(i);
1275                 if (fPerformValidation && fGrammarBucket.getStandalone()
1276                     && changedByNormalization
1277                     && fDTDGrammar.getAttributeDeclIsExternal(position)
1278                    ) {
1279                     // check VC: Standalone Document Declaration
1280                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1281                                                "MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE",
1282                                                new Object[]{attrRawName, oldValue, attrValue},
1283                                                XMLErrorReporter.SEVERITY_ERROR);
1284                 }
1285             }
1286             if (!fPerformValidation) {
1287                 continue;
1288             }
1289             if (fTempAttDecl.simpleType.defaultType ==
1290                 XMLSimpleType.DEFAULT_TYPE_FIXED) {
1291                 String defaultValue = fTempAttDecl.simpleType.defaultValue;
1292 
1293                 if (!attrValue.equals(defaultValue)) {
1294                     Object[] args = {elementName.localpart,
1295                         attrRawName,
1296                         attrValue,
1297                         defaultValue};
1298                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1299                                                "MSG_FIXED_ATTVALUE_INVALID",
1300                                                args, XMLErrorReporter.SEVERITY_ERROR);
1301                 }
1302             }
1303 
1304             if (fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENTITY ||
1305                 fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENUMERATION ||
1306                 fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ID ||
1307                 fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_IDREF ||
1308                 fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NMTOKEN ||
1309                 fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NOTATION
1310                ) {
1311                 validateDTDattribute(elementName, attrValue, fTempAttDecl);
1312             }
1313         } // for all attributes
1314 
1315     } // addDTDDefaultAttrsAndValidate(int,XMLAttrList)
1316 
1317     /** Checks entities in attribute values for standalone VC. */
1318     protected String getExternalEntityRefInAttrValue(String nonNormalizedValue) {
1319         int valLength = nonNormalizedValue.length();
1320         int ampIndex = nonNormalizedValue.indexOf('&');
1321         while (ampIndex != -1) {
1322             if (ampIndex + 1 < valLength &&
1323                 nonNormalizedValue.charAt(ampIndex+1) != '#') {
1324                 int semicolonIndex = nonNormalizedValue.indexOf(';', ampIndex+1);
1325                 String entityName = nonNormalizedValue.substring(ampIndex+1, semicolonIndex);
1326                 entityName = fSymbolTable.addSymbol(entityName);
1327                 int entIndex = fDTDGrammar.getEntityDeclIndex(entityName);
1328                 if (entIndex > -1) {
1329                     fDTDGrammar.getEntityDecl(entIndex, fEntityDecl);
1330                     if (fEntityDecl.inExternal ||
1331                         (entityName = getExternalEntityRefInAttrValue(fEntityDecl.value)) != null) {
1332                         return entityName;
1333                     }
1334                 }
1335             }
1336             ampIndex = nonNormalizedValue.indexOf('&', ampIndex+1);
1337         }
1338         return null;
1339     } // isExternalEntityRefInAttrValue(String):String
1340 
1341     /**
1342      * Validate attributes in DTD fashion.
1343      */
1344     protected void validateDTDattribute(QName element, String attValue,
1345                                       XMLAttributeDecl attributeDecl)
1346     throws XNIException {
1347 
1348         switch (attributeDecl.simpleType.type) {
1349         case XMLSimpleType.TYPE_ENTITY: {
1350                 // NOTE: Save this information because invalidStandaloneAttDef
1351                 boolean isAlistAttribute = attributeDecl.simpleType.list;
1352 
1353                 try {
1354                     if (isAlistAttribute) {
1355                         fValENTITIES.validate(attValue, fValidationState);
1356                     }
1357                     else {
1358                         fValENTITY.validate(attValue, fValidationState);
1359                     }
1360                 }
1361                 catch (InvalidDatatypeValueException ex) {
1362                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1363                                                ex.getKey(),
1364                                                ex.getArgs(),
1365                                                XMLErrorReporter.SEVERITY_ERROR );
1366 
1367                 }
1368                 break;
1369             }
1370 
1371         case XMLSimpleType.TYPE_NOTATION:
1372         case XMLSimpleType.TYPE_ENUMERATION: {
1373                 boolean found = false;
1374                 String [] enumVals = attributeDecl.simpleType.enumeration;
1375                 if (enumVals == null) {
1376                     found = false;
1377                 }
1378                 else
1379                     for (int i = 0; i < enumVals.length; i++) {
1380                         if (attValue == enumVals[i] || attValue.equals(enumVals[i])) {
1381                             found = true;
1382                             break;
1383                         }
1384                     }
1385 
1386                 if (!found) {
1387                     StringBuffer enumValueString = new StringBuffer();
1388                     if (enumVals != null)
1389                         for (int i = 0; i < enumVals.length; i++) {
1390                             enumValueString.append(enumVals[i]+" ");
1391                         }
1392                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1393                                                "MSG_ATTRIBUTE_VALUE_NOT_IN_LIST",
1394                                                new Object[]{attributeDecl.name.rawname, attValue, enumValueString},
1395                                                XMLErrorReporter.SEVERITY_ERROR);
1396                 }
1397                 break;
1398             }
1399 
1400         case XMLSimpleType.TYPE_ID: {
1401                 try {
1402                     fValID.validate(attValue, fValidationState);
1403                 }
1404                 catch (InvalidDatatypeValueException ex) {
1405                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1406                                                ex.getKey(),
1407                                                ex.getArgs(),
1408                                                XMLErrorReporter.SEVERITY_ERROR );
1409                 }
1410                 break;
1411             }
1412 
1413         case XMLSimpleType.TYPE_IDREF: {
1414                 boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef
1415 
1416                 try {
1417                     if (isAlistAttribute) {
1418                         fValIDRefs.validate(attValue, fValidationState);
1419                     }
1420                     else {
1421                         fValIDRef.validate(attValue, fValidationState);
1422                     }
1423                 }
1424                 catch (InvalidDatatypeValueException ex) {
1425                     if (isAlistAttribute) {
1426                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1427                                                    "IDREFSInvalid",
1428                                                    new Object[]{attValue},
1429                                                    XMLErrorReporter.SEVERITY_ERROR );
1430                     }
1431                     else {
1432                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1433                                                    ex.getKey(),
1434                                                    ex.getArgs(),
1435                                                    XMLErrorReporter.SEVERITY_ERROR );
1436                     }
1437 
1438                 }
1439                 break;
1440             }
1441 
1442         case XMLSimpleType.TYPE_NMTOKEN: {
1443                 boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef
1444                 //changes fTempAttDef
1445                 try {
1446                     if (isAlistAttribute) {
1447                         fValNMTOKENS.validate(attValue, fValidationState);
1448                     }
1449                     else {
1450                         fValNMTOKEN.validate(attValue, fValidationState);
1451                     }
1452                 }
1453                 catch (InvalidDatatypeValueException ex) {
1454                     if (isAlistAttribute) {
1455                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1456                                                    "NMTOKENSInvalid",
1457                                                    new Object[] { attValue},
1458                                                    XMLErrorReporter.SEVERITY_ERROR);
1459                     }
1460                     else {
1461                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1462                                                    "NMTOKENInvalid",
1463                                                    new Object[] { attValue},
1464                                                    XMLErrorReporter.SEVERITY_ERROR);
1465                     }
1466                 }
1467                 break;
1468             }
1469 
1470         } // switch
1471 
1472     } // validateDTDattribute(QName,String,XMLAttributeDecl)
1473 
1474 
1475     /** Returns true if invalid standalone attribute definition. */
1476     protected boolean invalidStandaloneAttDef(QName element, QName attribute) {
1477         // REVISIT: This obviously needs to be fixed! -Ac
1478         boolean state = true;
1479         /*
1480        if (fStandaloneReader == -1) {
1481           return false;
1482        }
1483        // we are normalizing a default att value...  this ok?
1484        if (element.rawname == -1) {
1485           return false;
1486        }
1487        return getAttDefIsExternal(element, attribute);
1488        */
1489         return state;
1490     }
1491 
1492 
1493     //
1494     // Private methods
1495     //
1496 
1497 
1498     /**
1499      * Normalize the attribute value of a non CDATA attributes collapsing
1500      * sequences of space characters (x20)
1501      *
1502      * @param attributes The list of attributes
1503      * @param index The index of the attribute to normalize
1504      */
1505     private boolean normalizeAttrValue(XMLAttributes attributes, int index) {
1506         // vars
1507         boolean leadingSpace = true;
1508         boolean spaceStart = false;
1509         boolean readingNonSpace = false;
1510         int count = 0;
1511         int eaten = 0;
1512         String attrValue = attributes.getValue(index);
1513         char[] attValue = new char[attrValue.length()];
1514 
1515         fBuffer.setLength(0);
1516         attrValue.getChars(0, attrValue.length(), attValue, 0);
1517         for (int i = 0; i < attValue.length; i++) {
1518 
1519             if (attValue[i] == ' ') {
1520 
1521                 // now the tricky part
1522                 if (readingNonSpace) {
1523                     spaceStart = true;
1524                     readingNonSpace = false;
1525                 }
1526 
1527                 if (spaceStart && !leadingSpace) {
1528                     spaceStart = false;
1529                     fBuffer.append(attValue[i]);
1530                     count++;
1531                 }
1532                 else {
1533                     if (leadingSpace || !spaceStart) {
1534                         eaten ++;
1535                         /*** BUG #3512 ***
1536                         int entityCount = attributes.getEntityCount(index);
1537                         for (int j = 0;  j < entityCount; j++) {
1538                             int offset = attributes.getEntityOffset(index, j);
1539                             int length = attributes.getEntityLength(index, j);
1540                             if (offset <= i-eaten+1) {
1541                                 if (offset+length >= i-eaten+1) {
1542                                     if (length > 0)
1543                                         length--;
1544                                 }
1545                             }
1546                             else {
1547                                 if (offset > 0)
1548                                     offset--;
1549                             }
1550                             attributes.setEntityOffset(index, j, offset);
1551                             attributes.setEntityLength(index, j, length);
1552                         }
1553                         /***/
1554                     }
1555                 }
1556 
1557             }
1558             else {
1559                 readingNonSpace = true;
1560                 spaceStart = false;
1561                 leadingSpace = false;
1562                 fBuffer.append(attValue[i]);
1563                 count++;
1564             }
1565         }
1566 
1567         // check if the last appended character is a space.
1568         if (count > 0 && fBuffer.charAt(count-1) == ' ') {
1569             fBuffer.setLength(count-1);
1570             /*** BUG #3512 ***
1571             int entityCount = attributes.getEntityCount(index);
1572             for (int j=0;  j < entityCount; j++) {
1573                 int offset = attributes.getEntityOffset(index, j);
1574                 int length = attributes.getEntityLength(index, j);
1575                 if (offset < count-1) {
1576                     if (offset+length == count) {
1577                         length--;
1578                     }
1579                 }
1580                 else {
1581                     offset--;
1582                 }
1583                 attributes.setEntityOffset(index, j, offset);
1584                 attributes.setEntityLength(index, j, length);
1585             }
1586             /***/
1587         }
1588         String newValue = fBuffer.toString();
1589         attributes.setValue(index, newValue);
1590         return ! attrValue.equals(newValue);
1591     }
1592 
1593     /** Root element specified. */
1594     private final void rootElementSpecified(QName rootElement) throws XNIException {
1595         if (fPerformValidation) {
1596             String root1 = fRootElement.rawname;
1597             String root2 = rootElement.rawname;
1598             if (root1 == null || !root1.equals(root2)) {
1599                 fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
1600                                             "RootElementTypeMustMatchDoctypedecl",
1601                                             new Object[]{root1, root2},
1602                                             XMLErrorReporter.SEVERITY_ERROR);
1603             }
1604         }
1605     } // rootElementSpecified(QName)
1606 
1607     /**
1608      * Check that the content of an element is valid.
1609      * <p>
1610      * This is the method of primary concern to the validator. This method is called
1611      * upon the scanner reaching the end tag of an element. At that time, the
1612      * element's children must be structurally validated, so it calls this method.
1613      * The index of the element being checked (in the decl pool), is provided as
1614      * well as an array of element name indexes of the children. The validator must
1615      * confirm that this element can have these children in this order.
1616      * <p>
1617      * This can also be called to do 'what if' testing of content models just to see
1618      * if they would be valid.
1619      * <p>
1620      * Note that the element index is an index into the element decl pool, whereas
1621      * the children indexes are name indexes, i.e. into the string pool.
1622      * <p>
1623      * A value of -1 in the children array indicates a PCDATA node. All other
1624      * indexes will be positive and represent child elements. The count can be
1625      * zero, since some elements have the EMPTY content model and that must be
1626      * confirmed.
1627      *
1628      * @param elementIndex The index within the <code>ElementDeclPool</code> of this
1629      *                     element.
1630      * @param childCount The number of entries in the <code>children</code> array.
1631      * @param children The children of this element.
1632      *
1633      * @return The value -1 if fully valid, else the 0 based index of the child
1634      *         that first failed. If the value returned is equal to the number
1635      *         of children, then additional content is required to reach a valid
1636      *         ending state.
1637      *
1638      * @exception Exception Thrown on error.
1639      */
1640     private int checkContent(int elementIndex,
1641                              QName[] children,
1642                              int childOffset,
1643                              int childCount) throws XNIException {
1644 
1645         fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
1646 
1647         // Get the element name index from the element
1648         final String elementType = fCurrentElement.rawname;
1649 
1650         // Get out the content spec for this element
1651         final int contentType = fCurrentContentSpecType;
1652 
1653 
1654         //
1655         //  Deal with the possible types of content. We try to optimized here
1656         //  by dealing specially with content models that don't require the
1657         //  full DFA treatment.
1658         //
1659         if (contentType == XMLElementDecl.TYPE_EMPTY) {
1660             //
1661             //  If the child count is greater than zero, then this is
1662             //  an error right off the bat at index 0.
1663             //
1664             if (childCount != 0) {
1665                 return 0;
1666             }
1667         }
1668         else if (contentType == XMLElementDecl.TYPE_ANY) {
1669             //
1670             //  This one is open game so we don't pass any judgement on it
1671             //  at all. Its assumed to fine since it can hold anything.
1672             //
1673         }
1674         else if (contentType == XMLElementDecl.TYPE_MIXED ||
1675                  contentType == XMLElementDecl.TYPE_CHILDREN) {
1676             // Get the content model for this element, faulting it in if needed
1677             ContentModelValidator cmElem = null;
1678             cmElem = fTempElementDecl.contentModelValidator;
1679             int result = cmElem.validate(children, childOffset, childCount);
1680             return result;
1681         }
1682         else if (contentType == -1) {
1683             //REVISIT
1684             /****
1685             reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
1686                                       XMLMessages.VC_ELEMENT_VALID,
1687                                       elementType);
1688             /****/
1689         }
1690         else if (contentType == XMLElementDecl.TYPE_SIMPLE) {
1691 
1692             //REVISIT
1693             // this should never be reached in the case of DTD validation.
1694 
1695         }
1696         else {
1697             //REVISIT
1698             /****
1699             fErrorReporter.reportError(fErrorReporter.getLocator(),
1700                                        ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
1701                                        ImplementationMessages.VAL_CST,
1702                                        0,
1703                                        null,
1704                                        XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
1705             /****/
1706         }
1707 
1708         // We succeeded
1709         return -1;
1710 
1711     } // checkContent(int,int,QName[]):int
1712 
1713     /** Returns the content spec type for an element index. */
1714     private int getContentSpecType(int elementIndex) {
1715 
1716         int contentSpecType = -1;
1717         if (elementIndex > -1) {
1718             if (fDTDGrammar.getElementDecl(elementIndex,fTempElementDecl)) {
1719                 contentSpecType = fTempElementDecl.type;
1720             }
1721         }
1722         return contentSpecType;
1723     }
1724 
1725     /** Character data in content. */
1726     private void charDataInContent() {
1727 
1728         if (DEBUG_ELEMENT_CHILDREN) {
1729             System.out.println("charDataInContent()");
1730         }
1731         if (fElementChildren.length <= fElementChildrenLength) {
1732             QName[] newarray = new QName[fElementChildren.length * 2];
1733             System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
1734             fElementChildren = newarray;
1735         }
1736         QName qname = fElementChildren[fElementChildrenLength];
1737         if (qname == null) {
1738             for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
1739                 fElementChildren[i] = new QName();
1740             }
1741             qname = fElementChildren[fElementChildrenLength];
1742         }
1743         qname.clear();
1744         fElementChildrenLength++;
1745 
1746     } // charDataInCount()
1747 
1748     /** convert attribute type from ints to strings */
1749     private String getAttributeTypeName(XMLAttributeDecl attrDecl) {
1750 
1751         switch (attrDecl.simpleType.type) {
1752         case XMLSimpleType.TYPE_ENTITY: {
1753                 return attrDecl.simpleType.list ? XMLSymbols.fENTITIESSymbol : XMLSymbols.fENTITYSymbol;
1754             }
1755         case XMLSimpleType.TYPE_ENUMERATION: {
1756                 StringBuffer buffer = new StringBuffer();
1757                 buffer.append('(');
1758                 for (int i=0; i<attrDecl.simpleType.enumeration.length ; i++) {
1759                     if (i > 0) {
1760                         buffer.append('|');
1761                     }
1762                     buffer.append(attrDecl.simpleType.enumeration[i]);
1763                 }
1764                 buffer.append(')');
1765                 return fSymbolTable.addSymbol(buffer.toString());
1766             }
1767         case XMLSimpleType.TYPE_ID: {
1768                 return XMLSymbols.fIDSymbol;
1769             }
1770         case XMLSimpleType.TYPE_IDREF: {
1771                 return attrDecl.simpleType.list ? XMLSymbols.fIDREFSSymbol : XMLSymbols.fIDREFSymbol;
1772             }
1773         case XMLSimpleType.TYPE_NMTOKEN: {
1774                 return attrDecl.simpleType.list ? XMLSymbols.fNMTOKENSSymbol : XMLSymbols.fNMTOKENSymbol;
1775             }
1776         case XMLSimpleType.TYPE_NOTATION: {
1777                 return XMLSymbols.fNOTATIONSymbol;
1778             }
1779         }
1780         return XMLSymbols.fCDATASymbol;
1781 
1782     } // getAttributeTypeName(XMLAttributeDecl):String
1783 
1784     /** initialization */
1785     protected void init() {
1786 
1787         // datatype validators
1788         if (fValidation || fDynamicValidation) {
1789             try {
1790                 //REVISIT: datatypeRegistry + initialization of datatype
1791                 //         why do we cast to ListDatatypeValidator?
1792                 fValID       = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDSymbol);
1793                 fValIDRef    = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDREFSymbol);
1794                 fValIDRefs   = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDREFSSymbol);
1795                 fValENTITY   = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fENTITYSymbol);
1796                 fValENTITIES = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fENTITIESSymbol);
1797                 fValNMTOKEN  = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNMTOKENSymbol);
1798                 fValNMTOKENS = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNMTOKENSSymbol);
1799                 fValNOTATION = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNOTATIONSymbol);
1800 
1801             }
1802             catch (Exception e) {
1803                 // should never happen
1804                 e.printStackTrace(System.err);
1805             }
1806 
1807         }
1808 
1809     } // init()
1810 
1811     /** ensure element stack capacity */
1812     private void ensureStackCapacity (int newElementDepth) {
1813         if (newElementDepth == fElementQNamePartsStack.length) {
1814 
1815             QName[] newStackOfQueue = new QName[newElementDepth * 2];
1816             System.arraycopy(this.fElementQNamePartsStack, 0, newStackOfQueue, 0, newElementDepth );
1817             fElementQNamePartsStack = newStackOfQueue;
1818 
1819             QName qname = fElementQNamePartsStack[newElementDepth];
1820             if (qname == null) {
1821                 for (int i = newElementDepth; i < fElementQNamePartsStack.length; i++) {
1822                     fElementQNamePartsStack[i] = new QName();
1823                 }
1824             }
1825 
1826             int[] newStack = new int[newElementDepth * 2];
1827             System.arraycopy(fElementIndexStack, 0, newStack, 0, newElementDepth);
1828             fElementIndexStack = newStack;
1829 
1830             newStack = new int[newElementDepth * 2];
1831             System.arraycopy(fContentSpecTypeStack, 0, newStack, 0, newElementDepth);
1832             fContentSpecTypeStack = newStack;
1833 
1834         }
1835     } // ensureStackCapacity
1836 
1837 
1838     //
1839     // Protected methods
1840     //
1841 
1842     /** Handle element
1843      * @return true if validator is removed from the pipeline
1844      */
1845     protected boolean handleStartElement(QName element, XMLAttributes attributes, Augmentations augs)
1846                         throws XNIException {
1847 
1848 
1849         // VC: Root Element Type
1850         // see if the root element's name matches the one in DoctypeDecl
1851         if (!fSeenRootElement) {
1852             // REVISIT: Here are current assumptions about validation features
1853             //          given that XMLSchema validator is in the pipeline
1854             //
1855             // http://xml.org/sax/features/validation = true
1856             // http://apache.org/xml/features/validation/schema = true
1857             //
1858             // [1] XML instance document only has reference to a DTD
1859             //  Outcome: report validation errors only against dtd.
1860             //
1861             // [2] XML instance document has only XML Schema grammars:
1862             //  Outcome: report validation errors only against schemas (no errors produced from DTD validator)
1863             //
1864             // [3] XML instance document has DTD and XML schemas:
1865             // [a] if schema language is not set outcome - validation errors reported against both grammars: DTD and schemas.
1866             // [b] if schema language is set to XML Schema - do not report validation errors
1867             //
1868             // if dynamic validation is on
1869             //            validate only against grammar we've found (depending on settings
1870             //            for schema feature)
1871             //
1872             //
1873             fPerformValidation = validate();
1874             fSeenRootElement = true;
1875             fValidationManager.setEntityState(fDTDGrammar);
1876             fValidationManager.setGrammarFound(fSeenDoctypeDecl);
1877             rootElementSpecified(element);
1878         }
1879         if (fDTDGrammar == null) {
1880 
1881             if (!fPerformValidation) {
1882                 fCurrentElementIndex = -1;
1883                 fCurrentContentSpecType = -1;
1884                 fInElementContent = false;
1885             }
1886             if (fPerformValidation) {
1887                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1888                                            "MSG_GRAMMAR_NOT_FOUND",
1889                                            new Object[]{ element.rawname},
1890                                            XMLErrorReporter.SEVERITY_ERROR);
1891             }
1892             // modify pipeline
1893             if (fDocumentSource !=null ) {
1894                 fDocumentSource.setDocumentHandler(fDocumentHandler);
1895                 if (fDocumentHandler != null)
1896                     fDocumentHandler.setDocumentSource(fDocumentSource);
1897                 return true;
1898             }
1899         }
1900         else {
1901             //  resolve the element
1902             fCurrentElementIndex = fDTDGrammar.getElementDeclIndex(element);
1903             //changed here.. new function for getContentSpecType
1904             fCurrentContentSpecType = fDTDGrammar.getContentSpecType(fCurrentElementIndex);
1905             if (fCurrentContentSpecType == -1 && fPerformValidation) {
1906                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1907                                            "MSG_ELEMENT_NOT_DECLARED",
1908                                            new Object[]{ element.rawname},
1909                                            XMLErrorReporter.SEVERITY_ERROR);
1910             }
1911 
1912             //  0. insert default attributes
1913             //  1. normalize the attributes
1914             //  2. validate the attrivute list.
1915             // TO DO:
1916             //changed here.. also pass element name,
1917             addDTDDefaultAttrsAndValidate(element, fCurrentElementIndex, attributes);
1918         }
1919 
1920         // set element content state
1921         fInElementContent = fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN;
1922 
1923         // increment the element depth, add this element's
1924         // QName to its enclosing element 's children list
1925         fElementDepth++;
1926         if (fPerformValidation) {
1927             // push current length onto stack
1928             if (fElementChildrenOffsetStack.length <= fElementDepth) {
1929                 int newarray[] = new int[fElementChildrenOffsetStack.length * 2];
1930                 System.arraycopy(fElementChildrenOffsetStack, 0, newarray, 0, fElementChildrenOffsetStack.length);
1931                 fElementChildrenOffsetStack = newarray;
1932             }
1933             fElementChildrenOffsetStack[fElementDepth] = fElementChildrenLength;
1934 
1935             // add this element to children
1936             if (fElementChildren.length <= fElementChildrenLength) {
1937                 QName[] newarray = new QName[fElementChildrenLength * 2];
1938                 System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
1939                 fElementChildren = newarray;
1940             }
1941             QName qname = fElementChildren[fElementChildrenLength];
1942             if (qname == null) {
1943                 for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
1944                     fElementChildren[i] = new QName();
1945                 }
1946                 qname = fElementChildren[fElementChildrenLength];
1947             }
1948             qname.setValues(element);
1949             fElementChildrenLength++;
1950         }
1951 
1952         // save current element information
1953         fCurrentElement.setValues(element);
1954         ensureStackCapacity(fElementDepth);
1955         fElementQNamePartsStack[fElementDepth].setValues(fCurrentElement);
1956         fElementIndexStack[fElementDepth] = fCurrentElementIndex;
1957         fContentSpecTypeStack[fElementDepth] = fCurrentContentSpecType;
1958         startNamespaceScope(element, attributes, augs);
1959         return false;
1960 
1961     } // handleStartElement(QName,XMLAttributes)
1962 
1963     protected void startNamespaceScope(QName element, XMLAttributes attributes, Augmentations augs){
1964     }
1965 
1966     /** Handle end element. */
1967     protected void handleEndElement(QName element,  Augmentations augs, boolean isEmpty)
1968     throws XNIException {
1969 
1970         // decrease element depth
1971         fElementDepth--;
1972 
1973         // validate
1974         if (fPerformValidation) {
1975             int elementIndex = fCurrentElementIndex;
1976             if (elementIndex != -1 && fCurrentContentSpecType != -1) {
1977                 QName children[] = fElementChildren;
1978                 int childrenOffset = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
1979                 int childrenLength = fElementChildrenLength - childrenOffset;
1980                 int result = checkContent(elementIndex,
1981                                           children, childrenOffset, childrenLength);
1982 
1983                 if (result != -1) {
1984                     fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
1985                     if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
1986                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1987                                                    "MSG_CONTENT_INVALID",
1988                                                    new Object[]{ element.rawname, "EMPTY"},
1989                                                    XMLErrorReporter.SEVERITY_ERROR);
1990                     }
1991                     else {
1992                         String messageKey = result != childrenLength ?
1993                                             "MSG_CONTENT_INVALID" : "MSG_CONTENT_INCOMPLETE";
1994                         fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1995                                                    messageKey,
1996                                                    new Object[]{ element.rawname,
1997                                                        fDTDGrammar.getContentSpecAsString(elementIndex)},
1998                                                    XMLErrorReporter.SEVERITY_ERROR);
1999                     }
2000                 }
2001             }
2002             fElementChildrenLength = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
2003         }
2004 
2005         endNamespaceScope(fCurrentElement, augs, isEmpty);
2006 
2007         // now pop this element off the top of the element stack
2008         if (fElementDepth < -1) {
2009             throw new RuntimeException("FWK008 Element stack underflow");
2010         }
2011         if (fElementDepth < 0) {
2012             fCurrentElement.clear();
2013             fCurrentElementIndex = -1;
2014             fCurrentContentSpecType = -1;
2015             fInElementContent = false;
2016 
2017             // TO DO : fix this
2018             //
2019             // Check after document is fully parsed
2020             // (1) check that there was an element with a matching id for every
2021             //   IDREF and IDREFS attr (V_IDREF0)
2022             //
2023             if (fPerformValidation) {
2024                 String value = fValidationState.checkIDRefID();
2025                 if (value != null) {
2026                     fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
2027                                                 "MSG_ELEMENT_WITH_ID_REQUIRED",
2028                                                 new Object[]{value},
2029                                                 XMLErrorReporter.SEVERITY_ERROR );
2030                 }
2031             }
2032             return;
2033         }
2034 
2035         // If Namespace enable then localName != rawName
2036         fCurrentElement.setValues(fElementQNamePartsStack[fElementDepth]);
2037 
2038         fCurrentElementIndex = fElementIndexStack[fElementDepth];
2039         fCurrentContentSpecType = fContentSpecTypeStack[fElementDepth];
2040         fInElementContent = (fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN);
2041 
2042     } // handleEndElement(QName,boolean)
2043 
2044     protected void endNamespaceScope(QName element,  Augmentations augs, boolean isEmpty){
2045 
2046         // call handlers
2047         if (fDocumentHandler != null && !isEmpty) {
2048             // NOTE: The binding of the element doesn't actually happen
2049             //       yet because the namespace binder does that. However,
2050             //       if it does it before this point, then the endPrefix-
2051             //       Mapping calls get made too soon! As long as the
2052             //       rawnames match, we know it'll have a good binding,
2053             //       so we can just use the current element. -Ac
2054             fDocumentHandler.endElement(fCurrentElement, augs);
2055         }
2056     }
2057 
2058     // returns whether a character is space according to the
2059     // version of XML this validator supports.
2060     protected boolean isSpace(int c) {
2061         return XMLChar.isSpace(c);
2062     } // isSpace(int):  boolean
2063 
2064     public boolean characterData(String data, Augmentations augs) {
2065         characters(new XMLString(data.toCharArray(), 0, data.length()), augs);
2066         return true;
2067     }
2068 
2069 } // class XMLDTDValidator