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  
23  
24  import com.sun.org.apache.xerces.internal.util.Status;
25  import com.sun.xml.internal.stream.XMLEntityStorage;
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import javax.xml.stream.events.XMLEvent;
30  import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
31  import com.sun.org.apache.xerces.internal.util.SymbolTable;
32  import com.sun.org.apache.xerces.internal.util.XMLChar;
33  import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;
34  import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
35  import com.sun.org.apache.xerces.internal.utils.XMLLimitAnalyzer;
36  import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
37  import com.sun.org.apache.xerces.internal.xni.Augmentations;
38  import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
39  import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
40  import com.sun.org.apache.xerces.internal.xni.XMLString;
41  import com.sun.org.apache.xerces.internal.xni.XNIException;
42  import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
43  import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
44  import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
45  import com.sun.xml.internal.stream.Entity;
46  
47  //import com.sun.xml.stream.XMLEntityManager;
48  //import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
49  
50  /**
51   * This class is responsible for holding scanning methods common to
52   * scanning the XML document structure and content as well as the DTD
53   * structure and content. Both XMLDocumentScanner and XMLDTDScanner inherit
54   * from this base class.
55   *
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 Andy Clark, IBM
68   * @author Arnaud  Le Hors, IBM
69   * @author Eric Ye, IBM
70   * @author K.Venugopal SUN Microsystems
71   * @author Sunitha Reddy, SUN Microsystems
72   * @version $Id: XMLScanner.java,v 1.12 2010-11-01 04:39:41 joehw Exp $
73   */
74  public abstract class XMLScanner
75          implements XMLComponent {
76  
77      //
78      // Constants
79      //
80  
81      // feature identifiers
82  
83      /** Feature identifier: namespaces. */
84      protected static final String NAMESPACES =
85              Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
86  
87      /** Feature identifier: validation. */
88      protected static final String VALIDATION =
89              Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
90  
91      /** Feature identifier: notify character references. */
92      protected static final String NOTIFY_CHAR_REFS =
93              Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_CHAR_REFS_FEATURE;
94  
95      // property identifiers
96  
97      protected static final String PARSER_SETTINGS =
98                                  Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
99      /** Property identifier: symbol table. */
100     protected static final String SYMBOL_TABLE =
101             Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
102 
103     /** Property identifier: error reporter. */
104     protected static final String ERROR_REPORTER =
105             Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
106 
107     /** Property identifier: entity manager. */
108     protected static final String ENTITY_MANAGER =
109             Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY;
110 
111     /** Property identifier: Security manager. */
112     private static final String SECURITY_MANAGER = Constants.SECURITY_MANAGER;
113 
114     // debugging
115 
116     /** Debug attribute normalization. */
117     protected static final boolean DEBUG_ATTR_NORMALIZATION = false;
118 
119 
120     //xxx: setting the default value as false, as we dont need to calculate this value
121     //we should have a feature when set to true computes this value
122     private boolean fNeedNonNormalizedValue = false;
123 
124     protected ArrayList attributeValueCache = new ArrayList();
125     protected ArrayList stringBufferCache = new ArrayList();
126     protected int fStringBufferIndex = 0;
127     protected boolean fAttributeCacheInitDone = false;
128     protected int fAttributeCacheUsedCount = 0;
129 
130     //
131     // Data
132     //
133 
134     // features
135 
136     /**
137      * Validation. This feature identifier is:
138      * http://xml.org/sax/features/validation
139      */
140     protected boolean fValidation = false;
141 
142     /** Namespaces. */
143     protected boolean fNamespaces;
144 
145     /** Character references notification. */
146     protected boolean fNotifyCharRefs = false;
147 
148     /** Internal parser-settings feature */
149         protected boolean fParserSettings = true;
150 
151     // properties
152 
153     protected PropertyManager fPropertyManager = null ;
154     /** Symbol table. */
155     protected SymbolTable fSymbolTable;
156 
157     /** Error reporter. */
158     protected XMLErrorReporter fErrorReporter;
159 
160     /** Entity manager. */
161     //protected XMLEntityManager fEntityManager = PropertyManager.getEntityManager();
162     protected XMLEntityManager fEntityManager = null ;
163 
164     /** xxx this should be available from EntityManager Entity storage */
165     protected XMLEntityStorage fEntityStore = null ;
166 
167     /** Security manager. */
168     protected XMLSecurityManager fSecurityManager = null;
169 
170     /** Limit analyzer. */
171     protected XMLLimitAnalyzer fLimitAnalyzer = null;
172 
173     // protected data
174 
175     /** event type */
176     protected XMLEvent fEvent ;
177 
178     /** Entity scanner, this alwasy works on last entity that was opened. */
179     protected XMLEntityScanner fEntityScanner = null;
180 
181     /** Entity depth. */
182     protected int fEntityDepth;
183 
184     /** Literal value of the last character refence scanned. */
185     protected String fCharRefLiteral = null;
186 
187     /** Scanning attribute. */
188     protected boolean fScanningAttribute;
189 
190     /** Report entity boundary. */
191     protected boolean fReportEntity;
192 
193     // symbols
194 
195     /** Symbol: "version". */
196     protected final static String fVersionSymbol = "version".intern();
197 
198     /** Symbol: "encoding". */
199     protected final static String fEncodingSymbol = "encoding".intern();
200 
201     /** Symbol: "standalone". */
202     protected final static String fStandaloneSymbol = "standalone".intern();
203 
204     /** Symbol: "amp". */
205     protected final static String fAmpSymbol = "amp".intern();
206 
207     /** Symbol: "lt". */
208     protected final static String fLtSymbol = "lt".intern();
209 
210     /** Symbol: "gt". */
211     protected final static String fGtSymbol = "gt".intern();
212 
213     /** Symbol: "quot". */
214     protected final static String fQuotSymbol = "quot".intern();
215 
216     /** Symbol: "apos". */
217     protected final static String fAposSymbol = "apos".intern();
218 
219     // temporary variables
220 
221     // NOTE: These objects are private to help prevent accidental modification
222     //       of values by a subclass. If there were protected *and* the sub-
223     //       modified the values, it would be difficult to track down the real
224     //       cause of the bug. By making these private, we avoid this
225     //       possibility.
226 
227     /** String. */
228     private XMLString fString = new XMLString();
229 
230     /** String buffer. */
231     private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
232 
233     /** String buffer. */
234     private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
235 
236     /** String buffer. */
237     private XMLStringBuffer fStringBuffer3 = new XMLStringBuffer();
238 
239     // temporary location for Resource identification information.
240     protected XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl();
241     int initialCacheCount = 6;
242     //
243     // XMLComponent methods
244     //
245 
246     /**
247      *
248      *
249      * @param componentManager The component manager.
250      *
251      * @throws SAXException Throws exception if required features and
252      *                      properties cannot be found.
253      */
254     public void reset(XMLComponentManager componentManager)
255     throws XMLConfigurationException {
256 
257                 fParserSettings = componentManager.getFeature(PARSER_SETTINGS, true);
258 
259                 if (!fParserSettings) {
260                         // parser settings have not been changed
261                         init();
262                         return;
263                 }
264 
265 
266         // Xerces properties
267         fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
268         fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
269         fEntityManager = (XMLEntityManager)componentManager.getProperty(ENTITY_MANAGER);
270         fSecurityManager = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER);
271 
272         //this step is extra because we have separated the storage of entity
273         fEntityStore = fEntityManager.getEntityStore() ;
274 
275         // sax features
276         fValidation = componentManager.getFeature(VALIDATION, false);
277         fNamespaces = componentManager.getFeature(NAMESPACES, true);
278         fNotifyCharRefs = componentManager.getFeature(NOTIFY_CHAR_REFS, false);
279 
280         init();
281     } // reset(XMLComponentManager)
282 
283     protected void setPropertyManager(PropertyManager propertyManager){
284         fPropertyManager = propertyManager ;
285     }
286 
287     /**
288      * Sets the value of a property during parsing.
289      *
290      * @param propertyId
291      * @param value
292      */
293     public void setProperty(String propertyId, Object value)
294     throws XMLConfigurationException {
295 
296         // Xerces properties
297         if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
298             String property =
299                     propertyId.substring(Constants.XERCES_PROPERTY_PREFIX.length());
300             if (property.equals(Constants.SYMBOL_TABLE_PROPERTY)) {
301                 fSymbolTable = (SymbolTable)value;
302             } else if (property.equals(Constants.ERROR_REPORTER_PROPERTY)) {
303                 fErrorReporter = (XMLErrorReporter)value;
304             } else if (property.equals(Constants.ENTITY_MANAGER_PROPERTY)) {
305                 fEntityManager = (XMLEntityManager)value;
306             }
307         }
308 
309         if (propertyId.equals(SECURITY_MANAGER)) {
310             fSecurityManager = (XMLSecurityManager)value;
311         }
312                 /*else if(propertyId.equals(Constants.STAX_PROPERTIES)){
313             fStaxProperties = (HashMap)value;
314             //TODO::discuss with neeraj what are his thoughts on passing properties.
315             //For now use this
316         }*/
317 
318     } // setProperty(String,Object)
319 
320     /*
321      * Sets the feature of the scanner.
322      */
323     public void setFeature(String featureId, boolean value)
324     throws XMLConfigurationException {
325 
326         if (VALIDATION.equals(featureId)) {
327             fValidation = value;
328         } else if (NOTIFY_CHAR_REFS.equals(featureId)) {
329             fNotifyCharRefs = value;
330         }
331     }
332 
333     /*
334      * Gets the state of the feature of the scanner.
335      */
336     public boolean getFeature(String featureId)
337     throws XMLConfigurationException {
338 
339         if (VALIDATION.equals(featureId)) {
340             return fValidation;
341         } else if (NOTIFY_CHAR_REFS.equals(featureId)) {
342             return fNotifyCharRefs;
343         }
344         throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId);
345     }
346 
347     //
348     // Protected methods
349     //
350 
351     // anybody calling this had better have set Symtoltable!
352     protected void reset() {
353         init();
354 
355         // DTD preparsing defaults:
356         fValidation = true;
357         fNotifyCharRefs = false;
358 
359     }
360 
361     public void reset(PropertyManager propertyManager) {
362         init();
363         // Xerces properties
364         fSymbolTable = (SymbolTable)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY);
365 
366         fErrorReporter = (XMLErrorReporter)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY);
367 
368         fEntityManager = (XMLEntityManager)propertyManager.getProperty(ENTITY_MANAGER);
369         fEntityStore = fEntityManager.getEntityStore() ;
370         fEntityScanner = (XMLEntityScanner)fEntityManager.getEntityScanner() ;
371         fSecurityManager = (XMLSecurityManager)propertyManager.getProperty(SECURITY_MANAGER);
372 
373         //fEntityManager.reset();
374         // DTD preparsing defaults:
375         fValidation = false;
376         fNotifyCharRefs = false;
377 
378     }
379     // common scanning methods
380 
381     /**
382      * Scans an XML or text declaration.
383      * <p>
384      * <pre>
385      * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
386      * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
387      * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' |  "'" EncName "'" )
388      * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
389      * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
390      *                 | ('"' ('yes' | 'no') '"'))
391      *
392      * [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
393      * </pre>
394      *
395      * @param scanningTextDecl True if a text declaration is to
396      *                         be scanned instead of an XML
397      *                         declaration.
398      * @param pseudoAttributeValues An array of size 3 to return the version,
399      *                         encoding and standalone pseudo attribute values
400      *                         (in that order).
401      *
402      * <strong>Note:</strong> This method uses fString, anything in it
403      * at the time of calling is lost.
404      */
405     protected void scanXMLDeclOrTextDecl(boolean scanningTextDecl,
406             String[] pseudoAttributeValues)
407             throws IOException, XNIException {
408 
409         // pseudo-attribute values
410         String version = null;
411         String encoding = null;
412         String standalone = null;
413 
414         // scan pseudo-attributes
415         final int STATE_VERSION = 0;
416         final int STATE_ENCODING = 1;
417         final int STATE_STANDALONE = 2;
418         final int STATE_DONE = 3;
419         int state = STATE_VERSION;
420 
421         boolean dataFoundForTarget = false;
422         boolean sawSpace = fEntityScanner.skipSpaces();
423         // since pseudoattributes are *not* attributes,
424         // their quotes don't need to be preserved in external parameter entities.
425         // the XMLEntityScanner#scanLiteral method will continue to
426         // emit -1 in such cases when it finds a quote; this is
427         // fine for other methods that parse scanned entities,
428         // but not for the scanning of pseudoattributes.  So,
429         // temporarily, we must mark the current entity as not being "literal"
430         Entity.ScannedEntity currEnt = fEntityManager.getCurrentEntity();
431         boolean currLiteral = currEnt.literal;
432         currEnt.literal = false;
433         while (fEntityScanner.peekChar() != '?') {
434             dataFoundForTarget = true;
435             String name = scanPseudoAttribute(scanningTextDecl, fString);
436             switch (state) {
437                 case STATE_VERSION: {
438                     if (name.equals(fVersionSymbol)) {
439                         if (!sawSpace) {
440                             reportFatalError(scanningTextDecl
441                                     ? "SpaceRequiredBeforeVersionInTextDecl"
442                                     : "SpaceRequiredBeforeVersionInXMLDecl",
443                                     null);
444                         }
445                         version = fString.toString();
446                         state = STATE_ENCODING;
447                         if (!versionSupported(version)) {
448                             reportFatalError("VersionNotSupported",
449                                     new Object[]{version});
450                         }
451 
452                         if (version.equals("1.1")) {
453                             Entity.ScannedEntity top = fEntityManager.getTopLevelEntity();
454                             if (top != null && (top.version == null || top.version.equals("1.0"))) {
455                                 reportFatalError("VersionMismatch", null);
456                             }
457                             fEntityManager.setScannerVersion(Constants.XML_VERSION_1_1);
458                         }
459 
460                     } else if (name.equals(fEncodingSymbol)) {
461                         if (!scanningTextDecl) {
462                             reportFatalError("VersionInfoRequired", null);
463                         }
464                         if (!sawSpace) {
465                             reportFatalError(scanningTextDecl
466                                     ? "SpaceRequiredBeforeEncodingInTextDecl"
467                                     : "SpaceRequiredBeforeEncodingInXMLDecl",
468                                     null);
469                         }
470                         encoding = fString.toString();
471                         state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
472                     } else {
473                         if (scanningTextDecl) {
474                             reportFatalError("EncodingDeclRequired", null);
475                         } else {
476                             reportFatalError("VersionInfoRequired", null);
477                         }
478                     }
479                     break;
480                 }
481                 case STATE_ENCODING: {
482                     if (name.equals(fEncodingSymbol)) {
483                         if (!sawSpace) {
484                             reportFatalError(scanningTextDecl
485                                     ? "SpaceRequiredBeforeEncodingInTextDecl"
486                                     : "SpaceRequiredBeforeEncodingInXMLDecl",
487                                     null);
488                         }
489                         encoding = fString.toString();
490                         state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
491                         // TODO: check encoding name; set encoding on
492                         //       entity scanner
493                     } else if (!scanningTextDecl && name.equals(fStandaloneSymbol)) {
494                         if (!sawSpace) {
495                             reportFatalError("SpaceRequiredBeforeStandalone",
496                                     null);
497                         }
498                         standalone = fString.toString();
499                         state = STATE_DONE;
500                         if (!standalone.equals("yes") && !standalone.equals("no")) {
501                             reportFatalError("SDDeclInvalid", new Object[] {standalone});
502                         }
503                     } else {
504                         reportFatalError("EncodingDeclRequired", null);
505                     }
506                     break;
507                 }
508                 case STATE_STANDALONE: {
509                     if (name.equals(fStandaloneSymbol)) {
510                         if (!sawSpace) {
511                             reportFatalError("SpaceRequiredBeforeStandalone",
512                                     null);
513                         }
514                         standalone = fString.toString();
515                         state = STATE_DONE;
516                         if (!standalone.equals("yes") && !standalone.equals("no")) {
517                             reportFatalError("SDDeclInvalid",  new Object[] {standalone});
518                         }
519                     } else {
520                         reportFatalError("SDDeclNameInvalid", null);
521                     }
522                     break;
523                 }
524                 default: {
525                     reportFatalError("NoMorePseudoAttributes", null);
526                 }
527             }
528             sawSpace = fEntityScanner.skipSpaces();
529         }
530         // restore original literal value
531         if(currLiteral) {
532             currEnt.literal = true;
533         }
534         // REVISIT: should we remove this error reporting?
535         if (scanningTextDecl && state != STATE_DONE) {
536             reportFatalError("MorePseudoAttributes", null);
537         }
538 
539         // If there is no data in the xml or text decl then we fail to report error
540         // for version or encoding info above.
541         if (scanningTextDecl) {
542             if (!dataFoundForTarget && encoding == null) {
543                 reportFatalError("EncodingDeclRequired", null);
544             }
545         } else {
546             if (!dataFoundForTarget && version == null) {
547                 reportFatalError("VersionInfoRequired", null);
548             }
549         }
550 
551         // end
552         if (!fEntityScanner.skipChar('?')) {
553             reportFatalError("XMLDeclUnterminated", null);
554         }
555         if (!fEntityScanner.skipChar('>')) {
556             reportFatalError("XMLDeclUnterminated", null);
557 
558         }
559 
560         // fill in return array
561         pseudoAttributeValues[0] = version;
562         pseudoAttributeValues[1] = encoding;
563         pseudoAttributeValues[2] = standalone;
564 
565     } // scanXMLDeclOrTextDecl(boolean)
566 
567     /**
568      * Scans a pseudo attribute.
569      *
570      * @param scanningTextDecl True if scanning this pseudo-attribute for a
571      *                         TextDecl; false if scanning XMLDecl. This
572      *                         flag is needed to report the correct type of
573      *                         error.
574      * @param value            The string to fill in with the attribute
575      *                         value.
576      *
577      * @return The name of the attribute
578      *
579      * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
580      * at the time of calling is lost.
581      */
582     public String scanPseudoAttribute(boolean scanningTextDecl,
583             XMLString value)
584             throws IOException, XNIException {
585 
586         String name = scanPseudoAttributeName();
587         // XMLEntityManager.print(fEntityManager.getCurrentEntity());
588 
589         if (name == null) {
590             reportFatalError("PseudoAttrNameExpected", null);
591         }
592         fEntityScanner.skipSpaces();
593         if (!fEntityScanner.skipChar('=')) {
594             reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl"
595                     : "EqRequiredInXMLDecl", new Object[]{name});
596         }
597         fEntityScanner.skipSpaces();
598         int quote = fEntityScanner.peekChar();
599         if (quote != '\'' && quote != '"') {
600             reportFatalError(scanningTextDecl ? "QuoteRequiredInTextDecl"
601                     : "QuoteRequiredInXMLDecl" , new Object[]{name});
602         }
603         fEntityScanner.scanChar();
604         int c = fEntityScanner.scanLiteral(quote, value);
605         if (c != quote) {
606             fStringBuffer2.clear();
607             do {
608                 fStringBuffer2.append(value);
609                 if (c != -1) {
610                     if (c == '&' || c == '%' || c == '<' || c == ']') {
611                         fStringBuffer2.append((char)fEntityScanner.scanChar());
612                     } else if (XMLChar.isHighSurrogate(c)) {
613                         scanSurrogates(fStringBuffer2);
614                     } else if (isInvalidLiteral(c)) {
615                         String key = scanningTextDecl
616                                 ? "InvalidCharInTextDecl" : "InvalidCharInXMLDecl";
617                         reportFatalError(key,
618                                 new Object[] {Integer.toString(c, 16)});
619                                 fEntityScanner.scanChar();
620                     }
621                 }
622                 c = fEntityScanner.scanLiteral(quote, value);
623             } while (c != quote);
624             fStringBuffer2.append(value);
625             value.setValues(fStringBuffer2);
626         }
627         if (!fEntityScanner.skipChar(quote)) {
628             reportFatalError(scanningTextDecl ? "CloseQuoteMissingInTextDecl"
629                     : "CloseQuoteMissingInXMLDecl",
630                     new Object[]{name});
631         }
632 
633         // return
634         return name;
635 
636     } // scanPseudoAttribute(XMLString):String
637 
638     /**
639      * Scans the name of a pseudo attribute. The only legal names
640      * in XML 1.0/1.1 documents are 'version', 'encoding' and 'standalone'.
641      *
642      * @return the name of the pseudo attribute or <code>null</code>
643      * if a legal pseudo attribute name could not be scanned.
644      */
645     private String scanPseudoAttributeName() throws IOException, XNIException {
646         final int ch = fEntityScanner.peekChar();
647         switch (ch) {
648             case 'v':
649                 if (fEntityScanner.skipString(fVersionSymbol)) {
650                     return fVersionSymbol;
651                 }
652                 break;
653             case 'e':
654                 if (fEntityScanner.skipString(fEncodingSymbol)) {
655                     return fEncodingSymbol;
656                 }
657                 break;
658             case 's':
659                 if (fEntityScanner.skipString(fStandaloneSymbol)) {
660                     return fStandaloneSymbol;
661                 }
662                 break;
663         }
664         return null;
665     } // scanPseudoAttributeName()
666 
667     /**
668      * Scans a processing instruction.
669      * <p>
670      * <pre>
671      * [16] PI ::= '&lt;?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
672      * [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
673      * </pre>
674      */
675     //CHANGED:
676     //EARLIER: scanPI()
677     //NOW: scanPI(XMLStringBuffer)
678     //it makes things more easy if XMLStringBUffer is passed. Motivation for this change is same
679     // as that for scanContent()
680 
681     protected void scanPI(XMLStringBuffer data) throws IOException, XNIException {
682 
683         // target
684         fReportEntity = false;
685         String target = fEntityScanner.scanName();
686         if (target == null) {
687             reportFatalError("PITargetRequired", null);
688         }
689 
690         // scan data
691         scanPIData(target, data);
692         fReportEntity = true;
693 
694     } // scanPI(XMLStringBuffer)
695 
696     /**
697      * Scans a processing data. This is needed to handle the situation
698      * where a document starts with a processing instruction whose
699      * target name <em>starts with</em> "xml". (e.g. xmlfoo)
700      *
701      * This method would always read the whole data. We have while loop and data is buffered
702      * until delimeter is encountered.
703      *
704      * @param target The PI target
705      * @param data The string to fill in with the data
706      */
707 
708     //CHANGED:
709     //Earlier:This method uses the fStringBuffer and later buffer values are set to
710     //the supplied XMLString....
711     //Now: Changed the signature of this function to pass XMLStringBuffer.. and data would
712     //be appended to that buffer
713 
714     protected void scanPIData(String target, XMLStringBuffer data)
715     throws IOException, XNIException {
716 
717         // check target
718         if (target.length() == 3) {
719             char c0 = Character.toLowerCase(target.charAt(0));
720             char c1 = Character.toLowerCase(target.charAt(1));
721             char c2 = Character.toLowerCase(target.charAt(2));
722             if (c0 == 'x' && c1 == 'm' && c2 == 'l') {
723                 reportFatalError("ReservedPITarget", null);
724             }
725         }
726 
727         // spaces
728         if (!fEntityScanner.skipSpaces()) {
729             if (fEntityScanner.skipString("?>")) {
730                 // we found the end, there is no data just return
731                 return;
732             } else {
733                 // if there is data there should be some space
734                 reportFatalError("SpaceRequiredInPI", null);
735             }
736         }
737 
738         // since scanData appends the parsed data to the buffer passed
739         // a while loop would append the whole of parsed data to the buffer(data:XMLStringBuffer)
740         //until all of the data is buffered.
741         if (fEntityScanner.scanData("?>", data)) {
742             do {
743                 int c = fEntityScanner.peekChar();
744                 if (c != -1) {
745                     if (XMLChar.isHighSurrogate(c)) {
746                         scanSurrogates(data);
747                     } else if (isInvalidLiteral(c)) {
748                         reportFatalError("InvalidCharInPI",
749                                 new Object[]{Integer.toHexString(c)});
750                                 fEntityScanner.scanChar();
751                     }
752                 }
753             } while (fEntityScanner.scanData("?>", data));
754         }
755 
756     } // scanPIData(String,XMLString)
757 
758     /**
759      * Scans a comment.
760      * <p>
761      * <pre>
762      * [15] Comment ::= '&lt!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
763      * </pre>
764      * <p>
765      * <strong>Note:</strong> Called after scanning past '&lt;!--'
766      * <strong>Note:</strong> This method uses fString, anything in it
767      * at the time of calling is lost.
768      *
769      * @param text The buffer to fill in with the text.
770      */
771     protected void scanComment(XMLStringBuffer text)
772     throws IOException, XNIException {
773 
774         //System.out.println( "XMLScanner#scanComment# In Scan Comment" );
775         // text
776         // REVISIT: handle invalid character, eof
777         text.clear();
778         while (fEntityScanner.scanData("--", text)) {
779             int c = fEntityScanner.peekChar();
780 
781             //System.out.println( "XMLScanner#scanComment#text.toString() == " + text.toString() );
782             //System.out.println( "XMLScanner#scanComment#c == " + c );
783 
784             if (c != -1) {
785                 if (XMLChar.isHighSurrogate(c)) {
786                     scanSurrogates(text);
787                 }
788                 if (isInvalidLiteral(c)) {
789                     reportFatalError("InvalidCharInComment",
790                             new Object[] { Integer.toHexString(c) });
791                             fEntityScanner.scanChar();
792                 }
793             }
794         }
795         if (!fEntityScanner.skipChar('>')) {
796             reportFatalError("DashDashInComment", null);
797         }
798 
799     } // scanComment()
800 
801     /**
802      * Scans an attribute value and normalizes whitespace converting all
803      * whitespace characters to space characters.
804      *
805      * [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
806      *
807      * @param value The XMLString to fill in with the value.
808      * @param nonNormalizedValue The XMLString to fill in with the
809      *                           non-normalized value.
810      * @param atName The name of the attribute being parsed (for error msgs).
811      * @param attributes The attributes list for the scanned attribute.
812      * @param attrIndex The index of the attribute to use from the list.
813      * @param checkEntities true if undeclared entities should be reported as VC violation,
814      *                      false if undeclared entities should be reported as WFC violation.
815      *
816      * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
817      * at the time of calling is lost.
818      **/
819     protected void scanAttributeValue(XMLString value,
820             XMLString nonNormalizedValue,
821             String atName,
822             XMLAttributes attributes, int attrIndex,
823             boolean checkEntities)
824             throws IOException, XNIException {
825         XMLStringBuffer stringBuffer = null;
826         // quote
827         int quote = fEntityScanner.peekChar();
828         if (quote != '\'' && quote != '"') {
829             reportFatalError("OpenQuoteExpected", new Object[]{atName});
830         }
831 
832         fEntityScanner.scanChar();
833         int entityDepth = fEntityDepth;
834 
835         int c = fEntityScanner.scanLiteral(quote, value);
836         if (DEBUG_ATTR_NORMALIZATION) {
837             System.out.println("** scanLiteral -> \""
838                     + value.toString() + "\"");
839         }
840         if(fNeedNonNormalizedValue){
841             fStringBuffer2.clear();
842             fStringBuffer2.append(value);
843         }
844         if(fEntityScanner.whiteSpaceLen > 0)
845             normalizeWhitespace(value);
846         if (DEBUG_ATTR_NORMALIZATION) {
847             System.out.println("** normalizeWhitespace -> \""
848                     + value.toString() + "\"");
849         }
850         if (c != quote) {
851             fScanningAttribute = true;
852             stringBuffer = getStringBuffer();
853             stringBuffer.clear();
854             do {
855                 stringBuffer.append(value);
856                 if (DEBUG_ATTR_NORMALIZATION) {
857                     System.out.println("** value2: \""
858                             + stringBuffer.toString() + "\"");
859                 }
860                 if (c == '&') {
861                     fEntityScanner.skipChar('&');
862                     if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
863                         fStringBuffer2.append('&');
864                     }
865                     if (fEntityScanner.skipChar('#')) {
866                         if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
867                             fStringBuffer2.append('#');
868                         }
869                         int ch ;
870                         if (fNeedNonNormalizedValue)
871                             ch = scanCharReferenceValue(stringBuffer, fStringBuffer2);
872                         else
873                             ch = scanCharReferenceValue(stringBuffer, null);
874 
875                         if (ch != -1) {
876                             if (DEBUG_ATTR_NORMALIZATION) {
877                                 System.out.println("** value3: \""
878                                         + stringBuffer.toString()
879                                         + "\"");
880                             }
881                         }
882                     } else {
883                         String entityName = fEntityScanner.scanName();
884                         if (entityName == null) {
885                             reportFatalError("NameRequiredInReference", null);
886                         } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
887                             fStringBuffer2.append(entityName);
888                         }
889                         if (!fEntityScanner.skipChar(';')) {
890                             reportFatalError("SemicolonRequiredInReference",
891                                     new Object []{entityName});
892                         } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
893                             fStringBuffer2.append(';');
894                         }
895                         if (entityName == fAmpSymbol) {
896                             stringBuffer.append('&');
897                             if (DEBUG_ATTR_NORMALIZATION) {
898                                 System.out.println("** value5: \""
899                                         + stringBuffer.toString()
900                                         + "\"");
901                             }
902                         } else if (entityName == fAposSymbol) {
903                             stringBuffer.append('\'');
904                             if (DEBUG_ATTR_NORMALIZATION) {
905                                 System.out.println("** value7: \""
906                                         + stringBuffer.toString()
907                                         + "\"");
908                             }
909                         } else if (entityName == fLtSymbol) {
910                             stringBuffer.append('<');
911                             if (DEBUG_ATTR_NORMALIZATION) {
912                                 System.out.println("** value9: \""
913                                         + stringBuffer.toString()
914                                         + "\"");
915                             }
916                         } else if (entityName == fGtSymbol) {
917                             stringBuffer.append('>');
918                             if (DEBUG_ATTR_NORMALIZATION) {
919                                 System.out.println("** valueB: \""
920                                         + stringBuffer.toString()
921                                         + "\"");
922                             }
923                         } else if (entityName == fQuotSymbol) {
924                             stringBuffer.append('"');
925                             if (DEBUG_ATTR_NORMALIZATION) {
926                                 System.out.println("** valueD: \""
927                                         + stringBuffer.toString()
928                                         + "\"");
929                             }
930                         } else {
931                             if (fEntityStore.isExternalEntity(entityName)) {
932                                 reportFatalError("ReferenceToExternalEntity",
933                                         new Object[] { entityName });
934                             } else {
935                                 if (!fEntityStore.isDeclaredEntity(entityName)) {
936                                     //WFC & VC: Entity Declared
937                                     if (checkEntities) {
938                                         if (fValidation) {
939                                             fErrorReporter.reportError(fEntityScanner,XMLMessageFormatter.XML_DOMAIN,
940                                                     "EntityNotDeclared",
941                                                     new Object[]{entityName},
942                                                     XMLErrorReporter.SEVERITY_ERROR);
943                                         }
944                                     } else {
945                                         reportFatalError("EntityNotDeclared",
946                                                 new Object[]{entityName});
947                                     }
948                                 }
949                                 fEntityManager.startEntity(entityName, true);
950                             }
951                         }
952                     }
953                 } else if (c == '<') {
954                     reportFatalError("LessthanInAttValue",
955                             new Object[] { null, atName });
956                             fEntityScanner.scanChar();
957                             if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
958                                 fStringBuffer2.append((char)c);
959                             }
960                 } else if (c == '%' || c == ']') {
961                     fEntityScanner.scanChar();
962                     stringBuffer.append((char)c);
963                     if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
964                         fStringBuffer2.append((char)c);
965                     }
966                     if (DEBUG_ATTR_NORMALIZATION) {
967                         System.out.println("** valueF: \""
968                                 + stringBuffer.toString() + "\"");
969                     }
970                 } else if (c == '\n' || c == '\r') {
971                     fEntityScanner.scanChar();
972                     stringBuffer.append(' ');
973                     if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
974                         fStringBuffer2.append('\n');
975                     }
976                 } else if (c != -1 && XMLChar.isHighSurrogate(c)) {
977                     if (scanSurrogates(fStringBuffer3)) {
978                         stringBuffer.append(fStringBuffer3);
979                         if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
980                             fStringBuffer2.append(fStringBuffer3);
981                         }
982                         if (DEBUG_ATTR_NORMALIZATION) {
983                             System.out.println("** valueI: \""
984                                     + stringBuffer.toString()
985                                     + "\"");
986                         }
987                     }
988                 } else if (c != -1 && isInvalidLiteral(c)) {
989                     reportFatalError("InvalidCharInAttValue",
990                             new Object[] {Integer.toString(c, 16)});
991                             fEntityScanner.scanChar();
992                             if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
993                                 fStringBuffer2.append((char)c);
994                             }
995                 }
996                 c = fEntityScanner.scanLiteral(quote, value);
997                 if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
998                     fStringBuffer2.append(value);
999                 }
1000                 if(fEntityScanner.whiteSpaceLen > 0)
1001                     normalizeWhitespace(value);
1002                 //Todo ::Move this check  to Attributes , do conversion
1003                 //only if attribute is being accessed. -Venu
1004             } while (c != quote || entityDepth != fEntityDepth);
1005             stringBuffer.append(value);
1006             if (DEBUG_ATTR_NORMALIZATION) {
1007                 System.out.println("** valueN: \""
1008                         + stringBuffer.toString() + "\"");
1009             }
1010             value.setValues(stringBuffer);
1011             fScanningAttribute = false;
1012         }
1013         if(fNeedNonNormalizedValue)
1014             nonNormalizedValue.setValues(fStringBuffer2);
1015 
1016         // quote
1017         int cquote = fEntityScanner.scanChar();
1018         if (cquote != quote) {
1019             reportFatalError("CloseQuoteExpected", new Object[]{atName});
1020         }
1021     } // scanAttributeValue()
1022 
1023 
1024     /**
1025      * Scans External ID and return the public and system IDs.
1026      *
1027      * @param identifiers An array of size 2 to return the system id,
1028      *                    and public id (in that order).
1029      * @param optionalSystemId Specifies whether the system id is optional.
1030      *
1031      * <strong>Note:</strong> This method uses fString and fStringBuffer,
1032      * anything in them at the time of calling is lost.
1033      */
1034     protected void scanExternalID(String[] identifiers,
1035             boolean optionalSystemId)
1036             throws IOException, XNIException {
1037 
1038         String systemId = null;
1039         String publicId = null;
1040         if (fEntityScanner.skipString("PUBLIC")) {
1041             if (!fEntityScanner.skipSpaces()) {
1042                 reportFatalError("SpaceRequiredAfterPUBLIC", null);
1043             }
1044             scanPubidLiteral(fString);
1045             publicId = fString.toString();
1046 
1047             if (!fEntityScanner.skipSpaces() && !optionalSystemId) {
1048                 reportFatalError("SpaceRequiredBetweenPublicAndSystem", null);
1049             }
1050         }
1051 
1052         if (publicId != null || fEntityScanner.skipString("SYSTEM")) {
1053             if (publicId == null && !fEntityScanner.skipSpaces()) {
1054                 reportFatalError("SpaceRequiredAfterSYSTEM", null);
1055             }
1056             int quote = fEntityScanner.peekChar();
1057             if (quote != '\'' && quote != '"') {
1058                 if (publicId != null && optionalSystemId) {
1059                     // looks like we don't have any system id
1060                     // simply return the public id
1061                     identifiers[0] = null;
1062                     identifiers[1] = publicId;
1063                     return;
1064                 }
1065                 reportFatalError("QuoteRequiredInSystemID", null);
1066             }
1067             fEntityScanner.scanChar();
1068             XMLString ident = fString;
1069             if (fEntityScanner.scanLiteral(quote, ident) != quote) {
1070                 fStringBuffer.clear();
1071                 do {
1072                     fStringBuffer.append(ident);
1073                     int c = fEntityScanner.peekChar();
1074                     if (XMLChar.isMarkup(c) || c == ']') {
1075                         fStringBuffer.append((char)fEntityScanner.scanChar());
1076                     } else if (c != -1 && isInvalidLiteral(c)) {
1077                         reportFatalError("InvalidCharInSystemID",
1078                             new Object[] {Integer.toString(c, 16)});
1079                     }
1080                 } while (fEntityScanner.scanLiteral(quote, ident) != quote);
1081                 fStringBuffer.append(ident);
1082                 ident = fStringBuffer;
1083             }
1084             systemId = ident.toString();
1085             if (!fEntityScanner.skipChar(quote)) {
1086                 reportFatalError("SystemIDUnterminated", null);
1087             }
1088         }
1089 
1090         // store result in array
1091         identifiers[0] = systemId;
1092         identifiers[1] = publicId;
1093     }
1094 
1095 
1096     /**
1097      * Scans public ID literal.
1098      *
1099      * [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
1100      * [13] PubidChar::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
1101      *
1102      * The returned string is normalized according to the following rule,
1103      * from http://www.w3.org/TR/REC-xml#dt-pubid:
1104      *
1105      * Before a match is attempted, all strings of white space in the public
1106      * identifier must be normalized to single space characters (#x20), and
1107      * leading and trailing white space must be removed.
1108      *
1109      * @param literal The string to fill in with the public ID literal.
1110      * @return True on success.
1111      *
1112      * <strong>Note:</strong> This method uses fStringBuffer, anything in it at
1113      * the time of calling is lost.
1114      */
1115     protected boolean scanPubidLiteral(XMLString literal)
1116     throws IOException, XNIException {
1117         int quote = fEntityScanner.scanChar();
1118         if (quote != '\'' && quote != '"') {
1119             reportFatalError("QuoteRequiredInPublicID", null);
1120             return false;
1121         }
1122 
1123         fStringBuffer.clear();
1124         // skip leading whitespace
1125         boolean skipSpace = true;
1126         boolean dataok = true;
1127         while (true) {
1128             int c = fEntityScanner.scanChar();
1129             if (c == ' ' || c == '\n' || c == '\r') {
1130                 if (!skipSpace) {
1131                     // take the first whitespace as a space and skip the others
1132                     fStringBuffer.append(' ');
1133                     skipSpace = true;
1134                 }
1135             } else if (c == quote) {
1136                 if (skipSpace) {
1137                     // if we finished on a space let's trim it
1138                     fStringBuffer.length--;
1139                 }
1140                 literal.setValues(fStringBuffer);
1141                 break;
1142             } else if (XMLChar.isPubid(c)) {
1143                 fStringBuffer.append((char)c);
1144                 skipSpace = false;
1145             } else if (c == -1) {
1146                 reportFatalError("PublicIDUnterminated", null);
1147                 return false;
1148             } else {
1149                 dataok = false;
1150                 reportFatalError("InvalidCharInPublicID",
1151                         new Object[]{Integer.toHexString(c)});
1152             }
1153         }
1154         return dataok;
1155     }
1156 
1157 
1158     /**
1159      * Normalize whitespace in an XMLString converting all whitespace
1160      * characters to space characters.
1161      */
1162     protected void normalizeWhitespace(XMLString value) {
1163         int i=0;
1164         int j=0;
1165         int [] buff = fEntityScanner.whiteSpaceLookup;
1166         int buffLen = fEntityScanner.whiteSpaceLen;
1167         int end = value.offset + value.length;
1168         while(i < buffLen){
1169             j = buff[i];
1170             if(j < end ){
1171                 value.ch[j] = ' ';
1172             }
1173             i++;
1174         }
1175     }
1176 
1177     //
1178     // XMLEntityHandler methods
1179     //
1180 
1181     /**
1182      * This method notifies of the start of an entity. The document entity
1183      * has the pseudo-name of "[xml]" the DTD has the pseudo-name of "[dtd]"
1184      * parameter entity names start with '%'; and general entities are just
1185      * specified by their name.
1186      *
1187      * @param name     The name of the entity.
1188      * @param identifier The resource identifier.
1189      * @param encoding The auto-detected IANA encoding name of the entity
1190      *                 stream. This value will be null in those situations
1191      *                 where the entity encoding is not auto-detected (e.g.
1192      *                 internal entities or a document entity that is
1193      *                 parsed from a java.io.Reader).
1194      *
1195      * @throws XNIException Thrown by handler to signal an error.
1196      */
1197     public void startEntity(String name,
1198             XMLResourceIdentifier identifier,
1199             String encoding, Augmentations augs) throws XNIException {
1200 
1201         // keep track of the entity depth
1202         fEntityDepth++;
1203         // must reset entity scanner
1204         fEntityScanner = fEntityManager.getEntityScanner();
1205         fEntityStore = fEntityManager.getEntityStore() ;
1206     } // startEntity(String,XMLResourceIdentifier,String)
1207 
1208     /**
1209      * This method notifies the end of an entity. The document entity has
1210      * the pseudo-name of "[xml]" the DTD has the pseudo-name of "[dtd]"
1211      * parameter entity names start with '%'; and general entities are just
1212      * specified by their name.
1213      *
1214      * @param name The name of the entity.
1215      *
1216      * @throws XNIException Thrown by handler to signal an error.
1217      */
1218     public void endEntity(String name, Augmentations augs) throws IOException, XNIException {
1219 
1220         // keep track of the entity depth
1221         fEntityDepth--;
1222 
1223     } // endEntity(String)
1224 
1225     /**
1226      * Scans a character reference and append the corresponding chars to the
1227      * specified buffer.
1228      *
1229      * <p>
1230      * <pre>
1231      * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
1232      * </pre>
1233      *
1234      * <strong>Note:</strong> This method uses fStringBuffer, anything in it
1235      * at the time of calling is lost.
1236      *
1237      * @param buf the character buffer to append chars to
1238      * @param buf2 the character buffer to append non-normalized chars to
1239      *
1240      * @return the character value or (-1) on conversion failure
1241      */
1242     protected int scanCharReferenceValue(XMLStringBuffer buf, XMLStringBuffer buf2)
1243     throws IOException, XNIException {
1244         // scan hexadecimal value
1245         boolean hex = false;
1246         if (fEntityScanner.skipChar('x')) {
1247             if (buf2 != null) { buf2.append('x'); }
1248             hex = true;
1249             fStringBuffer3.clear();
1250             boolean digit = true;
1251 
1252             int c = fEntityScanner.peekChar();
1253             digit = (c >= '0' && c <= '9') ||
1254                     (c >= 'a' && c <= 'f') ||
1255                     (c >= 'A' && c <= 'F');
1256             if (digit) {
1257                 if (buf2 != null) { buf2.append((char)c); }
1258                 fEntityScanner.scanChar();
1259                 fStringBuffer3.append((char)c);
1260 
1261                 do {
1262                     c = fEntityScanner.peekChar();
1263                     digit = (c >= '0' && c <= '9') ||
1264                             (c >= 'a' && c <= 'f') ||
1265                             (c >= 'A' && c <= 'F');
1266                     if (digit) {
1267                         if (buf2 != null) { buf2.append((char)c); }
1268                         fEntityScanner.scanChar();
1269                         fStringBuffer3.append((char)c);
1270                     }
1271                 } while (digit);
1272             } else {
1273                 reportFatalError("HexdigitRequiredInCharRef", null);
1274             }
1275         }
1276 
1277         // scan decimal value
1278         else {
1279             fStringBuffer3.clear();
1280             boolean digit = true;
1281 
1282             int c = fEntityScanner.peekChar();
1283             digit = c >= '0' && c <= '9';
1284             if (digit) {
1285                 if (buf2 != null) { buf2.append((char)c); }
1286                 fEntityScanner.scanChar();
1287                 fStringBuffer3.append((char)c);
1288 
1289                 do {
1290                     c = fEntityScanner.peekChar();
1291                     digit = c >= '0' && c <= '9';
1292                     if (digit) {
1293                         if (buf2 != null) { buf2.append((char)c); }
1294                         fEntityScanner.scanChar();
1295                         fStringBuffer3.append((char)c);
1296                     }
1297                 } while (digit);
1298             } else {
1299                 reportFatalError("DigitRequiredInCharRef", null);
1300             }
1301         }
1302 
1303         // end
1304         if (!fEntityScanner.skipChar(';')) {
1305             reportFatalError("SemicolonRequiredInCharRef", null);
1306         }
1307         if (buf2 != null) { buf2.append(';'); }
1308 
1309         // convert string to number
1310         int value = -1;
1311         try {
1312             value = Integer.parseInt(fStringBuffer3.toString(),
1313                     hex ? 16 : 10);
1314 
1315             // character reference must be a valid XML character
1316             if (isInvalid(value)) {
1317                 StringBuffer errorBuf = new StringBuffer(fStringBuffer3.length + 1);
1318                 if (hex) errorBuf.append('x');
1319                 errorBuf.append(fStringBuffer3.ch, fStringBuffer3.offset, fStringBuffer3.length);
1320                 reportFatalError("InvalidCharRef",
1321                         new Object[]{errorBuf.toString()});
1322             }
1323         } catch (NumberFormatException e) {
1324             // Conversion failed, let -1 value drop through.
1325             // If we end up here, the character reference was invalid.
1326             StringBuffer errorBuf = new StringBuffer(fStringBuffer3.length + 1);
1327             if (hex) errorBuf.append('x');
1328             errorBuf.append(fStringBuffer3.ch, fStringBuffer3.offset, fStringBuffer3.length);
1329             reportFatalError("InvalidCharRef",
1330                     new Object[]{errorBuf.toString()});
1331         }
1332 
1333         // append corresponding chars to the given buffer
1334         if (!XMLChar.isSupplemental(value)) {
1335             buf.append((char) value);
1336         } else {
1337             // character is supplemental, split it into surrogate chars
1338             buf.append(XMLChar.highSurrogate(value));
1339             buf.append(XMLChar.lowSurrogate(value));
1340         }
1341 
1342         // char refs notification code
1343         if (fNotifyCharRefs && value != -1) {
1344             String literal = "#" + (hex ? "x" : "") + fStringBuffer3.toString();
1345             if (!fScanningAttribute) {
1346                 fCharRefLiteral = literal;
1347             }
1348         }
1349 
1350         return value;
1351     }
1352     // returns true if the given character is not
1353     // valid with respect to the version of
1354     // XML understood by this scanner.
1355     protected boolean isInvalid(int value) {
1356         return (XMLChar.isInvalid(value));
1357     } // isInvalid(int):  boolean
1358 
1359     // returns true if the given character is not
1360     // valid or may not be used outside a character reference
1361     // with respect to the version of XML understood by this scanner.
1362     protected boolean isInvalidLiteral(int value) {
1363         return (XMLChar.isInvalid(value));
1364     } // isInvalidLiteral(int):  boolean
1365 
1366     // returns true if the given character is
1367     // a valid nameChar with respect to the version of
1368     // XML understood by this scanner.
1369     protected boolean isValidNameChar(int value) {
1370         return (XMLChar.isName(value));
1371     } // isValidNameChar(int):  boolean
1372 
1373     // returns true if the given character is
1374     // a valid NCName character with respect to the version of
1375     // XML understood by this scanner.
1376     protected boolean isValidNCName(int value) {
1377         return (XMLChar.isNCName(value));
1378     } // isValidNCName(int):  boolean
1379 
1380     // returns true if the given character is
1381     // a valid nameStartChar with respect to the version of
1382     // XML understood by this scanner.
1383     protected boolean isValidNameStartChar(int value) {
1384         return (XMLChar.isNameStart(value));
1385     } // isValidNameStartChar(int):  boolean
1386 
1387     protected boolean versionSupported(String version ) {
1388         return version.equals("1.0") || version.equals("1.1");
1389     } // version Supported
1390 
1391     /**
1392      * Scans surrogates and append them to the specified buffer.
1393      * <p>
1394      * <strong>Note:</strong> This assumes the current char has already been
1395      * identified as a high surrogate.
1396      *
1397      * @param buf The StringBuffer to append the read surrogates to.
1398      * @return True if it succeeded.
1399      */
1400     protected boolean scanSurrogates(XMLStringBuffer buf)
1401     throws IOException, XNIException {
1402 
1403         int high = fEntityScanner.scanChar();
1404         int low = fEntityScanner.peekChar();
1405         if (!XMLChar.isLowSurrogate(low)) {
1406             reportFatalError("InvalidCharInContent",
1407                     new Object[] {Integer.toString(high, 16)});
1408                     return false;
1409         }
1410         fEntityScanner.scanChar();
1411 
1412         // convert surrogates to supplemental character
1413         int c = XMLChar.supplemental((char)high, (char)low);
1414 
1415         // supplemental character must be a valid XML character
1416         if (isInvalid(c)) {
1417             reportFatalError("InvalidCharInContent",
1418                     new Object[]{Integer.toString(c, 16)});
1419                     return false;
1420         }
1421 
1422         // fill in the buffer
1423         buf.append((char)high);
1424         buf.append((char)low);
1425 
1426         return true;
1427 
1428     } // scanSurrogates():boolean
1429 
1430 
1431     /**
1432      * Convenience function used in all XML scanners.
1433      */
1434     protected void reportFatalError(String msgId, Object[] args)
1435     throws XNIException {
1436         fErrorReporter.reportError(fEntityScanner, XMLMessageFormatter.XML_DOMAIN,
1437                 msgId, args,
1438                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
1439     }
1440 
1441     // private methods
1442     private void init() {
1443         // initialize scanner
1444         fEntityScanner = null;
1445         // initialize vars
1446         fEntityDepth = 0;
1447         fReportEntity = true;
1448         fResourceIdentifier.clear();
1449 
1450         if(!fAttributeCacheInitDone){
1451             for(int i = 0; i < initialCacheCount; i++){
1452                 attributeValueCache.add(new XMLString());
1453                 stringBufferCache.add(new XMLStringBuffer());
1454             }
1455             fAttributeCacheInitDone = true;
1456         }
1457         fStringBufferIndex = 0;
1458         fAttributeCacheUsedCount = 0;
1459 
1460     }
1461 
1462     XMLStringBuffer getStringBuffer(){
1463         if((fStringBufferIndex < initialCacheCount )|| (fStringBufferIndex < stringBufferCache.size())){
1464             return (XMLStringBuffer)stringBufferCache.get(fStringBufferIndex++);
1465         }else{
1466             XMLStringBuffer tmpObj = new XMLStringBuffer();
1467             fStringBufferIndex++;
1468             stringBufferCache.add(tmpObj);
1469             return tmpObj;
1470         }
1471     }
1472 
1473 
1474 } // class XMLScanner