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