1 /* 2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 /* 6 * Copyright 2005 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.impl; 22 import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar; 23 import java.io.EOFException; 24 import java.io.IOException; 25 26 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; 27 28 import com.sun.org.apache.xerces.internal.util.SymbolTable; 29 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl; 30 import com.sun.org.apache.xerces.internal.util.XMLChar; 31 import com.sun.org.apache.xerces.internal.util.XMLStringBuffer; 32 33 import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler; 34 import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler; 35 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; 36 import com.sun.org.apache.xerces.internal.xni.XMLString; 37 import com.sun.org.apache.xerces.internal.xni.XNIException; 38 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; 39 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; 40 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 41 import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDScanner; 42 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; 43 import com.sun.org.apache.xerces.internal.xni.Augmentations; 44 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; 45 import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler; 46 import com.sun.org.apache.xerces.internal.impl.Constants; 47 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; 48 import com.sun.xml.internal.stream.Entity; 49 50 /** 51 * This class is responsible for scanning the declarations found 52 * in the internal and external subsets of a DTD in an XML document. 53 * The scanner acts as the sources for the DTD information which is 54 * communicated to the DTD handlers. 55 * <p> 56 * This component requires the following features and properties from the 57 * component manager that uses it: 58 * <ul> 59 * <li>http://xml.org/sax/features/validation</li> 60 * <li>http://apache.org/xml/features/scanner/notify-char-refs</li> 61 * <li>http://apache.org/xml/properties/internal/symbol-table</li> 62 * <li>http://apache.org/xml/properties/internal/error-reporter</li> 63 * <li>http://apache.org/xml/properties/internal/entity-manager</li> 64 * </ul> 65 * 66 * @author Arnaud Le Hors, IBM 67 * @author Andy Clark, IBM 68 * @author Glenn Marcy, IBM 69 * @author Eric Ye, IBM 70 * 71 * @version $Id: XMLDTDScannerImpl.java,v 1.8 2010-11-01 04:39:41 joehw Exp $ 72 */ 73 public class XMLDTDScannerImpl 74 extends XMLScanner 75 implements XMLDTDScanner, XMLComponent, XMLEntityHandler { 76 77 // 78 // Constants 79 // 80 81 // scanner states 82 83 /** Scanner state: end of input. */ 84 protected static final int SCANNER_STATE_END_OF_INPUT = 0; 85 86 /** Scanner state: text declaration. */ 87 protected static final int SCANNER_STATE_TEXT_DECL = 1; 88 89 /** Scanner state: markup declaration. */ 90 protected static final int SCANNER_STATE_MARKUP_DECL = 2; 91 92 // recognized features and properties 93 94 /** Recognized features. */ 95 private static final String[] RECOGNIZED_FEATURES = { 96 VALIDATION, 97 NOTIFY_CHAR_REFS, 98 }; 99 100 /** Feature defaults. */ 101 private static final Boolean[] FEATURE_DEFAULTS = { 102 null, 103 Boolean.FALSE, 104 }; 105 106 /** Recognized properties. */ 107 private static final String[] RECOGNIZED_PROPERTIES = { 108 SYMBOL_TABLE, 109 ERROR_REPORTER, 110 ENTITY_MANAGER, 111 }; 112 113 /** Property defaults. */ 114 private static final Object[] PROPERTY_DEFAULTS = { 115 null, 116 null, 117 null, 118 }; 119 120 // debugging 121 122 /** Debug scanner state. */ 123 private static final boolean DEBUG_SCANNER_STATE = false; 124 125 // 126 // Data 127 // 128 129 // handlers 130 131 /** DTD handler. */ 132 public XMLDTDHandler fDTDHandler = null; 133 134 /** DTD content model handler. */ 135 protected XMLDTDContentModelHandler fDTDContentModelHandler; 136 137 // state 138 139 /** Scanner state. */ 140 protected int fScannerState; 141 142 /** Standalone. */ 143 protected boolean fStandalone; 144 145 /** Seen external DTD. */ 146 protected boolean fSeenExternalDTD; 147 148 /** Seen external parameter entity. */ 149 protected boolean fSeenExternalPE; 150 151 // private data 152 153 /** Start DTD called. */ 154 private boolean fStartDTDCalled; 155 156 /** Default attribute */ 157 private XMLAttributesImpl fAttributes = new XMLAttributesImpl(); 158 159 /** 160 * Stack of content operators (either '|' or ',') in children 161 * content. 162 */ 163 private int[] fContentStack = new int[5]; 164 165 /** Size of content stack. */ 166 private int fContentDepth; 167 168 /** Parameter entity stack to check well-formedness. */ 169 private int[] fPEStack = new int[5]; 170 171 172 /** Parameter entity stack to report start/end entity calls. */ 173 private boolean[] fPEReport = new boolean[5]; 174 175 /** Number of opened parameter entities. */ 176 private int fPEDepth; 177 178 /** Markup depth. */ 179 private int fMarkUpDepth; 180 181 /** Number of opened external entities. */ 182 private int fExtEntityDepth; 183 184 /** Number of opened include sections. */ 185 private int fIncludeSectDepth; 186 187 // temporary variables 188 189 /** Array of 3 strings. */ 190 private String[] fStrings = new String[3]; 191 192 /** String. */ 193 private XMLString fString = new XMLString(); 194 195 /** String buffer. */ 196 private XMLStringBuffer fStringBuffer = new XMLStringBuffer(); 197 198 /** String buffer. */ 199 private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer(); 200 201 /** Literal text. */ 202 private XMLString fLiteral = new XMLString(); 203 204 /** Literal text. */ 205 private XMLString fLiteral2 = new XMLString(); 206 207 /** Enumeration values. */ 208 private String[] fEnumeration = new String[5]; 209 210 /** Enumeration values count. */ 211 private int fEnumerationCount; 212 213 /** Ignore conditional section buffer. */ 214 private XMLStringBuffer fIgnoreConditionalBuffer = new XMLStringBuffer(128); 215 216 /** Object contains grammar information for a non-validaing parser. */ 217 DTDGrammar nvGrammarInfo = null; 218 219 boolean nonValidatingMode = false; 220 // 221 // Constructors 222 // 223 224 /** Default constructor. */ 225 public XMLDTDScannerImpl() { 226 } // <init>() 227 228 /** Constructor for he use of non-XMLComponentManagers. */ 229 public XMLDTDScannerImpl(SymbolTable symbolTable, 230 XMLErrorReporter errorReporter, XMLEntityManager entityManager) { 231 fSymbolTable = symbolTable; 232 fErrorReporter = errorReporter; 233 fEntityManager = entityManager; 234 entityManager.setProperty(SYMBOL_TABLE, fSymbolTable); 235 } 236 237 // 238 // XMLDTDScanner methods 239 // 240 241 /** 242 * Sets the input source. 243 * 244 * @param inputSource The input source or null. 245 * 246 * @throws IOException Thrown on i/o error. 247 */ 248 public void setInputSource(XMLInputSource inputSource) throws IOException { 249 if (inputSource == null) { 250 // no system id was available 251 if (fDTDHandler != null) { 252 fDTDHandler.startDTD(null, null); 253 fDTDHandler.endDTD(null); 254 } 255 if (nonValidatingMode){ 256 nvGrammarInfo.startDTD(null,null); 257 nvGrammarInfo.endDTD(null); 258 } 259 return; 260 } 261 fEntityManager.setEntityHandler(this); 262 fEntityManager.startDTDEntity(inputSource); 263 } // setInputSource(XMLInputSource) 264 265 /** 266 * Scans the external subset of the document. 267 * 268 * @param complete True if the scanner should scan the document 269 * completely, pushing all events to the registered 270 * document handler. A value of false indicates that 271 * that the scanner should only scan the next portion 272 * of the document and return. A scanner instance is 273 * permitted to completely scan a document if it does 274 * not support this "pull" scanning model. 275 * 276 * @return True if there is more to scan, false otherwise. 277 */ 278 public boolean scanDTDExternalSubset(boolean complete) 279 throws IOException, XNIException { 280 281 fEntityManager.setEntityHandler(this); 282 if (fScannerState == SCANNER_STATE_TEXT_DECL) { 283 fSeenExternalDTD = true; 284 boolean textDecl = scanTextDecl(); 285 if (fScannerState == SCANNER_STATE_END_OF_INPUT) { 286 return false; 287 } 288 else { 289 // next state is markup decls regardless of whether there 290 // is a TextDecl or not 291 setScannerState(SCANNER_STATE_MARKUP_DECL); 292 if (textDecl && !complete) { 293 return true; 294 } 295 } 296 } 297 // keep dispatching "events" 298 do { 299 if (!scanDecls(complete)) { 300 return false; 301 } 302 } while (complete); 303 304 // return that there is more to scan 305 return true; 306 307 } // scanDTDExternalSubset(boolean):boolean 308 309 /** 310 * Scans the internal subset of the document. 311 * 312 * @param complete True if the scanner should scan the document 313 * completely, pushing all events to the registered 314 * document handler. A value of false indicates that 315 * that the scanner should only scan the next portion 316 * of the document and return. A scanner instance is 317 * permitted to completely scan a document if it does 318 * not support this "pull" scanning model. 319 * @param standalone True if the document was specified as standalone. 320 * This value is important for verifying certain 321 * well-formedness constraints. 322 * @param hasExternalDTD True if the document has an external DTD. 323 * This allows the scanner to properly notify 324 * the handler of the end of the DTD in the 325 * absence of an external subset. 326 * 327 * @return True if there is more to scan, false otherwise. 328 */ 329 public boolean scanDTDInternalSubset(boolean complete, boolean standalone, 330 boolean hasExternalSubset) 331 throws IOException, XNIException { 332 // reset entity scanner 333 //xxx:stax getText() is supposed to return only DTD internal subset 334 //shouldn't we record position here before we go ahead ?? 335 336 fEntityScanner = (XMLEntityScanner)fEntityManager.getEntityScanner(); 337 fEntityManager.setEntityHandler(this); 338 fStandalone = standalone; 339 //System.out.println("state"+fScannerState); 340 if (fScannerState == SCANNER_STATE_TEXT_DECL) { 341 // call handler 342 if (fDTDHandler != null) { 343 fDTDHandler.startDTD(fEntityScanner, null); 344 fStartDTDCalled = true; 345 } 346 347 if (nonValidatingMode){ 348 fStartDTDCalled = true; 349 nvGrammarInfo.startDTD(fEntityScanner,null); 350 } 351 // set starting state for internal subset 352 setScannerState(SCANNER_STATE_MARKUP_DECL); 353 } 354 // keep dispatching "events" 355 do { 356 if (!scanDecls(complete)) { 357 // call handler 358 if (fDTDHandler != null && hasExternalSubset == false) { 359 fDTDHandler.endDTD(null); 360 } 361 if (nonValidatingMode && hasExternalSubset == false ){ 362 nvGrammarInfo.endDTD(null); 363 } 364 // we're done, set starting state for external subset 365 setScannerState(SCANNER_STATE_TEXT_DECL); 366 return false; 367 } 368 } while (complete); 369 370 // return that there is more to scan 371 return true; 372 373 } // scanDTDInternalSubset(boolean,boolean,boolean):boolean 374 375 // 376 // XMLComponent methods 377 // 378 379 /** 380 * reset 381 * 382 * @param componentManager 383 */ 384 public void reset(XMLComponentManager componentManager) 385 throws XMLConfigurationException { 386 387 super.reset(componentManager); 388 init(); 389 390 } // reset(XMLComponentManager) 391 392 // this is made for something like XMLDTDLoader--XMLComponentManager-free operation... 393 public void reset() { 394 super.reset(); 395 init(); 396 397 } 398 399 public void reset(PropertyManager props) { 400 setPropertyManager(props); 401 super.reset(props); 402 init() ; 403 nonValidatingMode = true; 404 //Revisit : Create new grammar until we implement GrammarPool. 405 nvGrammarInfo = new DTDGrammar(fSymbolTable); 406 } 407 /** 408 * Returns a list of feature identifiers that are recognized by 409 * this component. This method may return null if no features 410 * are recognized by this component. 411 */ 412 public String[] getRecognizedFeatures() { 413 return (String[])(RECOGNIZED_FEATURES.clone()); 414 } // getRecognizedFeatures():String[] 415 416 /** 417 * Returns a list of property identifiers that are recognized by 418 * this component. This method may return null if no properties 419 * are recognized by this component. 420 */ 421 public String[] getRecognizedProperties() { 422 return (String[])(RECOGNIZED_PROPERTIES.clone()); 423 } // getRecognizedProperties():String[] 424 425 /** 426 * Returns the default state for a feature, or null if this 427 * component does not want to report a default value for this 428 * feature. 429 * 430 * @param featureId The feature identifier. 431 * 432 * @since Xerces 2.2.0 433 */ 434 public Boolean getFeatureDefault(String featureId) { 435 for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { 436 if (RECOGNIZED_FEATURES[i].equals(featureId)) { 437 return FEATURE_DEFAULTS[i]; 438 } 439 } 440 return null; 441 } // getFeatureDefault(String):Boolean 442 443 /** 444 * Returns the default state for a property, or null if this 445 * component does not want to report a default value for this 446 * property. 447 * 448 * @param propertyId The property identifier. 449 * 450 * @since Xerces 2.2.0 451 */ 452 public Object getPropertyDefault(String propertyId) { 453 for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { 454 if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { 455 return PROPERTY_DEFAULTS[i]; 456 } 457 } 458 return null; 459 } // getPropertyDefault(String):Object 460 461 // 462 // XMLDTDSource methods 463 // 464 465 /** 466 * setDTDHandler 467 * 468 * @param dtdHandler 469 */ 470 public void setDTDHandler(XMLDTDHandler dtdHandler) { 471 fDTDHandler = dtdHandler; 472 } // setDTDHandler(XMLDTDHandler) 473 474 /** 475 * getDTDHandler 476 * 477 * @return the XMLDTDHandler 478 */ 479 public XMLDTDHandler getDTDHandler() { 480 return fDTDHandler; 481 } // getDTDHandler(): XMLDTDHandler 482 483 // 484 // XMLDTDContentModelSource methods 485 // 486 487 /** 488 * setDTDContentModelHandler 489 * 490 * @param dtdContentModelHandler 491 */ 492 public void setDTDContentModelHandler(XMLDTDContentModelHandler 493 dtdContentModelHandler) { 494 fDTDContentModelHandler = dtdContentModelHandler; 495 } // setDTDContentModelHandler 496 497 /** 498 * getDTDContentModelHandler 499 * 500 * @return XMLDTDContentModelHandler 501 */ 502 public XMLDTDContentModelHandler getDTDContentModelHandler() { 503 return fDTDContentModelHandler ; 504 } // setDTDContentModelHandler 505 506 // 507 // XMLEntityHandler methods 508 // 509 510 /** 511 * This method notifies of the start of an entity. The DTD has the 512 * pseudo-name of "[dtd]" parameter entity names start with '%'; and 513 * general entities are just specified by their name. 514 * 515 * @param name The name of the entity. 516 * @param identifier The resource identifier. 517 * @param encoding The auto-detected IANA encoding name of the entity 518 * stream. This value will be null in those situations 519 * where the entity encoding is not auto-detected (e.g. 520 * internal entities or a document entity that is 521 * parsed from a java.io.Reader). 522 * @param augs Additional information that may include infoset augmentations 523 * 524 * @throws XNIException Thrown by handler to signal an error. 525 */ 526 public void startEntity(String name, 527 XMLResourceIdentifier identifier, 528 String encoding, Augmentations augs) throws XNIException { 529 530 super.startEntity(name, identifier, encoding, augs); 531 532 boolean dtdEntity = name.equals("[dtd]"); 533 if (dtdEntity) { 534 // call handler 535 if (fDTDHandler != null && !fStartDTDCalled ) { 536 fDTDHandler.startDTD(fEntityScanner, null); 537 } 538 if (fDTDHandler != null) { 539 fDTDHandler.startExternalSubset(identifier,null); 540 } 541 fEntityManager.startExternalSubset(); 542 fEntityStore.startExternalSubset(); 543 fExtEntityDepth++; 544 } 545 else if (name.charAt(0) == '%') { 546 pushPEStack(fMarkUpDepth, fReportEntity); 547 if (fEntityScanner.isExternal()) { 548 fExtEntityDepth++; 549 } 550 } 551 552 // call handler 553 if (fDTDHandler != null && !dtdEntity && fReportEntity) { 554 fDTDHandler.startParameterEntity(name, identifier, encoding, null); 555 } 556 557 } // startEntity(String,XMLResourceIdentifier,String) 558 559 /** 560 * This method notifies the end of an entity. The DTD has the pseudo-name 561 * of "[dtd]" parameter entity names start with '%'; and general entities 562 * are just specified by their name. 563 * 564 * @param name The name of the entity. 565 * 566 * @throws XNIException Thrown by handler to signal an error. 567 */ 568 public void endEntity(String name, Augmentations augs) 569 throws XNIException, IOException { 570 571 super.endEntity(name, augs); 572 573 // if there is no data after the doctype 574 // 575 if (fScannerState == SCANNER_STATE_END_OF_INPUT) 576 return; 577 578 // Handle end of PE 579 boolean reportEntity = fReportEntity; 580 if (name.startsWith("%")) { 581 reportEntity = peekReportEntity(); 582 // check well-formedness of the enity 583 int startMarkUpDepth = popPEStack(); 584 // throw fatalError if this entity was incomplete and 585 // was a freestanding decl 586 if(startMarkUpDepth == 0 && 587 startMarkUpDepth < fMarkUpDepth) { 588 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 589 "ILL_FORMED_PARAMETER_ENTITY_WHEN_USED_IN_DECL", 590 new Object[]{ fEntityManager.fCurrentEntity.name}, 591 XMLErrorReporter.SEVERITY_FATAL_ERROR); 592 } 593 if (startMarkUpDepth != fMarkUpDepth) { 594 reportEntity = false; 595 if (fValidation) { 596 // Proper nesting of parameter entities is a Validity Constraint 597 // and must not be enforced when validation is off 598 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 599 "ImproperDeclarationNesting", 600 new Object[]{ name }, 601 XMLErrorReporter.SEVERITY_ERROR); 602 } 603 } 604 if (fEntityScanner.isExternal()) { 605 fExtEntityDepth--; 606 } 607 } 608 609 // call handler 610 boolean dtdEntity = name.equals("[dtd]"); 611 if (fDTDHandler != null && !dtdEntity && reportEntity) { 612 fDTDHandler.endParameterEntity(name, null); 613 } 614 615 // end DTD 616 if (dtdEntity) { 617 if (fIncludeSectDepth != 0) { 618 reportFatalError("IncludeSectUnterminated", null); 619 } 620 fScannerState = SCANNER_STATE_END_OF_INPUT; 621 // call handler 622 fEntityManager.endExternalSubset(); 623 fEntityStore.endExternalSubset(); 624 625 if (fDTDHandler != null) { 626 fDTDHandler.endExternalSubset(null); 627 fDTDHandler.endDTD(null); 628 } 629 fExtEntityDepth--; 630 } 631 632 //XML (Document Entity) is the last opened entity, however 633 //if for some reason DTD Scanner receives this callback 634 //there is something wrong (probably invalid XML), throw exception. 635 //or 636 //For standalone DTD loader, it might be the last opened entity 637 //and if this is the last opened entity and fMarkUpDepth != 0 or 638 //fIncludeSectDepth != 0 or fExtEntityDepth != 0 throw Exception 639 if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.LAST_ENTITY)) 640 && ( fMarkUpDepth != 0 || fExtEntityDepth !=0 || fIncludeSectDepth != 0)){ 641 throw new EOFException(); 642 } 643 644 } // endEntity(String) 645 646 // helper methods 647 648 /** 649 * Sets the scanner state. 650 * 651 * @param state The new scanner state. 652 */ 653 protected final void setScannerState(int state) { 654 655 fScannerState = state; 656 if (DEBUG_SCANNER_STATE) { 657 System.out.print("### setScannerState: "); 658 System.out.print(getScannerStateName(state)); 659 //System.out.println(); 660 } 661 662 } // setScannerState(int) 663 664 // 665 // Private methods 666 // 667 668 /** Returns the scanner state name. */ 669 private static String getScannerStateName(int state) { 670 671 if (DEBUG_SCANNER_STATE) { 672 switch (state) { 673 case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT"; 674 case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL"; 675 case SCANNER_STATE_MARKUP_DECL: return "SCANNER_STATE_MARKUP_DECL"; 676 } 677 } 678 679 return "??? ("+state+')'; 680 681 } // getScannerStateName(int):String 682 683 protected final boolean scanningInternalSubset() { 684 return fExtEntityDepth == 0; 685 } 686 687 /** 688 * start a parameter entity dealing with the textdecl if there is any 689 * 690 * @param name The name of the parameter entity to start (without the '%') 691 * @param literal Whether this is happening within a literal 692 */ 693 protected void startPE(String name, boolean literal) 694 throws IOException, XNIException { 695 int depth = fPEDepth; 696 String pName = "%"+name; 697 if (fValidation && !fEntityStore.isDeclaredEntity(pName)) { 698 fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared", 699 new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR); 700 } 701 fEntityManager.startEntity(fSymbolTable.addSymbol(pName), 702 literal); 703 // if we actually got a new entity and it's external 704 // parse text decl if there is any 705 if (depth != fPEDepth && fEntityScanner.isExternal()) { 706 scanTextDecl(); 707 } 708 } 709 710 /** 711 * Dispatch an XML "event". 712 * 713 * @param complete True if this method is intended to scan 714 * and dispatch as much as possible. 715 * 716 * @return True if a TextDecl was scanned. 717 * 718 * @throws IOException Thrown on i/o error. 719 * @throws XNIException Thrown on parse error. 720 * 721 */ 722 protected final boolean scanTextDecl() 723 throws IOException, XNIException { 724 725 // scan XMLDecl 726 boolean textDecl = false; 727 if (fEntityScanner.skipString("<?xml")) { 728 fMarkUpDepth++; 729 // NOTE: special case where document starts with a PI 730 // whose name starts with "xml" (e.g. "xmlfoo") 731 if (isValidNameChar(fEntityScanner.peekChar())) { 732 fStringBuffer.clear(); 733 fStringBuffer.append("xml"); 734 while (isValidNameChar(fEntityScanner.peekChar())) { 735 fStringBuffer.append((char)fEntityScanner.scanChar()); 736 } 737 String target = 738 fSymbolTable.addSymbol(fStringBuffer.ch, 739 fStringBuffer.offset, 740 fStringBuffer.length); 741 scanPIData(target, fString); 742 } 743 744 // standard Text declaration 745 else { 746 // pseudo-attribute values 747 String version = null; 748 String encoding = null; 749 750 scanXMLDeclOrTextDecl(true, fStrings); 751 textDecl = true; 752 fMarkUpDepth--; 753 754 version = fStrings[0]; 755 encoding = fStrings[1]; 756 757 fEntityScanner.setEncoding(encoding); 758 759 // call handler 760 if (fDTDHandler != null) { 761 fDTDHandler.textDecl(version, encoding, null); 762 } 763 } 764 } 765 fEntityManager.fCurrentEntity.mayReadChunks = true; 766 767 return textDecl; 768 769 } // scanTextDecl(boolean):boolean 770 771 /** 772 * Scans a processing data. This is needed to handle the situation 773 * where a document starts with a processing instruction whose 774 * target name <em>starts with</em> "xml". (e.g. xmlfoo) 775 * 776 * @param target The PI target 777 * @param data The string to fill in with the data 778 */ 779 protected final void scanPIData(String target, XMLString data) 780 throws IOException, XNIException { 781 //Venu REVISIT 782 // super.scanPIData(target, data); 783 fMarkUpDepth--; 784 785 // call handler 786 if (fDTDHandler != null) { 787 fDTDHandler.processingInstruction(target, data, null); 788 } 789 790 } // scanPIData(String) 791 792 /** 793 * Scans a comment. 794 * <p> 795 * <pre> 796 * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' 797 * </pre> 798 * <p> 799 * <strong>Note:</strong> Called after scanning past '<!--' 800 */ 801 protected final void scanComment() throws IOException, XNIException { 802 803 fReportEntity = false; 804 scanComment(fStringBuffer); 805 fMarkUpDepth--; 806 807 // call handler 808 if (fDTDHandler != null) { 809 fDTDHandler.comment(fStringBuffer, null); 810 } 811 fReportEntity = true; 812 813 } // scanComment() 814 815 /** 816 * Scans an element declaration 817 * <p> 818 * <pre> 819 * [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>' 820 * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children 821 * </pre> 822 * <p> 823 * <strong>Note:</strong> Called after scanning past '<!ELEMENT' 824 */ 825 protected final void scanElementDecl() throws IOException, XNIException { 826 827 // spaces 828 fReportEntity = false; 829 if (!skipSeparator(true, !scanningInternalSubset())) { 830 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL", 831 null); 832 } 833 834 // element name 835 String name = fEntityScanner.scanName(); 836 if (name == null) { 837 reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL", 838 null); 839 } 840 841 // spaces 842 if (!skipSeparator(true, !scanningInternalSubset())) { 843 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_CONTENTSPEC_IN_ELEMENTDECL", 844 new Object[]{name}); 845 } 846 847 // content model 848 if (fDTDContentModelHandler != null) { 849 fDTDContentModelHandler.startContentModel(name, null); 850 } 851 String contentModel = null; 852 fReportEntity = true; 853 if (fEntityScanner.skipString("EMPTY")) { 854 contentModel = "EMPTY"; 855 // call handler 856 if (fDTDContentModelHandler != null) { 857 fDTDContentModelHandler.empty(null); 858 } 859 } 860 else if (fEntityScanner.skipString("ANY")) { 861 contentModel = "ANY"; 862 // call handler 863 if (fDTDContentModelHandler != null) { 864 fDTDContentModelHandler.any(null); 865 } 866 } 867 else { 868 if (!fEntityScanner.skipChar('(')) { 869 reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN", 870 new Object[]{name}); 871 } 872 if (fDTDContentModelHandler != null) { 873 fDTDContentModelHandler.startGroup(null); 874 } 875 fStringBuffer.clear(); 876 fStringBuffer.append('('); 877 fMarkUpDepth++; 878 skipSeparator(false, !scanningInternalSubset()); 879 880 // Mixed content model 881 if (fEntityScanner.skipString("#PCDATA")) { 882 scanMixed(name); 883 } 884 else { // children content 885 scanChildren(name); 886 } 887 contentModel = fStringBuffer.toString(); 888 } 889 890 // call handler 891 if (fDTDContentModelHandler != null) { 892 fDTDContentModelHandler.endContentModel(null); 893 } 894 895 fReportEntity = false; 896 skipSeparator(false, !scanningInternalSubset()); 897 // end 898 if (!fEntityScanner.skipChar('>')) { 899 reportFatalError("ElementDeclUnterminated", new Object[]{name}); 900 } 901 fReportEntity = true; 902 fMarkUpDepth--; 903 904 // call handler 905 if (fDTDHandler != null) { 906 fDTDHandler.elementDecl(name, contentModel, null); 907 } 908 if (nonValidatingMode) nvGrammarInfo.elementDecl(name, contentModel, null); 909 } // scanElementDecl() 910 911 /** 912 * scan Mixed content model 913 * This assumes the content model has been parsed up to #PCDATA and 914 * can simply append to fStringBuffer. 915 * <pre> 916 * [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' 917 * | '(' S? '#PCDATA' S? ')' 918 * </pre> 919 * 920 * @param elName The element type name this declaration is about. 921 * 922 * <strong>Note:</strong> Called after scanning past '(#PCDATA'. 923 */ 924 private final void scanMixed(String elName) 925 throws IOException, XNIException { 926 927 String childName = null; 928 929 fStringBuffer.append("#PCDATA"); 930 // call handler 931 if (fDTDContentModelHandler != null) { 932 fDTDContentModelHandler.pcdata(null); 933 } 934 skipSeparator(false, !scanningInternalSubset()); 935 while (fEntityScanner.skipChar('|')) { 936 fStringBuffer.append('|'); 937 // call handler 938 if (fDTDContentModelHandler != null) { 939 fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE, 940 null); 941 } 942 skipSeparator(false, !scanningInternalSubset()); 943 944 childName = fEntityScanner.scanName(); 945 if (childName == null) { 946 reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT", 947 new Object[]{elName}); 948 } 949 fStringBuffer.append(childName); 950 // call handler 951 if (fDTDContentModelHandler != null) { 952 fDTDContentModelHandler.element(childName, null); 953 } 954 skipSeparator(false, !scanningInternalSubset()); 955 } 956 // The following check must be done in a single call (as opposed to one 957 // for ')' and then one for '*') to guarantee that callbacks are 958 // properly nested. We do not want to trigger endEntity too early in 959 // case we cross the boundary of an entity between the two characters. 960 if (fEntityScanner.skipString(")*")) { 961 fStringBuffer.append(")*"); 962 // call handler 963 if (fDTDContentModelHandler != null) { 964 fDTDContentModelHandler.endGroup(null); 965 fDTDContentModelHandler.occurrence(XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE, 966 null); 967 } 968 } 969 else if (childName != null) { 970 reportFatalError("MixedContentUnterminated", 971 new Object[]{elName}); 972 } 973 else if (fEntityScanner.skipChar(')')){ 974 fStringBuffer.append(')'); 975 // call handler 976 if (fDTDContentModelHandler != null) { 977 fDTDContentModelHandler.endGroup(null); 978 } 979 } 980 else { 981 reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN", 982 new Object[]{elName}); 983 } 984 fMarkUpDepth--; 985 // we are done 986 } 987 988 /** 989 * scan children content model 990 * This assumes it can simply append to fStringBuffer. 991 * <pre> 992 * [47] children ::= (choice | seq) ('?' | '*' | '+')? 993 * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')? 994 * [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')' 995 * [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' 996 * </pre> 997 * 998 * @param elName The element type name this declaration is about. 999 * 1000 * <strong>Note:</strong> Called after scanning past the first open 1001 * paranthesis. 1002 */ 1003 private final void scanChildren(String elName) 1004 throws IOException, XNIException { 1005 1006 fContentDepth = 0; 1007 pushContentStack(0); 1008 int currentOp = 0; 1009 int c; 1010 while (true) { 1011 if (fEntityScanner.skipChar('(')) { 1012 fMarkUpDepth++; 1013 fStringBuffer.append('('); 1014 // call handler 1015 if (fDTDContentModelHandler != null) { 1016 fDTDContentModelHandler.startGroup(null); 1017 } 1018 // push current op on stack and reset it 1019 pushContentStack(currentOp); 1020 currentOp = 0; 1021 skipSeparator(false, !scanningInternalSubset()); 1022 continue; 1023 } 1024 skipSeparator(false, !scanningInternalSubset()); 1025 String childName = fEntityScanner.scanName(); 1026 if (childName == null) { 1027 reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN", 1028 new Object[]{elName}); 1029 return; 1030 } 1031 // call handler 1032 if (fDTDContentModelHandler != null) { 1033 fDTDContentModelHandler.element(childName, null); 1034 } 1035 fStringBuffer.append(childName); 1036 c = fEntityScanner.peekChar(); 1037 if (c == '?' || c == '*' || c == '+') { 1038 // call handler 1039 if (fDTDContentModelHandler != null) { 1040 short oc; 1041 if (c == '?') { 1042 oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE; 1043 } 1044 else if (c == '*') { 1045 oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE; 1046 } 1047 else { 1048 oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE; 1049 } 1050 fDTDContentModelHandler.occurrence(oc, null); 1051 } 1052 fEntityScanner.scanChar(); 1053 fStringBuffer.append((char)c); 1054 } 1055 while (true) { 1056 skipSeparator(false, !scanningInternalSubset()); 1057 c = fEntityScanner.peekChar(); 1058 if (c == ',' && currentOp != '|') { 1059 currentOp = c; 1060 // call handler 1061 if (fDTDContentModelHandler != null) { 1062 fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE, 1063 null); 1064 } 1065 fEntityScanner.scanChar(); 1066 fStringBuffer.append(','); 1067 break; 1068 } 1069 else if (c == '|' && currentOp != ',') { 1070 currentOp = c; 1071 // call handler 1072 if (fDTDContentModelHandler != null) { 1073 fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE, 1074 null); 1075 } 1076 fEntityScanner.scanChar(); 1077 fStringBuffer.append('|'); 1078 break; 1079 } 1080 else if (c != ')') { 1081 reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN", 1082 new Object[]{elName}); 1083 } 1084 // call handler 1085 if (fDTDContentModelHandler != null) { 1086 fDTDContentModelHandler.endGroup(null); 1087 } 1088 // restore previous op 1089 currentOp = popContentStack(); 1090 short oc; 1091 // The following checks must be done in a single call (as 1092 // opposed to one for ')' and then one for '?', '*', and '+') 1093 // to guarantee that callbacks are properly nested. We do not 1094 // want to trigger endEntity too early in case we cross the 1095 // boundary of an entity between the two characters. 1096 if (fEntityScanner.skipString(")?")) { 1097 fStringBuffer.append(")?"); 1098 // call handler 1099 if (fDTDContentModelHandler != null) { 1100 oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE; 1101 fDTDContentModelHandler.occurrence(oc, null); 1102 } 1103 } 1104 else if (fEntityScanner.skipString(")+")) { 1105 fStringBuffer.append(")+"); 1106 // call handler 1107 if (fDTDContentModelHandler != null) { 1108 oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE; 1109 fDTDContentModelHandler.occurrence(oc, null); 1110 } 1111 } 1112 else if (fEntityScanner.skipString(")*")) { 1113 fStringBuffer.append(")*"); 1114 // call handler 1115 if (fDTDContentModelHandler != null) { 1116 oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE; 1117 fDTDContentModelHandler.occurrence(oc, null); 1118 } 1119 } 1120 else { 1121 // no occurrence specified 1122 fEntityScanner.scanChar(); 1123 fStringBuffer.append(')'); 1124 } 1125 fMarkUpDepth--; 1126 if (fContentDepth == 0) { 1127 return; 1128 } 1129 } 1130 skipSeparator(false, !scanningInternalSubset()); 1131 } 1132 } 1133 1134 /** 1135 * Scans an attlist declaration 1136 * <p> 1137 * <pre> 1138 * [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>' 1139 * [53] AttDef ::= S Name S AttType S DefaultDecl 1140 * </pre> 1141 * <p> 1142 * <strong>Note:</strong> Called after scanning past '<!ATTLIST' 1143 */ 1144 protected final void scanAttlistDecl() throws IOException, XNIException { 1145 1146 // spaces 1147 fReportEntity = false; 1148 if (!skipSeparator(true, !scanningInternalSubset())) { 1149 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL", 1150 null); 1151 } 1152 1153 // element name 1154 String elName = fEntityScanner.scanName(); 1155 if (elName == null) { 1156 reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL", 1157 null); 1158 } 1159 1160 // call handler 1161 if (fDTDHandler != null) { 1162 fDTDHandler.startAttlist(elName, null); 1163 } 1164 1165 // spaces 1166 if (!skipSeparator(true, !scanningInternalSubset())) { 1167 // no space, is it the end yet? 1168 if (fEntityScanner.skipChar('>')) { 1169 // yes, stop here 1170 // call handler 1171 if (fDTDHandler != null) { 1172 fDTDHandler.endAttlist(null); 1173 } 1174 fMarkUpDepth--; 1175 return; 1176 } 1177 else { 1178 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF", 1179 new Object[]{elName}); 1180 } 1181 } 1182 1183 // definitions 1184 while (!fEntityScanner.skipChar('>')) { 1185 String name = fEntityScanner.scanName(); 1186 if (name == null) { 1187 reportFatalError("AttNameRequiredInAttDef", 1188 new Object[]{elName}); 1189 } 1190 // spaces 1191 if (!skipSeparator(true, !scanningInternalSubset())) { 1192 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTTYPE_IN_ATTDEF", 1193 new Object[]{elName, name}); 1194 } 1195 // type 1196 String type = scanAttType(elName, name); 1197 1198 // spaces 1199 if (!skipSeparator(true, !scanningInternalSubset())) { 1200 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_DEFAULTDECL_IN_ATTDEF", 1201 new Object[]{elName, name}); 1202 } 1203 1204 // default decl 1205 String defaultType = scanAttDefaultDecl(elName, name, 1206 type, 1207 fLiteral, fLiteral2); 1208 // REVISIT: Should we do anything with the non-normalized 1209 // default attribute value? -Ac 1210 // yes--according to bug 5073. - neilg 1211 String[] enumr = null; 1212 if( fDTDHandler != null || nonValidatingMode){ 1213 if (fEnumerationCount != 0) { 1214 enumr = new String[fEnumerationCount]; 1215 System.arraycopy(fEnumeration, 0, enumr, 1216 0, fEnumerationCount); 1217 } 1218 } 1219 // call handler 1220 // Determine whether the default value to be passed should be null. 1221 // REVISIT: should probably check whether fLiteral.ch is null instead. LM. 1222 if (defaultType!=null && (defaultType.equals("#REQUIRED") || 1223 defaultType.equals("#IMPLIED"))) { 1224 if (fDTDHandler != null){ 1225 fDTDHandler.attributeDecl(elName, name, type, enumr, 1226 defaultType, null, null, null); 1227 } 1228 if(nonValidatingMode){ 1229 nvGrammarInfo.attributeDecl(elName, name, type, enumr, 1230 defaultType, null, null, null); 1231 1232 } 1233 } 1234 else { 1235 if (fDTDHandler != null){ 1236 fDTDHandler.attributeDecl(elName, name, type, enumr, 1237 defaultType, fLiteral, fLiteral2, null); 1238 } 1239 if(nonValidatingMode){ 1240 nvGrammarInfo.attributeDecl(elName, name, type, enumr, 1241 defaultType, fLiteral, fLiteral2, null); 1242 } 1243 } 1244 skipSeparator(false, !scanningInternalSubset()); 1245 } 1246 1247 // call handler 1248 if (fDTDHandler != null) { 1249 fDTDHandler.endAttlist(null); 1250 } 1251 fMarkUpDepth--; 1252 fReportEntity = true; 1253 1254 } // scanAttlistDecl() 1255 1256 /** 1257 * Scans an attribute type definition 1258 * <p> 1259 * <pre> 1260 * [54] AttType ::= StringType | TokenizedType | EnumeratedType 1261 * [55] StringType ::= 'CDATA' 1262 * [56] TokenizedType ::= 'ID' 1263 * | 'IDREF' 1264 * | 'IDREFS' 1265 * | 'ENTITY' 1266 * | 'ENTITIES' 1267 * | 'NMTOKEN' 1268 * | 'NMTOKENS' 1269 * [57] EnumeratedType ::= NotationType | Enumeration 1270 * [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')' 1271 * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' 1272 * </pre> 1273 * <p> 1274 * <strong>Note:</strong> Called after scanning past '<!ATTLIST' 1275 * 1276 * @param elName The element type name this declaration is about. 1277 * @param atName The attribute name this declaration is about. 1278 */ 1279 private final String scanAttType(String elName, String atName) 1280 throws IOException, XNIException { 1281 1282 String type = null; 1283 fEnumerationCount = 0; 1284 /* 1285 * Watchout: the order here is important: when a string happens to 1286 * be a substring of another string, the longer one needs to be 1287 * looked for first!! 1288 */ 1289 if (fEntityScanner.skipString("CDATA")) { 1290 type = "CDATA"; 1291 } 1292 else if (fEntityScanner.skipString("IDREFS")) { 1293 type = "IDREFS"; 1294 } 1295 else if (fEntityScanner.skipString("IDREF")) { 1296 type = "IDREF"; 1297 } 1298 else if (fEntityScanner.skipString("ID")) { 1299 type = "ID"; 1300 } 1301 else if (fEntityScanner.skipString("ENTITY")) { 1302 type = "ENTITY"; 1303 } 1304 else if (fEntityScanner.skipString("ENTITIES")) { 1305 type = "ENTITIES"; 1306 } 1307 else if (fEntityScanner.skipString("NMTOKENS")) { 1308 type = "NMTOKENS"; 1309 } 1310 else if (fEntityScanner.skipString("NMTOKEN")) { 1311 type = "NMTOKEN"; 1312 } 1313 else if (fEntityScanner.skipString("NOTATION")) { 1314 type = "NOTATION"; 1315 // spaces 1316 if (!skipSeparator(true, !scanningInternalSubset())) { 1317 reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_IN_NOTATIONTYPE", 1318 new Object[]{elName, atName}); 1319 } 1320 // open paren 1321 int c = fEntityScanner.scanChar(); 1322 if (c != '(') { 1323 reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE", 1324 new Object[]{elName, atName}); 1325 } 1326 fMarkUpDepth++; 1327 do { 1328 skipSeparator(false, !scanningInternalSubset()); 1329 String aName = fEntityScanner.scanName(); 1330 if (aName == null) { 1331 reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE", 1332 new Object[]{elName, atName}); 1333 } 1334 ensureEnumerationSize(fEnumerationCount + 1); 1335 fEnumeration[fEnumerationCount++] = aName; 1336 skipSeparator(false, !scanningInternalSubset()); 1337 c = fEntityScanner.scanChar(); 1338 } while (c == '|'); 1339 if (c != ')') { 1340 reportFatalError("NotationTypeUnterminated", 1341 new Object[]{elName, atName}); 1342 } 1343 fMarkUpDepth--; 1344 } 1345 else { // Enumeration 1346 type = "ENUMERATION"; 1347 // open paren 1348 int c = fEntityScanner.scanChar(); 1349 if (c != '(') { 1350 // "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL", 1351 reportFatalError("AttTypeRequiredInAttDef", 1352 new Object[]{elName, atName}); 1353 } 1354 fMarkUpDepth++; 1355 do { 1356 skipSeparator(false, !scanningInternalSubset()); 1357 String token = fEntityScanner.scanNmtoken(); 1358 if (token == null) { 1359 reportFatalError("MSG_NMTOKEN_REQUIRED_IN_ENUMERATION", 1360 new Object[]{elName, atName}); 1361 } 1362 ensureEnumerationSize(fEnumerationCount + 1); 1363 fEnumeration[fEnumerationCount++] = token; 1364 skipSeparator(false, !scanningInternalSubset()); 1365 c = fEntityScanner.scanChar(); 1366 } while (c == '|'); 1367 if (c != ')') { 1368 reportFatalError("EnumerationUnterminated", 1369 new Object[]{elName, atName}); 1370 } 1371 fMarkUpDepth--; 1372 } 1373 return type; 1374 1375 } // scanAttType():String 1376 1377 1378 /** 1379 * Scans an attribute default declaration 1380 * <p> 1381 * <pre> 1382 * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) 1383 * </pre> 1384 * 1385 * @param name The name of the attribute being scanned. 1386 * @param defaultVal The string to fill in with the default value. 1387 */ 1388 protected final String scanAttDefaultDecl(String elName, String atName, 1389 String type, 1390 XMLString defaultVal, 1391 XMLString nonNormalizedDefaultVal) 1392 throws IOException, XNIException { 1393 1394 String defaultType = null; 1395 fString.clear(); 1396 defaultVal.clear(); 1397 if (fEntityScanner.skipString("#REQUIRED")) { 1398 defaultType = "#REQUIRED"; 1399 } 1400 else if (fEntityScanner.skipString("#IMPLIED")) { 1401 defaultType = "#IMPLIED"; 1402 } 1403 else { 1404 if (fEntityScanner.skipString("#FIXED")) { 1405 defaultType = "#FIXED"; 1406 // spaces 1407 if (!skipSeparator(true, !scanningInternalSubset())) { 1408 reportFatalError("MSG_SPACE_REQUIRED_AFTER_FIXED_IN_DEFAULTDECL", 1409 new Object[]{elName, atName}); 1410 } 1411 } 1412 // AttValue 1413 boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ; 1414 scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName, 1415 fAttributes, 0, isVC); 1416 } 1417 return defaultType; 1418 1419 } // ScanAttDefaultDecl 1420 1421 /** 1422 * Scans an entity declaration 1423 * <p> 1424 * <pre> 1425 * [70] EntityDecl ::= GEDecl | PEDecl 1426 * [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>' 1427 * [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>' 1428 * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) 1429 * [74] PEDef ::= EntityValue | ExternalID 1430 * [75] ExternalID ::= 'SYSTEM' S SystemLiteral 1431 * | 'PUBLIC' S PubidLiteral S SystemLiteral 1432 * [76] NDataDecl ::= S 'NDATA' S Name 1433 * </pre> 1434 * <p> 1435 * <strong>Note:</strong> Called after scanning past '<!ENTITY' 1436 */ 1437 private final void scanEntityDecl() throws IOException, XNIException { 1438 1439 boolean isPEDecl = false; 1440 boolean sawPERef = false; 1441 fReportEntity = false; 1442 if (fEntityScanner.skipSpaces()) { 1443 if (!fEntityScanner.skipChar('%')) { 1444 isPEDecl = false; // <!ENTITY x "x"> 1445 } 1446 else if (skipSeparator(true, !scanningInternalSubset())) { 1447 // <!ENTITY % x "x"> 1448 isPEDecl = true; 1449 } 1450 else if (scanningInternalSubset()) { 1451 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", 1452 null); 1453 isPEDecl = true; 1454 } 1455 else if (fEntityScanner.peekChar() == '%') { 1456 // <!ENTITY %%x; "x"> is legal 1457 skipSeparator(false, !scanningInternalSubset()); 1458 isPEDecl = true; 1459 } 1460 else { 1461 sawPERef = true; 1462 } 1463 } 1464 else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) { 1465 // <!ENTITY[^ ]...> or <!ENTITY[^ %]...> 1466 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", 1467 null); 1468 isPEDecl = false; 1469 } 1470 else if (fEntityScanner.skipSpaces()) { 1471 // <!ENTITY% ...> 1472 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_PERCENT_IN_PEDECL", 1473 null); 1474 isPEDecl = false; 1475 } 1476 else { 1477 sawPERef = true; 1478 } 1479 if (sawPERef) { 1480 while (true) { 1481 String peName = fEntityScanner.scanName(); 1482 if (peName == null) { 1483 reportFatalError("NameRequiredInPEReference", null); 1484 } 1485 else if (!fEntityScanner.skipChar(';')) { 1486 reportFatalError("SemicolonRequiredInPEReference", 1487 new Object[]{peName}); 1488 } 1489 else { 1490 startPE(peName, false); 1491 } 1492 fEntityScanner.skipSpaces(); 1493 if (!fEntityScanner.skipChar('%')) 1494 break; 1495 if (!isPEDecl) { 1496 if (skipSeparator(true, !scanningInternalSubset())) { 1497 isPEDecl = true; 1498 break; 1499 } 1500 isPEDecl = fEntityScanner.skipChar('%'); 1501 } 1502 } 1503 } 1504 1505 // name 1506 String name = fEntityScanner.scanName(); 1507 if (name == null) { 1508 reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null); 1509 } 1510 1511 // spaces 1512 if (!skipSeparator(true, !scanningInternalSubset())) { 1513 reportFatalError("MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL", 1514 new Object[]{name}); 1515 } 1516 1517 // external id 1518 scanExternalID(fStrings, false); 1519 String systemId = fStrings[0]; 1520 String publicId = fStrings[1]; 1521 1522 if (isPEDecl && systemId != null) { 1523 fSeenExternalPE = true; 1524 } 1525 1526 String notation = null; 1527 // NDATA 1528 boolean sawSpace = skipSeparator(true, !scanningInternalSubset()); 1529 if (!isPEDecl && fEntityScanner.skipString("NDATA")) { 1530 // check whether there was space before NDATA 1531 if (!sawSpace) { 1532 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NDATA_IN_UNPARSED_ENTITYDECL", 1533 new Object[]{name}); 1534 } 1535 1536 // spaces 1537 if (!skipSeparator(true, !scanningInternalSubset())) { 1538 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL", 1539 new Object[]{name}); 1540 } 1541 notation = fEntityScanner.scanName(); 1542 if (notation == null) { 1543 reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL", 1544 new Object[]{name}); 1545 } 1546 } 1547 1548 // internal entity 1549 if (systemId == null) { 1550 scanEntityValue(name, isPEDecl, fLiteral, fLiteral2); 1551 // since we need it's value anyway, let's snag it so it doesn't get corrupted 1552 // if a new load takes place before we store the entity values 1553 fStringBuffer.clear(); 1554 fStringBuffer2.clear(); 1555 fStringBuffer.append(fLiteral.ch, fLiteral.offset, fLiteral.length); 1556 fStringBuffer2.append(fLiteral2.ch, fLiteral2.offset, fLiteral2.length); 1557 } 1558 1559 // skip possible trailing space 1560 skipSeparator(false, !scanningInternalSubset()); 1561 1562 // end 1563 if (!fEntityScanner.skipChar('>')) { 1564 reportFatalError("EntityDeclUnterminated", new Object[]{name}); 1565 } 1566 fMarkUpDepth--; 1567 1568 // register entity and make callback 1569 if (isPEDecl) { 1570 name = "%" + name; 1571 } 1572 if (systemId != null) { 1573 String baseSystemId = fEntityScanner.getBaseSystemId(); 1574 if (notation != null) { 1575 fEntityStore.addUnparsedEntity(name, publicId, systemId, baseSystemId, notation); 1576 } 1577 else { 1578 fEntityStore.addExternalEntity(name, publicId, systemId, 1579 baseSystemId); 1580 } 1581 if (fDTDHandler != null) { 1582 //Venu Revisit : why false has been removed in expandSYstem 1583 fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId )); 1584 1585 if (notation != null) { 1586 fDTDHandler.unparsedEntityDecl(name, fResourceIdentifier, 1587 notation, null); 1588 } 1589 else { 1590 fDTDHandler.externalEntityDecl(name, fResourceIdentifier, null); 1591 } 1592 } 1593 } 1594 else { 1595 fEntityStore.addInternalEntity(name, fStringBuffer.toString()); 1596 if (fDTDHandler != null) { 1597 fDTDHandler.internalEntityDecl(name, fStringBuffer, fStringBuffer2, null); 1598 } 1599 } 1600 fReportEntity = true; 1601 1602 } // scanEntityDecl() 1603 1604 /** 1605 * Scans an entity value. 1606 * 1607 * @param value The string to fill in with the value. 1608 * @param nonNormalizedValue The string to fill in with the 1609 * non-normalized value. 1610 * 1611 * <strong>Note:</strong> This method uses fString, fStringBuffer (through 1612 * the use of scanCharReferenceValue), and fStringBuffer2, anything in them 1613 * at the time of calling is lost. 1614 */ 1615 protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value, 1616 XMLString nonNormalizedValue) 1617 throws IOException, XNIException { 1618 int quote = fEntityScanner.scanChar(); 1619 if (quote != '\'' && quote != '"') { 1620 reportFatalError("OpenQuoteMissingInDecl", null); 1621 } 1622 // store at which depth of entities we start 1623 int entityDepth = fEntityDepth; 1624 1625 XMLString literal = fString; 1626 XMLString literal2 = fString; 1627 int countChar = 0; 1628 if (fLimitAnalyzer == null && fSecurityManager != null) { 1629 fLimitAnalyzer = fSecurityManager.getLimitAnalyzer(); 1630 fLimitAnalyzer.startEntity(entityName); 1631 } 1632 1633 if (fEntityScanner.scanLiteral(quote, fString) != quote) { 1634 fStringBuffer.clear(); 1635 fStringBuffer2.clear(); 1636 do { 1637 if (isPEDecl && fLimitAnalyzer != null) { 1638 checkLimit("%" + entityName, fString.length + countChar); 1639 } 1640 countChar = 0; 1641 fStringBuffer.append(fString); 1642 fStringBuffer2.append(fString); 1643 if (fEntityScanner.skipChar('&')) { 1644 if (fEntityScanner.skipChar('#')) { 1645 fStringBuffer2.append("&#"); 1646 scanCharReferenceValue(fStringBuffer, fStringBuffer2); 1647 } 1648 else { 1649 fStringBuffer.append('&'); 1650 fStringBuffer2.append('&'); 1651 String eName = fEntityScanner.scanName(); 1652 if (eName == null) { 1653 reportFatalError("NameRequiredInReference", 1654 null); 1655 } 1656 else { 1657 fStringBuffer.append(eName); 1658 fStringBuffer2.append(eName); 1659 } 1660 if (!fEntityScanner.skipChar(';')) { 1661 reportFatalError("SemicolonRequiredInReference", 1662 new Object[]{eName}); 1663 } 1664 else { 1665 fStringBuffer.append(';'); 1666 fStringBuffer2.append(';'); 1667 } 1668 } 1669 } 1670 else if (fEntityScanner.skipChar('%')) { 1671 while (true) { 1672 fStringBuffer2.append('%'); 1673 String peName = fEntityScanner.scanName(); 1674 if (peName == null) { 1675 reportFatalError("NameRequiredInPEReference", 1676 null); 1677 } 1678 else if (!fEntityScanner.skipChar(';')) { 1679 reportFatalError("SemicolonRequiredInPEReference", 1680 new Object[]{peName}); 1681 } 1682 else { 1683 if (scanningInternalSubset()) { 1684 reportFatalError("PEReferenceWithinMarkup", 1685 new Object[]{peName}); 1686 } 1687 fStringBuffer2.append(peName); 1688 fStringBuffer2.append(';'); 1689 } 1690 startPE(peName, true); 1691 // REVISIT: [Q] Why do we skip spaces here? -Ac 1692 // REVISIT: This will make returning the non- 1693 // normalized value harder. -Ac 1694 fEntityScanner.skipSpaces(); 1695 if (!fEntityScanner.skipChar('%')) 1696 break; 1697 } 1698 } 1699 else { 1700 countChar++; 1701 int c = fEntityScanner.peekChar(); 1702 if (XMLChar.isHighSurrogate(c)) { 1703 scanSurrogates(fStringBuffer2); 1704 } 1705 else if (isInvalidLiteral(c)) { 1706 reportFatalError("InvalidCharInLiteral", 1707 new Object[]{Integer.toHexString(c)}); 1708 fEntityScanner.scanChar(); 1709 } 1710 // if it's not the delimiting quote or if it is but from a 1711 // different entity than the one this literal started from, 1712 // simply append the character to our buffer 1713 else if (c != quote || entityDepth != fEntityDepth) { 1714 fStringBuffer.append((char)c); 1715 fStringBuffer2.append((char)c); 1716 fEntityScanner.scanChar(); 1717 } 1718 } 1719 } while (fEntityScanner.scanLiteral(quote, fString) != quote); 1720 fStringBuffer.append(fString); 1721 fStringBuffer2.append(fString); 1722 literal = fStringBuffer; 1723 literal2 = fStringBuffer2; 1724 } else { 1725 if (isPEDecl) { 1726 checkLimit("%" + entityName, literal); 1727 } 1728 } 1729 value.setValues(literal); 1730 nonNormalizedValue.setValues(literal2); 1731 if (fLimitAnalyzer != null) { 1732 fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName); 1733 } 1734 1735 if (!fEntityScanner.skipChar(quote)) { 1736 reportFatalError("CloseQuoteMissingInDecl", null); 1737 } 1738 } // scanEntityValue(XMLString,XMLString):void 1739 1740 /** 1741 * Scans a notation declaration 1742 * <p> 1743 * <pre> 1744 * [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID|PublicID) S? '>' 1745 * [83] PublicID ::= 'PUBLIC' S PubidLiteral 1746 * </pre> 1747 * <p> 1748 * <strong>Note:</strong> Called after scanning past '<!NOTATION' 1749 */ 1750 private final void scanNotationDecl() throws IOException, XNIException { 1751 1752 // spaces 1753 fReportEntity = false; 1754 if (!skipSeparator(true, !scanningInternalSubset())) { 1755 reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_NOTATIONDECL", 1756 null); 1757 } 1758 1759 // notation name 1760 String name = fEntityScanner.scanName(); 1761 if (name == null) { 1762 reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL", 1763 null); 1764 } 1765 1766 // spaces 1767 if (!skipSeparator(true, !scanningInternalSubset())) { 1768 reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL", 1769 new Object[]{name}); 1770 } 1771 1772 // external id 1773 scanExternalID(fStrings, true); 1774 String systemId = fStrings[0]; 1775 String publicId = fStrings[1]; 1776 String baseSystemId = fEntityScanner.getBaseSystemId(); 1777 1778 if (systemId == null && publicId == null) { 1779 reportFatalError("ExternalIDorPublicIDRequired", 1780 new Object[]{name}); 1781 } 1782 1783 // skip possible trailing space 1784 skipSeparator(false, !scanningInternalSubset()); 1785 1786 // end 1787 if (!fEntityScanner.skipChar('>')) { 1788 reportFatalError("NotationDeclUnterminated", new Object[]{name}); 1789 } 1790 fMarkUpDepth--; 1791 1792 fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId )); 1793 if (nonValidatingMode) nvGrammarInfo.notationDecl(name, fResourceIdentifier, null); 1794 // call handler 1795 if (fDTDHandler != null) { 1796 //Venu Revisit wby false has been removed. 1797 //fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId, false)); 1798 fDTDHandler.notationDecl(name, fResourceIdentifier, null); 1799 } 1800 fReportEntity = true; 1801 1802 } // scanNotationDecl() 1803 1804 /** 1805 * Scans a conditional section. If it's a section to ignore the whole 1806 * section gets scanned through and this method only returns after the 1807 * closing bracket has been found. When it's an include section though, it 1808 * returns to let the main loop take care of scanning it. In that case the 1809 * end of the section if handled by the main loop (scanDecls). 1810 * <p> 1811 * <pre> 1812 * [61] conditionalSect ::= includeSect | ignoreSect 1813 * [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>' 1814 * [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>' 1815 * [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)* 1816 * [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*) 1817 * </pre> 1818 * <p> 1819 * <strong>Note:</strong> Called after scanning past '<![' */ 1820 private final void scanConditionalSect(int currPEDepth) 1821 throws IOException, XNIException { 1822 1823 fReportEntity = false; 1824 skipSeparator(false, !scanningInternalSubset()); 1825 1826 if (fEntityScanner.skipString("INCLUDE")) { 1827 skipSeparator(false, !scanningInternalSubset()); 1828 if(currPEDepth != fPEDepth && fValidation) { 1829 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 1830 "INVALID_PE_IN_CONDITIONAL", 1831 new Object[]{ fEntityManager.fCurrentEntity.name}, 1832 XMLErrorReporter.SEVERITY_ERROR); 1833 } 1834 // call handler 1835 if (!fEntityScanner.skipChar('[')) { 1836 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); 1837 } 1838 1839 if (fDTDHandler != null) { 1840 fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_INCLUDE, 1841 null); 1842 } 1843 fIncludeSectDepth++; 1844 // just stop there and go back to the main loop 1845 fReportEntity = true; 1846 } 1847 else if (fEntityScanner.skipString("IGNORE")) { 1848 skipSeparator(false, !scanningInternalSubset()); 1849 if(currPEDepth != fPEDepth && fValidation) { 1850 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 1851 "INVALID_PE_IN_CONDITIONAL", 1852 new Object[]{ fEntityManager.fCurrentEntity.name}, 1853 XMLErrorReporter.SEVERITY_ERROR); 1854 } 1855 // call handler 1856 if (fDTDHandler != null) { 1857 fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE, 1858 null); 1859 } 1860 if (!fEntityScanner.skipChar('[')) { 1861 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); 1862 } 1863 fReportEntity = true; 1864 int initialDepth = ++fIncludeSectDepth; 1865 if (fDTDHandler != null) { 1866 fIgnoreConditionalBuffer.clear(); 1867 } 1868 while (true) { 1869 if (fEntityScanner.skipChar('<')) { 1870 if (fDTDHandler != null) { 1871 fIgnoreConditionalBuffer.append('<'); 1872 } 1873 // 1874 // These tests are split so that we handle cases like 1875 // '<<![' and '<!<![' which we might otherwise miss. 1876 // 1877 if (fEntityScanner.skipChar('!')) { 1878 if(fEntityScanner.skipChar('[')) { 1879 if (fDTDHandler != null) { 1880 fIgnoreConditionalBuffer.append("!["); 1881 } 1882 fIncludeSectDepth++; 1883 } else { 1884 if (fDTDHandler != null) { 1885 fIgnoreConditionalBuffer.append("!"); 1886 } 1887 } 1888 } 1889 } 1890 else if (fEntityScanner.skipChar(']')) { 1891 if (fDTDHandler != null) { 1892 fIgnoreConditionalBuffer.append(']'); 1893 } 1894 // 1895 // The same thing goes for ']<![' and '<]]>', etc. 1896 // 1897 if (fEntityScanner.skipChar(']')) { 1898 if (fDTDHandler != null) { 1899 fIgnoreConditionalBuffer.append(']'); 1900 } 1901 while (fEntityScanner.skipChar(']')) { 1902 /* empty loop body */ 1903 if (fDTDHandler != null) { 1904 fIgnoreConditionalBuffer.append(']'); 1905 } 1906 } 1907 if (fEntityScanner.skipChar('>')) { 1908 if (fIncludeSectDepth-- == initialDepth) { 1909 fMarkUpDepth--; 1910 // call handler 1911 if (fDTDHandler != null) { 1912 fLiteral.setValues(fIgnoreConditionalBuffer.ch, 0, 1913 fIgnoreConditionalBuffer.length - 2); 1914 fDTDHandler.ignoredCharacters(fLiteral, null); 1915 fDTDHandler.endConditional(null); 1916 } 1917 return; 1918 } else if(fDTDHandler != null) { 1919 fIgnoreConditionalBuffer.append('>'); 1920 } 1921 } 1922 } 1923 } 1924 else { 1925 int c = fEntityScanner.scanChar(); 1926 if (fScannerState == SCANNER_STATE_END_OF_INPUT) { 1927 reportFatalError("IgnoreSectUnterminated", null); 1928 return; 1929 } 1930 if (fDTDHandler != null) { 1931 fIgnoreConditionalBuffer.append((char)c); 1932 } 1933 } 1934 } 1935 } 1936 else { 1937 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); 1938 } 1939 1940 } // scanConditionalSect() 1941 1942 /** 1943 * Dispatch an XML "event". 1944 * 1945 * @param complete True if this method is intended to scan 1946 * and dispatch as much as possible. 1947 * 1948 * @return True if there is more to scan. 1949 * 1950 * @throws IOException Thrown on i/o error. 1951 * @throws XNIException Thrown on parse error. 1952 * 1953 */ 1954 protected final boolean scanDecls(boolean complete) 1955 throws IOException, XNIException { 1956 1957 skipSeparator(false, true); 1958 boolean again = true; 1959 //System.out.println("scanDecls"+fScannerState); 1960 while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) { 1961 again = complete; 1962 if (fEntityScanner.skipChar('<')) { 1963 fMarkUpDepth++; 1964 if (fEntityScanner.skipChar('?')) { 1965 fStringBuffer.clear(); 1966 scanPI(fStringBuffer); 1967 fMarkUpDepth--; // we're done with this decl 1968 } 1969 else if (fEntityScanner.skipChar('!')) { 1970 if (fEntityScanner.skipChar('-')) { 1971 if (!fEntityScanner.skipChar('-')) { 1972 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", 1973 null); 1974 } else { 1975 scanComment(); 1976 } 1977 } 1978 else if (fEntityScanner.skipString("ELEMENT")) { 1979 scanElementDecl(); 1980 } 1981 else if (fEntityScanner.skipString("ATTLIST")) { 1982 scanAttlistDecl(); 1983 } 1984 else if (fEntityScanner.skipString("ENTITY")) { 1985 scanEntityDecl(); 1986 } 1987 else if (fEntityScanner.skipString("NOTATION")) { 1988 scanNotationDecl(); 1989 } 1990 else if (fEntityScanner.skipChar('[') && 1991 !scanningInternalSubset()) { 1992 scanConditionalSect(fPEDepth); 1993 } 1994 else { 1995 fMarkUpDepth--; 1996 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", 1997 null); 1998 } 1999 } 2000 else { 2001 fMarkUpDepth--; 2002 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); 2003 } 2004 } 2005 else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) { 2006 // end of conditional section? 2007 if (!fEntityScanner.skipChar(']') 2008 || !fEntityScanner.skipChar('>')) { 2009 reportFatalError("IncludeSectUnterminated", null); 2010 } 2011 // call handler 2012 if (fDTDHandler != null) { 2013 fDTDHandler.endConditional(null); 2014 } 2015 // decreaseMarkupDepth(); 2016 fIncludeSectDepth--; 2017 fMarkUpDepth--; 2018 } 2019 else if (scanningInternalSubset() && 2020 fEntityScanner.peekChar() == ']') { 2021 // this is the end of the internal subset, let's stop here 2022 return false; 2023 } 2024 else if (fEntityScanner.skipSpaces()) { 2025 // simply skip 2026 } 2027 else { 2028 reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null); 2029 } 2030 skipSeparator(false, true); 2031 } 2032 return fScannerState != SCANNER_STATE_END_OF_INPUT; 2033 } 2034 2035 /** 2036 * Skip separator. This is typically just whitespace but it can also be one 2037 * or more parameter entity references. 2038 * <p> 2039 * If there are some it "expands them" by calling the corresponding entity 2040 * from the entity manager. 2041 * <p> 2042 * This is recursive and will process has many refs as possible. 2043 * 2044 * @param spaceRequired Specify whether some leading whitespace should be 2045 * found 2046 * @param lookForPERefs Specify whether parameter entity references should 2047 * be looked for 2048 * @return True if any leading whitespace was found or the end of a 2049 * parameter entity was crossed. 2050 */ 2051 private boolean skipSeparator(boolean spaceRequired, boolean lookForPERefs) 2052 throws IOException, XNIException { 2053 int depth = fPEDepth; 2054 boolean sawSpace = fEntityScanner.skipSpaces(); 2055 if (!lookForPERefs || !fEntityScanner.skipChar('%')) { 2056 return !spaceRequired || sawSpace || (depth != fPEDepth); 2057 } 2058 while (true) { 2059 String name = fEntityScanner.scanName(); 2060 if (name == null) { 2061 reportFatalError("NameRequiredInPEReference", null); 2062 } 2063 else if (!fEntityScanner.skipChar(';')) { 2064 reportFatalError("SemicolonRequiredInPEReference", 2065 new Object[]{name}); 2066 } 2067 startPE(name, false); 2068 fEntityScanner.skipSpaces(); 2069 if (!fEntityScanner.skipChar('%')) 2070 return true; 2071 } 2072 } 2073 2074 2075 /* 2076 * Element Children Content Stack 2077 */ 2078 private final void pushContentStack(int c) { 2079 if (fContentStack.length == fContentDepth) { 2080 int[] newStack = new int[fContentDepth * 2]; 2081 System.arraycopy(fContentStack, 0, newStack, 0, fContentDepth); 2082 fContentStack = newStack; 2083 } 2084 fContentStack[fContentDepth++] = c; 2085 } 2086 2087 private final int popContentStack() { 2088 return fContentStack[--fContentDepth]; 2089 } 2090 2091 2092 /* 2093 * Parameter Entity Stack 2094 */ 2095 private final void pushPEStack(int depth, boolean report) { 2096 if (fPEStack.length == fPEDepth) { 2097 int[] newIntStack = new int[fPEDepth * 2]; 2098 System.arraycopy(fPEStack, 0, newIntStack, 0, fPEDepth); 2099 fPEStack = newIntStack; 2100 // report end/start calls 2101 boolean[] newBooleanStack = new boolean[fPEDepth * 2]; 2102 System.arraycopy(fPEReport, 0, newBooleanStack, 0, fPEDepth); 2103 fPEReport = newBooleanStack; 2104 2105 } 2106 fPEReport[fPEDepth] = report; 2107 fPEStack[fPEDepth++] = depth; 2108 } 2109 2110 /** pop the stack */ 2111 private final int popPEStack() { 2112 return fPEStack[--fPEDepth]; 2113 } 2114 2115 /** look at the top of the stack */ 2116 private final boolean peekReportEntity() { 2117 return fPEReport[fPEDepth-1]; 2118 } 2119 2120 2121 /* 2122 * Utility method 2123 */ 2124 private final void ensureEnumerationSize(int size) { 2125 if (fEnumeration.length == size) { 2126 String[] newEnum = new String[size * 2]; 2127 System.arraycopy(fEnumeration, 0, newEnum, 0, size); 2128 fEnumeration = newEnum; 2129 } 2130 } 2131 2132 // private methods 2133 private void init() { 2134 // reset state related data 2135 fStartDTDCalled = false; 2136 fExtEntityDepth = 0; 2137 fIncludeSectDepth = 0; 2138 fMarkUpDepth = 0; 2139 fPEDepth = 0; 2140 2141 fStandalone = false; 2142 fSeenExternalDTD = false; 2143 fSeenExternalPE = false; 2144 2145 // set starting state 2146 setScannerState(SCANNER_STATE_TEXT_DECL); 2147 //new SymbolTable()); 2148 } 2149 2150 /** 2151 * Add the count of the content buffer and check if the accumulated 2152 * value exceeds the limit 2153 * @param entityName entity name 2154 * @param buffer content buffer 2155 */ 2156 private void checkLimit(String entityName, XMLString buffer) { 2157 checkLimit(entityName, buffer.length); 2158 } 2159 2160 /** 2161 * Add the count and check limit 2162 * @param entityName entity name 2163 * @param len length of the buffer 2164 */ 2165 private void checkLimit(String entityName, int len) { 2166 if (fLimitAnalyzer == null) { 2167 fLimitAnalyzer = fSecurityManager.getLimitAnalyzer(); 2168 } 2169 fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName, len); 2170 if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)) { 2171 fSecurityManager.debugPrint(); 2172 reportFatalError("MaxEntitySizeLimit", new Object[]{entityName, 2173 fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), 2174 fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT), 2175 fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)}); 2176 } 2177 if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)) { 2178 fSecurityManager.debugPrint(); 2179 reportFatalError("TotalEntitySizeLimit", 2180 new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), 2181 fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT), 2182 fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)}); 2183 } 2184 2185 } 2186 2187 public DTDGrammar getGrammar(){ 2188 return nvGrammarInfo; 2189 } 2190 2191 } // class XMLDTDScannerImpl