1 /*
   2  * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 
   5 /*
   6  * Copyright 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;
  22 import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar;
  23 import java.io.EOFException;
  24 import java.io.IOException;
  25 
  26 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  27 
  28 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  29 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  30 import com.sun.org.apache.xerces.internal.util.XMLChar;
  31 import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
  32 
  33 import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler;
  34 import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
  35 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  36 import com.sun.org.apache.xerces.internal.xni.XMLString;
  37 import com.sun.org.apache.xerces.internal.xni.XNIException;
  38 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  39 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  40 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  41 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDScanner;
  42 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  43 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  44 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  45 import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler;
  46 import com.sun.org.apache.xerces.internal.impl.Constants;
  47 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
  48 import com.sun.xml.internal.stream.Entity;
  49 
  50 /**
  51  * This class is responsible for scanning the declarations found
  52  * in the internal and external subsets of a DTD in an XML document.
  53  * The scanner acts as the sources for the DTD information which is
  54  * communicated to the DTD handlers.
  55  * <p>
  56  * This component requires the following features and properties from the
  57  * component manager that uses it:
  58  * <ul>
  59  *  <li>http://xml.org/sax/features/validation</li>
  60  *  <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
  61  *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
  62  *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
  63  *  <li>http://apache.org/xml/properties/internal/entity-manager</li>
  64  * </ul>
  65  *
  66  * @author Arnaud  Le Hors, IBM
  67  * @author Andy Clark, IBM
  68  * @author Glenn Marcy, IBM
  69  * @author Eric Ye, IBM
  70  *
  71  * @version $Id: XMLDTDScannerImpl.java,v 1.8 2010-11-01 04:39:41 joehw Exp $
  72  */
  73 public class XMLDTDScannerImpl
  74 extends XMLScanner
  75 implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
  76 
  77     //
  78     // Constants
  79     //
  80 
  81     // scanner states
  82 
  83     /** Scanner state: end of input. */
  84     protected static final int SCANNER_STATE_END_OF_INPUT = 0;
  85 
  86     /** Scanner state: text declaration. */
  87     protected static final int SCANNER_STATE_TEXT_DECL = 1;
  88 
  89     /** Scanner state: markup declaration. */
  90     protected static final int SCANNER_STATE_MARKUP_DECL = 2;
  91 
  92     // recognized features and properties
  93 
  94     /** Recognized features. */
  95     private static final String[] RECOGNIZED_FEATURES = {
  96         VALIDATION,
  97         NOTIFY_CHAR_REFS,
  98     };
  99 
 100     /** Feature defaults. */
 101     private static final Boolean[] FEATURE_DEFAULTS = {
 102         null,
 103         Boolean.FALSE,
 104     };
 105 
 106     /** Recognized properties. */
 107     private static final String[] RECOGNIZED_PROPERTIES = {
 108         SYMBOL_TABLE,
 109         ERROR_REPORTER,
 110         ENTITY_MANAGER,
 111     };
 112 
 113     /** Property defaults. */
 114     private static final Object[] PROPERTY_DEFAULTS = {
 115         null,
 116         null,
 117         null,
 118     };
 119 
 120     // debugging
 121 
 122     /** Debug scanner state. */
 123     private static final boolean DEBUG_SCANNER_STATE = false;
 124 
 125     //
 126     // Data
 127     //
 128 
 129     // handlers
 130 
 131     /** DTD handler. */
 132     public XMLDTDHandler fDTDHandler = null;
 133 
 134     /** DTD content model handler. */
 135     protected XMLDTDContentModelHandler fDTDContentModelHandler;
 136 
 137     // state
 138 
 139     /** Scanner state. */
 140     protected int fScannerState;
 141 
 142     /** Standalone. */
 143     protected boolean fStandalone;
 144 
 145     /** Seen external DTD. */
 146     protected boolean fSeenExternalDTD;
 147 
 148     /** Seen external parameter entity. */
 149     protected boolean fSeenExternalPE;
 150 
 151     // private data
 152 
 153     /** Start DTD called. */
 154     private boolean fStartDTDCalled;
 155 
 156     /** Default attribute */
 157     private XMLAttributesImpl fAttributes = new XMLAttributesImpl();
 158 
 159     /**
 160      * Stack of content operators (either '|' or ',') in children
 161      * content.
 162      */
 163     private int[] fContentStack = new int[5];
 164 
 165     /** Size of content stack. */
 166     private int fContentDepth;
 167 
 168     /** Parameter entity stack to check well-formedness. */
 169     private int[] fPEStack = new int[5];
 170 
 171 
 172     /** Parameter entity stack to report start/end entity calls. */
 173     private boolean[] fPEReport = new boolean[5];
 174 
 175     /** Number of opened parameter entities. */
 176     private int fPEDepth;
 177 
 178     /** Markup depth. */
 179     private int fMarkUpDepth;
 180 
 181     /** Number of opened external entities. */
 182     private int fExtEntityDepth;
 183 
 184     /** Number of opened include sections. */
 185     private int fIncludeSectDepth;
 186 
 187     // temporary variables
 188 
 189     /** Array of 3 strings. */
 190     private String[] fStrings = new String[3];
 191 
 192     /** String. */
 193     private XMLString fString = new XMLString();
 194 
 195     /** String buffer. */
 196     private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
 197 
 198     /** String buffer. */
 199     private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
 200 
 201     /** Literal text. */
 202     private XMLString fLiteral = new XMLString();
 203 
 204     /** Literal text. */
 205     private XMLString fLiteral2 = new XMLString();
 206 
 207     /** Enumeration values. */
 208     private String[] fEnumeration = new String[5];
 209 
 210     /** Enumeration values count. */
 211     private int fEnumerationCount;
 212 
 213     /** Ignore conditional section buffer. */
 214     private XMLStringBuffer fIgnoreConditionalBuffer = new XMLStringBuffer(128);
 215 
 216     /** Object contains grammar information for a non-validaing parser. */
 217     DTDGrammar nvGrammarInfo = null;
 218 
 219     boolean nonValidatingMode = false;
 220     //
 221     // Constructors
 222     //
 223 
 224     /** Default constructor. */
 225     public XMLDTDScannerImpl() {
 226     } // <init>()
 227 
 228     /** Constructor for he use of non-XMLComponentManagers. */
 229     public XMLDTDScannerImpl(SymbolTable symbolTable,
 230             XMLErrorReporter errorReporter, XMLEntityManager entityManager) {
 231         fSymbolTable = symbolTable;
 232         fErrorReporter = errorReporter;
 233         fEntityManager = entityManager;
 234         entityManager.setProperty(SYMBOL_TABLE, fSymbolTable);
 235     }
 236 
 237     //
 238     // XMLDTDScanner methods
 239     //
 240 
 241     /**
 242      * Sets the input source.
 243      *
 244      * @param inputSource The input source or null.
 245      *
 246      * @throws IOException Thrown on i/o error.
 247      */
 248     public void setInputSource(XMLInputSource inputSource) throws IOException {
 249         if (inputSource == null) {
 250             // no system id was available
 251             if (fDTDHandler != null) {
 252                 fDTDHandler.startDTD(null, null);
 253                 fDTDHandler.endDTD(null);
 254             }
 255             if (nonValidatingMode){
 256                 nvGrammarInfo.startDTD(null,null);
 257                 nvGrammarInfo.endDTD(null);
 258             }
 259             return;
 260         }
 261         fEntityManager.setEntityHandler(this);
 262         fEntityManager.startDTDEntity(inputSource);
 263     } // setInputSource(XMLInputSource)
 264 
 265     /**
 266      * Scans the external subset of the document.
 267      *
 268      * @param complete True if the scanner should scan the document
 269      *                 completely, pushing all events to the registered
 270      *                 document handler. A value of false indicates that
 271      *                 that the scanner should only scan the next portion
 272      *                 of the document and return. A scanner instance is
 273      *                 permitted to completely scan a document if it does
 274      *                 not support this "pull" scanning model.
 275      *
 276      * @return True if there is more to scan, false otherwise.
 277      */
 278     public boolean scanDTDExternalSubset(boolean complete)
 279     throws IOException, XNIException {
 280 
 281         fEntityManager.setEntityHandler(this);
 282         if (fScannerState == SCANNER_STATE_TEXT_DECL) {
 283             fSeenExternalDTD = true;
 284             boolean textDecl = scanTextDecl();
 285             if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
 286                 return false;
 287             }
 288             else {
 289                 // next state is markup decls regardless of whether there
 290                 // is a TextDecl or not
 291                 setScannerState(SCANNER_STATE_MARKUP_DECL);
 292                 if (textDecl && !complete) {
 293                     return true;
 294                 }
 295             }
 296         }
 297         // keep dispatching "events"
 298         do {
 299             if (!scanDecls(complete)) {
 300                 return false;
 301             }
 302         } while (complete);
 303 
 304         // return that there is more to scan
 305         return true;
 306 
 307     } // scanDTDExternalSubset(boolean):boolean
 308 
 309     /**
 310      * Scans the internal subset of the document.
 311      *
 312      * @param complete True if the scanner should scan the document
 313      *                 completely, pushing all events to the registered
 314      *                 document handler. A value of false indicates that
 315      *                 that the scanner should only scan the next portion
 316      *                 of the document and return. A scanner instance is
 317      *                 permitted to completely scan a document if it does
 318      *                 not support this "pull" scanning model.
 319      * @param standalone True if the document was specified as standalone.
 320      *                   This value is important for verifying certain
 321      *                   well-formedness constraints.
 322      * @param hasExternalDTD True if the document has an external DTD.
 323      *                       This allows the scanner to properly notify
 324      *                       the handler of the end of the DTD in the
 325      *                       absence of an external subset.
 326      *
 327      * @return True if there is more to scan, false otherwise.
 328      */
 329     public boolean scanDTDInternalSubset(boolean complete, boolean standalone,
 330     boolean hasExternalSubset)
 331     throws IOException, XNIException {
 332         // reset entity scanner
 333         //xxx:stax getText() is supposed to return only DTD internal subset
 334         //shouldn't we record position here before we go ahead ??
 335 
 336         fEntityScanner = (XMLEntityScanner)fEntityManager.getEntityScanner();
 337         fEntityManager.setEntityHandler(this);
 338         fStandalone = standalone;
 339         //System.out.println("state"+fScannerState);
 340         if (fScannerState == SCANNER_STATE_TEXT_DECL) {
 341             // call handler
 342             if (fDTDHandler != null) {
 343                 fDTDHandler.startDTD(fEntityScanner, null);
 344                 fStartDTDCalled = true;
 345             }
 346 
 347             if (nonValidatingMode){
 348                 fStartDTDCalled = true;
 349                 nvGrammarInfo.startDTD(fEntityScanner,null);
 350             }
 351             // set starting state for internal subset
 352             setScannerState(SCANNER_STATE_MARKUP_DECL);
 353         }
 354         // keep dispatching "events"
 355         do {
 356             if (!scanDecls(complete)) {
 357                 // call handler
 358                 if (fDTDHandler != null && hasExternalSubset == false) {
 359                     fDTDHandler.endDTD(null);
 360                 }
 361                 if (nonValidatingMode && hasExternalSubset == false ){
 362                     nvGrammarInfo.endDTD(null);
 363                 }
 364                 // we're done, set starting state for external subset
 365                 setScannerState(SCANNER_STATE_TEXT_DECL);
 366                 return false;
 367             }
 368         } while (complete);
 369 
 370         // return that there is more to scan
 371         return true;
 372 
 373     } // scanDTDInternalSubset(boolean,boolean,boolean):boolean
 374 
 375     //
 376     // XMLComponent methods
 377     //
 378 
 379     /**
 380      * reset
 381      *
 382      * @param componentManager
 383      */
 384     public void reset(XMLComponentManager componentManager)
 385     throws XMLConfigurationException {
 386 
 387         super.reset(componentManager);
 388         init();
 389 
 390     } // reset(XMLComponentManager)
 391 
 392     // this is made for something like XMLDTDLoader--XMLComponentManager-free operation...
 393     public void reset() {
 394         super.reset();
 395         init();
 396 
 397     }
 398 
 399     public void reset(PropertyManager props) {
 400         setPropertyManager(props);
 401         super.reset(props);
 402         init() ;
 403         nonValidatingMode = true;
 404         //Revisit : Create new grammar until we implement GrammarPool.
 405         nvGrammarInfo = new DTDGrammar(fSymbolTable);
 406     }
 407     /**
 408      * Returns a list of feature identifiers that are recognized by
 409      * this component. This method may return null if no features
 410      * are recognized by this component.
 411      */
 412     public String[] getRecognizedFeatures() {
 413         return (String[])(RECOGNIZED_FEATURES.clone());
 414     } // getRecognizedFeatures():String[]
 415 
 416     /**
 417      * Returns a list of property identifiers that are recognized by
 418      * this component. This method may return null if no properties
 419      * are recognized by this component.
 420      */
 421     public String[] getRecognizedProperties() {
 422         return (String[])(RECOGNIZED_PROPERTIES.clone());
 423     } // getRecognizedProperties():String[]
 424 
 425     /**
 426      * Returns the default state for a feature, or null if this
 427      * component does not want to report a default value for this
 428      * feature.
 429      *
 430      * @param featureId The feature identifier.
 431      *
 432      * @since Xerces 2.2.0
 433      */
 434     public Boolean getFeatureDefault(String featureId) {
 435         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
 436             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
 437                 return FEATURE_DEFAULTS[i];
 438             }
 439         }
 440         return null;
 441     } // getFeatureDefault(String):Boolean
 442 
 443     /**
 444      * Returns the default state for a property, or null if this
 445      * component does not want to report a default value for this
 446      * property.
 447      *
 448      * @param propertyId The property identifier.
 449      *
 450      * @since Xerces 2.2.0
 451      */
 452     public Object getPropertyDefault(String propertyId) {
 453         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
 454             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
 455                 return PROPERTY_DEFAULTS[i];
 456             }
 457         }
 458         return null;
 459     } // getPropertyDefault(String):Object
 460 
 461     //
 462     // XMLDTDSource methods
 463     //
 464 
 465     /**
 466      * setDTDHandler
 467      *
 468      * @param dtdHandler
 469      */
 470     public void setDTDHandler(XMLDTDHandler dtdHandler) {
 471         fDTDHandler = dtdHandler;
 472     } // setDTDHandler(XMLDTDHandler)
 473 
 474     /**
 475      * getDTDHandler
 476      *
 477      * @return the XMLDTDHandler
 478      */
 479     public XMLDTDHandler getDTDHandler() {
 480         return fDTDHandler;
 481     } // getDTDHandler():  XMLDTDHandler
 482 
 483     //
 484     // XMLDTDContentModelSource methods
 485     //
 486 
 487     /**
 488      * setDTDContentModelHandler
 489      *
 490      * @param dtdContentModelHandler
 491      */
 492     public void setDTDContentModelHandler(XMLDTDContentModelHandler
 493     dtdContentModelHandler) {
 494         fDTDContentModelHandler = dtdContentModelHandler;
 495     } // setDTDContentModelHandler
 496 
 497     /**
 498      * getDTDContentModelHandler
 499      *
 500      * @return XMLDTDContentModelHandler
 501      */
 502     public XMLDTDContentModelHandler getDTDContentModelHandler() {
 503         return fDTDContentModelHandler ;
 504     } // setDTDContentModelHandler
 505 
 506     //
 507     // XMLEntityHandler methods
 508     //
 509 
 510     /**
 511      * This method notifies of the start of an entity. The DTD has the
 512      * pseudo-name of "[dtd]" parameter entity names start with '%'; and
 513      * general entities are just specified by their name.
 514      *
 515      * @param name     The name of the entity.
 516      * @param identifier The resource identifier.
 517      * @param encoding The auto-detected IANA encoding name of the entity
 518      *                 stream. This value will be null in those situations
 519      *                 where the entity encoding is not auto-detected (e.g.
 520      *                 internal entities or a document entity that is
 521      *                 parsed from a java.io.Reader).
 522      * @param augs     Additional information that may include infoset augmentations
 523      *
 524      * @throws XNIException Thrown by handler to signal an error.
 525      */
 526     public void startEntity(String name,
 527                             XMLResourceIdentifier identifier,
 528                             String encoding, Augmentations augs) throws XNIException {
 529 
 530         super.startEntity(name, identifier, encoding, augs);
 531 
 532         boolean dtdEntity = name.equals("[dtd]");
 533         if (dtdEntity) {
 534             // call handler
 535             if (fDTDHandler != null && !fStartDTDCalled ) {
 536                 fDTDHandler.startDTD(fEntityScanner, null);
 537             }
 538             if (fDTDHandler != null) {
 539                 fDTDHandler.startExternalSubset(identifier,null);
 540             }
 541             fEntityManager.startExternalSubset();
 542             fEntityStore.startExternalSubset();
 543             fExtEntityDepth++;
 544         }
 545         else if (name.charAt(0) == '%') {
 546             pushPEStack(fMarkUpDepth, fReportEntity);
 547             if (fEntityScanner.isExternal()) {
 548                 fExtEntityDepth++;
 549             }
 550         }
 551 
 552         // call handler
 553         if (fDTDHandler != null && !dtdEntity && fReportEntity) {
 554             fDTDHandler.startParameterEntity(name, identifier, encoding, null);
 555         }
 556 
 557     } // startEntity(String,XMLResourceIdentifier,String)
 558 
 559     /**
 560      * This method notifies the end of an entity. The DTD has the pseudo-name
 561      * of "[dtd]" parameter entity names start with '%'; and general entities
 562      * are just specified by their name.
 563      *
 564      * @param name The name of the entity.
 565      *
 566      * @throws XNIException Thrown by handler to signal an error.
 567      */
 568     public void endEntity(String name, Augmentations augs)
 569     throws XNIException, IOException {
 570 
 571         super.endEntity(name, augs);
 572 
 573         // if there is no data after the doctype
 574         //
 575         if (fScannerState == SCANNER_STATE_END_OF_INPUT)
 576             return;
 577 
 578         // Handle end of PE
 579         boolean reportEntity = fReportEntity;
 580         if (name.startsWith("%")) {
 581             reportEntity = peekReportEntity();
 582             // check well-formedness of the enity
 583             int startMarkUpDepth = popPEStack();
 584             // throw fatalError if this entity was incomplete and
 585             // was a freestanding decl
 586             if(startMarkUpDepth == 0 &&
 587             startMarkUpDepth < fMarkUpDepth) {
 588                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 589                 "ILL_FORMED_PARAMETER_ENTITY_WHEN_USED_IN_DECL",
 590                 new Object[]{ fEntityManager.fCurrentEntity.name},
 591                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
 592             }
 593             if (startMarkUpDepth != fMarkUpDepth) {
 594                 reportEntity = false;
 595                 if (fValidation) {
 596                     // Proper nesting of parameter entities is a Validity Constraint
 597                     // and must not be enforced when validation is off
 598                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
 599                     "ImproperDeclarationNesting",
 600                     new Object[]{ name },
 601                     XMLErrorReporter.SEVERITY_ERROR);
 602                 }
 603             }
 604             if (fEntityScanner.isExternal()) {
 605                 fExtEntityDepth--;
 606             }
 607         }
 608 
 609         // call handler
 610         boolean dtdEntity = name.equals("[dtd]");
 611         if (fDTDHandler != null && !dtdEntity && reportEntity) {
 612             fDTDHandler.endParameterEntity(name, null);
 613         }
 614 
 615         // end DTD
 616         if (dtdEntity) {
 617             if (fIncludeSectDepth != 0) {
 618                 reportFatalError("IncludeSectUnterminated", null);
 619             }
 620             fScannerState = SCANNER_STATE_END_OF_INPUT;
 621             // call handler
 622             fEntityManager.endExternalSubset();
 623             fEntityStore.endExternalSubset();
 624 
 625             if (fDTDHandler != null) {
 626                 fDTDHandler.endExternalSubset(null);
 627                 fDTDHandler.endDTD(null);
 628             }
 629             fExtEntityDepth--;
 630         }
 631 
 632         //XML (Document Entity) is the last opened entity, however
 633         //if for some reason DTD Scanner receives this callback
 634         //there is something wrong (probably invalid XML), throw exception.
 635         //or
 636         //For standalone DTD loader, it might be the last opened entity
 637         //and if this is the last opened entity and fMarkUpDepth != 0 or
 638         //fIncludeSectDepth != 0 or fExtEntityDepth != 0 throw Exception
 639         if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.LAST_ENTITY))
 640             && ( fMarkUpDepth != 0 || fExtEntityDepth !=0 || fIncludeSectDepth != 0)){
 641             throw new EOFException();
 642         }
 643 
 644     } // endEntity(String)
 645 
 646     // helper methods
 647 
 648     /**
 649      * Sets the scanner state.
 650      *
 651      * @param state The new scanner state.
 652      */
 653     protected final void setScannerState(int state) {
 654 
 655         fScannerState = state;
 656         if (DEBUG_SCANNER_STATE) {
 657             System.out.print("### setScannerState: ");
 658             System.out.print(getScannerStateName(state));
 659             //System.out.println();
 660         }
 661 
 662     } // setScannerState(int)
 663 
 664     //
 665     // Private methods
 666     //
 667 
 668     /** Returns the scanner state name. */
 669     private static String getScannerStateName(int state) {
 670 
 671         if (DEBUG_SCANNER_STATE) {
 672             switch (state) {
 673                 case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT";
 674                 case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL";
 675                 case SCANNER_STATE_MARKUP_DECL: return "SCANNER_STATE_MARKUP_DECL";
 676             }
 677         }
 678 
 679         return "??? ("+state+')';
 680 
 681     } // getScannerStateName(int):String
 682 
 683     protected final boolean scanningInternalSubset() {
 684         return fExtEntityDepth == 0;
 685     }
 686 
 687     /**
 688      * start a parameter entity dealing with the textdecl if there is any
 689      *
 690      * @param name The name of the parameter entity to start (without the '%')
 691      * @param literal Whether this is happening within a literal
 692      */
 693     protected void startPE(String name, boolean literal)
 694     throws IOException, XNIException {
 695         int depth = fPEDepth;
 696         String pName = "%"+name;
 697         if (fValidation && !fEntityStore.isDeclaredEntity(pName)) {
 698             fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared",
 699             new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
 700         }
 701         fEntityManager.startEntity(fSymbolTable.addSymbol(pName),
 702         literal);
 703         // if we actually got a new entity and it's external
 704         // parse text decl if there is any
 705         if (depth != fPEDepth && fEntityScanner.isExternal()) {
 706             scanTextDecl();
 707         }
 708     }
 709 
 710     /**
 711      * Dispatch an XML "event".
 712      *
 713      * @param complete True if this method is intended to scan
 714      *                 and dispatch as much as possible.
 715      *
 716      * @return True if a TextDecl was scanned.
 717      *
 718      * @throws IOException  Thrown on i/o error.
 719      * @throws XNIException Thrown on parse error.
 720      *
 721      */
 722     protected final boolean scanTextDecl()
 723     throws IOException, XNIException {
 724 
 725         // scan XMLDecl
 726         boolean textDecl = false;
 727         if (fEntityScanner.skipString("<?xml")) {
 728             fMarkUpDepth++;
 729             // NOTE: special case where document starts with a PI
 730             //       whose name starts with "xml" (e.g. "xmlfoo")
 731             if (isValidNameChar(fEntityScanner.peekChar())) {
 732                 fStringBuffer.clear();
 733                 fStringBuffer.append("xml");
 734                 while (isValidNameChar(fEntityScanner.peekChar())) {
 735                     fStringBuffer.append((char)fEntityScanner.scanChar());
 736                 }
 737                 String target =
 738                 fSymbolTable.addSymbol(fStringBuffer.ch,
 739                 fStringBuffer.offset,
 740                 fStringBuffer.length);
 741                 scanPIData(target, fString);
 742             }
 743 
 744             // standard Text declaration
 745             else {
 746                 // pseudo-attribute values
 747                 String version = null;
 748                 String encoding = null;
 749 
 750                 scanXMLDeclOrTextDecl(true, fStrings);
 751                 textDecl = true;
 752                 fMarkUpDepth--;
 753 
 754                 version = fStrings[0];
 755                 encoding = fStrings[1];
 756 
 757                 fEntityScanner.setEncoding(encoding);
 758 
 759                 // call handler
 760                 if (fDTDHandler != null) {
 761                     fDTDHandler.textDecl(version, encoding, null);
 762                 }
 763             }
 764         }
 765         fEntityManager.fCurrentEntity.mayReadChunks = true;
 766 
 767         return textDecl;
 768 
 769     } // scanTextDecl(boolean):boolean
 770 
 771     /**
 772      * Scans a processing data. This is needed to handle the situation
 773      * where a document starts with a processing instruction whose
 774      * target name <em>starts with</em> "xml". (e.g. xmlfoo)
 775      *
 776      * @param target The PI target
 777      * @param data The string to fill in with the data
 778      */
 779     protected final void scanPIData(String target, XMLString data)
 780     throws IOException, XNIException {
 781         //Venu REVISIT
 782         //      super.scanPIData(target, data);
 783         fMarkUpDepth--;
 784 
 785         // call handler
 786         if (fDTDHandler != null) {
 787             fDTDHandler.processingInstruction(target, data, null);
 788         }
 789 
 790     } // scanPIData(String)
 791 
 792     /**
 793      * Scans a comment.
 794      * <p>
 795      * <pre>
 796      * [15] Comment ::= '&lt!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
 797      * </pre>
 798      * <p>
 799      * <strong>Note:</strong> Called after scanning past '&lt;!--'
 800      */
 801     protected final void scanComment() throws IOException, XNIException {
 802 
 803         fReportEntity = false;
 804         scanComment(fStringBuffer);
 805         fMarkUpDepth--;
 806 
 807         // call handler
 808         if (fDTDHandler != null) {
 809             fDTDHandler.comment(fStringBuffer, null);
 810         }
 811         fReportEntity = true;
 812 
 813     } // scanComment()
 814 
 815     /**
 816      * Scans an element declaration
 817      * <p>
 818      * <pre>
 819      * [45]    elementdecl    ::=    '&lt;!ELEMENT' S Name S contentspec S? '>'
 820      * [46]    contentspec    ::=    'EMPTY' | 'ANY' | Mixed | children
 821      * </pre>
 822      * <p>
 823      * <strong>Note:</strong> Called after scanning past '&lt;!ELEMENT'
 824      */
 825     protected final void scanElementDecl() throws IOException, XNIException {
 826 
 827         // spaces
 828         fReportEntity = false;
 829         if (!skipSeparator(true, !scanningInternalSubset())) {
 830             reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL",
 831             null);
 832         }
 833 
 834         // element name
 835         String name = fEntityScanner.scanName();
 836         if (name == null) {
 837             reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL",
 838             null);
 839         }
 840 
 841         // spaces
 842         if (!skipSeparator(true, !scanningInternalSubset())) {
 843             reportFatalError("MSG_SPACE_REQUIRED_BEFORE_CONTENTSPEC_IN_ELEMENTDECL",
 844             new Object[]{name});
 845         }
 846 
 847         // content model
 848         if (fDTDContentModelHandler != null) {
 849             fDTDContentModelHandler.startContentModel(name, null);
 850         }
 851         String contentModel = null;
 852         fReportEntity = true;
 853         if (fEntityScanner.skipString("EMPTY")) {
 854             contentModel = "EMPTY";
 855             // call handler
 856             if (fDTDContentModelHandler != null) {
 857                 fDTDContentModelHandler.empty(null);
 858             }
 859         }
 860         else if (fEntityScanner.skipString("ANY")) {
 861             contentModel = "ANY";
 862             // call handler
 863             if (fDTDContentModelHandler != null) {
 864                 fDTDContentModelHandler.any(null);
 865             }
 866         }
 867         else {
 868             if (!fEntityScanner.skipChar('(')) {
 869                 reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
 870                 new Object[]{name});
 871             }
 872             if (fDTDContentModelHandler != null) {
 873                 fDTDContentModelHandler.startGroup(null);
 874             }
 875             fStringBuffer.clear();
 876             fStringBuffer.append('(');
 877             fMarkUpDepth++;
 878             skipSeparator(false, !scanningInternalSubset());
 879 
 880             // Mixed content model
 881             if (fEntityScanner.skipString("#PCDATA")) {
 882                 scanMixed(name);
 883             }
 884             else {              // children content
 885                 scanChildren(name);
 886             }
 887             contentModel = fStringBuffer.toString();
 888         }
 889 
 890         // call handler
 891         if (fDTDContentModelHandler != null) {
 892             fDTDContentModelHandler.endContentModel(null);
 893         }
 894 
 895         fReportEntity = false;
 896         skipSeparator(false, !scanningInternalSubset());
 897         // end
 898         if (!fEntityScanner.skipChar('>')) {
 899             reportFatalError("ElementDeclUnterminated", new Object[]{name});
 900         }
 901         fReportEntity = true;
 902         fMarkUpDepth--;
 903 
 904         // call handler
 905         if (fDTDHandler != null) {
 906             fDTDHandler.elementDecl(name, contentModel, null);
 907         }
 908         if (nonValidatingMode) nvGrammarInfo.elementDecl(name, contentModel, null);
 909     } // scanElementDecl()
 910 
 911     /**
 912      * scan Mixed content model
 913      * This assumes the content model has been parsed up to #PCDATA and
 914      * can simply append to fStringBuffer.
 915      * <pre>
 916      * [51]    Mixed    ::=    '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
 917      *                       | '(' S? '#PCDATA' S? ')'
 918      * </pre>
 919      *
 920      * @param elName The element type name this declaration is about.
 921      *
 922      * <strong>Note:</strong> Called after scanning past '(#PCDATA'.
 923      */
 924     private final void scanMixed(String elName)
 925     throws IOException, XNIException {
 926 
 927         String childName = null;
 928 
 929         fStringBuffer.append("#PCDATA");
 930         // call handler
 931         if (fDTDContentModelHandler != null) {
 932             fDTDContentModelHandler.pcdata(null);
 933         }
 934         skipSeparator(false, !scanningInternalSubset());
 935         while (fEntityScanner.skipChar('|')) {
 936             fStringBuffer.append('|');
 937             // call handler
 938             if (fDTDContentModelHandler != null) {
 939                 fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
 940                 null);
 941             }
 942             skipSeparator(false, !scanningInternalSubset());
 943 
 944             childName = fEntityScanner.scanName();
 945             if (childName == null) {
 946                 reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
 947                 new Object[]{elName});
 948             }
 949             fStringBuffer.append(childName);
 950             // call handler
 951             if (fDTDContentModelHandler != null) {
 952                 fDTDContentModelHandler.element(childName, null);
 953             }
 954             skipSeparator(false, !scanningInternalSubset());
 955         }
 956         // The following check must be done in a single call (as opposed to one
 957         // for ')' and then one for '*') to guarantee that callbacks are
 958         // properly nested. We do not want to trigger endEntity too early in
 959         // case we cross the boundary of an entity between the two characters.
 960         if (fEntityScanner.skipString(")*")) {
 961             fStringBuffer.append(")*");
 962             // call handler
 963             if (fDTDContentModelHandler != null) {
 964                 fDTDContentModelHandler.endGroup(null);
 965                 fDTDContentModelHandler.occurrence(XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE,
 966                 null);
 967             }
 968         }
 969         else if (childName != null) {
 970             reportFatalError("MixedContentUnterminated",
 971             new Object[]{elName});
 972         }
 973         else if (fEntityScanner.skipChar(')')){
 974             fStringBuffer.append(')');
 975             // call handler
 976             if (fDTDContentModelHandler != null) {
 977                 fDTDContentModelHandler.endGroup(null);
 978             }
 979         }
 980         else {
 981             reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
 982             new Object[]{elName});
 983         }
 984         fMarkUpDepth--;
 985         // we are done
 986     }
 987 
 988     /**
 989      * scan children content model
 990      * This assumes it can simply append to fStringBuffer.
 991      * <pre>
 992      * [47]    children  ::=    (choice | seq) ('?' | '*' | '+')?
 993      * [48]    cp        ::=    (Name | choice | seq) ('?' | '*' | '+')?
 994      * [49]    choice    ::=    '(' S? cp ( S? '|' S? cp )+ S? ')'
 995      * [50]    seq       ::=    '(' S? cp ( S? ',' S? cp )* S? ')'
 996      * </pre>
 997      *
 998      * @param elName The element type name this declaration is about.
 999      *
1000      * <strong>Note:</strong> Called after scanning past the first open
1001      * paranthesis.
1002      */
1003     private final void scanChildren(String elName)
1004     throws IOException, XNIException {
1005 
1006         fContentDepth = 0;
1007         pushContentStack(0);
1008         int currentOp = 0;
1009         int c;
1010         while (true) {
1011             if (fEntityScanner.skipChar('(')) {
1012                 fMarkUpDepth++;
1013                 fStringBuffer.append('(');
1014                 // call handler
1015                 if (fDTDContentModelHandler != null) {
1016                     fDTDContentModelHandler.startGroup(null);
1017                 }
1018                 // push current op on stack and reset it
1019                 pushContentStack(currentOp);
1020                 currentOp = 0;
1021                 skipSeparator(false, !scanningInternalSubset());
1022                 continue;
1023             }
1024             skipSeparator(false, !scanningInternalSubset());
1025             String childName = fEntityScanner.scanName();
1026             if (childName == null) {
1027                 reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
1028                 new Object[]{elName});
1029                 return;
1030             }
1031             // call handler
1032             if (fDTDContentModelHandler != null) {
1033                 fDTDContentModelHandler.element(childName, null);
1034             }
1035             fStringBuffer.append(childName);
1036             c = fEntityScanner.peekChar();
1037             if (c == '?' || c == '*' || c == '+') {
1038                 // call handler
1039                 if (fDTDContentModelHandler != null) {
1040                     short oc;
1041                     if (c == '?') {
1042                         oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
1043                     }
1044                     else if (c == '*') {
1045                         oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
1046                     }
1047                     else {
1048                         oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
1049                     }
1050                     fDTDContentModelHandler.occurrence(oc, null);
1051                 }
1052                 fEntityScanner.scanChar();
1053                 fStringBuffer.append((char)c);
1054             }
1055             while (true) {
1056                 skipSeparator(false, !scanningInternalSubset());
1057                 c = fEntityScanner.peekChar();
1058                 if (c == ',' && currentOp != '|') {
1059                     currentOp = c;
1060                     // call handler
1061                     if (fDTDContentModelHandler != null) {
1062                         fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
1063                         null);
1064                     }
1065                     fEntityScanner.scanChar();
1066                     fStringBuffer.append(',');
1067                     break;
1068                 }
1069                 else if (c == '|' && currentOp != ',') {
1070                     currentOp = c;
1071                     // call handler
1072                     if (fDTDContentModelHandler != null) {
1073                         fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
1074                         null);
1075                     }
1076                     fEntityScanner.scanChar();
1077                     fStringBuffer.append('|');
1078                     break;
1079                 }
1080                 else if (c != ')') {
1081                     reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
1082                     new Object[]{elName});
1083                 }
1084                 // call handler
1085                 if (fDTDContentModelHandler != null) {
1086                     fDTDContentModelHandler.endGroup(null);
1087                 }
1088                 // restore previous op
1089                 currentOp = popContentStack();
1090                 short oc;
1091                 // The following checks must be done in a single call (as
1092                 // opposed to one for ')' and then one for '?', '*', and '+')
1093                 // to guarantee that callbacks are properly nested. We do not
1094                 // want to trigger endEntity too early in case we cross the
1095                 // boundary of an entity between the two characters.
1096                 if (fEntityScanner.skipString(")?")) {
1097                     fStringBuffer.append(")?");
1098                     // call handler
1099                     if (fDTDContentModelHandler != null) {
1100                         oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
1101                         fDTDContentModelHandler.occurrence(oc, null);
1102                     }
1103                 }
1104                 else if (fEntityScanner.skipString(")+")) {
1105                     fStringBuffer.append(")+");
1106                     // call handler
1107                     if (fDTDContentModelHandler != null) {
1108                         oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
1109                         fDTDContentModelHandler.occurrence(oc, null);
1110                     }
1111                 }
1112                 else if (fEntityScanner.skipString(")*")) {
1113                     fStringBuffer.append(")*");
1114                     // call handler
1115                     if (fDTDContentModelHandler != null) {
1116                         oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
1117                         fDTDContentModelHandler.occurrence(oc, null);
1118                     }
1119                 }
1120                 else {
1121                     // no occurrence specified
1122                     fEntityScanner.scanChar();
1123                     fStringBuffer.append(')');
1124                 }
1125                 fMarkUpDepth--;
1126                 if (fContentDepth == 0) {
1127                     return;
1128                 }
1129             }
1130             skipSeparator(false, !scanningInternalSubset());
1131         }
1132     }
1133 
1134     /**
1135      * Scans an attlist declaration
1136      * <p>
1137      * <pre>
1138      * [52]  AttlistDecl    ::=   '&lt;!ATTLIST' S Name AttDef* S? '>'
1139      * [53]  AttDef         ::=   S Name S AttType S DefaultDecl
1140      * </pre>
1141      * <p>
1142      * <strong>Note:</strong> Called after scanning past '&lt;!ATTLIST'
1143      */
1144     protected final void scanAttlistDecl() throws IOException, XNIException {
1145 
1146         // spaces
1147         fReportEntity = false;
1148         if (!skipSeparator(true, !scanningInternalSubset())) {
1149             reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL",
1150             null);
1151         }
1152 
1153         // element name
1154         String elName = fEntityScanner.scanName();
1155         if (elName == null) {
1156             reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL",
1157             null);
1158         }
1159 
1160         // call handler
1161         if (fDTDHandler != null) {
1162             fDTDHandler.startAttlist(elName, null);
1163         }
1164 
1165         // spaces
1166         if (!skipSeparator(true, !scanningInternalSubset())) {
1167             // no space, is it the end yet?
1168             if (fEntityScanner.skipChar('>')) {
1169                 // yes, stop here
1170                 // call handler
1171                 if (fDTDHandler != null) {
1172                     fDTDHandler.endAttlist(null);
1173                 }
1174                 fMarkUpDepth--;
1175                 return;
1176             }
1177             else {
1178                 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF",
1179                 new Object[]{elName});
1180             }
1181         }
1182 
1183         // definitions
1184         while (!fEntityScanner.skipChar('>')) {
1185             String name = fEntityScanner.scanName();
1186             if (name == null) {
1187                 reportFatalError("AttNameRequiredInAttDef",
1188                 new Object[]{elName});
1189             }
1190             // spaces
1191             if (!skipSeparator(true, !scanningInternalSubset())) {
1192                 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTTYPE_IN_ATTDEF",
1193                 new Object[]{elName, name});
1194             }
1195             // type
1196             String type = scanAttType(elName, name);
1197 
1198             // spaces
1199             if (!skipSeparator(true, !scanningInternalSubset())) {
1200                 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_DEFAULTDECL_IN_ATTDEF",
1201                 new Object[]{elName, name});
1202             }
1203 
1204             // default decl
1205             String defaultType = scanAttDefaultDecl(elName, name,
1206             type,
1207             fLiteral, fLiteral2);
1208             // REVISIT: Should we do anything with the non-normalized
1209             //          default attribute value? -Ac
1210             // yes--according to bug 5073.  - neilg
1211             String[] enumr = null;
1212             if( fDTDHandler != null || nonValidatingMode){
1213                 if (fEnumerationCount != 0) {
1214                     enumr = new String[fEnumerationCount];
1215                     System.arraycopy(fEnumeration, 0, enumr,
1216                     0, fEnumerationCount);
1217                 }
1218             }
1219             // call handler
1220             // Determine whether the default value to be passed should be null.
1221             // REVISIT: should probably check whether fLiteral.ch is null instead. LM.
1222             if (defaultType!=null && (defaultType.equals("#REQUIRED") ||
1223             defaultType.equals("#IMPLIED"))) {
1224                 if (fDTDHandler != null){
1225                     fDTDHandler.attributeDecl(elName, name, type, enumr,
1226                     defaultType, null, null, null);
1227                 }
1228                 if(nonValidatingMode){
1229                     nvGrammarInfo.attributeDecl(elName, name, type, enumr,
1230                     defaultType, null, null, null);
1231 
1232                 }
1233             }
1234             else {
1235                 if (fDTDHandler != null){
1236                     fDTDHandler.attributeDecl(elName, name, type, enumr,
1237                     defaultType, fLiteral, fLiteral2, null);
1238                 }
1239                 if(nonValidatingMode){
1240                     nvGrammarInfo.attributeDecl(elName, name, type, enumr,
1241                     defaultType, fLiteral, fLiteral2, null);
1242                 }
1243             }
1244             skipSeparator(false, !scanningInternalSubset());
1245         }
1246 
1247         // call handler
1248         if (fDTDHandler != null) {
1249             fDTDHandler.endAttlist(null);
1250         }
1251         fMarkUpDepth--;
1252         fReportEntity = true;
1253 
1254     } // scanAttlistDecl()
1255 
1256     /**
1257      * Scans an attribute type definition
1258      * <p>
1259      * <pre>
1260      * [54]  AttType        ::=   StringType | TokenizedType | EnumeratedType
1261      * [55]  StringType     ::=   'CDATA'
1262      * [56]  TokenizedType  ::=   'ID'
1263      *                          | 'IDREF'
1264      *                          | 'IDREFS'
1265      *                          | 'ENTITY'
1266      *                          | 'ENTITIES'
1267      *                          | 'NMTOKEN'
1268      *                          | 'NMTOKENS'
1269      * [57]  EnumeratedType ::=    NotationType | Enumeration
1270      * [58]  NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
1271      * [59]  Enumeration    ::=    '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
1272      * </pre>
1273      * <p>
1274      * <strong>Note:</strong> Called after scanning past '&lt;!ATTLIST'
1275      *
1276      * @param elName The element type name this declaration is about.
1277      * @param atName The attribute name this declaration is about.
1278      */
1279     private final String scanAttType(String elName, String atName)
1280     throws IOException, XNIException {
1281 
1282         String type = null;
1283         fEnumerationCount = 0;
1284         /*
1285          * Watchout: the order here is important: when a string happens to
1286          * be a substring of another string, the longer one needs to be
1287          * looked for first!!
1288          */
1289         if (fEntityScanner.skipString("CDATA")) {
1290             type = "CDATA";
1291         }
1292         else if (fEntityScanner.skipString("IDREFS")) {
1293             type = "IDREFS";
1294         }
1295         else if (fEntityScanner.skipString("IDREF")) {
1296             type = "IDREF";
1297         }
1298         else if (fEntityScanner.skipString("ID")) {
1299             type = "ID";
1300         }
1301         else if (fEntityScanner.skipString("ENTITY")) {
1302             type = "ENTITY";
1303         }
1304         else if (fEntityScanner.skipString("ENTITIES")) {
1305             type = "ENTITIES";
1306         }
1307         else if (fEntityScanner.skipString("NMTOKENS")) {
1308             type = "NMTOKENS";
1309         }
1310         else if (fEntityScanner.skipString("NMTOKEN")) {
1311             type = "NMTOKEN";
1312         }
1313         else if (fEntityScanner.skipString("NOTATION")) {
1314             type = "NOTATION";
1315             // spaces
1316             if (!skipSeparator(true, !scanningInternalSubset())) {
1317                 reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_IN_NOTATIONTYPE",
1318                 new Object[]{elName, atName});
1319             }
1320             // open paren
1321             int c = fEntityScanner.scanChar();
1322             if (c != '(') {
1323                 reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE",
1324                 new Object[]{elName, atName});
1325             }
1326             fMarkUpDepth++;
1327             do {
1328                 skipSeparator(false, !scanningInternalSubset());
1329                 String aName = fEntityScanner.scanName();
1330                 if (aName == null) {
1331                     reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE",
1332                     new Object[]{elName, atName});
1333                 }
1334                 ensureEnumerationSize(fEnumerationCount + 1);
1335                 fEnumeration[fEnumerationCount++] = aName;
1336                 skipSeparator(false, !scanningInternalSubset());
1337                 c = fEntityScanner.scanChar();
1338             } while (c == '|');
1339             if (c != ')') {
1340                 reportFatalError("NotationTypeUnterminated",
1341                 new Object[]{elName, atName});
1342             }
1343             fMarkUpDepth--;
1344         }
1345         else {              // Enumeration
1346             type = "ENUMERATION";
1347             // open paren
1348             int c = fEntityScanner.scanChar();
1349             if (c != '(') {
1350                 //                       "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL",
1351                 reportFatalError("AttTypeRequiredInAttDef",
1352                 new Object[]{elName, atName});
1353             }
1354             fMarkUpDepth++;
1355             do {
1356                 skipSeparator(false, !scanningInternalSubset());
1357                 String token = fEntityScanner.scanNmtoken();
1358                 if (token == null) {
1359                     reportFatalError("MSG_NMTOKEN_REQUIRED_IN_ENUMERATION",
1360                     new Object[]{elName, atName});
1361                 }
1362                 ensureEnumerationSize(fEnumerationCount + 1);
1363                 fEnumeration[fEnumerationCount++] = token;
1364                 skipSeparator(false, !scanningInternalSubset());
1365                 c = fEntityScanner.scanChar();
1366             } while (c == '|');
1367             if (c != ')') {
1368                 reportFatalError("EnumerationUnterminated",
1369                 new Object[]{elName, atName});
1370             }
1371             fMarkUpDepth--;
1372         }
1373         return type;
1374 
1375     } // scanAttType():String
1376 
1377 
1378     /**
1379      * Scans an attribute default declaration
1380      * <p>
1381      * <pre>
1382      * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
1383      * </pre>
1384      *
1385      * @param name The name of the attribute being scanned.
1386      * @param defaultVal The string to fill in with the default value.
1387      */
1388     protected final String scanAttDefaultDecl(String elName, String atName,
1389     String type,
1390     XMLString defaultVal,
1391     XMLString nonNormalizedDefaultVal)
1392     throws IOException, XNIException {
1393 
1394         String defaultType = null;
1395         fString.clear();
1396         defaultVal.clear();
1397         if (fEntityScanner.skipString("#REQUIRED")) {
1398             defaultType = "#REQUIRED";
1399         }
1400         else if (fEntityScanner.skipString("#IMPLIED")) {
1401             defaultType = "#IMPLIED";
1402         }
1403         else {
1404             if (fEntityScanner.skipString("#FIXED")) {
1405                 defaultType = "#FIXED";
1406                 // spaces
1407                 if (!skipSeparator(true, !scanningInternalSubset())) {
1408                     reportFatalError("MSG_SPACE_REQUIRED_AFTER_FIXED_IN_DEFAULTDECL",
1409                     new Object[]{elName, atName});
1410                 }
1411             }
1412             // AttValue
1413             boolean isVC = !fStandalone  &&  (fSeenExternalDTD || fSeenExternalPE) ;
1414             scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName,
1415             fAttributes, 0, isVC);
1416         }
1417         return defaultType;
1418 
1419     } // ScanAttDefaultDecl
1420 
1421     /**
1422      * Scans an entity declaration
1423      * <p>
1424      * <pre>
1425      * [70]    EntityDecl  ::=    GEDecl | PEDecl
1426      * [71]    GEDecl      ::=    '&lt;!ENTITY' S Name S EntityDef S? '>'
1427      * [72]    PEDecl      ::=    '&lt;!ENTITY' S '%' S Name S PEDef S? '>'
1428      * [73]    EntityDef   ::=    EntityValue | (ExternalID NDataDecl?)
1429      * [74]    PEDef       ::=    EntityValue | ExternalID
1430      * [75]    ExternalID  ::=    'SYSTEM' S SystemLiteral
1431      *                          | 'PUBLIC' S PubidLiteral S SystemLiteral
1432      * [76]    NDataDecl   ::=    S 'NDATA' S Name
1433      * </pre>
1434      * <p>
1435      * <strong>Note:</strong> Called after scanning past '&lt;!ENTITY'
1436      */
1437     private final void scanEntityDecl() throws IOException, XNIException {
1438 
1439         boolean isPEDecl = false;
1440         boolean sawPERef = false;
1441         fReportEntity = false;
1442         if (fEntityScanner.skipSpaces()) {
1443             if (!fEntityScanner.skipChar('%')) {
1444                 isPEDecl = false; // <!ENTITY x "x">
1445             }
1446             else if (skipSeparator(true, !scanningInternalSubset())) {
1447                 // <!ENTITY % x "x">
1448                 isPEDecl = true;
1449             }
1450             else if (scanningInternalSubset()) {
1451                 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
1452                 null);
1453                 isPEDecl = true;
1454             }
1455             else if (fEntityScanner.peekChar() == '%') {
1456                 // <!ENTITY %%x; "x"> is legal
1457                 skipSeparator(false, !scanningInternalSubset());
1458                 isPEDecl = true;
1459             }
1460             else {
1461                 sawPERef = true;
1462             }
1463         }
1464         else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) {
1465             // <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
1466             reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
1467             null);
1468             isPEDecl = false;
1469         }
1470         else if (fEntityScanner.skipSpaces()) {
1471             // <!ENTITY% ...>
1472             reportFatalError("MSG_SPACE_REQUIRED_BEFORE_PERCENT_IN_PEDECL",
1473             null);
1474             isPEDecl = false;
1475         }
1476         else {
1477             sawPERef = true;
1478         }
1479         if (sawPERef) {
1480             while (true) {
1481                 String peName = fEntityScanner.scanName();
1482                 if (peName == null) {
1483                     reportFatalError("NameRequiredInPEReference", null);
1484                 }
1485                 else if (!fEntityScanner.skipChar(';')) {
1486                     reportFatalError("SemicolonRequiredInPEReference",
1487                     new Object[]{peName});
1488                 }
1489                 else {
1490                     startPE(peName, false);
1491                 }
1492                 fEntityScanner.skipSpaces();
1493                 if (!fEntityScanner.skipChar('%'))
1494                     break;
1495                 if (!isPEDecl) {
1496                     if (skipSeparator(true, !scanningInternalSubset())) {
1497                         isPEDecl = true;
1498                         break;
1499                     }
1500                     isPEDecl = fEntityScanner.skipChar('%');
1501                 }
1502             }
1503         }
1504 
1505         // name
1506         String name = fEntityScanner.scanName();
1507         if (name == null) {
1508             reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null);
1509         }
1510 
1511         // spaces
1512         if (!skipSeparator(true, !scanningInternalSubset())) {
1513             reportFatalError("MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL",
1514             new Object[]{name});
1515         }
1516 
1517         // external id
1518         scanExternalID(fStrings, false);
1519         String systemId = fStrings[0];
1520         String publicId = fStrings[1];
1521 
1522         if (isPEDecl && systemId != null) {
1523             fSeenExternalPE = true;
1524         }
1525 
1526         String notation = null;
1527         // NDATA
1528         boolean sawSpace = skipSeparator(true, !scanningInternalSubset());
1529         if (!isPEDecl && fEntityScanner.skipString("NDATA")) {
1530             // check whether there was space before NDATA
1531             if (!sawSpace) {
1532                 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NDATA_IN_UNPARSED_ENTITYDECL",
1533                 new Object[]{name});
1534             }
1535 
1536             // spaces
1537             if (!skipSeparator(true, !scanningInternalSubset())) {
1538                 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL",
1539                 new Object[]{name});
1540             }
1541             notation = fEntityScanner.scanName();
1542             if (notation == null) {
1543                 reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL",
1544                 new Object[]{name});
1545             }
1546         }
1547 
1548         // internal entity
1549         if (systemId == null) {
1550             scanEntityValue(name, isPEDecl, fLiteral, fLiteral2);
1551             // since we need it's value anyway, let's snag it so it doesn't get corrupted
1552             // if a new load takes place before we store the entity values
1553             fStringBuffer.clear();
1554             fStringBuffer2.clear();
1555             fStringBuffer.append(fLiteral.ch, fLiteral.offset, fLiteral.length);
1556             fStringBuffer2.append(fLiteral2.ch, fLiteral2.offset, fLiteral2.length);
1557         }
1558 
1559         // skip possible trailing space
1560         skipSeparator(false, !scanningInternalSubset());
1561 
1562         // end
1563         if (!fEntityScanner.skipChar('>')) {
1564             reportFatalError("EntityDeclUnterminated", new Object[]{name});
1565         }
1566         fMarkUpDepth--;
1567 
1568         // register entity and make callback
1569         if (isPEDecl) {
1570             name = "%" + name;
1571         }
1572         if (systemId != null) {
1573             String baseSystemId = fEntityScanner.getBaseSystemId();
1574             if (notation != null) {
1575                 fEntityStore.addUnparsedEntity(name, publicId, systemId, baseSystemId, notation);
1576             }
1577             else {
1578                 fEntityStore.addExternalEntity(name, publicId, systemId,
1579                 baseSystemId);
1580             }
1581             if (fDTDHandler != null) {
1582                 //Venu Revisit : why false has been removed in expandSYstem
1583                 fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId ));
1584 
1585                 if (notation != null) {
1586                     fDTDHandler.unparsedEntityDecl(name, fResourceIdentifier,
1587                     notation, null);
1588                 }
1589                 else {
1590                     fDTDHandler.externalEntityDecl(name, fResourceIdentifier, null);
1591                 }
1592             }
1593         }
1594         else {
1595             fEntityStore.addInternalEntity(name, fStringBuffer.toString());
1596             if (fDTDHandler != null) {
1597                 fDTDHandler.internalEntityDecl(name, fStringBuffer, fStringBuffer2, null);
1598             }
1599         }
1600         fReportEntity = true;
1601 
1602     } // scanEntityDecl()
1603 
1604     /**
1605      * Scans an entity value.
1606      *
1607      * @param value The string to fill in with the value.
1608      * @param nonNormalizedValue The string to fill in with the
1609      *                           non-normalized value.
1610      *
1611      * <strong>Note:</strong> This method uses fString, fStringBuffer (through
1612      * the use of scanCharReferenceValue), and fStringBuffer2, anything in them
1613      * at the time of calling is lost.
1614      */
1615     protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value,
1616     XMLString nonNormalizedValue)
1617     throws IOException, XNIException {
1618         int quote = fEntityScanner.scanChar();
1619         if (quote != '\'' && quote != '"') {
1620             reportFatalError("OpenQuoteMissingInDecl", null);
1621         }
1622         // store at which depth of entities we start
1623         int entityDepth = fEntityDepth;
1624 
1625         XMLString literal = fString;
1626         XMLString literal2 = fString;
1627         int countChar = 0;
1628         if (fLimitAnalyzer == null && fSecurityManager != null) {
1629             fLimitAnalyzer = fSecurityManager.getLimitAnalyzer();
1630             fLimitAnalyzer.startEntity(entityName);
1631         }
1632 
1633         if (fEntityScanner.scanLiteral(quote, fString) != quote) {
1634             fStringBuffer.clear();
1635             fStringBuffer2.clear();
1636             do {
1637                 if (isPEDecl && fLimitAnalyzer != null) {
1638                     checkLimit("%" + entityName, fString.length + countChar);
1639                 }
1640                 countChar = 0;
1641                 fStringBuffer.append(fString);
1642                 fStringBuffer2.append(fString);
1643                 if (fEntityScanner.skipChar('&')) {
1644                     if (fEntityScanner.skipChar('#')) {
1645                         fStringBuffer2.append("&#");
1646                         scanCharReferenceValue(fStringBuffer, fStringBuffer2);
1647                     }
1648                     else {
1649                         fStringBuffer.append('&');
1650                         fStringBuffer2.append('&');
1651                         String eName = fEntityScanner.scanName();
1652                         if (eName == null) {
1653                             reportFatalError("NameRequiredInReference",
1654                             null);
1655                         }
1656                         else {
1657                             fStringBuffer.append(eName);
1658                             fStringBuffer2.append(eName);
1659                         }
1660                         if (!fEntityScanner.skipChar(';')) {
1661                             reportFatalError("SemicolonRequiredInReference",
1662                             new Object[]{eName});
1663                         }
1664                         else {
1665                             fStringBuffer.append(';');
1666                             fStringBuffer2.append(';');
1667                         }
1668                     }
1669                 }
1670                 else if (fEntityScanner.skipChar('%')) {
1671                     while (true) {
1672                         fStringBuffer2.append('%');
1673                         String peName = fEntityScanner.scanName();
1674                         if (peName == null) {
1675                             reportFatalError("NameRequiredInPEReference",
1676                             null);
1677                         }
1678                         else if (!fEntityScanner.skipChar(';')) {
1679                             reportFatalError("SemicolonRequiredInPEReference",
1680                             new Object[]{peName});
1681                         }
1682                         else {
1683                             if (scanningInternalSubset()) {
1684                                 reportFatalError("PEReferenceWithinMarkup",
1685                                 new Object[]{peName});
1686                             }
1687                             fStringBuffer2.append(peName);
1688                             fStringBuffer2.append(';');
1689                         }
1690                         startPE(peName, true);
1691                         // REVISIT: [Q] Why do we skip spaces here? -Ac
1692                         // REVISIT: This will make returning the non-
1693                         //          normalized value harder. -Ac
1694                         fEntityScanner.skipSpaces();
1695                         if (!fEntityScanner.skipChar('%'))
1696                             break;
1697                     }
1698                 }
1699                 else {
1700                     countChar++;
1701                     int c = fEntityScanner.peekChar();
1702                     if (XMLChar.isHighSurrogate(c)) {
1703                         scanSurrogates(fStringBuffer2);
1704                     }
1705                     else if (isInvalidLiteral(c)) {
1706                         reportFatalError("InvalidCharInLiteral",
1707                         new Object[]{Integer.toHexString(c)});
1708                         fEntityScanner.scanChar();
1709                     }
1710                     // if it's not the delimiting quote or if it is but from a
1711                     // different entity than the one this literal started from,
1712                     // simply append the character to our buffer
1713                     else if (c != quote || entityDepth != fEntityDepth) {
1714                         fStringBuffer.append((char)c);
1715                         fStringBuffer2.append((char)c);
1716                         fEntityScanner.scanChar();
1717                     }
1718                 }
1719             } while (fEntityScanner.scanLiteral(quote, fString) != quote);
1720             fStringBuffer.append(fString);
1721             fStringBuffer2.append(fString);
1722             literal = fStringBuffer;
1723             literal2 = fStringBuffer2;
1724         } else {
1725             if (isPEDecl) {
1726                 checkLimit("%" + entityName, literal);
1727         }
1728         }
1729         value.setValues(literal);
1730         nonNormalizedValue.setValues(literal2);
1731         if (fLimitAnalyzer != null) {
1732             fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName);
1733         }
1734 
1735         if (!fEntityScanner.skipChar(quote)) {
1736             reportFatalError("CloseQuoteMissingInDecl", null);
1737         }
1738     } // scanEntityValue(XMLString,XMLString):void
1739 
1740     /**
1741      * Scans a notation declaration
1742      * <p>
1743      * <pre>
1744      * [82] NotationDecl ::= '&lt;!NOTATION' S Name S (ExternalID|PublicID) S? '>'
1745      * [83]  PublicID    ::= 'PUBLIC' S PubidLiteral
1746      * </pre>
1747      * <p>
1748      * <strong>Note:</strong> Called after scanning past '&lt;!NOTATION'
1749      */
1750     private final void scanNotationDecl() throws IOException, XNIException {
1751 
1752         // spaces
1753         fReportEntity = false;
1754         if (!skipSeparator(true, !scanningInternalSubset())) {
1755             reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_NOTATIONDECL",
1756             null);
1757         }
1758 
1759         // notation name
1760         String name = fEntityScanner.scanName();
1761         if (name == null) {
1762             reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL",
1763             null);
1764         }
1765 
1766         // spaces
1767         if (!skipSeparator(true, !scanningInternalSubset())) {
1768             reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL",
1769             new Object[]{name});
1770         }
1771 
1772         // external id
1773         scanExternalID(fStrings, true);
1774         String systemId = fStrings[0];
1775         String publicId = fStrings[1];
1776         String baseSystemId = fEntityScanner.getBaseSystemId();
1777 
1778         if (systemId == null && publicId == null) {
1779             reportFatalError("ExternalIDorPublicIDRequired",
1780             new Object[]{name});
1781         }
1782 
1783         // skip possible trailing space
1784         skipSeparator(false, !scanningInternalSubset());
1785 
1786         // end
1787         if (!fEntityScanner.skipChar('>')) {
1788             reportFatalError("NotationDeclUnterminated", new Object[]{name});
1789         }
1790         fMarkUpDepth--;
1791 
1792         fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId ));
1793         if (nonValidatingMode) nvGrammarInfo.notationDecl(name, fResourceIdentifier, null);
1794         // call handler
1795         if (fDTDHandler != null) {
1796             //Venu Revisit wby false has been removed.
1797             //fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId, false));
1798             fDTDHandler.notationDecl(name, fResourceIdentifier, null);
1799         }
1800         fReportEntity = true;
1801 
1802     } // scanNotationDecl()
1803 
1804     /**
1805      * Scans a conditional section. If it's a section to ignore the whole
1806      * section gets scanned through and this method only returns after the
1807      * closing bracket has been found. When it's an include section though, it
1808      * returns to let the main loop take care of scanning it. In that case the
1809      * end of the section if handled by the main loop (scanDecls).
1810      * <p>
1811      * <pre>
1812      * [61] conditionalSect   ::= includeSect | ignoreSect
1813      * [62] includeSect       ::= '&lt;![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
1814      * [63] ignoreSect   ::= '&lt;![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
1815      * [64] ignoreSectContents ::= Ignore ('&lt;![' ignoreSectContents ']]>' Ignore)*
1816      * [65] Ignore            ::=    Char* - (Char* ('&lt;![' | ']]>') Char*)
1817      * </pre>
1818      * <p>
1819      * <strong>Note:</strong> Called after scanning past '&lt;![' */
1820     private final void scanConditionalSect(int currPEDepth)
1821     throws IOException, XNIException {
1822 
1823         fReportEntity = false;
1824         skipSeparator(false, !scanningInternalSubset());
1825 
1826         if (fEntityScanner.skipString("INCLUDE")) {
1827             skipSeparator(false, !scanningInternalSubset());
1828             if(currPEDepth != fPEDepth && fValidation) {
1829                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1830                 "INVALID_PE_IN_CONDITIONAL",
1831                 new Object[]{ fEntityManager.fCurrentEntity.name},
1832                 XMLErrorReporter.SEVERITY_ERROR);
1833             }
1834             // call handler
1835             if (!fEntityScanner.skipChar('[')) {
1836                 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
1837             }
1838 
1839             if (fDTDHandler != null) {
1840                 fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_INCLUDE,
1841                 null);
1842             }
1843             fIncludeSectDepth++;
1844             // just stop there and go back to the main loop
1845             fReportEntity = true;
1846         }
1847         else if (fEntityScanner.skipString("IGNORE")) {
1848             skipSeparator(false, !scanningInternalSubset());
1849             if(currPEDepth != fPEDepth && fValidation) {
1850                 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
1851                 "INVALID_PE_IN_CONDITIONAL",
1852                 new Object[]{ fEntityManager.fCurrentEntity.name},
1853                 XMLErrorReporter.SEVERITY_ERROR);
1854             }
1855             // call handler
1856             if (fDTDHandler != null) {
1857                 fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE,
1858                 null);
1859             }
1860             if (!fEntityScanner.skipChar('[')) {
1861                 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
1862             }
1863             fReportEntity = true;
1864             int initialDepth = ++fIncludeSectDepth;
1865             if (fDTDHandler != null) {
1866                 fIgnoreConditionalBuffer.clear();
1867             }
1868             while (true) {
1869                 if (fEntityScanner.skipChar('<')) {
1870                     if (fDTDHandler != null) {
1871                         fIgnoreConditionalBuffer.append('<');
1872                     }
1873                     //
1874                     // These tests are split so that we handle cases like
1875                     // '<<![' and '<!<![' which we might otherwise miss.
1876                     //
1877                     if (fEntityScanner.skipChar('!')) {
1878                         if(fEntityScanner.skipChar('[')) {
1879                             if (fDTDHandler != null) {
1880                                 fIgnoreConditionalBuffer.append("![");
1881                             }
1882                             fIncludeSectDepth++;
1883                         } else {
1884                             if (fDTDHandler != null) {
1885                                 fIgnoreConditionalBuffer.append("!");
1886                             }
1887                         }
1888                     }
1889                 }
1890                 else if (fEntityScanner.skipChar(']')) {
1891                     if (fDTDHandler != null) {
1892                         fIgnoreConditionalBuffer.append(']');
1893                     }
1894                     //
1895                     // The same thing goes for ']<![' and '<]]>', etc.
1896                     //
1897                     if (fEntityScanner.skipChar(']')) {
1898                         if (fDTDHandler != null) {
1899                             fIgnoreConditionalBuffer.append(']');
1900                         }
1901                         while (fEntityScanner.skipChar(']')) {
1902                             /* empty loop body */
1903                             if (fDTDHandler != null) {
1904                                 fIgnoreConditionalBuffer.append(']');
1905                             }
1906                         }
1907                         if (fEntityScanner.skipChar('>')) {
1908                             if (fIncludeSectDepth-- == initialDepth) {
1909                                 fMarkUpDepth--;
1910                                 // call handler
1911                                 if (fDTDHandler != null) {
1912                                     fLiteral.setValues(fIgnoreConditionalBuffer.ch, 0,
1913                                     fIgnoreConditionalBuffer.length - 2);
1914                                     fDTDHandler.ignoredCharacters(fLiteral, null);
1915                                     fDTDHandler.endConditional(null);
1916                                 }
1917                                 return;
1918                             } else if(fDTDHandler != null) {
1919                                 fIgnoreConditionalBuffer.append('>');
1920                             }
1921                         }
1922                     }
1923                 }
1924                 else {
1925                     int c = fEntityScanner.scanChar();
1926                     if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
1927                         reportFatalError("IgnoreSectUnterminated", null);
1928                         return;
1929                     }
1930                     if (fDTDHandler != null) {
1931                         fIgnoreConditionalBuffer.append((char)c);
1932                     }
1933                 }
1934             }
1935         }
1936         else {
1937             reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
1938         }
1939 
1940     } // scanConditionalSect()
1941 
1942     /**
1943      * Dispatch an XML "event".
1944      *
1945      * @param complete True if this method is intended to scan
1946      *                 and dispatch as much as possible.
1947      *
1948      * @return True if there is more to scan.
1949      *
1950      * @throws IOException  Thrown on i/o error.
1951      * @throws XNIException Thrown on parse error.
1952      *
1953      */
1954     protected final boolean scanDecls(boolean complete)
1955     throws IOException, XNIException {
1956 
1957         skipSeparator(false, true);
1958         boolean again = true;
1959         //System.out.println("scanDecls"+fScannerState);
1960         while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) {
1961             again = complete;
1962             if (fEntityScanner.skipChar('<')) {
1963                 fMarkUpDepth++;
1964                 if (fEntityScanner.skipChar('?')) {
1965                     fStringBuffer.clear();
1966                     scanPI(fStringBuffer);
1967                     fMarkUpDepth--; // we're done with this decl
1968                 }
1969                 else if (fEntityScanner.skipChar('!')) {
1970                     if (fEntityScanner.skipChar('-')) {
1971                         if (!fEntityScanner.skipChar('-')) {
1972                             reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
1973                             null);
1974                         } else {
1975                             scanComment();
1976                         }
1977                     }
1978                     else if (fEntityScanner.skipString("ELEMENT")) {
1979                         scanElementDecl();
1980                     }
1981                     else if (fEntityScanner.skipString("ATTLIST")) {
1982                         scanAttlistDecl();
1983                     }
1984                     else if (fEntityScanner.skipString("ENTITY")) {
1985                         scanEntityDecl();
1986                     }
1987                     else if (fEntityScanner.skipString("NOTATION")) {
1988                         scanNotationDecl();
1989                     }
1990                     else if (fEntityScanner.skipChar('[') &&
1991                     !scanningInternalSubset()) {
1992                         scanConditionalSect(fPEDepth);
1993                     }
1994                     else {
1995                         fMarkUpDepth--;
1996                         reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
1997                         null);
1998                     }
1999                 }
2000                 else {
2001                     fMarkUpDepth--;
2002                     reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
2003                 }
2004             }
2005             else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) {
2006                 // end of conditional section?
2007                 if (!fEntityScanner.skipChar(']')
2008                 || !fEntityScanner.skipChar('>')) {
2009                     reportFatalError("IncludeSectUnterminated", null);
2010                 }
2011                 // call handler
2012                 if (fDTDHandler != null) {
2013                     fDTDHandler.endConditional(null);
2014                 }
2015                 // decreaseMarkupDepth();
2016                 fIncludeSectDepth--;
2017                 fMarkUpDepth--;
2018             }
2019             else if (scanningInternalSubset() &&
2020             fEntityScanner.peekChar() == ']') {
2021                 // this is the end of the internal subset, let's stop here
2022                 return false;
2023             }
2024             else if (fEntityScanner.skipSpaces()) {
2025                 // simply skip
2026             }
2027             else {
2028                 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
2029             }
2030             skipSeparator(false, true);
2031         }
2032         return fScannerState != SCANNER_STATE_END_OF_INPUT;
2033     }
2034 
2035     /**
2036      * Skip separator. This is typically just whitespace but it can also be one
2037      * or more parameter entity references.
2038      * <p>
2039      * If there are some it "expands them" by calling the corresponding entity
2040      * from the entity manager.
2041      * <p>
2042      * This is recursive and will process has many refs as possible.
2043      *
2044      * @param spaceRequired Specify whether some leading whitespace should be
2045      *                      found
2046      * @param lookForPERefs Specify whether parameter entity references should
2047      *                      be looked for
2048      * @return True if any leading whitespace was found or the end of a
2049      *         parameter entity was crossed.
2050      */
2051     private boolean skipSeparator(boolean spaceRequired, boolean lookForPERefs)
2052     throws IOException, XNIException {
2053         int depth = fPEDepth;
2054         boolean sawSpace = fEntityScanner.skipSpaces();
2055         if (!lookForPERefs || !fEntityScanner.skipChar('%')) {
2056             return !spaceRequired || sawSpace || (depth != fPEDepth);
2057         }
2058         while (true) {
2059             String name = fEntityScanner.scanName();
2060             if (name == null) {
2061                 reportFatalError("NameRequiredInPEReference", null);
2062             }
2063             else if (!fEntityScanner.skipChar(';')) {
2064                 reportFatalError("SemicolonRequiredInPEReference",
2065                 new Object[]{name});
2066             }
2067             startPE(name, false);
2068             fEntityScanner.skipSpaces();
2069             if (!fEntityScanner.skipChar('%'))
2070                 return true;
2071         }
2072     }
2073 
2074 
2075     /*
2076      * Element Children Content Stack
2077      */
2078     private final void pushContentStack(int c) {
2079         if (fContentStack.length == fContentDepth) {
2080             int[] newStack = new int[fContentDepth * 2];
2081             System.arraycopy(fContentStack, 0, newStack, 0, fContentDepth);
2082             fContentStack = newStack;
2083         }
2084         fContentStack[fContentDepth++] = c;
2085     }
2086 
2087     private final int popContentStack() {
2088         return fContentStack[--fContentDepth];
2089     }
2090 
2091 
2092     /*
2093      * Parameter Entity Stack
2094      */
2095     private final void pushPEStack(int depth, boolean report) {
2096         if (fPEStack.length == fPEDepth) {
2097             int[] newIntStack = new int[fPEDepth * 2];
2098             System.arraycopy(fPEStack, 0, newIntStack, 0, fPEDepth);
2099             fPEStack = newIntStack;
2100             // report end/start calls
2101             boolean[] newBooleanStack = new boolean[fPEDepth * 2];
2102             System.arraycopy(fPEReport, 0, newBooleanStack, 0, fPEDepth);
2103             fPEReport = newBooleanStack;
2104 
2105         }
2106         fPEReport[fPEDepth] = report;
2107         fPEStack[fPEDepth++] = depth;
2108     }
2109 
2110     /** pop the stack */
2111     private final int popPEStack() {
2112         return fPEStack[--fPEDepth];
2113     }
2114 
2115     /** look at the top of the stack */
2116     private final boolean peekReportEntity() {
2117         return fPEReport[fPEDepth-1];
2118     }
2119 
2120 
2121     /*
2122      * Utility method
2123      */
2124     private final void ensureEnumerationSize(int size) {
2125         if (fEnumeration.length == size) {
2126             String[] newEnum = new String[size * 2];
2127             System.arraycopy(fEnumeration, 0, newEnum, 0, size);
2128             fEnumeration = newEnum;
2129         }
2130     }
2131 
2132     // private methods
2133     private void init() {
2134         // reset state related data
2135         fStartDTDCalled = false;
2136         fExtEntityDepth = 0;
2137         fIncludeSectDepth = 0;
2138         fMarkUpDepth = 0;
2139         fPEDepth = 0;
2140 
2141         fStandalone = false;
2142         fSeenExternalDTD = false;
2143         fSeenExternalPE = false;
2144 
2145         // set starting state
2146         setScannerState(SCANNER_STATE_TEXT_DECL);
2147         //new SymbolTable());
2148     }
2149 
2150     /**
2151      * Add the count of the content buffer and check if the accumulated
2152      * value exceeds the limit
2153      * @param entityName entity name
2154      * @param buffer content buffer
2155      */
2156     private void checkLimit(String entityName, XMLString buffer) {
2157         checkLimit(entityName, buffer.length);
2158     }
2159 
2160     /**
2161      * Add the count and check limit
2162      * @param entityName entity name
2163      * @param len length of the buffer
2164      */
2165     private void checkLimit(String entityName, int len) {
2166         if (fLimitAnalyzer == null) {
2167             fLimitAnalyzer = fSecurityManager.getLimitAnalyzer();
2168         }
2169         fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName, len);
2170         if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)) {
2171                     fSecurityManager.debugPrint();
2172             reportFatalError("MaxEntitySizeLimit", new Object[]{entityName,
2173                 fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
2174                 fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
2175                 fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)});
2176         }
2177         if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)) {
2178             fSecurityManager.debugPrint();
2179             reportFatalError("TotalEntitySizeLimit",
2180                 new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
2181                 fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
2182                 fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)});
2183         }
2184 
2185     }
2186 
2187     public DTDGrammar getGrammar(){
2188         return nvGrammarInfo;
2189     }
2190 
2191 } // class XMLDTDScannerImpl