1 /*
2  * Copyright (c) 2003, 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 package com.sun.org.apache.xerces.internal.impl;
22 
23 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
24 import com.sun.org.apache.xerces.internal.util.SymbolTable;
25 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
26 import com.sun.org.apache.xerces.internal.xni.Augmentations;
27 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
28 import com.sun.org.apache.xerces.internal.xni.QName;
29 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
30 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
31 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
32 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
33 import com.sun.org.apache.xerces.internal.xni.XMLString;
34 import com.sun.org.apache.xerces.internal.xni.XNIException;
35 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
36 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
37 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
38 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
39 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
40 
41 /**
42  * This class performs namespace binding on the startElement and endElement
43  * method calls and passes all other methods through to the registered
44  * document handler. This class can be configured to only pass the
45  * start and end prefix mappings (start/endPrefixMapping).
46  * <p>
47  * This component requires the following features and properties from the
48  * component manager that uses it:
49  * <ul>
50  *  <li>http://xml.org/sax/features/namespaces</li>
51  *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
52  *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
53  * </ul>
54  *
55  * @xerces.internal
56  *
57  * @author Andy Clark, IBM
58  *
59  * @LastModified: Nov 2017
60  */
61 public class XMLNamespaceBinder
62     implements XMLComponent, XMLDocumentFilter {
63 
64     //
65     // Constants
66     //
67 
68     // feature identifiers
69 
70     /** Feature identifier: namespaces. */
71     protected static final String NAMESPACES =
72         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
73 
74     // property identifiers
75 
76     /** Property identifier: symbol table. */
77     protected static final String SYMBOL_TABLE =
78         Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
79 
80     /** Property identifier: error reporter. */
81     protected static final String ERROR_REPORTER =
82         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
83 
84     // recognized features and properties
85 
86     /** Recognized features. */
87     private static final String[] RECOGNIZED_FEATURES = {
88         NAMESPACES,
89     };
90 
91     /** Feature defaults. */
92     private static final Boolean[] FEATURE_DEFAULTS = {
93         null,
94     };
95 
96     /** Recognized properties. */
97     private static final String[] RECOGNIZED_PROPERTIES = {
98         SYMBOL_TABLE,
99         ERROR_REPORTER,
100     };
101 
102     /** Property defaults. */
103     private static final Object[] PROPERTY_DEFAULTS = {
104         null,
105         null,
106     };
107 
108     //
109     // Data
110     //
111 
112     // features
113 
114     /** Namespaces. */
115     protected boolean fNamespaces;
116 
117     // properties
118 
119     /** Symbol table. */
120     protected SymbolTable fSymbolTable;
121 
122     /** Error reporter. */
123     protected XMLErrorReporter fErrorReporter;
124 
125     // handlers
126 
127     /** Document handler. */
128     protected XMLDocumentHandler fDocumentHandler;
129 
130     protected XMLDocumentSource fDocumentSource;
131 
132     // settings
133 
134     /** Only pass start and end prefix mapping events. */
135     protected boolean fOnlyPassPrefixMappingEvents;
136 
137     // shared context
138 
139     /** Namespace context. */
140     private NamespaceContext fNamespaceContext;
141 
142     // temp vars
143 
144     /** Attribute QName. */
145     private QName fAttributeQName = new QName();
146 
147     //
148     // Constructors
149     //
150 
151     /** Default constructor. */
XMLNamespaceBinder()152     public XMLNamespaceBinder() {
153     } // <init>()
154 
155     //
156     // Public methods
157     //
158 
159     // settings
160 
161     /**
162      * Sets whether the namespace binder only passes the prefix mapping
163      * events to the registered document handler or passes all document
164      * events.
165      *
166      * @param onlyPassPrefixMappingEvents True to pass only the prefix
167      *                                    mapping events; false to pass
168      *                                    all events.
169      */
setOnlyPassPrefixMappingEvents(boolean onlyPassPrefixMappingEvents)170     public void setOnlyPassPrefixMappingEvents(boolean onlyPassPrefixMappingEvents) {
171         fOnlyPassPrefixMappingEvents = onlyPassPrefixMappingEvents;
172     } // setOnlyPassPrefixMappingEvents(boolean)
173 
174     /**
175      * Returns true if the namespace binder only passes the prefix mapping
176      * events to the registered document handler; false if the namespace
177      * binder passes all document events.
178      */
getOnlyPassPrefixMappingEvents()179     public boolean getOnlyPassPrefixMappingEvents() {
180         return fOnlyPassPrefixMappingEvents;
181     } // getOnlyPassPrefixMappingEvents():boolean
182 
183     //
184     // XMLComponent methods
185     //
186 
187     /**
188      * Resets the component. The component can query the component manager
189      * about any features and properties that affect the operation of the
190      * component.
191      *
192      * @param componentManager The component manager.
193      *
194      * @throws SAXException Thrown by component on initialization error.
195      *                      For example, if a feature or property is
196      *                      required for the operation of the component, the
197      *                      component manager may throw a
198      *                      SAXNotRecognizedException or a
199      *                      SAXNotSupportedException.
200      */
reset(XMLComponentManager componentManager)201     public void reset(XMLComponentManager componentManager)
202         throws XNIException {
203 
204         // features
205         fNamespaces = componentManager.getFeature(NAMESPACES, true);
206 
207         // Xerces properties
208         fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
209         fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
210 
211     } // reset(XMLComponentManager)
212 
213     /**
214      * Returns a list of feature identifiers that are recognized by
215      * this component. This method may return null if no features
216      * are recognized by this component.
217      */
getRecognizedFeatures()218     public String[] getRecognizedFeatures() {
219         return RECOGNIZED_FEATURES.clone();
220     } // getRecognizedFeatures():String[]
221 
222     /**
223      * Sets the state of a feature. This method is called by the component
224      * manager any time after reset when a feature changes state.
225      * <p>
226      * <strong>Note:</strong> Components should silently ignore features
227      * that do not affect the operation of the component.
228      *
229      * @param featureId The feature identifier.
230      * @param state     The state of the feature.
231      *
232      * @throws SAXNotRecognizedException The component should not throw
233      *                                   this exception.
234      * @throws SAXNotSupportedException The component should not throw
235      *                                  this exception.
236      */
setFeature(String featureId, boolean state)237     public void setFeature(String featureId, boolean state)
238         throws XMLConfigurationException {
239     } // setFeature(String,boolean)
240 
241     /**
242      * Returns a list of property identifiers that are recognized by
243      * this component. This method may return null if no properties
244      * are recognized by this component.
245      */
getRecognizedProperties()246     public String[] getRecognizedProperties() {
247         return RECOGNIZED_PROPERTIES.clone();
248     } // getRecognizedProperties():String[]
249 
250     /**
251      * Sets the value of a property during parsing.
252      *
253      * @param propertyId
254      * @param value
255      */
setProperty(String propertyId, Object value)256     public void setProperty(String propertyId, Object value)
257         throws XMLConfigurationException {
258 
259         // Xerces properties
260         if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
261                 final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
262 
263             if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() &&
264                 propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) {
265                 fSymbolTable = (SymbolTable)value;
266             }
267             else if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() &&
268                 propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) {
269                 fErrorReporter = (XMLErrorReporter)value;
270             }
271             return;
272         }
273 
274     } // setProperty(String,Object)
275 
276     /**
277      * Returns the default state for a feature, or null if this
278      * component does not want to report a default value for this
279      * feature.
280      *
281      * @param featureId The feature identifier.
282      *
283      * @since Xerces 2.2.0
284      */
getFeatureDefault(String featureId)285     public Boolean getFeatureDefault(String featureId) {
286         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
287             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
288                 return FEATURE_DEFAULTS[i];
289             }
290         }
291         return null;
292     } // getFeatureDefault(String):Boolean
293 
294     /**
295      * Returns the default state for a property, or null if this
296      * component does not want to report a default value for this
297      * property.
298      *
299      * @param propertyId The property identifier.
300      *
301      * @since Xerces 2.2.0
302      */
getPropertyDefault(String propertyId)303     public Object getPropertyDefault(String propertyId) {
304         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
305             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
306                 return PROPERTY_DEFAULTS[i];
307             }
308         }
309         return null;
310     } // getPropertyDefault(String):Object
311 
312     //
313     // XMLDocumentSource methods
314     //
315 
316     /** Sets the document handler to receive information about the document. */
setDocumentHandler(XMLDocumentHandler documentHandler)317     public void setDocumentHandler(XMLDocumentHandler documentHandler) {
318         fDocumentHandler = documentHandler;
319     } // setDocumentHandler(XMLDocumentHandler)
320 
321     /** Returns the document handler */
getDocumentHandler()322     public XMLDocumentHandler getDocumentHandler() {
323         return fDocumentHandler;
324     } // setDocumentHandler(XMLDocumentHandler)
325 
326 
327     //
328     // XMLDocumentHandler methods
329     //
330 
331     /** Sets the document source */
setDocumentSource(XMLDocumentSource source)332     public void setDocumentSource(XMLDocumentSource source){
333         fDocumentSource = source;
334     } // setDocumentSource
335 
336     /** Returns the document source */
getDocumentSource()337     public XMLDocumentSource getDocumentSource (){
338         return fDocumentSource;
339     } // getDocumentSource
340 
341 
342     /**
343      * This method notifies the start of a general entity.
344      * <p>
345      * <strong>Note:</strong> This method is not called for entity references
346      * appearing as part of attribute values.
347      *
348      * @param name     The name of the general entity.
349      * @param identifier The resource identifier.
350      * @param encoding The auto-detected IANA encoding name of the entity
351      *                 stream. This value will be null in those situations
352      *                 where the entity encoding is not auto-detected (e.g.
353      *                 internal entities or a document entity that is
354      *                 parsed from a java.io.Reader).
355      * @param augs     Additional information that may include infoset augmentations
356      *
357      * @exception XNIException Thrown by handler to signal an error.
358      */
startGeneralEntity(String name, XMLResourceIdentifier identifier, String encoding, Augmentations augs)359     public void startGeneralEntity(String name,
360                                    XMLResourceIdentifier identifier,
361                                    String encoding, Augmentations augs)
362         throws XNIException {
363         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
364             fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
365         }
366     } // startEntity(String,String,String,String,String)
367 
368     /**
369      * Notifies of the presence of a TextDecl line in an entity. If present,
370      * this method will be called immediately following the startEntity call.
371      * <p>
372      * <strong>Note:</strong> This method will never be called for the
373      * document entity; it is only called for external general entities
374      * referenced in document content.
375      * <p>
376      * <strong>Note:</strong> This method is not called for entity references
377      * appearing as part of attribute values.
378      *
379      * @param version  The XML version, or null if not specified.
380      * @param encoding The IANA encoding name of the entity.
381      * @param augs     Additional information that may include infoset augmentations
382      *
383      * @throws XNIException Thrown by handler to signal an error.
384      */
textDecl(String version, String encoding, Augmentations augs)385     public void textDecl(String version, String encoding, Augmentations augs)
386         throws XNIException {
387         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
388             fDocumentHandler.textDecl(version, encoding, augs);
389         }
390     } // textDecl(String,String)
391 
392     /**
393      * The start of the document.
394      *
395      * @param locator  The system identifier of the entity if the entity
396      *                 is external, null otherwise.
397      * @param encoding The auto-detected IANA encoding name of the entity
398      *                 stream. This value will be null in those situations
399      *                 where the entity encoding is not auto-detected (e.g.
400      *                 internal entities or a document entity that is
401      *                 parsed from a java.io.Reader).
402      * @param namespaceContext
403      *                 The namespace context in effect at the
404      *                 start of this document.
405      *                 This object represents the current context.
406      *                 Implementors of this class are responsible
407      *                 for copying the namespace bindings from the
408      *                 the current context (and its parent contexts)
409      *                 if that information is important.
410      * @param augs     Additional information that may include infoset augmentations
411      *
412      * @throws XNIException Thrown by handler to signal an error.
413      */
startDocument(XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs)414         public void startDocument(XMLLocator locator, String encoding,
415                                 NamespaceContext namespaceContext, Augmentations augs)
416                                       throws XNIException {
417                 fNamespaceContext = namespaceContext;
418 
419                 if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
420                         fDocumentHandler.startDocument(locator, encoding, namespaceContext, augs);
421                 }
422         } // startDocument(XMLLocator,String)
423 
424     /**
425      * Notifies of the presence of an XMLDecl line in the document. If
426      * present, this method will be called immediately following the
427      * startDocument call.
428      *
429      * @param version    The XML version.
430      * @param encoding   The IANA encoding name of the document, or null if
431      *                   not specified.
432      * @param standalone The standalone value, or null if not specified.
433      * @param augs     Additional information that may include infoset augmentations
434      *
435      * @throws XNIException Thrown by handler to signal an error.
436      */
xmlDecl(String version, String encoding, String standalone, Augmentations augs)437     public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
438         throws XNIException {
439         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
440             fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
441         }
442     } // xmlDecl(String,String,String)
443 
444     /**
445      * Notifies of the presence of the DOCTYPE line in the document.
446      *
447      * @param rootElement The name of the root element.
448      * @param publicId    The public identifier if an external DTD or null
449      *                    if the external DTD is specified using SYSTEM.
450      * @param systemId    The system identifier if an external DTD, null
451      *                    otherwise.
452      * @param augs     Additional information that may include infoset augmentations
453      *
454      * @throws XNIException Thrown by handler to signal an error.
455      */
doctypeDecl(String rootElement, String publicId, String systemId, Augmentations augs)456     public void doctypeDecl(String rootElement,
457                             String publicId, String systemId, Augmentations augs)
458         throws XNIException {
459         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
460             fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
461         }
462     } // doctypeDecl(String,String,String)
463 
464     /**
465      * A comment.
466      *
467      * @param text The text in the comment.
468      * @param augs     Additional information that may include infoset augmentations
469      *
470      * @throws XNIException Thrown by application to signal an error.
471      */
comment(XMLString text, Augmentations augs)472     public void comment(XMLString text, Augmentations augs) throws XNIException {
473         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
474             fDocumentHandler.comment(text, augs);
475         }
476     } // comment(XMLString)
477 
478     /**
479      * A processing instruction. Processing instructions consist of a
480      * target name and, optionally, text data. The data is only meaningful
481      * to the application.
482      * <p>
483      * Typically, a processing instruction's data will contain a series
484      * of pseudo-attributes. These pseudo-attributes follow the form of
485      * element attributes but are <strong>not</strong> parsed or presented
486      * to the application as anything other than text. The application is
487      * responsible for parsing the data.
488      *
489      * @param target The target.
490      * @param data   The data or null if none specified.
491      * @param augs     Additional information that may include infoset augmentations
492      *
493      * @throws XNIException Thrown by handler to signal an error.
494      */
processingInstruction(String target, XMLString data, Augmentations augs)495     public void processingInstruction(String target, XMLString data, Augmentations augs)
496         throws XNIException {
497         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
498             fDocumentHandler.processingInstruction(target, data, augs);
499         }
500     } // processingInstruction(String,XMLString)
501 
502 
503     /**
504      * Binds the namespaces. This method will handle calling the
505      * document handler to start the prefix mappings.
506      * <p>
507      * <strong>Note:</strong> This method makes use of the
508      * fAttributeQName variable. Any contents of the variable will
509      * be destroyed. Caller should copy the values out of this
510      * temporary variable before calling this method.
511      *
512      * @param element    The name of the element.
513      * @param attributes The element attributes.
514      * @param augs     Additional information that may include infoset augmentations
515      *
516      * @throws XNIException Thrown by handler to signal an error.
517      */
startElement(QName element, XMLAttributes attributes, Augmentations augs)518     public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
519         throws XNIException {
520 
521         if (fNamespaces) {
522             handleStartElement(element, attributes, augs, false);
523         }
524         else if (fDocumentHandler != null) {
525             fDocumentHandler.startElement(element, attributes, augs);
526         }
527 
528 
529     } // startElement(QName,XMLAttributes)
530 
531     /**
532      * An empty element.
533      *
534      * @param element    The name of the element.
535      * @param attributes The element attributes.
536      * @param augs     Additional information that may include infoset augmentations
537      *
538      * @throws XNIException Thrown by handler to signal an error.
539      */
emptyElement(QName element, XMLAttributes attributes, Augmentations augs)540     public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
541         throws XNIException {
542 
543         if (fNamespaces) {
544             handleStartElement(element, attributes, augs, true);
545             handleEndElement(element, augs, true);
546         }
547         else if (fDocumentHandler != null) {
548             fDocumentHandler.emptyElement(element, attributes, augs);
549         }
550 
551     } // emptyElement(QName,XMLAttributes)
552 
553     /**
554      * Character content.
555      *
556      * @param text The content.
557      * @param augs     Additional information that may include infoset augmentations
558      *
559      * @throws XNIException Thrown by handler to signal an error.
560      */
characters(XMLString text, Augmentations augs)561     public void characters(XMLString text, Augmentations augs) throws XNIException {
562         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
563             fDocumentHandler.characters(text, augs);
564         }
565     } // characters(XMLString)
566 
567     /**
568      * Ignorable whitespace. For this method to be called, the document
569      * source must have some way of determining that the text containing
570      * only whitespace characters should be considered ignorable. For
571      * example, the validator can determine if a length of whitespace
572      * characters in the document are ignorable based on the element
573      * content model.
574      *
575      * @param text The ignorable whitespace.
576      * @param augs     Additional information that may include infoset augmentations
577      *
578      * @throws XNIException Thrown by handler to signal an error.
579      */
ignorableWhitespace(XMLString text, Augmentations augs)580     public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
581         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
582             fDocumentHandler.ignorableWhitespace(text, augs);
583         }
584     } // ignorableWhitespace(XMLString)
585 
586     /**
587      * The end of an element.
588      *
589      * @param element The name of the element.
590      * @param augs     Additional information that may include infoset augmentations
591      *
592      * @throws XNIException Thrown by handler to signal an error.
593      */
endElement(QName element, Augmentations augs)594     public void endElement(QName element, Augmentations augs) throws XNIException {
595 
596         if (fNamespaces) {
597             handleEndElement(element, augs, false);
598         }
599         else if (fDocumentHandler != null) {
600             fDocumentHandler.endElement(element, augs);
601         }
602 
603     } // endElement(QName)
604 
605     /**
606      * The start of a CDATA section.
607      * @param augs     Additional information that may include infoset augmentations
608      *
609      * @throws XNIException Thrown by handler to signal an error.
610      */
startCDATA(Augmentations augs)611     public void startCDATA(Augmentations augs) throws XNIException {
612         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
613             fDocumentHandler.startCDATA(augs);
614         }
615     } // startCDATA()
616 
617     /**
618      * The end of a CDATA section.
619      * @param augs     Additional information that may include infoset augmentations
620      *
621      * @throws XNIException Thrown by handler to signal an error.
622      */
endCDATA(Augmentations augs)623     public void endCDATA(Augmentations augs) throws XNIException {
624         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
625             fDocumentHandler.endCDATA(augs);
626         }
627     } // endCDATA()
628 
629     /**
630      * The end of the document.
631      * @param augs     Additional information that may include infoset augmentations
632      *
633      * @throws XNIException Thrown by handler to signal an error.
634      */
endDocument(Augmentations augs)635     public void endDocument(Augmentations augs) throws XNIException {
636         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
637             fDocumentHandler.endDocument(augs);
638         }
639     } // endDocument()
640 
641     /**
642      * This method notifies the end of a general entity.
643      * <p>
644      * <strong>Note:</strong> This method is not called for entity references
645      * appearing as part of attribute values.
646      *
647      * @param name   The name of the entity.
648      * @param augs   Additional information that may include infoset augmentations
649      *
650      * @exception XNIException
651      *                   Thrown by handler to signal an error.
652      */
endGeneralEntity(String name, Augmentations augs)653     public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
654         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
655             fDocumentHandler.endGeneralEntity(name, augs);
656         }
657     } // endEntity(String)
658 
659     //
660     // Protected methods
661     //
662 
663     /** Handles start element. */
handleStartElement(QName element, XMLAttributes attributes, Augmentations augs, boolean isEmpty)664     protected void handleStartElement(QName element, XMLAttributes attributes,
665                                       Augmentations augs,
666                                       boolean isEmpty) throws XNIException {
667 
668         // add new namespace context
669         fNamespaceContext.pushContext();
670 
671         if (element.prefix == XMLSymbols.PREFIX_XMLNS) {
672             fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
673                                        "ElementXMLNSPrefix",
674                                        new Object[]{element.rawname},
675                                        XMLErrorReporter.SEVERITY_FATAL_ERROR);
676         }
677 
678         // search for new namespace bindings
679         int length = attributes.getLength();
680         for (int i = 0; i < length; i++) {
681             String localpart = attributes.getLocalName(i);
682             String prefix = attributes.getPrefix(i);
683             // when it's of form xmlns="..." or xmlns:prefix="...",
684             // it's a namespace declaration. but prefix:xmlns="..." isn't.
685             if (prefix == XMLSymbols.PREFIX_XMLNS ||
686                 prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS) {
687 
688                 // get the internalized value of this attribute
689                 String uri = fSymbolTable.addSymbol(attributes.getValue(i));
690 
691                 // 1. "xmlns" can't be bound to any namespace
692                 if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == XMLSymbols.PREFIX_XMLNS) {
693                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
694                                                "CantBindXMLNS",
695                                                new Object[]{attributes.getQName(i)},
696                                                XMLErrorReporter.SEVERITY_FATAL_ERROR);
697                 }
698 
699                 // 2. the namespace for "xmlns" can't be bound to any prefix
700                 if (uri == NamespaceContext.XMLNS_URI) {
701                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
702                                                "CantBindXMLNS",
703                                                new Object[]{attributes.getQName(i)},
704                                                XMLErrorReporter.SEVERITY_FATAL_ERROR);
705                 }
706 
707                 // 3. "xml" can't be bound to any other namespace than it's own
708                 if (localpart == XMLSymbols.PREFIX_XML) {
709                     if (uri != NamespaceContext.XML_URI) {
710                         fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
711                                                    "CantBindXML",
712                                                    new Object[]{attributes.getQName(i)},
713                                                    XMLErrorReporter.SEVERITY_FATAL_ERROR);
714                     }
715                 }
716                 // 4. the namespace for "xml" can't be bound to any other prefix
717                 else {
718                     if (uri ==NamespaceContext.XML_URI) {
719                         fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
720                                                    "CantBindXML",
721                                                    new Object[]{attributes.getQName(i)},
722                                                    XMLErrorReporter.SEVERITY_FATAL_ERROR);
723                     }
724                 }
725 
726                 prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart : XMLSymbols.EMPTY_STRING;
727 
728                 // http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-prefix
729                 // We should only report an error if there is a prefix,
730                 // that is, the local part is not "xmlns". -SG
731                 // Since this is an error condition in XML 1.0,
732                 // and should be relatively uncommon in XML 1.1,
733                 // making this test into a method call to reuse code
734                 // should be acceptable.  - NG
735                 if(prefixBoundToNullURI(uri, localpart)) {
736                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
737                                                "EmptyPrefixedAttName",
738                                                new Object[]{attributes.getQName(i)},
739                                                XMLErrorReporter.SEVERITY_FATAL_ERROR);
740                     continue;
741                 }
742 
743                 // declare prefix in context
744                 fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : null);
745 
746             }
747         }
748 
749         // bind the element
750         String prefix = element.prefix != null
751                       ? element.prefix : XMLSymbols.EMPTY_STRING;
752         element.uri = fNamespaceContext.getURI(prefix);
753         if (element.prefix == null && element.uri != null) {
754             element.prefix = XMLSymbols.EMPTY_STRING;
755         }
756         if (element.prefix != null && element.uri == null) {
757             fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
758                                        "ElementPrefixUnbound",
759                                        new Object[]{element.prefix, element.rawname},
760                                        XMLErrorReporter.SEVERITY_FATAL_ERROR);
761         }
762 
763         // bind the attributes
764         for (int i = 0; i < length; i++) {
765             attributes.getName(i, fAttributeQName);
766             String aprefix = fAttributeQName.prefix != null
767                            ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
768             String arawname = fAttributeQName.rawname;
769             if (arawname == XMLSymbols.PREFIX_XMLNS) {
770                 fAttributeQName.uri = fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS);
771                 attributes.setName(i, fAttributeQName);
772             }
773             else if (aprefix != XMLSymbols.EMPTY_STRING) {
774                 fAttributeQName.uri = fNamespaceContext.getURI(aprefix);
775                 if (fAttributeQName.uri == null) {
776                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
777                                                "AttributePrefixUnbound",
778                                                new Object[]{element.rawname,arawname,aprefix},
779                                                XMLErrorReporter.SEVERITY_FATAL_ERROR);
780                 }
781                 attributes.setName(i, fAttributeQName);
782             }
783         }
784 
785         // verify that duplicate attributes don't exist
786         // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/>
787         int attrCount = attributes.getLength();
788         for (int i = 0; i < attrCount - 1; i++) {
789             String auri = attributes.getURI(i);
790             if (auri == null || auri == NamespaceContext.XMLNS_URI) {
791                 continue;
792             }
793             String alocalpart = attributes.getLocalName(i);
794             for (int j = i + 1; j < attrCount; j++) {
795                 String blocalpart = attributes.getLocalName(j);
796                 String buri = attributes.getURI(j);
797                 if (alocalpart == blocalpart && auri == buri) {
798                     fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
799                                                "AttributeNSNotUnique",
800                                                new Object[]{element.rawname,alocalpart, auri},
801                                                XMLErrorReporter.SEVERITY_FATAL_ERROR);
802                 }
803             }
804         }
805 
806         // call handler
807         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
808             if (isEmpty) {
809                 fDocumentHandler.emptyElement(element, attributes, augs);
810             }
811             else {
812                 fDocumentHandler.startElement(element, attributes, augs);
813             }
814         }
815 
816 
817     } // handleStartElement(QName,XMLAttributes,boolean)
818 
819     /** Handles end element. */
handleEndElement(QName element, Augmentations augs, boolean isEmpty)820     protected void handleEndElement(QName element, Augmentations augs, boolean isEmpty)
821         throws XNIException {
822 
823         // bind element
824         String eprefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
825         element.uri = fNamespaceContext.getURI(eprefix);
826         if (element.uri != null) {
827             element.prefix = eprefix;
828         }
829 
830         // call handlers
831         if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
832             if (!isEmpty) {
833                 fDocumentHandler.endElement(element, augs);
834             }
835         }
836 
837         // pop context
838         fNamespaceContext.popContext();
839 
840     } // handleEndElement(QName,boolean)
841 
842     // returns true iff the given prefix is bound to "" *and*
843     // this is disallowed by the version of XML namespaces in use.
prefixBoundToNullURI(String uri, String localpart)844     protected boolean prefixBoundToNullURI(String uri, String localpart) {
845         return (uri == XMLSymbols.EMPTY_STRING && localpart != XMLSymbols.PREFIX_XMLNS);
846     } // prefixBoundToNullURI(String, String):  boolean
847 
848 } // class XMLNamespaceBinder
849