1
2
3
4
5
6
7
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
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
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
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(" ");
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
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
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
282
283 /*** @return the ID of the given <code>Element</code>
284 */
285 protected String elementID(Element element) {
286
287
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422