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