content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package mf.org.apache.xerces.impl;
  
  import mf.org.apache.xerces.impl.msg.XMLMessageFormatter;
  import mf.org.apache.xerces.util.SymbolTable;
  import mf.org.apache.xerces.util.XMLSymbols;
  import mf.org.apache.xerces.xni.Augmentations;
  import mf.org.apache.xerces.xni.NamespaceContext;
  import mf.org.apache.xerces.xni.QName;
  import mf.org.apache.xerces.xni.XMLAttributes;
  import mf.org.apache.xerces.xni.XMLDocumentHandler;
  import mf.org.apache.xerces.xni.XMLLocator;
  import mf.org.apache.xerces.xni.XMLResourceIdentifier;
  import mf.org.apache.xerces.xni.XMLString;
  import mf.org.apache.xerces.xni.XNIException;
  import mf.org.apache.xerces.xni.parser.XMLComponent;
  import mf.org.apache.xerces.xni.parser.XMLComponentManager;
  import mf.org.apache.xerces.xni.parser.XMLConfigurationException;
  import mf.org.apache.xerces.xni.parser.XMLDocumentFilter;
  import mf.org.apache.xerces.xni.parser.XMLDocumentSource;
  
  
  /**
   * This class performs namespace binding on the startElement and endElement
   * method calls and passes all other methods through to the registered
   * document handler. This class can be configured to only pass the
   * start and end prefix mappings (start/endPrefixMapping).
   * <p/>
   * This component requires the following features and properties from the
   * component manager that uses it:
   * <ul>
   * <li>http://xml.org/sax/features/namespaces</li>
   * <li>http://apache.org/xml/properties/internal/symbol-table</li>
   * <li>http://apache.org/xml/properties/internal/error-reporter</li>
   * </ul>
   *
   * @author Andy Clark, IBM
   * @version $Id: XMLNamespaceBinder.java 572055 2007-09-02 17:55:43Z mrglavas $
   * @xerces.internal
   */
  public class XMLNamespaceBinder
          implements XMLComponent, XMLDocumentFilter {
  
      //
      // Constants
      //
  
      // feature identifiers
  
      /**
       * Feature identifier: namespaces.
       */
      protected static final String NAMESPACES =
              Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
  
      // property identifiers
  
      /**
       * Property identifier: symbol table.
       */
      protected static final String SYMBOL_TABLE =
              Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
  
      /**
       * Property identifier: error reporter.
       */
      protected static final String ERROR_REPORTER =
              Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
  
      // recognized features and properties
  
      /**
       * Recognized features.
       */
      private static final String[] RECOGNIZED_FEATURES = {
              NAMESPACES,
      };
  
      /**
       * Feature defaults.
       */
      private static final Boolean[] FEATURE_DEFAULTS = {
              null,
      };
  
      /**
       * Recognized properties.
       */
      private static final String[] RECOGNIZED_PROPERTIES = {
              SYMBOL_TABLE,
              ERROR_REPORTER,
      };
  
      /**
       * Property defaults.
       */
      private static final Object[] PROPERTY_DEFAULTS = {
              null,
              null,
      };
  
      //
      // Data
      //
  
      // features
      /**
       * Attribute QName.
       */
      private final QName fAttributeQName = new QName();
  
      // properties
      /**
       * Namespaces.
       */
      protected boolean fNamespaces;
      /**
       * Symbol table.
       */
      protected SymbolTable fSymbolTable;
  
      // handlers
      /**
       * Error reporter.
       */
      protected XMLErrorReporter fErrorReporter;
      /**
       * Document handler.
       */
      protected XMLDocumentHandler fDocumentHandler;
  
      // settings
      protected XMLDocumentSource fDocumentSource;
  
      // shared context
      /**
       * Only pass start and end prefix mapping events.
       */
      protected boolean fOnlyPassPrefixMappingEvents;
  
      // temp vars
      /**
       * Namespace context.
       */
      private NamespaceContext fNamespaceContext;
  
      //
      // Constructors
      //
  
      /**
       * Default constructor.
       */
      public XMLNamespaceBinder() {
      } // <init>()
  
      //
      // Public methods
      //
  
      // settings
  
      /**
       * Returns true if the namespace binder only passes the prefix mapping
       * events to the registered document handler; false if the namespace
       * binder passes all document events.
       */
      public boolean getOnlyPassPrefixMappingEvents() {
          return fOnlyPassPrefixMappingEvents;
      } // getOnlyPassPrefixMappingEvents():boolean
  
      /**
       * Sets whether the namespace binder only passes the prefix mapping
       * events to the registered document handler or passes all document
       * events.
       *
       * @param onlyPassPrefixMappingEvents True to pass only the prefix
       *                                    mapping events; false to pass
       *                                    all events.
       */
      public void setOnlyPassPrefixMappingEvents(boolean onlyPassPrefixMappingEvents) {
          fOnlyPassPrefixMappingEvents = onlyPassPrefixMappingEvents;
      } // setOnlyPassPrefixMappingEvents(boolean)
  
      //
      // XMLComponent methods
      //
  
      /**
       * Resets the component. The component can query the component manager
       * about any features and properties that affect the operation of the
       * component.
       *
       * @param componentManager The component manager.
       * @throws SAXException Thrown by component on initialization error.
       *                      For example, if a feature or property is
       *                      required for the operation of the component, the
       *                      component manager may throw a
       *                      SAXNotRecognizedException or a
       *                      SAXNotSupportedException.
       */
      @Override
      public void reset(XMLComponentManager componentManager)
              throws XNIException {
  
          // features
          try {
              fNamespaces = componentManager.getFeature(NAMESPACES);
          } catch (XMLConfigurationException e) {
              fNamespaces = true;
          }
  
          // Xerces properties
          fSymbolTable = (SymbolTable) componentManager.getProperty(SYMBOL_TABLE);
          fErrorReporter = (XMLErrorReporter) componentManager.getProperty(ERROR_REPORTER);
  
      } // reset(XMLComponentManager)
  
      /**
       * Returns a list of feature identifiers that are recognized by
       * this component. This method may return null if no features
       * are recognized by this component.
       */
      @Override
      public String[] getRecognizedFeatures() {
          return (RECOGNIZED_FEATURES.clone());
      } // getRecognizedFeatures():String[]
  
      /**
       * Sets the state of a feature. This method is called by the component
       * manager any time after reset when a feature changes state.
       * <p/>
       * <strong>Note:</strong> Components should silently ignore features
       * that do not affect the operation of the component.
       *
       * @param featureId The feature identifier.
       * @param state     The state of the feature.
       * @throws SAXNotRecognizedException The component should not throw
       *                                   this exception.
       * @throws SAXNotSupportedException  The component should not throw
       *                                   this exception.
       */
      @Override
      public void setFeature(String featureId, boolean state)
              throws XMLConfigurationException {
      } // setFeature(String,boolean)
  
      /**
       * Returns a list of property identifiers that are recognized by
       * this component. This method may return null if no properties
       * are recognized by this component.
       */
      @Override
      public String[] getRecognizedProperties() {
          return (RECOGNIZED_PROPERTIES.clone());
      } // getRecognizedProperties():String[]
  
      /**
       * Sets the value of a property during parsing.
       *
       * @param propertyId
       * @param value
       */
      @Override
      public void setProperty(String propertyId, Object value)
              throws XMLConfigurationException {
  
          // Xerces properties
          if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
              final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
  
              if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() &&
                      propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) {
                  fSymbolTable = (SymbolTable) value;
              } else if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() &&
                      propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) {
                  fErrorReporter = (XMLErrorReporter) value;
              }
              return;
          }
  
      } // setProperty(String,Object)
  
      /**
       * Returns the default state for a feature, or null if this
       * component does not want to report a default value for this
       * feature.
       *
       * @param featureId The feature identifier.
       * @since Xerces 2.2.0
       */
      @Override
      public Boolean getFeatureDefault(String featureId) {
          for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
              if (RECOGNIZED_FEATURES[i].equals(featureId)) {
                  return FEATURE_DEFAULTS[i];
              }
          }
          return null;
      } // getFeatureDefault(String):Boolean
  
      /**
       * Returns the default state for a property, or null if this
       * component does not want to report a default value for this
       * property.
       *
       * @param propertyId The property identifier.
       * @since Xerces 2.2.0
       */
      @Override
      public Object getPropertyDefault(String propertyId) {
          for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
              if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
                  return PROPERTY_DEFAULTS[i];
              }
          }
          return null;
      } // getPropertyDefault(String):Object
  
      //
      // XMLDocumentSource methods
      //
  
      /**
       * Returns the document handler
       */
      @Override
      public XMLDocumentHandler getDocumentHandler() {
          return fDocumentHandler;
      } // setDocumentHandler(XMLDocumentHandler)
  
      /**
       * Sets the document handler to receive information about the document.
       */
      @Override
      public void setDocumentHandler(XMLDocumentHandler documentHandler) {
          fDocumentHandler = documentHandler;
      } // setDocumentHandler(XMLDocumentHandler)
  
  
      //
      // XMLDocumentHandler methods
      //
  
      /**
       * Returns the document source
       */
      @Override
      public XMLDocumentSource getDocumentSource() {
          return fDocumentSource;
      } // getDocumentSource
  
      /**
       * Sets the document source
       */
      @Override
      public void setDocumentSource(XMLDocumentSource source) {
          fDocumentSource = source;
      } // setDocumentSource
  
      /**
       * This method notifies the start of a general entity.
       * <p/>
       * <strong>Note:</strong> This method is not called for entity references
       * appearing as part of attribute values.
       *
       * @param name       The name of the general entity.
       * @param identifier The resource identifier.
       * @param encoding   The auto-detected IANA encoding name of the entity
       *                   stream. This value will be null in those situations
       *                   where the entity encoding is not auto-detected (e.g.
       *                   internal entities or a document entity that is
       *                   parsed from a java.io.Reader).
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startGeneralEntity(String name,
                                     XMLResourceIdentifier identifier,
                                     String encoding, Augmentations augs)
              throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs);
          }
      } // startEntity(String,String,String,String,String)
  
      /**
       * Notifies of the presence of a TextDecl line in an entity. If present,
       * this method will be called immediately following the startEntity call.
       * <p/>
       * <strong>Note:</strong> This method will never be called for the
       * document entity; it is only called for external general entities
       * referenced in document content.
       * <p/>
       * <strong>Note:</strong> This method is not called for entity references
       * appearing as part of attribute values.
       *
       * @param version  The XML version, or null if not specified.
       * @param encoding The IANA encoding name of the entity.
       * @param augs     Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void textDecl(String version, String encoding, Augmentations augs)
              throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.textDecl(version, encoding, augs);
          }
      } // textDecl(String,String)
  
      /**
       * The start of the document.
       *
       * @param locator          The system identifier of the entity if the entity
       *                         is external, null otherwise.
       * @param encoding         The auto-detected IANA encoding name of the entity
       *                         stream. This value will be null in those situations
       *                         where the entity encoding is not auto-detected (e.g.
       *                         internal entities or a document entity that is
       *                         parsed from a java.io.Reader).
       * @param namespaceContext The namespace context in effect at the
       *                         start of this document.
       *                         This object represents the current context.
       *                         Implementors of this class are responsible
       *                         for copying the namespace bindings from the
       *                         the current context (and its parent contexts)
       *                         if that information is important.
       * @param augs             Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startDocument(XMLLocator locator, String encoding,
                                NamespaceContext namespaceContext, Augmentations augs)
              throws XNIException {
          fNamespaceContext = namespaceContext;
  
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.startDocument(locator, encoding, namespaceContext, augs);
          }
      } // startDocument(XMLLocator,String)
  
      /**
       * Notifies of the presence of an XMLDecl line in the document. If
       * present, this method will be called immediately following the
       * startDocument call.
       *
       * @param version    The XML version.
       * @param encoding   The IANA encoding name of the document, or null if
       *                   not specified.
       * @param standalone The standalone value, or null if not specified.
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
              throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
          }
      } // xmlDecl(String,String,String)
  
      /**
       * Notifies of the presence of the DOCTYPE line in the document.
       *
       * @param rootElement The name of the root element.
       * @param publicId    The public identifier if an external DTD or null
       *                    if the external DTD is specified using SYSTEM.
       * @param systemId    The system identifier if an external DTD, null
       *                    otherwise.
       * @param augs        Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void doctypeDecl(String rootElement,
                              String publicId, String systemId, Augmentations augs)
              throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
          }
      } // doctypeDecl(String,String,String)
  
      /**
       * A comment.
       *
       * @param text The text in the comment.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by application to signal an error.
       */
      @Override
      public void comment(XMLString text, Augmentations augs) throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.comment(text, augs);
          }
      } // comment(XMLString)
  
      /**
       * A processing instruction. Processing instructions consist of a
       * target name and, optionally, text data. The data is only meaningful
       * to the application.
       * <p/>
       * Typically, a processing instruction's data will contain a series
       * of pseudo-attributes. These pseudo-attributes follow the form of
       * element attributes but are <strong>not</strong> parsed or presented
       * to the application as anything other than text. The application is
       * responsible for parsing the data.
       *
       * @param target The target.
       * @param data   The data or null if none specified.
       * @param augs   Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void processingInstruction(String target, XMLString data, Augmentations augs)
              throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.processingInstruction(target, data, augs);
          }
      } // processingInstruction(String,XMLString)
  
  
      /**
       * Binds the namespaces. This method will handle calling the
       * document handler to start the prefix mappings.
       * <p/>
       * <strong>Note:</strong> This method makes use of the
       * fAttributeQName variable. Any contents of the variable will
       * be destroyed. Caller should copy the values out of this
       * temporary variable before calling this method.
       *
       * @param element    The name of the element.
       * @param attributes The element attributes.
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
              throws XNIException {
  
          if (fNamespaces) {
              handleStartElement(element, attributes, augs, false);
          } else if (fDocumentHandler != null) {
              fDocumentHandler.startElement(element, attributes, augs);
          }
  
  
      } // startElement(QName,XMLAttributes)
  
      /**
       * An empty element.
       *
       * @param element    The name of the element.
       * @param attributes The element attributes.
       * @param augs       Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
              throws XNIException {
  
          if (fNamespaces) {
              handleStartElement(element, attributes, augs, true);
              handleEndElement(element, augs, true);
          } else if (fDocumentHandler != null) {
              fDocumentHandler.emptyElement(element, attributes, augs);
          }
  
      } // emptyElement(QName,XMLAttributes)
  
      /**
       * Character content.
       *
       * @param text The content.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void characters(XMLString text, Augmentations augs) throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.characters(text, augs);
          }
      } // characters(XMLString)
  
      /**
       * Ignorable whitespace. For this method to be called, the document
       * source must have some way of determining that the text containing
       * only whitespace characters should be considered ignorable. For
       * example, the validator can determine if a length of whitespace
       * characters in the document are ignorable based on the element
       * content model.
       *
       * @param text The ignorable whitespace.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.ignorableWhitespace(text, augs);
          }
      } // ignorableWhitespace(XMLString)
  
      /**
       * The end of an element.
       *
       * @param element The name of the element.
       * @param augs    Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endElement(QName element, Augmentations augs) throws XNIException {
  
          if (fNamespaces) {
              handleEndElement(element, augs, false);
          } else if (fDocumentHandler != null) {
              fDocumentHandler.endElement(element, augs);
          }
  
      } // endElement(QName)
  
      /**
       * The start of a CDATA section.
       *
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void startCDATA(Augmentations augs) throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.startCDATA(augs);
          }
      } // startCDATA()
  
      /**
       * The end of a CDATA section.
       *
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endCDATA(Augmentations augs) throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.endCDATA(augs);
          }
      } // endCDATA()
  
      /**
       * The end of the document.
       *
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endDocument(Augmentations augs) throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.endDocument(augs);
          }
      } // endDocument()
  
      /**
       * This method notifies the end of a general entity.
       * <p/>
       * <strong>Note:</strong> This method is not called for entity references
       * appearing as part of attribute values.
       *
       * @param name The name of the entity.
       * @param augs Additional information that may include infoset augmentations
       * @throws XNIException Thrown by handler to signal an error.
       */
      @Override
      public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              fDocumentHandler.endGeneralEntity(name, augs);
          }
      } // endEntity(String)
  
      //
      // Protected methods
      //
  
      /**
       * Handles start element.
       */
      protected void handleStartElement(QName element, XMLAttributes attributes,
                                        Augmentations augs,
                                        boolean isEmpty) throws XNIException {
  
          // add new namespace context
          fNamespaceContext.pushContext();
  
          if (element.prefix == XMLSymbols.PREFIX_XMLNS) {
              fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                      "ElementXMLNSPrefix",
                      new Object[]{element.rawname},
                      XMLErrorReporter.SEVERITY_FATAL_ERROR);
          }
  
          // search for new namespace bindings
          int length = attributes.getLength();
          for (int i = 0; i < length; i++) {
              String localpart = attributes.getLocalName(i);
              String prefix = attributes.getPrefix(i);
              // when it's of form xmlns="..." or xmlns:prefix="...",
              // it's a namespace declaration. but prefix:xmlns="..." isn't.
              if (prefix == XMLSymbols.PREFIX_XMLNS ||
                      prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS) {
  
                  // get the internalized value of this attribute
                  String uri = fSymbolTable.addSymbol(attributes.getValue(i));
  
                  // 1. "xmlns" can't be bound to any namespace
                  if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == XMLSymbols.PREFIX_XMLNS) {
                      fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                              "CantBindXMLNS",
                              new Object[]{attributes.getQName(i)},
                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
  
                  // 2. the namespace for "xmlns" can't be bound to any prefix
                  if (uri == NamespaceContext.XMLNS_URI) {
                      fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                              "CantBindXMLNS",
                              new Object[]{attributes.getQName(i)},
                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
  
                  // 3. "xml" can't be bound to any other namespace than it's own
                  if (localpart == XMLSymbols.PREFIX_XML) {
                      if (uri != NamespaceContext.XML_URI) {
                          fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                                  "CantBindXML",
                                  new Object[]{attributes.getQName(i)},
                                  XMLErrorReporter.SEVERITY_FATAL_ERROR);
                      }
                  }
                  // 4. the namespace for "xml" can't be bound to any other prefix
                  else {
                      if (uri == NamespaceContext.XML_URI) {
                          fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                                  "CantBindXML",
                                  new Object[]{attributes.getQName(i)},
                                  XMLErrorReporter.SEVERITY_FATAL_ERROR);
                      }
                  }
  
                  prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart : XMLSymbols.EMPTY_STRING;
  
                  // http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-prefix
                  // We should only report an error if there is a prefix,
                  // that is, the local part is not "xmlns". -SG
                  // Since this is an error condition in XML 1.0,
                  // and should be relatively uncommon in XML 1.1,
                  // making this test into a method call to reuse code
                  // should be acceptable.  - NG
                  if (prefixBoundToNullURI(uri, localpart)) {
                      fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                              "EmptyPrefixedAttName",
                              new Object[]{attributes.getQName(i)},
                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
                      continue;
                  }
  
                  // declare prefix in context
                  fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : null);
  
              }
          }
  
          // bind the element
          String prefix = element.prefix != null
                  ? element.prefix : XMLSymbols.EMPTY_STRING;
          element.uri = fNamespaceContext.getURI(prefix);
          if (element.prefix == null && element.uri != null) {
              element.prefix = XMLSymbols.EMPTY_STRING;
          }
          if (element.prefix != null && element.uri == null) {
              fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                      "ElementPrefixUnbound",
                      new Object[]{element.prefix, element.rawname},
                      XMLErrorReporter.SEVERITY_FATAL_ERROR);
          }
  
          // bind the attributes
          for (int i = 0; i < length; i++) {
              attributes.getName(i, fAttributeQName);
              String aprefix = fAttributeQName.prefix != null
                      ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
              String arawname = fAttributeQName.rawname;
              if (arawname == XMLSymbols.PREFIX_XMLNS) {
                  fAttributeQName.uri = fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS);
                  attributes.setName(i, fAttributeQName);
              } else if (aprefix != XMLSymbols.EMPTY_STRING) {
                  fAttributeQName.uri = fNamespaceContext.getURI(aprefix);
                  if (fAttributeQName.uri == null) {
                      fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                              "AttributePrefixUnbound",
                              new Object[]{element.rawname, arawname, aprefix},
                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
                  attributes.setName(i, fAttributeQName);
              }
          }
  
          // verify that duplicate attributes don't exist
          // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/>
          int attrCount = attributes.getLength();
          for (int i = 0; i < attrCount - 1; i++) {
              String auri = attributes.getURI(i);
              if (auri == null || auri == NamespaceContext.XMLNS_URI) {
                  continue;
              }
              String alocalpart = attributes.getLocalName(i);
              for (int j = i + 1; j < attrCount; j++) {
                  String blocalpart = attributes.getLocalName(j);
                  String buri = attributes.getURI(j);
                  if (alocalpart == blocalpart && auri == buri) {
                      fErrorReporter.reportError(XMLMessageFormatter.XMLNS_DOMAIN,
                              "AttributeNSNotUnique",
                              new Object[]{element.rawname, alocalpart, auri},
                              XMLErrorReporter.SEVERITY_FATAL_ERROR);
                  }
              }
          }
  
          // call handler
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              if (isEmpty) {
                  fDocumentHandler.emptyElement(element, attributes, augs);
              } else {
                  fDocumentHandler.startElement(element, attributes, augs);
              }
          }
  
  
      } // handleStartElement(QName,XMLAttributes,boolean)
  
      /**
       * Handles end element.
       */
      protected void handleEndElement(QName element, Augmentations augs, boolean isEmpty)
              throws XNIException {
  
          // bind element
          String eprefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
          element.uri = fNamespaceContext.getURI(eprefix);
          if (element.uri != null) {
              element.prefix = eprefix;
          }
  
          // call handlers
          if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) {
              if (!isEmpty) {
                  fDocumentHandler.endElement(element, augs);
              }
          }
  
          // pop context
          fNamespaceContext.popContext();
  
      } // handleEndElement(QName,boolean)
  
      // returns true iff the given prefix is bound to "" *and*
      // this is disallowed by the version of XML namespaces in use.
      protected boolean prefixBoundToNullURI(String uri, String localpart) {
          return (uri == XMLSymbols.EMPTY_STRING && localpart != XMLSymbols.PREFIX_XMLNS);
      } // prefixBoundToNullURI(String, String):  boolean
  
  } // class XMLNamespaceBinder
  