diff NGSrich_0.5.5/src/org/jdom/Element.java @ 0:89ad0a9cca52 default tip

Uploaded
author pfrommolt
date Mon, 21 Nov 2011 08:12:19 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NGSrich_0.5.5/src/org/jdom/Element.java	Mon Nov 21 08:12:19 2011 -0500
@@ -0,0 +1,1564 @@
+/*--
+
+ $Id: Element.java,v 1.159 2007/11/14 05:02:08 jhunter Exp $
+
+ Copyright (C) 2000-2007 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 <request_AT_jdom_DOT_org>.
+
+ 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 <request_AT_jdom_DOT_org>.
+
+ 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 <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>.  For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+
+package org.jdom;
+
+import java.io.*;
+import java.util.*;
+
+import org.jdom.filter.*;
+
+/**
+ * An XML element. Methods allow the user to get and manipulate its child
+ * elements and content, directly access the element's textual content,
+ * manipulate its attributes, and manage namespaces.
+ *
+ * @version $Revision: 1.159 $, $Date: 2007/11/14 05:02:08 $
+ * @author  Brett McLaughlin
+ * @author  Jason Hunter
+ * @author  Lucas Gonze
+ * @author  Kevin Regan
+ * @author  Dan Schaffer
+ * @author  Yusuf Goolamabbas
+ * @author  Kent C. Johnson
+ * @author  Jools Enticknap
+ * @author  Alex Rosen
+ * @author  Bradley S. Huffman
+ * @author  Victor Toni
+ */
+public class Element extends Content implements Parent {
+
+    private static final String CVS_ID =
+    "@(#) $RCSfile: Element.java,v $ $Revision: 1.159 $ $Date: 2007/11/14 05:02:08 $ $Name: jdom_1_1_1 $";
+
+    private static final int INITIAL_ARRAY_SIZE = 5;
+
+    /** The local name of the element */
+    protected String name;
+
+    /** The namespace of the element */
+    protected transient Namespace namespace;
+
+    /** Additional namespace declarations to store on this element; useful
+     * during output */
+    protected transient List additionalNamespaces;
+
+    // See http://lists.denveronline.net/lists/jdom-interest/2000-September/003030.html
+    // for a possible memory optimization here (using a RootElement subclass)
+
+    /**
+     *  The attributes of the element.  Subclassers have to
+     * track attributes using their own mechanism.
+     */
+    AttributeList attributes = new AttributeList(this);
+
+    /**
+     * The content of the element.  Subclassers have to
+     * track content using their own mechanism.
+     */
+    ContentList content = new ContentList(this);
+
+    /**
+     * This protected constructor is provided in order to support an Element
+     * subclass that wants full control over variable initialization. It
+     * intentionally leaves all instance variables null, allowing a lightweight
+     * subclass implementation. The subclass is responsible for ensuring all the
+     * get and set methods on Element behave as documented.
+     * <p>
+     * When implementing an Element subclass which doesn't require full control
+     * over variable initialization, be aware that simply calling super() (or
+     * letting the compiler add the implicit super() call) will not initialize
+     * the instance variables which will cause many of the methods to throw a
+     * NullPointerException. Therefore, the constructor for these subclasses
+     * should call one of the public constructors so variable initialization is
+     * handled automatically.
+     */
+    protected Element() { }
+
+    /**
+     * Creates a new element with the supplied (local) name and namespace. If
+     * the provided namespace is null, the element will have no namespace.
+     *
+     * @param  name                 local name of the element
+     * @param  namespace            namespace for the element
+     * @throws IllegalNameException if the given name is illegal as an element
+     *                              name
+     */
+    public Element(final String name, final Namespace namespace) {
+        setName(name);
+        setNamespace(namespace);
+    }
+
+    /**
+     * Create a new element with the supplied (local) name and no namespace.
+     *
+     * @param  name                 local name of the element
+     * @throws IllegalNameException if the given name is illegal as an element
+     *                              name.
+     */
+    public Element(final String name) {
+        this(name, (Namespace) null);
+    }
+
+    /**
+     * Creates a new element with the supplied (local) name and a namespace
+     * given by a URI. The element will be put into the unprefixed (default)
+     * namespace.
+     *
+     * @param  name                 name of the element
+     * @param  uri                  namespace URI for the element
+     * @throws IllegalNameException if the given name is illegal as an element
+     *                              name or the given URI is illegal as a
+     *                              namespace URI
+     */
+    public Element(final String name, final String uri) {
+        this(name, Namespace.getNamespace("", uri));
+    }
+
+    /**
+     * Creates a new element with the supplied (local) name and a namespace
+     * given by the supplied prefix and URI combination.
+     *
+     * @param  name                 local name of the element
+     * @param  prefix               namespace prefix
+     * @param  uri                  namespace URI for the element
+     * @throws IllegalNameException if the given name is illegal as an element
+     *                              name, the given prefix is illegal as a
+     *                              namespace prefix, or the given URI is
+     *                              illegal as a namespace URI
+     */
+    public Element(final String name, final String prefix, final String uri) {
+        this(name, Namespace.getNamespace(prefix, uri));
+    }
+
+    /**
+     * Returns the (local) name of the element (without any namespace prefix).
+     *
+     * @return                     local element name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the (local) name of the element.
+     *
+     * @param  name                 the new (local) name of the element
+     * @return                      the target element
+     * @throws IllegalNameException if the given name is illegal as an Element
+     *                              name
+     */
+    public Element setName(final String name) {
+        final String reason = Verifier.checkElementName(name);
+        if (reason != null) {
+            throw new IllegalNameException(name, "element", reason);
+        }
+        this.name = name;
+        return this;
+    }
+
+    /**
+     * Returns the element's {@link Namespace}.
+     *
+     * @return                     the element's namespace
+     */
+    public Namespace getNamespace() {
+        return namespace;
+    }
+
+    /**
+     * Sets the element's {@link Namespace}. If the provided namespace is null,
+     * the element will have no namespace.
+     *
+     * @param  namespace           the new namespace
+     * @return                     the target element
+     */
+    public Element setNamespace(Namespace namespace) {
+        if (namespace == null) {
+            namespace = Namespace.NO_NAMESPACE;
+        }
+
+        this.namespace = namespace;
+        return this;
+    }
+
+    /**
+     * Returns the namespace prefix of the element or an empty string if none
+     * exists.
+     *
+     * @return                     the namespace prefix
+     */
+    public String getNamespacePrefix() {
+        return namespace.getPrefix();
+    }
+
+    /**
+     * Returns the namespace URI mapped to this element's prefix (or the
+     * in-scope default namespace URI if no prefix). If no mapping is found, an
+     * empty string is returned.
+     *
+     * @return                     the namespace URI for this element
+     */
+    public String getNamespaceURI() {
+        return namespace.getURI();
+    }
+
+    /**
+     * Returns the {@link Namespace} corresponding to the given prefix in scope
+     * for this element. This involves searching up the tree, so the results
+     * depend on the current location of the element. Returns null if there is
+     * no namespace in scope with the given prefix at this point in the
+     * document.
+     *
+     * @param  prefix              namespace prefix to look up
+     * @return                     the Namespace for this prefix at this
+     *                             location, or null if none
+     */
+    public Namespace getNamespace(final String prefix) {
+        if (prefix == null) {
+            return null;
+        }
+
+        if ("xml".equals(prefix)) {
+            // Namespace "xml" is always bound.
+            return Namespace.XML_NAMESPACE;
+        }
+
+        // Check if the prefix is the prefix for this element
+        if (prefix.equals(getNamespacePrefix())) {
+            return getNamespace();
+        }
+
+        // Scan the additional namespaces
+        if (additionalNamespaces != null) {
+            for (int i = 0; i < additionalNamespaces.size(); i++) {
+                final Namespace ns = (Namespace) additionalNamespaces.get(i);
+                if (prefix.equals(ns.getPrefix())) {
+                    return ns;
+                }
+            }
+        }
+
+        // If we still don't have a match, ask the parent
+        if (parent instanceof Element) {
+            return ((Element)parent).getNamespace(prefix);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the full name of the element, in the form
+     * [namespacePrefix]:[localName]. If the element does not have a namespace
+     * prefix, then the local name is returned.
+     *
+     * @return                     qualified name of the element (including
+     *                             namespace prefix)
+     */
+    public String getQualifiedName() {
+        // Note: Any changes here should be reflected in
+        // XMLOutputter.printQualifiedName()
+        if ("".equals(namespace.getPrefix())) {
+            return getName();
+        }
+
+        return new StringBuffer(namespace.getPrefix())
+            .append(':')
+            .append(name)
+            .toString();
+    }
+
+    /**
+     * Adds a namespace declarations to this element. This should <i>not</i> be
+     * used to add the declaration for this element itself; that should be
+     * assigned in the construction of the element. Instead, this is for adding
+     * namespace declarations on the element not relating directly to itself.
+     * It's used during output to for stylistic reasons move namespace
+     * declarations higher in the tree than they would have to be.
+     *
+     * @param  additionalNamespace namespace to add
+     * @throws IllegalAddException if the namespace prefix collides with another
+     *                             namespace prefix on the element
+     */
+    public void addNamespaceDeclaration(final Namespace additionalNamespace) {
+
+        // Verify the new namespace prefix doesn't collide with another
+        // declared namespace, an attribute prefix, or this element's prefix
+        final String reason = Verifier.checkNamespaceCollision(additionalNamespace, this);
+        if (reason != null) {
+            throw new IllegalAddException(this, additionalNamespace, reason);
+        }
+
+        if (additionalNamespaces == null) {
+            additionalNamespaces = new ArrayList(INITIAL_ARRAY_SIZE);
+        }
+
+        additionalNamespaces.add(additionalNamespace);
+    }
+
+    /**
+     * Removes an additional namespace declarations from this element. This
+     * should <i>not</i> be used to remove the declaration for this element
+     * itself; that should be handled in the construction of the element.
+     * Instead, this is for removing namespace declarations on the element not
+     * relating directly to itself. If the declaration is not present, this
+     * method does nothing.
+     *
+     * @param additionalNamespace namespace to remove
+     */
+    public void removeNamespaceDeclaration(final Namespace additionalNamespace) {
+        if (additionalNamespaces == null) {
+            return;
+        }
+        additionalNamespaces.remove(additionalNamespace);
+    }
+
+    /**
+     * Returns a list of the additional namespace declarations on this element.
+     * This includes only additional namespace, not the namespace of the element
+     * itself, which can be obtained through {@link #getNamespace()}. If there
+     * are no additional declarations, this returns an empty list. Note, the
+     * returned list is unmodifiable.
+     *
+     * @return                     a List of the additional namespace
+     *                             declarations
+     */
+    public List getAdditionalNamespaces() {
+        // Not having the returned list be live allows us to avoid creating a
+        // new list object when XMLOutputter calls this method on an element
+        // with an empty list.
+        if (additionalNamespaces == null) {
+            return Collections.EMPTY_LIST;
+        }
+        return Collections.unmodifiableList(additionalNamespaces);
+    }
+
+    /**
+     * Returns the XPath 1.0 string value of this element, which is the
+     * complete, ordered content of all text node descendants of this element
+     * (i&#46;e&#46; the text that's left after all references are resolved
+     * and all other markup is stripped out.)
+     *
+     * @return a concatentation of all text node descendants
+     */
+    public String getValue() {
+        final StringBuffer buffer = new StringBuffer();
+
+        final Iterator iter = getContent().iterator();
+        while (iter.hasNext()) {
+            final Content child = (Content) iter.next();
+            if (child instanceof Element || child instanceof Text) {
+                buffer.append(child.getValue());
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns whether this element is a root element. This can be used in
+     * tandem with {@link #getParent} to determine if an element has any
+     * "attachments" to a parent element or document.
+     *
+     * @return                     whether this is a root element
+     */
+    public boolean isRootElement() {
+        return parent instanceof Document;
+    }
+
+    public int getContentSize() {
+        return content.size();
+    }
+
+    public int indexOf(final Content child) {
+        return content.indexOf(child);
+    }
+
+//    private int indexOf(int start, Filter filter) {
+//        int size = getContentSize();
+//        for (int i = start; i < size; i++) {
+//            if (filter.matches(getContent(i))) {
+//                return i;
+//            }
+//        }
+//        return -1;
+//    }
+
+
+    /**
+     * Returns the textual content directly held under this element as a string.
+     * This includes all text within this single element, including whitespace
+     * and CDATA sections if they exist. It's essentially the concatenation of
+     * all {@link Text} and {@link CDATA} nodes returned by {@link #getContent}.
+     * The call does not recurse into child elements. If no textual value exists
+     * for the element, an empty string is returned.
+     *
+     * @return                     text content for this element, or empty
+     *                             string if none
+     */
+    public String getText() {
+        if (content.size() == 0) {
+            return "";
+        }
+
+        // If we hold only a Text or CDATA, return it directly
+        if (content.size() == 1) {
+            final Object obj = content.get(0);
+            if (obj instanceof Text) {
+                return ((Text) obj).getText();
+            }
+            else {
+                return "";
+            }
+        }
+
+        // Else build String up
+        final StringBuffer textContent = new StringBuffer();
+        boolean hasText = false;
+
+        for (int i = 0; i < content.size(); i++) {
+            final Object obj = content.get(i);
+            if (obj instanceof Text) {
+                textContent.append(((Text) obj).getText());
+                hasText = true;
+            }
+        }
+
+        if (!hasText) {
+            return "";
+        }
+        else {
+            return textContent.toString();
+        }
+    }
+
+    /**
+     * Returns the textual content of this element with all surrounding
+     * whitespace removed. If no textual value exists for the element, or if
+     * only whitespace exists, the empty string is returned.
+     *
+     * @return                     trimmed text content for this element, or
+     *                             empty string if none
+     */
+    public String getTextTrim() {
+        return getText().trim();
+    }
+
+    /**
+     * Returns the textual content of this element with all surrounding
+     * whitespace removed and internal whitespace normalized to a single space.
+     * If no textual value exists for the element, or if only whitespace exists,
+     * the empty string is returned.
+     *
+     * @return                     normalized text content for this element, or
+     *                             empty string if none
+     */
+    public String getTextNormalize() {
+        return Text.normalizeString(getText());
+    }
+
+    /**
+     * Returns the textual content of the named child element, or null if
+     * there's no such child. This method is a convenience because calling
+     * <code>getChild().getText()</code> can throw a NullPointerException.
+     *
+     * @param  name                the name of the child
+     * @return                     text content for the named child, or null if
+     *                             no such child
+     */
+    public String getChildText(final String name) {
+        final Element child = getChild(name);
+        if (child == null) {
+            return null;
+        }
+        return child.getText();
+    }
+
+    /**
+     * Returns the trimmed textual content of the named child element, or null
+     * if there's no such child. See <code>{@link #getTextTrim()}</code> for
+     * details of text trimming.
+     *
+     * @param  name                the name of the child
+     * @return                     trimmed text content for the named child, or
+     *                             null if no such child
+     */
+    public String getChildTextTrim(final String name) {
+        final Element child = getChild(name);
+        if (child == null) {
+            return null;
+        }
+        return child.getTextTrim();
+    }
+
+    /**
+     * Returns the normalized textual content of the named child element, or
+     * null if there's no such child. See <code>{@link
+     * #getTextNormalize()}</code> for details of text normalizing.
+     *
+     * @param  name                the name of the child
+     * @return                     normalized text content for the named child,
+     *                             or null if no such child
+     */
+    public String getChildTextNormalize(final String name) {
+        final Element child = getChild(name);
+        if (child == null) {
+            return null;
+        }
+        return child.getTextNormalize();
+    }
+
+    /**
+     * Returns the textual content of the named child element, or null if
+     * there's no such child.
+     *
+     * @param  name                the name of the child
+     * @param  ns                  the namespace of the child
+     * @return                     text content for the named child, or null if
+     *                             no such child
+     */
+    public String getChildText(final String name, final Namespace ns) {
+        final Element child = getChild(name, ns);
+        if (child == null) {
+            return null;
+        }
+        return child.getText();
+    }
+
+    /**
+     * Returns the trimmed textual content of the named child element, or null
+     * if there's no such child.
+     *
+     * @param  name                the name of the child
+     * @param  ns                  the namespace of the child
+     * @return                     trimmed text content for the named child, or
+     *                             null if no such child
+     */
+    public String getChildTextTrim(final String name, final Namespace ns) {
+        final Element child = getChild(name, ns);
+        if (child == null) {
+            return null;
+        }
+        return child.getTextTrim();
+    }
+
+    /**
+     * Returns the normalized textual content of the named child element, or
+     * null if there's no such child.
+     *
+     * @param  name                the name of the child
+     * @param  ns                  the namespace of the child
+     * @return                     normalized text content for the named child,
+     *                             or null if no such child
+     */
+    public String getChildTextNormalize(final String name, final Namespace ns) {
+        final Element child = getChild(name, ns);
+        if (child == null) {
+            return null;
+        }
+        return child.getTextNormalize();
+    }
+
+    /**
+     * Sets the content of the element to be the text given. All existing text
+     * content and non-text context is removed. If this element should have both
+     * textual content and nested elements, use <code>{@link #setContent}</code>
+     * instead. Setting a null text value is equivalent to setting an empty
+     * string value.
+     *
+     * @param  text                 new text content for the element
+     * @return                      the target element
+     * @throws IllegalDataException if the assigned text contains an illegal
+     *                              character such as a vertical tab (as
+     *                              determined by {@link
+     *                              org.jdom.Verifier#checkCharacterData})
+     */
+    public Element setText(final String text) {
+        content.clear();
+
+        if (text != null) {
+            addContent(new Text(text));
+        }
+
+        return this;
+    }
+
+    /**
+     * This returns the full content of the element as a List which
+     * may contain objects of type <code>Text</code>, <code>Element</code>,
+     * <code>Comment</code>, <code>ProcessingInstruction</code>,
+     * <code>CDATA</code>, and <code>EntityRef</code>.
+     * The List returned is "live" in document order and modifications
+     * to it affect the element's actual contents.  Whitespace content is
+     * returned in its entirety.
+     *
+     * <p>
+     * Sequential traversal through the List is best done with an Iterator
+     * since the underlying implement of List.size() may require walking the
+     * entire list.
+     * </p>
+     *
+     * @return a <code>List</code> containing the mixed content of the
+     *         element: may contain <code>Text</code>,
+     *         <code>{@link Element}</code>, <code>{@link Comment}</code>,
+     *         <code>{@link ProcessingInstruction}</code>,
+     *         <code>{@link CDATA}</code>, and
+     *         <code>{@link EntityRef}</code> objects.
+     */
+    public List getContent() {
+        return content;
+    }
+
+    /**
+     * Return a filter view of this <code>Element</code>'s content.
+     *
+     * <p>
+     * Sequential traversal through the List is best done with a Iterator
+     * since the underlying implement of List.size() may require walking the
+     * entire list.
+     * </p>
+     *
+     * @param filter <code>Filter</code> to apply
+     * @return <code>List</code> - filtered Element content
+     */
+    public List getContent(final Filter filter) {
+        return content.getView(filter);
+    }
+
+    /**
+     * Removes all child content from this parent.
+     *
+     * @return list of the old children detached from this parent
+     */
+    public List removeContent() {
+        final List old = new ArrayList(content);
+        content.clear();
+        return old;
+    }
+
+    /**
+     * Remove all child content from this parent matching the supplied filter.
+     *
+     * @param filter filter to select which content to remove
+     * @return list of the old children detached from this parent
+     */
+    public List removeContent(final Filter filter) {
+        final List old = new ArrayList();
+        final Iterator iter = content.getView(filter).iterator();
+        while (iter.hasNext()) {
+            final Content child = (Content) iter.next();
+            old.add(child);
+            iter.remove();
+        }
+        return old;
+    }
+
+    /**
+     * This sets the content of the element.  The supplied List should
+     * contain only objects of type <code>Element</code>, <code>Text</code>,
+     * <code>CDATA</code>, <code>Comment</code>,
+     * <code>ProcessingInstruction</code>, and <code>EntityRef</code>.
+     *
+     * <p>
+     * When all objects in the supplied List are legal and before the new
+     * content is added, all objects in the old content will have their
+     * parentage set to null (no parent) and the old content list will be
+     * cleared. This has the effect that any active list (previously obtained
+     * with a call to {@link #getContent} or {@link #getChildren}) will also
+     * change to reflect the new content.  In addition, all objects in the
+     * supplied List will have their parentage set to this element, but the
+     * List itself will not be "live" and further removals and additions will
+     * have no effect on this elements content. If the user wants to continue
+     * working with a "live" list, then a call to setContent should be
+     * followed by a call to {@link #getContent} or {@link #getChildren} to
+     * obtain a "live" version of the content.
+     * </p>
+     *
+     * <p>
+     * Passing a null or empty List clears the existing content.
+     * </p>
+     *
+     * <p>
+     * In event of an exception the original content will be unchanged and
+     * the objects in the supplied content will be unaltered.
+     * </p>
+     *
+     * @param newContent <code>Collection</code> of content to set
+     * @return this element modified
+     * @throws IllegalAddException if the List contains objects of
+     *         illegal types or with existing parentage.
+     */
+    public Element setContent(final Collection newContent) {
+        content.clearAndSet(newContent);
+        return this;
+    }
+
+    /**
+     * Replace the current child the given index with the supplied child.
+     * <p>
+     * In event of an exception the original content will be unchanged and
+     * the supplied child will be unaltered.
+     * </p>
+     *
+     * @param index - index of child to replace.
+     * @param child - child to add.
+     * @return element on which this method was invoked
+     * @throws IllegalAddException if the supplied child is already attached
+     *                             or not legal content for this parent.
+     * @throws IndexOutOfBoundsException if index is negative or greater
+     *         than the current number of children.
+     */
+    public Element setContent(final int index, final Content child) {
+        content.set(index, child);
+        return this;
+    }
+
+    /**
+     * Replace the child at the given index whith the supplied
+     * collection.
+     * <p>
+     * In event of an exception the original content will be unchanged and
+     * the content in the supplied collection will be unaltered.
+     * </p>
+     *
+     * @param index - index of child to replace.
+     * @param newContent - <code>Collection</code> of content to replace child.
+     * @return object on which this method was invoked
+     * @throws IllegalAddException if the collection contains objects of
+     *         illegal types.
+     * @throws IndexOutOfBoundsException if index is negative or greater
+     *         than the current number of children.
+     */
+    public Parent setContent(final int index, final Collection newContent) {
+        content.remove(index);
+        content.addAll(index, newContent);
+        return this;
+    }
+
+    /**
+     * This adds text content to this element.  It does not replace the
+     * existing content as does <code>setText()</code>.
+     *
+     * @param str <code>String</code> to add
+     * @return this element modified
+     * @throws IllegalDataException if <code>str</code> contains an
+     *         illegal character such as a vertical tab (as determined
+     *         by {@link org.jdom.Verifier#checkCharacterData})
+     */
+    public Element addContent(final String str) {
+        return addContent(new Text(str));
+    }
+
+    /**
+     * Appends the child to the end of the element's content list.
+     *
+     * @param child   child to append to end of content list
+     * @return        the element on which the method was called
+     * @throws IllegalAddException if the given child already has a parent.     */
+    public Element addContent(final Content child) {
+        content.add(child);
+        return this;
+    }
+
+    /**
+     * Appends all children in the given collection to the end of
+     * the content list.  In event of an exception during add the
+     * original content will be unchanged and the objects in the supplied
+     * collection will be unaltered.
+     *
+     * @param newContent <code>Collection</code> of content to append
+     * @return           the element on which the method was called
+     * @throws IllegalAddException if any item in the collection
+     *         already has a parent or is of an inappropriate type.
+     */
+    public Element addContent(final Collection newContent) {
+        content.addAll(newContent);
+        return this;
+    }
+
+    /**
+     * Inserts the child into the content list at the given index.
+     *
+     * @param index location for adding the collection
+     * @param child      child to insert
+     * @return           the parent on which the method was called
+     * @throws IndexOutOfBoundsException if index is negative or beyond
+     *         the current number of children
+     * @throws IllegalAddException if the given child already has a parent.
+     */
+    public Element addContent(final int index, final Content child) {
+        content.add(index, child);
+        return this;
+    }
+
+    /**
+     * Inserts the content in a collection into the content list
+     * at the given index.  In event of an exception the original content
+     * will be unchanged and the objects in the supplied collection will be
+     * unaltered.
+     *
+     * @param index location for adding the collection
+     * @param newContent  <code>Collection</code> of content to insert
+     * @return            the parent on which the method was called
+     * @throws IndexOutOfBoundsException if index is negative or beyond
+     *         the current number of children
+     * @throws IllegalAddException if any item in the collection
+     *         already has a parent or is of an inappropriate type.
+     */
+    public Element addContent(final int index, final Collection newContent) {
+        content.addAll(index, newContent);
+        return this;
+    }
+
+    public List cloneContent() {
+        final int size = getContentSize();
+        final List list = new ArrayList(size);
+        for (int i = 0; i < size; i++) {
+            final Content child = getContent(i);
+            list.add(child.clone());
+        }
+        return list;
+    }
+
+    public Content getContent(final int index) {
+        return (Content) content.get(index);
+    }
+
+//    public Content getChild(Filter filter) {
+//        int i = indexOf(0, filter);
+//        return (i < 0) ? null : getContent(i);
+//    }
+
+    public boolean removeContent(final Content child) {
+        return content.remove(child);
+    }
+
+    public Content removeContent(final int index) {
+        return (Content) content.remove(index);
+    }
+
+    /**
+     * Set this element's content to be the supplied child.
+     * <p>
+     * If the supplied child is legal content for this parent and before
+     * it is added, all content in the current content list will
+     * be cleared and all current children will have their parentage set to
+     * null.
+     * <p>
+     * This has the effect that any active list (previously obtained with
+     * a call to one of the {@link #getContent} methods will also change
+     * to reflect the new content.  In addition, all content in the supplied
+     * collection will have their parentage set to this parent.  If the user
+     * wants to continue working with a <b>"live"</b> list of this parent's
+     * child, then a call to setContent should be followed by a call to one
+     * of the {@link #getContent} methods to obtain a <b>"live"</b>
+     * version of the children.
+     * <p>
+     * Passing a null child clears the existing content.
+     * <p>
+     * In event of an exception the original content will be unchanged and
+     * the supplied child will be unaltered.
+     *
+     * @param child new content to replace existing content
+     * @return           the parent on which the method was called
+     * @throws IllegalAddException if the supplied child is already attached
+     *                             or not legal content for an Element
+     */
+    public Element setContent(final Content child) {
+        content.clear();
+        content.add(child);
+        return this;
+    }
+
+
+    /**
+     * Determines if this element is the ancestor of another element.
+     *
+     * @param element <code>Element</code> to check against
+     * @return <code>true</code> if this element is the ancestor of the
+     *         supplied element
+     */
+    public boolean isAncestor(final Element element) {
+        Parent p = element.getParent();
+        while (p instanceof Element) {
+            if (p == this) {
+                return true;
+            }
+            p = p.getParent();
+        }
+        return false;
+    }
+
+    /**
+     * <p>
+     * This returns the complete set of attributes for this element, as a
+     * <code>List</code> of <code>Attribute</code> objects in no particular
+     * order, or an empty list if there are none.
+     * The returned list is "live" and changes to it affect the
+     * element's actual attributes.
+     * </p>
+     *
+     * @return attributes for the element
+     */
+    public List getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * <p>
+     * This returns the attribute for this element with the given name
+     * and within no namespace, or null if no such attribute exists.
+     * </p>
+     *
+     * @param name name of the attribute to return
+     * @return attribute for the element
+     */
+    public Attribute getAttribute(final String name) {
+        return getAttribute(name, Namespace.NO_NAMESPACE);
+    }
+
+    /**
+     * <p>
+     * This returns the attribute for this element with the given name
+     * and within the given Namespace, or null if no such attribute exists.
+     * </p>
+     *
+     * @param name name of the attribute to return
+     * @param ns <code>Namespace</code> to search within
+     * @return attribute for the element
+     */
+    public Attribute getAttribute(final String name, final Namespace ns) {
+        return (Attribute) attributes.get(name, ns);
+    }
+
+    /**
+     * <p>
+     * This returns the attribute value for the attribute with the given name
+     * and within no namespace, null if there is no such attribute, and the
+     * empty string if the attribute value is empty.
+     * </p>
+     *
+     * @param name name of the attribute whose value to be returned
+     * @return the named attribute's value, or null if no such attribute
+     */
+    public String getAttributeValue(final String name) {
+        return getAttributeValue(name, Namespace.NO_NAMESPACE);
+    }
+
+    /**
+     * <p>
+     * This returns the attribute value for the attribute with the given name
+     * and within no namespace, or the passed-in default if there is no
+     * such attribute.
+     * </p>
+     *
+     * @param name name of the attribute whose value to be returned
+     * @param def a default value to return if the attribute does not exist
+     * @return the named attribute's value, or the default if no such attribute
+     */
+    public String getAttributeValue(final String name, final String def) {
+        return getAttributeValue(name, Namespace.NO_NAMESPACE, def);
+    }
+
+    /**
+     * <p>
+     * This returns the attribute value for the attribute with the given name
+     * and within the given Namespace, null if there is no such attribute, and
+     * the empty string if the attribute value is empty.
+     * </p>
+     *
+     * @param name name of the attribute whose valud is to be returned
+     * @param ns <code>Namespace</code> to search within
+     * @return the named attribute's value, or null if no such attribute
+     */
+    public String getAttributeValue(final String name, final Namespace ns) {
+        return getAttributeValue(name, ns, null);
+    }
+
+    /**
+     * <p>
+     * This returns the attribute value for the attribute with the given name
+     * and within the given Namespace, or the passed-in default if there is no
+     * such attribute.
+     * </p>
+     *
+     * @param name name of the attribute whose valud is to be returned
+     * @param ns <code>Namespace</code> to search within
+     * @param def a default value to return if the attribute does not exist
+     * @return the named attribute's value, or the default if no such attribute
+     */
+    public String getAttributeValue(final String name, final Namespace ns, final String def) {
+        final Attribute attribute = (Attribute) attributes.get(name, ns);
+        if (attribute == null) {
+            return def;
+        }
+
+        return attribute.getValue();
+    }
+
+    /**
+     * <p>
+     * This sets the attributes of the element.  The supplied Collection should
+     * contain only objects of type <code>Attribute</code>.
+     * </p>
+     *
+     * <p>
+     * When all objects in the supplied List are legal and before the new
+     * attributes are added, all old attributes will have their
+     * parentage set to null (no parent) and the old attribute list will be
+     * cleared. This has the effect that any active attribute list (previously
+     * obtained with a call to {@link #getAttributes}) will also change to
+     * reflect the new attributes.  In addition, all attributes in the supplied
+     * List will have their parentage set to this element, but the List itself
+     * will not be "live" and further removals and additions will have no
+     * effect on this elements attributes. If the user wants to continue
+     * working with a "live" attribute list, then a call to setAttributes
+     * should be followed by a call to {@link #getAttributes} to obtain a
+     * "live" version of the attributes.
+     * </p>
+     *
+     * <p>
+     * Passing a null or empty List clears the existing attributes.
+     * </p>
+     *
+     * <p>
+     * In cases where the List contains duplicate attributes, only the last
+     * one will be retained.  This has the same effect as calling
+     * {@link #setAttribute(Attribute)} sequentially.
+     * </p>
+     *
+     * <p>
+     * In event of an exception the original attributes will be unchanged and
+     * the attributes in the supplied attributes will be unaltered.
+     * </p>
+     *
+     * @param newAttributes <code>Collection</code> of attributes to set
+     * @return this element modified
+     * @throws IllegalAddException if the List contains objects
+     *         that are not instances of <code>Attribute</code>,
+     *         or if any of the <code>Attribute</code> objects have
+     *         conflicting namespace prefixes.
+     */
+    public Element setAttributes(final Collection newAttributes) {
+        attributes.clearAndSet(newAttributes);
+        return this;
+    }
+
+    /**
+     * <p>
+     * This sets the attributes of the element.  It's an alternate form of
+     * the method, accepting a <code>List</code> instead of a
+     * <code>Collection</code>, for backward compatibility.
+     * </p>
+     */
+    public Element setAttributes(final List newAttributes) {
+        return setAttributes((Collection)newAttributes);
+    }
+
+    /**
+     * <p>
+     * This sets an attribute value for this element.  Any existing attribute
+     * with the same name and namespace URI is removed.
+     * </p>
+     *
+     * @param name name of the attribute to set
+     * @param value value of the attribute to set
+     * @return this element modified
+     * @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 Element setAttribute(final String name, final String value) {
+        final Attribute attribute = getAttribute(name);
+        if (attribute == null) {
+            final Attribute newAttribute = new Attribute(name, value);
+            setAttribute(newAttribute);
+        } else {
+            attribute.setValue(value);
+        }
+
+        return this;
+    }
+
+    /**
+     * <p>
+     * This sets an attribute value for this element.  Any existing attribute
+     * with the same name and namespace URI is removed.
+     * </p>
+     *
+     * @param name name of the attribute to set
+     * @param value value of the attribute to set
+     * @param ns namespace of the attribute to set
+     * @return this element modified
+     * @throws IllegalNameException if the given name is illegal as an
+     *         attribute name, or if the namespace is an unprefixed default
+     *         namespace
+     * @throws IllegalDataException if the given attribute value is
+     *         illegal character data (as determined by
+     *         {@link org.jdom.Verifier#checkCharacterData}).
+     * @throws IllegalAddException if the attribute namespace prefix
+     *         collides with another namespace prefix on the element.
+     */
+    public Element setAttribute(final String name, final String value, final Namespace ns) {
+        final Attribute attribute = getAttribute(name, ns);
+        if (attribute == null) {
+            final Attribute newAttribute = new Attribute(name, value, ns);
+            setAttribute(newAttribute);
+        } else {
+            attribute.setValue(value);
+        }
+
+        return this;
+    }
+
+    /**
+     * <p>
+     * This sets an attribute value for this element.  Any existing attribute
+     * with the same name and namespace URI is removed.
+     * </p>
+     *
+     * @param attribute <code>Attribute</code> to set
+     * @return this element modified
+     * @throws IllegalAddException if the attribute being added already has a
+     *   parent or if the attribute namespace prefix collides with another
+     *   namespace prefix on the element.
+     */
+    public Element setAttribute(final Attribute attribute) {
+        attributes.add(attribute);
+        return this;
+    }
+
+    /**
+     * <p>
+     * This removes the attribute with the given name and within no
+     * namespace. If no such attribute exists, this method does nothing.
+     * </p>
+     *
+     * @param name name of attribute to remove
+     * @return whether the attribute was removed
+     */
+    public boolean removeAttribute(final String name) {
+        return removeAttribute(name, Namespace.NO_NAMESPACE);
+    }
+
+    /**
+     * <p>
+     * This removes the attribute with the given name and within the
+     * given Namespace.  If no such attribute exists, this method does
+     * nothing.
+     * </p>
+     *
+     * @param name name of attribute to remove
+     * @param ns namespace URI of attribute to remove
+     * @return whether the attribute was removed
+     */
+    public boolean removeAttribute(final String name, final Namespace ns) {
+        return attributes.remove(name, ns);
+    }
+
+    /**
+     * <p>
+     * This removes the supplied Attribute should it exist.
+     * </p>
+     *
+     * @param attribute Reference to the attribute to be removed.
+     * @return whether the attribute was removed
+     */
+    public boolean removeAttribute(final Attribute attribute) {
+        return attributes.remove(attribute);
+    }
+
+    /**
+     * <p>
+     *  This returns a <code>String</code> representation of the
+     *    <code>Element</code>, suitable for debugging. If the XML
+     *    representation of the <code>Element</code> is desired,
+     *    {@link org.jdom.output.XMLOutputter#outputString(Element)}
+     *    should be used.
+     * </p>
+     *
+     * @return <code>String</code> - information about the
+     *         <code>Element</code>
+     */
+    public String toString() {
+        final StringBuffer stringForm = new StringBuffer(64)
+            .append("[Element: <")
+            .append(getQualifiedName());
+
+        final String nsuri = getNamespaceURI();
+        if (!"".equals(nsuri)) {
+            stringForm
+            .append(" [Namespace: ")
+            .append(nsuri)
+            .append("]");
+        }
+        stringForm.append("/>]");
+
+        return stringForm.toString();
+    }
+
+    /**
+     * <p>
+     *  This returns a deep clone of this element.
+     *  The new element is detached from its parent, and getParent()
+     *  on the clone will return null.
+     * </p>
+     *
+     * @return the clone of this element
+     */
+   public Object clone() {
+
+       // Ken Rune Helland <kenh@csc.no> is our local clone() guru
+
+       final Element element = (Element) super.clone();
+
+       // name and namespace are references to immutable objects
+       // so super.clone() handles them ok
+
+       // Reference to parent is copied by super.clone()
+       // (Object.clone()) so we have to remove it
+       // Actually, super is a Content, which has already detached in the
+       // clone().
+       // element.parent = null;
+
+       // Reference to content list and attribute lists are copyed by
+       // super.clone() so we set it new lists if the original had lists
+       element.content = new ContentList(element);
+       element.attributes = new AttributeList(element);
+
+       // Cloning attributes
+       if (attributes != null) {
+           for(int i = 0; i < attributes.size(); i++) {
+               final Attribute attribute = (Attribute) attributes.get(i);
+               element.attributes.add(attribute.clone());
+           }
+       }
+
+       // Cloning additional namespaces
+       if (additionalNamespaces != null) {
+           element.additionalNamespaces = new ArrayList(additionalNamespaces);
+       }
+
+       // Cloning content
+       if (content != null) {
+           for(int i = 0; i < content.size(); i++) {
+               final Content c = (Content) content.get(i);
+               element.content.add(c.clone());
+           }
+       }
+
+       return element;
+   }
+
+
+    // Support a custom Namespace serialization so no two namespace
+    // object instances may exist for the same prefix/uri pair
+    private void writeObject(final 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());
+
+        if (additionalNamespaces == null) {
+            out.write(0);
+        }
+        else {
+            final int size = additionalNamespaces.size();
+            out.write(size);
+            for (int i = 0; i < size; i++) {
+                final Namespace additional = (Namespace) additionalNamespaces.get(i);
+                out.writeObject(additional.getPrefix());
+                out.writeObject(additional.getURI());
+            }
+        }
+    }
+
+    private void readObject(final ObjectInputStream in)
+        throws IOException, ClassNotFoundException {
+
+        in.defaultReadObject();
+
+        namespace = Namespace.getNamespace(
+            (String)in.readObject(), (String)in.readObject());
+
+        final int size = in.read();
+
+        if (size != 0) {
+            additionalNamespaces = new ArrayList(size);
+            for (int i = 0; i < size; i++) {
+                final Namespace additional = Namespace.getNamespace(
+                    (String)in.readObject(), (String)in.readObject());
+                additionalNamespaces.add(additional);
+            }
+        }
+    }
+
+    /**
+     * Returns an iterator that walks over all descendants in document order.
+     *
+     * @return an iterator to walk descendants
+     */
+    public Iterator getDescendants() {
+        return new DescendantIterator(this);
+    }
+
+    /**
+     * Returns an iterator that walks over all descendants in document order
+     * applying the Filter to return only elements that match the filter rule.
+     * With filters you can match only Elements, only Comments, Elements or
+     * Comments, only Elements with a given name and/or prefix, and so on.
+     *
+     * @param filter filter to select which descendants to see
+     * @return an iterator to walk descendants within a filter
+     */
+    public Iterator getDescendants(final Filter filter) {
+        final Iterator iterator = new DescendantIterator(this);
+        return new FilterIterator(iterator, filter);
+    }
+
+
+
+    /**
+     * This returns a <code>List</code> of all the child elements
+     * nested directly (one level deep) within this element, as
+     * <code>Element</code> objects.  If this target element has no nested
+     * elements, an empty List is returned.  The returned list is "live"
+     * in document order and changes to it affect the element's actual
+     * contents.
+     *
+     * <p>
+     * Sequential traversal through the List is best done with a Iterator
+     * since the underlying implement of List.size() may not be the most
+     * efficient.
+     * </p>
+     *
+     * <p>
+     * No recursion is performed, so elements nested two levels deep
+     * would have to be obtained with:
+     * <pre>
+     * <code>
+     *   Iterator itr = (currentElement.getChildren()).iterator();
+     *   while(itr.hasNext()) {
+     *     Element oneLevelDeep = (Element)itr.next();
+     *     List twoLevelsDeep = oneLevelDeep.getChildren();
+     *     // Do something with these children
+     *   }
+     * </code>
+     * </pre>
+     * </p>
+     *
+     * @return list of child <code>Element</code> objects for this element
+     */
+    public List getChildren() {
+        return content.getView(new ElementFilter());
+    }
+
+    /**
+     * This returns a <code>List</code> of all the child elements
+     * nested directly (one level deep) within this element with the given
+     * local name and belonging to no namespace, returned as
+     * <code>Element</code> objects.  If this target element has no nested
+     * elements with the given name outside a namespace, an empty List
+     * is returned.  The returned list is "live" in document order
+     * and changes to it affect the element's actual contents.
+     * <p>
+     * Please see the notes for <code>{@link #getChildren}</code>
+     * for a code example.
+     * </p>
+     *
+     * @param name local name for the children to match
+     * @return all matching child elements
+     */
+    public List getChildren(final String name) {
+        return getChildren(name, Namespace.NO_NAMESPACE);
+    }
+
+    /**
+     * This returns a <code>List</code> of all the child elements
+     * nested directly (one level deep) within this element with the given
+     * local name and belonging to the given Namespace, returned as
+     * <code>Element</code> objects.  If this target element has no nested
+     * elements with the given name in the given Namespace, an empty List
+     * is returned.  The returned list is "live" in document order
+     * and changes to it affect the element's actual contents.
+     * <p>
+     * Please see the notes for <code>{@link #getChildren}</code>
+     * for a code example.
+     * </p>
+     *
+     * @param name local name for the children to match
+     * @param ns <code>Namespace</code> to search within
+     * @return all matching child elements
+     */
+    public List getChildren(final String name, final Namespace ns) {
+        return content.getView(new ElementFilter(name, ns));
+    }
+
+    /**
+     * This returns the first child element within this element with the
+     * given local name and belonging to the given namespace.
+     * If no elements exist for the specified name and namespace, null is
+     * returned.
+     *
+     * @param name local name of child element to match
+     * @param ns <code>Namespace</code> to search within
+     * @return the first matching child element, or null if not found
+     */
+    public Element getChild(final String name, final Namespace ns) {
+        final List elements = content.getView(new ElementFilter(name, ns));
+        final Iterator iter = elements.iterator();
+        if (iter.hasNext()) {
+            return (Element) iter.next();
+        }
+        return null;
+    }
+
+    /**
+     * This returns the first child element within this element with the
+     * given local name and belonging to no namespace.
+     * If no elements exist for the specified name and namespace, null is
+     * returned.
+     *
+     * @param name local name of child element to match
+     * @return the first matching child element, or null if not found
+     */
+    public Element getChild(final String name) {
+        return getChild(name, Namespace.NO_NAMESPACE);
+    }
+
+    /**
+     * <p>
+     * This removes the first child element (one level deep) with the
+     * given local name and belonging to no namespace.
+     * Returns true if a child was removed.
+     * </p>
+     *
+     * @param name the name of child elements to remove
+     * @return whether deletion occurred
+     */
+    public boolean removeChild(final String name) {
+        return removeChild(name, Namespace.NO_NAMESPACE);
+    }
+
+    /**
+     * <p>
+     * This removes the first child element (one level deep) with the
+     * given local name and belonging to the given namespace.
+     * Returns true if a child was removed.
+     * </p>
+     *
+     * @param name the name of child element to remove
+     * @param ns <code>Namespace</code> to search within
+     * @return whether deletion occurred
+     */
+    public boolean removeChild(final String name, final Namespace ns) {
+        final Filter filter = new ElementFilter(name, ns);
+        final List old = content.getView(filter);
+        final Iterator iter = old.iterator();
+        if (iter.hasNext()) {
+            iter.next();
+            iter.remove();
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * <p>
+     * This removes all child elements (one level deep) with the
+     * given local name and belonging to no namespace.
+     * Returns true if any were removed.
+     * </p>
+     *
+     * @param name the name of child elements to remove
+     * @return whether deletion occurred
+     */
+    public boolean removeChildren(final String name) {
+        return removeChildren(name, Namespace.NO_NAMESPACE);
+    }
+
+    /**
+     * <p>
+     * This removes all child elements (one level deep) with the
+     * given local name and belonging to the given namespace.
+     * Returns true if any were removed.
+     * </p>
+     *
+     * @param name the name of child elements to remove
+     * @param ns <code>Namespace</code> to search within
+     * @return whether deletion occurred
+     */
+    public boolean removeChildren(final String name, final Namespace ns) {
+        boolean deletedSome = false;
+
+        final Filter filter = new ElementFilter(name, ns);
+        final List old = content.getView(filter);
+        final Iterator iter = old.iterator();
+        while (iter.hasNext()) {
+            iter.next();
+            iter.remove();
+            deletedSome = true;
+        }
+
+        return deletedSome;
+    }
+
+}