KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > Element


1 /*--
2
3  $Id: Element.java,v 1.154 2004/12/11 00:27:38 jhunter Exp $
4
5  Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6  All rights reserved.
7
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions
10  are met:
11
12  1. Redistributions of source code must retain the above copyright
13     notice, this list of conditions, and the following disclaimer.
14
15  2. Redistributions in binary form must reproduce the above copyright
16     notice, this list of conditions, and the disclaimer that follows
17     these conditions in the documentation and/or other materials
18     provided with the distribution.
19
20  3. The name "JDOM" must not be used to endorse or promote products
21     derived from this software without prior written permission. For
22     written permission, please contact <request_AT_jdom_DOT_org>.
23
24  4. Products derived from this software may not be called "JDOM", nor
25     may "JDOM" appear in their name, without prior written permission
26     from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28  In addition, we request (but do not require) that you include in the
29  end-user documentation provided with the redistribution and/or in the
30  software itself an acknowledgement equivalent to the following:
31      "This product includes software developed by the
32       JDOM Project (http://www.jdom.org/)."
33  Alternatively, the acknowledgment may be graphical using the logos
34  available at http://www.jdom.org/images/logos.
35
36  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  SUCH DAMAGE.
48
49  This software consists of voluntary contributions made by many
50  individuals on behalf of the JDOM Project and was originally
51  created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52  Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53  on the JDOM Project, please see <http://www.jdom.org/>.
54
55  */

56
57 package org.jdom;
58
59 import java.io.*;
60 import java.util.*;
61
62 import org.jdom.filter.*;
63
64 /**
65  * An XML element. Methods allow the user to get and manipulate its child
66  * elements and content, directly access the element's textual content,
67  * manipulate its attributes, and manage namespaces.
68  *
69  * @version $Revision: 1.154 $, $Date: 2004/12/11 00:27:38 $
70  * @author Brett McLaughlin
71  * @author Jason Hunter
72  * @author Lucas Gonze
73  * @author Kevin Regan
74  * @author Dan Schaffer
75  * @author Yusuf Goolamabbas
76  * @author Kent C. Johnson
77  * @author Jools Enticknap
78  * @author Alex Rosen
79  * @author Bradley S. Huffman
80  */

81 public class Element extends Content implements Parent {
82
83     private static final String JavaDoc CVS_ID =
84     "@(#) $RCSfile: Element.java,v $ $Revision: 1.154 $ $Date: 2004/12/11 00:27:38 $ $Name: $";
85
86     private static final int INITIAL_ARRAY_SIZE = 5;
87
88     /** The local name of the element */
89     protected String JavaDoc name;
90
91     /** The namespace of the element */
92     protected transient Namespace namespace;
93
94     /** Additional namespace declarations to store on this element; useful
95      * during output */

96     protected transient List additionalNamespaces;
97
98     // See http://lists.denveronline.net/lists/jdom-interest/2000-September/003030.html
99
// for a possible memory optimization here (using a RootElement subclass)
100

101     /**
102      * The attributes of the element. Subclassers have to
103      * track attributes using their own mechanism.
104      */

105     AttributeList attributes = new AttributeList(this);
106
107     /**
108      * The content of the element. Subclassers have to
109      * track content using their own mechanism.
110      */

111     ContentList content = new ContentList(this);
112
113     /**
114      * This protected constructor is provided in order to support an Element
115      * subclass that wants full control over variable initialization. It
116      * intentionally leaves all instance variables null, allowing a lightweight
117      * subclass implementation. The subclass is responsible for ensuring all the
118      * get and set methods on Element behave as documented.
119      * <p>
120      * When implementing an Element subclass which doesn't require full control
121      * over variable initialization, be aware that simply calling super() (or
122      * letting the compiler add the implicit super() call) will not initialize
123      * the instance variables which will cause many of the methods to throw a
124      * NullPointerException. Therefore, the constructor for these subclasses
125      * should call one of the public constructors so variable initialization is
126      * handled automatically.
127      */

128     protected Element() { }
129
130     /**
131      * Creates a new element with the supplied (local) name and namespace. If
132      * the provided namespace is null, the element will have no namespace.
133      *
134      * @param name local name of the element
135      * @param namespace namespace for the element
136      * @throws IllegalNameException if the given name is illegal as an element
137      * name
138      */

139     public Element(String JavaDoc name, Namespace namespace) {
140         setName(name);
141         setNamespace(namespace);
142     }
143
144     /**
145      * Create a new element with the supplied (local) name and no namespace.
146      *
147      * @param name local name of the element
148      * @throws IllegalNameException if the given name is illegal as an element
149      * name.
150      */

151     public Element(String JavaDoc name) {
152         this(name, (Namespace) null);
153     }
154
155     /**
156      * Creates a new element with the supplied (local) name and a namespace
157      * given by a URI. The element will be put into the unprefixed (default)
158      * namespace.
159      *
160      * @param name name of the element
161      * @param uri namespace URI for the element
162      * @throws IllegalNameException if the given name is illegal as an element
163      * name or the given URI is illegal as a
164      * namespace URI
165      */

166     public Element(String JavaDoc name, String JavaDoc uri) {
167         this(name, Namespace.getNamespace("", uri));
168     }
169
170     /**
171      * Creates a new element with the supplied (local) name and a namespace
172      * given by the supplied prefix and URI combination.
173      *
174      * @param name local name of the element
175      * @param prefix namespace prefix
176      * @param uri namespace URI for the element
177      * @throws IllegalNameException if the given name is illegal as an element
178      * name, the given prefix is illegal as a
179      * namespace prefix, or the given URI is
180      * illegal as a namespace URI
181      */

182     public Element(String JavaDoc name, String JavaDoc prefix, String JavaDoc uri) {
183         this(name, Namespace.getNamespace(prefix, uri));
184     }
185
186     /**
187      * Returns the (local) name of the element (without any namespace prefix).
188      *
189      * @return local element name
190      */

191     public String JavaDoc getName() {
192         return name;
193     }
194
195     /**
196      * Sets the (local) name of the element.
197      *
198      * @param name the new (local) name of the element
199      * @return the target element
200      * @throws IllegalNameException if the given name is illegal as an Element
201      * name
202      */

203     public Element setName(String JavaDoc name) {
204         String JavaDoc reason = Verifier.checkElementName(name);
205         if (reason != null) {
206             throw new IllegalNameException(name, "element", reason);
207         }
208         this.name = name;
209         return this;
210     }
211
212     /**
213      * Returns the element's {@link Namespace}.
214      *
215      * @return the element's namespace
216      */

217     public Namespace getNamespace() {
218         return namespace;
219     }
220
221     /**
222      * Sets the element's {@link Namespace}. If the provided namespace is null,
223      * the element will have no namespace.
224      *
225      * @param namespace the new namespace
226      * @return the target element
227      */

228     public Element setNamespace(Namespace namespace) {
229         if (namespace == null) {
230             namespace = Namespace.NO_NAMESPACE;
231         }
232
233         this.namespace = namespace;
234         return this;
235     }
236
237     /**
238      * Returns the namespace prefix of the element or an empty string if none
239      * exists.
240      *
241      * @return the namespace prefix
242      */

243     public String JavaDoc getNamespacePrefix() {
244         return namespace.getPrefix();
245     }
246
247     /**
248      * Returns the namespace URI mapped to this element's prefix (or the
249      * in-scope default namespace URI if no prefix). If no mapping is found, an
250      * empty string is returned.
251      *
252      * @return the namespace URI for this element
253      */

254     public String JavaDoc getNamespaceURI() {
255         return namespace.getURI();
256     }
257
258     /**
259      * Returns the {@link Namespace} corresponding to the given prefix in scope
260      * for this element. This involves searching up the tree, so the results
261      * depend on the current location of the element. Returns null if there is
262      * no namespace in scope with the given prefix at this point in the
263      * document.
264      *
265      * @param prefix namespace prefix to look up
266      * @return the Namespace for this prefix at this
267      * location, or null if none
268      */

269     public Namespace getNamespace(String JavaDoc prefix) {
270         if (prefix == null) {
271             return null;
272         }
273
274         if (prefix.equals("xml")) {
275             // Namespace "xml" is always bound.
276
return Namespace.XML_NAMESPACE;
277         }
278
279         // Check if the prefix is the prefix for this element
280
if (prefix.equals(getNamespacePrefix())) {
281             return getNamespace();
282         }
283
284         // Scan the additional namespaces
285
if (additionalNamespaces != null) {
286             for (int i = 0; i < additionalNamespaces.size(); i++) {
287                 Namespace ns = (Namespace) additionalNamespaces.get(i);
288                 if (prefix.equals(ns.getPrefix())) {
289                     return ns;
290                 }
291             }
292         }
293
294         // If we still don't have a match, ask the parent
295
if (parent instanceof Element) {
296             return ((Element)parent).getNamespace(prefix);
297         }
298
299         return null;
300     }
301
302     /**
303      * Returns the full name of the element, in the form
304      * [namespacePrefix]:[localName]. If the element does not have a namespace
305      * prefix, then the local name is returned.
306      *
307      * @return qualified name of the element (including
308      * namespace prefix)
309      */

310     public String JavaDoc getQualifiedName() {
311         // Note: Any changes here should be reflected in
312
// XMLOutputter.printQualifiedName()
313
if (namespace.getPrefix().equals("")) {
314             return getName();
315         }
316
317         return new StringBuffer JavaDoc(namespace.getPrefix())
318             .append(':')
319             .append(name)
320             .toString();
321     }
322
323     /**
324      * Adds a namespace declarations to this element. This should <i>not</i> be
325      * used to add the declaration for this element itself; that should be
326      * assigned in the construction of the element. Instead, this is for adding
327      * namespace declarations on the element not relating directly to itself.
328      * It's used during output to for stylistic reasons move namespace
329      * declarations higher in the tree than they would have to be.
330      *
331      * @param additional namespace to add
332      * @throws IllegalAddException if the namespace prefix collides with another
333      * namespace prefix on the element
334      */

335     public void addNamespaceDeclaration(Namespace additional) {
336
337         // Verify the new namespace prefix doesn't collide with another
338
// declared namespace, an attribute prefix, or this element's prefix
339
String JavaDoc reason = Verifier.checkNamespaceCollision(additional, this);
340         if (reason != null) {
341             throw new IllegalAddException(this, additional, reason);
342         }
343
344         if (additionalNamespaces == null) {
345             additionalNamespaces = new ArrayList(INITIAL_ARRAY_SIZE);
346         }
347
348         additionalNamespaces.add(additional);
349     }
350
351     /**
352      * Removes an additional namespace declarations from this element. This
353      * should <i>not</i> be used to remove the declaration for this element
354      * itself; that should be handled in the construction of the element.
355      * Instead, this is for removing namespace declarations on the element not
356      * relating directly to itself. If the declaration is not present, this
357      * method does nothing.
358      *
359      * @param additionalNamespace namespace to remove
360      */

361     public void removeNamespaceDeclaration(Namespace additionalNamespace) {
362         if (additionalNamespaces == null) {
363             return;
364         }
365         additionalNamespaces.remove(additionalNamespace);
366     }
367
368     /**
369      * Returns a list of the additional namespace declarations on this element.
370      * This includes only additional namespace, not the namespace of the element
371      * itself, which can be obtained through {@link #getNamespace()}. If there
372      * are no additional declarations, this returns an empty list. Note, the
373      * returned list is unmodifiable.
374      *
375      * @return a List of the additional namespace
376      * declarations
377      */

378     public List getAdditionalNamespaces() {
379         // Not having the returned list be live allows us to avoid creating a
380
// new list object when XMLOutputter calls this method on an element
381
// with an empty list.
382
if (additionalNamespaces == null) {
383             return Collections.EMPTY_LIST;
384         }
385         return Collections.unmodifiableList(additionalNamespaces);
386     }
387
388     /**
389      * Returns the XPath 1.0 string value of this element, which is the
390      * complete, ordered content of all text node descendants of this element
391      * (i&#46;e&#46; the text that's left after all references are resolved
392      * and all other markup is stripped out.)
393      *
394      * @return a concatentation of all text node descendants
395      */

396     public String JavaDoc getValue() {
397         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
398
399         Iterator itr = getContent().iterator();
400         while (itr.hasNext()) {
401             Content child = (Content) itr.next();
402             if (child instanceof Element || child instanceof Text) {
403                 buffer.append(child.getValue());
404             }
405         }
406         return buffer.toString();
407     }
408
409     /**
410      * Returns whether this element is a root element. This can be used in
411      * tandem with {@link #getParent} to determine if an element has any
412      * "attachments" to a parent element or document.
413      *
414      * @return whether this is a root element
415      */

416     public boolean isRootElement() {
417         return parent instanceof Document;
418     }
419
420     public int getContentSize() {
421         return content.size();
422     }
423
424     public int indexOf(Content child) {
425         return content.indexOf(child);
426     }
427
428 // private int indexOf(int start, Filter filter) {
429
// int size = getContentSize();
430
// for (int i = start; i < size; i++) {
431
// if (filter.matches(getContent(i))) {
432
// return i;
433
// }
434
// }
435
// return -1;
436
// }
437

438
439     /**
440      * Returns the textual content directly held under this element as a string.
441      * This includes all text within this single element, including whitespace
442      * and CDATA sections if they exist. It's essentially the concatenation of
443      * all {@link Text} and {@link CDATA} nodes returned by {@link #getContent}.
444      * The call does not recurse into child elements. If no textual value exists
445      * for the element, an empty string is returned.
446      *
447      * @return text content for this element, or empty
448      * string if none
449      */

450     public String JavaDoc getText() {
451         if (content.size() == 0) {
452             return "";
453         }
454
455         // If we hold only a Text or CDATA, return it directly
456
if (content.size() == 1) {
457             Object JavaDoc obj = content.get(0);
458             if (obj instanceof Text) {
459                 return ((Text) obj).getText();
460             } else {
461                 return "";
462             }
463         }
464
465         // Else build String up
466
StringBuffer JavaDoc textContent = new StringBuffer JavaDoc();
467         boolean hasText = false;
468
469         for (int i = 0; i < content.size(); i++) {
470             Object JavaDoc obj = content.get(i);
471             if (obj instanceof Text) {
472                 textContent.append(((Text) obj).getText());
473                 hasText = true;
474             }
475         }
476
477         if (!hasText) {
478             return "";
479         }
480         else {
481             return textContent.toString();
482         }
483     }
484
485     /**
486      * Returns the textual content of this element with all surrounding
487      * whitespace removed. If no textual value exists for the element, or if
488      * only whitespace exists, the empty string is returned.
489      *
490      * @return trimmed text content for this element, or
491      * empty string if none
492      */

493     public String JavaDoc getTextTrim() {
494         return getText().trim();
495     }
496
497     /**
498      * Returns the textual content of this element with all surrounding
499      * whitespace removed and internal whitespace normalized to a single space.
500      * If no textual value exists for the element, or if only whitespace exists,
501      * the empty string is returned.
502      *
503      * @return normalized text content for this element, or
504      * empty string if none
505      */

506     public String JavaDoc getTextNormalize() {
507         return Text.normalizeString(getText());
508     }
509
510     /**
511      * Returns the textual content of the named child element, or null if
512      * there's no such child. This method is a convenience because calling
513      * <code>getChild().getText()</code> can throw a NullPointerException.
514      *
515      * @param name the name of the child
516      * @return text content for the named child, or null if
517      * no such child
518      */

519     public String JavaDoc getChildText(String JavaDoc name) {
520         Element child = getChild(name);
521         if (child == null) {
522             return null;
523         }
524         return child.getText();
525     }
526
527     /**
528      * Returns the trimmed textual content of the named child element, or null
529      * if there's no such child. See <code>{@link #getTextTrim()}</code> for
530      * details of text trimming.
531      *
532      * @param name the name of the child
533      * @return trimmed text content for the named child, or
534      * null if no such child
535      */

536     public String JavaDoc getChildTextTrim(String JavaDoc name) {
537         Element child = getChild(name);
538         if (child == null) {
539             return null;
540         }
541         return child.getTextTrim();
542     }
543
544     /**
545      * Returns the normalized textual content of the named child element, or
546      * null if there's no such child. See <code>{@link
547      * #getTextNormalize()}</code> for details of text normalizing.
548      *
549      * @param name the name of the child
550      * @return normalized text content for the named child,
551      * or null if no such child
552      */

553     public String JavaDoc getChildTextNormalize(String JavaDoc name) {
554         Element child = getChild(name);
555         if (child == null) {
556             return null;
557         }
558         return child.getTextNormalize();
559     }
560
561     /**
562      * Returns the textual content of the named child element, or null if
563      * there's no such child.
564      *
565      * @param name the name of the child
566      * @param ns the namespace of the child
567      * @return text content for the named child, or null if
568      * no such child
569      */

570     public String JavaDoc getChildText(String JavaDoc name, Namespace ns) {
571         Element child = getChild(name, ns);
572         if (child == null) {
573             return null;
574         }
575         return child.getText();
576     }
577
578     /**
579      * Returns the trimmed textual content of the named child element, or null
580      * if there's no such child.
581      *
582      * @param name the name of the child
583      * @param ns the namespace of the child
584      * @return trimmed text content for the named child, or
585      * null if no such child
586      */

587     public String JavaDoc getChildTextTrim(String JavaDoc name, Namespace ns) {
588         Element child = getChild(name, ns);
589         if (child == null) {
590             return null;
591         }
592         return child.getTextTrim();
593     }
594
595     /**
596      * Returns the normalized textual content of the named child element, or
597      * null if there's no such child.
598      *
599      * @param name the name of the child
600      * @param ns the namespace of the child
601      * @return normalized text content for the named child,
602      * or null if no such child
603      */

604     public String JavaDoc getChildTextNormalize(String JavaDoc name, Namespace ns) {
605         Element child = getChild(name, ns);
606         if (child == null) {
607             return null;
608         }
609         return child.getTextNormalize();
610     }
611
612     /**
613      * Sets the content of the element to be the text given. All existing text
614      * content and non-text context is removed. If this element should have both
615      * textual content and nested elements, use <code>{@link #setContent}</code>
616      * instead. Setting a null text value is equivalent to setting an empty
617      * string value.
618      *
619      * @param text new text content for the element
620      * @return the target element
621      * @throws IllegalDataException if the assigned text contains an illegal
622      * character such as a vertical tab (as
623      * determined by {@link
624      * org.jdom.Verifier#checkCharacterData})
625      */

626     public Element setText(String JavaDoc text) {
627         content.clear();
628
629         if (text != null) {
630             addContent(new Text(text));
631         }
632
633         return this;
634     }
635
636     /**
637      * This returns the full content of the element as a List which
638      * may contain objects of type <code>Text</code>, <code>Element</code>,
639      * <code>Comment</code>, <code>ProcessingInstruction</code>,
640      * <code>CDATA</code>, and <code>EntityRef</code>.
641      * The List returned is "live" in document order and modifications
642      * to it affect the element's actual contents. Whitespace content is
643      * returned in its entirety.
644      *
645      * <p>
646      * Sequential traversal through the List is best done with an Iterator
647      * since the underlying implement of List.size() may require walking the
648      * entire list.
649      * </p>
650      *
651      * @return a <code>List</code> containing the mixed content of the
652      * element: may contain <code>Text</code>,
653      * <code>{@link Element}</code>, <code>{@link Comment}</code>,
654      * <code>{@link ProcessingInstruction}</code>,
655      * <code>{@link CDATA}</code>, and
656      * <code>{@link EntityRef}</code> objects.
657      */

658     public List getContent() {
659         return content;
660     }
661
662     /**
663      * Return a filter view of this <code>Element</code>'s content.
664      *
665      * <p>
666      * Sequential traversal through the List is best done with a Iterator
667      * since the underlying implement of List.size() may require walking the
668      * entire list.
669      * </p>
670      *
671      * @param filter <code>Filter</code> to apply
672      * @return <code>List</code> - filtered Element content
673      */

674     public List getContent(Filter filter) {
675         return content.getView(filter);
676     }
677
678     /**
679      * Removes all child content from this parent.
680      *
681      * @return list of the old children detached from this parent
682      */

683     public List removeContent() {
684         List old = new ArrayList(content);
685         content.clear();
686         return old;
687     }
688
689     /**
690      * Remove all child content from this parent matching the supplied filter.
691      *
692      * @param filter filter to select which content to remove
693      * @return list of the old children detached from this parent
694      */

695     public List removeContent(Filter filter) {
696         List old = new ArrayList();
697         Iterator itr = content.getView(filter).iterator();
698         while (itr.hasNext()) {
699             Content child = (Content) itr.next();
700             old.add(child);
701             itr.remove();
702         }
703         return old;
704     }
705
706     /**
707      * This sets the content of the element. The supplied List should
708      * contain only objects of type <code>Element</code>, <code>Text</code>,
709      * <code>CDATA</code>, <code>Comment</code>,
710      * <code>ProcessingInstruction</code>, and <code>EntityRef</code>.
711      *
712      * <p>
713      * When all objects in the supplied List are legal and before the new
714      * content is added, all objects in the old content will have their
715      * parentage set to null (no parent) and the old content list will be
716      * cleared. This has the effect that any active list (previously obtained
717      * with a call to {@link #getContent} or {@link #getChildren}) will also
718      * change to reflect the new content. In addition, all objects in the
719      * supplied List will have their parentage set to this element, but the
720      * List itself will not be "live" and further removals and additions will
721      * have no effect on this elements content. If the user wants to continue
722      * working with a "live" list, then a call to setContent should be
723      * followed by a call to {@link #getContent} or {@link #getChildren} to
724      * obtain a "live" version of the content.
725      * </p>
726      *
727      * <p>
728      * Passing a null or empty List clears the existing content.
729      * </p>
730      *
731      * <p>
732      * In event of an exception the original content will be unchanged and
733      * the objects in the supplied content will be unaltered.
734      * </p>
735      *
736      * @param newContent <code>List</code> of content to set
737      * @return this element modified
738      * @throws IllegalAddException if the List contains objects of
739      * illegal types or with existing parentage.
740      */

741     public Element setContent(Collection newContent) {
742         content.clearAndSet(newContent);
743         return this;
744     }
745
746     /**
747      * Replace the current child the given index with the supplied child.
748      * <p>
749      * In event of an exception the original content will be unchanged and
750      * the supplied child will be unaltered.
751      * </p>
752      *
753      * @param index - index of child to replace.
754      * @param child - child to add.
755      * @return element on which this method was invoked
756      * @throws IllegalAddException if the supplied child is already attached
757      * or not legal content for this parent.
758      * @throws IndexOutOfBoundsException if index is negative or greater
759      * than the current number of children.
760      */

761     public Element setContent(int index, Content child) {
762         content.set(index, child);
763         return this;
764     }
765
766     /**
767      * Replace the child at the given index whith the supplied
768      * collection.
769      * <p>
770      * In event of an exception the original content will be unchanged and
771      * the content in the supplied collection will be unaltered.
772      * </p>
773      *
774      * @param index - index of child to replace.
775      * @param collection - collection of content to add.
776      * @return object on which this method was invoked
777      * @throws IllegalAddException if the collection contains objects of
778      * illegal types.
779      * @throws IndexOutOfBoundsException if index is negative or greater
780      * than the current number of children.
781      */

782     public Element setContent(int index, Collection collection) {
783         content.remove(index);
784         content.addAll(index, collection);
785         return this;
786     }
787
788     /**
789      * This adds text content to this element. It does not replace the
790      * existing content as does <code>setText()</code>.
791      *
792      * @param str <code>String</code> to add
793      * @return this element modified
794      * @throws IllegalDataException if <code>str</code> contains an
795      * illegal character such as a vertical tab (as determined
796      * by {@link org.jdom.Verifier#checkCharacterData})
797      */

798     public Element addContent(String JavaDoc str) {
799         return addContent(new Text(str));
800     }
801
802     /**
803      * Appends the child to the end of the element's content list.
804      *
805      * @param child child to append to end of content list
806      * @return the element on which the method was called
807      * @throws IllegalAddException if the given child already has a parent. */

808     public Element addContent(Content child) {
809         content.add(child);
810         return this;
811     }
812
813     /**
814      * Appends all children in the given collection to the end of
815      * the content list. In event of an exception during add the
816      * original content will be unchanged and the objects in the supplied
817      * collection will be unaltered.
818      *
819      * @param collection collection to append
820      * @return the element on which the method was called
821      * @throws IllegalAddException if any item in the collection
822      * already has a parent or is of an inappropriate type.
823      */

824     public Element addContent(Collection collection) {
825         content.addAll(collection);
826         return this;
827     }
828
829     /**
830      * Inserts the child into the content list at the given index.
831      *
832      * @param index location for adding the collection
833      * @param child child to insert
834      * @return the parent on which the method was called
835      * @throws IndexOutOfBoundsException if index is negative or beyond
836      * the current number of children
837      * @throws IllegalAddException if the given child already has a parent.
838      */

839     public Element addContent(int index, Content child) {
840         content.add(index, child);
841         return this;
842     }
843
844     /**
845      * Inserts the content in a collection into the content list
846      * at the given index. In event of an exception the original content
847      * will be unchanged and the objects in the supplied collection will be
848      * unaltered.
849      *
850      * @param index location for adding the collection
851      * @param c collection to insert
852      * @return the parent on which the method was called
853      * @throws IndexOutOfBoundsException if index is negative or beyond
854      * the current number of children
855      * @throws IllegalAddException if any item in the collection
856      * already has a parent or is of an inappropriate type.
857      */

858     public Element addContent(int index, Collection c) {
859         content.addAll(index, c);
860         return this;
861     }
862
863     public List cloneContent() {
864         int size = getContentSize();
865         List list = new ArrayList(size);
866         for (int i = 0; i < size; i++) {
867             Content child = getContent(i);
868             list.add(child.clone());
869         }
870         return list;
871     }
872
873     public Content getContent(int index) {
874         return (Content) content.get(index);
875     }
876
877 // public Content getChild(Filter filter) {
878
// int i = indexOf(0, filter);
879
// return (i < 0) ? null : getContent(i);
880
// }
881

882     public boolean removeContent(Content child) {
883         return content.remove(child);
884     }
885
886     public Content removeContent(int index) {
887         return (Content) content.remove(index);
888     }
889
890     /**
891      * Set this element's content to be the supplied child.
892      * <p>
893      * If the supplied child is legal content for this parent and before
894      * it is added, all content in the current content list will
895      * be cleared and all current children will have their parentage set to
896      * null.
897      * <p>
898      * This has the effect that any active list (previously obtained with
899      * a call to one of the {@link #getContent} methods will also change
900      * to reflect the new content. In addition, all content in the supplied
901      * collection will have their parentage set to this parent. If the user
902      * wants to continue working with a <b>"live"</b> list of this parent's
903      * child, then a call to setContent should be followed by a call to one
904      * of the {@link #getContent} methods to obtain a <b>"live"</b>
905      * version of the children.
906      * <p>
907      * Passing a null child clears the existing content.
908      * <p>
909      * In event of an exception the original content will be unchanged and
910      * the supplied child will be unaltered.
911      *
912      * @param child new content to replace existing content
913      * @return the parent on which the method was called
914      * @throws IllegalAddException if the supplied child is already attached
915      * or not legal content for an Element
916      */

917     public Element setContent(Content child) {
918         content.clear();
919         content.add(child);
920         return this;
921     }
922
923
924     /**
925      * Determines if this element is the ancestor of another element.
926      *
927      * @param element <code>Element</code> to check against
928      * @return <code>true</code> if this element is the ancestor of the
929      * supplied element
930      */

931     public boolean isAncestor(Element element) {
932         Object JavaDoc p = element.getParent();
933         while (p instanceof Element) {
934             if (p == this) {
935                 return true;
936             }
937             p = ((Element) p).getParent();
938         }
939         return false;
940     }
941
942     /**
943      * <p>
944      * This returns the complete set of attributes for this element, as a
945      * <code>List</code> of <code>Attribute</code> objects in no particular
946      * order, or an empty list if there are none.
947      * The returned list is "live" and changes to it affect the
948      * element's actual attributes.
949      * </p>
950      *
951      * @return attributes for the element
952      */

953     public List getAttributes() {
954         return attributes;
955     }
956
957     /**
958      * <p>
959      * This returns the attribute for this element with the given name
960      * and within no namespace, or null if no such attribute exists.
961      * </p>
962      *
963      * @param name name of the attribute to return
964      * @return attribute for the element
965      */

966     public Attribute getAttribute(String JavaDoc name) {
967         return getAttribute(name, Namespace.NO_NAMESPACE);
968     }
969
970     /**
971      * <p>
972      * This returns the attribute for this element with the given name
973      * and within the given Namespace, or null if no such attribute exists.
974      * </p>
975      *
976      * @param name name of the attribute to return
977      * @param ns <code>Namespace</code> to search within
978      * @return attribute for the element
979      */

980     public Attribute getAttribute(String JavaDoc name, Namespace ns) {
981         return (Attribute) attributes.get(name, ns);
982     }
983
984     /**
985      * <p>
986      * This returns the attribute value for the attribute with the given name
987      * and within no namespace, null if there is no such attribute, and the
988      * empty string if the attribute value is empty.
989      * </p>
990      *
991      * @param name name of the attribute whose value to be returned
992      * @return the named attribute's value, or null if no such attribute
993      */

994     public String JavaDoc getAttributeValue(String JavaDoc name) {
995         return getAttributeValue(name, Namespace.NO_NAMESPACE);
996     }
997
998     /**
999      * <p>
1000     * This returns the attribute value for the attribute with the given name
1001     * and within no namespace, or the passed-in default if there is no
1002     * such attribute.
1003     * </p>
1004     *
1005     * @param name name of the attribute whose value to be returned
1006     * @param def a default value to return if the attribute does not exist
1007     * @return the named attribute's value, or the default if no such attribute
1008     */

1009    public String JavaDoc getAttributeValue(String JavaDoc name, String JavaDoc def) {
1010        return getAttributeValue(name, Namespace.NO_NAMESPACE, def);
1011    }
1012
1013    /**
1014     * <p>
1015     * This returns the attribute value for the attribute with the given name
1016     * and within the given Namespace, null if there is no such attribute, and
1017     * the empty string if the attribute value is empty.
1018     * </p>
1019     *
1020     * @param name name of the attribute whose valud is to be returned
1021     * @param ns <code>Namespace</code> to search within
1022     * @return the named attribute's value, or null if no such attribute
1023     */

1024    public String JavaDoc getAttributeValue(String JavaDoc name, Namespace ns) {
1025        return getAttributeValue(name, ns, null);
1026    }
1027
1028    /**
1029     * <p>
1030     * This returns the attribute value for the attribute with the given name
1031     * and within the given Namespace, or the passed-in default if there is no
1032     * such attribute.
1033     * </p>
1034     *
1035     * @param name name of the attribute whose valud is to be returned
1036     * @param ns <code>Namespace</code> to search within
1037     * @param def a default value to return if the attribute does not exist
1038     * @return the named attribute's value, or the default if no such attribute
1039     */

1040    public String JavaDoc getAttributeValue(String JavaDoc name, Namespace ns, String JavaDoc def) {
1041        Attribute attribute = (Attribute) attributes.get(name, ns);
1042        return (attribute == null) ? def : attribute.getValue();
1043    }
1044
1045    /**
1046     * <p>
1047     * This sets the attributes of the element. The supplied List should
1048     * contain only objects of type <code>Attribute</code>.
1049     * </p>
1050     *
1051     * <p>
1052     * When all objects in the supplied List are legal and before the new
1053     * attributes are added, all old attributes will have their
1054     * parentage set to null (no parent) and the old attribute list will be
1055     * cleared. This has the effect that any active attribute list (previously
1056     * obtained with a call to {@link #getAttributes}) will also change to
1057     * reflect the new attributes. In addition, all attributes in the supplied
1058     * List will have their parentage set to this element, but the List itself
1059     * will not be "live" and further removals and additions will have no
1060     * effect on this elements attributes. If the user wants to continue
1061     * working with a "live" attribute list, then a call to setAttributes
1062     * should be followed by a call to {@link #getAttributes} to obtain a
1063     * "live" version of the attributes.
1064     * </p>
1065     *
1066     * <p>
1067     * Passing a null or empty List clears the existing attributes.
1068     * </p>
1069     *
1070     * <p>
1071     * In cases where the List contains duplicate attributes, only the last
1072     * one will be retained. This has the same effect as calling
1073     * {@link #setAttribute(Attribute)} sequentially.
1074     * </p>
1075     *
1076     * <p>
1077     * In event of an exception the original attributes will be unchanged and
1078     * the attributes in the supplied attributes will be unaltered.
1079     * </p>
1080     *
1081     * @param newAttributes <code>List</code> of attributes to set
1082     * @return this element modified
1083     * @throws IllegalAddException if the List contains objects
1084     * that are not instances of <code>Attribute</code>,
1085     * or if any of the <code>Attribute</code> objects have
1086     * conflicting namespace prefixes.
1087     */

1088    public Element setAttributes(List newAttributes) {
1089        attributes.clearAndSet(newAttributes);
1090        return this;
1091    }
1092
1093    /**
1094     * <p>
1095     * This sets an attribute value for this element. Any existing attribute
1096     * with the same name and namespace URI is removed.
1097     * </p>
1098     *
1099     * @param name name of the attribute to set
1100     * @param value value of the attribute to set
1101     * @return this element modified
1102     * @throws IllegalNameException if the given name is illegal as an
1103     * attribute name.
1104     * @throws IllegalDataException if the given attribute value is
1105     * illegal character data (as determined by
1106     * {@link org.jdom.Verifier#checkCharacterData}).
1107     */

1108    public Element setAttribute(String JavaDoc name, String JavaDoc value) {
1109        return setAttribute(new Attribute(name, value));
1110    }
1111
1112    /**
1113     * <p>
1114     * This sets an attribute value for this element. Any existing attribute
1115     * with the same name and namespace URI is removed.
1116     * </p>
1117     *
1118     * @param name name of the attribute to set
1119     * @param value value of the attribute to set
1120     * @param ns namespace of the attribute to set
1121     * @return this element modified
1122     * @throws IllegalNameException if the given name is illegal as an
1123     * attribute name, or if the namespace is an unprefixed default
1124     * namespace
1125     * @throws IllegalDataException if the given attribute value is
1126     * illegal character data (as determined by
1127     * {@link org.jdom.Verifier#checkCharacterData}).
1128     * @throws IllegalAddException if the attribute namespace prefix
1129     * collides with another namespace prefix on the element.
1130     */

1131    public Element setAttribute(String JavaDoc name, String JavaDoc value, Namespace ns) {
1132        return setAttribute(new Attribute(name, value, ns));
1133    }
1134
1135    /**
1136     * <p>
1137     * This sets an attribute value for this element. Any existing attribute
1138     * with the same name and namespace URI is removed.
1139     * </p>
1140     *
1141     * @param attribute <code>Attribute</code> to set
1142     * @return this element modified
1143     * @throws IllegalAddException if the attribute being added already has a
1144     * parent or if the attribute namespace prefix collides with another
1145     * namespace prefix on the element.
1146     */

1147    public Element setAttribute(Attribute attribute) {
1148        attributes.add(attribute);
1149        return this;
1150    }
1151
1152    /**
1153     * <p>
1154     * This removes the attribute with the given name and within no
1155     * namespace. If no such attribute exists, this method does nothing.
1156     * </p>
1157     *
1158     * @param name name of attribute to remove
1159     * @return whether the attribute was removed
1160     */

1161    public boolean removeAttribute(String JavaDoc name) {
1162        return removeAttribute(name, Namespace.NO_NAMESPACE);
1163    }
1164
1165    /**
1166     * <p>
1167     * This removes the attribute with the given name and within the
1168     * given Namespace. If no such attribute exists, this method does
1169     * nothing.
1170     * </p>
1171     *
1172     * @param name name of attribute to remove
1173     * @param ns namespace URI of attribute to remove
1174     * @return whether the attribute was removed
1175     */

1176    public boolean removeAttribute(String JavaDoc name, Namespace ns) {
1177        return attributes.remove(name, ns);
1178    }
1179
1180    /**
1181     * <p>
1182     * This removes the supplied Attribute should it exist.
1183     * </p>
1184     *
1185     * @param attribute Reference to the attribute to be removed.
1186     * @return whether the attribute was removed
1187     */

1188    public boolean removeAttribute(Attribute attribute) {
1189        return attributes.remove(attribute);
1190    }
1191
1192    /**
1193     * <p>
1194     * This returns a <code>String</code> representation of the
1195     * <code>Element</code>, suitable for debugging. If the XML
1196     * representation of the <code>Element</code> is desired,
1197     * {@link org.jdom.output.XMLOutputter#outputString(Element)}
1198     * should be used.
1199     * </p>
1200     *
1201     * @return <code>String</code> - information about the
1202     * <code>Element</code>
1203     */

1204    public String JavaDoc toString() {
1205        StringBuffer JavaDoc stringForm = new StringBuffer JavaDoc(64)
1206            .append("[Element: <")
1207            .append(getQualifiedName());
1208
1209        String JavaDoc nsuri = getNamespaceURI();
1210        if (!nsuri.equals("")) {
1211            stringForm
1212            .append(" [Namespace: ")
1213            .append(nsuri)
1214            .append("]");
1215        }
1216        stringForm.append("/>]");
1217
1218        return stringForm.toString();
1219    }
1220
1221    /**
1222     * <p>
1223     * This returns a deep clone of this element.
1224     * The new element is detached from its parent, and getParent()
1225     * on the clone will return null.
1226     * </p>
1227     *
1228     * @return the clone of this element
1229     */

1230   public Object JavaDoc clone() {
1231
1232       // Ken Rune Helland <kenh@csc.no> is our local clone() guru
1233

1234       final Element element = (Element) super.clone();
1235
1236       // name and namespace are references to immutable objects
1237
// so super.clone() handles them ok
1238

1239       // Reference to parent is copied by super.clone()
1240
// (Object.clone()) so we have to remove it
1241
// Actually, super is a Content, which has already detached in the
1242
// clone().
1243
// element.parent = null;
1244

1245       // Reference to content list and attribute lists are copyed by
1246
// super.clone() so we set it new lists if the original had lists
1247
element.content = new ContentList(element);
1248       element.attributes = new AttributeList(element);
1249
1250       // Cloning attributes
1251
if (attributes != null) {
1252           for(int i = 0; i < attributes.size(); i++) {
1253               final Attribute attribute = (Attribute) attributes.get(i);
1254               element.attributes.add(attribute.clone());
1255           }
1256       }
1257
1258       // Cloning additional namespaces
1259
if (additionalNamespaces != null) {
1260           element.additionalNamespaces = new ArrayList(additionalNamespaces);
1261       }
1262
1263       // Cloning content
1264
if (content != null) {
1265           for(int i = 0; i < content.size(); i++) {
1266               final Content c = (Content) content.get(i);
1267               element.content.add(c.clone());
1268           }
1269       }
1270
1271       return element;
1272   }
1273
1274
1275    // Support a custom Namespace serialization so no two namespace
1276
// object instances may exist for the same prefix/uri pair
1277
private void writeObject(ObjectOutputStream out) throws IOException {
1278
1279        out.defaultWriteObject();
1280
1281        // We use writeObject() and not writeUTF() to minimize space
1282
// This allows for writing pointers to already written strings
1283
out.writeObject(namespace.getPrefix());
1284        out.writeObject(namespace.getURI());
1285
1286        if (additionalNamespaces == null) {
1287            out.write(0);
1288        }
1289        else {
1290            int size = additionalNamespaces.size();
1291            out.write(size);
1292            for (int i = 0; i < size; i++) {
1293                Namespace additional = (Namespace) additionalNamespaces.get(i);
1294                out.writeObject(additional.getPrefix());
1295                out.writeObject(additional.getURI());
1296            }
1297        }
1298    }
1299
1300    private void readObject(ObjectInputStream in)
1301        throws IOException, ClassNotFoundException JavaDoc {
1302
1303        in.defaultReadObject();
1304
1305        namespace = Namespace.getNamespace(
1306            (String JavaDoc)in.readObject(), (String JavaDoc)in.readObject());
1307
1308        int size = in.read();
1309
1310        if (size != 0) {
1311            additionalNamespaces = new ArrayList(size);
1312            for (int i = 0; i < size; i++) {
1313                Namespace additional = Namespace.getNamespace(
1314                    (String JavaDoc)in.readObject(), (String JavaDoc)in.readObject());
1315                additionalNamespaces.add(additional);
1316            }
1317        }
1318    }
1319
1320    /**
1321     * Returns an iterator that walks over all descendants in document order.
1322     *
1323     * @return an iterator to walk descendants
1324     */

1325    public Iterator getDescendants() {
1326        return new DescendantIterator(this);
1327    }
1328
1329    /**
1330     * Returns an iterator that walks over all descendants in document order
1331     * applying the Filter to return only elements that match the filter rule.
1332     * With filters you can match only Elements, only Comments, Elements or
1333     * Comments, only Elements with a given name and/or prefix, and so on.
1334     *
1335     * @param filter filter to select which descendants to see
1336     * @return an iterator to walk descendants within a filter
1337     */

1338    public Iterator getDescendants(Filter filter) {
1339        return new FilterIterator(new DescendantIterator(this), filter);
1340    }
1341
1342
1343
1344    /**
1345     * This returns a <code>List</code> of all the child elements
1346     * nested directly (one level deep) within this element, as
1347     * <code>Element</code> objects. If this target element has no nested
1348     * elements, an empty List is returned. The returned list is "live"
1349     * in document order and changes to it affect the element's actual
1350     * contents.
1351     *
1352     * <p>
1353     * Sequential traversal through the List is best done with a Iterator
1354     * since the underlying implement of List.size() may not be the most
1355     * efficient.
1356     * </p>
1357     *
1358     * <p>
1359     * No recursion is performed, so elements nested two levels deep
1360     * would have to be obtained with:
1361     * <pre>
1362     * <code>
1363     * Iterator itr = (currentElement.getChildren()).iterator();
1364     * while(itr.hasNext()) {
1365     * Element oneLevelDeep = (Element)itr.next();
1366     * List twoLevelsDeep = oneLevelDeep.getChildren();
1367     * // Do something with these children
1368     * }
1369     * </code>
1370     * </pre>
1371     * </p>
1372     *
1373     * @return list of child <code>Element</code> objects for this element
1374     */

1375    public List getChildren() {
1376        return content.getView(new ElementFilter());
1377    }
1378
1379    /**
1380     * This returns a <code>List</code> of all the child elements
1381     * nested directly (one level deep) within this element with the given
1382     * local name and belonging to no namespace, returned as
1383     * <code>Element</code> objects. If this target element has no nested
1384     * elements with the given name outside a namespace, an empty List
1385     * is returned. The returned list is "live" in document order
1386     * and changes to it affect the element's actual contents.
1387     * <p>
1388     * Please see the notes for <code>{@link #getChildren}</code>
1389     * for a code example.
1390     * </p>
1391     *
1392     * @param name local name for the children to match
1393     * @return all matching child elements
1394     */

1395    public List getChildren(String JavaDoc name) {
1396        return getChildren(name, Namespace.NO_NAMESPACE);
1397    }
1398
1399    /**
1400     * This returns a <code>List</code> of all the child elements
1401     * nested directly (one level deep) within this element with the given
1402     * local name and belonging to the given Namespace, returned as
1403     * <code>Element</code> objects. If this target element has no nested
1404     * elements with the given name in the given Namespace, an empty List
1405     * is returned. The returned list is "live" in document order
1406     * and changes to it affect the element's actual contents.
1407     * <p>
1408     * Please see the notes for <code>{@link #getChildren}</code>
1409     * for a code example.
1410     * </p>
1411     *
1412     * @param name local name for the children to match
1413     * @param ns <code>Namespace</code> to search within
1414     * @return all matching child elements
1415     */

1416    public List getChildren(String JavaDoc name, Namespace ns) {
1417        return content.getView(new ElementFilter(name, ns));
1418    }
1419
1420    /**
1421     * This returns the first child element within this element with the
1422     * given local name and belonging to the given namespace.
1423     * If no elements exist for the specified name and namespace, null is
1424     * returned.
1425     *
1426     * @param name local name of child element to match
1427     * @param ns <code>Namespace</code> to search within
1428     * @return the first matching child element, or null if not found
1429     */

1430    public Element getChild(String JavaDoc name, Namespace ns) {
1431        List elements = content.getView(new ElementFilter(name, ns));
1432        Iterator i = elements.iterator();
1433        if (i.hasNext()) {
1434            return (Element) i.next();
1435        }
1436        return null;
1437    }
1438
1439    /**
1440     * This returns the first child element within this element with the
1441     * given local name and belonging to no namespace.
1442     * If no elements exist for the specified name and namespace, null is
1443     * returned.
1444     *
1445     * @param name local name of child element to match
1446     * @return the first matching child element, or null if not found
1447     */

1448    public Element getChild(String JavaDoc name) {
1449        return getChild(name, Namespace.NO_NAMESPACE);
1450    }
1451
1452    /**
1453     * <p>
1454     * This removes the first child element (one level deep) with the
1455     * given local name and belonging to no namespace.
1456     * Returns true if a child was removed.
1457     * </p>
1458     *
1459     * @param name the name of child elements to remove
1460     * @return whether deletion occurred
1461     */

1462    public boolean removeChild(String JavaDoc name) {
1463        return removeChild(name, Namespace.NO_NAMESPACE);
1464    }
1465
1466    /**
1467     * <p>
1468     * This removes the first child element (one level deep) with the
1469     * given local name and belonging to the given namespace.
1470     * Returns true if a child was removed.
1471     * </p>
1472     *
1473     * @param name the name of child element to remove
1474     * @param ns <code>Namespace</code> to search within
1475     * @return whether deletion occurred
1476     */

1477    public boolean removeChild(String JavaDoc name, Namespace ns) {
1478        List old = content.getView(new ElementFilter(name, ns));
1479        Iterator i = old.iterator();
1480        if (i.hasNext()) {
1481            i.next();
1482            i.remove();
1483            return true;
1484        }
1485
1486        return false;
1487    }
1488
1489    /**
1490     * <p>
1491     * This removes all child elements (one level deep) with the
1492     * given local name and belonging to no namespace.
1493     * Returns true if any were removed.
1494     * </p>
1495     *
1496     * @param name the name of child elements to remove
1497     * @return whether deletion occurred
1498     */

1499    public boolean removeChildren(String JavaDoc name) {
1500        return removeChildren(name, Namespace.NO_NAMESPACE);
1501    }
1502
1503    /**
1504     * <p>
1505     * This removes all child elements (one level deep) with the
1506     * given local name and belonging to the given namespace.
1507     * Returns true if any were removed.
1508     * </p>
1509     *
1510     * @param name the name of child elements to remove
1511     * @param ns <code>Namespace</code> to search within
1512     * @return whether deletion occurred
1513     */

1514    public boolean removeChildren(String JavaDoc name, Namespace ns) {
1515        boolean deletedSome = false;
1516
1517        List old = content.getView(new ElementFilter(name, ns));
1518        Iterator i = old.iterator();
1519        while (i.hasNext()) {
1520            i.next();
1521            i.remove();
1522            deletedSome = true;
1523        }
1524
1525        return deletedSome;
1526    }
1527
1528}
1529
Popular Tags