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