View Javadoc

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