All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xerces.impl.XMLNamespaceBinder Maven / Gradle / Ivy

/*
 * 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 org.apache.xerces.impl;

import org.apache.xerces.impl.msg.XMLMessageFormatter;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLSymbols;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLDocumentHandler;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLComponent;
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLConfigurationException;
import org.apache.xerces.xni.parser.XMLDocumentFilter;
import 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).
 * 

* This component requires the following features and properties from the * component manager that uses it: *

    *
  • http://xml.org/sax/features/namespaces
  • *
  • http://apache.org/xml/properties/internal/symbol-table
  • *
  • http://apache.org/xml/properties/internal/error-reporter
  • *
* * @xerces.internal * * @author Andy Clark, IBM * * @version $Id: XMLNamespaceBinder.java 572055 2007-09-02 17:55:43Z mrglavas $ */ 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 /** Namespaces. */ protected boolean fNamespaces; // properties /** Symbol table. */ protected SymbolTable fSymbolTable; /** Error reporter. */ protected XMLErrorReporter fErrorReporter; // handlers /** Document handler. */ protected XMLDocumentHandler fDocumentHandler; protected XMLDocumentSource fDocumentSource; // settings /** Only pass start and end prefix mapping events. */ protected boolean fOnlyPassPrefixMappingEvents; // shared context /** Namespace context. */ private NamespaceContext fNamespaceContext; // temp vars /** Attribute QName. */ private final QName fAttributeQName = new QName(); // // Constructors // /** Default constructor. */ public XMLNamespaceBinder() { } // () // // Public methods // // settings /** * 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) /** * 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 // // 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. */ 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. */ public String[] getRecognizedFeatures() { return (String[])(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. *

* Note: 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. */ 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. */ public String[] getRecognizedProperties() { return (String[])(RECOGNIZED_PROPERTIES.clone()); } // getRecognizedProperties():String[] /** * Sets the value of a property during parsing. * * @param propertyId * @param value */ 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 */ 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 */ 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 // /** Sets the document handler to receive information about the document. */ public void setDocumentHandler(XMLDocumentHandler documentHandler) { fDocumentHandler = documentHandler; } // setDocumentHandler(XMLDocumentHandler) /** Returns the document handler */ public XMLDocumentHandler getDocumentHandler() { return fDocumentHandler; } // setDocumentHandler(XMLDocumentHandler) // // XMLDocumentHandler methods // /** Sets the document source */ public void setDocumentSource(XMLDocumentSource source){ fDocumentSource = source; } // setDocumentSource /** Returns the document source */ public XMLDocumentSource getDocumentSource (){ return fDocumentSource; } // getDocumentSource /** * This method notifies the start of a general entity. *

* Note: 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 * * @exception XNIException Thrown by handler to signal an error. */ 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. *

* Note: This method will never be called for the * document entity; it is only called for external general entities * referenced in document content. *

* Note: 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. */ 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. */ 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. */ 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. */ 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. */ 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. *

* Typically, a processing instruction's data will contain a series * of pseudo-attributes. These pseudo-attributes follow the form of * element attributes but are not 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. */ 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. *

* Note: 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ public void endDocument(Augmentations augs) throws XNIException { if (fDocumentHandler != null && !fOnlyPassPrefixMappingEvents) { fDocumentHandler.endDocument(augs); } } // endDocument() /** * This method notifies the end of a general entity. *

* Note: 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 * * @exception XNIException * Thrown by handler to signal an error. */ 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: 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





© 2015 - 2024 Weber Informatics LLC | Privacy Policy