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