View Javadoc

1   /*
2    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3    *
4    * This software is open source.
5    * See the bottom of this file for the licence.
6    */
7   
8   package org.dom4j.tree;
9   
10  import java.util.ArrayList;
11  import java.util.Iterator;
12  import java.util.List;
13  import java.util.StringTokenizer;
14  
15  import org.dom4j.Branch;
16  import org.dom4j.Comment;
17  import org.dom4j.Element;
18  import org.dom4j.IllegalAddException;
19  import org.dom4j.Namespace;
20  import org.dom4j.Node;
21  import org.dom4j.ProcessingInstruction;
22  import org.dom4j.QName;
23  
24  /***
25   * <p>
26   * <code>AbstractBranch</code> is an abstract base class for tree implementors
27   * to use for implementation inheritence.
28   * </p>
29   * 
30   * @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
31   * @version $Revision: 1.44 $
32   */
33  public abstract class AbstractBranch extends AbstractNode implements Branch {
34      protected static final int DEFAULT_CONTENT_LIST_SIZE = 5;
35  
36      public AbstractBranch() {
37      }
38  
39      public boolean isReadOnly() {
40          return false;
41      }
42  
43      public boolean hasContent() {
44          return nodeCount() > 0;
45      }
46  
47      public List content() {
48          List backingList = contentList();
49  
50          return new ContentListFacade(this, backingList);
51      }
52  
53      public String getText() {
54          List content = contentList();
55  
56          if (content != null) {
57              int size = content.size();
58  
59              if (size >= 1) {
60                  Object first = content.get(0);
61                  String firstText = getContentAsText(first);
62  
63                  if (size == 1) {
64                      // optimised to avoid StringBuffer creation
65                      return firstText;
66                  } else {
67                      StringBuffer buffer = new StringBuffer(firstText);
68  
69                      for (int i = 1; i < size; i++) {
70                          Object node = content.get(i);
71                          buffer.append(getContentAsText(node));
72                      }
73  
74                      return buffer.toString();
75                  }
76              }
77          }
78  
79          return "";
80      }
81  
82      /***
83       * DOCUMENT ME!
84       * 
85       * @param content
86       *            DOCUMENT ME!
87       * 
88       * @return the text value of the given content object as text which returns
89       *         the text value of CDATA, Entity or Text nodes
90       */
91      protected String getContentAsText(Object content) {
92          if (content instanceof Node) {
93              Node node = (Node) content;
94  
95              switch (node.getNodeType()) {
96                  case CDATA_SECTION_NODE:
97  
98                  // case ENTITY_NODE:
99                  case ENTITY_REFERENCE_NODE:
100                 case TEXT_NODE:
101                     return node.getText();
102 
103                 default:
104                     break;
105             }
106         } else if (content instanceof String) {
107             return (String) content;
108         }
109 
110         return "";
111     }
112 
113     /***
114      * DOCUMENT ME!
115      * 
116      * @param content
117      *            DOCUMENT ME!
118      * 
119      * @return the XPath defined string-value of the given content object
120      */
121     protected String getContentAsStringValue(Object content) {
122         if (content instanceof Node) {
123             Node node = (Node) content;
124 
125             switch (node.getNodeType()) {
126                 case CDATA_SECTION_NODE:
127 
128                 // case ENTITY_NODE:
129                 case ENTITY_REFERENCE_NODE:
130                 case TEXT_NODE:
131                 case ELEMENT_NODE:
132                     return node.getStringValue();
133 
134                 default:
135                     break;
136             }
137         } else if (content instanceof String) {
138             return (String) content;
139         }
140 
141         return "";
142     }
143 
144     public String getTextTrim() {
145         String text = getText();
146 
147         StringBuffer textContent = new StringBuffer();
148         StringTokenizer tokenizer = new StringTokenizer(text);
149 
150         while (tokenizer.hasMoreTokens()) {
151             String str = tokenizer.nextToken();
152             textContent.append(str);
153 
154             if (tokenizer.hasMoreTokens()) {
155                 textContent.append(" "); // separator
156             }
157         }
158 
159         return textContent.toString();
160     }
161 
162     public void setProcessingInstructions(List listOfPIs) {
163         for (Iterator iter = listOfPIs.iterator(); iter.hasNext();) {
164             ProcessingInstruction pi = (ProcessingInstruction) iter.next();
165             addNode(pi);
166         }
167     }
168 
169     public Element addElement(String name) {
170         Element node = getDocumentFactory().createElement(name);
171         add(node);
172 
173         return node;
174     }
175 
176     public Element addElement(String qualifiedName, String namespaceURI) {
177         Element node = getDocumentFactory().createElement(qualifiedName,
178                 namespaceURI);
179         add(node);
180 
181         return node;
182     }
183 
184     public Element addElement(QName qname) {
185         Element node = getDocumentFactory().createElement(qname);
186         add(node);
187 
188         return node;
189     }
190 
191     public Element addElement(String name, String prefix, String uri) {
192         Namespace namespace = Namespace.get(prefix, uri);
193         QName qName = getDocumentFactory().createQName(name, namespace);
194 
195         return addElement(qName);
196     }
197 
198     // polymorphic node methods
199     public void add(Node node) {
200         switch (node.getNodeType()) {
201             case ELEMENT_NODE:
202                 add((Element) node);
203 
204                 break;
205 
206             case COMMENT_NODE:
207                 add((Comment) node);
208 
209                 break;
210 
211             case PROCESSING_INSTRUCTION_NODE:
212                 add((ProcessingInstruction) node);
213 
214                 break;
215 
216             default:
217                 invalidNodeTypeAddException(node);
218         }
219     }
220 
221     public boolean remove(Node node) {
222         switch (node.getNodeType()) {
223             case ELEMENT_NODE:
224                 return remove((Element) node);
225 
226             case COMMENT_NODE:
227                 return remove((Comment) node);
228 
229             case PROCESSING_INSTRUCTION_NODE:
230                 return remove((ProcessingInstruction) node);
231 
232             default:
233                 invalidNodeTypeAddException(node);
234 
235                 return false;
236         }
237     }
238 
239     // typesafe versions using node classes
240     public void add(Comment comment) {
241         addNode(comment);
242     }
243 
244     public void add(Element element) {
245         addNode(element);
246     }
247 
248     public void add(ProcessingInstruction pi) {
249         addNode(pi);
250     }
251 
252     public boolean remove(Comment comment) {
253         return removeNode(comment);
254     }
255 
256     public boolean remove(Element element) {
257         return removeNode(element);
258     }
259 
260     public boolean remove(ProcessingInstruction pi) {
261         return removeNode(pi);
262     }
263 
264     public Element elementByID(String elementID) {
265         for (int i = 0, size = nodeCount(); i < size; i++) {
266             Node node = node(i);
267 
268             if (node instanceof Element) {
269                 Element element = (Element) node;
270                 String id = elementID(element);
271 
272                 if ((id != null) && id.equals(elementID)) {
273                     return element;
274                 } else {
275                     element = element.elementByID(elementID);
276 
277                     if (element != null) {
278                         return element;
279                     }
280                 }
281             }
282         }
283 
284         return null;
285     }
286 
287     public void appendContent(Branch branch) {
288         for (int i = 0, size = branch.nodeCount(); i < size; i++) {
289             Node node = branch.node(i);
290             add((Node) node.clone());
291         }
292     }
293 
294     public Node node(int index) {
295         Object object = contentList().get(index);
296 
297         if (object instanceof Node) {
298             return (Node) object;
299         }
300 
301         if (object instanceof String) {
302             return getDocumentFactory().createText(object.toString());
303         }
304 
305         return null;
306     }
307 
308     public int nodeCount() {
309         return contentList().size();
310     }
311 
312     public int indexOf(Node node) {
313         return contentList().indexOf(node);
314     }
315 
316     public Iterator nodeIterator() {
317         return contentList().iterator();
318     }
319 
320     // Implementation methods
321 
322     /***
323      * DOCUMENT ME!
324      * 
325      * @param element
326      *            DOCUMENT ME!
327      * 
328      * @return the ID of the given <code>Element</code>
329      */
330     protected String elementID(Element element) {
331         // XXX: there will be other ways of finding the ID
332         // XXX: should probably have an IDResolver or something
333         return element.attributeValue("ID");
334     }
335 
336     /***
337      * DOCUMENT ME!
338      * 
339      * @return the internal List used to manage the content
340      */
341     protected abstract List contentList();
342 
343     /***
344      * A Factory Method pattern which creates a List implementation used to
345      * store content
346      * 
347      * @return DOCUMENT ME!
348      */
349     protected List createContentList() {
350         return new ArrayList(DEFAULT_CONTENT_LIST_SIZE);
351     }
352 
353     /***
354      * A Factory Method pattern which creates a List implementation used to
355      * store content
356      * 
357      * @param size
358      *            DOCUMENT ME!
359      * 
360      * @return DOCUMENT ME!
361      */
362     protected List createContentList(int size) {
363         return new ArrayList(size);
364     }
365 
366     /***
367      * A Factory Method pattern which creates a BackedList implementation used
368      * to store results of a filtered content query.
369      * 
370      * @return DOCUMENT ME!
371      */
372     protected BackedList createResultList() {
373         return new BackedList(this, contentList());
374     }
375 
376     /***
377      * A Factory Method pattern which creates a BackedList implementation which
378      * contains a single result
379      * 
380      * @param result
381      *            DOCUMENT ME!
382      * 
383      * @return DOCUMENT ME!
384      */
385     protected List createSingleResultList(Object result) {
386         BackedList list = new BackedList(this, contentList(), 1);
387         list.addLocal(result);
388 
389         return list;
390     }
391 
392     /***
393      * A Factory Method pattern which creates an empty a BackedList
394      * implementation
395      * 
396      * @return DOCUMENT ME!
397      */
398     protected List createEmptyList() {
399         return new BackedList(this, contentList(), 0);
400     }
401 
402     protected abstract void addNode(Node node);
403 
404     protected abstract void addNode(int index, Node node);
405 
406     protected abstract boolean removeNode(Node node);
407 
408     /***
409      * Called when a new child node has been added to me to allow any parent
410      * relationships to be created or events to be fired.
411      * 
412      * @param node
413      *            DOCUMENT ME!
414      */
415     protected abstract void childAdded(Node node);
416 
417     /***
418      * Called when a child node has been removed to allow any parent
419      * relationships to be deleted or events to be fired.
420      * 
421      * @param node
422      *            DOCUMENT ME!
423      */
424     protected abstract void childRemoved(Node node);
425 
426     /***
427      * Called when the given List content has been removed so each node should
428      * have its parent and document relationships cleared
429      */
430     protected void contentRemoved() {
431         List content = contentList();
432 
433         for (int i = 0, size = content.size(); i < size; i++) {
434             Object object = content.get(i);
435 
436             if (object instanceof Node) {
437                 childRemoved((Node) object);
438             }
439         }
440     }
441 
442     /***
443      * Called when an invalid node has been added. Throws an {@link
444      * IllegalAddException}.
445      * 
446      * @param node
447      *            DOCUMENT ME!
448      * 
449      * @throws IllegalAddException
450      *             DOCUMENT ME!
451      */
452     protected void invalidNodeTypeAddException(Node node) {
453         throw new IllegalAddException("Invalid node type. Cannot add node: "
454                 + node + " to this branch: " + this);
455     }
456 }
457 
458 /*
459  * Redistribution and use of this software and associated documentation
460  * ("Software"), with or without modification, are permitted provided that the
461  * following conditions are met:
462  * 
463  * 1. Redistributions of source code must retain copyright statements and
464  * notices. Redistributions must also contain a copy of this document.
465  * 
466  * 2. Redistributions in binary form must reproduce the above copyright notice,
467  * this list of conditions and the following disclaimer in the documentation
468  * and/or other materials provided with the distribution.
469  * 
470  * 3. The name "DOM4J" must not be used to endorse or promote products derived
471  * from this Software without prior written permission of MetaStuff, Ltd. For
472  * written permission, please contact dom4j-info@metastuff.com.
473  * 
474  * 4. Products derived from this Software may not be called "DOM4J" nor may
475  * "DOM4J" appear in their names without prior written permission of MetaStuff,
476  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
477  * 
478  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
479  * 
480  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
481  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
482  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
483  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
484  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
485  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
486  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
487  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
488  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
489  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
490  * POSSIBILITY OF SUCH DAMAGE.
491  * 
492  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
493  */