1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564 |
- /*--
-
- $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 $";
-
- 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.e. 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;
- }
-
- }
|