The Attribute Class

The Attribute class, shown in Example 15.10, represents an attribute of an element, other than one that declares a namespace. Each attribute has these five basic properties.

Local name

A String accessible through the setName() and getName() methods

Namespace

A Namespace object which encapsulates both the namespace URI and prefix. This is accessible through the setNamespace() and getNamespace() methods. For all unprefixed attributes this is Namespace.NO_NAMESPACE. The prefix, URI, and fully qualified name are separately readable through the getNamespaceURI(), getNamespacePrefix(), and getQualifiedName() convenience methods.

Value

A String containing the attribute’s normalized value. This is accessible through the getValue() and setValue() methods. The unnormalized value is not available. There are also convenience methods that read the attribute value as a double, float, long, int, or boolean.

Parent

The Element that possesses this Attribute. This is accessible through the getParent() and setParent() methods. An Attribute cannot have more than one parent. Before attaching a new parent Element, you must first invoke detach() to remove the Attribute from its current parent.

Type

The Attribute’s type, as specified in the DTD. This is one of the ten named constants Attribute.CDATA_ATTRIBUTE, Attribute.ID_ATTRIBUTE, Attribute.IDREFS_ATTRIBUTE, Attribute.IDREFS_ATTRIBUTE, Attribute.ENUMERATED_ATTRIBUTE, and so forth. If the DTD does not specify the attribute’s type, then the type is set to Attribute.UNDECLARED_ATTRIBUTE.[3] The getAttributeType() and setAttributeType() methods access this property.

In addition, you can get the Document to which the Attribute belongs with the getDocument() method. However, this is not really independent of the Element to which the attribute is attached.

Example 15.10. The JDOM Attribute class

package org.jdom;

public class Attribute implements Serializable, Cloneable {

  public final static int UNDECLARED_ATTRIBUTE = 0;
  public final static int CDATA_ATTRIBUTE      = 1;
  public final static int ID_ATTRIBUTE         = 2;
  public final static int IDREF_ATTRIBUTE      = 3;
  public final static int IDREFS_ATTRIBUTE     = 4;
  public final static int ENTITY_ATTRIBUTE     = 5;
  public final static int ENTITIES_ATTRIBUTE   = 6;
  public final static int NMTOKEN_ATTRIBUTE    = 7;
  public final static int NMTOKENS_ATTRIBUTE   = 8;
  public final static int NOTATION_ATTRIBUTE   = 9;
  public final static int ENUMERATED_ATTRIBUTE = 10;

  protected           String    name;
  protected transient Namespace namespace;
  protected           String    value;
  protected           int       type;
  protected           Object    parent;

  protected Attribute();
  
  public Attribute(String name, String value, 
   Namespace namespace);
  public Attribute(String name, String value, int type, 
   Namespace namespace);
  public Attribute(String name, String value);
  public Attribute(String name, String value, int type);

  public Document     getDocument();
  public Element      getParent();
  protected Attribute setParent(Element parent);
  public Attribute    detach();
  public String       getName();
  public Attribute    setName(String name);
  public String       getQualifiedName();
  public String       getNamespacePrefix();
  public String       getNamespaceURI();
  public Namespace    getNamespace();
  public Attribute    setNamespace(Namespace namespace);
  public String       getValue();
  public Attribute    setValue(String value);
  public int          getAttributeType();
  public Attribute    setAttributeType(int type);
  
  public String        toString();
  public final boolean equals(Object o);
  public final int     hashCode();
  public Object        clone();

  public int     getIntValue() throws DataConversionException;
  public long    getLongValue() throws DataConversionException;
  public float   getFloatValue() throws DataConversionException;
  public double  getDoubleValue() throws DataConversionException;
  public boolean getBooleanValue() throws DataConversionException;

}

Tip

One of the key things to remember when working with attributes, whether in JDOM or any other XML technology, is that unprefixed attributes are never in any namespace. In particular,

  1. Attributes are never in the default namespace.

  2. An attribute is not in the same namespace as its parent element (except in the unusual case where it happens to have the same prefix as its parent element).

90% or more of the time, you’re just going to use the the set/get/removeAttribute() methods in the Element class rather than using the Attribute class. The only major case where you really need to use the Attribute class directly is when the attribute type matters; for instance when you want to treat an ID-type attribute differently than a CDATA attribute or a NOTATIONS attribute differently than a NMTOKENS attribute.

For example, consider attribute value normalization. When a parser reports an attribute value to the client application it adjusts the white space according to the attribute type. Attributes of type CDATA and undeclared attributes preserve all white space. However, white space is trimmed from the edges of all other attribute types, and all runs of white space are compressed to a single space. For instance, consider this fact start-tag:

<fact source=" f21 f32   f33
 f122 f87 f893  ">

If the document’s DTD declares that the source attribute has type IDREFS, then the parser will report its value as f21 f32 f33 f122 f87 f893. On the other hand, if the DTD declares that it has type CDATA or does not assign it a type at all, then the parser will report its value with all the spaces and line breaks intact.

JDOM will accept whatever value the parser initially reports when the document is constructed. However, if attributes are added or their values modified later, then attributes will no longer be in normalized form. To fix this, we can write a method that searches a document for attributes whose type is something other than CDATA and normalizes their space. As usual the method is recursive:

  public void normalizeAttributes(Element element) {

    List attributes = element.getAttributes();
    Iterator iterator = attributes.iterator();
    while (iterator.hasNext()) {
      Attribute attribute = (Attribute) iterator.next();
      int type = attribute.getAttributeType();
      if (type != Attribute.CDATA_ATTRIBUTE 
       && type != Attribute.UNDECLARED_ATTRIBUTE) {
         String oldValue = attribute.getValue();
         String newValue = Text.normalizeString(oldValue);
         attribute.setValue(newValue);
      }
    }
    
    List content = element.getContent();
    Iterator children = content.iterator();
    while (children.hasNext()) {
      Object o = children.next();
      if (o instanceof Element) {
        Element child = (Element) o;
        normalizeAttributes(child);
      }
    }
    
  }

The actual normalization is performed by the static Text.normalizeString() method from the Text class.



[3] In practice, SAX gets this wrong. It does not distinguish between CDATA type attributes and undeclared attributes. Thus when a SAX parser builds a JDOM Document, no attributes will have type Attribute.UNDECLARED_ATTRIBUTE.


Copyright 2001, 2002 Elliotte Rusty Haroldelharo@metalab.unc.eduLast Modified April 30, 2002
Up To Cafe con Leche