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 ::= '&lt;' Name (S Attribute)* S? '/>'
 120      * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
 121      * </pre>
 122      * <p>
 123      * <strong>Note:</strong> This method assumes that the leading
 124      * '&lt;' 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 ::= '&lt;/' 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. 
 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 }