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 ::= '<?' 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 ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' 763 * </pre> 764 * <p> 765 * <strong>Note:</strong> Called after scanning past '<!--' 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