View Javadoc
1   /*
2    * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3    */
4   
5   /*
6    * Copyright 2005 The Apache Software Foundation.
7    *
8    * Licensed under the Apache License, Version 2.0 (the "License");
9    * you may not use this file except in compliance with the License.
10   * You may obtain a copy of the License at
11   *
12   *      http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package com.sun.org.apache.xerces.internal.impl;
22  
23  import java.io.IOException;
24  import com.sun.org.apache.xerces.internal.xni.XMLString;
25  import com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidatorFilter;
26  import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
27  import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
28  import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl;
29  import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
30  import com.sun.org.apache.xerces.internal.util.XMLSymbols;
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.XNIException;
34  import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
35  import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
36  import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
37  import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
38  import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
39  import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
40  
41  import javax.xml.stream.XMLInputFactory;
42  import javax.xml.stream.XMLStreamConstants;
43  import javax.xml.stream.events.XMLEvent;
44  
45  /**
46   * This class adds the functionality of namespace processing.
47   *
48   * This class has been modified as per the new design which is more suited to
49   * efficiently build pull parser. Lot of improvements have been done and
50   * the code has been added to support stax functionality/features.
51   *
52   *
53   * This class scans an XML document, checks if document has a DTD, and if
54   * DTD is not found the scanner will remove the DTD Validator from the pipeline and perform
55   * namespace binding.
56   *
57   *
58   * @author Neeraj Bajaj, Sun Microsystems
59   * @author Venugopal Rao K, Sun Microsystems
60   * @author Elena Litani, IBM
61   * @version $Id: XMLNSDocumentScannerImpl.java,v 1.11 2010-11-01 04:39:41 joehw Exp $
62   */
63  public class XMLNSDocumentScannerImpl
64          extends XMLDocumentScannerImpl {
65  
66      /**
67       * If is true, the dtd validator is no longer in the pipeline
68       * and the scanner should bind namespaces
69       */
70      protected boolean fBindNamespaces;
71  
72      /** If validating parser, make sure we report an error in the
73       *   scanner if DTD grammar is missing.*/
74      protected boolean fPerformValidation;
75  
76  
77      /** Default value of this feature is false, when in Stax mode this should be true */
78      protected boolean fNotAddNSDeclAsAttribute = false;
79  
80      /** DTD validator */
81       private XMLDTDValidatorFilter fDTDValidator;
82  
83       /** xmlns, default Namespace, declared */
84       private boolean fXmlnsDeclared = false;
85  
86      /** Resets the fields of this scanner.
87       */
88      public void reset(PropertyManager propertyManager) {
89          setPropertyManager(propertyManager);
90          super.reset(propertyManager);
91          fBindNamespaces = false;
92          fNotAddNSDeclAsAttribute = !((Boolean)propertyManager.getProperty(Constants.ADD_NAMESPACE_DECL_AS_ATTRIBUTE)).booleanValue();
93      }
94  
95      public void reset(XMLComponentManager componentManager)
96      throws XMLConfigurationException {
97          super.reset(componentManager);
98          fNotAddNSDeclAsAttribute = false ;
99          fPerformValidation = false;
100         fBindNamespaces = false;
101     }
102 
103     /** return the next state on the input
104      *
105      * @return int
106      */
107 
108     public int next() throws IOException, XNIException {
109         //since namespace context should still be valid when the parser is at the end element state therefore
110         //we pop the context only when next() has been called after the end element state was encountered. - nb.
111 
112         if((fScannerLastState == XMLEvent.END_ELEMENT) && fBindNamespaces){
113             fScannerLastState = -1;
114             fNamespaceContext.popContext();
115         }
116 
117         return fScannerLastState = super.next();
118     }
119 
120     /**
121      * The scanner is responsible for removing DTD validator
122      * from the pipeline if it is not needed.
123      *
124      * @param previous The filter component before DTDValidator
125      * @param dtdValidator
126      *                 The DTDValidator
127      * @param next     The documentHandler after the DTDValidator
128      */
129     public void setDTDValidator(XMLDTDValidatorFilter dtd){
130         fDTDValidator = dtd;
131     }
132 
133 
134 
135     /**
136      * Scans a start element. This method will handle the binding of
137      * namespace information and notifying the handler of the start
138      * of the element.
139      * <p>
140      * <pre>
141      * [44] EmptyElemTag ::= '&lt;' Name (S Attribute)* S? '/>'
142      * [40] STag ::= '&lt;' Name (S Attribute)* S? '>'
143      * </pre>
144      * <p>
145      * <strong>Note:</strong> This method assumes that the leading
146      * '&lt;' character has been consumed.
147      * <p>
148      * <strong>Note:</strong> This method uses the fElementQName and
149      * fAttributes variables. The contents of these variables will be
150      * destroyed. The caller should copy important information out of
151      * these variables before calling this method.
152      *
153      * @return True if element is empty. (i.e. It matches
154      *          production [44].
155      */
156     protected boolean scanStartElement()
157     throws IOException, XNIException {
158 
159         if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanStartElement()");
160         //when skipping is true and no more elements should be added
161         if(fSkip && !fAdd){
162             //get the stored element -- if everything goes right this should match the
163             //token in the buffer
164 
165             QName name = fElementStack.getNext();
166 
167             if(DEBUG_SKIP_ALGORITHM){
168                 System.out.println("Trying to skip String = " + name.rawname);
169             }
170 
171             //Be conservative -- if skipping fails -- stop.
172             fSkip = fEntityScanner.skipString(name.rawname); // skipQElement(name);
173 
174             if(fSkip){
175                 if(DEBUG_SKIP_ALGORITHM){
176                     System.out.println("Element SUCESSFULLY skipped = " + name.rawname);
177                 }
178                 fElementStack.push();
179                 fElementQName = name;
180             }else{
181                 //if skipping fails reposition the stack or fallback to normal way of processing
182                 fElementStack.reposition();
183                 if(DEBUG_SKIP_ALGORITHM){
184                     System.out.println("Element was NOT skipped, REPOSITIONING stack" );
185                 }
186             }
187         }
188 
189         //we are still at the stage of adding elements
190         //the elements were not matched or
191         //fSkip is not set to true
192         if(!fSkip || fAdd){
193             //get the next element from the stack
194             fElementQName = fElementStack.nextElement();
195             // There are two variables,fNamespaces and fBindNamespaces
196             //StAX uses XMLNSDocumentScannerImpl so this distinction needs to be maintained
197             if (fNamespaces) {
198                 fEntityScanner.scanQName(fElementQName);
199             } else {
200                 String name = fEntityScanner.scanName();
201                 fElementQName.setValues(null, name, name, null);
202             }
203 
204             if(DEBUG)System.out.println("Element scanned in start element is " + fElementQName.toString());
205             if(DEBUG_SKIP_ALGORITHM){
206                 if(fAdd){
207                     System.out.println("Elements are being ADDED -- elemet added is = " + fElementQName.rawname + " at count = " + fElementStack.fCount);
208                 }
209             }
210 
211         }
212 
213         //when the elements are being added , we need to check if we are set for skipping the elements
214         if(fAdd){
215             //this sets the value of fAdd variable
216             fElementStack.matchElement(fElementQName);
217         }
218 
219         //xxx: We dont need another pointer, fCurrentElement, we can use fElementQName
220         fCurrentElement = fElementQName;
221 
222         String rawname = fElementQName.rawname;
223         if (fBindNamespaces) {
224             fNamespaceContext.pushContext();
225             if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
226                 if (fPerformValidation) {
227                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
228                             "MSG_GRAMMAR_NOT_FOUND",
229                             new Object[]{ rawname},
230                             XMLErrorReporter.SEVERITY_ERROR);
231 
232                             if (fDoctypeName == null || !fDoctypeName.equals(rawname)) {
233                                 fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
234                                         "RootElementTypeMustMatchDoctypedecl",
235                                         new Object[]{fDoctypeName, rawname},
236                                         XMLErrorReporter.SEVERITY_ERROR);
237                             }
238                 }
239             }
240         }
241 
242 
243         fEmptyElement = false;
244         fAttributes.removeAllAttributes();
245 
246         if(!seekCloseOfStartTag()){
247             fReadingAttributes = true;
248             fAttributeCacheUsedCount =0;
249             fStringBufferIndex =0;
250             fAddDefaultAttr = true;
251             fXmlnsDeclared = false;
252 
253             do {
254                 scanAttribute(fAttributes);
255                 if (fSecurityManager != null && (!fSecurityManager.isNoLimit(fElementAttributeLimit)) &&
256                         fAttributes.getLength() > fElementAttributeLimit){
257                     fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
258                                                  "ElementAttributeLimit",
259                                                  new Object[]{rawname, fElementAttributeLimit },
260                                                  XMLErrorReporter.SEVERITY_FATAL_ERROR );
261                 }
262 
263             } while (!seekCloseOfStartTag());
264             fReadingAttributes=false;
265         }
266 
267         if (fBindNamespaces) {
268             // REVISIT: is it required? forbit xmlns prefix for element
269             if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
270                 fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
271                         "ElementXMLNSPrefix",
272                         new Object[]{fElementQName.rawname},
273                         XMLErrorReporter.SEVERITY_FATAL_ERROR);
274             }
275 
276             // bind the element
277             String prefix = fElementQName.prefix != null
278                     ? fElementQName.prefix : XMLSymbols.EMPTY_STRING;
279             // assign uri to the element
280             fElementQName.uri = fNamespaceContext.getURI(prefix);
281             // make sure that object in the element stack is updated as well
282             fCurrentElement.uri = fElementQName.uri;
283 
284             if (fElementQName.prefix == null && fElementQName.uri != null) {
285                 fElementQName.prefix = XMLSymbols.EMPTY_STRING;
286             }
287             if (fElementQName.prefix != null && fElementQName.uri == null) {
288                 fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
289                         "ElementPrefixUnbound",
290                         new Object[]{fElementQName.prefix, fElementQName.rawname},
291                         XMLErrorReporter.SEVERITY_FATAL_ERROR);
292             }
293 
294             // bind attributes (xmlns are already bound bellow)
295             int length = fAttributes.getLength();
296             // fLength = 0; //initialize structure
297             for (int i = 0; i < length; i++) {
298                 fAttributes.getName(i, fAttributeQName);
299 
300                 String aprefix = fAttributeQName.prefix != null
301                         ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
302                 String uri = fNamespaceContext.getURI(aprefix);
303                 // REVISIT: try removing the first "if" and see if it is faster.
304                 //
305                 if (fAttributeQName.uri != null && fAttributeQName.uri == uri) {
306                     // checkDuplicates(fAttributeQName, fAttributes);
307                     continue;
308                 }
309                 if (aprefix != XMLSymbols.EMPTY_STRING) {
310                     fAttributeQName.uri = uri;
311                     if (uri == null) {
312                         fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
313                                 "AttributePrefixUnbound",
314                                 new Object[]{fElementQName.rawname,fAttributeQName.rawname,aprefix},
315                                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
316                     }
317                     fAttributes.setURI(i, uri);
318                     // checkDuplicates(fAttributeQName, fAttributes);
319                 }
320             }
321 
322             if (length > 1) {
323                 QName name = fAttributes.checkDuplicatesNS();
324                 if (name != null) {
325                     if (name.uri != null) {
326                         fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
327                                 "AttributeNSNotUnique",
328                                 new Object[]{fElementQName.rawname, name.localpart, name.uri},
329                                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
330                     } else {
331                         fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
332                                 "AttributeNotUnique",
333                                 new Object[]{fElementQName.rawname, name.rawname},
334                                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
335                     }
336                 }
337             }
338         }
339 
340 
341         if (fEmptyElement) {
342             //decrease the markup depth..
343             fMarkupDepth--;
344 
345             // check that this element was opened in the same entity
346             if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
347                 reportFatalError("ElementEntityMismatch",
348                         new Object[]{fCurrentElement.rawname});
349             }
350             // call handler
351             if (fDocumentHandler != null) {
352                 if(DEBUG)
353                     System.out.println("emptyElement = " + fElementQName);
354 
355                 fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
356             }
357 
358             //We should not be popping out the context here in endELement becaause the namespace context is still
359             //valid when parser is at the endElement state.
360             fScanEndElement = true;
361             //if (fBindNamespaces) {
362             //  fNamespaceContext.popContext();
363             //}
364 
365             //pop the element off the stack..
366             fElementStack.popElement();
367 
368         } else {
369 
370             if(dtdGrammarUtil != null)
371                 dtdGrammarUtil.startElement(fElementQName,fAttributes);
372             if(fDocumentHandler != null){
373                 //complete element and attributes are traversed in this function so we can send a callback
374                 //here.
375                 //<strong>we shouldn't be sending callback in scanDocument()</strong>
376                 if(DEBUG)
377                     System.out.println("startElement = " + fElementQName);
378                 fDocumentHandler.startElement(fElementQName, fAttributes, null);
379             }
380         }
381 
382 
383         if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanStartElement(): "+fEmptyElement);
384         return fEmptyElement;
385 
386     } // scanStartElement():boolean
387 
388 
389 
390     /**
391      * Scans an attribute.
392      * <p>
393      * <pre>
394      * [41] Attribute ::= Name Eq AttValue
395      * </pre>
396      * <p>
397      * <strong>Note:</strong> This method assumes that the next
398      * character on the stream is the first character of the attribute
399      * name.
400      * <p>
401      * <strong>Note:</strong> This method uses the fAttributeQName and
402      * fQName variables. The contents of these variables will be
403      * destroyed.
404      *
405      * @param attributes The attributes list for the scanned attribute.
406      */
407     protected void scanAttribute(XMLAttributesImpl attributes)
408     throws IOException, XNIException {
409         if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()");
410 
411         // name
412         fEntityScanner.scanQName(fAttributeQName);
413 
414         // equals
415         fEntityScanner.skipSpaces();
416         if (!fEntityScanner.skipChar('=')) {
417             reportFatalError("EqRequiredInAttribute",
418                     new Object[]{fCurrentElement.rawname,fAttributeQName.rawname});
419         }
420         fEntityScanner.skipSpaces();
421 
422         // content
423         int attrIndex = 0 ;
424 
425 
426         //REVISIT: one more case needs to be included: external PE and standalone is no
427         boolean isVC =  fHasExternalDTD && !fStandalone;
428 
429         // REVISIT: it seems that this function should not take attributes, and length
430         //fTempString would store attribute value
431         ///fTempString2 would store attribute non-normalized value
432 
433         //this function doesn't use 'attIndex'. We are adding the attribute later
434         //after we have figured out that current attribute is not namespace declaration
435         //since scanAttributeValue doesn't use attIndex parameter therefore we
436         //can safely add the attribute later..
437         XMLString tmpStr = getString();
438         scanAttributeValue(tmpStr, fTempString2,
439                 fAttributeQName.rawname, attributes,
440                 attrIndex, isVC);
441 
442         String value = null;
443         //fTempString.toString();
444 
445         // record namespace declarations if any.
446         if (fBindNamespaces) {
447 
448             String localpart = fAttributeQName.localpart;
449             String prefix = fAttributeQName.prefix != null
450                     ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
451             // when it's of form xmlns="..." or xmlns:prefix="...",
452             // it's a namespace declaration. but prefix:xmlns="..." isn't.
453             if (prefix == XMLSymbols.PREFIX_XMLNS ||
454                     prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS) {
455 
456                 // get the internalized value of this attribute
457                 String uri = fSymbolTable.addSymbol(tmpStr.ch,tmpStr.offset,tmpStr.length);
458                 value = uri;
459                 // 1. "xmlns" can't be bound to any namespace
460                 if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == XMLSymbols.PREFIX_XMLNS) {
461                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
462                             "CantBindXMLNS",
463                             new Object[]{fAttributeQName},
464                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
465                 }
466 
467                 // 2. the namespace for "xmlns" can't be bound to any prefix
468                 if (uri == NamespaceContext.XMLNS_URI) {
469                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
470                             "CantBindXMLNS",
471                             new Object[]{fAttributeQName},
472                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
473                 }
474 
475                 // 3. "xml" can't be bound to any other namespace than it's own
476                 if (localpart == XMLSymbols.PREFIX_XML) {
477                     if (uri != NamespaceContext.XML_URI) {
478                         fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
479                                 "CantBindXML",
480                                 new Object[]{fAttributeQName},
481                                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
482                     }
483                 }
484                 // 4. the namespace for "xml" can't be bound to any other prefix
485                 else {
486                     if (uri ==NamespaceContext.XML_URI) {
487                         fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
488                                 "CantBindXML",
489                                 new Object[]{fAttributeQName},
490                                 XMLErrorReporter.SEVERITY_FATAL_ERROR);
491                     }
492                 }
493                 prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart : XMLSymbols.EMPTY_STRING;
494                 //set it equal to XMLSymbols.PREFIX_XMLNS when namespace declaration
495                 // is of type xmlns = "..", in this case prefix = "" and localname = XMLSymbols.PREFIX_XMLNS
496                 //this special behavior is because of dependency on this behavior in DOM components
497                 if(prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS){
498                     fAttributeQName.prefix = XMLSymbols.PREFIX_XMLNS;
499                 }
500                 // http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-prefix
501                 // We should only report an error if there is a prefix,
502                 // that is, the local part is not "xmlns". -SG
503                 if (uri == XMLSymbols.EMPTY_STRING && localpart != XMLSymbols.PREFIX_XMLNS) {
504                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
505                             "EmptyPrefixedAttName",
506                             new Object[]{fAttributeQName},
507                             XMLErrorReporter.SEVERITY_FATAL_ERROR);
508                 }
509 
510                 // check for duplicate prefix bindings
511                 if (((com.sun.org.apache.xerces.internal.util.NamespaceSupport) fNamespaceContext).containsPrefixInCurrentContext(prefix)) {
512                     reportFatalError("AttributeNotUnique",
513                             new Object[]{fCurrentElement.rawname,
514                             fAttributeQName.rawname});
515                 }
516 
517                 // declare prefix in context
518                 boolean declared = fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : null);
519 
520                 // check for duplicate xmlns declarations
521                 if (!declared) { // by convention, prefix == "xmlns" | "xml"
522                     // error if duplicate declaration
523                     if (fXmlnsDeclared) {
524                         reportFatalError("AttributeNotUnique",
525                                 new Object[]{fCurrentElement.rawname,
526                                 fAttributeQName.rawname});
527                     }
528 
529                     // xmlns declared
530                     fXmlnsDeclared = true;
531                 }
532 
533                 //xerces internals (XSAttributeChecker) has dependency on namespace declaration returned
534                 //as part of XMLAttributes.
535                 //addition of namespace declaration to the attribute list is controlled by fNotAddNSDeclAsAttribute
536                 //feature. This is required in Stax where namespace declarations are not considered as attribute
537 
538                 if(fNotAddNSDeclAsAttribute){
539                     return ;
540                 }
541             }
542         }
543 
544         //add the attributes to the list of attributes
545         if (fBindNamespaces) {
546             attrIndex = attributes.getLength();
547             attributes.addAttributeNS(fAttributeQName, XMLSymbols.fCDATASymbol, null);
548         } else {
549             int oldLen = attributes.getLength();
550             attrIndex = attributes.addAttribute(fAttributeQName, XMLSymbols.fCDATASymbol, null);
551 
552             // WFC: Unique Att Spec
553             if (oldLen == attributes.getLength()) {
554                 reportFatalError("AttributeNotUnique",
555                         new Object[]{fCurrentElement.rawname,
556                                 fAttributeQName.rawname});
557             }
558         }
559 
560         attributes.setValue(attrIndex, value,tmpStr);
561         //attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
562         //removing  as we are not using non-normalized values . -Venu
563         attributes.setSpecified(attrIndex, true);
564 
565         // attempt to bind attribute
566         if (fAttributeQName.prefix != null) {
567             attributes.setURI(attrIndex, fNamespaceContext.getURI(fAttributeQName.prefix));
568         }
569 
570         if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +"<<< scanAttribute()");
571     } // scanAttribute(XMLAttributes)
572 
573 
574 
575 
576 
577     /** Creates a content driver. */
578     protected Driver createContentDriver() {
579         return new NSContentDriver();
580     } // createContentDriver():Driver
581 
582     /**
583      * Driver to handle content scanning.
584      */
585     protected final class NSContentDriver
586             extends ContentDriver {
587         /**
588          * Scan for root element hook. This method is a hook for
589          * subclasses to add code that handles scanning for the root
590          * element. This method will also attempt to remove DTD validator
591          * from the pipeline, if there is no DTD grammar. If DTD validator
592          * is no longer in the pipeline bind namespaces in the scanner.
593          *
594          *
595          * @return True if the caller should stop and return true which
596          *          allows the scanner to switch to a new scanning
597          *          driver. A return value of false indicates that
598          *          the content driver should continue as normal.
599          */
600         protected boolean scanRootElementHook()
601         throws IOException, XNIException {
602 
603             reconfigurePipeline();
604             if (scanStartElement()) {
605                 setScannerState(SCANNER_STATE_TRAILING_MISC);
606                 setDriver(fTrailingMiscDriver);
607                 return true;
608             }
609             return false;
610 
611         } // scanRootElementHook():boolean
612 
613         /**
614          * Re-configures pipeline by removing the DTD validator
615          * if no DTD grammar exists. If no validator exists in the
616          * pipeline or there is no DTD grammar, namespace binding
617          * is performed by the scanner in the enclosing class.
618          */
619         private void reconfigurePipeline() {
620             //fDTDValidator will be null in Stax mode
621             if (fNamespaces && fDTDValidator == null) {
622                 fBindNamespaces = true;
623             }
624             else if (fNamespaces && !fDTDValidator.hasGrammar() ) {
625                 fBindNamespaces = true;
626                 fPerformValidation = fDTDValidator.validate();
627                 // re-configure pipeline by removing DTDValidator
628                 XMLDocumentSource source = fDTDValidator.getDocumentSource();
629                 XMLDocumentHandler handler = fDTDValidator.getDocumentHandler();
630                 source.setDocumentHandler(handler);
631                 if (handler != null)
632                     handler.setDocumentSource(source);
633                 fDTDValidator.setDocumentSource(null);
634                 fDTDValidator.setDocumentHandler(null);
635             }
636         } // reconfigurePipeline()
637     }
638 
639 } // class XMLNSDocumentScannerImpl