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