Source code: org/jdom/Element.java
1 /*--
2
3 $Id: Element.java,v 1.152 2004/09/03 06:35:39 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.152 $, $Date: 2004/09/03 06:35:39 $
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 CVS_ID =
84 "@(#) $RCSfile: Element.java,v $ $Revision: 1.152 $ $Date: 2004/09/03 06:35:39 $ $Name: jdom_1_0 $";
85
86 private static final int INITIAL_ARRAY_SIZE = 5;
87
88 /** The local name of the element */
89 protected String 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 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 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 name, String 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 name, String prefix, String 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 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 name) {
204 String 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 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 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 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 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(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 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.e. 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 getValue() {
397 StringBuffer buffer = new StringBuffer();
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 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 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 textContent = new StringBuffer();
467 boolean hasText = false;
468
469 for (int i = 0; i < content.size(); i++) {
470 Object 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 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 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 getChildText(String 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 getChildTextTrim(String 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 getChildTextNormalize(String 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 getChildText(String 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 getChildTextTrim(String 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 getChildTextNormalize(String 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 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 Parent 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 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 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 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 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 getAttributeValue(String 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 getAttributeValue(String name, String 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 getAttributeValue(String 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 getAttributeValue(String name, Namespace ns, String 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 name, String 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 name, String 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 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 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 toString() {
1205 StringBuffer stringForm = new StringBuffer(64)
1206 .append("[Element: <")
1207 .append(getQualifiedName());
1208
1209 String 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 clone() {
1231
1232 // Ken Rune Helland <kenh@csc.no> is our local clone() guru
1233
1234 Element element = null;
1235
1236 element = (Element) super.clone();
1237
1238 // name and namespace are references to immutable objects
1239 // so super.clone() handles them ok
1240
1241 // Reference to parent is copied by super.clone()
1242 // (Object.clone()) so we have to remove it
1243 // Actually, super is a Content, which has already detached in the clone().
1244 // element.parent = null;
1245
1246 // Reference to content list and attribute lists are copyed by
1247 // super.clone() so we set it new lists if the original had lists
1248 element.content = new ContentList(element);
1249 element.attributes = new AttributeList(element);
1250
1251 // Cloning attributes
1252 if (attributes != null) {
1253 for (int i = 0; i < attributes.size(); i++) {
1254 Object obj = attributes.get(i);
1255 Attribute attribute = (Attribute)((Attribute)obj).clone();
1256 element.attributes.add(attribute);
1257 }
1258 }
1259
1260 // Cloning additional namespaces
1261 if (additionalNamespaces != null) {
1262 int additionalSize = additionalNamespaces.size();
1263 element.additionalNamespaces = new ArrayList(additionalSize);
1264 for (int i = 0; i < additionalSize; i++) {
1265 Object additional = additionalNamespaces.get(i);
1266 element.additionalNamespaces.add(additional);
1267 }
1268 }
1269
1270 // Cloning content
1271 if (content != null) {
1272 for (int i = 0; i < content.size(); i++) {
1273 Object obj = content.get(i);
1274 if (obj instanceof Element) {
1275 Element elt = (Element)((Element)obj).clone();
1276 element.content.add(elt);
1277 } else if (obj instanceof CDATA) {
1278 CDATA cdata = (CDATA)((CDATA)obj).clone();
1279 element.content.add(cdata);
1280 } else if (obj instanceof Text) {
1281 Text text = (Text)((Text)obj).clone();
1282 element.content.add(text);
1283 } else if (obj instanceof Comment) {
1284 Comment comment = (Comment)((Comment)obj).clone();
1285 element.content.add(comment);
1286 } else if (obj instanceof ProcessingInstruction) {
1287 ProcessingInstruction pi = (ProcessingInstruction)
1288 ((ProcessingInstruction)obj).clone();
1289 element.content.add(pi);
1290 } else if (obj instanceof EntityRef) {
1291 EntityRef entity = (EntityRef)((EntityRef)obj).clone();
1292 element.content.add(entity);
1293 }
1294 }
1295 }
1296
1297 // Handle additional namespaces
1298 if (additionalNamespaces != null) {
1299 // Avoid additionalNamespaces.clone() because List isn't Cloneable
1300 element.additionalNamespaces = new ArrayList();
1301 element.additionalNamespaces.addAll(additionalNamespaces);
1302 }
1303
1304 return element;
1305 }
1306
1307 // Support a custom Namespace serialization so no two namespace
1308 // object instances may exist for the same prefix/uri pair
1309 private void writeObject(ObjectOutputStream out) throws IOException {
1310
1311 out.defaultWriteObject();
1312
1313 // We use writeObject() and not writeUTF() to minimize space
1314 // This allows for writing pointers to already written strings
1315 out.writeObject(namespace.getPrefix());
1316 out.writeObject(namespace.getURI());
1317
1318 if (additionalNamespaces == null) {
1319 out.write(0);
1320 }
1321 else {
1322 int size = additionalNamespaces.size();
1323 out.write(size);
1324 for (int i = 0; i < size; i++) {
1325 Namespace additional = (Namespace) additionalNamespaces.get(i);
1326 out.writeObject(additional.getPrefix());
1327 out.writeObject(additional.getURI());
1328 }
1329 }
1330 }
1331
1332 private void readObject(ObjectInputStream in)
1333 throws IOException, ClassNotFoundException {
1334
1335 in.defaultReadObject();
1336
1337 namespace = Namespace.getNamespace(
1338 (String)in.readObject(), (String)in.readObject());
1339
1340 int size = in.read();
1341
1342 if (size != 0) {
1343 additionalNamespaces = new ArrayList(size);
1344 for (int i = 0; i < size; i++) {
1345 Namespace additional = Namespace.getNamespace(
1346 (String)in.readObject(), (String)in.readObject());
1347 additionalNamespaces.add(additional);
1348 }
1349 }
1350 }
1351
1352 /**
1353 * Returns an iterator that walks over all descendants in document order.
1354 *
1355 * @return an iterator to walk descendants
1356 */
1357 public Iterator getDescendants() {
1358 return new DescendantIterator(this);
1359 }
1360
1361 /**
1362 * Returns an iterator that walks over all descendants in document order
1363 * applying the Filter to return only elements that match the filter rule.
1364 * With filters you can match only Elements, only Comments, Elements or
1365 * Comments, only Elements with a given name and/or prefix, and so on.
1366 *
1367 * @param filter filter to select which descendants to see
1368 * @return an iterator to walk descendants within a filter
1369 */
1370 public Iterator getDescendants(Filter filter) {
1371 return new FilterIterator(new DescendantIterator(this), filter);
1372 }
1373
1374
1375
1376 /**
1377 * This returns a <code>List</code> of all the child elements
1378 * nested directly (one level deep) within this element, as
1379 * <code>Element</code> objects. If this target element has no nested
1380 * elements, an empty List is returned. The returned list is "live"
1381 * in document order and changes to it affect the element's actual
1382 * contents.
1383 *
1384 * <p>
1385 * Sequential traversal through the List is best done with a Iterator
1386 * since the underlying implement of List.size() may not be the most
1387 * efficient.
1388 * </p>
1389 *
1390 * <p>
1391 * No recursion is performed, so elements nested two levels deep
1392 * would have to be obtained with:
1393 * <pre>
1394 * <code>
1395 * Iterator itr = (currentElement.getChildren()).iterator();
1396 * while(itr.hasNext()) {
1397 * Element oneLevelDeep = (Element)itr.next();
1398 * List twoLevelsDeep = oneLevelDeep.getChildren();
1399 * // Do something with these children
1400 * }
1401 * </code>
1402 * </pre>
1403 * </p>
1404 *
1405 * @return list of child <code>Element</code> objects for this element
1406 */
1407 public List getChildren() {
1408 return content.getView(new ElementFilter());
1409 }
1410
1411 /**
1412 * This returns a <code>List</code> of all the child elements
1413 * nested directly (one level deep) within this element with the given
1414 * local name and belonging to no namespace, returned as
1415 * <code>Element</code> objects. If this target element has no nested
1416 * elements with the given name outside a namespace, an empty List
1417 * is returned. The returned list is "live" in document order
1418 * and changes to it affect the element's actual contents.
1419 * <p>
1420 * Please see the notes for <code>{@link #getChildren}</code>
1421 * for a code example.
1422 * </p>
1423 *
1424 * @param name local name for the children to match
1425 * @return all matching child elements
1426 */
1427 public List getChildren(String name) {
1428 return getChildren(name, Namespace.NO_NAMESPACE);
1429 }
1430
1431 /**
1432 * This returns a <code>List</code> of all the child elements
1433 * nested directly (one level deep) within this element with the given
1434 * local name and belonging to the given Namespace, returned as
1435 * <code>Element</code> objects. If this target element has no nested
1436 * elements with the given name in the given Namespace, an empty List
1437 * is returned. The returned list is "live" in document order
1438 * and changes to it affect the element's actual contents.
1439 * <p>
1440 * Please see the notes for <code>{@link #getChildren}</code>
1441 * for a code example.
1442 * </p>
1443 *
1444 * @param name local name for the children to match
1445 * @param ns <code>Namespace</code> to search within
1446 * @return all matching child elements
1447 */
1448 public List getChildren(String name, Namespace ns) {
1449 return content.getView(new ElementFilter(name, ns));
1450 }
1451
1452 /**
1453 * This returns the first child element within this element with the
1454 * given local name and belonging to the given namespace.
1455 * If no elements exist for the specified name and namespace, null is
1456 * returned.
1457 *
1458 * @param name local name of child element to match
1459 * @param ns <code>Namespace</code> to search within
1460 * @return the first matching child element, or null if not found
1461 */
1462 public Element getChild(String name, Namespace ns) {
1463 List elements = content.getView(new ElementFilter(name, ns));
1464 Iterator i = elements.iterator();
1465 if (i.hasNext()) {
1466 return (Element) i.next();
1467 }
1468 return null;
1469 }
1470
1471 /**
1472 * This returns the first child element within this element with the
1473 * given local name and belonging to no namespace.
1474 * If no elements exist for the specified name and namespace, null is
1475 * returned.
1476 *
1477 * @param name local name of child element to match
1478 * @return the first matching child element, or null if not found
1479 */
1480 public Element getChild(String name) {
1481 return getChild(name, Namespace.NO_NAMESPACE);
1482 }
1483
1484 /**
1485 * <p>
1486 * This removes the first child element (one level deep) with the
1487 * given local name and belonging to no namespace.
1488 * Returns true if a child was removed.
1489 * </p>
1490 *
1491 * @param name the name of child elements to remove
1492 * @return whether deletion occurred
1493 */
1494 public boolean