1   
2   
3   
4   
5   
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                      
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                  
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                 
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(" "); 
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     
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     
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     
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         
332         
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 
460 
461 
462 
463 
464 
465 
466 
467 
468 
469 
470 
471 
472 
473 
474 
475 
476 
477 
478 
479 
480 
481 
482 
483 
484 
485 
486 
487 
488 
489 
490 
491 
492 
493