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