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 22 package com.sun.org.apache.xerces.internal.impl; 23 24 import com.sun.xml.internal.stream.XMLBufferListener; 25 import com.sun.xml.internal.stream.XMLEntityStorage; 26 import com.sun.xml.internal.stream.XMLInputFactoryImpl; 27 import com.sun.xml.internal.stream.dtd.DTDGrammarUtil; 28 29 import java.io.EOFException; 30 import java.io.IOException; 31 import javax.xml.stream.XMLInputFactory; 32 import javax.xml.stream.events.XMLEvent; 33 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; 34 import com.sun.org.apache.xerces.internal.util.AugmentationsImpl; 35 import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl; 36 import com.sun.org.apache.xerces.internal.util.XMLChar; 37 import com.sun.org.apache.xerces.internal.util.XMLStringBuffer; 38 import com.sun.org.apache.xerces.internal.util.XMLSymbols; 39 import com.sun.org.apache.xerces.internal.xni.QName; 40 import com.sun.org.apache.xerces.internal.xni.XMLAttributes; 41 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; 42 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; 43 import com.sun.org.apache.xerces.internal.xni.XMLString; 44 import com.sun.org.apache.xerces.internal.xni.XNIException; 45 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; 46 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; 47 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 48 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentScanner; 49 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; 50 import com.sun.org.apache.xerces.internal.xni.Augmentations; 51 import com.sun.org.apache.xerces.internal.impl.Constants; 52 import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler; 53 import com.sun.org.apache.xerces.internal.util.SecurityManager; 54 import com.sun.org.apache.xerces.internal.util.NamespaceSupport; 55 import com.sun.org.apache.xerces.internal.utils.SecuritySupport; 56 import com.sun.org.apache.xerces.internal.xni.NamespaceContext; 57 import com.sun.xml.internal.stream.Entity; 58 import javax.xml.XMLConstants; 59 import javax.xml.stream.XMLStreamConstants; 60 import javax.xml.stream.events.XMLEvent; 61 62 /** 63 * 64 * This class is responsible for scanning the structure and content 65 * of document fragments. 66 * 67 * This class has been modified as per the new design which is more suited to 68 * efficiently build pull parser. Lot of improvements have been done and 69 * the code has been added to support stax functionality/features. 70 * 71 * @author Neeraj Bajaj SUN Microsystems 72 * @author K.Venugopal SUN Microsystems 73 * @author Glenn Marcy, IBM 74 * @author Andy Clark, IBM 75 * @author Arnaud Le Hors, IBM 76 * @author Eric Ye, IBM 77 * @author Sunitha Reddy, SUN Microsystems 78 * @version $Id: XMLDocumentFragmentScannerImpl.java,v 1.19 2010-11-02 19:54:55 joehw Exp $ 79 * 80 */ 81 public class XMLDocumentFragmentScannerImpl 82 extends XMLScanner 83 implements XMLDocumentScanner, XMLComponent, XMLEntityHandler, XMLBufferListener { 84 85 // 86 // Constants 87 // 88 89 protected int fElementAttributeLimit; 90 91 /** External subset resolver. **/ 92 protected ExternalSubsetResolver fExternalSubsetResolver; 93 94 // scanner states 95 96 //XXX this should be divided into more states. 97 /** Scanner state: start of markup. */ 98 protected static final int SCANNER_STATE_START_OF_MARKUP = 21; 99 100 /** Scanner state: content. */ 101 protected static final int SCANNER_STATE_CONTENT = 22; 102 103 /** Scanner state: processing instruction. */ 104 protected static final int SCANNER_STATE_PI = 23; 105 106 /** Scanner state: DOCTYPE. */ 107 protected static final int SCANNER_STATE_DOCTYPE = 24; 108 109 /** Scanner state: XML Declaration */ 110 protected static final int SCANNER_STATE_XML_DECL = 25; 111 112 /** Scanner state: root element. */ 113 protected static final int SCANNER_STATE_ROOT_ELEMENT = 26; 114 115 /** Scanner state: comment. */ 116 protected static final int SCANNER_STATE_COMMENT = 27; 117 118 /** Scanner state: reference. */ 119 protected static final int SCANNER_STATE_REFERENCE = 28; 120 121 // <book type="hard"> reading attribute name 'type' 122 protected static final int SCANNER_STATE_ATTRIBUTE = 29; 123 124 // <book type="hard"> //reading attribute value. 125 protected static final int SCANNER_STATE_ATTRIBUTE_VALUE = 30; 126 127 /** Scanner state: trailing misc. USED BY DOCUMENT_SCANNER_IMPL*/ 128 //protected static final int SCANNER_STATE_TRAILING_MISC = 32; 129 130 /** Scanner state: end of input. */ 131 protected static final int SCANNER_STATE_END_OF_INPUT = 33; 132 133 /** Scanner state: terminated. */ 134 protected static final int SCANNER_STATE_TERMINATED = 34; 135 136 /** Scanner state: CDATA section. */ 137 protected static final int SCANNER_STATE_CDATA = 35; 138 139 /** Scanner state: Text declaration. */ 140 protected static final int SCANNER_STATE_TEXT_DECL = 36; 141 142 /** Scanner state: Text declaration. */ 143 protected static final int SCANNER_STATE_CHARACTER_DATA = 37; 144 145 //<book type="hard">foo</book> 146 protected static final int SCANNER_STATE_START_ELEMENT_TAG = 38; 147 148 //<book type="hard">foo</book> reading </book> 149 protected static final int SCANNER_STATE_END_ELEMENT_TAG = 39; 150 151 protected static final int SCANNER_STATE_CHAR_REFERENCE = 40; 152 protected static final int SCANNER_STATE_BUILT_IN_REFS = 41; 153 154 // feature identifiers 155 156 157 /** Feature identifier: notify built-in refereces. */ 158 protected static final String NOTIFY_BUILTIN_REFS = 159 Constants.XERCES_FEATURE_PREFIX + Constants.NOTIFY_BUILTIN_REFS_FEATURE; 160 161 /** Property identifier: entity resolver. */ 162 protected static final String ENTITY_RESOLVER = 163 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; 164 165 /** Feature identifier: standard uri conformant */ 166 protected static final String STANDARD_URI_CONFORMANT = 167 Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE; 168 169 /** property identifier: access external dtd. */ 170 protected static final String ACCESS_EXTERNAL_DTD = XMLConstants.ACCESS_EXTERNAL_DTD; 171 172 /** access external dtd: file protocol 173 * For DOM/SAX, the secure feature is set to true by default 174 */ 175 final static String EXTERNAL_ACCESS_DEFAULT = Constants.EXTERNAL_ACCESS_DEFAULT; 176 177 // recognized features and properties 178 179 /** Recognized features. */ 180 private static final String[] RECOGNIZED_FEATURES = { 181 NAMESPACES, 182 VALIDATION, 183 NOTIFY_BUILTIN_REFS, 184 NOTIFY_CHAR_REFS, 185 Constants.STAX_REPORT_CDATA_EVENT 186 }; 187 188 /** Feature defaults. */ 189 private static final Boolean[] FEATURE_DEFAULTS = { 190 Boolean.TRUE, 191 null, 192 Boolean.FALSE, 193 Boolean.FALSE, 194 Boolean.TRUE 195 }; 196 197 /** Recognized properties. */ 198 private static final String[] RECOGNIZED_PROPERTIES = { 199 SYMBOL_TABLE, 200 ERROR_REPORTER, 201 ENTITY_MANAGER, 202 ACCESS_EXTERNAL_DTD 203 }; 204 205 /** Property defaults. */ 206 private static final Object[] PROPERTY_DEFAULTS = { 207 null, 208 null, 209 null, 210 EXTERNAL_ACCESS_DEFAULT 211 }; 212 213 private static final char [] cdata = {'[','C','D','A','T','A','['}; 214 private static final char [] endTag = {'<','/'}; 215 216 //this variable is also used by XMLDocumentScannerImpl in the same package 217 static final char [] xmlDecl = {'<','?','x','m','l'}; 218 219 // debugging 220 221 /** Debug scanner state. */ 222 private static final boolean DEBUG_SCANNER_STATE = false; 223 224 /** Debug driver. */ 225 private static final boolean DEBUG_DISPATCHER = false; 226 227 /** Debug content driver scanning. */ 228 protected static final boolean DEBUG_START_END_ELEMENT = false; 229 230 231 /** Debug driver next */ 232 protected static final boolean DEBUG_NEXT = false ; 233 234 /** Debug driver next */ 235 protected static final boolean DEBUG = false; 236 protected static final boolean DEBUG_COALESCE = false; 237 // 238 // Data 239 // 240 241 // protected data 242 243 /** Document handler. */ 244 protected XMLDocumentHandler fDocumentHandler; 245 protected int fScannerLastState ; 246 247 /** Entity Storage */ 248 protected XMLEntityStorage fEntityStore; 249 250 /** Entity stack. */ 251 protected int[] fEntityStack = new int[4]; 252 253 /** Markup depth. */ 254 protected int fMarkupDepth; 255 256 //is the element empty 257 protected boolean fEmptyElement ; 258 259 //track if we are reading attributes, this is usefule while 260 //there is a callback 261 protected boolean fReadingAttributes = false; 262 263 /** Scanner state. */ 264 protected int fScannerState; 265 266 /** SubScanner state: inside scanContent method. */ 267 protected boolean fInScanContent = false; 268 protected boolean fLastSectionWasCData = false; 269 protected boolean fLastSectionWasEntityReference = false; 270 protected boolean fLastSectionWasCharacterData = false; 271 272 /** has external dtd */ 273 protected boolean fHasExternalDTD; 274 275 /** Standalone. */ 276 protected boolean fStandaloneSet; 277 protected boolean fStandalone; 278 protected String fVersion; 279 280 // element information 281 282 /** Current element. */ 283 protected QName fCurrentElement; 284 285 /** Element stack. */ 286 protected ElementStack fElementStack = new ElementStack(); 287 protected ElementStack2 fElementStack2 = new ElementStack2(); 288 289 // other info 290 291 /** Document system identifier. 292 * REVISIT: So what's this used for? - NG 293 * protected String fDocumentSystemId; 294 ******/ 295 296 protected String fPITarget ; 297 298 //xxx do we need to create an extra XMLString object... look for using fTempString for collecting all the data values 299 protected XMLString fPIData = new XMLString(); 300 301 // features 302 303 304 /** Notify built-in references. */ 305 protected boolean fNotifyBuiltInRefs = false; 306 307 //STAX related properties 308 //defaultValues. 309 protected boolean fSupportDTD = true; 310 protected boolean fReplaceEntityReferences = true; 311 protected boolean fSupportExternalEntities = false; 312 protected boolean fReportCdataEvent = false ; 313 protected boolean fIsCoalesce = false ; 314 protected String fDeclaredEncoding = null; 315 /** Xerces Feature: Disallow doctype declaration. */ 316 protected boolean fDisallowDoctype = false; 317 /** 318 * comma-delimited list of protocols that are allowed for the purpose 319 * of accessing external dtd or entity references 320 */ 321 protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT; 322 323 /** 324 * standard uri conformant (strict uri). 325 * http://apache.org/xml/features/standard-uri-conformant 326 */ 327 protected boolean fStrictURI; 328 329 // drivers 330 331 /** Active driver. */ 332 protected Driver fDriver; 333 334 /** Content driver. */ 335 protected Driver fContentDriver = createContentDriver(); 336 337 // temporary variables 338 339 /** Element QName. */ 340 protected QName fElementQName = new QName(); 341 342 /** Attribute QName. */ 343 protected QName fAttributeQName = new QName(); 344 345 /** 346 * CHANGED: Using XMLAttributesIteratorImpl instead of XMLAttributesImpl. This class 347 * implements Iterator interface so we can directly give Attributes in the form of 348 * iterator. 349 */ 350 protected XMLAttributesIteratorImpl fAttributes = new XMLAttributesIteratorImpl(); 351 352 353 /** String. */ 354 protected XMLString fTempString = new XMLString(); 355 356 /** String. */ 357 protected XMLString fTempString2 = new XMLString(); 358 359 /** Array of 3 strings. */ 360 private String[] fStrings = new String[3]; 361 362 /** Making the buffer accesible to derived class -- String buffer. */ 363 protected XMLStringBuffer fStringBuffer = new XMLStringBuffer(); 364 365 /** Making the buffer accesible to derived class -- String buffer. */ 366 protected XMLStringBuffer fStringBuffer2 = new XMLStringBuffer(); 367 368 /** stores character data. */ 369 /** Making the buffer accesible to derived class -- stores PI data */ 370 protected XMLStringBuffer fContentBuffer = new XMLStringBuffer(); 371 372 /** Single character array. */ 373 private final char[] fSingleChar = new char[1]; 374 private String fCurrentEntityName = null; 375 376 // New members 377 protected boolean fScanToEnd = false; 378 379 protected DTDGrammarUtil dtdGrammarUtil= null; 380 381 protected boolean fAddDefaultAttr = false; 382 383 protected boolean foundBuiltInRefs = false; 384 385 protected SecurityManager fSecurityManager = null; 386 387 //skip element algorithm 388 static final short MAX_DEPTH_LIMIT = 5 ; 389 static final short ELEMENT_ARRAY_LENGTH = 200 ; 390 static final short MAX_POINTER_AT_A_DEPTH = 4 ; 391 static final boolean DEBUG_SKIP_ALGORITHM = false; 392 //create a elemnet array of length equal to ELEMENT_ARRAY_LENGTH 393 String [] fElementArray = new String[ELEMENT_ARRAY_LENGTH] ; 394 //pointer location where last element was skipped 395 short fLastPointerLocation = 0 ; 396 short fElementPointer = 0 ; 397 //2D array to store pointer info 398 short [] [] fPointerInfo = new short[MAX_DEPTH_LIMIT] [MAX_POINTER_AT_A_DEPTH] ; 399 protected String fElementRawname ; 400 protected boolean fShouldSkip = false; 401 protected boolean fAdd = false ; 402 protected boolean fSkip = false; 403 404 /** Reusable Augmentations. */ 405 private Augmentations fTempAugmentations = null; 406 // 407 // Constructors 408 // 409 410 /** Default constructor. */ 411 public XMLDocumentFragmentScannerImpl() { 412 } // <init>() 413 414 // 415 // XMLDocumentScanner methods 416 // 417 418 /** 419 * Sets the input source. 420 * 421 * @param inputSource The input source. 422 * 423 * @throws IOException Thrown on i/o error. 424 */ 425 public void setInputSource(XMLInputSource inputSource) throws IOException { 426 fEntityManager.setEntityHandler(this); 427 fEntityManager.startEntity("$fragment$", inputSource, false, true); 428 // fDocumentSystemId = fEntityManager.expandSystemId(inputSource.getSystemId()); 429 } // setInputSource(XMLInputSource) 430 431 /** 432 * Scans a document. 433 * 434 * @param complete True if the scanner should scan the document 435 * completely, pushing all events to the registered 436 * document handler. A value of false indicates that 437 * that the scanner should only scan the next portion 438 * of the document and return. A scanner instance is 439 * permitted to completely scan a document if it does 440 * not support this "pull" scanning model. 441 * 442 * @return True if there is more to scan, false otherwise. 443 */ 444 public boolean scanDocument(boolean complete) 445 throws IOException, XNIException { 446 447 // keep dispatching "events" 448 fEntityManager.setEntityHandler(this); 449 //System.out.println(" get Document Handler in NSDocumentHandler " + fDocumentHandler ); 450 451 int event = next(); 452 do { 453 switch (event) { 454 case XMLStreamConstants.START_DOCUMENT : 455 //fDocumentHandler.startDocument(fEntityManager.getEntityScanner(),fEntityManager.getEntityScanner().getVersion(),fNamespaceContext,null);// not able to get 456 break; 457 case XMLStreamConstants.START_ELEMENT : 458 //System.out.println(" in scann element"); 459 //fDocumentHandler.startElement(getElementQName(),fAttributes,null); 460 break; 461 case XMLStreamConstants.CHARACTERS : 462 fDocumentHandler.characters(getCharacterData(),null); 463 break; 464 case XMLStreamConstants.SPACE: 465 //check if getCharacterData() is the right function to retrieve ignorableWhitespace information. 466 //System.out.println("in the space"); 467 //fDocumentHandler.ignorableWhitespace(getCharacterData(), null); 468 break; 469 case XMLStreamConstants.ENTITY_REFERENCE : 470 //entity reference callback are given in startEntity 471 break; 472 case XMLStreamConstants.PROCESSING_INSTRUCTION : 473 fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null); 474 break; 475 case XMLStreamConstants.COMMENT : 476 //System.out.println(" in COMMENT of the XMLNSDocumentScannerImpl"); 477 fDocumentHandler.comment(getCharacterData(),null); 478 break; 479 case XMLStreamConstants.DTD : 480 //all DTD related callbacks are handled in DTDScanner. 481 //1. Stax doesn't define DTD states as it does for XML Document. 482 //therefore we don't need to take care of anything here. So Just break; 483 break; 484 case XMLStreamConstants.CDATA: 485 fDocumentHandler.startCDATA(null); 486 //xxx: check if CDATA values comes from getCharacterData() function 487 fDocumentHandler.characters(getCharacterData(),null); 488 fDocumentHandler.endCDATA(null); 489 //System.out.println(" in CDATA of the XMLNSDocumentScannerImpl"); 490 break; 491 case XMLStreamConstants.NOTATION_DECLARATION : 492 break; 493 case XMLStreamConstants.ENTITY_DECLARATION : 494 break; 495 case XMLStreamConstants.NAMESPACE : 496 break; 497 case XMLStreamConstants.ATTRIBUTE : 498 break; 499 case XMLStreamConstants.END_ELEMENT : 500 //do not give callback here. 501 //this callback is given in scanEndElement function. 502 //fDocumentHandler.endElement(getElementQName(),null); 503 break; 504 default : 505 throw new InternalError("processing event: " + event); 506 507 } 508 //System.out.println("here in before calling next"); 509 event = next(); 510 //System.out.println("here in after calling next"); 511 } while (event!=XMLStreamConstants.END_DOCUMENT && complete); 512 513 if(event == XMLStreamConstants.END_DOCUMENT) { 514 fDocumentHandler.endDocument(null); 515 return false; 516 } 517 518 return true; 519 520 } // scanDocument(boolean):boolean 521 522 523 524 public com.sun.org.apache.xerces.internal.xni.QName getElementQName(){ 525 if(fScannerLastState == XMLEvent.END_ELEMENT){ 526 fElementQName.setValues(fElementStack.getLastPoppedElement()); 527 } 528 return fElementQName ; 529 } 530 531 /** return the next state on the input 532 * @return int 533 */ 534 535 public int next() throws IOException, XNIException { 536 return fDriver.next(); 537 } 538 539 // 540 // XMLComponent methods 541 // 542 543 /** 544 * Resets the component. The component can query the component manager 545 * about any features and properties that affect the operation of the 546 * component. 547 * 548 * @param componentManager The component manager. 549 * 550 * @throws SAXException Thrown by component on initialization error. 551 * For example, if a feature or property is 552 * required for the operation of the component, the 553 * component manager may throw a 554 * SAXNotRecognizedException or a 555 * SAXNotSupportedException. 556 */ 557 558 public void reset(XMLComponentManager componentManager) 559 throws XMLConfigurationException { 560 561 super.reset(componentManager); 562 563 // other settings 564 // fDocumentSystemId = null; 565 566 // sax features 567 //fAttributes.setNamespaces(fNamespaces); 568 569 // xerces features 570 fReportCdataEvent = componentManager.getFeature(Constants.STAX_REPORT_CDATA_EVENT, true); 571 572 fSecurityManager = (SecurityManager)componentManager.getProperty(Constants.SECURITY_MANAGER, null); 573 fElementAttributeLimit = (fSecurityManager != null)?fSecurityManager.getElementAttrLimit():0; 574 575 fNotifyBuiltInRefs = componentManager.getFeature(NOTIFY_BUILTIN_REFS, false); 576 577 Object resolver = componentManager.getProperty(ENTITY_RESOLVER, null); 578 fExternalSubsetResolver = (resolver instanceof ExternalSubsetResolver) ? 579 (ExternalSubsetResolver) resolver : null; 580 581 // initialize vars 582 fMarkupDepth = 0; 583 fCurrentElement = null; 584 fElementStack.clear(); 585 fHasExternalDTD = false; 586 fStandaloneSet = false; 587 fStandalone = false; 588 fInScanContent = false; 589 //skipping algorithm 590 fShouldSkip = false; 591 fAdd = false; 592 fSkip = false; 593 594 //attribute 595 fReadingAttributes = false; 596 //xxx: external entities are supported in Xerces 597 // it would be good to define feature for this case 598 fSupportExternalEntities = true; 599 fSupportExternalEntities = true; 600 fSupportExternalEntities = true; 601 fSupportExternalEntities = true; 602 fReplaceEntityReferences = true; 603 fIsCoalesce = false; 604 605 // setup Driver 606 setScannerState(SCANNER_STATE_CONTENT); 607 setDriver(fContentDriver); 608 fEntityStore = fEntityManager.getEntityStore(); 609 610 dtdGrammarUtil = null; 611 612 // JAXP 1.5 features and properties 613 fAccessExternalDTD = (String) componentManager.getProperty(ACCESS_EXTERNAL_DTD, EXTERNAL_ACCESS_DEFAULT); 614 fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT, false); 615 616 //fEntityManager.test(); 617 } // reset(XMLComponentManager) 618 619 620 public void reset(PropertyManager propertyManager){ 621 622 super.reset(propertyManager); 623 624 // other settings 625 // fDocumentSystemId = null; 626 fNamespaces = ((Boolean)propertyManager.getProperty(XMLInputFactory.IS_NAMESPACE_AWARE)).booleanValue(); 627 fNotifyBuiltInRefs = false ; 628 629 // initialize vars 630 fMarkupDepth = 0; 631 fCurrentElement = null; 632 fShouldSkip = false; 633 fAdd = false; 634 fSkip = false; 635 fElementStack.clear(); 636 //fElementStack2.clear(); 637 fHasExternalDTD = false; 638 fStandaloneSet = false; 639 fStandalone = false; 640 //fReplaceEntityReferences = true; 641 //fSupportExternalEntities = true; 642 Boolean bo = (Boolean)propertyManager.getProperty(XMLInputFactoryImpl.IS_REPLACING_ENTITY_REFERENCES); 643 fReplaceEntityReferences = bo.booleanValue(); 644 bo = (Boolean)propertyManager.getProperty(XMLInputFactoryImpl.IS_SUPPORTING_EXTERNAL_ENTITIES); 645 fSupportExternalEntities = bo.booleanValue(); 646 Boolean cdata = (Boolean)propertyManager.getProperty(Constants.ZEPHYR_PROPERTY_PREFIX + Constants.STAX_REPORT_CDATA_EVENT) ; 647 if(cdata != null) 648 fReportCdataEvent = cdata.booleanValue() ; 649 Boolean coalesce = (Boolean)propertyManager.getProperty(XMLInputFactory.IS_COALESCING) ; 650 if(coalesce != null) 651 fIsCoalesce = coalesce.booleanValue(); 652 fReportCdataEvent = fIsCoalesce ? false : (fReportCdataEvent && true) ; 653 //if fIsCoalesce is set to true, set the value of fReplaceEntityReferences to true, 654 //if fIsCoalesce is set to false, take the value of fReplaceEntityReferences as set by application 655 fReplaceEntityReferences = fIsCoalesce ? true : fReplaceEntityReferences; 656 // setup Driver 657 //we dont need to do this -- nb. 658 //setScannerState(SCANNER_STATE_CONTENT); 659 //setDriver(fContentDriver); 660 fEntityStore = fEntityManager.getEntityStore(); 661 //fEntityManager.test(); 662 663 dtdGrammarUtil = null; 664 665 // Oracle jdk feature 666 fAccessExternalDTD = (String) propertyManager.getProperty(ACCESS_EXTERNAL_DTD); 667 668 } // reset(XMLComponentManager) 669 670 /** 671 * Returns a list of feature identifiers that are recognized by 672 * this component. This method may return null if no features 673 * are recognized by this component. 674 */ 675 public String[] getRecognizedFeatures() { 676 return (String[])(RECOGNIZED_FEATURES.clone()); 677 } // getRecognizedFeatures():String[] 678 679 /** 680 * Sets the state of a feature. This method is called by the component 681 * manager any time after reset when a feature changes state. 682 * <p> 683 * <strong>Note:</strong> Components should silently ignore features 684 * that do not affect the operation of the component. 685 * 686 * @param featureId The feature identifier. 687 * @param state The state of the feature. 688 * 689 * @throws SAXNotRecognizedException The component should not throw 690 * this exception. 691 * @throws SAXNotSupportedException The component should not throw 692 * this exception. 693 */ 694 public void setFeature(String featureId, boolean state) 695 throws XMLConfigurationException { 696 697 super.setFeature(featureId, state); 698 699 // Xerces properties 700 if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { 701 String feature = featureId.substring(Constants.XERCES_FEATURE_PREFIX.length()); 702 if (feature.equals(Constants.NOTIFY_BUILTIN_REFS_FEATURE)) { 703 fNotifyBuiltInRefs = state; 704 } 705 } 706 707 } // setFeature(String,boolean) 708 709 /** 710 * Returns a list of property identifiers that are recognized by 711 * this component. This method may return null if no properties 712 * are recognized by this component. 713 */ 714 public String[] getRecognizedProperties() { 715 return (String[])(RECOGNIZED_PROPERTIES.clone()); 716 } // getRecognizedProperties():String[] 717 718 /** 719 * Sets the value of a property. This method is called by the component 720 * manager any time after reset when a property changes value. 721 * <p> 722 * <strong>Note:</strong> Components should silently ignore properties 723 * that do not affect the operation of the component. 724 * 725 * @param propertyId The property identifier. 726 * @param value The value of the property. 727 * 728 * @throws SAXNotRecognizedException The component should not throw 729 * this exception. 730 * @throws SAXNotSupportedException The component should not throw 731 * this exception. 732 */ 733 public void setProperty(String propertyId, Object value) 734 throws XMLConfigurationException { 735 736 super.setProperty(propertyId, value); 737 738 // Xerces properties 739 if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { 740 final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length(); 741 if (suffixLength == Constants.ENTITY_MANAGER_PROPERTY.length() && 742 propertyId.endsWith(Constants.ENTITY_MANAGER_PROPERTY)) { 743 fEntityManager = (XMLEntityManager)value; 744 return; 745 } 746 if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() && 747 propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) { 748 fExternalSubsetResolver = (value instanceof ExternalSubsetResolver) ? 749 (ExternalSubsetResolver) value : null; 750 return; 751 } 752 } 753 754 755 // Xerces properties 756 if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { 757 String property = propertyId.substring(Constants.XERCES_PROPERTY_PREFIX.length()); 758 if (property.equals(Constants.ENTITY_MANAGER_PROPERTY)) { 759 fEntityManager = (XMLEntityManager)value; 760 } 761 return; 762 } 763 764 //JAXP 1.5 properties 765 if (propertyId.startsWith(Constants.JAXPAPI_PROPERTY_PREFIX)) { 766 if (propertyId.equals(ACCESS_EXTERNAL_DTD)) 767 { 768 fAccessExternalDTD = (String)value; 769 } 770 } 771 772 } // setProperty(String,Object) 773 774 /** 775 * Returns the default state for a feature, or null if this 776 * component does not want to report a default value for this 777 * feature. 778 * 779 * @param featureId The feature identifier. 780 * 781 * @since Xerces 2.2.0 782 */ 783 public Boolean getFeatureDefault(String featureId) { 784 for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { 785 if (RECOGNIZED_FEATURES[i].equals(featureId)) { 786 return FEATURE_DEFAULTS[i]; 787 } 788 } 789 return null; 790 } // getFeatureDefault(String):Boolean 791 792 /** 793 * Returns the default state for a property, or null if this 794 * component does not want to report a default value for this 795 * property. 796 * 797 * @param propertyId The property identifier. 798 * 799 * @since Xerces 2.2.0 800 */ 801 public Object getPropertyDefault(String propertyId) { 802 for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { 803 if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { 804 return PROPERTY_DEFAULTS[i]; 805 } 806 } 807 return null; 808 } // getPropertyDefault(String):Object 809 810 // 811 // XMLDocumentSource methods 812 // 813 814 /** 815 * setDocumentHandler 816 * 817 * @param documentHandler 818 */ 819 public void setDocumentHandler(XMLDocumentHandler documentHandler) { 820 fDocumentHandler = documentHandler; 821 //System.out.println(" In Set DOCUMENT HANDLER" + fDocumentHandler + " scanner =" + this); 822 } // setDocumentHandler(XMLDocumentHandler) 823 824 825 /** Returns the document handler */ 826 public XMLDocumentHandler getDocumentHandler(){ 827 return fDocumentHandler; 828 } 829 830 // 831 // XMLEntityHandler methods 832 // 833 834 /** 835 * This method notifies of the start of an entity. The DTD has the 836 * pseudo-name of "[dtd]" parameter entity names start with '%'; and 837 * general entities are just specified by their name. 838 * 839 * @param name The name of the entity. 840 * @param identifier The resource identifier. 841 * @param encoding The auto-detected IANA encoding name of the entity 842 * stream. This value will be null in those situations 843 * where the entity encoding is not auto-detected (e.g. 844 * internal entities or a document entity that is 845 * parsed from a java.io.Reader). 846 * @param augs Additional information that may include infoset augmentations 847 * 848 * @throws XNIException Thrown by handler to signal an error. 849 */ 850 public void startEntity(String name, 851 XMLResourceIdentifier identifier, 852 String encoding, Augmentations augs) throws XNIException { 853 854 // keep track of this entity before fEntityDepth is increased 855 if (fEntityDepth == fEntityStack.length) { 856 int[] entityarray = new int[fEntityStack.length * 2]; 857 System.arraycopy(fEntityStack, 0, entityarray, 0, fEntityStack.length); 858 fEntityStack = entityarray; 859 } 860 fEntityStack[fEntityDepth] = fMarkupDepth; 861 862 super.startEntity(name, identifier, encoding, augs); 863 864 // WFC: entity declared in external subset in standalone doc 865 if(fStandalone && fEntityStore.isEntityDeclInExternalSubset(name)) { 866 reportFatalError("MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE", 867 new Object[]{name}); 868 } 869 870 /** we are not calling the handlers yet.. */ 871 // call handler 872 if (fDocumentHandler != null && !fScanningAttribute) { 873 if (!name.equals("[xml]")) { 874 fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs); 875 } 876 } 877 878 } // startEntity(String,XMLResourceIdentifier,String) 879 880 /** 881 * This method notifies the end of an entity. The DTD has the pseudo-name 882 * of "[dtd]" parameter entity names start with '%'; and general entities 883 * are just specified by their name. 884 * 885 * @param name The name of the entity. 886 * @param augs Additional information that may include infoset augmentations 887 * 888 * @throws XNIException Thrown by handler to signal an error. 889 */ 890 public void endEntity(String name, Augmentations augs) throws IOException, XNIException { 891 892 /** 893 * // flush possible pending output buffer - see scanContent 894 * if (fInScanContent && fStringBuffer.length != 0 895 * && fDocumentHandler != null) { 896 * fDocumentHandler.characters(fStringBuffer, null); 897 * fStringBuffer.length = 0; // make sure we know it's been flushed 898 * } 899 */ 900 super.endEntity(name, augs); 901 902 // make sure markup is properly balanced 903 if (fMarkupDepth != fEntityStack[fEntityDepth]) { 904 reportFatalError("MarkupEntityMismatch", null); 905 } 906 907 /**/ 908 // call handler 909 if (fDocumentHandler != null && !fScanningAttribute) { 910 if (!name.equals("[xml]")) { 911 fDocumentHandler.endGeneralEntity(name, augs); 912 } 913 } 914 915 916 } // endEntity(String) 917 918 // 919 // Protected methods 920 // 921 922 // Driver factory methods 923 924 /** Creates a content Driver. */ 925 protected Driver createContentDriver() { 926 return new FragmentContentDriver(); 927 } // createContentDriver():Driver 928 929 // scanning methods 930 931 /** 932 * Scans an XML or text declaration. 933 * <p> 934 * <pre> 935 * [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' 936 * [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ") 937 * [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) 938 * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* 939 * [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") 940 * | ('"' ('yes' | 'no') '"')) 941 * 942 * [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>' 943 * </pre> 944 * 945 * @param scanningTextDecl True if a text declaration is to 946 * be scanned instead of an XML 947 * declaration. 948 */ 949 protected void scanXMLDeclOrTextDecl(boolean scanningTextDecl) 950 throws IOException, XNIException { 951 952 // scan decl 953 super.scanXMLDeclOrTextDecl(scanningTextDecl, fStrings); 954 fMarkupDepth--; 955 956 // pseudo-attribute values 957 String version = fStrings[0]; 958 String encoding = fStrings[1]; 959 String standalone = fStrings[2]; 960 fDeclaredEncoding = encoding; 961 // set standalone 962 fStandaloneSet = standalone != null; 963 fStandalone = fStandaloneSet && standalone.equals("yes"); 964 ///xxx see where its used.. this is not used anywhere. it may be useful for entity to store this information 965 //but this information is only related with Document Entity. 966 fEntityManager.setStandalone(fStandalone); 967 968 969 // call handler 970 if (fDocumentHandler != null) { 971 if (scanningTextDecl) { 972 fDocumentHandler.textDecl(version, encoding, null); 973 } else { 974 fDocumentHandler.xmlDecl(version, encoding, standalone, null); 975 } 976 } 977 978 if(version != null){ 979 fEntityScanner.setVersion(version); 980 fEntityScanner.setXMLVersion(version); 981 } 982 // set encoding on reader, only if encoding was not specified by the application explicitly 983 if (encoding != null && !fEntityScanner.getCurrentEntity().isEncodingExternallySpecified()) { 984 fEntityScanner.setEncoding(encoding); 985 } 986 987 } // scanXMLDeclOrTextDecl(boolean) 988 989 public String getPITarget(){ 990 return fPITarget ; 991 } 992 993 public XMLStringBuffer getPIData(){ 994 return fContentBuffer ; 995 } 996 997 //XXX: why not this function behave as per the state of the parser? 998 public XMLString getCharacterData(){ 999 if(fUsebuffer){ 1000 return fContentBuffer ; 1001 }else{ 1002 return fTempString; 1003 } 1004 1005 } 1006 1007 1008 /** 1009 * Scans a processing data. This is needed to handle the situation 1010 * where a document starts with a processing instruction whose 1011 * target name <em>starts with</em> "xml". (e.g. xmlfoo) 1012 * 1013 * @param target The PI target 1014 * @param data The XMLStringBuffer to fill in with the data 1015 */ 1016 protected void scanPIData(String target, XMLStringBuffer data) 1017 throws IOException, XNIException { 1018 1019 super.scanPIData(target, data); 1020 1021 //set the PI target and values 1022 fPITarget = target ; 1023 1024 fMarkupDepth--; 1025 1026 } // scanPIData(String) 1027 1028 /** 1029 * Scans a comment. 1030 * <p> 1031 * <pre> 1032 * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' 1033 * </pre> 1034 * <p> 1035 * <strong>Note:</strong> Called after scanning past '<!--' 1036 */ 1037 protected void scanComment() throws IOException, XNIException { 1038 fContentBuffer.clear(); 1039 scanComment(fContentBuffer); 1040 //getTextCharacters can also be called for reading comments 1041 fUsebuffer = true; 1042 fMarkupDepth--; 1043 1044 } // scanComment() 1045 1046 //xxx value returned by this function may not remain valid if another event is scanned. 1047 public String getComment(){ 1048 return fContentBuffer.toString(); 1049 } 1050 1051 void addElement(String rawname){ 1052 if(fElementPointer < ELEMENT_ARRAY_LENGTH){ 1053 //storing element raw name in a linear list of array 1054 fElementArray[fElementPointer] = rawname ; 1055 //storing elemnetPointer for particular element depth 1056 1057 if(DEBUG_SKIP_ALGORITHM){ 1058 StringBuffer sb = new StringBuffer() ; 1059 sb.append(" Storing element information ") ; 1060 sb.append(" fElementPointer = " + fElementPointer) ; 1061 sb.append(" fElementRawname = " + fElementQName.rawname) ; 1062 sb.append(" fElementStack.fDepth = " + fElementStack.fDepth); 1063 System.out.println(sb.toString()) ; 1064 } 1065 1066 //store pointer information only when element depth is less MAX_DEPTH_LIMIT 1067 if(fElementStack.fDepth < MAX_DEPTH_LIMIT){ 1068 short column = storePointerForADepth(fElementPointer); 1069 if(column > 0){ 1070 short pointer = getElementPointer((short)fElementStack.fDepth, (short)(column - 1) ); 1071 //identity comparison shouldn't take much time and we can rely on this 1072 //since its guaranteed to have same object id for same string. 1073 if(rawname == fElementArray[pointer]){ 1074 fShouldSkip = true ; 1075 fLastPointerLocation = pointer ; 1076 //reset the things and return. 1077 resetPointer((short)fElementStack.fDepth , column) ; 1078 fElementArray[fElementPointer] = null ; 1079 return ; 1080 }else{ 1081 fShouldSkip = false ; 1082 } 1083 } 1084 } 1085 fElementPointer++ ; 1086 } 1087 } 1088 1089 1090 void resetPointer(short depth, short column){ 1091 fPointerInfo[depth] [column] = (short)0; 1092 } 1093 1094 //returns column information at which pointer was stored. 1095 short storePointerForADepth(short elementPointer){ 1096 short depth = (short) fElementStack.fDepth ; 1097 1098 //Stores element pointer locations at particular depth , only 4 pointer locations 1099 //are stored at particular depth for now. 1100 for(short i = 0 ; i < MAX_POINTER_AT_A_DEPTH ; i++){ 1101 1102 if(canStore(depth, i)){ 1103 fPointerInfo[depth][i] = elementPointer ; 1104 if(DEBUG_SKIP_ALGORITHM){ 1105 StringBuffer sb = new StringBuffer() ; 1106 sb.append(" Pointer information ") ; 1107 sb.append(" fElementPointer = " + fElementPointer) ; 1108 sb.append(" fElementStack.fDepth = " + fElementStack.fDepth); 1109 sb.append(" column = " + i ) ; 1110 System.out.println(sb.toString()) ; 1111 } 1112 return i; 1113 } 1114 //else 1115 //pointer was not stored because we reached the limit 1116 } 1117 return -1 ; 1118 } 1119 1120 boolean canStore(short depth, short column){ 1121 //colum = 0 , means first element at particular depth 1122 //column = 1, means second element at particular depth 1123 // calle should make sure that it doesn't call for value outside allowed co-ordinates 1124 return fPointerInfo[depth][column] == 0 ? true : false ; 1125 } 1126 1127 1128 short getElementPointer(short depth, short column){ 1129 //colum = 0 , means first element at particular depth 1130 //column = 1, means second element at particular depth 1131 // calle should make sure that it doesn't call for value outside allowed co-ordinates 1132 return fPointerInfo[depth][column] ; 1133 } 1134 1135 //this function assumes that string passed is not null and skips 1136 //the following string from the buffer this makes sure 1137 boolean skipFromTheBuffer(String rawname) throws IOException{ 1138 if(fEntityScanner.skipString(rawname)){ 1139 char c = (char)fEntityScanner.peekChar() ; 1140 //If the start element was completely skipped we should encounter either ' '(space), 1141 //or '/' (in case of empty element) or '>' 1142 if( c == ' ' || c == '/' || c == '>'){ 1143 fElementRawname = rawname ; 1144 return true ; 1145 } else{ 1146 return false; 1147 } 1148 } else 1149 return false ; 1150 } 1151 1152 boolean skipQElement(String rawname) throws IOException{ 1153 1154 final int c = fEntityScanner.getChar(rawname.length()); 1155 //if this character is still valid element name -- this means string can't match 1156 if(XMLChar.isName(c)){ 1157 return false; 1158 }else{ 1159 return fEntityScanner.skipString(rawname); 1160 } 1161 } 1162 1163 protected boolean skipElement() throws IOException { 1164 1165 if(!fShouldSkip) return false ; 1166 1167 if(fLastPointerLocation != 0){ 1168 //Look at the next element stored in the array list.. we might just get a match. 1169 String rawname = fElementArray[fLastPointerLocation + 1] ; 1170 if(rawname != null && skipFromTheBuffer(rawname)){ 1171 fLastPointerLocation++ ; 1172 if(DEBUG_SKIP_ALGORITHM){ 1173 System.out.println("Element " + fElementRawname + " was SKIPPED at pointer location = " + fLastPointerLocation); 1174 } 1175 return true ; 1176 } else{ 1177 //reset it back to zero... we haven't got the correct subset yet. 1178 fLastPointerLocation = 0 ; 1179 1180 } 1181 } 1182 //xxx: we can put some logic here as from what column it should start looking 1183 //for now we always start at 0 1184 //fallback to tolerant algorithm, it would look for differnt element stored at different 1185 //depth and get us the pointer location. 1186 return fShouldSkip && skipElement((short)0); 1187 1188 } 1189 1190 //start of the column at which it should try searching 1191 boolean skipElement(short column) throws IOException { 1192 short depth = (short)fElementStack.fDepth ; 1193 1194 if(depth > MAX_DEPTH_LIMIT){ 1195 return fShouldSkip = false ; 1196 } 1197 for(short i = column ; i < MAX_POINTER_AT_A_DEPTH ; i++){ 1198 short pointer = getElementPointer(depth , i ) ; 1199 1200 if(pointer == 0){ 1201 return fShouldSkip = false ; 1202 } 1203 1204 if(fElementArray[pointer] != null && skipFromTheBuffer(fElementArray[pointer])){ 1205 if(DEBUG_SKIP_ALGORITHM){ 1206 System.out.println(); 1207 System.out.println("Element " + fElementRawname + " was SKIPPED at depth = " + fElementStack.fDepth + " column = " + column ); 1208 System.out.println(); 1209 } 1210 fLastPointerLocation = pointer ; 1211 return fShouldSkip = true ; 1212 } 1213 } 1214 return fShouldSkip = false ; 1215 } 1216 1217 /** 1218 * Scans a start element. This method will handle the binding of 1219 * namespace information and notifying the handler of the start 1220 * of the element. 1221 * <p> 1222 * <pre> 1223 * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' 1224 * [40] STag ::= '<' Name (S Attribute)* S? '>' 1225 * </pre> 1226 * <p> 1227 * <strong>Note:</strong> This method assumes that the leading 1228 * '<' character has been consumed. 1229 * <p> 1230 * <strong>Note:</strong> This method uses the fElementQName and 1231 * fAttributes variables. The contents of these variables will be 1232 * destroyed. The caller should copy important information out of 1233 * these variables before calling this method. 1234 * NB: Content in fAttributes is valid only till the state of the parser is XMLEvent.START_ELEMENT 1235 * 1236 * @return True if element is empty. (i.e. It matches 1237 * production [44]. 1238 */ 1239 // fElementQName will have the details of element just read.. 1240 // fAttributes will have the details of all the attributes. 1241 protected boolean scanStartElement() 1242 throws IOException, XNIException { 1243 1244 if (DEBUG_START_END_ELEMENT) System.out.println( this.getClass().toString() + ">>> scanStartElement()"); 1245 //when skipping is true and no more elements should be added 1246 if(fSkip && !fAdd){ 1247 //get the stored element -- if everything goes right this should match the 1248 //token in the buffer 1249 1250 QName name = fElementStack.getNext(); 1251 1252 if(DEBUG_SKIP_ALGORITHM){ 1253 System.out.println("Trying to skip String = " + name.rawname); 1254 } 1255 1256 //Be conservative -- if skipping fails -- stop. 1257 fSkip = fEntityScanner.skipString(name.rawname); 1258 1259 if(fSkip){ 1260 if(DEBUG_SKIP_ALGORITHM){ 1261 System.out.println("Element SUCESSFULLY skipped = " + name.rawname); 1262 } 1263 fElementStack.push(); 1264 fElementQName = name; 1265 }else{ 1266 //if skipping fails reposition the stack or fallback to normal way of processing 1267 fElementStack.reposition(); 1268 if(DEBUG_SKIP_ALGORITHM){ 1269 System.out.println("Element was NOT skipped, REPOSITIONING stack" ); 1270 } 1271 } 1272 } 1273 1274 //we are still at the stage of adding elements 1275 //the elements were not matched or 1276 //fSkip is not set to true 1277 if(!fSkip || fAdd){ 1278 //get the next element from the stack 1279 fElementQName = fElementStack.nextElement(); 1280 // name 1281 if (fNamespaces) { 1282 fEntityScanner.scanQName(fElementQName); 1283 } else { 1284 String name = fEntityScanner.scanName(); 1285 fElementQName.setValues(null, name, name, null); 1286 } 1287 1288 if(DEBUG)System.out.println("Element scanned in start element is " + fElementQName.toString()); 1289 if(DEBUG_SKIP_ALGORITHM){ 1290 if(fAdd){ 1291 System.out.println("Elements are being ADDED -- elemet added is = " + fElementQName.rawname + " at count = " + fElementStack.fCount); 1292 } 1293 } 1294 1295 } 1296 1297 //when the elements are being added , we need to check if we are set for skipping the elements 1298 if(fAdd){ 1299 //this sets the value of fAdd variable 1300 fElementStack.matchElement(fElementQName); 1301 } 1302 1303 1304 //xxx: We dont need another pointer, fCurrentElement, we can use fElementQName 1305 fCurrentElement = fElementQName; 1306 1307 String rawname = fElementQName.rawname; 1308 1309 fEmptyElement = false; 1310 1311 fAttributes.removeAllAttributes(); 1312 1313 if(!seekCloseOfStartTag()){ 1314 fReadingAttributes = true; 1315 fAttributeCacheUsedCount =0; 1316 fStringBufferIndex =0; 1317 fAddDefaultAttr = true; 1318 do { 1319 scanAttribute(fAttributes); 1320 if (fSecurityManager != null && fAttributes.getLength() > fElementAttributeLimit){ 1321 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 1322 "ElementAttributeLimit", 1323 new Object[]{rawname, new Integer(fAttributes.getLength()) }, 1324 XMLErrorReporter.SEVERITY_FATAL_ERROR ); 1325 } 1326 1327 } while (!seekCloseOfStartTag()); 1328 fReadingAttributes=false; 1329 } 1330 1331 if (fEmptyElement) { 1332 //decrease the markup depth.. 1333 fMarkupDepth--; 1334 1335 // check that this element was opened in the same entity 1336 if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { 1337 reportFatalError("ElementEntityMismatch", 1338 new Object[]{fCurrentElement.rawname}); 1339 } 1340 // call handler 1341 if (fDocumentHandler != null) { 1342 fDocumentHandler.emptyElement(fElementQName, fAttributes, null); 1343 } 1344 1345 //We should not be popping out the context here in endELement becaause the namespace context is still 1346 //valid when parser is at the endElement state. 1347 //if (fNamespaces) { 1348 // fNamespaceContext.popContext(); 1349 //} 1350 1351 //pop the element off the stack.. 1352 fElementStack.popElement(); 1353 1354 } else { 1355 1356 if(dtdGrammarUtil != null) 1357 dtdGrammarUtil.startElement(fElementQName, fAttributes); 1358 if(fDocumentHandler != null){ 1359 //complete element and attributes are traversed in this function so we can send a callback 1360 //here. 1361 //<strong>we shouldn't be sending callback in scanDocument()</strong> 1362 fDocumentHandler.startElement(fElementQName, fAttributes, null); 1363 } 1364 } 1365 1366 1367 if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() + "<<< scanStartElement(): "+fEmptyElement); 1368 return fEmptyElement; 1369 1370 } // scanStartElement():boolean 1371 1372 /** 1373 * Looks for the close of start tag, i.e. if it finds '>' or '/>' 1374 * Characters are consumed. 1375 */ 1376 protected boolean seekCloseOfStartTag() throws IOException, XNIException { 1377 // spaces 1378 boolean sawSpace = fEntityScanner.skipSpaces(); 1379 1380 // end tag? 1381 final int c = fEntityScanner.peekChar(); 1382 if (c == '>') { 1383 fEntityScanner.scanChar(); 1384 return true; 1385 } else if (c == '/') { 1386 fEntityScanner.scanChar(); 1387 if (!fEntityScanner.skipChar('>')) { 1388 reportFatalError("ElementUnterminated", 1389 new Object[]{fElementQName.rawname}); 1390 } 1391 fEmptyElement = true; 1392 return true; 1393 } else if (!isValidNameStartChar(c) || !sawSpace) { 1394 reportFatalError("ElementUnterminated", new Object[]{fElementQName.rawname}); 1395 } 1396 1397 return false; 1398 } 1399 1400 public boolean hasAttributes(){ 1401 return fAttributes.getLength() > 0 ? true : false ; 1402 } 1403 1404 1405 /** 1406 * Scans an attribute. 1407 * <p> 1408 * <pre> 1409 * [41] Attribute ::= Name Eq AttValue 1410 * </pre> 1411 * <p> 1412 * <strong>Note:</strong> This method assumes that the next 1413 * character on the stream is the first character of the attribute 1414 * name. 1415 * <p> 1416 * <strong>Note:</strong> This method uses the fAttributeQName and 1417 * fQName variables. The contents of these variables will be 1418 * destroyed. 1419 * 1420 * @param attributes The attributes list for the scanned attribute. 1421 */ 1422 1423 /** 1424 * protected void scanAttribute(AttributeIteratorImpl attributes) 1425 * throws IOException, XNIException { 1426 * if (DEBUG_START_END_ELEMENT) System.out.println(">>> scanAttribute()"); 1427 * 1428 * 1429 * // name 1430 * if (fNamespaces) { 1431 * fEntityScanner.scanQName(fAttributeQName); 1432 * } 1433 * else { 1434 * String name = fEntityScanner.scanName(); 1435 * fAttributeQName.setValues(null, name, name, null); 1436 * } 1437 * 1438 * // equals 1439 * fEntityScanner.skipSpaces(); 1440 * if (!fEntityScanner.skipChar('=')) { 1441 * reportFatalError("EqRequiredInAttribute", 1442 * new Object[]{fAttributeQName.rawname}); 1443 * } 1444 * fEntityScanner.skipSpaces(); 1445 * 1446 * 1447 * // content 1448 * int oldLen = attributes.getLength(); 1449 */ 1450 /**xxx there is one check of duplicate attribute that has been removed. 1451 * attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null); 1452 * 1453 * // WFC: Unique Att Spec 1454 * if (oldLen == attributes.getLength()) { 1455 * reportFatalError("AttributeNotUnique", 1456 * new Object[]{fCurrentElement.rawname, 1457 * fAttributeQName.rawname}); 1458 * } 1459 */ 1460 1461 /* 1462 //REVISIT: one more case needs to be included: external PE and standalone is no 1463 boolean isVC = fHasExternalDTD && !fStandalone; 1464 scanAttributeValue(fTempString, fTempString2, 1465 fAttributeQName.rawname, attributes, 1466 oldLen, isVC); 1467 1468 //attributes.setValue(oldLen, fTempString.toString()); 1469 //attributes.setNonNormalizedValue(oldLen, fTempString2.toString()); 1470 //attributes.setSpecified(oldLen, true); 1471 1472 AttributeImpl attribute = new AttributeImpl(fAttributeQName.prefix,fAttributeQName.localpart,fAttributeQName.uri,fTempString.toString(),fTempString2.toString(),XMLSymbols.fCDATASymbol,true); 1473 fAttributes.addAttribute(attribute); 1474 if (DEBUG_START_END_ELEMENT) System.out.println("<<< scanAttribute()"); 1475 } // scanAttribute(XMLAttributes) 1476 1477 */ 1478 1479 /** return the attribute iterator implementation */ 1480 public XMLAttributesIteratorImpl getAttributeIterator(){ 1481 if(dtdGrammarUtil != null && fAddDefaultAttr){ 1482 dtdGrammarUtil.addDTDDefaultAttrs(fElementQName,fAttributes); 1483 fAddDefaultAttr = false; 1484 } 1485 return fAttributes; 1486 } 1487 1488 /** return if standalone is set */ 1489 public boolean standaloneSet(){ 1490 return fStandaloneSet; 1491 } 1492 /** return if the doucment is standalone */ 1493 public boolean isStandAlone(){ 1494 return fStandalone ; 1495 } 1496 /** 1497 * Scans an attribute name value pair. 1498 * <p> 1499 * <pre> 1500 * [41] Attribute ::= Name Eq AttValue 1501 * </pre> 1502 * <p> 1503 * <strong>Note:</strong> This method assumes that the next 1504 * character on the stream is the first character of the attribute 1505 * name. 1506 * <p> 1507 * <strong>Note:</strong> This method uses the fAttributeQName and 1508 * fQName variables. The contents of these variables will be 1509 * destroyed. 1510 * 1511 * @param attributes The attributes list for the scanned attribute. 1512 */ 1513 1514 protected void scanAttribute(XMLAttributes attributes) 1515 throws IOException, XNIException { 1516 if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()"); 1517 1518 // name 1519 if (fNamespaces) { 1520 fEntityScanner.scanQName(fAttributeQName); 1521 } else { 1522 String name = fEntityScanner.scanName(); 1523 fAttributeQName.setValues(null, name, name, null); 1524 } 1525 1526 // equals 1527 fEntityScanner.skipSpaces(); 1528 if (!fEntityScanner.skipChar('=')) { 1529 reportFatalError("EqRequiredInAttribute", 1530 new Object[] {fCurrentElement.rawname, fAttributeQName.rawname}); 1531 } 1532 fEntityScanner.skipSpaces(); 1533 1534 int attIndex = 0 ; 1535 //REVISIT: one more case needs to be included: external PE and standalone is no 1536 boolean isVC = fHasExternalDTD && !fStandalone; 1537 //fTempString would store attribute value 1538 ///fTempString2 would store attribute non-normalized value 1539 1540 //this function doesn't use 'attIndex'. We are adding the attribute later 1541 //after we have figured out that current attribute is not namespace declaration 1542 //since scanAttributeValue doesn't use attIndex parameter therefore we 1543 //can safely add the attribute later.. 1544 XMLString tmpStr = getString(); 1545 1546 scanAttributeValue(tmpStr, fTempString2, 1547 fAttributeQName.rawname, attributes, 1548 attIndex, isVC); 1549 1550 // content 1551 int oldLen = attributes.getLength(); 1552 //if the attribute name already exists.. new value is replaced with old value 1553 attIndex = attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null); 1554 1555 // WFC: Unique Att Spec 1556 //attributes count will be same if the current attribute name already exists for this element name. 1557 //this means there are two duplicate attributes. 1558 if (oldLen == attributes.getLength()) { 1559 reportFatalError("AttributeNotUnique", 1560 new Object[]{fCurrentElement.rawname, 1561 fAttributeQName.rawname}); 1562 } 1563 1564 //tmpString contains attribute value 1565 //we are passing null as the attribute value 1566 attributes.setValue(attIndex, null, tmpStr); 1567 1568 ///xxx: nonNormalizedValue is not being set as it is not required by SAX & DOM 1569 //attributes.setNonNormalizedValue(oldLen, fTempString2.toString()); 1570 attributes.setSpecified(attIndex, true); 1571 1572 if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanAttribute()"); 1573 1574 } // scanAttribute(XMLAttributes) 1575 1576 /** 1577 * Scans element content. 1578 * 1579 * @return Returns the next character on the stream. 1580 */ 1581 //CHANGED: 1582 //EARLIER: scanContent() 1583 //NOW: scanContent(XMLStringBuffer) 1584 //It makes things easy if this functions takes XMLStringBuffer as parameter.. 1585 //this function appends the data to the buffer. 1586 protected int scanContent(XMLStringBuffer content) throws IOException, XNIException { 1587 //set the fTempString length to 0 before passing it on to scanContent 1588 //scanContent sets the correct co-ordinates as per the content read 1589 fTempString.length = 0; 1590 int c = fEntityScanner.scanContent(fTempString); 1591 content.append(fTempString); 1592 fTempString.length = 0; 1593 if (c == '\r') { 1594 // happens when there is the character reference 1595 //xxx: We know the next chracter.. we should just skip it and add ']' directlry 1596 fEntityScanner.scanChar(); 1597 content.append((char)c); 1598 c = -1; 1599 } else if (c == ']') { 1600 //fStringBuffer.clear(); 1601 //xxx: We know the next chracter.. we should just skip it and add ']' directlry 1602 content.append((char)fEntityScanner.scanChar()); 1603 // remember where we are in case we get an endEntity before we 1604 // could flush the buffer out - this happens when we're parsing an 1605 // entity which ends with a ] 1606 fInScanContent = true; 1607 // 1608 // We work on a single character basis to handle cases such as: 1609 // ']]]>' which we might otherwise miss. 1610 // 1611 if (fEntityScanner.skipChar(']')) { 1612 content.append(']'); 1613 while (fEntityScanner.skipChar(']')) { 1614 content.append(']'); 1615 } 1616 if (fEntityScanner.skipChar('>')) { 1617 reportFatalError("CDEndInContent", null); 1618 } 1619 } 1620 fInScanContent = false; 1621 c = -1; 1622 } 1623 if (fDocumentHandler != null && content.length > 0) { 1624 //fDocumentHandler.characters(content, null); 1625 } 1626 return c; 1627 1628 } // scanContent():int 1629 1630 1631 /** 1632 * Scans a CDATA section. 1633 * <p> 1634 * <strong>Note:</strong> This method uses the fTempString and 1635 * fStringBuffer variables. 1636 * 1637 * @param complete True if the CDATA section is to be scanned 1638 * completely. 1639 * 1640 * @return True if CDATA is completely scanned. 1641 */ 1642 //CHANGED: 1643 protected boolean scanCDATASection(XMLStringBuffer contentBuffer, boolean complete) 1644 throws IOException, XNIException { 1645 1646 // call handler 1647 if (fDocumentHandler != null) { 1648 //fDocumentHandler.startCDATA(null); 1649 } 1650 1651 while (true) { 1652 //scanData will fill the contentBuffer 1653 if (!fEntityScanner.scanData("]]>", contentBuffer)) { 1654 break ; 1655 /** We dont need all this code if we pass ']]>' as delimeter.. 1656 * int brackets = 2; 1657 * while (fEntityScanner.skipChar(']')) { 1658 * brackets++; 1659 * } 1660 * 1661 * //When we find more than 2 square brackets 1662 * if (fDocumentHandler != null && brackets > 2) { 1663 * //we dont need to clear the buffer.. 1664 * //contentBuffer.clear(); 1665 * for (int i = 2; i < brackets; i++) { 1666 * contentBuffer.append(']'); 1667 * } 1668 * fDocumentHandler.characters(contentBuffer, null); 1669 * } 1670 * 1671 * if (fEntityScanner.skipChar('>')) { 1672 * break; 1673 * } 1674 * if (fDocumentHandler != null) { 1675 * //we dont need to clear the buffer now.. 1676 * //contentBuffer.clear(); 1677 * contentBuffer.append("]]"); 1678 * fDocumentHandler.characters(contentBuffer, null); 1679 * } 1680 **/ 1681 } else { 1682 int c = fEntityScanner.peekChar(); 1683 if (c != -1 && isInvalidLiteral(c)) { 1684 if (XMLChar.isHighSurrogate(c)) { 1685 //contentBuffer.clear(); 1686 //scan surrogates if any.... 1687 scanSurrogates(contentBuffer); 1688 } else { 1689 reportFatalError("InvalidCharInCDSect", 1690 new Object[]{Integer.toString(c,16)}); 1691 fEntityScanner.scanChar(); 1692 } 1693 } 1694 //by this time we have also read surrogate contents if any... 1695 if (fDocumentHandler != null) { 1696 //fDocumentHandler.characters(contentBuffer, null); 1697 } 1698 } 1699 } 1700 fMarkupDepth--; 1701 1702 if (fDocumentHandler != null && contentBuffer.length > 0) { 1703 //fDocumentHandler.characters(contentBuffer, null); 1704 } 1705 1706 // call handler 1707 if (fDocumentHandler != null) { 1708 //fDocumentHandler.endCDATA(null); 1709 } 1710 1711 return true; 1712 1713 } // scanCDATASection(XMLStringBuffer, boolean):boolean 1714 1715 /** 1716 * Scans an end element. 1717 * <p> 1718 * <pre> 1719 * [42] ETag ::= '</' Name S? '>' 1720 * </pre> 1721 * <p> 1722 * <strong>Note:</strong> This method uses the fElementQName variable. 1723 * The contents of this variable will be destroyed. The caller should 1724 * copy the needed information out of this variable before calling 1725 * this method. 1726 * 1727 * @return The element depth. 1728 */ 1729 protected int scanEndElement() throws IOException, XNIException { 1730 if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanEndElement()"); 1731 1732 // pop context 1733 QName endElementName = fElementStack.popElement(); 1734 1735 String rawname = endElementName.rawname; 1736 if(DEBUG)System.out.println("endElementName = " + endElementName.toString()); 1737 // Take advantage of the fact that next string _should_ be "fElementQName.rawName", 1738 //In scanners most of the time is consumed on checks done for XML characters, we can 1739 // optimize on it and avoid the checks done for endElement, 1740 //we will also avoid symbol table lookup - neeraj.bajaj@sun.com 1741 1742 // this should work both for namespace processing true or false... 1743 1744 //REVISIT: if the string is not the same as expected.. we need to do better error handling.. 1745 //We can skip this for now... In any case if the string doesn't match -- document is not well formed. 1746 1747 if (!fEntityScanner.skipString(endElementName.rawname)) { 1748 reportFatalError("ETagRequired", new Object[]{rawname}); 1749 } 1750 1751 // end 1752 fEntityScanner.skipSpaces(); 1753 if (!fEntityScanner.skipChar('>')) { 1754 reportFatalError("ETagUnterminated", 1755 new Object[]{rawname}); 1756 } 1757 fMarkupDepth--; 1758 1759 //we have increased the depth for two markup "<" characters 1760 fMarkupDepth--; 1761 1762 // check that this element was opened in the same entity 1763 if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { 1764 reportFatalError("ElementEntityMismatch", 1765 new Object[]{rawname}); 1766 } 1767 1768 //We should not be popping out the context here in endELement becaause the namespace context is still 1769 //valid when parser is at the endElement state. 1770 1771 //if (fNamespaces) { 1772 // fNamespaceContext.popContext(); 1773 //} 1774 1775 // call handler 1776 if (fDocumentHandler != null ) { 1777 //end element is scanned in this function so we can send a callback 1778 //here. 1779 //<strong>we shouldn't be sending callback in scanDocument()</strong> 1780 1781 fDocumentHandler.endElement(endElementName, null); 1782 } 1783 if(dtdGrammarUtil != null) 1784 dtdGrammarUtil.endElement(endElementName); 1785 1786 return fMarkupDepth; 1787 1788 } // scanEndElement():int 1789 1790 /** 1791 * Scans a character reference. 1792 * <p> 1793 * <pre> 1794 * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';' 1795 * </pre> 1796 */ 1797 protected void scanCharReference() 1798 throws IOException, XNIException { 1799 1800 fStringBuffer2.clear(); 1801 int ch = scanCharReferenceValue(fStringBuffer2, null); 1802 fMarkupDepth--; 1803 if (ch != -1) { 1804 // call handler 1805 1806 if (fDocumentHandler != null) { 1807 if (fNotifyCharRefs) { 1808 fDocumentHandler.startGeneralEntity(fCharRefLiteral, null, null, null); 1809 } 1810 Augmentations augs = null; 1811 if (fValidation && ch <= 0x20) { 1812 if (fTempAugmentations != null) { 1813 fTempAugmentations.removeAllItems(); 1814 } 1815 else { 1816 fTempAugmentations = new AugmentationsImpl(); 1817 } 1818 augs = fTempAugmentations; 1819 augs.putItem(Constants.CHAR_REF_PROBABLE_WS, Boolean.TRUE); 1820 } 1821 //xxx: How do we deal with this - how to return charReferenceValues 1822 //now this is being commented because this is taken care in scanDocument() 1823 //fDocumentHandler.characters(fStringBuffer2, null); 1824 if (fNotifyCharRefs) { 1825 fDocumentHandler.endGeneralEntity(fCharRefLiteral, null); 1826 } 1827 } 1828 } 1829 1830 } // scanCharReference() 1831 1832 1833 /** 1834 * Scans an entity reference. 1835 * 1836 * @return returns true if the new entity is started. If it was built-in entity 1837 * 'false' is returned. 1838 * @throws IOException Thrown if i/o error occurs. 1839 * @throws XNIException Thrown if handler throws exception upon 1840 * notification. 1841 */ 1842 protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException { 1843 String name = fEntityScanner.scanName(); 1844 if (name == null) { 1845 reportFatalError("NameRequiredInReference", null); 1846 return; 1847 } 1848 if (!fEntityScanner.skipChar(';')) { 1849 reportFatalError("SemicolonRequiredInReference", new Object []{name}); 1850 } 1851 if (fEntityStore.isUnparsedEntity(name)) { 1852 reportFatalError("ReferenceToUnparsedEntity", new Object[]{name}); 1853 } 1854 fMarkupDepth--; 1855 fCurrentEntityName = name; 1856 1857 // handle built-in entities 1858 if (name == fAmpSymbol) { 1859 handleCharacter('&', fAmpSymbol, content); 1860 fScannerState = SCANNER_STATE_BUILT_IN_REFS; 1861 return ; 1862 } else if (name == fLtSymbol) { 1863 handleCharacter('<', fLtSymbol, content); 1864 fScannerState = SCANNER_STATE_BUILT_IN_REFS; 1865 return ; 1866 } else if (name == fGtSymbol) { 1867 handleCharacter('>', fGtSymbol, content); 1868 fScannerState = SCANNER_STATE_BUILT_IN_REFS; 1869 return ; 1870 } else if (name == fQuotSymbol) { 1871 handleCharacter('"', fQuotSymbol, content); 1872 fScannerState = SCANNER_STATE_BUILT_IN_REFS; 1873 return ; 1874 } else if (name == fAposSymbol) { 1875 handleCharacter('\'', fAposSymbol, content); 1876 fScannerState = SCANNER_STATE_BUILT_IN_REFS; 1877 return ; 1878 } 1879 1880 //1. if the entity is external and support to external entities is not required 1881 // 2. or entities should not be replaced 1882 //3. or if it is built in entity reference. 1883 boolean isEE = fEntityStore.isExternalEntity(name); 1884 if((isEE && !fSupportExternalEntities) || (!isEE && !fReplaceEntityReferences) || foundBuiltInRefs){ 1885 fScannerState = SCANNER_STATE_REFERENCE; 1886 return ; 1887 } 1888 // start general entity 1889 if (!fEntityStore.isDeclaredEntity(name)) { 1890 //SUPPORT_DTD=false && ReplaceEntityReferences should throw exception 1891 if (!fSupportDTD && fReplaceEntityReferences) { 1892 reportFatalError("EntityNotDeclared", new Object[]{name}); 1893 return; 1894 } 1895 //REVISIT: one more case needs to be included: external PE and standalone is no 1896 if ( fHasExternalDTD && !fStandalone) { 1897 if (fValidation) 1898 fErrorReporter.reportError(fEntityScanner, XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared", 1899 new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR); 1900 } else 1901 reportFatalError("EntityNotDeclared", new Object[]{name}); 1902 } 1903 //we are starting the entity even if the entity was not declared 1904 //if that was the case it its taken care in XMLEntityManager.startEntity() 1905 //we immediately call the endEntity. Application gets to know if there was 1906 //any entity that was not declared. 1907 fEntityManager.startEntity(name, false); 1908 //set the scaner state to content.. parser will automatically revive itself at any point of time. 1909 //setScannerState(SCANNER_STATE_CONTENT); 1910 //return true ; 1911 } // scanEntityReference() 1912 1913 // utility methods 1914 1915 /** 1916 * Calls document handler with a single character resulting from 1917 * built-in entity resolution. 1918 * 1919 * @param c 1920 * @param entity built-in name 1921 * @param XMLStringBuffer append the character to buffer 1922 * 1923 * we really dont need to call this function -- this function is only required when 1924 * we integrate with rest of Xerces2. SO maintaining the current behavior and still 1925 * calling this function to hanlde built-in entity reference. 1926 * 1927 */ 1928 private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException { 1929 foundBuiltInRefs = true; 1930 content.append(c); 1931 if (fDocumentHandler != null) { 1932 fSingleChar[0] = c; 1933 if (fNotifyBuiltInRefs) { 1934 fDocumentHandler.startGeneralEntity(entity, null, null, null); 1935 } 1936 fTempString.setValues(fSingleChar, 0, 1); 1937 //fDocumentHandler.characters(fTempString, null); 1938 1939 if (fNotifyBuiltInRefs) { 1940 fDocumentHandler.endGeneralEntity(entity, null); 1941 } 1942 } 1943 } // handleCharacter(char) 1944 1945 // helper methods 1946 1947 /** 1948 * Sets the scanner state. 1949 * 1950 * @param state The new scanner state. 1951 */ 1952 protected final void setScannerState(int state) { 1953 1954 fScannerState = state; 1955 if (DEBUG_SCANNER_STATE) { 1956 System.out.print("### setScannerState: "); 1957 //System.out.print(fScannerState); 1958 System.out.print(getScannerStateName(state)); 1959 System.out.println(); 1960 } 1961 1962 } // setScannerState(int) 1963 1964 1965 /** 1966 * Sets the Driver. 1967 * 1968 * @param Driver The new Driver. 1969 */ 1970 protected final void setDriver(Driver driver) { 1971 fDriver = driver; 1972 if (DEBUG_DISPATCHER) { 1973 System.out.print("%%% setDriver: "); 1974 System.out.print(getDriverName(driver)); 1975 System.out.println(); 1976 } 1977 } 1978 1979 // 1980 // Private methods 1981 // 1982 1983 /** Returns the scanner state name. */ 1984 protected String getScannerStateName(int state) { 1985 1986 switch (state) { 1987 case SCANNER_STATE_DOCTYPE: return "SCANNER_STATE_DOCTYPE"; 1988 case SCANNER_STATE_ROOT_ELEMENT: return "SCANNER_STATE_ROOT_ELEMENT"; 1989 case SCANNER_STATE_START_OF_MARKUP: return "SCANNER_STATE_START_OF_MARKUP"; 1990 case SCANNER_STATE_COMMENT: return "SCANNER_STATE_COMMENT"; 1991 case SCANNER_STATE_PI: return "SCANNER_STATE_PI"; 1992 case SCANNER_STATE_CONTENT: return "SCANNER_STATE_CONTENT"; 1993 case SCANNER_STATE_REFERENCE: return "SCANNER_STATE_REFERENCE"; 1994 case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT"; 1995 case SCANNER_STATE_TERMINATED: return "SCANNER_STATE_TERMINATED"; 1996 case SCANNER_STATE_CDATA: return "SCANNER_STATE_CDATA"; 1997 case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL"; 1998 case SCANNER_STATE_ATTRIBUTE: return "SCANNER_STATE_ATTRIBUTE"; 1999 case SCANNER_STATE_ATTRIBUTE_VALUE: return "SCANNER_STATE_ATTRIBUTE_VALUE"; 2000 case SCANNER_STATE_START_ELEMENT_TAG: return "SCANNER_STATE_START_ELEMENT_TAG"; 2001 case SCANNER_STATE_END_ELEMENT_TAG: return "SCANNER_STATE_END_ELEMENT_TAG"; 2002 case SCANNER_STATE_CHARACTER_DATA: return "SCANNER_STATE_CHARACTER_DATA" ; 2003 } 2004 2005 return "??? ("+state+')'; 2006 2007 } // getScannerStateName(int):String 2008 public String getEntityName(){ 2009 //return the cached name 2010 return fCurrentEntityName; 2011 } 2012 2013 /** Returns the driver name. */ 2014 public String getDriverName(Driver driver) { 2015 2016 if (DEBUG_DISPATCHER) { 2017 if (driver != null) { 2018 String name = driver.getClass().getName(); 2019 int index = name.lastIndexOf('.'); 2020 if (index != -1) { 2021 name = name.substring(index + 1); 2022 index = name.lastIndexOf('$'); 2023 if (index != -1) { 2024 name = name.substring(index + 1); 2025 } 2026 } 2027 return name; 2028 } 2029 } 2030 return "null"; 2031 2032 } // getDriverName():String 2033 2034 String checkAccess(String systemId, String allowedProtocols) throws IOException { 2035 String baseSystemId = fEntityScanner.getBaseSystemId(); 2036 String expandedSystemId = fEntityManager.expandSystemId(systemId, baseSystemId,fStrictURI); 2037 return SecuritySupport.checkAccess(expandedSystemId, allowedProtocols, Constants.ACCESS_EXTERNAL_ALL); 2038 } 2039 2040 // 2041 // Classes 2042 // 2043 2044 /** 2045 * @author Neeraj Bajaj, Sun Microsystems. 2046 */ 2047 protected static final class Element { 2048 2049 // 2050 // Data 2051 // 2052 2053 /** Symbol. */ 2054 public QName qname; 2055 2056 //raw name stored as characters 2057 public char[] fRawname; 2058 2059 /** The next Element entry. */ 2060 public Element next; 2061 2062 // 2063 // Constructors 2064 // 2065 2066 /** 2067 * Constructs a new Element from the given QName and next Element 2068 * reference. 2069 */ 2070 public Element(QName qname, Element next) { 2071 this.qname.setValues(qname); 2072 this.fRawname = qname.rawname.toCharArray(); 2073 this.next = next; 2074 } 2075 2076 } // class Element 2077 2078 /** 2079 * Element stack. 2080 * 2081 * @author Neeraj Bajaj, Sun Microsystems. 2082 */ 2083 protected class ElementStack2 { 2084 2085 // 2086 // Data 2087 // 2088 2089 /** The stack data. */ 2090 protected QName [] fQName = new QName[20]; 2091 2092 //Element depth 2093 protected int fDepth; 2094 //total number of elements 2095 protected int fCount; 2096 //current position 2097 protected int fPosition; 2098 //Mark refers to the position 2099 protected int fMark; 2100 2101 protected int fLastDepth ; 2102 2103 // 2104 // Constructors 2105 // 2106 2107 /** Default constructor. */ 2108 public ElementStack2() { 2109 for (int i = 0; i < fQName.length; i++) { 2110 fQName[i] = new QName(); 2111 } 2112 fMark = fPosition = 1; 2113 } // <init>() 2114 2115 public void resize(){ 2116 /** 2117 * int length = fElements.length; 2118 * Element [] temp = new Element[length * 2]; 2119 * System.arraycopy(fElements, 0, temp, 0, length); 2120 * fElements = temp; 2121 */ 2122 //resize QNames 2123 int oldLength = fQName.length; 2124 QName [] tmp = new QName[oldLength * 2]; 2125 System.arraycopy(fQName, 0, tmp, 0, oldLength); 2126 fQName = tmp; 2127 2128 for (int i = oldLength; i < fQName.length; i++) { 2129 fQName[i] = new QName(); 2130 } 2131 2132 } 2133 2134 2135 // 2136 // Public methods 2137 // 2138 2139 /** Check if the element scanned during the start element 2140 *matches the stored element. 2141 * 2142 *@return true if the match suceeds. 2143 */ 2144 public boolean matchElement(QName element) { 2145 //last depth is the depth when last elemnt was pushed 2146 //if last depth is greater than current depth 2147 if(DEBUG_SKIP_ALGORITHM){ 2148 System.out.println("fLastDepth = " + fLastDepth); 2149 System.out.println("fDepth = " + fDepth); 2150 } 2151 boolean match = false; 2152 if(fLastDepth > fDepth && fDepth <= 2){ 2153 if(DEBUG_SKIP_ALGORITHM){ 2154 System.out.println("Checking if the elements match " + element.rawname + " , " + fQName[fDepth].rawname); 2155 } 2156 if(element.rawname == fQName[fDepth].rawname){ 2157 fAdd = false; 2158 //mark this position 2159 //decrease the depth by 1 as arrays are 0 based 2160 fMark = fDepth - 1; 2161 //we found the match and from next element skipping will start, add 1 2162 fPosition = fMark + 1 ; 2163 match = true; 2164 //Once we get match decrease the count -- this was increased by nextElement() 2165 --fCount; 2166 if(DEBUG_SKIP_ALGORITHM){ 2167 System.out.println("fAdd FALSE -- NOW ELEMENT SHOULD NOT BE ADDED"); 2168 System.out.println("fMark = " + fMark); 2169 System.out.println("fPosition = " + fPosition); 2170 System.out.println("fDepth = " + fDepth); 2171 System.out.println("fCount = " + fCount); 2172 } 2173 }else{ 2174 fAdd = true; 2175 if(DEBUG_SKIP_ALGORITHM)System.out.println("fAdd is " + fAdd); 2176 } 2177 } 2178 //store the last depth 2179 fLastDepth = fDepth++; 2180 return match; 2181 } // pushElement(QName):QName 2182 2183 /** 2184 * This function doesn't increase depth. The function in this function is 2185 *broken down into two functions for efficiency. <@see>matchElement</see>. 2186 * This function just returns the pointer to the object and its values are set. 2187 * 2188 *@return QName reference to the next element in the list 2189 */ 2190 public QName nextElement() { 2191 2192 //if number of elements becomes equal to the length of array -- stop the skipping 2193 if (fCount == fQName.length) { 2194 fShouldSkip = false; 2195 fAdd = false; 2196 if(DEBUG_SKIP_ALGORITHM)System.out.println("SKIPPING STOPPED, fShouldSkip = " + fShouldSkip); 2197 //xxx: this is not correct, we are returning the last element 2198 //this wont make any difference since flag has been set to 'false' 2199 return fQName[--fCount]; 2200 } 2201 if(DEBUG_SKIP_ALGORITHM){ 2202 System.out.println("fCount = " + fCount); 2203 } 2204 return fQName[fCount++]; 2205 2206 } 2207 2208 /** Note that this function is considerably different than nextElement() 2209 * This function just returns the previously stored elements 2210 */ 2211 public QName getNext(){ 2212 //when position reaches number of elements in the list.. 2213 //set the position back to mark, making it a circular linked list. 2214 if(fPosition == fCount){ 2215 fPosition = fMark; 2216 } 2217 return fQName[fPosition++]; 2218 } 2219 2220 /** returns the current depth 2221 */ 2222 public int popElement(){ 2223 return fDepth--; 2224 } 2225 2226 2227 /** Clears the stack without throwing away existing QName objects. */ 2228 public void clear() { 2229 fLastDepth = 0; 2230 fDepth = 0; 2231 fCount = 0 ; 2232 fPosition = fMark = 1; 2233 } // clear() 2234 2235 } // class ElementStack 2236 2237 /** 2238 * Element stack. This stack operates without synchronization, error 2239 * checking, and it re-uses objects instead of throwing popped items 2240 * away. 2241 * 2242 * @author Andy Clark, IBM 2243 */ 2244 protected class ElementStack { 2245 2246 // 2247 // Data 2248 // 2249 2250 /** The stack data. */ 2251 protected QName[] fElements; 2252 protected int [] fInt = new int[20]; 2253 2254 2255 //Element depth 2256 protected int fDepth; 2257 //total number of elements 2258 protected int fCount; 2259 //current position 2260 protected int fPosition; 2261 //Mark refers to the position 2262 protected int fMark; 2263 2264 protected int fLastDepth ; 2265 2266 // 2267 // Constructors 2268 // 2269 2270 /** Default constructor. */ 2271 public ElementStack() { 2272 fElements = new QName[20]; 2273 for (int i = 0; i < fElements.length; i++) { 2274 fElements[i] = new QName(); 2275 } 2276 } // <init>() 2277 2278 // 2279 // Public methods 2280 // 2281 2282 /** 2283 * Pushes an element on the stack. 2284 * <p> 2285 * <strong>Note:</strong> The QName values are copied into the 2286 * stack. In other words, the caller does <em>not</em> orphan 2287 * the element to the stack. Also, the QName object returned 2288 * is <em>not</em> orphaned to the caller. It should be 2289 * considered read-only. 2290 * 2291 * @param element The element to push onto the stack. 2292 * 2293 * @return Returns the actual QName object that stores the 2294 */ 2295 //XXX: THIS FUNCTION IS NOT USED 2296 public QName pushElement(QName element) { 2297 if (fDepth == fElements.length) { 2298 QName[] array = new QName[fElements.length * 2]; 2299 System.arraycopy(fElements, 0, array, 0, fDepth); 2300 fElements = array; 2301 for (int i = fDepth; i < fElements.length; i++) { 2302 fElements[i] = new QName(); 2303 } 2304 } 2305 fElements[fDepth].setValues(element); 2306 return fElements[fDepth++]; 2307 } // pushElement(QName):QName 2308 2309 2310 /** Note that this function is considerably different than nextElement() 2311 * This function just returns the previously stored elements 2312 */ 2313 public QName getNext(){ 2314 //when position reaches number of elements in the list.. 2315 //set the position back to mark, making it a circular linked list. 2316 if(fPosition == fCount){ 2317 fPosition = fMark; 2318 } 2319 //store the position of last opened tag at particular depth 2320 //fInt[++fDepth] = fPosition; 2321 if(DEBUG_SKIP_ALGORITHM){ 2322 System.out.println("Element at fPosition = " + fPosition + " is " + fElements[fPosition].rawname); 2323 } 2324 //return fElements[fPosition++]; 2325 return fElements[fPosition]; 2326 } 2327 2328 /** This function should be called only when element was skipped sucessfully. 2329 * 1. Increase the depth - because element was sucessfully skipped. 2330 *2. Store the position of the element token in array "last opened tag" at depth. 2331 *3. increase the position counter so as to point to the next element in the array 2332 */ 2333 public void push(){ 2334 2335 fInt[++fDepth] = fPosition++; 2336 } 2337 2338 /** Check if the element scanned during the start element 2339 *matches the stored element. 2340 * 2341 *@return true if the match suceeds. 2342 */ 2343 public boolean matchElement(QName element) { 2344 //last depth is the depth when last elemnt was pushed 2345 //if last depth is greater than current depth 2346 //if(DEBUG_SKIP_ALGORITHM){ 2347 // System.out.println("Check if the element " + element.rawname + " matches"); 2348 // System.out.println("fLastDepth = " + fLastDepth); 2349 // System.out.println("fDepth = " + fDepth); 2350 //} 2351 boolean match = false; 2352 if(fLastDepth > fDepth && fDepth <= 3){ 2353 if(DEBUG_SKIP_ALGORITHM){ 2354 System.out.println("----------ENTERED THE LOOP WHERE WE CHECK FOR MATCHING OF ELMENT-----"); 2355 System.out.println("Depth = " + fDepth + " Checking if INCOMING element " + element.rawname + " match STORED ELEMENT " + fElements[fDepth - 1].rawname); 2356 } 2357 if(element.rawname == fElements[fDepth - 1].rawname){ 2358 fAdd = false; 2359 //mark this position 2360 //decrease the depth by 1 as arrays are 0 based 2361 fMark = fDepth - 1; 2362 //we found the match 2363 fPosition = fMark; 2364 match = true; 2365 //Once we get match decrease the count -- this was increased by nextElement() 2366 --fCount; 2367 if(DEBUG_SKIP_ALGORITHM){ 2368 System.out.println("NOW ELEMENT SHOULD NOT BE ADDED, fAdd is set to false"); 2369 System.out.println("fMark = " + fMark); 2370 System.out.println("fPosition = " + fPosition); 2371 System.out.println("fDepth = " + fDepth); 2372 System.out.println("fCount = " + fCount); 2373 System.out.println("---------MATCH SUCEEDED-----------------"); 2374 System.out.println(""); 2375 } 2376 }else{ 2377 fAdd = true; 2378 if(DEBUG_SKIP_ALGORITHM)System.out.println("fAdd is " + fAdd); 2379 } 2380 } 2381 //store the position for the current depth 2382 //when we are adding the elements, when skipping 2383 //starts even then this should be tracked ie. when 2384 //calling getNext() 2385 if(match){ 2386 //from next element skipping will start, add 1 2387 fInt[fDepth] = fPosition++; 2388 } else{ 2389 if(DEBUG_SKIP_ALGORITHM){ 2390 System.out.println("At depth = " + fDepth + "array position is = " + (fCount - 1)); 2391 } 2392 //sicne fInt[fDepth] contains pointer to the element array which are 0 based. 2393 fInt[fDepth] = fCount - 1; 2394 } 2395 2396 //if number of elements becomes equal to the length of array -- stop the skipping 2397 //xxx: should we do "fCount == fInt.length" 2398 if (fCount == fElements.length) { 2399 fSkip = false; 2400 fAdd = false; 2401 //reposition the stack -- it seems to be too complex document and there is no symmerty in structure 2402 reposition(); 2403 if(DEBUG_SKIP_ALGORITHM){ 2404 System.out.println("ALL THE ELMENTS IN ARRAY HAVE BEEN FILLED"); 2405 System.out.println("REPOSITIONING THE STACK"); 2406 System.out.println("-----------SKIPPING STOPPED----------"); 2407 System.out.println(""); 2408 } 2409 return false; 2410 } 2411 if(DEBUG_SKIP_ALGORITHM){ 2412 if(match){ 2413 System.out.println("Storing fPosition = " + fInt[fDepth] + " at fDepth = " + fDepth); 2414 }else{ 2415 System.out.println("Storing fCount = " + fInt[fDepth] + " at fDepth = " + fDepth); 2416 } 2417 } 2418 //store the last depth 2419 fLastDepth = fDepth; 2420 return match; 2421 } // matchElement(QName):QName 2422 2423 2424 /** 2425 * Returns the next element on the stack. 2426 * 2427 * @return Returns the actual QName object. Callee should 2428 * use this object to store the details of next element encountered. 2429 */ 2430 public QName nextElement() { 2431 if(fSkip){ 2432 fDepth++; 2433 //boundary checks are done in matchElement() 2434 return fElements[fCount++]; 2435 } else if (fDepth == fElements.length) { 2436 QName[] array = new QName[fElements.length * 2]; 2437 System.arraycopy(fElements, 0, array, 0, fDepth); 2438 fElements = array; 2439 for (int i = fDepth; i < fElements.length; i++) { 2440 fElements[i] = new QName(); 2441 } 2442 } 2443 2444 return fElements[fDepth++]; 2445 2446 } // pushElement(QName):QName 2447 2448 2449 /** 2450 * Pops an element off of the stack by setting the values of 2451 * the specified QName. 2452 * <p> 2453 * <strong>Note:</strong> The object returned is <em>not</em> 2454 * orphaned to the caller. Therefore, the caller should consider 2455 * the object to be read-only. 2456 */ 2457 public QName popElement() { 2458 //return the same object that was pushed -- this would avoid 2459 //setting the values for every end element. 2460 //STRONG: this object is read only -- this object reference shouldn't be stored. 2461 if(fSkip || fAdd ){ 2462 if(DEBUG_SKIP_ALGORITHM){ 2463 System.out.println("POPPING Element, at position " + fInt[fDepth] + " element at that count is = " + fElements[fInt[fDepth]].rawname); 2464 System.out.println(""); 2465 } 2466 return fElements[fInt[fDepth--]]; 2467 } else{ 2468 if(DEBUG_SKIP_ALGORITHM){ 2469 System.out.println("Retrieveing element at depth = " + fDepth + " is " + fElements[fDepth].rawname ); 2470 } 2471 return fElements[--fDepth] ; 2472 } 2473 //element.setValues(fElements[--fDepth]); 2474 } // popElement(QName) 2475 2476 /** Reposition the stack. fInt [] contains all the opened tags at particular depth. 2477 * Transfer all the opened tags starting from depth '2' to the current depth and reposition them 2478 *as per the depth. 2479 */ 2480 public void reposition(){ 2481 for( int i = 2 ; i <= fDepth ; i++){ 2482 fElements[i-1] = fElements[fInt[i]]; 2483 } 2484 if(DEBUG_SKIP_ALGORITHM){ 2485 for( int i = 0 ; i < fDepth ; i++){ 2486 System.out.println("fElements[" + i + "]" + " = " + fElements[i].rawname); 2487 } 2488 } 2489 } 2490 2491 /** Clears the stack without throwing away existing QName objects. */ 2492 public void clear() { 2493 fDepth = 0; 2494 fLastDepth = 0; 2495 fCount = 0 ; 2496 fPosition = fMark = 1; 2497 2498 } // clear() 2499 2500 /** 2501 * This function is as a result of optimization done for endElement -- 2502 * we dont need to set the value for every end element encouterd. 2503 * For Well formedness checks we can have the same QName object that was pushed. 2504 * the values will be set only if application need to know about the endElement 2505 * -- neeraj.bajaj@sun.com 2506 */ 2507 2508 public QName getLastPoppedElement(){ 2509 return fElements[fDepth]; 2510 } 2511 } // class ElementStack 2512 2513 /** 2514 * Drives the parser to the next state/event on the input. Parser is guaranteed 2515 * to stop at the next state/event. 2516 * 2517 * Internally XML document is divided into several states. Each state represents 2518 * a sections of XML document. When this functions returns normally, it has read 2519 * the section of XML document and returns the state corresponding to section of 2520 * document which has been read. For optimizations, a particular driver 2521 * can read ahead of the section of document (state returned) just read and 2522 * can maintain a different internal state. 2523 * 2524 * 2525 * @author Neeraj Bajaj, Sun Microsystems 2526 */ 2527 protected interface Driver { 2528 2529 2530 /** 2531 * Drives the parser to the next state/event on the input. Parser is guaranteed 2532 * to stop at the next state/event. 2533 * 2534 * Internally XML document is divided into several states. Each state represents 2535 * a sections of XML document. When this functions returns normally, it has read 2536 * the section of XML document and returns the state corresponding to section of 2537 * document which has been read. For optimizations, a particular driver 2538 * can read ahead of the section of document (state returned) just read and 2539 * can maintain a different internal state. 2540 * 2541 * @return state representing the section of document just read. 2542 * 2543 * @throws IOException Thrown on i/o error. 2544 * @throws XNIException Thrown on parse error. 2545 */ 2546 2547 public int next() throws IOException, XNIException; 2548 2549 } // interface Driver 2550 2551 /** 2552 * Driver to handle content scanning. This driver is capable of reading 2553 * the fragment of XML document. When it has finished reading fragment 2554 * of XML documents, it can pass the job of reading to another driver. 2555 * 2556 * This class has been modified as per the new design which is more suited to 2557 * efficiently build pull parser. Lot of performance improvements have been done and 2558 * the code has been added to support stax functionality/features. 2559 * 2560 * @author Neeraj Bajaj, Sun Microsystems 2561 * 2562 * 2563 * @author Andy Clark, IBM 2564 * @author Eric Ye, IBM 2565 */ 2566 protected class FragmentContentDriver 2567 implements Driver { 2568 2569 // 2570 // Driver methods 2571 // 2572 private boolean fContinueDispatching = true; 2573 private boolean fScanningForMarkup = true; 2574 2575 /** 2576 * decides the appropriate state of the parser 2577 */ 2578 private void startOfMarkup() throws IOException { 2579 fMarkupDepth++; 2580 final int ch = fEntityScanner.peekChar(); 2581 2582 switch(ch){ 2583 case '?' :{ 2584 setScannerState(SCANNER_STATE_PI); 2585 fEntityScanner.skipChar(ch); 2586 break; 2587 } 2588 case '!' :{ 2589 fEntityScanner.skipChar(ch); 2590 if (fEntityScanner.skipChar('-')) { 2591 if (!fEntityScanner.skipChar('-')) { 2592 reportFatalError("InvalidCommentStart", 2593 null); 2594 } 2595 setScannerState(SCANNER_STATE_COMMENT); 2596 } else if (fEntityScanner.skipString(cdata)) { 2597 setScannerState(SCANNER_STATE_CDATA ); 2598 } else if (!scanForDoctypeHook()) { 2599 reportFatalError("MarkupNotRecognizedInContent", 2600 null); 2601 } 2602 break; 2603 } 2604 case '/' :{ 2605 setScannerState(SCANNER_STATE_END_ELEMENT_TAG); 2606 fEntityScanner.skipChar(ch); 2607 break; 2608 } 2609 default :{ 2610 if (isValidNameStartChar(ch)) { 2611 setScannerState(SCANNER_STATE_START_ELEMENT_TAG); 2612 } else { 2613 reportFatalError("MarkupNotRecognizedInContent", 2614 null); 2615 } 2616 } 2617 } 2618 2619 }//startOfMarkup 2620 2621 private void startOfContent() throws IOException { 2622 if (fEntityScanner.skipChar('<')) { 2623 setScannerState(SCANNER_STATE_START_OF_MARKUP); 2624 } else if (fEntityScanner.skipChar('&')) { 2625 setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE 2626 } else { 2627 //element content is there.. 2628 setScannerState(SCANNER_STATE_CHARACTER_DATA); 2629 } 2630 }//startOfContent 2631 2632 2633 /** 2634 * 2635 * SCANNER_STATE_CONTENT and SCANNER_STATE_START_OF_MARKUP are two super states of the parser. 2636 * At any point of time when in doubt over the current state of the parser, the state should be 2637 * set to SCANNER_STATE_CONTENT. Parser will automatically revive itself and will set state of 2638 * the parser to one of its sub state. 2639 * sub states are defined in the parser on the basis of different XML component like 2640 * SCANNER_STATE_ENTITY_REFERENCE , SCANNER_STATE_START_ELEMENT, SCANNER_STATE_CDATA etc.. 2641 * These sub states help the parser to have fine control over the parsing. These are the 2642 * different milepost, parser stops at each sub state (milepost). Based on this state it is 2643 * decided if paresr needs to stop at next milepost ?? 2644 * 2645 */ 2646 public void decideSubState() throws IOException { 2647 while( fScannerState == SCANNER_STATE_CONTENT || fScannerState == SCANNER_STATE_START_OF_MARKUP){ 2648 2649 switch (fScannerState) { 2650 2651 case SCANNER_STATE_CONTENT: { 2652 startOfContent() ; 2653 break; 2654 } 2655 2656 case SCANNER_STATE_START_OF_MARKUP: { 2657 startOfMarkup() ; 2658 break; 2659 } 2660 } 2661 } 2662 }//decideSubState 2663 2664 /** 2665 * Drives the parser to the next state/event on the input. Parser is guaranteed 2666 * to stop at the next state/event. Internally XML document 2667 * is divided into several states. Each state represents a sections of XML 2668 * document. When this functions returns normally, it has read the section 2669 * of XML document and returns the state corresponding to section of 2670 * document which has been read. For optimizations, a particular driver 2671 * can read ahead of the section of document (state returned) just read and 2672 * can maintain a different internal state. 2673 * 2674 * State returned corresponds to Stax states. 2675 * 2676 * @return state representing the section of document just read. 2677 * 2678 * @throws IOException Thrown on i/o error. 2679 * @throws XNIException Thrown on parse error. 2680 */ 2681 2682 public int next() throws IOException, XNIException { 2683 while (true) { 2684 try { 2685 if(DEBUG_NEXT){ 2686 System.out.println("NOW IN FragmentContentDriver"); 2687 System.out.println("Entering the FragmentContentDriver with = " + getScannerStateName(fScannerState)); 2688 } 2689 2690 //decide the actual sub state of the scanner.For more information refer to the javadoc of 2691 //decideSubState. 2692 2693 switch (fScannerState) { 2694 case SCANNER_STATE_CONTENT: { 2695 final int ch = fEntityScanner.peekChar(); 2696 if (ch == '<') { 2697 fEntityScanner.scanChar(); 2698 setScannerState(SCANNER_STATE_START_OF_MARKUP); 2699 } else if (ch == '&') { 2700 fEntityScanner.scanChar(); 2701 setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE 2702 break; 2703 } else { 2704 //element content is there.. 2705 setScannerState(SCANNER_STATE_CHARACTER_DATA); 2706 break; 2707 } 2708 } 2709 2710 case SCANNER_STATE_START_OF_MARKUP: { 2711 startOfMarkup(); 2712 break; 2713 }//case: SCANNER_STATE_START_OF_MARKUP 2714 2715 }//end of switch 2716 //decideSubState() ; 2717 2718 //do some special handling if isCoalesce is set to true. 2719 if(fIsCoalesce){ 2720 fUsebuffer = true ; 2721 //if the last section was character data 2722 if(fLastSectionWasCharacterData){ 2723 2724 //if we dont encounter any CDATA or ENITY REFERENCE and current state is also not SCANNER_STATE_CHARACTER_DATA 2725 //return the last scanned charactrer data. 2726 if((fScannerState != SCANNER_STATE_CDATA) && (fScannerState != SCANNER_STATE_REFERENCE) 2727 && (fScannerState != SCANNER_STATE_CHARACTER_DATA)){ 2728 fLastSectionWasCharacterData = false; 2729 return XMLEvent.CHARACTERS; 2730 } 2731 }//if last section was CDATA or ENTITY REFERENCE 2732 //xxx: there might be another entity reference or CDATA after this 2733 //<foo>blah blah &<<![CDATA[[aa]]>blah blah</foo> 2734 else if((fLastSectionWasCData || fLastSectionWasEntityReference)){ 2735 //and current state is not SCANNER_STATE_CHARACTER_DATA 2736 //or SCANNER_STATE_CDATA or SCANNER_STATE_REFERENCE 2737 //this means there is nothing more to be coalesced. 2738 //return the CHARACTERS event. 2739 if((fScannerState != SCANNER_STATE_CDATA) && (fScannerState != SCANNER_STATE_REFERENCE) 2740 && (fScannerState != SCANNER_STATE_CHARACTER_DATA)){ 2741 2742 fLastSectionWasCData = false; 2743 fLastSectionWasEntityReference = false; 2744 return XMLEvent.CHARACTERS; 2745 } 2746 } 2747 } 2748 2749 2750 if(DEBUG_NEXT){ 2751 System.out.println("Actual scanner state set by decideSubState is = " + getScannerStateName(fScannerState)); 2752 } 2753 2754 switch(fScannerState){ 2755 2756 case XMLEvent.START_DOCUMENT : 2757 return XMLEvent.START_DOCUMENT; 2758 2759 case SCANNER_STATE_START_ELEMENT_TAG :{ 2760 2761 //xxx this function returns true when element is empty.. can be linked to end element event. 2762 //returns true if the element is empty 2763 fEmptyElement = scanStartElement() ; 2764 //if the element is empty the next event is "end element" 2765 if(fEmptyElement){ 2766 setScannerState(SCANNER_STATE_END_ELEMENT_TAG); 2767 }else{ 2768 //set the next possible state 2769 setScannerState(SCANNER_STATE_CONTENT); 2770 } 2771 return XMLEvent.START_ELEMENT ; 2772 } 2773 2774 case SCANNER_STATE_CHARACTER_DATA: { 2775 if(DEBUG_COALESCE){ 2776 System.out.println("fLastSectionWasCData = " + fLastSectionWasCData); 2777 System.out.println("fIsCoalesce = " + fIsCoalesce); 2778 } 2779 //if last section was either entity reference or cdata or character data we should be using buffer 2780 fUsebuffer = fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData ; 2781 2782 //When coalesce is set to true and last state was REFERENCE or CDATA or CHARACTER_DATA, buffer should not be cleared. 2783 if( fIsCoalesce && (fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData) ){ 2784 fLastSectionWasEntityReference = false; 2785 fLastSectionWasCData = false; 2786 fLastSectionWasCharacterData = true ; 2787 fUsebuffer = true; 2788 }else{ 2789 //clear the buffer 2790 fContentBuffer.clear(); 2791 } 2792 2793 //set the fTempString length to 0 before passing it on to scanContent 2794 //scanContent sets the correct co-ordinates as per the content read 2795 fTempString.length = 0; 2796 int c = fEntityScanner.scanContent(fTempString); 2797 if(DEBUG){ 2798 System.out.println("fTempString = " + fTempString); 2799 } 2800 if(fEntityScanner.skipChar('<')){ 2801 //check if we have reached end of element 2802 if(fEntityScanner.skipChar('/')){ 2803 //increase the mark up depth 2804 fMarkupDepth++; 2805 fLastSectionWasCharacterData = false; 2806 setScannerState(SCANNER_STATE_END_ELEMENT_TAG); 2807 //check if its start of new element 2808 }else if(XMLChar.isNameStart(fEntityScanner.peekChar())){ 2809 fMarkupDepth++; 2810 fLastSectionWasCharacterData = false; 2811 setScannerState(SCANNER_STATE_START_ELEMENT_TAG); 2812 }else{ 2813 setScannerState(SCANNER_STATE_START_OF_MARKUP); 2814 //there can be cdata ahead if coalesce is true we should call again 2815 if(fIsCoalesce){ 2816 fUsebuffer = true; 2817 fLastSectionWasCharacterData = true; 2818 fContentBuffer.append(fTempString); 2819 fTempString.length = 0; 2820 continue; 2821 } 2822 } 2823 //in case last section was either entity reference or cdata or character data -- we should be using buffer 2824 if(fUsebuffer){ 2825 fContentBuffer.append(fTempString); 2826 fTempString.length = 0; 2827 } 2828 if(DEBUG){ 2829 System.out.println("NOT USING THE BUFFER, STRING = " + fTempString.toString()); 2830 } 2831 if(dtdGrammarUtil!= null && dtdGrammarUtil.isIgnorableWhiteSpace(fContentBuffer)){ 2832 if(DEBUG)System.out.println("Return SPACE EVENT"); 2833 return XMLEvent.SPACE; 2834 }else 2835 return XMLEvent.CHARACTERS; 2836 2837 } else{ 2838 fUsebuffer = true ; 2839 if(DEBUG){ 2840 System.out.println("fContentBuffer = " + fContentBuffer); 2841 System.out.println("fTempString = " + fTempString); 2842 } 2843 fContentBuffer.append(fTempString); 2844 fTempString.length = 0; 2845 } 2846 if (c == '\r') { 2847 if(DEBUG){ 2848 System.out.println("'\r' character found"); 2849 } 2850 // happens when there is the character reference 2851 //xxx: We know the next chracter.. we should just skip it and add ']' directlry 2852 fEntityScanner.scanChar(); 2853 fUsebuffer = true; 2854 fContentBuffer.append((char)c); 2855 c = -1 ; 2856 } else if (c == ']') { 2857 //fStringBuffer.clear(); 2858 //xxx: We know the next chracter.. we should just skip it and add ']' directlry 2859 fUsebuffer = true; 2860 fContentBuffer.append((char)fEntityScanner.scanChar()); 2861 // remember where we are in case we get an endEntity before we 2862 // could flush the buffer out - this happens when we're parsing an 2863 // entity which ends with a ] 2864 fInScanContent = true; 2865 2866 // We work on a single character basis to handle cases such as: 2867 // ']]]>' which we might otherwise miss. 2868 // 2869 if (fEntityScanner.skipChar(']')) { 2870 fContentBuffer.append(']'); 2871 while (fEntityScanner.skipChar(']')) { 2872 fContentBuffer.append(']'); 2873 } 2874 if (fEntityScanner.skipChar('>')) { 2875 reportFatalError("CDEndInContent", null); 2876 } 2877 } 2878 c = -1 ; 2879 fInScanContent = false; 2880 } 2881 2882 do{ 2883 //xxx: we should be using only one buffer.. 2884 // we need not to grow the buffer only when isCoalesce() is not true; 2885 2886 if (c == '<') { 2887 fEntityScanner.scanChar(); 2888 setScannerState(SCANNER_STATE_START_OF_MARKUP); 2889 break; 2890 }//xxx what should be the behavior if entity reference is present in the content ? 2891 else if (c == '&') { 2892 fEntityScanner.scanChar(); 2893 setScannerState(SCANNER_STATE_REFERENCE); 2894 break; 2895 }///xxx since this part is also characters, it should be merged... 2896 else if (c != -1 && isInvalidLiteral(c)) { 2897 if (XMLChar.isHighSurrogate(c)) { 2898 // special case: surrogates 2899 scanSurrogates(fContentBuffer) ; 2900 setScannerState(SCANNER_STATE_CONTENT); 2901 } else { 2902 reportFatalError("InvalidCharInContent", 2903 new Object[] { 2904 Integer.toString(c, 16)}); 2905 fEntityScanner.scanChar(); 2906 } 2907 break; 2908 } 2909 //xxx: scanContent also gives character callback. 2910 c = scanContent(fContentBuffer) ; 2911 //we should not be iterating again if fIsCoalesce is not set to true 2912 2913 if(!fIsCoalesce){ 2914 setScannerState(SCANNER_STATE_CONTENT); 2915 break; 2916 } 2917 2918 }while(true); 2919 2920 //if (fDocumentHandler != null) { 2921 // fDocumentHandler.characters(fContentBuffer, null); 2922 //} 2923 if(DEBUG)System.out.println("USING THE BUFFER, STRING START=" + fContentBuffer.toString() +"=END"); 2924 //if fIsCoalesce is true there might be more data so call fDriver.next() 2925 if(fIsCoalesce){ 2926 fLastSectionWasCharacterData = true ; 2927 continue; 2928 }else{ 2929 if(dtdGrammarUtil!= null && dtdGrammarUtil.isIgnorableWhiteSpace(fContentBuffer)){ 2930 if(DEBUG)System.out.println("Return SPACE EVENT"); 2931 return XMLEvent.SPACE; 2932 } else 2933 return XMLEvent.CHARACTERS ; 2934 } 2935 } 2936 2937 case SCANNER_STATE_END_ELEMENT_TAG :{ 2938 if(fEmptyElement){ 2939 //set it back to false. 2940 fEmptyElement = false; 2941 setScannerState(SCANNER_STATE_CONTENT); 2942 //check the case when there is comment after single element document 2943 //<foo/> and some comment after this 2944 return (fMarkupDepth == 0 && elementDepthIsZeroHook() ) ? XMLEvent.END_ELEMENT : XMLEvent.END_ELEMENT ; 2945 2946 } else if(scanEndElement() == 0) { 2947 //It is last element of the document 2948 if (elementDepthIsZeroHook()) { 2949 //if element depth is zero , it indicates the end of the document 2950 //the state shouldn't be set, because it is set by elementDepthIsZeroHook() function 2951 //xxx understand this point once again.. 2952 return XMLEvent.END_ELEMENT ; 2953 } 2954 2955 } 2956 setScannerState(SCANNER_STATE_CONTENT); 2957 return XMLEvent.END_ELEMENT ; 2958 } 2959 2960 case SCANNER_STATE_COMMENT: { //SCANNER_STATE_COMMENT: 2961 scanComment(); 2962 setScannerState(SCANNER_STATE_CONTENT); 2963 return XMLEvent.COMMENT; 2964 //break; 2965 } 2966 case SCANNER_STATE_PI:{ //SCANNER_STATE_PI: { 2967 //clear the buffer first 2968 fContentBuffer.clear() ; 2969 //xxx: which buffer should be passed. Ideally we shouldn't have 2970 //more than two buffers -- 2971 //xxx: where should we add the switch for buffering. 2972 scanPI(fContentBuffer); 2973 setScannerState(SCANNER_STATE_CONTENT); 2974 return XMLEvent.PROCESSING_INSTRUCTION; 2975 //break; 2976 } 2977 case SCANNER_STATE_CDATA :{ //SCANNER_STATE_CDATA: { 2978 //xxx: What if CDATA is the first event 2979 //<foo><![CDATA[hello<><>]]>append</foo> 2980 2981 //we should not clear the buffer only when the last state was either SCANNER_STATE_REFERENCE or 2982 //SCANNER_STATE_CHARACTER_DATA or SCANNER_STATE_REFERENCE 2983 if(fIsCoalesce && ( fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData)){ 2984 fLastSectionWasCData = true ; 2985 fLastSectionWasEntityReference = false; 2986 fLastSectionWasCharacterData = false; 2987 }//if we dont need to coalesce clear the buffer 2988 else{ 2989 fContentBuffer.clear(); 2990 } 2991 fUsebuffer = true; 2992 //CDATA section is completely read in all the case. 2993 scanCDATASection(fContentBuffer , true); 2994 setScannerState(SCANNER_STATE_CONTENT); 2995 //1. if fIsCoalesce is set to true we set the variable fLastSectionWasCData to true 2996 //and just call fDispatche.next(). Since we have set the scanner state to 2997 //SCANNER_STATE_CONTENT (super state) parser will automatically recover and 2998 //behave appropriately. When isCoalesce is set to true we dont need to reportCDATA event 2999 //2. Check if application has set for reporting CDATA event 3000 //3. if the application has neither set the fIsCoalesce to true nor fReportCdataEvent 3001 //return the cdata event as characters. 3002 if(fIsCoalesce){ 3003 fLastSectionWasCData = true ; 3004 //there might be more data to coalesce. 3005 continue; 3006 }else if(fReportCdataEvent){ 3007 return XMLEvent.CDATA; 3008 } else{ 3009 return XMLEvent.CHARACTERS; 3010 } 3011 } 3012 3013 case SCANNER_STATE_REFERENCE :{ 3014 fMarkupDepth++; 3015 foundBuiltInRefs = false; 3016 3017 //we should not clear the buffer only when the last state was either CDATA or 3018 //SCANNER_STATE_CHARACTER_DATA or SCANNER_STATE_REFERENCE 3019 if(fIsCoalesce && ( fLastSectionWasEntityReference || fLastSectionWasCData || fLastSectionWasCharacterData)){ 3020 //fLastSectionWasEntityReference or fLastSectionWasCData are only 3021 //used when fIsCoalesce is set to true. 3022 fLastSectionWasEntityReference = true ; 3023 fLastSectionWasCData = false; 3024 fLastSectionWasCharacterData = false; 3025 }//if we dont need to coalesce clear the buffer 3026 else{ 3027 fContentBuffer.clear(); 3028 } 3029 fUsebuffer = true ; 3030 //take care of character reference 3031 if (fEntityScanner.skipChar('#')) { 3032 scanCharReferenceValue(fContentBuffer, null); 3033 fMarkupDepth--; 3034 if(!fIsCoalesce){ 3035 setScannerState(SCANNER_STATE_CONTENT); 3036 return XMLEvent.CHARACTERS; 3037 } 3038 } else { 3039 // this function also starts new entity 3040 scanEntityReference(fContentBuffer); 3041 //if there was built-in entity reference & coalesce is not true 3042 //return CHARACTERS 3043 if(fScannerState == SCANNER_STATE_BUILT_IN_REFS && !fIsCoalesce){ 3044 setScannerState(SCANNER_STATE_CONTENT); 3045 return XMLEvent.CHARACTERS; 3046 } 3047 3048 //if there was a text declaration, call next() it will be taken care. 3049 if(fScannerState == SCANNER_STATE_TEXT_DECL){ 3050 fLastSectionWasEntityReference = true ; 3051 continue; 3052 } 3053 3054 if(fScannerState == SCANNER_STATE_REFERENCE){ 3055 setScannerState(SCANNER_STATE_CONTENT); 3056 if (fReplaceEntityReferences && fEntityStore.isDeclaredEntity(fCurrentEntityName)) { 3057 // Skip the entity reference, we don't care 3058 continue; 3059 } 3060 return XMLEvent.ENTITY_REFERENCE; 3061 } 3062 } 3063 //Wether it was character reference, entity reference or built-in entity 3064 //set the next possible state to SCANNER_STATE_CONTENT 3065 setScannerState(SCANNER_STATE_CONTENT); 3066 fLastSectionWasEntityReference = true ; 3067 continue; 3068 } 3069 3070 case SCANNER_STATE_TEXT_DECL: { 3071 // scan text decl 3072 if (fEntityScanner.skipString("<?xml")) { 3073 fMarkupDepth++; 3074 // NOTE: special case where entity starts with a PI 3075 // whose name starts with "xml" (e.g. "xmlfoo") 3076 if (isValidNameChar(fEntityScanner.peekChar())) { 3077 fStringBuffer.clear(); 3078 fStringBuffer.append("xml"); 3079 3080 if (fNamespaces) { 3081 while (isValidNCName(fEntityScanner.peekChar())) { 3082 fStringBuffer.append((char)fEntityScanner.scanChar()); 3083 } 3084 } else { 3085 while (isValidNameChar(fEntityScanner.peekChar())) { 3086 fStringBuffer.append((char)fEntityScanner.scanChar()); 3087 } 3088 } 3089 String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length); 3090 fContentBuffer.clear(); 3091 scanPIData(target, fContentBuffer); 3092 } 3093 3094 // standard text declaration 3095 else { 3096 //xxx: this function gives callback 3097 scanXMLDeclOrTextDecl(true); 3098 } 3099 } 3100 // now that we've straightened out the readers, we can read in chunks: 3101 fEntityManager.fCurrentEntity.mayReadChunks = true; 3102 setScannerState(SCANNER_STATE_CONTENT); 3103 //xxx: we don't return any state, so how do we get to know about TEXT declarations. 3104 //it seems we have to careful when to allow function issue a callback 3105 //and when to allow adapter issue a callback. 3106 continue; 3107 } 3108 3109 3110 case SCANNER_STATE_ROOT_ELEMENT: { 3111 if (scanRootElementHook()) { 3112 fEmptyElement = true; 3113 //rest would be taken care by fTrailingMiscDriver set by scanRootElementHook 3114 return XMLEvent.START_ELEMENT; 3115 } 3116 setScannerState(SCANNER_STATE_CONTENT); 3117 return XMLEvent.START_ELEMENT ; 3118 } 3119 case SCANNER_STATE_CHAR_REFERENCE : { 3120 fContentBuffer.clear(); 3121 scanCharReferenceValue(fContentBuffer, null); 3122 fMarkupDepth--; 3123 setScannerState(SCANNER_STATE_CONTENT); 3124 return XMLEvent.CHARACTERS; 3125 } 3126 default: 3127 throw new XNIException("Scanner State " + fScannerState + " not Recognized "); 3128 3129 }//switch 3130 } 3131 // premature end of file 3132 catch (EOFException e) { 3133 endOfFileHook(e); 3134 return -1; 3135 } 3136 } //while loop 3137 }//next 3138 3139 3140 // 3141 // Protected methods 3142 // 3143 3144 // hooks 3145 3146 // NOTE: These hook methods are added so that the full document 3147 // scanner can share the majority of code with this class. 3148 3149 /** 3150 * Scan for DOCTYPE hook. This method is a hook for subclasses 3151 * to add code to handle scanning for a the "DOCTYPE" string 3152 * after the string "<!" has been scanned. 3153 * 3154 * @return True if the "DOCTYPE" was scanned; false if "DOCTYPE" 3155 * was not scanned. 3156 */ 3157 protected boolean scanForDoctypeHook() 3158 throws IOException, XNIException { 3159 return false; 3160 } // scanForDoctypeHook():boolean 3161 3162 /** 3163 * Element depth iz zero. This methos is a hook for subclasses 3164 * to add code to handle when the element depth hits zero. When 3165 * scanning a document fragment, an element depth of zero is 3166 * normal. However, when scanning a full XML document, the 3167 * scanner must handle the trailing miscellanous section of 3168 * the document after the end of the document's root element. 3169 * 3170 * @return True if the caller should stop and return true which 3171 * allows the scanner to switch to a new scanning 3172 * driver. A return value of false indicates that 3173 * the content driver should continue as normal. 3174 */ 3175 protected boolean elementDepthIsZeroHook() 3176 throws IOException, XNIException { 3177 return false; 3178 } // elementDepthIsZeroHook():boolean 3179 3180 /** 3181 * Scan for root element hook. This method is a hook for 3182 * subclasses to add code that handles scanning for the root 3183 * element. When scanning a document fragment, there is no 3184 * "root" element. However, when scanning a full XML document, 3185 * the scanner must handle the root element specially. 3186 * 3187 * @return True if the caller should stop and return true which 3188 * allows the scanner to switch to a new scanning 3189 * driver. A return value of false indicates that 3190 * the content driver should continue as normal. 3191 */ 3192 protected boolean scanRootElementHook() 3193 throws IOException, XNIException { 3194 return false; 3195 } // scanRootElementHook():boolean 3196 3197 /** 3198 * End of file hook. This method is a hook for subclasses to 3199 * add code that handles the end of file. The end of file in 3200 * a document fragment is OK if the markup depth is zero. 3201 * However, when scanning a full XML document, an end of file 3202 * is always premature. 3203 */ 3204 protected void endOfFileHook(EOFException e) 3205 throws IOException, XNIException { 3206 3207 // NOTE: An end of file is only only an error if we were 3208 // in the middle of scanning some markup. -Ac 3209 if (fMarkupDepth != 0) { 3210 reportFatalError("PrematureEOF", null); 3211 } 3212 3213 } // endOfFileHook() 3214 3215 } // class FragmentContentDriver 3216 3217 static void pr(String str) { 3218 System.out.println(str) ; 3219 } 3220 3221 protected boolean fUsebuffer ; 3222 3223 /** this function gets an XMLString (which is used to store the attribute value) from the special pool 3224 * maintained for attributes. 3225 * fAttributeCacheUsedCount tracks the number of attributes that has been consumed from the pool. 3226 * if all the attributes has been consumed, it adds a new XMLString inthe pool and returns the same 3227 * XMLString. 3228 * 3229 * @return XMLString XMLString used to store an attribute value. 3230 */ 3231 3232 protected XMLString getString(){ 3233 if(fAttributeCacheUsedCount < initialCacheCount || fAttributeCacheUsedCount < attributeValueCache.size()){ 3234 return (XMLString)attributeValueCache.get(fAttributeCacheUsedCount++); 3235 } else{ 3236 XMLString str = new XMLString(); 3237 fAttributeCacheUsedCount++; 3238 attributeValueCache.add(str); 3239 return str; 3240 } 3241 } 3242 3243 /** 3244 * Implements XMLBufferListener interface. 3245 */ 3246 3247 public void refresh(){ 3248 refresh(0); 3249 } 3250 3251 /** 3252 * receives callbacks from {@link XMLEntityReader } when buffer 3253 * is being changed. 3254 * @param refreshPosition 3255 */ 3256 public void refresh(int refreshPosition){ 3257 //If you are reading attributes and you got a callback 3258 //cache available attributes. 3259 if(fReadingAttributes){ 3260 fAttributes.refresh(); 3261 } 3262 if(fScannerState == SCANNER_STATE_CHARACTER_DATA){ 3263 //since fTempString directly matches to the underlying main buffer 3264 //store the data into buffer 3265 fContentBuffer.append(fTempString); 3266 //clear the XMLString so that data can't be added again. 3267 fTempString.length = 0; 3268 fUsebuffer = true; 3269 } 3270 } 3271 3272 } // class XMLDocumentFragmentScannerImpl