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 
  22 package com.sun.org.apache.xerces.internal.impl;
  23 
  24 import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  25 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  26 import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  27 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  28 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  29 import com.sun.org.apache.xerces.internal.xni.QName;
  30 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  31 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
  32 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  33 import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  34 import com.sun.org.apache.xerces.internal.xni.XMLString;
  35 import com.sun.org.apache.xerces.internal.xni.XNIException;
  36 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  37 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  38 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  39 import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
  40 import 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  */
  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. */
 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