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