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