KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nu > xom > ParentNode


1 /* Copyright 2002-2004 Elliotte Rusty Harold
2    
3    This library is free software; you can redistribute it and/or modify
4    it under the terms of version 2.1 of the GNU Lesser General Public
5    License as published by the Free Software Foundation.
6    
7    This library is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10    GNU Lesser General Public License for more details.
11    
12    You should have received a copy of the GNU Lesser General Public
13    License along with this library; if not, write to the
14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15    Boston, MA 02111-1307 USA
16    
17    You can contact Elliotte Rusty Harold by sending e-mail to
18    elharo@metalab.unc.edu. Please include the word "XOM" in the
19    subject line. The XOM home page is located at http://www.xom.nu/
20 */

21
22 package nu.xom;
23
24 import java.util.ArrayList JavaDoc;
25
26
27 /**
28  *
29  * <p>
30  * The generic superclass for nodes that have children.
31  * Not counting subclasses, there are exactly two such classes in XOM:
32  * </p>
33  *
34  * <ul>
35  * <li><code>Document</code></li>
36  * <li><code>Element</code></li>
37  * </ul>
38  *
39  * <p>
40  * This class provides methods to add and remove child nodes.
41  * </p>
42  *
43  *
44  * @author Elliotte Rusty Harold
45  * @version 1.0
46  *
47  */

48 public abstract class ParentNode extends Node {
49
50     ArrayList JavaDoc children;
51     String JavaDoc actualBaseURI;
52
53     /**
54      * <p>
55      * Creates a new <code>ParentNode</code> object.
56      * Can only be invoked by other members of
57      * the <code>nu.xom</code> package.
58      * </p>
59      *
60      */

61     ParentNode() {}
62
63
64     /**
65      * <p>
66      * Returns the number of child nodes this node contains.
67      * This is always greater than or equal to 0.
68      * </p>
69      *
70      * @return the number of children of this node
71      */

72     public int getChildCount() {
73         if (children == null) return 0;
74         return children.size();
75     }
76
77     
78     /**
79      * <p>
80      * Inserts a child node at the specified position.
81      * The child node previously at that position (if any)
82      * and all subsequent child nodes are moved up by one.
83      * That is, when inserting a node at 2, the old node at 2
84      * is moved to 3, the old child at 3 is moved to 4, and so
85      * forth. Inserting at position 0 makes the child the first
86      * child of this node. Inserting at the position
87      * <code>getChildCount()</code> makes the child the
88      * last child of the node.
89      * </p>
90      *
91      * <p>
92      * All the other methods that add a node to the tree ultimately
93      * invoke this method.
94      * </p>
95      *
96      * @param position where to insert the child
97      * @param child the node to insert
98      *
99      * @throws IllegalAddException if this node cannot have a child of
100      * the argument's type
101      * @throws MultipleParentException if <code>child</code> already
102      * has a parent
103      * @throws NullPointerException if <code>child</code> is null
104      * @throws IndexOutOfBoundsException if the position is negative or
105      * greater than the number of children of this node
106      */

107     public void insertChild(Node child, int position) {
108         _insertChild(child, position);
109     }
110     
111     
112     // because this method is called from Document constructor and
113
// constructors should not call overridable methods
114
final void _insertChild(Node child, int position) {
115         insertionAllowed(child, position);
116         fastInsertChild(child, position);
117     }
118
119
120     void fastInsertChild(Node child, int position) {
121         if (children == null) children = new ArrayList JavaDoc(1);
122         children.add(position, child);
123         child.setParent(this);
124     }
125
126
127     abstract void insertionAllowed(Node child, int position);
128
129     
130     /**
131      * <p>
132      * Appends a node to the children of this node.
133      * </p>
134      *
135      * @param child node to append to this node
136      *
137      * @throws IllegalAddException if this node cannot have children
138      * of this type
139      * @throws MultipleParentException if child already has a parent
140      * @throws NullPointerException if <code>child</code> is null
141      *
142      */

143     public void appendChild(Node child) {
144         insertChild(child, getChildCount());
145     }
146
147     
148     /**
149      *<p>
150      * Returns the child of this node at the specified position.
151      * Indexes begin at 0 and count up to one less than the number
152      * of children in this node.
153      * </p>
154      *
155      * @param position index of the node to return
156      *
157      * @return the node at the requested position
158      *
159      * @throws IndexOutOfBoundsException if the index is negative or
160      * greater than or equal to the number of children of this node
161      */

162     public Node getChild(int position) {
163         
164         if (children == null) {
165             throw new IndexOutOfBoundsException JavaDoc(
166               "This node has no children"
167             );
168         }
169         return (Node) children.get(position);
170         
171     }
172     
173     
174     // private int lastPosition = -1;
175

176     /**
177      *<p>
178      * Returns the position of a node within the children of this
179      * node. This is a number between 0 and one less than the number of
180      * children of this node. It returns -1 if <code>child</code>
181      * does not have this node as a parent.
182      * </p>
183      *
184      * <p>
185      * This method does a linear search through the node's children.
186      * On average, it executes in O(N) where N is the number of
187      * children of the node.
188      * </p>
189      *
190      * @param child the node whose position is desired
191      *
192      * @return the position of the argument node among
193      * the children of this node
194      */

195     public int indexOf(Node child) {
196         
197         if (children == null) return -1;
198         
199         // Programs tend to iterate through in order so we store the
200
// last index returned and check the one immediately after it
201
// first; before searching the list from the beginning.
202
/* lastPosition++;
203         if (lastPosition != children.size()) {
204             if (child == children.get(lastPosition)) {
205               return lastPosition;
206             }
207             else lastPosition = -1;
208         }
209         lastPosition = children.indexOf(child);
210         return lastPosition; */

211         return children.indexOf(child);
212         
213     }
214
215     
216     /**
217      * <p>
218      * Removes the child of this node at the specified position.
219      * Indexes begin at 0 and count up to one less than the number
220      * of children in this node.
221      * </p>
222      *
223      * @param position index of the node to remove
224      *
225      * @return the node which was removed
226      *
227      * @throws IndexOutOfBoundsException if the index is negative or
228      * greater than or equal to the number of children of this node
229      */

230     public Node removeChild(int position) {
231         
232         if (children == null) {
233             throw new IndexOutOfBoundsException JavaDoc(
234               "This node has no children"
235             );
236         }
237         Node removed = (Node) children.get(position);
238         // fill in actual base URI
239
// This way does add base URIs to elements created in memory
240
// XXX but this is a HotSpot when building; we need a fastRemoveChild
241
if (removed.isElement()) fillInBaseURI((Element) removed);
242         children.remove(position);
243         removed.setParent(null);
244                 
245         return removed;
246         
247     }
248
249     
250     void fillInBaseURI(Element removed) {
251
252         ParentNode parent = removed;
253         String JavaDoc actualBaseURI = "";
254         while (parent != null && actualBaseURI.equals("")) {
255             actualBaseURI = parent.getActualBaseURI();
256             parent = parent.getParent();
257         }
258         removed.setActualBaseURI(actualBaseURI);
259         
260     }
261
262
263     /**
264      * <p>
265      * Removes the specified child of this node.
266      * </p>
267      *
268      * @param child child node to remove
269      *
270      * @return the node which was removed
271      *
272      * @throws NoSuchChildException if <code>child</code> is
273      * not in fact a child of this node
274      */

275     public Node removeChild(Node child) {
276         
277         if (children == null) {
278             throw new NoSuchChildException(
279               "Child does not belong to this node"
280             );
281         }
282         // This next line is a hotspot
283
int position = children.indexOf(child);
284         if (position == -1) {
285             throw new NoSuchChildException(
286               "Child does not belong to this node"
287             );
288         }
289         if (child.isElement()) fillInBaseURI((Element) child);
290         children.remove(position);
291         
292         child.setParent(null);
293
294         return child;
295         
296     }
297
298     
299     /**
300      * <p>
301      * Replaces an existing child with a new child node.
302      * If <code>oldChild</code> is not a child of this node,
303      * then a <code>NoSuchChildException</code> is thrown.
304      * </p>
305      *
306      * @param oldChild the node removed from the tree
307      * @param newChild the node inserted into the tree
308      *
309      * @throws MultipleParentException if <code>newChild</code> already
310      * has a parent
311      * @throws NoSuchChildException if <code>oldChild</code>
312      * is not a child of this node
313      * @throws NullPointerException if either argument is null
314      * @throws IllegalAddException if this node cannot have children
315      * of the type of <code>newChild</code>
316      */

317     public void replaceChild(Node oldChild, Node newChild) {
318         
319         if (oldChild == null) {
320             throw new NullPointerException JavaDoc(
321               "Tried to replace null child"
322             );
323         }
324         if (newChild == null) {
325             throw new NullPointerException JavaDoc(
326               "Tried to replace child with null"
327             );
328         }
329         if (children == null) {
330             throw new NoSuchChildException(
331               "Reference node is not a child of this node."
332             );
333         }
334         int position = children.indexOf(oldChild);
335         if (position == -1) {
336             throw new NoSuchChildException(
337               "Reference node is not a child of this node."
338             );
339         }
340         
341         if (oldChild == newChild) return;
342         
343         insertionAllowed(newChild, position);
344         removeChild(position);
345         insertChild(newChild, position);
346         
347     }
348
349     
350     /**
351      *
352      * <p>
353      * Sets the URI against which relative URIs in this node will be
354      * resolved. Generally, it's only necessary to set this property if
355      * it's different from a node's parent's base URI, as it may
356      * be in a document assembled from multiple entities
357      * or by XInclude.
358      * </p>
359      *
360      * <p>
361      * Relative URIs are not allowed here. Base URIs must be absolute.
362      * However, the base URI may be set to null or the empty string
363      * to indicate that the node has no explicit base URI. In this
364      * case, it inherits the base URI of its parent node, if any.
365      * </p>
366      *
367      * <p>
368      * URIs with fragment identifiers are also not allowed. The value
369      * passed to this method must be a pure URI, not a URI reference.
370      * </p>
371      *
372      * <p>
373      * You can also add an <code>xml:base</code> attribute to
374      * an element in the same way you'd add any other namespaced
375      * attribute to an element. If an element's base URI
376      * conflicts with its <code>xml:base</code> attribute,
377      * then the value found in the <code>xml:base</code> attribute
378      * is used.
379      * </p>
380      *
381      * <p>
382      * If the base URI is null or the empty string and there is
383      * no <code>xml:base</code> attribute, then the base URI is
384      * determined by the nearest ancestor node which does have a
385      * base URI. Moving such a node from one location to another
386      * can change its base URI.
387      * </p>
388      *
389      * @param URI the new base URI for this node
390      *
391      * @throws MalformedURIException if <code>URI</code> is
392      * not a legal RFC 2396 absolute URI
393      */

394     public abstract void setBaseURI(String JavaDoc URI);
395
396
397     String JavaDoc getActualBaseURI() {
398         if (actualBaseURI == null) return "";
399         return actualBaseURI;
400     }
401
402
403     void setActualBaseURI(String JavaDoc uri) {
404         if (uri == null) uri = "";
405         if (!"".equals(uri)) Verifier.checkAbsoluteURI(uri);
406         actualBaseURI = uri;
407     }
408
409
410     final String JavaDoc findActualBaseURI() {
411         
412         ParentNode current = this;
413         while (true) {
414             String JavaDoc actualBase = current.getActualBaseURI();
415             ParentNode parent = current.getParent();
416     
417             if (parent == null) return actualBase;
418                
419             if ("".equals(actualBase)) {
420                 current = parent;
421                 continue;
422             }
423                
424             // The parent is loaded from a different entity.
425
// Therefore just return the actual base.
426
return actualBase;
427         }
428         
429     }
430
431     
432 }
433
Popular Tags