1 /* 2 * Copyright (c) 2015, 2017, 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 22 package com.sun.org.apache.xerces.internal.impl; 23 24 import java.io.IOException; 25 26 import com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidatorFilter; 27 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter; 28 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl; 29 import com.sun.org.apache.xerces.internal.util.XMLSymbols; 30 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager; 31 import com.sun.org.apache.xerces.internal.xni.NamespaceContext; 32 import com.sun.org.apache.xerces.internal.xni.QName; 33 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; 34 import com.sun.org.apache.xerces.internal.xni.XNIException; 35 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; 36 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 37 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; 38 import javax.xml.stream.events.XMLEvent; 39 40 41 /** 42 * The scanner acts as the source for the document 43 * information which is communicated to the document handler. 44 * 45 * This class scans an XML document, checks if document has a DTD, and if 46 * DTD is not found the scanner will remove the DTD Validator from the pipeline and perform 47 * namespace binding. 48 * 49 * Note: This scanner should only be used when the namespace processing is on! 50 * 51 * <p> 52 * This component requires the following features and properties from the 53 * component manager that uses it: 54 * <ul> 55 * <li>http://xml.org/sax/features/namespaces {true} -- if the value of this 56 * feature is set to false this scanner must not be used.</li> 57 * <li>http://xml.org/sax/features/validation</li> 58 * <li>http://apache.org/xml/features/nonvalidating/load-external-dtd</li> 59 * <li>http://apache.org/xml/features/scanner/notify-char-refs</li> 60 * <li>http://apache.org/xml/features/scanner/notify-builtin-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 * <li>http://apache.org/xml/properties/internal/dtd-scanner</li> 65 * </ul> 66 * 67 * @xerces.internal 68 * 69 * @author Elena Litani, IBM 70 * @author Michael Glavassevich, IBM 71 * @author Sunitha Reddy, Sun Microsystems 72 */ 73 public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl { 74 75 /** 76 * If is true, the dtd validator is no longer in the pipeline 77 * and the scanner should bind namespaces 78 */ 79 protected boolean fBindNamespaces; 80 81 /** 82 * If validating parser, make sure we report an error in the 83 * scanner if DTD grammar is missing. 84 */ 85 protected boolean fPerformValidation; 86 87 // private data 88 // 89 90 /** DTD validator */ 91 private XMLDTDValidatorFilter fDTDValidator; 92 93 /** 94 * Saw spaces after element name or between attributes. 95 * 96 * This is reserved for the case where scanning of a start element spans 97 * several methods, as is the case when scanning the start of a root element 98 * where a DTD external subset may be read after scanning the element name. 99 */ 100 private boolean fSawSpace; 101 102 103 /** 104 * The scanner is responsible for removing DTD validator 105 * from the pipeline if it is not needed. 106 * 107 * @param validator the DTD validator from the pipeline 108 */ 109 public void setDTDValidator(XMLDTDValidatorFilter validator) { 110 fDTDValidator = validator; 111 } 112 113 /** 114 * Scans a start element. This method will handle the binding of 115 * namespace information and notifying the handler of the start 116 * of the element. 117 * <p> 118 * <pre> 119 * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' 120 * [40] STag ::= '<' Name (S Attribute)* S? '>' 121 * </pre> 122 * <p> 123 * <strong>Note:</strong> This method assumes that the leading 124 * '<' character has been consumed. 125 * <p> 126 * <strong>Note:</strong> This method uses the fElementQName and 127 * fAttributes variables. The contents of these variables will be 128 * destroyed. The caller should copy important information out of 129 * these variables before calling this method. 130 * 131 * @return True if element is empty. (i.e. It matches 132 * production [44]. 133 */ 134 protected boolean scanStartElement() throws IOException, XNIException { 135 136 if (DEBUG_START_END_ELEMENT) 137 System.out.println(">>> scanStartElementNS()"); 138 // Note: namespace processing is on by default 139 fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART); 140 // REVISIT - [Q] Why do we need this local variable? -- mrglavas 141 String rawname = fElementQName.rawname; 142 if (fBindNamespaces) { 143 fNamespaceContext.pushContext(); 144 if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) { 145 if (fPerformValidation) { 146 fErrorReporter.reportError( 147 XMLMessageFormatter.XML_DOMAIN, 148 "MSG_GRAMMAR_NOT_FOUND", 149 new Object[] { rawname }, 150 XMLErrorReporter.SEVERITY_ERROR); 151 152 if (fDoctypeName == null 153 || !fDoctypeName.equals(rawname)) { 154 fErrorReporter.reportError( 155 XMLMessageFormatter.XML_DOMAIN, 156 "RootElementTypeMustMatchDoctypedecl", 157 new Object[] { fDoctypeName, rawname }, 158 XMLErrorReporter.SEVERITY_ERROR); 159 } 160 } 161 } 162 } 163 164 // push element stack 165 fCurrentElement = fElementStack.pushElement(fElementQName); 166 167 // attributes 168 boolean empty = false; 169 fAttributes.removeAllAttributes(); 170 do { 171 // spaces 172 boolean sawSpace = fEntityScanner.skipSpaces(); 173 174 // end tag? 175 int c = fEntityScanner.peekChar(); 176 if (c == '>') { 177 fEntityScanner.scanChar(null); 178 break; 179 } else if (c == '/') { 180 fEntityScanner.scanChar(null); 181 if (!fEntityScanner.skipChar('>', null)) { 182 reportFatalError( 183 "ElementUnterminated", 184 new Object[] { rawname }); 185 } 186 empty = true; 187 break; 188 } else if (!isValidNameStartChar(c) || !sawSpace) { 189 // Second chance. Check if this character is a high 190 // surrogate of a valid name start character. 191 if (!isValidNameStartHighSurrogate(c) || !sawSpace) { 192 reportFatalError( 193 "ElementUnterminated", 194 new Object[] { rawname }); 195 } 196 } 197 198 // attributes 199 scanAttribute(fAttributes); 200 if (fSecurityManager != null && (!fSecurityManager.isNoLimit(fElementAttributeLimit)) && 201 fAttributes.getLength() > fElementAttributeLimit){ 202 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 203 "ElementAttributeLimit", 204 new Object[]{rawname, fElementAttributeLimit }, 205 XMLErrorReporter.SEVERITY_FATAL_ERROR ); 206 } 207 208 } while (true); 209 210 if (fBindNamespaces) { 211 // REVISIT: is it required? forbit xmlns prefix for element 212 if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) { 213 fErrorReporter.reportError( 214 XMLMessageFormatter.XMLNS_DOMAIN, 215 "ElementXMLNSPrefix", 216 new Object[] { fElementQName.rawname }, 217 XMLErrorReporter.SEVERITY_FATAL_ERROR); 218 } 219 220 // bind the element 221 String prefix = 222 fElementQName.prefix != null 223 ? fElementQName.prefix 224 : XMLSymbols.EMPTY_STRING; 225 // assign uri to the element 226 fElementQName.uri = fNamespaceContext.getURI(prefix); 227 // make sure that object in the element stack is updated as well 228 fCurrentElement.uri = fElementQName.uri; 229 230 if (fElementQName.prefix == null && fElementQName.uri != null) { 231 fElementQName.prefix = XMLSymbols.EMPTY_STRING; 232 // making sure that the object in the element stack is updated too. 233 fCurrentElement.prefix = XMLSymbols.EMPTY_STRING; 234 } 235 if (fElementQName.prefix != null && fElementQName.uri == null) { 236 fErrorReporter.reportError( 237 XMLMessageFormatter.XMLNS_DOMAIN, 238 "ElementPrefixUnbound", 239 new Object[] { 240 fElementQName.prefix, 241 fElementQName.rawname }, 242 XMLErrorReporter.SEVERITY_FATAL_ERROR); 243 } 244 245 // bind attributes (xmlns are already bound bellow) 246 int length = fAttributes.getLength(); 247 for (int i = 0; i < length; i++) { 248 fAttributes.getName(i, fAttributeQName); 249 250 String aprefix = 251 fAttributeQName.prefix != null 252 ? fAttributeQName.prefix 253 : XMLSymbols.EMPTY_STRING; 254 String uri = fNamespaceContext.getURI(aprefix); 255 // REVISIT: try removing the first "if" and see if it is faster. 256 // 257 if (fAttributeQName.uri != null 258 && fAttributeQName.uri == uri) { 259 continue; 260 } 261 if (aprefix != XMLSymbols.EMPTY_STRING) { 262 fAttributeQName.uri = uri; 263 if (uri == null) { 264 fErrorReporter.reportError( 265 XMLMessageFormatter.XMLNS_DOMAIN, 266 "AttributePrefixUnbound", 267 new Object[] { 268 fElementQName.rawname, 269 fAttributeQName.rawname, 270 aprefix }, 271 XMLErrorReporter.SEVERITY_FATAL_ERROR); 272 } 273 fAttributes.setURI(i, uri); 274 } 275 } 276 277 if (length > 1) { 278 QName name = fAttributes.checkDuplicatesNS(); 279 if (name != null) { 280 if (name.uri != null) { 281 fErrorReporter.reportError( 282 XMLMessageFormatter.XMLNS_DOMAIN, 283 "AttributeNSNotUnique", 284 new Object[] { 285 fElementQName.rawname, 286 name.localpart, 287 name.uri }, 288 XMLErrorReporter.SEVERITY_FATAL_ERROR); 289 } else { 290 fErrorReporter.reportError( 291 XMLMessageFormatter.XMLNS_DOMAIN, 292 "AttributeNotUnique", 293 new Object[] { 294 fElementQName.rawname, 295 name.rawname }, 296 XMLErrorReporter.SEVERITY_FATAL_ERROR); 297 } 298 } 299 } 300 } 301 302 // call handler 303 if (empty) { 304 //decrease the markup depth.. 305 fMarkupDepth--; 306 307 // check that this element was opened in the same entity 308 if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { 309 reportFatalError( 310 "ElementEntityMismatch", 311 new Object[] { fCurrentElement.rawname }); 312 } 313 314 if (fDocumentHandler != null) { 315 fDocumentHandler.emptyElement(fElementQName, fAttributes, null); 316 } 317 318 /*if (fBindNamespaces) { 319 fNamespaceContext.popContext(); 320 }*/ 321 fScanEndElement = true; 322 323 //pop the element off the stack.. 324 fElementStack.popElement(); 325 } else { 326 if(dtdGrammarUtil != null) { 327 dtdGrammarUtil.startElement(fElementQName, fAttributes); 328 } 329 330 if (fDocumentHandler != null) { 331 fDocumentHandler.startElement(fElementQName, fAttributes, null); 332 } 333 } 334 335 if (DEBUG_START_END_ELEMENT) 336 System.out.println("<<< scanStartElement(): " + empty); 337 return empty; 338 339 } // scanStartElement():boolean 340 341 /** 342 * Scans the name of an element in a start or empty tag. 343 * 344 * @see #scanStartElement() 345 */ 346 protected void scanStartElementName () 347 throws IOException, XNIException { 348 // Note: namespace processing is on by default 349 fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART); 350 // Must skip spaces here because the DTD scanner 351 // would consume them at the end of the external subset. 352 fSawSpace = fEntityScanner.skipSpaces(); 353 } // scanStartElementName() 354 355 /** 356 * Scans the remainder of a start or empty tag after the element name. 357 * 358 * @see #scanStartElement 359 * @return True if element is empty. 360 */ 361 protected boolean scanStartElementAfterName() 362 throws IOException, XNIException { 363 364 // REVISIT - [Q] Why do we need this local variable? -- mrglavas 365 String rawname = fElementQName.rawname; 366 if (fBindNamespaces) { 367 fNamespaceContext.pushContext(); 368 if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) { 369 if (fPerformValidation) { 370 fErrorReporter.reportError( 371 XMLMessageFormatter.XML_DOMAIN, 372 "MSG_GRAMMAR_NOT_FOUND", 373 new Object[] { rawname }, 374 XMLErrorReporter.SEVERITY_ERROR); 375 376 if (fDoctypeName == null 377 || !fDoctypeName.equals(rawname)) { 378 fErrorReporter.reportError( 379 XMLMessageFormatter.XML_DOMAIN, 380 "RootElementTypeMustMatchDoctypedecl", 381 new Object[] { fDoctypeName, rawname }, 382 XMLErrorReporter.SEVERITY_ERROR); 383 } 384 } 385 } 386 } 387 388 // push element stack 389 fCurrentElement = fElementStack.pushElement(fElementQName); 390 391 // attributes 392 boolean empty = false; 393 fAttributes.removeAllAttributes(); 394 do { 395 396 // end tag? 397 int c = fEntityScanner.peekChar(); 398 if (c == '>') { 399 fEntityScanner.scanChar(null); 400 break; 401 } else if (c == '/') { 402 fEntityScanner.scanChar(null); 403 if (!fEntityScanner.skipChar('>', null)) { 404 reportFatalError( 405 "ElementUnterminated", 406 new Object[] { rawname }); 407 } 408 empty = true; 409 break; 410 } else if (!isValidNameStartChar(c) || !fSawSpace) { 411 // Second chance. Check if this character is a high 412 // surrogate of a valid name start character. 413 if (!isValidNameStartHighSurrogate(c) || !fSawSpace) { 414 reportFatalError( 415 "ElementUnterminated", 416 new Object[] { rawname }); 417 } 418 } 419 420 // attributes 421 scanAttribute(fAttributes); 422 423 // spaces 424 fSawSpace = fEntityScanner.skipSpaces(); 425 426 } while (true); 427 428 if (fBindNamespaces) { 429 // REVISIT: is it required? forbit xmlns prefix for element 430 if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) { 431 fErrorReporter.reportError( 432 XMLMessageFormatter.XMLNS_DOMAIN, 433 "ElementXMLNSPrefix", 434 new Object[] { fElementQName.rawname }, 435 XMLErrorReporter.SEVERITY_FATAL_ERROR); 436 } 437 438 // bind the element 439 String prefix = 440 fElementQName.prefix != null 441 ? fElementQName.prefix 442 : XMLSymbols.EMPTY_STRING; 443 // assign uri to the element 444 fElementQName.uri = fNamespaceContext.getURI(prefix); 445 // make sure that object in the element stack is updated as well 446 fCurrentElement.uri = fElementQName.uri; 447 448 if (fElementQName.prefix == null && fElementQName.uri != null) { 449 fElementQName.prefix = XMLSymbols.EMPTY_STRING; 450 // making sure that the object in the element stack is updated too. 451 fCurrentElement.prefix = XMLSymbols.EMPTY_STRING; 452 } 453 if (fElementQName.prefix != null && fElementQName.uri == null) { 454 fErrorReporter.reportError( 455 XMLMessageFormatter.XMLNS_DOMAIN, 456 "ElementPrefixUnbound", 457 new Object[] { 458 fElementQName.prefix, 459 fElementQName.rawname }, 460 XMLErrorReporter.SEVERITY_FATAL_ERROR); 461 } 462 463 // bind attributes (xmlns are already bound bellow) 464 int length = fAttributes.getLength(); 465 for (int i = 0; i < length; i++) { 466 fAttributes.getName(i, fAttributeQName); 467 468 String aprefix = 469 fAttributeQName.prefix != null 470 ? fAttributeQName.prefix 471 : XMLSymbols.EMPTY_STRING; 472 String uri = fNamespaceContext.getURI(aprefix); 473 // REVISIT: try removing the first "if" and see if it is faster. 474 // 475 if (fAttributeQName.uri != null 476 && fAttributeQName.uri == uri) { 477 continue; 478 } 479 if (aprefix != XMLSymbols.EMPTY_STRING) { 480 fAttributeQName.uri = uri; 481 if (uri == null) { 482 fErrorReporter.reportError( 483 XMLMessageFormatter.XMLNS_DOMAIN, 484 "AttributePrefixUnbound", 485 new Object[] { 486 fElementQName.rawname, 487 fAttributeQName.rawname, 488 aprefix }, 489 XMLErrorReporter.SEVERITY_FATAL_ERROR); 490 } 491 fAttributes.setURI(i, uri); 492 } 493 } 494 495 if (length > 1) { 496 QName name = fAttributes.checkDuplicatesNS(); 497 if (name != null) { 498 if (name.uri != null) { 499 fErrorReporter.reportError( 500 XMLMessageFormatter.XMLNS_DOMAIN, 501 "AttributeNSNotUnique", 502 new Object[] { 503 fElementQName.rawname, 504 name.localpart, 505 name.uri }, 506 XMLErrorReporter.SEVERITY_FATAL_ERROR); 507 } else { 508 fErrorReporter.reportError( 509 XMLMessageFormatter.XMLNS_DOMAIN, 510 "AttributeNotUnique", 511 new Object[] { 512 fElementQName.rawname, 513 name.rawname }, 514 XMLErrorReporter.SEVERITY_FATAL_ERROR); 515 } 516 } 517 } 518 } 519 520 // call handler 521 if (fDocumentHandler != null) { 522 if (empty) { 523 524 //decrease the markup depth.. 525 fMarkupDepth--; 526 527 // check that this element was opened in the same entity 528 if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { 529 reportFatalError( 530 "ElementEntityMismatch", 531 new Object[] { fCurrentElement.rawname }); 532 } 533 534 fDocumentHandler.emptyElement(fElementQName, fAttributes, null); 535 536 if (fBindNamespaces) { 537 fNamespaceContext.popContext(); 538 } 539 //pop the element off the stack.. 540 fElementStack.popElement(); 541 } else { 542 fDocumentHandler.startElement(fElementQName, fAttributes, null); 543 } 544 } 545 546 if (DEBUG_START_END_ELEMENT) 547 System.out.println("<<< scanStartElementAfterName(): " + empty); 548 return empty; 549 550 } // scanStartElementAfterName() 551 552 /** 553 * Scans an attribute. 554 * <p> 555 * <pre> 556 * [41] Attribute ::= Name Eq AttValue 557 * </pre> 558 * <p> 559 * <strong>Note:</strong> This method assumes that the next 560 * character on the stream is the first character of the attribute 561 * name. 562 * <p> 563 * <strong>Note:</strong> This method uses the fAttributeQName and 564 * fQName variables. The contents of these variables will be 565 * destroyed. 566 * 567 * @param attributes The attributes list for the scanned attribute. 568 */ 569 protected void scanAttribute(XMLAttributesImpl attributes) 570 throws IOException, XNIException { 571 if (DEBUG_START_END_ELEMENT) 572 System.out.println(">>> scanAttribute()"); 573 574 // name 575 fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME); 576 577 // equals 578 fEntityScanner.skipSpaces(); 579 if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) { 580 reportFatalError( 581 "EqRequiredInAttribute", 582 new Object[] { 583 fCurrentElement.rawname, 584 fAttributeQName.rawname }); 585 } 586 fEntityScanner.skipSpaces(); 587 588 // content 589 int attrIndex; 590 591 if (fBindNamespaces) { 592 attrIndex = attributes.getLength(); 593 attributes.addAttributeNS( 594 fAttributeQName, 595 XMLSymbols.fCDATASymbol, 596 null); 597 } else { 598 int oldLen = attributes.getLength(); 599 attrIndex = 600 attributes.addAttribute( 601 fAttributeQName, 602 XMLSymbols.fCDATASymbol, 603 null); 604 605 // WFC: Unique Att Spec 606 if (oldLen == attributes.getLength()) { 607 reportFatalError( 608 "AttributeNotUnique", 609 new Object[] { 610 fCurrentElement.rawname, 611 fAttributeQName.rawname }); 612 } 613 } 614 615 //REVISIT: one more case needs to be included: external PE and standalone is no 616 boolean isVC = fHasExternalDTD && !fStandalone; 617 618 /** 619 * Determine whether this is a namespace declaration that will be subject 620 * to the name limit check in the scanAttributeValue operation. 621 * Namespace declaration format: xmlns="..." or xmlns:prefix="..." 622 * Note that prefix:xmlns="..." isn't a namespace. 623 */ 624 String localpart = fAttributeQName.localpart; 625 String prefix = fAttributeQName.prefix != null 626 ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING; 627 boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS || 628 prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS); 629 630 scanAttributeValue(this.fTempString, fTempString2, fAttributeQName.rawname, 631 isVC, fCurrentElement.rawname, isNSDecl); 632 String value = fTempString.toString(); 633 attributes.setValue(attrIndex, value); 634 attributes.setNonNormalizedValue(attrIndex, fTempString2.toString()); 635 attributes.setSpecified(attrIndex, true); 636 637 // record namespace declarations if any. 638 if (fBindNamespaces) { 639 if (isNSDecl) { 640 if (value.length() > fXMLNameLimit) { 641 fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, 642 "MaxXMLNameLimit", 643 new Object[]{value, value.length(), fXMLNameLimit, 644 fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.MAX_NAME_LIMIT)}, 645 XMLErrorReporter.SEVERITY_FATAL_ERROR); 646 } 647 // get the internalized value of this attribute 648 String uri = fSymbolTable.addSymbol(value); 649 650 // 1. "xmlns" can't be bound to any namespace 651 if (prefix == XMLSymbols.PREFIX_XMLNS 652 && localpart == XMLSymbols.PREFIX_XMLNS) { 653 fErrorReporter.reportError( 654 XMLMessageFormatter.XMLNS_DOMAIN, 655 "CantBindXMLNS", 656 new Object[] { fAttributeQName }, 657 XMLErrorReporter.SEVERITY_FATAL_ERROR); 658 } 659 660 // 2. the namespace for "xmlns" can't be bound to any prefix 661 if (uri == NamespaceContext.XMLNS_URI) { 662 fErrorReporter.reportError( 663 XMLMessageFormatter.XMLNS_DOMAIN, 664 "CantBindXMLNS", 665 new Object[] { fAttributeQName }, 666 XMLErrorReporter.SEVERITY_FATAL_ERROR); 667 } 668 669 // 3. "xml" can't be bound to any other namespace than it's own 670 if (localpart == XMLSymbols.PREFIX_XML) { 671 if (uri != NamespaceContext.XML_URI) { 672 fErrorReporter.reportError( 673 XMLMessageFormatter.XMLNS_DOMAIN, 674 "CantBindXML", 675 new Object[] { fAttributeQName }, 676 XMLErrorReporter.SEVERITY_FATAL_ERROR); 677 } 678 } 679 // 4. the namespace for "xml" can't be bound to any other prefix 680 else { 681 if (uri == NamespaceContext.XML_URI) { 682 fErrorReporter.reportError( 683 XMLMessageFormatter.XMLNS_DOMAIN, 684 "CantBindXML", 685 new Object[] { fAttributeQName }, 686 XMLErrorReporter.SEVERITY_FATAL_ERROR); 687 } 688 } 689 690 prefix = 691 localpart != XMLSymbols.PREFIX_XMLNS 692 ? localpart 693 : XMLSymbols.EMPTY_STRING; 694 695 // Declare prefix in context. Removing the association between a prefix and a 696 // namespace name is permitted in XML 1.1, so if the uri value is the empty string, 697 // the prefix is being unbound. -- mrglavas 698 fNamespaceContext.declarePrefix( 699 prefix, 700 uri.length() != 0 ? uri : null); 701 // bind namespace attribute to a namespace 702 attributes.setURI( 703 attrIndex, 704 fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS)); 705 706 } else { 707 // attempt to bind attribute 708 if (fAttributeQName.prefix != null) { 709 attributes.setURI( 710 attrIndex, 711 fNamespaceContext.getURI(fAttributeQName.prefix)); 712 } 713 } 714 } 715 716 if (DEBUG_START_END_ELEMENT) 717 System.out.println("<<< scanAttribute()"); 718 } // scanAttribute(XMLAttributes) 719 720 /** 721 * Scans an end element. 722 * <p> 723 * <pre> 724 * [42] ETag ::= '</' Name S? '>' 725 * </pre> 726 * <p> 727 * <strong>Note:</strong> This method uses the fElementQName variable. 728 * The contents of this variable will be destroyed. The caller should 729 * copy the needed information out of this variable before calling 730 * this method. 731 * 732 * @return The element depth. 733 */ 734 protected int scanEndElement() throws IOException, XNIException { 735 if (DEBUG_START_END_ELEMENT) 736 System.out.println(">>> scanEndElement()"); 737 738 // pop context 739 QName endElementName = fElementStack.popElement(); 740 741 // Take advantage of the fact that next string _should_ be "fElementQName.rawName", 742 //In scanners most of the time is consumed on checks done for XML characters, we can 743 // optimize on it and avoid the checks done for endElement, 744 //we will also avoid symbol table lookup - neeraj.bajaj@sun.com 745 746 // this should work both for namespace processing true or false... 747 748 //REVISIT: if the string is not the same as expected.. we need to do better error handling.. 749 //We can skip this for now... In any case if the string doesn't match -- document is not well formed. 750 751 if (!fEntityScanner.skipString(endElementName.rawname)) { 752 reportFatalError( 753 "ETagRequired", 754 new Object[] { endElementName.rawname }); 755 } 756 757 // end 758 fEntityScanner.skipSpaces(); 759 if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) { 760 reportFatalError( 761 "ETagUnterminated", 762 new Object[] { endElementName.rawname }); 763 } 764 fMarkupDepth--; 765 766 //we have increased the depth for two markup "<" characters 767 fMarkupDepth--; 768 769 // check that this element was opened in the same entity 770 if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) { 771 reportFatalError( 772 "ElementEntityMismatch", 773 new Object[] { endElementName.rawname }); 774 } 775 776 // call handler 777 if (fDocumentHandler != null) { 778 fDocumentHandler.endElement(endElementName, null); 779 780 /*if (fBindNamespaces) { 781 fNamespaceContext.popContext(); 782 }*/ 783 784 } 785 786 if(dtdGrammarUtil != null) 787 dtdGrammarUtil.endElement(endElementName); 788 789 return fMarkupDepth; 790 791 } // scanEndElement():int 792 793 public void reset(XMLComponentManager componentManager) 794 throws XMLConfigurationException { 795 796 super.reset(componentManager); 797 fPerformValidation = false; 798 fBindNamespaces = false; 799 } 800 801 /** Creates a content Driver. */ 802 protected Driver createContentDriver() { 803 return new NS11ContentDriver(); 804 } // createContentDriver():Driver 805 806 807 /** return the next state on the input 808 * 809 * @return int 810 */ 811 812 public int next() throws IOException, XNIException { 813 //since namespace context should still be valid when the parser is at the end element state therefore 814 //we pop the context only when next() has been called after the end element state was encountered. - nb. 815 816 if((fScannerLastState == XMLEvent.END_ELEMENT) && fBindNamespaces){ 817 fScannerLastState = -1; 818 fNamespaceContext.popContext(); 819 } 820 821 return fScannerLastState = super.next(); 822 } 823 824 825 /** 826 * Driver to handle content scanning. 827 */ 828 protected final class NS11ContentDriver extends ContentDriver { 829 /** 830 * Scan for root element hook. This method is a hook for 831 * subclasses to add code that handles scanning for the root 832 * element. This method will also attempt to remove DTD validator 833 * from the pipeline, if there is no DTD grammar. If DTD validator 834 * is no longer in the pipeline bind namespaces in the scanner. 835 * 836 * 837 * @return True if the caller should stop and return true which 838 * allows the scanner to switch to a new scanning 839 * Driver. A return value of false indicates that 840 * the content Driver should continue as normal. 841 */ 842 protected boolean scanRootElementHook() 843 throws IOException, XNIException { 844 845 if (fExternalSubsetResolver != null && !fSeenDoctypeDecl 846 && !fDisallowDoctype && (fValidation || fLoadExternalDTD)) { 847 scanStartElementName(); 848 resolveExternalSubsetAndRead(); 849 reconfigurePipeline(); 850 if (scanStartElementAfterName()) { 851 setScannerState(SCANNER_STATE_TRAILING_MISC); 852 setDriver(fTrailingMiscDriver); 853 return true; 854 } 855 } 856 else { 857 reconfigurePipeline(); 858 if (scanStartElement()) { 859 setScannerState(SCANNER_STATE_TRAILING_MISC); 860 setDriver(fTrailingMiscDriver); 861 return true; 862 } 863 } 864 return false; 865 866 } // scanRootElementHook():boolean 867 868 /** 869 * Re-configures pipeline by removing the DTD validator 870 * if no DTD grammar exists. If no validator exists in the 871 * pipeline or there is no DTD grammar, namespace binding 872 * is performed by the scanner in the enclosing class. 873 */ 874 private void reconfigurePipeline() { 875 if (fDTDValidator == null) { 876 fBindNamespaces = true; 877 } 878 else if (!fDTDValidator.hasGrammar()) { 879 fBindNamespaces = true; 880 fPerformValidation = fDTDValidator.validate(); 881 // re-configure pipeline 882 XMLDocumentSource source = fDTDValidator.getDocumentSource(); 883 XMLDocumentHandler handler = fDTDValidator.getDocumentHandler(); 884 source.setDocumentHandler(handler); 885 if (handler != null) 886 handler.setDocumentSource(source); 887 fDTDValidator.setDocumentSource(null); 888 fDTDValidator.setDocumentHandler(null); 889 } 890 } // reconfigurePipeline() 891 } 892 }