1 /*
   2  * Copyright (c) 2003, 2006, 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