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