1 /*
   2  * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 
   5 /*
   6  * Copyright 2005 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl;
  22 
  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 javax.xml.stream.events.XMLEvent;
  29 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  30 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  31 import com.sun.org.apache.xerces.internal.util.XMLChar;
  32 import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;
  33 import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
  34 import com.sun.org.apache.xerces.internal.utils.XMLLimitAnalyzer;
  35 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
  36 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  37 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  38 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  39 import com.sun.org.apache.xerces.internal.xni.XMLString;
  40 import com.sun.org.apache.xerces.internal.xni.XNIException;
  41 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  42 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  43 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  44 import com.sun.xml.internal.stream.Entity;
  45 
  46 //import com.sun.xml.stream.XMLEntityManager;
  47 //import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  48 
  49 /**
  50  * This class is responsible for holding scanning methods common to
  51  * scanning the XML document structure and content as well as the DTD
  52  * structure and content. Both XMLDocumentScanner and XMLDTDScanner inherit
  53  * from this base class.
  54  *
  55  * <p>
  56  * This component requires the following features and properties from the
  57  * component manager that uses it:
  58  * <ul>
  59  *  <li>http://xml.org/sax/features/validation</li>
  60  *  <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
  61  *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
  62  *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
  63  *  <li>http://apache.org/xml/properties/internal/entity-manager</li>
  64  * </ul>
  65  *
  66  * @author Andy Clark, IBM
  67  * @author Arnaud  Le Hors, IBM
  68  * @author Eric Ye, IBM
  69  * @author K.Venugopal SUN Microsystems
  70  * @author Sunitha Reddy, SUN Microsystems
  71  */
  72 public abstract class XMLScanner
  73         implements XMLComponent {
  74 
  75     //
  76     // Constants
  77     //
  78 
  79     // feature identifiers
  80 
  81     /** Feature identifier: namespaces. */
  82     protected static final String NAMESPACES =
  83             Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
  84 
  85     /** Feature identifier: validation. */
  86     protected static final String VALIDATION =
  87             Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
  88 
  89     /** Feature identifier: notify character references. */
  90     protected static final String NOTIFY_CHAR_REFS =
  91             Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_CHAR_REFS_FEATURE;
  92 
  93     // property identifiers
  94 
  95     protected static final String PARSER_SETTINGS =
  96                                 Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
  97     /** Property identifier: symbol table. */
  98     protected static final String SYMBOL_TABLE =
  99             Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
 100 
 101     /** Property identifier: error reporter. */
 102     protected static final String ERROR_REPORTER =
 103             Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
 104 
 105     /** Property identifier: entity manager. */
 106     protected static final String ENTITY_MANAGER =
 107             Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY;
 108 
 109     /** Property identifier: Security manager. */
 110     private static final String SECURITY_MANAGER = Constants.SECURITY_MANAGER;
 111 
 112     // debugging
 113 
 114     /** Debug attribute normalization. */
 115     protected static final boolean DEBUG_ATTR_NORMALIZATION = false;
 116 
 117 
 118     //xxx: setting the default value as false, as we dont need to calculate this value
 119     //we should have a feature when set to true computes this value
 120     private boolean fNeedNonNormalizedValue = false;
 121 
 122     protected ArrayList<XMLString> attributeValueCache = new ArrayList<>();
 123     protected ArrayList<XMLStringBuffer> stringBufferCache = new ArrayList<>();
 124     protected int fStringBufferIndex = 0;
 125     protected boolean fAttributeCacheInitDone = false;
 126     protected int fAttributeCacheUsedCount = 0;
 127 
 128     //
 129     // Data
 130     //
 131 
 132     // features
 133 
 134     /**
 135      * Validation. This feature identifier is:
 136      * http://xml.org/sax/features/validation
 137      */
 138     protected boolean fValidation = false;
 139 
 140     /** Namespaces. */
 141     protected boolean fNamespaces;
 142 
 143     /** Character references notification. */
 144     protected boolean fNotifyCharRefs = false;
 145 
 146     /** Internal parser-settings feature */
 147         protected boolean fParserSettings = true;
 148 
 149     // properties
 150 
 151     protected PropertyManager fPropertyManager = null ;
 152     /** Symbol table. */
 153     protected SymbolTable fSymbolTable;
 154 
 155     /** Error reporter. */
 156     protected XMLErrorReporter fErrorReporter;
 157 
 158     /** Entity manager. */
 159     //protected XMLEntityManager fEntityManager = PropertyManager.getEntityManager();
 160     protected XMLEntityManager fEntityManager = null ;
 161 
 162     /** xxx this should be available from EntityManager Entity storage */
 163     protected XMLEntityStorage fEntityStore = null ;
 164 
 165     /** Security manager. */
 166     protected XMLSecurityManager fSecurityManager = null;
 167 
 168     /** Limit analyzer. */
 169     protected XMLLimitAnalyzer fLimitAnalyzer = null;
 170 
 171     // protected data
 172 
 173     /** event type */
 174     protected XMLEvent fEvent ;
 175 
 176     /** Entity scanner, this alwasy works on last entity that was opened. */
 177     protected XMLEntityScanner fEntityScanner = null;
 178 
 179     /** Entity depth. */
 180     protected int fEntityDepth;
 181 
 182     /** Literal value of the last character refence scanned. */
 183     protected String fCharRefLiteral = null;
 184 
 185     /** Scanning attribute. */
 186     protected boolean fScanningAttribute;
 187 
 188     /** Report entity boundary. */
 189     protected boolean fReportEntity;
 190 
 191     // symbols
 192 
 193     /** Symbol: "version". */
 194     protected final static String fVersionSymbol = "version".intern();
 195 
 196     /** Symbol: "encoding". */
 197     protected final static String fEncodingSymbol = "encoding".intern();
 198 
 199     /** Symbol: "standalone". */
 200     protected final static String fStandaloneSymbol = "standalone".intern();
 201 
 202     /** Symbol: "amp". */
 203     protected final static String fAmpSymbol = "amp".intern();
 204 
 205     /** Symbol: "lt". */
 206     protected final static String fLtSymbol = "lt".intern();
 207 
 208     /** Symbol: "gt". */
 209     protected final static String fGtSymbol = "gt".intern();
 210 
 211     /** Symbol: "quot". */
 212     protected final static String fQuotSymbol = "quot".intern();
 213 
 214     /** Symbol: "apos". */
 215     protected final static String fAposSymbol = "apos".intern();
 216 
 217     // temporary variables
 218 
 219     // NOTE: These objects are private to help prevent accidental modification
 220     //       of values by a subclass. If there were protected *and* the sub-
 221     //       modified the values, it would be difficult to track down the real
 222     //       cause of the bug. By making these private, we avoid this
 223     //       possibility.
 224 
 225     /** String. */
 226     private XMLString fString = new XMLString();
 227 
 228     /** String buffer. */
 229     private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
 230 
 231     /** String buffer. */
 232     private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
 233 
 234     /** String buffer. */
 235     private XMLStringBuffer fStringBuffer3 = new XMLStringBuffer();
 236 
 237     // temporary location for Resource identification information.
 238     protected XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl();
 239     int initialCacheCount = 6;
 240     //
 241     // XMLComponent methods
 242     //
 243 
 244     /**
 245      *
 246      *
 247      * @param componentManager The component manager.
 248      *
 249      * @throws SAXException Throws exception if required features and
 250      *                      properties cannot be found.
 251      */
 252     public void reset(XMLComponentManager componentManager)
 253     throws XMLConfigurationException {
 254 
 255                 fParserSettings = componentManager.getFeature(PARSER_SETTINGS, true);
 256 
 257                 if (!fParserSettings) {
 258                         // parser settings have not been changed
 259                         init();
 260                         return;
 261                 }
 262 
 263 
 264         // Xerces properties
 265         fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
 266         fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
 267         fEntityManager = (XMLEntityManager)componentManager.getProperty(ENTITY_MANAGER);
 268         fSecurityManager = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER);
 269 
 270         //this step is extra because we have separated the storage of entity
 271         fEntityStore = fEntityManager.getEntityStore() ;
 272 
 273         // sax features
 274         fValidation = componentManager.getFeature(VALIDATION, false);
 275         fNamespaces = componentManager.getFeature(NAMESPACES, true);
 276         fNotifyCharRefs = componentManager.getFeature(NOTIFY_CHAR_REFS, false);
 277 
 278         init();
 279     } // reset(XMLComponentManager)
 280 
 281     protected void setPropertyManager(PropertyManager propertyManager){
 282         fPropertyManager = propertyManager ;
 283     }
 284 
 285     /**
 286      * Sets the value of a property during parsing.
 287      *
 288      * @param propertyId
 289      * @param value
 290      */
 291     public void setProperty(String propertyId, Object value)
 292     throws XMLConfigurationException {
 293 
 294         // Xerces properties
 295         if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
 296             String property =
 297                     propertyId.substring(Constants.XERCES_PROPERTY_PREFIX.length());
 298             if (property.equals(Constants.SYMBOL_TABLE_PROPERTY)) {
 299                 fSymbolTable = (SymbolTable)value;
 300             } else if (property.equals(Constants.ERROR_REPORTER_PROPERTY)) {
 301                 fErrorReporter = (XMLErrorReporter)value;
 302             } else if (property.equals(Constants.ENTITY_MANAGER_PROPERTY)) {
 303                 fEntityManager = (XMLEntityManager)value;
 304             }
 305         }
 306 
 307         if (propertyId.equals(SECURITY_MANAGER)) {
 308             fSecurityManager = (XMLSecurityManager)value;
 309         }
 310                 /*else if(propertyId.equals(Constants.STAX_PROPERTIES)){
 311             fStaxProperties = (HashMap)value;
 312             //TODO::discuss with neeraj what are his thoughts on passing properties.
 313             //For now use this
 314         }*/
 315 
 316     } // setProperty(String,Object)
 317 
 318     /*
 319      * Sets the feature of the scanner.
 320      */
 321     public void setFeature(String featureId, boolean value)
 322     throws XMLConfigurationException {
 323 
 324         if (VALIDATION.equals(featureId)) {
 325             fValidation = value;
 326         } else if (NOTIFY_CHAR_REFS.equals(featureId)) {
 327             fNotifyCharRefs = value;
 328         }
 329     }
 330 
 331     /*
 332      * Gets the state of the feature of the scanner.
 333      */
 334     public boolean getFeature(String featureId)
 335     throws XMLConfigurationException {
 336 
 337         if (VALIDATION.equals(featureId)) {
 338             return fValidation;
 339         } else if (NOTIFY_CHAR_REFS.equals(featureId)) {
 340             return fNotifyCharRefs;
 341         }
 342         throw new XMLConfigurationException(Status.NOT_RECOGNIZED, featureId);
 343     }
 344 
 345     //
 346     // Protected methods
 347     //
 348 
 349     // anybody calling this had better have set Symtoltable!
 350     protected void reset() {
 351         init();
 352 
 353         // DTD preparsing defaults:
 354         fValidation = true;
 355         fNotifyCharRefs = false;
 356 
 357     }
 358 
 359     public void reset(PropertyManager propertyManager) {
 360         init();
 361         // Xerces properties
 362         fSymbolTable = (SymbolTable)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY);
 363 
 364         fErrorReporter = (XMLErrorReporter)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY);
 365 
 366         fEntityManager = (XMLEntityManager)propertyManager.getProperty(ENTITY_MANAGER);
 367         fEntityStore = fEntityManager.getEntityStore() ;
 368         fEntityScanner = (XMLEntityScanner)fEntityManager.getEntityScanner() ;
 369         fSecurityManager = (XMLSecurityManager)propertyManager.getProperty(SECURITY_MANAGER);
 370 
 371         //fEntityManager.reset();
 372         // DTD preparsing defaults:
 373         fValidation = false;
 374         fNotifyCharRefs = false;
 375 
 376     }
 377     // common scanning methods
 378 
 379     /**
 380      * Scans an XML or text declaration.
 381      * <p>
 382      * <pre>
 383      * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
 384      * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ")
 385      * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' |  "'" EncName "'" )
 386      * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
 387      * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'")
 388      *                 | ('"' ('yes' | 'no') '"'))
 389      *
 390      * [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
 391      * </pre>
 392      *
 393      * @param scanningTextDecl True if a text declaration is to
 394      *                         be scanned instead of an XML
 395      *                         declaration.
 396      * @param pseudoAttributeValues An array of size 3 to return the version,
 397      *                         encoding and standalone pseudo attribute values
 398      *                         (in that order).
 399      *
 400      * <strong>Note:</strong> This method uses fString, anything in it
 401      * at the time of calling is lost.
 402      */
 403     protected void scanXMLDeclOrTextDecl(boolean scanningTextDecl,
 404             String[] pseudoAttributeValues)
 405             throws IOException, XNIException {
 406 
 407         // pseudo-attribute values
 408         String version = null;
 409         String encoding = null;
 410         String standalone = null;
 411 
 412         // scan pseudo-attributes
 413         final int STATE_VERSION = 0;
 414         final int STATE_ENCODING = 1;
 415         final int STATE_STANDALONE = 2;
 416         final int STATE_DONE = 3;
 417         int state = STATE_VERSION;
 418 
 419         boolean dataFoundForTarget = false;
 420         boolean sawSpace = fEntityScanner.skipSpaces();
 421         // since pseudoattributes are *not* attributes,
 422         // their quotes don't need to be preserved in external parameter entities.
 423         // the XMLEntityScanner#scanLiteral method will continue to
 424         // emit -1 in such cases when it finds a quote; this is
 425         // fine for other methods that parse scanned entities,
 426         // but not for the scanning of pseudoattributes.  So,
 427         // temporarily, we must mark the current entity as not being "literal"
 428         Entity.ScannedEntity currEnt = fEntityManager.getCurrentEntity();
 429         boolean currLiteral = currEnt.literal;
 430         currEnt.literal = false;
 431         while (fEntityScanner.peekChar() != '?') {
 432             dataFoundForTarget = true;
 433             String name = scanPseudoAttribute(scanningTextDecl, fString);
 434             switch (state) {
 435                 case STATE_VERSION: {
 436                     if (name.equals(fVersionSymbol)) {
 437                         if (!sawSpace) {
 438                             reportFatalError(scanningTextDecl
 439                                     ? "SpaceRequiredBeforeVersionInTextDecl"
 440                                     : "SpaceRequiredBeforeVersionInXMLDecl",
 441                                     null);
 442                         }
 443                         version = fString.toString();
 444                         state = STATE_ENCODING;
 445                         if (!versionSupported(version)) {
 446                             reportFatalError("VersionNotSupported",
 447                                     new Object[]{version});
 448                         }
 449 
 450                         if (version.equals("1.1")) {
 451                             Entity.ScannedEntity top = fEntityManager.getTopLevelEntity();
 452                             if (top != null && (top.version == null || top.version.equals("1.0"))) {
 453                                 reportFatalError("VersionMismatch", null);
 454                             }
 455                             fEntityManager.setScannerVersion(Constants.XML_VERSION_1_1);
 456                         }
 457 
 458                     } else if (name.equals(fEncodingSymbol)) {
 459                         if (!scanningTextDecl) {
 460                             reportFatalError("VersionInfoRequired", null);
 461                         }
 462                         if (!sawSpace) {
 463                             reportFatalError(scanningTextDecl
 464                                     ? "SpaceRequiredBeforeEncodingInTextDecl"
 465                                     : "SpaceRequiredBeforeEncodingInXMLDecl",
 466                                     null);
 467                         }
 468                         encoding = fString.toString();
 469                         state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
 470                     } else {
 471                         if (scanningTextDecl) {
 472                             reportFatalError("EncodingDeclRequired", null);
 473                         } else {
 474                             reportFatalError("VersionInfoRequired", null);
 475                         }
 476                     }
 477                     break;
 478                 }
 479                 case STATE_ENCODING: {
 480                     if (name.equals(fEncodingSymbol)) {
 481                         if (!sawSpace) {
 482                             reportFatalError(scanningTextDecl
 483                                     ? "SpaceRequiredBeforeEncodingInTextDecl"
 484                                     : "SpaceRequiredBeforeEncodingInXMLDecl",
 485                                     null);
 486                         }
 487                         encoding = fString.toString();
 488                         state = scanningTextDecl ? STATE_DONE : STATE_STANDALONE;
 489                         // TODO: check encoding name; set encoding on
 490                         //       entity scanner
 491                     } else if (!scanningTextDecl && name.equals(fStandaloneSymbol)) {
 492                         if (!sawSpace) {
 493                             reportFatalError("SpaceRequiredBeforeStandalone",
 494                                     null);
 495                         }
 496                         standalone = fString.toString();
 497                         state = STATE_DONE;
 498                         if (!standalone.equals("yes") && !standalone.equals("no")) {
 499                             reportFatalError("SDDeclInvalid", new Object[] {standalone});
 500                         }
 501                     } else {
 502                         reportFatalError("EncodingDeclRequired", null);
 503                     }
 504                     break;
 505                 }
 506                 case STATE_STANDALONE: {
 507                     if (name.equals(fStandaloneSymbol)) {
 508                         if (!sawSpace) {
 509                             reportFatalError("SpaceRequiredBeforeStandalone",
 510                                     null);
 511                         }
 512                         standalone = fString.toString();
 513                         state = STATE_DONE;
 514                         if (!standalone.equals("yes") && !standalone.equals("no")) {
 515                             reportFatalError("SDDeclInvalid",  new Object[] {standalone});
 516                         }
 517                     } else {
 518                         reportFatalError("SDDeclNameInvalid", null);
 519                     }
 520                     break;
 521                 }
 522                 default: {
 523                     reportFatalError("NoMorePseudoAttributes", null);
 524                 }
 525             }
 526             sawSpace = fEntityScanner.skipSpaces();
 527         }
 528         // restore original literal value
 529         if(currLiteral) {
 530             currEnt.literal = true;
 531         }
 532         // REVISIT: should we remove this error reporting?
 533         if (scanningTextDecl && state != STATE_DONE) {
 534             reportFatalError("MorePseudoAttributes", null);
 535         }
 536 
 537         // If there is no data in the xml or text decl then we fail to report error
 538         // for version or encoding info above.
 539         if (scanningTextDecl) {
 540             if (!dataFoundForTarget && encoding == null) {
 541                 reportFatalError("EncodingDeclRequired", null);
 542             }
 543         } else {
 544             if (!dataFoundForTarget && version == null) {
 545                 reportFatalError("VersionInfoRequired", null);
 546             }
 547         }
 548 
 549         // end
 550         if (!fEntityScanner.skipChar('?')) {
 551             reportFatalError("XMLDeclUnterminated", null);
 552         }
 553         if (!fEntityScanner.skipChar('>')) {
 554             reportFatalError("XMLDeclUnterminated", null);
 555 
 556         }
 557 
 558         // fill in return array
 559         pseudoAttributeValues[0] = version;
 560         pseudoAttributeValues[1] = encoding;
 561         pseudoAttributeValues[2] = standalone;
 562 
 563     } // scanXMLDeclOrTextDecl(boolean)
 564 
 565     /**
 566      * Scans a pseudo attribute.
 567      *
 568      * @param scanningTextDecl True if scanning this pseudo-attribute for a
 569      *                         TextDecl; false if scanning XMLDecl. This
 570      *                         flag is needed to report the correct type of
 571      *                         error.
 572      * @param value            The string to fill in with the attribute
 573      *                         value.
 574      *
 575      * @return The name of the attribute
 576      *
 577      * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
 578      * at the time of calling is lost.
 579      */
 580     public String scanPseudoAttribute(boolean scanningTextDecl,
 581             XMLString value)
 582             throws IOException, XNIException {
 583 
 584         String name = scanPseudoAttributeName();
 585         // XMLEntityManager.print(fEntityManager.getCurrentEntity());
 586 
 587         if (name == null) {
 588             reportFatalError("PseudoAttrNameExpected", null);
 589         }
 590         fEntityScanner.skipSpaces();
 591         if (!fEntityScanner.skipChar('=')) {
 592             reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl"
 593                     : "EqRequiredInXMLDecl", new Object[]{name});
 594         }
 595         fEntityScanner.skipSpaces();
 596         int quote = fEntityScanner.peekChar();
 597         if (quote != '\'' && quote != '"') {
 598             reportFatalError(scanningTextDecl ? "QuoteRequiredInTextDecl"
 599                     : "QuoteRequiredInXMLDecl" , new Object[]{name});
 600         }
 601         fEntityScanner.scanChar();
 602         int c = fEntityScanner.scanLiteral(quote, value);
 603         if (c != quote) {
 604             fStringBuffer2.clear();
 605             do {
 606                 fStringBuffer2.append(value);
 607                 if (c != -1) {
 608                     if (c == '&' || c == '%' || c == '<' || c == ']') {
 609                         fStringBuffer2.append((char)fEntityScanner.scanChar());
 610                     } else if (XMLChar.isHighSurrogate(c)) {
 611                         scanSurrogates(fStringBuffer2);
 612                     } else if (isInvalidLiteral(c)) {
 613                         String key = scanningTextDecl
 614                                 ? "InvalidCharInTextDecl" : "InvalidCharInXMLDecl";
 615                         reportFatalError(key,
 616                                 new Object[] {Integer.toString(c, 16)});
 617                                 fEntityScanner.scanChar();
 618                     }
 619                 }
 620                 c = fEntityScanner.scanLiteral(quote, value);
 621             } while (c != quote);
 622             fStringBuffer2.append(value);
 623             value.setValues(fStringBuffer2);
 624         }
 625         if (!fEntityScanner.skipChar(quote)) {
 626             reportFatalError(scanningTextDecl ? "CloseQuoteMissingInTextDecl"
 627                     : "CloseQuoteMissingInXMLDecl",
 628                     new Object[]{name});
 629         }
 630 
 631         // return
 632         return name;
 633 
 634     } // scanPseudoAttribute(XMLString):String
 635 
 636     /**
 637      * Scans the name of a pseudo attribute. The only legal names
 638      * in XML 1.0/1.1 documents are 'version', 'encoding' and 'standalone'.
 639      *
 640      * @return the name of the pseudo attribute or <code>null</code>
 641      * if a legal pseudo attribute name could not be scanned.
 642      */
 643     private String scanPseudoAttributeName() throws IOException, XNIException {
 644         final int ch = fEntityScanner.peekChar();
 645         switch (ch) {
 646             case 'v':
 647                 if (fEntityScanner.skipString(fVersionSymbol)) {
 648                     return fVersionSymbol;
 649                 }
 650                 break;
 651             case 'e':
 652                 if (fEntityScanner.skipString(fEncodingSymbol)) {
 653                     return fEncodingSymbol;
 654                 }
 655                 break;
 656             case 's':
 657                 if (fEntityScanner.skipString(fStandaloneSymbol)) {
 658                     return fStandaloneSymbol;
 659                 }
 660                 break;
 661         }
 662         return null;
 663     } // scanPseudoAttributeName()
 664 
 665     /**
 666      * Scans a processing instruction.
 667      * <p>
 668      * <pre>
 669      * [16] PI ::= '&lt;?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
 670      * [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
 671      * </pre>
 672      */
 673     //CHANGED:
 674     //EARLIER: scanPI()
 675     //NOW: scanPI(XMLStringBuffer)
 676     //it makes things more easy if XMLStringBUffer is passed. Motivation for this change is same
 677     // as that for scanContent()
 678 
 679     protected void scanPI(XMLStringBuffer data) throws IOException, XNIException {
 680 
 681         // target
 682         fReportEntity = false;
 683         String target = fEntityScanner.scanName();
 684         if (target == null) {
 685             reportFatalError("PITargetRequired", null);
 686         }
 687 
 688         // scan data
 689         scanPIData(target, data);
 690         fReportEntity = true;
 691 
 692     } // scanPI(XMLStringBuffer)
 693 
 694     /**
 695      * Scans a processing data. This is needed to handle the situation
 696      * where a document starts with a processing instruction whose
 697      * target name <em>starts with</em> "xml". (e.g. xmlfoo)
 698      *
 699      * This method would always read the whole data. We have while loop and data is buffered
 700      * until delimeter is encountered.
 701      *
 702      * @param target The PI target
 703      * @param data The string to fill in with the data
 704      */
 705 
 706     //CHANGED:
 707     //Earlier:This method uses the fStringBuffer and later buffer values are set to
 708     //the supplied XMLString....
 709     //Now: Changed the signature of this function to pass XMLStringBuffer.. and data would
 710     //be appended to that buffer
 711 
 712     protected void scanPIData(String target, XMLStringBuffer data)
 713     throws IOException, XNIException {
 714 
 715         // check target
 716         if (target.length() == 3) {
 717             char c0 = Character.toLowerCase(target.charAt(0));
 718             char c1 = Character.toLowerCase(target.charAt(1));
 719             char c2 = Character.toLowerCase(target.charAt(2));
 720             if (c0 == 'x' && c1 == 'm' && c2 == 'l') {
 721                 reportFatalError("ReservedPITarget", null);
 722             }
 723         }
 724 
 725         // spaces
 726         if (!fEntityScanner.skipSpaces()) {
 727             if (fEntityScanner.skipString("?>")) {
 728                 // we found the end, there is no data just return
 729                 return;
 730             } else {
 731                 // if there is data there should be some space
 732                 reportFatalError("SpaceRequiredInPI", null);
 733             }
 734         }
 735 
 736         // since scanData appends the parsed data to the buffer passed
 737         // a while loop would append the whole of parsed data to the buffer(data:XMLStringBuffer)
 738         //until all of the data is buffered.
 739         if (fEntityScanner.scanData("?>", data)) {
 740             do {
 741                 int c = fEntityScanner.peekChar();
 742                 if (c != -1) {
 743                     if (XMLChar.isHighSurrogate(c)) {
 744                         scanSurrogates(data);
 745                     } else if (isInvalidLiteral(c)) {
 746                         reportFatalError("InvalidCharInPI",
 747                                 new Object[]{Integer.toHexString(c)});
 748                                 fEntityScanner.scanChar();
 749                     }
 750                 }
 751             } while (fEntityScanner.scanData("?>", data));
 752         }
 753 
 754     } // scanPIData(String,XMLString)
 755 
 756     /**
 757      * Scans a comment.
 758      * <p>
 759      * <pre>
 760      * [15] Comment ::= '&lt!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
 761      * </pre>
 762      * <p>
 763      * <strong>Note:</strong> Called after scanning past '&lt;!--'
 764      * <strong>Note:</strong> This method uses fString, anything in it
 765      * at the time of calling is lost.
 766      *
 767      * @param text The buffer to fill in with the text.
 768      */
 769     protected void scanComment(XMLStringBuffer text)
 770     throws IOException, XNIException {
 771 
 772         //System.out.println( "XMLScanner#scanComment# In Scan Comment" );
 773         // text
 774         // REVISIT: handle invalid character, eof
 775         text.clear();
 776         while (fEntityScanner.scanData("--", text)) {
 777             int c = fEntityScanner.peekChar();
 778 
 779             //System.out.println( "XMLScanner#scanComment#text.toString() == " + text.toString() );
 780             //System.out.println( "XMLScanner#scanComment#c == " + c );
 781 
 782             if (c != -1) {
 783                 if (XMLChar.isHighSurrogate(c)) {
 784                     scanSurrogates(text);
 785                 }
 786                 else if (isInvalidLiteral(c)) {
 787                     reportFatalError("InvalidCharInComment",
 788                             new Object[] { Integer.toHexString(c) });
 789                             fEntityScanner.scanChar();
 790                 }
 791             }
 792         }
 793         if (!fEntityScanner.skipChar('>')) {
 794             reportFatalError("DashDashInComment", null);
 795         }
 796 
 797     } // scanComment()
 798 
 799     /**
 800      * Scans an attribute value and normalizes whitespace converting all
 801      * whitespace characters to space characters.
 802      *
 803      * [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
 804      *
 805      * @param value The XMLString to fill in with the value.
 806      * @param nonNormalizedValue The XMLString to fill in with the
 807      *                           non-normalized value.
 808      * @param atName The name of the attribute being parsed (for error msgs).
 809      * @param attributes The attributes list for the scanned attribute.
 810      * @param attrIndex The index of the attribute to use from the list.
 811      * @param checkEntities true if undeclared entities should be reported as VC violation,
 812      *                      false if undeclared entities should be reported as WFC violation.
 813      * @param eleName The name of element to which this attribute belongs.
 814      *
 815      * <strong>Note:</strong> This method uses fStringBuffer2, anything in it
 816      * at the time of calling is lost.
 817      **/
 818     protected void scanAttributeValue(XMLString value,
 819             XMLString nonNormalizedValue,
 820             String atName,
 821             XMLAttributes attributes, int attrIndex,
 822             boolean checkEntities, String eleName)
 823             throws IOException, XNIException {
 824         XMLStringBuffer stringBuffer = null;
 825         // quote
 826         int quote = fEntityScanner.peekChar();
 827         if (quote != '\'' && quote != '"') {
 828             reportFatalError("OpenQuoteExpected", new Object[]{eleName, atName});
 829         }
 830 
 831         fEntityScanner.scanChar();
 832         int entityDepth = fEntityDepth;
 833 
 834         int c = fEntityScanner.scanLiteral(quote, value);
 835         if (DEBUG_ATTR_NORMALIZATION) {
 836             System.out.println("** scanLiteral -> \""
 837                     + value.toString() + "\"");
 838         }
 839         if(fNeedNonNormalizedValue){
 840             fStringBuffer2.clear();
 841             fStringBuffer2.append(value);
 842         }
 843         if(fEntityScanner.whiteSpaceLen > 0)
 844             normalizeWhitespace(value);
 845         if (DEBUG_ATTR_NORMALIZATION) {
 846             System.out.println("** normalizeWhitespace -> \""
 847                     + value.toString() + "\"");
 848         }
 849         if (c != quote) {
 850             fScanningAttribute = true;
 851             stringBuffer = getStringBuffer();
 852             stringBuffer.clear();
 853             do {
 854                 stringBuffer.append(value);
 855                 if (DEBUG_ATTR_NORMALIZATION) {
 856                     System.out.println("** value2: \""
 857                             + stringBuffer.toString() + "\"");
 858                 }
 859                 if (c == '&') {
 860                     fEntityScanner.skipChar('&');
 861                     if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
 862                         fStringBuffer2.append('&');
 863                     }
 864                     if (fEntityScanner.skipChar('#')) {
 865                         if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
 866                             fStringBuffer2.append('#');
 867                         }
 868                         int ch ;
 869                         if (fNeedNonNormalizedValue)
 870                             ch = scanCharReferenceValue(stringBuffer, fStringBuffer2);
 871                         else
 872                             ch = scanCharReferenceValue(stringBuffer, null);
 873 
 874                         if (ch != -1) {
 875                             if (DEBUG_ATTR_NORMALIZATION) {
 876                                 System.out.println("** value3: \""
 877                                         + stringBuffer.toString()
 878                                         + "\"");
 879                             }
 880                         }
 881                     } else {
 882                         String entityName = fEntityScanner.scanName();
 883                         if (entityName == null) {
 884                             reportFatalError("NameRequiredInReference", null);
 885                         } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
 886                             fStringBuffer2.append(entityName);
 887                         }
 888                         if (!fEntityScanner.skipChar(';')) {
 889                             reportFatalError("SemicolonRequiredInReference",
 890                                     new Object []{entityName});
 891                         } else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
 892                             fStringBuffer2.append(';');
 893                         }
 894                         if (entityName == fAmpSymbol) {
 895                             stringBuffer.append('&');
 896                             if (DEBUG_ATTR_NORMALIZATION) {
 897                                 System.out.println("** value5: \""
 898                                         + stringBuffer.toString()
 899                                         + "\"");
 900                             }
 901                         } else if (entityName == fAposSymbol) {
 902                             stringBuffer.append('\'');
 903                             if (DEBUG_ATTR_NORMALIZATION) {
 904                                 System.out.println("** value7: \""
 905                                         + stringBuffer.toString()
 906                                         + "\"");
 907                             }
 908                         } else if (entityName == fLtSymbol) {
 909                             stringBuffer.append('<');
 910                             if (DEBUG_ATTR_NORMALIZATION) {
 911                                 System.out.println("** value9: \""
 912                                         + stringBuffer.toString()
 913                                         + "\"");
 914                             }
 915                         } else if (entityName == fGtSymbol) {
 916                             stringBuffer.append('>');
 917                             if (DEBUG_ATTR_NORMALIZATION) {
 918                                 System.out.println("** valueB: \""
 919                                         + stringBuffer.toString()
 920                                         + "\"");
 921                             }
 922                         } else if (entityName == fQuotSymbol) {
 923                             stringBuffer.append('"');
 924                             if (DEBUG_ATTR_NORMALIZATION) {
 925                                 System.out.println("** valueD: \""
 926                                         + stringBuffer.toString()
 927                                         + "\"");
 928                             }
 929                         } else {
 930                             if (fEntityStore.isExternalEntity(entityName)) {
 931                                 reportFatalError("ReferenceToExternalEntity",
 932                                         new Object[] { entityName });
 933                             } else {
 934                                 if (!fEntityStore.isDeclaredEntity(entityName)) {
 935                                     //WFC & VC: Entity Declared
 936                                     if (checkEntities) {
 937                                         if (fValidation) {
 938                                             fErrorReporter.reportError(fEntityScanner,XMLMessageFormatter.XML_DOMAIN,
 939                                                     "EntityNotDeclared",
 940                                                     new Object[]{entityName},
 941                                                     XMLErrorReporter.SEVERITY_ERROR);
 942                                         }
 943                                     } else {
 944                                         reportFatalError("EntityNotDeclared",
 945                                                 new Object[]{entityName});
 946                                     }
 947                                 }
 948                                 fEntityManager.startEntity(true, entityName, true);
 949                             }
 950                         }
 951                     }
 952                 } else if (c == '<') {
 953                     reportFatalError("LessthanInAttValue",
 954                             new Object[] { eleName, atName });
 955                             fEntityScanner.scanChar();
 956                             if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
 957                                 fStringBuffer2.append((char)c);
 958                             }
 959                 } else if (c == '%' || c == ']') {
 960                     fEntityScanner.scanChar();
 961                     stringBuffer.append((char)c);
 962                     if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
 963                         fStringBuffer2.append((char)c);
 964                     }
 965                     if (DEBUG_ATTR_NORMALIZATION) {
 966                         System.out.println("** valueF: \""
 967                                 + stringBuffer.toString() + "\"");
 968                     }
 969                 } else if (c == '\n' || c == '\r') {
 970                     fEntityScanner.scanChar();
 971                     stringBuffer.append(' ');
 972                     if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
 973                         fStringBuffer2.append('\n');
 974                     }
 975                 } else if (c != -1 && XMLChar.isHighSurrogate(c)) {
 976                     fStringBuffer3.clear();
 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[] {eleName, atName, 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[]{eleName, 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     // returns true if the given character is
1388     // a valid high surrogate for a nameStartChar
1389     // with respect to the version of XML understood
1390     // by this scanner.
1391     protected boolean isValidNameStartHighSurrogate(int value) {
1392         return false;
1393     } // isValidNameStartHighSurrogate(int):  boolean
1394 
1395     protected boolean versionSupported(String version ) {
1396         return version.equals("1.0") || version.equals("1.1");
1397     } // version Supported
1398 
1399     /**
1400      * Scans surrogates and append them to the specified buffer.
1401      * <p>
1402      * <strong>Note:</strong> This assumes the current char has already been
1403      * identified as a high surrogate.
1404      *
1405      * @param buf The StringBuffer to append the read surrogates to.
1406      * @return True if it succeeded.
1407      */
1408     protected boolean scanSurrogates(XMLStringBuffer buf)
1409     throws IOException, XNIException {
1410 
1411         int high = fEntityScanner.scanChar();
1412         int low = fEntityScanner.peekChar();
1413         if (!XMLChar.isLowSurrogate(low)) {
1414             reportFatalError("InvalidCharInContent",
1415                     new Object[] {Integer.toString(high, 16)});
1416                     return false;
1417         }
1418         fEntityScanner.scanChar();
1419 
1420         // convert surrogates to supplemental character
1421         int c = XMLChar.supplemental((char)high, (char)low);
1422 
1423         // supplemental character must be a valid XML character
1424         if (isInvalid(c)) {
1425             reportFatalError("InvalidCharInContent",
1426                     new Object[]{Integer.toString(c, 16)});
1427                     return false;
1428         }
1429 
1430         // fill in the buffer
1431         buf.append((char)high);
1432         buf.append((char)low);
1433 
1434         return true;
1435 
1436     } // scanSurrogates():boolean
1437 
1438 
1439     /**
1440      * Convenience function used in all XML scanners.
1441      */
1442     protected void reportFatalError(String msgId, Object[] args)
1443     throws XNIException {
1444         fErrorReporter.reportError(fEntityScanner, XMLMessageFormatter.XML_DOMAIN,
1445                 msgId, args,
1446                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
1447     }
1448 
1449     // private methods
1450     private void init() {
1451         // initialize scanner
1452         fEntityScanner = null;
1453         // initialize vars
1454         fEntityDepth = 0;
1455         fReportEntity = true;
1456         fResourceIdentifier.clear();
1457 
1458         if(!fAttributeCacheInitDone){
1459             for(int i = 0; i < initialCacheCount; i++){
1460                 attributeValueCache.add(new XMLString());
1461                 stringBufferCache.add(new XMLStringBuffer());
1462             }
1463             fAttributeCacheInitDone = true;
1464         }
1465         fStringBufferIndex = 0;
1466         fAttributeCacheUsedCount = 0;
1467 
1468     }
1469 
1470     XMLStringBuffer getStringBuffer(){
1471         if((fStringBufferIndex < initialCacheCount )|| (fStringBufferIndex < stringBufferCache.size())){
1472             return stringBufferCache.get(fStringBufferIndex++);
1473         }else{
1474             XMLStringBuffer tmpObj = new XMLStringBuffer();
1475             fStringBufferIndex++;
1476             stringBufferCache.add(tmpObj);
1477             return tmpObj;
1478         }
1479     }
1480 
1481 
1482 } // class XMLScanner