blob: 728456e5e394e3d9a4c61542bdf10ec41596a45c [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000,2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xerces.util;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.Augmentations;
import org.xml.sax.AttributeList;
import org.xml.sax.Attributes;
/**
* The XMLAttributesImpl class is an implementation of the XMLAttributes
* interface which defines a collection of attributes for an element.
* In the parser, the document source would scan the entire start element
* and collect the attributes. The attributes are communicated to the
* document handler in the startElement method.
* <p>
* The attributes are read-write so that subsequent stages in the document
* pipeline can modify the values or change the attributes that are
* propogated to the next stage.
*
* @see org.apache.xerces.xni.XMLDocumentHandler#startElement
*
* @author Andy Clark, IBM
* @author Elena Litani, IBM
*
* @version $Id$
*/
public class XMLAttributesImpl
implements XMLAttributes, AttributeList, Attributes {
//
// Data
//
// features
/** Namespaces. */
protected boolean fNamespaces = true;
// data
/** Attribute count. */
protected int fLength;
/** Attribute information. */
protected Attribute[] fAttributes = new Attribute[4];
/** Augmentations information for each attribute
number of augmentations is equal to the number of attributes
*/
// XMLAttributes has no knowledge if any augmentations
// we attached to Augmentations.
protected Augmentations[] fAugmentations = new AugmentationsImpl[4];
//
// Constructors
//
/** Default constructor. */
public XMLAttributesImpl() {
for (int i = 0; i < fAttributes.length; i++) {
fAttributes[i] = new Attribute();
fAugmentations[i] = new AugmentationsImpl();
}
} // <init>()
//
// Public methods
//
/**
* Sets whether namespace processing is being performed. This state
* is needed to return the correct value from the getLocalName method.
*
* @param namespaces True if namespace processing is turned on.
*
* @see #getLocalName
*/
public void setNamespaces(boolean namespaces) {
fNamespaces = namespaces;
} // setNamespaces(boolean)
//
// XMLAttributes methods
//
/**
* Adds an attribute. The attribute's non-normalized value of the
* attribute will have the same value as the attribute value until
* set using the <code>setNonNormalizedValue</code> method. Also,
* the added attribute will be marked as specified in the XML instance
* document unless set otherwise using the <code>setSpecified</code>
* method.
* <p>
* <strong>Note:</strong> If an attribute of the same name already
* exists, the old values for the attribute are replaced by the new
* values.
*
* @param attrName The attribute name.
* @param attrType The attribute type. The type name is determined by
* the type specified for this attribute in the DTD.
* For example: "CDATA", "ID", "NMTOKEN", etc. However,
* attributes of type enumeration will have the type
* value specified as the pipe ('|') separated list of
* the enumeration values prefixed by an open
* parenthesis and suffixed by a close parenthesis.
* For example: "(true|false)".
* @param attrValue The attribute value.
*
* @return Returns the attribute index.
*
* @see #setNonNormalizedValue
* @see #setSpecified
*/
public int addAttribute(QName name, String type, String value) {
// find attribute; create, if necessary
int index = name.uri != null && !name.uri.equals("")
? getIndex(name.uri, name.localpart)
: getIndex(name.rawname);
if (index == -1) {
index = fLength;
if (fLength++ == fAttributes.length) {
Attribute[] attributes = new Attribute[fAttributes.length + 4];
Augmentations[] augs = new AugmentationsImpl[fAttributes.length +4];
System.arraycopy(fAttributes, 0, attributes, 0, fAttributes.length);
System.arraycopy(fAugmentations, 0, augs, 0, fAttributes.length);
for (int i = fAttributes.length; i < attributes.length; i++) {
attributes[i] = new Attribute();
augs[i] = new AugmentationsImpl();
}
fAttributes = attributes;
fAugmentations = augs;
}
}
// clear augmentations
fAugmentations[index].clear();
// set values
Attribute attribute = fAttributes[index];
attribute.name.setValues(name);
attribute.type = type;
attribute.value = value;
attribute.nonNormalizedValue = value;
attribute.specified = false;
// return
return index;
} // addAttribute(QName,String,XMLString)
/**
* Removes all of the attributes. This method will also remove all
* entities associated to the attributes.
*/
public void removeAllAttributes() {
fLength = 0;
} // removeAllAttributes()
/**
* Removes the attribute at the specified index.
* <p>
* <strong>Note:</strong> This operation changes the indexes of all
* attributes following the attribute at the specified index.
*
* @param attrIndex The attribute index.
*/
public void removeAttributeAt(int attrIndex) {
if (attrIndex < fLength - 1) {
Attribute removedAttr = fAttributes[attrIndex];
Augmentations removedAug = fAugmentations[attrIndex];
System.arraycopy(fAttributes, attrIndex + 1,
fAttributes, attrIndex, fLength - attrIndex - 1);
System.arraycopy(fAugmentations, attrIndex + 1,
fAugmentations, attrIndex, fLength - attrIndex - 1);
// Make the discarded Attribute object available for re-use
// by tucking it after the Attributes that are still in use
fAttributes[fLength-1] = removedAttr;
fAugmentations[fLength-1] = removedAug;
}
fLength--;
} // removeAttributeAt(int)
/**
* Sets the name of the attribute at the specified index.
*
* @param attrIndex The attribute index.
* @param attrName The new attribute name.
*/
public void setName(int attrIndex, QName attrName) {
fAttributes[attrIndex].name.setValues(attrName);
} // setName(int,QName)
/**
* Sets the fields in the given QName structure with the values
* of the attribute name at the specified index.
*
* @param attrIndex The attribute index.
* @param attrName The attribute name structure to fill in.
*/
public void getName(int attrIndex, QName attrName) {
attrName.setValues(fAttributes[attrIndex].name);
} // getName(int,QName)
/**
* Sets the type of the attribute at the specified index.
*
* @param attrIndex The attribute index.
* @param attrType The attribute type. The type name is determined by
* the type specified for this attribute in the DTD.
* For example: "CDATA", "ID", "NMTOKEN", etc. However,
* attributes of type enumeration will have the type
* value specified as the pipe ('|') separated list of
* the enumeration values prefixed by an open
* parenthesis and suffixed by a close parenthesis.
* For example: "(true|false)".
*/
public void setType(int attrIndex, String attrType) {
fAttributes[attrIndex].type = attrType;
} // setType(int,String)
/**
* Sets the value of the attribute at the specified index. This
* method will overwrite the non-normalized value of the attribute.
*
* @param attrIndex The attribute index.
* @param attrValue The new attribute value.
*
* @see #setNonNormalizedValue
*/
public void setValue(int attrIndex, String attrValue) {
Attribute attribute = fAttributes[attrIndex];
attribute.value = attrValue;
attribute.nonNormalizedValue = attrValue;
} // setValue(int,String)
/**
* Sets the non-normalized value of the attribute at the specified
* index.
*
* @param attrIndex The attribute index.
* @param attrValue The new non-normalized attribute value.
*/
public void setNonNormalizedValue(int attrIndex, String attrValue) {
if (attrValue == null) {
attrValue = fAttributes[attrIndex].value;
}
fAttributes[attrIndex].nonNormalizedValue = attrValue;
} // setNonNormalizedValue(int,String)
/**
* Returns the non-normalized value of the attribute at the specified
* index. If no non-normalized value is set, this method will return
* the same value as the <code>getValue(int)</code> method.
*
* @param attrIndex The attribute index.
*/
public String getNonNormalizedValue(int attrIndex) {
String value = fAttributes[attrIndex].nonNormalizedValue;
return value;
} // getNonNormalizedValue(int):String
/**
* Sets whether an attribute is specified in the instance document
* or not.
*
* @param attrIndex The attribute index.
* @param specified True if the attribute is specified in the instance
* document.
*/
public void setSpecified(int attrIndex, boolean specified) {
fAttributes[attrIndex].specified = specified;
} // setSpecified(int,boolean)
/**
* Returns true if the attribute is specified in the instance document.
*
* @param attrIndex The attribute index.
*/
public boolean isSpecified(int attrIndex) {
return fAttributes[attrIndex].specified;
} // isSpecified(int):boolean
//
// AttributeList and Attributes methods
//
/**
* Return the number of attributes in the list.
*
* <p>Once you know the number of attributes, you can iterate
* through the list.</p>
*
* @return The number of attributes in the list.
*/
public int getLength() {
return fLength;
} // getLength():int
/**
* Look up an attribute's type by index.
*
* <p>The attribute type is one of the strings "CDATA", "ID",
* "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES",
* or "NOTATION" (always in upper case).</p>
*
* <p>If the parser has not read a declaration for the attribute,
* or if the parser does not report attribute types, then it must
* return the value "CDATA" as stated in the XML 1.0 Recommentation
* (clause 3.3.3, "Attribute-Value Normalization").</p>
*
* <p>For an enumerated attribute that is not a notation, the
* parser will report the type as "NMTOKEN".</p>
*
* @param index The attribute index (zero-based).
* @return The attribute's type as a string, or null if the
* index is out of range.
* @see #getLength
*/
public String getType(int index) {
if (index < 0 || index >= fLength) {
return null;
}
String type = fAttributes[index].type;
if(type.indexOf('(') == 0 && type.lastIndexOf(')') == type.length()-1) {
return "NMTOKEN";
}
return type;
} // getType(int):String
/**
* Look up an attribute's type by XML 1.0 qualified name.
*
* <p>See {@link #getType(int) getType(int)} for a description
* of the possible types.</p>
*
* @param qname The XML 1.0 qualified name.
* @return The attribute type as a string, or null if the
* attribute is not in the list or if qualified names
* are not available.
*/
public String getType(String qname) {
int index = getIndex(qname);
return index != -1 ? fAttributes[index].type : null;
} // getType(String):String
/**
* Look up an attribute's value by index.
*
* <p>If the attribute value is a list of tokens (IDREFS,
* ENTITIES, or NMTOKENS), the tokens will be concatenated
* into a single string with each token separated by a
* single space.</p>
*
* @param index The attribute index (zero-based).
* @return The attribute's value as a string, or null if the
* index is out of range.
* @see #getLength
*/
public String getValue(int index) {
if (index < 0 || index >= fLength) {
return null;
}
return fAttributes[index].value;
} // getValue(int):String
/**
* Look up an attribute's value by XML 1.0 qualified name.
*
* <p>See {@link #getValue(int) getValue(int)} for a description
* of the possible values.</p>
*
* @param qname The XML 1.0 qualified name.
* @return The attribute value as a string, or null if the
* attribute is not in the list or if qualified names
* are not available.
*/
public String getValue(String qname) {
int index = getIndex(qname);
return index != -1 ? fAttributes[index].value : null;
} // getValue(String):String
//
// AttributeList methods
//
/**
* Return the name of an attribute in this list (by position).
*
* <p>The names must be unique: the SAX parser shall not include the
* same attribute twice. Attributes without values (those declared
* #IMPLIED without a value specified in the start tag) will be
* omitted from the list.</p>
*
* <p>If the attribute name has a namespace prefix, the prefix
* will still be attached.</p>
*
* @param i The index of the attribute in the list (starting at 0).
* @return The name of the indexed attribute, or null
* if the index is out of range.
* @see #getLength
*/
public String getName(int index) {
if (index < 0 || index >= fLength) {
return null;
}
return fAttributes[index].name.rawname;
} // getName(int):String
//
// Attributes methods
//
/**
* Look up the index of an attribute by XML 1.0 qualified name.
*
* @param qName The qualified (prefixed) name.
* @return The index of the attribute, or -1 if it does not
* appear in the list.
*/
public int getIndex(String qName) {
for (int i = 0; i < fLength; i++) {
Attribute attribute = fAttributes[i];
if (attribute.name.rawname != null &&
attribute.name.rawname.equals(qName)) {
return i;
}
}
return -1;
} // getIndex(String):int
/**
* Look up the index of an attribute by Namespace name.
*
* @param uri The Namespace URI, or null if
* the name has no Namespace URI.
* @param localName The attribute's local name.
* @return The index of the attribute, or -1 if it does not
* appear in the list.
*/
public int getIndex(String uri, String localPart) {
for (int i = 0; i < fLength; i++) {
Attribute attribute = fAttributes[i];
if (attribute.name.localpart != null &&
attribute.name.localpart.equals(localPart) &&
((uri==attribute.name.uri) ||
(uri!=null && attribute.name.uri!=null && attribute.name.uri.equals(uri))))
{
return i;
}
}
return -1;
} // getIndex(String,String):int
/**
* Look up an attribute's local name by index.
*
* @param index The attribute index (zero-based).
* @return The local name, or the empty string if Namespace
* processing is not being performed, or null
* if the index is out of range.
* @see #getLength
*/
public String getLocalName(int index) {
if (!fNamespaces) {
return "";
}
if (index < 0 || index >= fLength) {
return null;
}
return fAttributes[index].name.localpart;
} // getLocalName(int):String
/**
* Look up an attribute's XML 1.0 qualified name by index.
*
* @param index The attribute index (zero-based).
* @return The XML 1.0 qualified name, or the empty string
* if none is available, or null if the index
* is out of range.
* @see #getLength
*/
public String getQName(int index) {
if (index < 0 || index >= fLength) {
return null;
}
String rawname = fAttributes[index].name.rawname;
return rawname != null ? rawname : "";
} // getQName(int):String
/**
* Look up an attribute's type by Namespace name.
*
* <p>See {@link #getType(int) getType(int)} for a description
* of the possible types.</p>
*
* @param uri The Namespace URI, or null if the
* name has no Namespace URI.
* @param localName The local name of the attribute.
* @return The attribute type as a string, or null if the
* attribute is not in the list or if Namespace
* processing is not being performed.
*/
public String getType(String uri, String localName) {
if (!fNamespaces) {
return null;
}
int index = getIndex(uri, localName);
return index != -1 ? getType(index) : null;
} // getType(String,String):String
/**
* Returns the prefix of the attribute at the specified index.
*
* @param index The index of the attribute.
*/
public String getPrefix(int index) {
if (index < 0 || index >= fLength) {
return null;
}
String prefix = fAttributes[index].name.prefix;
// REVISIT: The empty string is not entered in the symbol table!
return prefix != null ? prefix : "";
} // getPrefix(int):String
/**
* Look up an attribute's Namespace URI by index.
*
* @param index The attribute index (zero-based).
* @return The Namespace URI
* @see #getLength
*/
public String getURI(int index) {
if (index < 0 || index >= fLength) {
return null;
}
String uri = fAttributes[index].name.uri;
return uri;
} // getURI(int):String
/**
* Look up an attribute's value by Namespace name.
*
* <p>See {@link #getValue(int) getValue(int)} for a description
* of the possible values.</p>
*
* @param uri The Namespace URI, or null if the
* @param localName The local name of the attribute.
* @return The attribute value as a string, or null if the
* attribute is not in the list.
*/
public String getValue(String uri, String localName) {
int index = getIndex(uri, localName);
return index != -1 ? getValue(index) : null;
} // getValue(String,String):String
/**
* Look up an augmentations by Namespace name.
*
* @param uri The Namespace URI, or null if the
* @param localName The local name of the attribute.
* @return Augmentations
*/
public Augmentations getAugmentations (String uri, String localName) {
int index = getIndex(uri, localName);
if (index < 0 || index >= fLength) {
return null;
}
return fAugmentations[index];
}
/**
* Look up an augmentations by attributes index.
*
* @param attributeIndex The attribute index.
* @return Augmentations
*/
public Augmentations getAugmentations (int attributeIndex){
if (attributeIndex < 0 || attributeIndex >= fLength) {
return null;
}
return fAugmentations[attributeIndex];
}
//
// Classes
//
/**
* Attribute information.
*
* @author Andy Clark, IBM
*/
static class Attribute {
//
// Data
//
// basic info
/** Name. */
public QName name = new QName();
/** Type. */
public String type;
/** Value. */
public String value;
/** Non-normalized value. */
public String nonNormalizedValue;
/** Specified. */
public boolean specified;
} // class Attribute
} // class XMLAttributesImpl