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

Uploaded
author pfrommolt
date Mon, 21 Nov 2011 08:12:19 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:89ad0a9cca52
1 /*--
2
3 $Id: Element.java,v 1.159 2007/11/14 05:02:08 jhunter Exp $
4
5 Copyright (C) 2000-2007 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.159 $, $Date: 2007/11/14 05:02:08 $
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 * @author Victor Toni
81 */
82 public class Element extends Content implements Parent {
83
84 private static final String CVS_ID =
85 "@(#) $RCSfile: Element.java,v $ $Revision: 1.159 $ $Date: 2007/11/14 05:02:08 $ $Name: jdom_1_1_1 $";
86
87 private static final int INITIAL_ARRAY_SIZE = 5;
88
89 /** The local name of the element */
90 protected String name;
91
92 /** The namespace of the element */
93 protected transient Namespace namespace;
94
95 /** Additional namespace declarations to store on this element; useful
96 * during output */
97 protected transient List additionalNamespaces;
98
99 // See http://lists.denveronline.net/lists/jdom-interest/2000-September/003030.html
100 // for a possible memory optimization here (using a RootElement subclass)
101
102 /**
103 * The attributes of the element. Subclassers have to
104 * track attributes using their own mechanism.
105 */
106 AttributeList attributes = new AttributeList(this);
107
108 /**
109 * The content of the element. Subclassers have to
110 * track content using their own mechanism.
111 */
112 ContentList content = new ContentList(this);
113
114 /**
115 * This protected constructor is provided in order to support an Element
116 * subclass that wants full control over variable initialization. It
117 * intentionally leaves all instance variables null, allowing a lightweight
118 * subclass implementation. The subclass is responsible for ensuring all the
119 * get and set methods on Element behave as documented.
120 * <p>
121 * When implementing an Element subclass which doesn't require full control
122 * over variable initialization, be aware that simply calling super() (or
123 * letting the compiler add the implicit super() call) will not initialize
124 * the instance variables which will cause many of the methods to throw a
125 * NullPointerException. Therefore, the constructor for these subclasses
126 * should call one of the public constructors so variable initialization is
127 * handled automatically.
128 */
129 protected Element() { }
130
131 /**
132 * Creates a new element with the supplied (local) name and namespace. If
133 * the provided namespace is null, the element will have no namespace.
134 *
135 * @param name local name of the element
136 * @param namespace namespace for the element
137 * @throws IllegalNameException if the given name is illegal as an element
138 * name
139 */
140 public Element(final String name, final Namespace namespace) {
141 setName(name);
142 setNamespace(namespace);
143 }
144
145 /**
146 * Create a new element with the supplied (local) name and no namespace.
147 *
148 * @param name local name of the element
149 * @throws IllegalNameException if the given name is illegal as an element
150 * name.
151 */
152 public Element(final String name) {
153 this(name, (Namespace) null);
154 }
155
156 /**
157 * Creates a new element with the supplied (local) name and a namespace
158 * given by a URI. The element will be put into the unprefixed (default)
159 * namespace.
160 *
161 * @param name name of the element
162 * @param uri namespace URI for the element
163 * @throws IllegalNameException if the given name is illegal as an element
164 * name or the given URI is illegal as a
165 * namespace URI
166 */
167 public Element(final String name, final String uri) {
168 this(name, Namespace.getNamespace("", uri));
169 }
170
171 /**
172 * Creates a new element with the supplied (local) name and a namespace
173 * given by the supplied prefix and URI combination.
174 *
175 * @param name local name of the element
176 * @param prefix namespace prefix
177 * @param uri namespace URI for the element
178 * @throws IllegalNameException if the given name is illegal as an element
179 * name, the given prefix is illegal as a
180 * namespace prefix, or the given URI is
181 * illegal as a namespace URI
182 */
183 public Element(final String name, final String prefix, final String uri) {
184 this(name, Namespace.getNamespace(prefix, uri));
185 }
186
187 /**
188 * Returns the (local) name of the element (without any namespace prefix).
189 *
190 * @return local element name
191 */
192 public String getName() {
193 return name;
194 }
195
196 /**
197 * Sets the (local) name of the element.
198 *
199 * @param name the new (local) name of the element
200 * @return the target element
201 * @throws IllegalNameException if the given name is illegal as an Element
202 * name
203 */
204 public Element setName(final String name) {
205 final String reason = Verifier.checkElementName(name);
206 if (reason != null) {
207 throw new IllegalNameException(name, "element", reason);
208 }
209 this.name = name;
210 return this;
211 }
212
213 /**
214 * Returns the element's {@link Namespace}.
215 *
216 * @return the element's namespace
217 */
218 public Namespace getNamespace() {
219 return namespace;
220 }
221
222 /**
223 * Sets the element's {@link Namespace}. If the provided namespace is null,
224 * the element will have no namespace.
225 *
226 * @param namespace the new namespace
227 * @return the target element
228 */
229 public Element setNamespace(Namespace namespace) {
230 if (namespace == null) {
231 namespace = Namespace.NO_NAMESPACE;
232 }
233
234 this.namespace = namespace;
235 return this;
236 }
237
238 /**
239 * Returns the namespace prefix of the element or an empty string if none
240 * exists.
241 *
242 * @return the namespace prefix
243 */
244 public String getNamespacePrefix() {
245 return namespace.getPrefix();
246 }
247
248 /**
249 * Returns the namespace URI mapped to this element's prefix (or the
250 * in-scope default namespace URI if no prefix). If no mapping is found, an
251 * empty string is returned.
252 *
253 * @return the namespace URI for this element
254 */
255 public String getNamespaceURI() {
256 return namespace.getURI();
257 }
258
259 /**
260 * Returns the {@link Namespace} corresponding to the given prefix in scope
261 * for this element. This involves searching up the tree, so the results
262 * depend on the current location of the element. Returns null if there is
263 * no namespace in scope with the given prefix at this point in the
264 * document.
265 *
266 * @param prefix namespace prefix to look up
267 * @return the Namespace for this prefix at this
268 * location, or null if none
269 */
270 public Namespace getNamespace(final String prefix) {
271 if (prefix == null) {
272 return null;
273 }
274
275 if ("xml".equals(prefix)) {
276 // Namespace "xml" is always bound.
277 return Namespace.XML_NAMESPACE;
278 }
279
280 // Check if the prefix is the prefix for this element
281 if (prefix.equals(getNamespacePrefix())) {
282 return getNamespace();
283 }
284
285 // Scan the additional namespaces
286 if (additionalNamespaces != null) {
287 for (int i = 0; i < additionalNamespaces.size(); i++) {
288 final Namespace ns = (Namespace) additionalNamespaces.get(i);
289 if (prefix.equals(ns.getPrefix())) {
290 return ns;
291 }
292 }
293 }
294
295 // If we still don't have a match, ask the parent
296 if (parent instanceof Element) {
297 return ((Element)parent).getNamespace(prefix);
298 }
299
300 return null;
301 }
302
303 /**
304 * Returns the full name of the element, in the form
305 * [namespacePrefix]:[localName]. If the element does not have a namespace
306 * prefix, then the local name is returned.
307 *
308 * @return qualified name of the element (including
309 * namespace prefix)
310 */
311 public String getQualifiedName() {
312 // Note: Any changes here should be reflected in
313 // XMLOutputter.printQualifiedName()
314 if ("".equals(namespace.getPrefix())) {
315 return getName();
316 }
317
318 return new StringBuffer(namespace.getPrefix())
319 .append(':')
320 .append(name)
321 .toString();
322 }
323
324 /**
325 * Adds a namespace declarations to this element. This should <i>not</i> be
326 * used to add the declaration for this element itself; that should be
327 * assigned in the construction of the element. Instead, this is for adding
328 * namespace declarations on the element not relating directly to itself.
329 * It's used during output to for stylistic reasons move namespace
330 * declarations higher in the tree than they would have to be.
331 *
332 * @param additionalNamespace namespace to add
333 * @throws IllegalAddException if the namespace prefix collides with another
334 * namespace prefix on the element
335 */
336 public void addNamespaceDeclaration(final Namespace additionalNamespace) {
337
338 // Verify the new namespace prefix doesn't collide with another
339 // declared namespace, an attribute prefix, or this element's prefix
340 final String reason = Verifier.checkNamespaceCollision(additionalNamespace, this);
341 if (reason != null) {
342 throw new IllegalAddException(this, additionalNamespace, reason);
343 }
344
345 if (additionalNamespaces == null) {
346 additionalNamespaces = new ArrayList(INITIAL_ARRAY_SIZE);
347 }
348
349 additionalNamespaces.add(additionalNamespace);
350 }
351
352 /**
353 * Removes an additional namespace declarations from this element. This
354 * should <i>not</i> be used to remove the declaration for this element
355 * itself; that should be handled in the construction of the element.
356 * Instead, this is for removing namespace declarations on the element not
357 * relating directly to itself. If the declaration is not present, this
358 * method does nothing.
359 *
360 * @param additionalNamespace namespace to remove
361 */
362 public void removeNamespaceDeclaration(final Namespace additionalNamespace) {
363 if (additionalNamespaces == null) {
364 return;
365 }
366 additionalNamespaces.remove(additionalNamespace);
367 }
368
369 /**
370 * Returns a list of the additional namespace declarations on this element.
371 * This includes only additional namespace, not the namespace of the element
372 * itself, which can be obtained through {@link #getNamespace()}. If there
373 * are no additional declarations, this returns an empty list. Note, the
374 * returned list is unmodifiable.
375 *
376 * @return a List of the additional namespace
377 * declarations
378 */
379 public List getAdditionalNamespaces() {
380 // Not having the returned list be live allows us to avoid creating a
381 // new list object when XMLOutputter calls this method on an element
382 // with an empty list.
383 if (additionalNamespaces == null) {
384 return Collections.EMPTY_LIST;
385 }
386 return Collections.unmodifiableList(additionalNamespaces);
387 }
388
389 /**
390 * Returns the XPath 1.0 string value of this element, which is the
391 * complete, ordered content of all text node descendants of this element
392 * (i&#46;e&#46; the text that's left after all references are resolved
393 * and all other markup is stripped out.)
394 *
395 * @return a concatentation of all text node descendants
396 */
397 public String getValue() {
398 final StringBuffer buffer = new StringBuffer();
399
400 final Iterator iter = getContent().iterator();
401 while (iter.hasNext()) {
402 final Content child = (Content) iter.next();
403 if (child instanceof Element || child instanceof Text) {
404 buffer.append(child.getValue());
405 }
406 }
407 return buffer.toString();
408 }
409
410 /**
411 * Returns whether this element is a root element. This can be used in
412 * tandem with {@link #getParent} to determine if an element has any
413 * "attachments" to a parent element or document.
414 *
415 * @return whether this is a root element
416 */
417 public boolean isRootElement() {
418 return parent instanceof Document;
419 }
420
421 public int getContentSize() {
422 return content.size();
423 }
424
425 public int indexOf(final Content child) {
426 return content.indexOf(child);
427 }
428
429 // private int indexOf(int start, Filter filter) {
430 // int size = getContentSize();
431 // for (int i = start; i < size; i++) {
432 // if (filter.matches(getContent(i))) {
433 // return i;
434 // }
435 // }
436 // return -1;
437 // }
438
439
440 /**
441 * Returns the textual content directly held under this element as a string.
442 * This includes all text within this single element, including whitespace
443 * and CDATA sections if they exist. It's essentially the concatenation of
444 * all {@link Text} and {@link CDATA} nodes returned by {@link #getContent}.
445 * The call does not recurse into child elements. If no textual value exists
446 * for the element, an empty string is returned.
447 *
448 * @return text content for this element, or empty
449 * string if none
450 */
451 public String getText() {
452 if (content.size() == 0) {
453 return "";
454 }
455
456 // If we hold only a Text or CDATA, return it directly
457 if (content.size() == 1) {
458 final Object obj = content.get(0);
459 if (obj instanceof Text) {
460 return ((Text) obj).getText();
461 }
462 else {
463 return "";
464 }
465 }
466
467 // Else build String up
468 final StringBuffer textContent = new StringBuffer();
469 boolean hasText = false;
470
471 for (int i = 0; i < content.size(); i++) {
472 final Object obj = content.get(i);
473 if (obj instanceof Text) {
474 textContent.append(((Text) obj).getText());
475 hasText = true;
476 }
477 }
478
479 if (!hasText) {
480 return "";
481 }
482 else {
483 return textContent.toString();
484 }
485 }
486
487 /**
488 * Returns the textual content of this element with all surrounding
489 * whitespace removed. If no textual value exists for the element, or if
490 * only whitespace exists, the empty string is returned.
491 *
492 * @return trimmed text content for this element, or
493 * empty string if none
494 */
495 public String getTextTrim() {
496 return getText().trim();
497 }
498
499 /**
500 * Returns the textual content of this element with all surrounding
501 * whitespace removed and internal whitespace normalized to a single space.
502 * If no textual value exists for the element, or if only whitespace exists,
503 * the empty string is returned.
504 *
505 * @return normalized text content for this element, or
506 * empty string if none
507 */
508 public String getTextNormalize() {
509 return Text.normalizeString(getText());
510 }
511
512 /**
513 * Returns the textual content of the named child element, or null if
514 * there's no such child. This method is a convenience because calling
515 * <code>getChild().getText()</code> can throw a NullPointerException.
516 *
517 * @param name the name of the child
518 * @return text content for the named child, or null if
519 * no such child
520 */
521 public String getChildText(final String name) {
522 final Element child = getChild(name);
523 if (child == null) {
524 return null;
525 }
526 return child.getText();
527 }
528
529 /**
530 * Returns the trimmed textual content of the named child element, or null
531 * if there's no such child. See <code>{@link #getTextTrim()}</code> for
532 * details of text trimming.
533 *
534 * @param name the name of the child
535 * @return trimmed text content for the named child, or
536 * null if no such child
537 */
538 public String getChildTextTrim(final String name) {
539 final Element child = getChild(name);
540 if (child == null) {
541 return null;
542 }
543 return child.getTextTrim();
544 }
545
546 /**
547 * Returns the normalized textual content of the named child element, or
548 * null if there's no such child. See <code>{@link
549 * #getTextNormalize()}</code> for details of text normalizing.
550 *
551 * @param name the name of the child
552 * @return normalized text content for the named child,
553 * or null if no such child
554 */
555 public String getChildTextNormalize(final String name) {
556 final Element child = getChild(name);
557 if (child == null) {
558 return null;
559 }
560 return child.getTextNormalize();
561 }
562
563 /**
564 * Returns the textual content of the named child element, or null if
565 * there's no such child.
566 *
567 * @param name the name of the child
568 * @param ns the namespace of the child
569 * @return text content for the named child, or null if
570 * no such child
571 */
572 public String getChildText(final String name, final Namespace ns) {
573 final Element child = getChild(name, ns);
574 if (child == null) {
575 return null;
576 }
577 return child.getText();
578 }
579
580 /**
581 * Returns the trimmed textual content of the named child element, or null
582 * if there's no such child.
583 *
584 * @param name the name of the child
585 * @param ns the namespace of the child
586 * @return trimmed text content for the named child, or
587 * null if no such child
588 */
589 public String getChildTextTrim(final String name, final Namespace ns) {
590 final Element child = getChild(name, ns);
591 if (child == null) {
592 return null;
593 }
594 return child.getTextTrim();
595 }
596
597 /**
598 * Returns the normalized textual content of the named child element, or
599 * null if there's no such child.
600 *
601 * @param name the name of the child
602 * @param ns the namespace of the child
603 * @return normalized text content for the named child,
604 * or null if no such child
605 */
606 public String getChildTextNormalize(final String name, final Namespace ns) {
607 final Element child = getChild(name, ns);
608 if (child == null) {
609 return null;
610 }
611 return child.getTextNormalize();
612 }
613
614 /**
615 * Sets the content of the element to be the text given. All existing text
616 * content and non-text context is removed. If this element should have both
617 * textual content and nested elements, use <code>{@link #setContent}</code>
618 * instead. Setting a null text value is equivalent to setting an empty
619 * string value.
620 *
621 * @param text new text content for the element
622 * @return the target element
623 * @throws IllegalDataException if the assigned text contains an illegal
624 * character such as a vertical tab (as
625 * determined by {@link
626 * org.jdom.Verifier#checkCharacterData})
627 */
628 public Element setText(final String text) {
629 content.clear();
630
631 if (text != null) {
632 addContent(new Text(text));
633 }
634
635 return this;
636 }
637
638 /**
639 * This returns the full content of the element as a List which
640 * may contain objects of type <code>Text</code>, <code>Element</code>,
641 * <code>Comment</code>, <code>ProcessingInstruction</code>,
642 * <code>CDATA</code>, and <code>EntityRef</code>.
643 * The List returned is "live" in document order and modifications
644 * to it affect the element's actual contents. Whitespace content is
645 * returned in its entirety.
646 *
647 * <p>
648 * Sequential traversal through the List is best done with an Iterator
649 * since the underlying implement of List.size() may require walking the
650 * entire list.
651 * </p>
652 *
653 * @return a <code>List</code> containing the mixed content of the
654 * element: may contain <code>Text</code>,
655 * <code>{@link Element}</code>, <code>{@link Comment}</code>,
656 * <code>{@link ProcessingInstruction}</code>,
657 * <code>{@link CDATA}</code>, and
658 * <code>{@link EntityRef}</code> objects.
659 */
660 public List getContent() {
661 return content;
662 }
663
664 /**
665 * Return a filter view of this <code>Element</code>'s content.
666 *
667 * <p>
668 * Sequential traversal through the List is best done with a Iterator
669 * since the underlying implement of List.size() may require walking the
670 * entire list.
671 * </p>
672 *
673 * @param filter <code>Filter</code> to apply
674 * @return <code>List</code> - filtered Element content
675 */
676 public List getContent(final Filter filter) {
677 return content.getView(filter);
678 }
679
680 /**
681 * Removes all child content from this parent.
682 *
683 * @return list of the old children detached from this parent
684 */
685 public List removeContent() {
686 final List old = new ArrayList(content);
687 content.clear();
688 return old;
689 }
690
691 /**
692 * Remove all child content from this parent matching the supplied filter.
693 *
694 * @param filter filter to select which content to remove
695 * @return list of the old children detached from this parent
696 */
697 public List removeContent(final Filter filter) {
698 final List old = new ArrayList();
699 final Iterator iter = content.getView(filter).iterator();
700 while (iter.hasNext()) {
701 final Content child = (Content) iter.next();
702 old.add(child);
703 iter.remove();
704 }
705 return old;
706 }
707
708 /**
709 * This sets the content of the element. The supplied List should
710 * contain only objects of type <code>Element</code>, <code>Text</code>,
711 * <code>CDATA</code>, <code>Comment</code>,
712 * <code>ProcessingInstruction</code>, and <code>EntityRef</code>.
713 *
714 * <p>
715 * When all objects in the supplied List are legal and before the new
716 * content is added, all objects in the old content will have their
717 * parentage set to null (no parent) and the old content list will be
718 * cleared. This has the effect that any active list (previously obtained
719 * with a call to {@link #getContent} or {@link #getChildren}) will also
720 * change to reflect the new content. In addition, all objects in the
721 * supplied List will have their parentage set to this element, but the
722 * List itself will not be "live" and further removals and additions will
723 * have no effect on this elements content. If the user wants to continue
724 * working with a "live" list, then a call to setContent should be
725 * followed by a call to {@link #getContent} or {@link #getChildren} to
726 * obtain a "live" version of the content.
727 * </p>
728 *
729 * <p>
730 * Passing a null or empty List clears the existing content.
731 * </p>
732 *
733 * <p>
734 * In event of an exception the original content will be unchanged and
735 * the objects in the supplied content will be unaltered.
736 * </p>
737 *
738 * @param newContent <code>Collection</code> of content to set
739 * @return this element modified
740 * @throws IllegalAddException if the List contains objects of
741 * illegal types or with existing parentage.
742 */
743 public Element setContent(final Collection newContent) {
744 content.clearAndSet(newContent);
745 return this;
746 }
747
748 /**
749 * Replace the current child the given index with the supplied child.
750 * <p>
751 * In event of an exception the original content will be unchanged and
752 * the supplied child will be unaltered.
753 * </p>
754 *
755 * @param index - index of child to replace.
756 * @param child - child to add.
757 * @return element on which this method was invoked
758 * @throws IllegalAddException if the supplied child is already attached
759 * or not legal content for this parent.
760 * @throws IndexOutOfBoundsException if index is negative or greater
761 * than the current number of children.
762 */
763 public Element setContent(final int index, final Content child) {
764 content.set(index, child);
765 return this;
766 }
767
768 /**
769 * Replace the child at the given index whith the supplied
770 * collection.
771 * <p>
772 * In event of an exception the original content will be unchanged and
773 * the content in the supplied collection will be unaltered.
774 * </p>
775 *
776 * @param index - index of child to replace.
777 * @param newContent - <code>Collection</code> of content to replace child.
778 * @return object on which this method was invoked
779 * @throws IllegalAddException if the collection contains objects of
780 * illegal types.
781 * @throws IndexOutOfBoundsException if index is negative or greater
782 * than the current number of children.
783 */
784 public Parent setContent(final int index, final Collection newContent) {
785 content.remove(index);
786 content.addAll(index, newContent);
787 return this;
788 }
789
790 /**
791 * This adds text content to this element. It does not replace the
792 * existing content as does <code>setText()</code>.
793 *
794 * @param str <code>String</code> to add
795 * @return this element modified
796 * @throws IllegalDataException if <code>str</code> contains an
797 * illegal character such as a vertical tab (as determined
798 * by {@link org.jdom.Verifier#checkCharacterData})
799 */
800 public Element addContent(final String str) {
801 return addContent(new Text(str));
802 }
803
804 /**
805 * Appends the child to the end of the element's content list.
806 *
807 * @param child child to append to end of content list
808 * @return the element on which the method was called
809 * @throws IllegalAddException if the given child already has a parent. */
810 public Element addContent(final Content child) {
811 content.add(child);
812 return this;
813 }
814
815 /**
816 * Appends all children in the given collection to the end of
817 * the content list. In event of an exception during add the
818 * original content will be unchanged and the objects in the supplied
819 * collection will be unaltered.
820 *
821 * @param newContent <code>Collection</code> of content to append
822 * @return the element on which the method was called
823 * @throws IllegalAddException if any item in the collection
824 * already has a parent or is of an inappropriate type.
825 */
826 public Element addContent(final Collection newContent) {
827 content.addAll(newContent);
828 return this;
829 }
830
831 /**
832 * Inserts the child into the content list at the given index.
833 *
834 * @param index location for adding the collection
835 * @param child child to insert
836 * @return the parent on which the method was called
837 * @throws IndexOutOfBoundsException if index is negative or beyond
838 * the current number of children
839 * @throws IllegalAddException if the given child already has a parent.
840 */
841 public Element addContent(final int index, final Content child) {
842 content.add(index, child);
843 return this;
844 }
845
846 /**
847 * Inserts the content in a collection into the content list
848 * at the given index. In event of an exception the original content
849 * will be unchanged and the objects in the supplied collection will be
850 * unaltered.
851 *
852 * @param index location for adding the collection
853 * @param newContent <code>Collection</code> of content to insert
854 * @return the parent on which the method was called
855 * @throws IndexOutOfBoundsException if index is negative or beyond
856 * the current number of children
857 * @throws IllegalAddException if any item in the collection
858 * already has a parent or is of an inappropriate type.
859 */
860 public Element addContent(final int index, final Collection newContent) {
861 content.addAll(index, newContent);
862 return this;
863 }
864
865 public List cloneContent() {
866 final int size = getContentSize();
867 final List list = new ArrayList(size);
868 for (int i = 0; i < size; i++) {
869 final Content child = getContent(i);
870 list.add(child.clone());
871 }
872 return list;
873 }
874
875 public Content getContent(final int index) {
876 return (Content) content.get(index);
877 }
878
879 // public Content getChild(Filter filter) {
880 // int i = indexOf(0, filter);
881 // return (i < 0) ? null : getContent(i);
882 // }
883
884 public boolean removeContent(final Content child) {
885 return content.remove(child);
886 }
887
888 public Content removeContent(final int index) {
889 return (Content) content.remove(index);
890 }
891
892 /**
893 * Set this element's content to be the supplied child.
894 * <p>
895 * If the supplied child is legal content for this parent and before
896 * it is added, all content in the current content list will
897 * be cleared and all current children will have their parentage set to
898 * null.
899 * <p>
900 * This has the effect that any active list (previously obtained with
901 * a call to one of the {@link #getContent} methods will also change
902 * to reflect the new content. In addition, all content in the supplied
903 * collection will have their parentage set to this parent. If the user
904 * wants to continue working with a <b>"live"</b> list of this parent's
905 * child, then a call to setContent should be followed by a call to one
906 * of the {@link #getContent} methods to obtain a <b>"live"</b>
907 * version of the children.
908 * <p>
909 * Passing a null child clears the existing content.
910 * <p>
911 * In event of an exception the original content will be unchanged and
912 * the supplied child will be unaltered.
913 *
914 * @param child new content to replace existing content
915 * @return the parent on which the method was called
916 * @throws IllegalAddException if the supplied child is already attached
917 * or not legal content for an Element
918 */
919 public Element setContent(final Content child) {
920 content.clear();
921 content.add(child);
922 return this;
923 }
924
925
926 /**
927 * Determines if this element is the ancestor of another element.
928 *
929 * @param element <code>Element</code> to check against
930 * @return <code>true</code> if this element is the ancestor of the
931 * supplied element
932 */
933 public boolean isAncestor(final Element element) {
934 Parent p = element.getParent();
935 while (p instanceof Element) {
936 if (p == this) {
937 return true;
938 }
939 p = p.getParent();
940 }
941 return false;
942 }
943
944 /**
945 * <p>
946 * This returns the complete set of attributes for this element, as a
947 * <code>List</code> of <code>Attribute</code> objects in no particular
948 * order, or an empty list if there are none.
949 * The returned list is "live" and changes to it affect the
950 * element's actual attributes.
951 * </p>
952 *
953 * @return attributes for the element
954 */
955 public List getAttributes() {
956 return attributes;
957 }
958
959 /**
960 * <p>
961 * This returns the attribute for this element with the given name
962 * and within no namespace, or null if no such attribute exists.
963 * </p>
964 *
965 * @param name name of the attribute to return
966 * @return attribute for the element
967 */
968 public Attribute getAttribute(final String name) {
969 return getAttribute(name, Namespace.NO_NAMESPACE);
970 }
971
972 /**
973 * <p>
974 * This returns the attribute for this element with the given name
975 * and within the given Namespace, or null if no such attribute exists.
976 * </p>
977 *
978 * @param name name of the attribute to return
979 * @param ns <code>Namespace</code> to search within
980 * @return attribute for the element
981 */
982 public Attribute getAttribute(final String name, final Namespace ns) {
983 return (Attribute) attributes.get(name, ns);
984 }
985
986 /**
987 * <p>
988 * This returns the attribute value for the attribute with the given name
989 * and within no namespace, null if there is no such attribute, and the
990 * empty string if the attribute value is empty.
991 * </p>
992 *
993 * @param name name of the attribute whose value to be returned
994 * @return the named attribute's value, or null if no such attribute
995 */
996 public String getAttributeValue(final String name) {
997 return getAttributeValue(name, Namespace.NO_NAMESPACE);
998 }
999
1000 /**
1001 * <p>
1002 * This returns the attribute value for the attribute with the given name
1003 * and within no namespace, or the passed-in default if there is no
1004 * such attribute.
1005 * </p>
1006 *
1007 * @param name name of the attribute whose value to be returned
1008 * @param def a default value to return if the attribute does not exist
1009 * @return the named attribute's value, or the default if no such attribute
1010 */
1011 public String getAttributeValue(final String name, final String def) {
1012 return getAttributeValue(name, Namespace.NO_NAMESPACE, def);
1013 }
1014
1015 /**
1016 * <p>
1017 * This returns the attribute value for the attribute with the given name
1018 * and within the given Namespace, null if there is no such attribute, and
1019 * the empty string if the attribute value is empty.
1020 * </p>
1021 *
1022 * @param name name of the attribute whose valud is to be returned
1023 * @param ns <code>Namespace</code> to search within
1024 * @return the named attribute's value, or null if no such attribute
1025 */
1026 public String getAttributeValue(final String name, final Namespace ns) {
1027 return getAttributeValue(name, ns, null);
1028 }
1029
1030 /**
1031 * <p>
1032 * This returns the attribute value for the attribute with the given name
1033 * and within the given Namespace, or the passed-in default if there is no
1034 * such attribute.
1035 * </p>
1036 *
1037 * @param name name of the attribute whose valud is to be returned
1038 * @param ns <code>Namespace</code> to search within
1039 * @param def a default value to return if the attribute does not exist
1040 * @return the named attribute's value, or the default if no such attribute
1041 */
1042 public String getAttributeValue(final String name, final Namespace ns, final String def) {
1043 final Attribute attribute = (Attribute) attributes.get(name, ns);
1044 if (attribute == null) {
1045 return def;
1046 }
1047
1048 return attribute.getValue();
1049 }
1050
1051 /**
1052 * <p>
1053 * This sets the attributes of the element. The supplied Collection should
1054 * contain only objects of type <code>Attribute</code>.
1055 * </p>
1056 *
1057 * <p>
1058 * When all objects in the supplied List are legal and before the new
1059 * attributes are added, all old attributes will have their
1060 * parentage set to null (no parent) and the old attribute list will be
1061 * cleared. This has the effect that any active attribute list (previously
1062 * obtained with a call to {@link #getAttributes}) will also change to
1063 * reflect the new attributes. In addition, all attributes in the supplied
1064 * List will have their parentage set to this element, but the List itself
1065 * will not be "live" and further removals and additions will have no
1066 * effect on this elements attributes. If the user wants to continue
1067 * working with a "live" attribute list, then a call to setAttributes
1068 * should be followed by a call to {@link #getAttributes} to obtain a
1069 * "live" version of the attributes.
1070 * </p>
1071 *
1072 * <p>
1073 * Passing a null or empty List clears the existing attributes.
1074 * </p>
1075 *
1076 * <p>
1077 * In cases where the List contains duplicate attributes, only the last
1078 * one will be retained. This has the same effect as calling
1079 * {@link #setAttribute(Attribute)} sequentially.
1080 * </p>
1081 *
1082 * <p>
1083 * In event of an exception the original attributes will be unchanged and
1084 * the attributes in the supplied attributes will be unaltered.
1085 * </p>
1086 *
1087 * @param newAttributes <code>Collection</code> of attributes to set
1088 * @return this element modified
1089 * @throws IllegalAddException if the List contains objects
1090 * that are not instances of <code>Attribute</code>,
1091 * or if any of the <code>Attribute</code> objects have
1092 * conflicting namespace prefixes.
1093 */
1094 public Element setAttributes(final Collection newAttributes) {
1095 attributes.clearAndSet(newAttributes);
1096 return this;
1097 }
1098
1099 /**
1100 * <p>
1101 * This sets the attributes of the element. It's an alternate form of
1102 * the method, accepting a <code>List</code> instead of a
1103 * <code>Collection</code>, for backward compatibility.
1104 * </p>
1105 */
1106 public Element setAttributes(final List newAttributes) {
1107 return setAttributes((Collection)newAttributes);
1108 }
1109
1110 /**
1111 * <p>
1112 * This sets an attribute value for this element. Any existing attribute
1113 * with the same name and namespace URI is removed.
1114 * </p>
1115 *
1116 * @param name name of the attribute to set
1117 * @param value value of the attribute to set
1118 * @return this element modified
1119 * @throws IllegalNameException if the given name is illegal as an
1120 * attribute name.
1121 * @throws IllegalDataException if the given attribute value is
1122 * illegal character data (as determined by
1123 * {@link org.jdom.Verifier#checkCharacterData}).
1124 */
1125 public Element setAttribute(final String name, final String value) {
1126 final Attribute attribute = getAttribute(name);
1127 if (attribute == null) {
1128 final Attribute newAttribute = new Attribute(name, value);
1129 setAttribute(newAttribute);
1130 } else {
1131 attribute.setValue(value);
1132 }
1133
1134 return this;
1135 }
1136
1137 /**
1138 * <p>
1139 * This sets an attribute value for this element. Any existing attribute
1140 * with the same name and namespace URI is removed.
1141 * </p>
1142 *
1143 * @param name name of the attribute to set
1144 * @param value value of the attribute to set
1145 * @param ns namespace of the attribute to set
1146 * @return this element modified
1147 * @throws IllegalNameException if the given name is illegal as an
1148 * attribute name, or if the namespace is an unprefixed default
1149 * namespace
1150 * @throws IllegalDataException if the given attribute value is
1151 * illegal character data (as determined by
1152 * {@link org.jdom.Verifier#checkCharacterData}).
1153 * @throws IllegalAddException if the attribute namespace prefix
1154 * collides with another namespace prefix on the element.
1155 */
1156 public Element setAttribute(final String name, final String value, final Namespace ns) {
1157 final Attribute attribute = getAttribute(name, ns);
1158 if (attribute == null) {
1159 final Attribute newAttribute = new Attribute(name, value, ns);
1160 setAttribute(newAttribute);
1161 } else {
1162 attribute.setValue(value);
1163 }
1164
1165 return this;
1166 }
1167
1168 /**
1169 * <p>
1170 * This sets an attribute value for this element. Any existing attribute
1171 * with the same name and namespace URI is removed.
1172 * </p>
1173 *
1174 * @param attribute <code>Attribute</code> to set
1175 * @return this element modified
1176 * @throws IllegalAddException if the attribute being added already has a
1177 * parent or if the attribute namespace prefix collides with another
1178 * namespace prefix on the element.
1179 */
1180 public Element setAttribute(final Attribute attribute) {
1181 attributes.add(attribute);
1182 return this;
1183 }
1184
1185 /**
1186 * <p>
1187 * This removes the attribute with the given name and within no
1188 * namespace. If no such attribute exists, this method does nothing.
1189 * </p>
1190 *
1191 * @param name name of attribute to remove
1192 * @return whether the attribute was removed
1193 */
1194 public boolean removeAttribute(final String name) {
1195 return removeAttribute(name, Namespace.NO_NAMESPACE);
1196 }
1197
1198 /**
1199 * <p>
1200 * This removes the attribute with the given name and within the
1201 * given Namespace. If no such attribute exists, this method does
1202 * nothing.
1203 * </p>
1204 *
1205 * @param name name of attribute to remove
1206 * @param ns namespace URI of attribute to remove
1207 * @return whether the attribute was removed
1208 */
1209 public boolean removeAttribute(final String name, final Namespace ns) {
1210 return attributes.remove(name, ns);
1211 }
1212
1213 /**
1214 * <p>
1215 * This removes the supplied Attribute should it exist.
1216 * </p>
1217 *
1218 * @param attribute Reference to the attribute to be removed.
1219 * @return whether the attribute was removed
1220 */
1221 public boolean removeAttribute(final Attribute attribute) {
1222 return attributes.remove(attribute);
1223 }
1224
1225 /**
1226 * <p>
1227 * This returns a <code>String</code> representation of the
1228 * <code>Element</code>, suitable for debugging. If the XML
1229 * representation of the <code>Element</code> is desired,
1230 * {@link org.jdom.output.XMLOutputter#outputString(Element)}
1231 * should be used.
1232 * </p>
1233 *
1234 * @return <code>String</code> - information about the
1235 * <code>Element</code>
1236 */
1237 public String toString() {
1238 final StringBuffer stringForm = new StringBuffer(64)
1239 .append("[Element: <")
1240 .append(getQualifiedName());
1241
1242 final String nsuri = getNamespaceURI();
1243 if (!"".equals(nsuri)) {
1244 stringForm
1245 .append(" [Namespace: ")
1246 .append(nsuri)
1247 .append("]");
1248 }
1249 stringForm.append("/>]");
1250
1251 return stringForm.toString();
1252 }
1253
1254 /**
1255 * <p>
1256 * This returns a deep clone of this element.
1257 * The new element is detached from its parent, and getParent()
1258 * on the clone will return null.
1259 * </p>
1260 *
1261 * @return the clone of this element
1262 */
1263 public Object clone() {
1264
1265 // Ken Rune Helland <kenh@csc.no> is our local clone() guru
1266
1267 final Element element = (Element) super.clone();
1268
1269 // name and namespace are references to immutable objects
1270 // so super.clone() handles them ok
1271
1272 // Reference to parent is copied by super.clone()
1273 // (Object.clone()) so we have to remove it
1274 // Actually, super is a Content, which has already detached in the
1275 // clone().
1276 // element.parent = null;
1277
1278 // Reference to content list and attribute lists are copyed by
1279 // super.clone() so we set it new lists if the original had lists
1280 element.content = new ContentList(element);
1281 element.attributes = new AttributeList(element);
1282
1283 // Cloning attributes
1284 if (attributes != null) {
1285 for(int i = 0; i < attributes.size(); i++) {
1286 final Attribute attribute = (Attribute) attributes.get(i);
1287 element.attributes.add(attribute.clone());
1288 }
1289 }
1290
1291 // Cloning additional namespaces
1292 if (additionalNamespaces != null) {
1293 element.additionalNamespaces = new ArrayList(additionalNamespaces);
1294 }
1295
1296 // Cloning content
1297 if (content != null) {
1298 for(int i = 0; i < content.size(); i++) {
1299 final Content c = (Content) content.get(i);
1300 element.content.add(c.clone());
1301 }
1302 }
1303
1304 return element;
1305 }
1306
1307
1308 // Support a custom Namespace serialization so no two namespace
1309 // object instances may exist for the same prefix/uri pair
1310 private void writeObject(final ObjectOutputStream out) throws IOException {
1311
1312 out.defaultWriteObject();
1313
1314 // We use writeObject() and not writeUTF() to minimize space
1315 // This allows for writing pointers to already written strings
1316 out.writeObject(namespace.getPrefix());
1317 out.writeObject(namespace.getURI());
1318
1319 if (additionalNamespaces == null) {
1320 out.write(0);
1321 }
1322 else {
1323 final int size = additionalNamespaces.size();
1324 out.write(size);
1325 for (int i = 0; i < size; i++) {
1326 final Namespace additional = (Namespace) additionalNamespaces.get(i);
1327 out.writeObject(additional.getPrefix());
1328 out.writeObject(additional.getURI());
1329 }
1330 }
1331 }
1332
1333 private void readObject(final ObjectInputStream in)
1334 throws IOException, ClassNotFoundException {
1335
1336 in.defaultReadObject();
1337
1338 namespace = Namespace.getNamespace(
1339 (String)in.readObject(), (String)in.readObject());
1340
1341 final int size = in.read();
1342
1343 if (size != 0) {
1344 additionalNamespaces = new ArrayList(size);
1345 for (int i = 0; i < size; i++) {
1346 final Namespace additional = Namespace.getNamespace(
1347 (String)in.readObject(), (String)in.readObject());
1348 additionalNamespaces.add(additional);
1349 }
1350 }
1351 }
1352
1353 /**
1354 * Returns an iterator that walks over all descendants in document order.
1355 *
1356 * @return an iterator to walk descendants
1357 */
1358 public Iterator getDescendants() {
1359 return new DescendantIterator(this);
1360 }
1361
1362 /**
1363 * Returns an iterator that walks over all descendants in document order
1364 * applying the Filter to return only elements that match the filter rule.
1365 * With filters you can match only Elements, only Comments, Elements or
1366 * Comments, only Elements with a given name and/or prefix, and so on.
1367 *
1368 * @param filter filter to select which descendants to see
1369 * @return an iterator to walk descendants within a filter
1370 */
1371 public Iterator getDescendants(final Filter filter) {
1372 final Iterator iterator = new DescendantIterator(this);
1373 return new FilterIterator(iterator, filter);
1374 }
1375
1376
1377
1378 /**
1379 * This returns a <code>List</code> of all the child elements
1380 * nested directly (one level deep) within this element, as
1381 * <code>Element</code> objects. If this target element has no nested
1382 * elements, an empty List is returned. The returned list is "live"
1383 * in document order and changes to it affect the element's actual
1384 * contents.
1385 *
1386 * <p>
1387 * Sequential traversal through the List is best done with a Iterator
1388 * since the underlying implement of List.size() may not be the most
1389 * efficient.
1390 * </p>
1391 *
1392 * <p>
1393 * No recursion is performed, so elements nested two levels deep
1394 * would have to be obtained with:
1395 * <pre>
1396 * <code>
1397 * Iterator itr = (currentElement.getChildren()).iterator();
1398 * while(itr.hasNext()) {
1399 * Element oneLevelDeep = (Element)itr.next();
1400 * List twoLevelsDeep = oneLevelDeep.getChildren();
1401 * // Do something with these children
1402 * }
1403 * </code>
1404 * </pre>
1405 * </p>
1406 *
1407 * @return list of child <code>Element</code> objects for this element
1408 */
1409 public List getChildren() {
1410 return content.getView(new ElementFilter());
1411 }
1412
1413 /**
1414 * This returns a <code>List</code> of all the child elements
1415 * nested directly (one level deep) within this element with the given
1416 * local name and belonging to no namespace, returned as
1417 * <code>Element</code> objects. If this target element has no nested
1418 * elements with the given name outside a namespace, an empty List
1419 * is returned. The returned list is "live" in document order
1420 * and changes to it affect the element's actual contents.
1421 * <p>
1422 * Please see the notes for <code>{@link #getChildren}</code>
1423 * for a code example.
1424 * </p>
1425 *
1426 * @param name local name for the children to match
1427 * @return all matching child elements
1428 */
1429 public List getChildren(final String name) {
1430 return getChildren(name, Namespace.NO_NAMESPACE);
1431 }
1432
1433 /**
1434 * This returns a <code>List</code> of all the child elements
1435 * nested directly (one level deep) within this element with the given
1436 * local name and belonging to the given Namespace, returned as
1437 * <code>Element</code> objects. If this target element has no nested
1438 * elements with the given name in the given Namespace, an empty List
1439 * is returned. The returned list is "live" in document order
1440 * and changes to it affect the element's actual contents.
1441 * <p>
1442 * Please see the notes for <code>{@link #getChildren}</code>
1443 * for a code example.
1444 * </p>
1445 *
1446 * @param name local name for the children to match
1447 * @param ns <code>Namespace</code> to search within
1448 * @return all matching child elements
1449 */
1450 public List getChildren(final String name, final Namespace ns) {
1451 return content.getView(new ElementFilter(name, ns));
1452 }
1453
1454 /**
1455 * This returns the first child element within this element with the
1456 * given local name and belonging to the given namespace.
1457 * If no elements exist for the specified name and namespace, null is
1458 * returned.
1459 *
1460 * @param name local name of child element to match
1461 * @param ns <code>Namespace</code> to search within
1462 * @return the first matching child element, or null if not found
1463 */
1464 public Element getChild(final String name, final Namespace ns) {
1465 final List elements = content.getView(new ElementFilter(name, ns));
1466 final Iterator iter = elements.iterator();
1467 if (iter.hasNext()) {
1468 return (Element) iter.next();
1469 }
1470 return null;
1471 }
1472
1473 /**
1474 * This returns the first child element within this element with the
1475 * given local name and belonging to no namespace.
1476 * If no elements exist for the specified name and namespace, null is
1477 * returned.
1478 *
1479 * @param name local name of child element to match
1480 * @return the first matching child element, or null if not found
1481 */
1482 public Element getChild(final String name) {
1483 return getChild(name, Namespace.NO_NAMESPACE);
1484 }
1485
1486 /**
1487 * <p>
1488 * This removes the first child element (one level deep) with the
1489 * given local name and belonging to no namespace.
1490 * Returns true if a child was removed.
1491 * </p>
1492 *
1493 * @param name the name of child elements to remove
1494 * @return whether deletion occurred
1495 */
1496 public boolean removeChild(final String name) {
1497 return removeChild(name, Namespace.NO_NAMESPACE);
1498 }
1499
1500 /**
1501 * <p>
1502 * This removes the first child element (one level deep) with the
1503 * given local name and belonging to the given namespace.
1504 * Returns true if a child was removed.
1505 * </p>
1506 *
1507 * @param name the name of child element to remove
1508 * @param ns <code>Namespace</code> to search within
1509 * @return whether deletion occurred
1510 */
1511 public boolean removeChild(final String name, final Namespace ns) {
1512 final Filter filter = new ElementFilter(name, ns);
1513 final List old = content.getView(filter);
1514 final Iterator iter = old.iterator();
1515 if (iter.hasNext()) {
1516 iter.next();
1517 iter.remove();
1518 return true;
1519 }
1520
1521 return false;
1522 }
1523
1524 /**
1525 * <p>
1526 * This removes all child elements (one level deep) with the
1527 * given local name and belonging to no namespace.
1528 * Returns true if any were removed.
1529 * </p>
1530 *
1531 * @param name the name of child elements to remove
1532 * @return whether deletion occurred
1533 */
1534 public boolean removeChildren(final String name) {
1535 return removeChildren(name, Namespace.NO_NAMESPACE);
1536 }
1537
1538 /**
1539 * <p>
1540 * This removes all child elements (one level deep) with the
1541 * given local name and belonging to the given namespace.
1542 * Returns true if any were removed.
1543 * </p>
1544 *
1545 * @param name the name of child elements to remove
1546 * @param ns <code>Namespace</code> to search within
1547 * @return whether deletion occurred
1548 */
1549 public boolean removeChildren(final String name, final Namespace ns) {
1550 boolean deletedSome = false;
1551
1552 final Filter filter = new ElementFilter(name, ns);
1553 final List old = content.getView(filter);
1554 final Iterator iter = old.iterator();
1555 while (iter.hasNext()) {
1556 iter.next();
1557 iter.remove();
1558 deletedSome = true;
1559 }
1560
1561 return deletedSome;
1562 }
1563
1564 }