/*-- $Id: Attribute.java,v 1.2 2013-09-20 15:11:33 fbusquets Exp $ Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin. 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 disclaimer that follows these conditions in the documentation and/or other materials provided with the distribution. 3. The name "JDOM" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact . 4. Products derived from this software may not be called "JDOM", nor may "JDOM" appear in their name, without prior written permission from the JDOM Project Management . In addition, we request (but do not require) that you include in the end-user documentation provided with the redistribution and/or in the software itself an acknowledgement equivalent to the following: "This product includes software developed by the JDOM Project (http://www.jdom.org/)." Alternatively, the acknowledgment may be graphical using the logos available at http://www.jdom.org/images/logos. 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 JDOM AUTHORS OR THE PROJECT 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 JDOM Project and was originally created by Jason Hunter and Brett McLaughlin . For more information on the JDOM Project, please see . */ package org.jdom; import java.io.*; /** * An XML attribute. Methods allow the user to obtain the value of the attribute * as well as namespace and type information. * * @version $Revision: 1.2 $, $Date: 2013-09-20 15:11:33 $ * @author Brett McLaughlin * @author Jason Hunter * @author Elliotte Rusty Harold * @author Wesley Biggs * @version Modified for JClic: 2013.08.09 */ public class Attribute implements Serializable, Cloneable { private static final String CVS_ID = "@(#) $RCSfile: Attribute.java,v $ $Revision: 1.2 $ $Date: 2013-09-20 15:11:33 $ $Name: $"; /** * Attribute type: the attribute has not been declared or type * is unknown. * * @see #getAttributeType */ public final static int UNDECLARED_TYPE = 0; /** * Attribute type: the attribute value is a string. * * @see #getAttributeType */ public final static int CDATA_TYPE = 1; /** * Attribute type: the attribute value is a unique identifier. * * @see #getAttributeType */ public final static int ID_TYPE = 2; /** * Attribute type: the attribute value is a reference to a * unique identifier. * * @see #getAttributeType */ public final static int IDREF_TYPE = 3; /** * Attribute type: the attribute value is a list of references to * unique identifiers. * * @see #getAttributeType */ public final static int IDREFS_TYPE = 4; /** * Attribute type: the attribute value is the name of an entity. * * @see #getAttributeType */ public final static int ENTITY_TYPE = 5; /** *

* Attribute type: the attribute value is a list of entity names. *

* * @see #getAttributeType */ public final static int ENTITIES_TYPE = 6; /** * Attribute type: the attribute value is a name token. *

* According to SAX 2.0 specification, attributes of enumerated * types should be reported as "NMTOKEN" by SAX parsers. But the * major parsers (Xerces and Crimson) provide specific values * that permit to recognize them as {@link #ENUMERATED_TYPE}. * * @see #getAttributeType */ public final static int NMTOKEN_TYPE = 7; /** * Attribute type: the attribute value is a list of name tokens. * * @see #getAttributeType */ public final static int NMTOKENS_TYPE = 8; /** * Attribute type: the attribute value is the name of a notation. * * @see #getAttributeType */ public final static int NOTATION_TYPE = 9; /** * Attribute type: the attribute value is a name token from an * enumeration. * * @see #getAttributeType */ public final static int ENUMERATED_TYPE = 10; // Keep the old constant names for one beta cycle to help migration /** The local name of the Attribute */ protected String name; /** The {@link Namespace} of the Attribute */ protected transient Namespace namespace; /** The value of the Attribute */ protected String value; /** The type of the Attribute */ protected int type = UNDECLARED_TYPE; /** Parent element, or null if none */ protected Object parent; /** * Default, no-args constructor for implementations to use if needed. */ protected Attribute() {} /** * This will create a new Attribute with the * specified (local) name and value, and in the provided * {@link Namespace}. * * @param name String name of Attribute. * @param value String value for new attribute. * @param namespace Namespace namespace for new attribute. * @throws IllegalNameException if the given name is illegal as an * attribute name or if if the new namespace is the default * namespace. Attributes cannot be in a default namespace. * @throws IllegalDataException if the given attribute value is * illegal character data (as determined by * {@link org.jdom.Verifier#checkCharacterData}). */ public Attribute(String name, String value, Namespace namespace) { setName(name); setValue(value); setNamespace(namespace); } /** * This will create a new Attribute with the * specified (local) name, value, and type, and in the provided * {@link Namespace}. * * @param name String name of Attribute. * @param value String value for new attribute. * @param type int type for new attribute. * @param namespace Namespace namespace for new attribute. * @throws IllegalNameException if the given name is illegal as an * attribute name or if if the new namespace is the default * namespace. Attributes cannot be in a default namespace. * @throws IllegalDataException if the given attribute value is * illegal character data (as determined by * {@link org.jdom.Verifier#checkCharacterData}) or * if the given attribute type is not one of the * supported types. */ public Attribute(String name, String value, int type, Namespace namespace) { setName(name); setValue(value); setAttributeType(type); setNamespace(namespace); } /** * This will create a new Attribute with the * specified (local) name and value, and does not place * the attribute in a {@link Namespace}. *

* Note: This actually explicitly puts the * Attribute in the "empty" Namespace * ({@link Namespace#NO_NAMESPACE}). * * @param name String name of Attribute. * @param value String value for new attribute. * @throws IllegalNameException if the given name is illegal as an * attribute name. * @throws IllegalDataException if the given attribute value is * illegal character data (as determined by * {@link org.jdom.Verifier#checkCharacterData}). */ public Attribute(String name, String value) { this(name, value, UNDECLARED_TYPE, Namespace.NO_NAMESPACE); } /** * This will create a new Attribute with the * specified (local) name, value and type, and does not place * the attribute in a {@link Namespace}. *

* Note: This actually explicitly puts the * Attribute in the "empty" Namespace * ({@link Namespace#NO_NAMESPACE}). * * @param name String name of Attribute. * @param value String value for new attribute. * @param type int type for new attribute. * @throws IllegalNameException if the given name is illegal as an * attribute name. * @throws IllegalDataException if the given attribute value is * illegal character data (as determined by * {@link org.jdom.Verifier#checkCharacterData}) or * if the given attribute type is not one of the * supported types. */ public Attribute(String name, String value, int type) { this(name, value, type, Namespace.NO_NAMESPACE); } /** * This will return the parent of this Attribute. * If there is no parent, then this returns null. * * @return parent of this Attribute */ public Element getParent() { return (Element) parent; } /** * This retrieves the owning {@link Document} for * this Attribute, or null if not a currently a member of a * {@link Document}. * * @return Document owning this Attribute, or null. */ public Document getDocument() { if (parent != null) { return ((Element)parent).getDocument(); } return null; } /** * This will set the parent of this Attribute. * * @param parent Element to be new parent. * @return this Attribute modified. */ protected Attribute setParent(Element parent) { this.parent = parent; return this; } /** * This detaches the Attribute from its parent, or does * nothing if the Attribute has no parent. * * @return Attribute - this Attribute modified. */ public Attribute detach() { Element p = getParent(); if (p != null) { p.removeAttribute(this.getName(), this.getNamespace()); } return this; } /** * This will retrieve the local name of the * Attribute. For any XML attribute * which appears as * [namespacePrefix]:[attributeName], * the local name of the attribute would be * [attributeName]. When the attribute * has no namespace, the local name is simply the attribute * name. *

* To obtain the namespace prefix for this * attribute, the * {@link #getNamespacePrefix()} * method should be used. * * @return String - name of this attribute, * without any namespace prefix. */ public String getName() { return name; } /** * This sets the local name of the Attribute. * * @param name the new local name to set * @return Attribute - the attribute modified. * @throws IllegalNameException if the given name is illegal as an * attribute name. */ public Attribute setName(String name) { String reason; if ((reason = Verifier.checkAttributeName(name)) != null) { throw new IllegalNameException(name, "attribute", reason); } this.name = name; return this; } /** * This will retrieve the qualified name of the Attribute. * For any XML attribute whose name is * [namespacePrefix]:[elementName], * the qualified name of the attribute would be * everything (both namespace prefix and * element name). When the attribute has no * namespace, the qualified name is simply the attribute's * local name. *

* To obtain the local name of the attribute, the * {@link #getName()} method should be used. *

* To obtain the namespace prefix for this attribute, * the {@link #getNamespacePrefix()} * method should be used. * * @return String - full name for this element. */ public String getQualifiedName() { // Note: Any changes here should be reflected in // XMLOutputter.printQualifiedName() String prefix = namespace.getPrefix(); if ((prefix != null) && (!prefix.equals(""))) { return new StringBuilder(prefix) .append(':') .append(getName()) // JCLIC modification //.toString(); .substring(0); } else { return getName(); } } /** * This will retrieve the namespace prefix of the * Attribute. For any XML attribute * which appears as * [namespacePrefix]:[attributeName], * the namespace prefix of the attribute would be * [namespacePrefix]. When the attribute * has no namespace, an empty String is returned. * * @return String - namespace prefix of this * attribute. */ public String getNamespacePrefix() { return namespace.getPrefix(); } /** * This returns the URI mapped to this Attribute's * prefix. If no mapping is found, an empty String is * returned. * * @return String - namespace URI for this Attribute. */ public String getNamespaceURI() { return namespace.getURI(); } /** * This will return this Attribute's * {@link Namespace}. * * @return Namespace - Namespace object for this Attribute */ public Namespace getNamespace() { return namespace; } /** * This sets this Attribute's {@link Namespace}. * If the provided namespace is null, the attribute will have no namespace. * The namespace must have a prefix. * * @param namespace the new namespace * @return Element - the element modified. * @throws IllegalNameException if the new namespace is the default * namespace. Attributes cannot be in a default namespace. */ public Attribute setNamespace(Namespace namespace) { if (namespace == null) { namespace = Namespace.NO_NAMESPACE; } // Verify the attribute isn't trying to be in a default namespace // Attributes can't be in a default namespace if (namespace != Namespace.NO_NAMESPACE && namespace.getPrefix().equals("")) { throw new IllegalNameException("", "attribute namespace", "An attribute namespace without a prefix can only be the " + "NO_NAMESPACE namespace"); } this.namespace = namespace; return this; } /** * This will return the actual textual value of this * Attribute. This will include all text * within the quotation marks. * * @return String - value for this attribute. */ public String getValue() { return value; } /** * This will set the value of the Attribute. * * @param value String value for the attribute. * @return Attribute - this Attribute modified. * @throws IllegalDataException if the given attribute value is * illegal character data (as determined by * {@link org.jdom.Verifier#checkCharacterData}). */ public Attribute setValue(String value) { String reason; if ((reason = Verifier.checkCharacterData(value)) != null) { throw new IllegalDataException(value, "attribute", reason); } this.value = value; return this; } /** * This will return the actual declared type of this * Attribute. * * @return int - type for this attribute. */ public int getAttributeType() { return type; } /** * This will set the type of the Attribute. * * @param type int type for the attribute. * @return Attribute - this Attribute modified. * @throws IllegalDataException if the given attribute type is * not one of the supported types. */ public Attribute setAttributeType(int type) { if ((type < UNDECLARED_TYPE) || (type > ENUMERATED_TYPE)) { throw new IllegalDataException(String.valueOf(type), "attribute", "Illegal attribute type"); } this.type = type; return this; } /** * This returns a String representation of the * Attribute, suitable for debugging. * * @return String - information about the * Attribute */ @Override public String toString() { return new StringBuilder() .append("[Attribute: ") .append(getQualifiedName()) .append("=\"") .append(value) .append("\"") .append("]") // JCLIC modification //.toString(); .substring(0); } /** * This tests for equality of this Attribute to the supplied * Object. * * @param ob Object to compare to. * @return boolean - whether the Attribute is * equal to the supplied Object. */ @Override public final boolean equals(Object ob) { return (ob == this); } /** * This returns the hash code for this Attribute. * * @return int - hash code. */ @Override public final int hashCode() { return super.hashCode(); } /** * This will return a clone of this Attribute. * * @return Object - clone of this Attribute. */ @Override public Object clone() { Attribute attribute = null; try { attribute = (Attribute) super.clone(); } catch(CloneNotSupportedException ce) { // Won't happen } // Name, namespace, and value are references to imutable objects // and are copied by super.clone() (aka Object.clone()) // super.clone() copies reference to set parent to null attribute.parent = null; return attribute; } ///////////////////////////////////////////////////////////////// // Convenience Methods below here ///////////////////////////////////////////////////////////////// /** * This gets the value of the attribute, in * int form, and if no conversion * can occur, throws a * {@link DataConversionException} * * @return int value of attribute. * @throws DataConversionException when conversion fails. */ public int getIntValue() throws DataConversionException { try { return Integer.parseInt(value.trim()); } catch (NumberFormatException e) { throw new DataConversionException(name, "int"); } } /** * This gets the value of the attribute, in * long form, and if no conversion * can occur, throws a * {@link DataConversionException} * * @return long value of attribute. * @throws DataConversionException when conversion fails. */ public long getLongValue() throws DataConversionException { try { return Long.parseLong(value.trim()); } catch (NumberFormatException e) { throw new DataConversionException(name, "long"); } } /** * This gets the value of the attribute, in * float form, and if no conversion * can occur, throws a * {@link DataConversionException} * * @return float value of attribute. * @throws DataConversionException when conversion fails. */ public float getFloatValue() throws DataConversionException { try { // Avoid Float.parseFloat() to support JDK 1.1 return Float.valueOf(value.trim()).floatValue(); } catch (NumberFormatException e) { throw new DataConversionException(name, "float"); } } /** * This gets the value of the attribute, in * double form, and if no conversion * can occur, throws a * {@link DataConversionException} * * @return double value of attribute. * @throws DataConversionException when conversion fails. */ public double getDoubleValue() throws DataConversionException { try { // Avoid Double.parseDouble() to support JDK 1.1 return Double.valueOf(value.trim()).doubleValue(); } catch (NumberFormatException e) { throw new DataConversionException(name, "double"); } } /** * This gets the effective boolean value of the attribute, or throws a * {@link DataConversionException} if a conversion can't be * performed. True values are: "true", "on", "1", and "yes". False * values are: "false", "off", "0", and "no". Values are trimmed before * comparison. Values other than those listed here throw the exception. * * @return boolean value of attribute. * @throws DataConversionException when conversion fails. */ public boolean getBooleanValue() throws DataConversionException { String valueTrim = value.trim(); if ((valueTrim.equalsIgnoreCase("true")) || (valueTrim.equalsIgnoreCase("on")) || (valueTrim.equalsIgnoreCase("1")) || (valueTrim.equalsIgnoreCase("yes"))) { return true; } else if ((valueTrim.equalsIgnoreCase("false")) || (valueTrim.equalsIgnoreCase("off")) || (valueTrim.equalsIgnoreCase("0")) || (valueTrim.equalsIgnoreCase("no"))) { return false; } else { throw new DataConversionException(name, "boolean"); } } // Support a custom Namespace serialization so no two namespace // object instances may exist for the same prefix/uri pair private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // We use writeObject() and not writeUTF() to minimize space // This allows for writing pointers to already written strings out.writeObject(namespace.getPrefix()); out.writeObject(namespace.getURI()); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); namespace = Namespace.getNamespace( (String)in.readObject(), (String)in.readObject()); } }