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