1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xerces.internal.impl;
23
24import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
25import com.sun.org.apache.xerces.internal.util.SymbolTable;
26import com.sun.org.apache.xerces.internal.util.XMLSymbols;
27import com.sun.org.apache.xerces.internal.xni.Augmentations;
28import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
29import com.sun.org.apache.xerces.internal.xni.QName;
30import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
31import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
32import com.sun.org.apache.xerces.internal.xni.XMLLocator;
33import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
34import com.sun.org.apache.xerces.internal.xni.XMLString;
35import com.sun.org.apache.xerces.internal.xni.XNIException;
36import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
37import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
38import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
39import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
40import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
41
42/**
43 * This class performs namespace binding on the startElement and endElement
44 * method calls and passes all other methods through to the registered
45 * document handler. This class can be configured to only pass the
46 * start and end prefix mappings (start/endPrefixMapping).
47 * <p>
48 * This component requires the following features and properties from the
49 * component manager that uses it:
50 * <ul>
51 *  <li>http://xml.org/sax/features/namespaces</li>
52 *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
53 *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
54 * </ul>
55 *
56 * @xerces.internal
57 *
58 * @author Andy Clark, IBM
59 *
60 */
61public 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. */
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     */
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     */
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     */
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     */
218    public String[] getRecognizedFeatures() {
219        return (String[])(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     */
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     */
246    public String[] getRecognizedProperties() {
247        return (String[])(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     */
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     */
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     */
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. */
317    public void setDocumentHandler(XMLDocumentHandler documentHandler) {
318        fDocumentHandler = documentHandler;
319    } // setDocumentHandler(XMLDocumentHandler)
320
321    /** Returns the document handler */
322    public XMLDocumentHandler getDocumentHandler() {
323        return fDocumentHandler;
324    } // setDocumentHandler(XMLDocumentHandler)
325
326
327    //
328    // XMLDocumentHandler methods
329    //
330
331    /** Sets the document source */
332    public void setDocumentSource(XMLDocumentSource source){
333        fDocumentSource = source;
334    } // setDocumentSource
335
336    /** Returns the document source */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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. */
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. */
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.
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