KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ozoneDB > xml > dom > CollectionImpl


1 /**
2  * org/ozone-db/xml/dom/CollectionImpl.java
3  *
4  * The contents of this file are subject to the OpenXML Public
5  * License Version 1.0; you may not use this file except in compliance
6  * with the License. You may obtain a copy of the License at
7  * http://www.openxml.org/license.html
8  *
9  * THIS SOFTWARE IS DISTRIBUTED ON AN "AS IS" BASIS WITHOUT WARRANTY
10  * OF ANY KIND, EITHER EXPRESSED OR IMPLIED. THE INITIAL DEVELOPER
11  * AND ALL CONTRIBUTORS SHALL NOT BE LIABLE FOR ANY DAMAGES AS A
12  * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
13  * DERIVATIVES. SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING
14  * RIGHTS AND LIMITATIONS UNDER THE LICENSE.
15  *
16  * The Initial Developer of this code under the License is Assaf Arkin.
17  * Portions created by Assaf Arkin are Copyright (C) 1998, 1999.
18  * All Rights Reserved.
19  */

20
21 /**
22  * Changes for Persistent DOM running with ozone are
23  * Copyright 1999 by SMB GmbH. All rights reserved.
24  */

25
26 package org.ozoneDB.xml.dom;
27
28 import org.w3c.dom.*;
29 import org.w3c.dom.html.*;
30 //import org.openxml.XMLCollection;
31

32
33 /**
34  * Implements a live collection of elements. This collection is based on the
35  * {@link org.w3c.dom.html.HTMLCollection} defined for HTML documents but works
36  * with XML documents.
37  * <P>
38  * The collection is defined in terms of a root element and the elements to look
39  * for under this root. Only elements of the specified type are contained in the
40  * collection. Elements are returned by index or by identifier (the <TT>id</TT>
41  * attribute). The collection is live, meaning that changes to the document tree
42  * are immediately reflected in the collection. The collection is not optimized for
43  * traversing large document trees.
44  * <P>
45  * The collection has to meet two requirements: it has to be live, and it has
46  * to traverse depth first and always return results in that order. As such,
47  * using an object container (such as {@link java.util.Vector}) is expensive on
48  * insert/remove operations. Instead, the collection has been implemented using
49  * three traversing functions. As a result, operations on large documents will
50  * result in traversal of the entire document tree and consume a considerable
51  * amount of time. * <P> * Note that synchronization on the traversed document cannot be achieved.
52  * The document itself cannot be locked, and locking each traversed node is
53  * likely to lead to a dead lock condition. Therefore, there is a chance of the
54  * document being changed as results are fetched; in all likelihood, the results
55  * might be out dated, but not erroneous.
56  * <P>
57  * Used to implement both {@link org.ozoneDB.xml.core.XMLCollection} and
58  * {@link org.ozoneDB.xml.dom.html.HTMLAnchorElementImpl}. *
59  *
60  * @version $Revision: 1.2 $ $Date: 2003/11/20 23:18:42 $
61  * @author <a HREF="mailto:arkin@trendline.co.il">Assaf Arkin</a>
62  * @see org.w3c.dom.html.HTMLCollection
63  */

64 public class CollectionImpl implements HTMLCollection {
65
66
67     /**
68      * Returns the length of the collection. This method might traverse the entire
69      * document tree.
70      *
71      * @return Length of the collection
72      */

73     public final int getLength() {
74         // Call recursive function on top-level element.
75
return getLength( getTopLevel() );
76     }
77
78
79     /**
80      * Retrieves the indexed node from the collection. Nodes are numbered in tree
81      * order - depth-first traversal order. This method might traverse the entire
82      * document tree.
83      *
84      * @param index The index of the node to return
85      * @return The specified node or null if no such node found
86      */

87     public final Node item( int index ) {
88         if (index < 0) {
89             throw new IllegalArgumentException JavaDoc( "Argument 'index' is negative." );
90         }
91         // Call recursive function on top-level element.
92
return item( getTopLevel(), new CollectionIndex( index ) );
93     }
94
95
96     /**
97      * Retrieves the named node from the collection. The name is matched case
98      * sensitive against the <TT>id</TT> attribute of each element in the
99      * collection, returning the first match. The tree is traversed in depth-first
100      * order. This method might traverse the entire document tree.
101      *
102      * @param name The name of the node to return
103      * @return The specified node or null if no such node found
104      */

105     public final Node namedItem( String JavaDoc name ) {
106         if (name == null) {
107             throw new NullPointerException JavaDoc( "Argument 'name' is null." );
108         }
109         // Call recursive function on top-level element.
110
return namedItem( getTopLevel(), name );
111     }
112
113
114     /**
115      * Returns the top level element underneath which the collection exists.
116      *
117      * @return Top level element from which to scan
118      */

119     private Element getTopLevel() {
120         return _topLevel;
121     }
122
123
124     /**
125      * Recursive function returns the number of elements of a particular type that
126      * exist under the top level element. This is a recursive function and the
127      * top level element is passed along.
128      *
129      * @param topLevel Top level element from which to scan
130      * @return Number of elements
131      */

132     private int getLength( Element topLevel ) {
133         int length;
134         Node node;
135
136         synchronized (topLevel) {
137             // Always count from zero and traverse all the childs of the current
138
// element in the order they appear.
139
length = 0;
140             node = topLevel.getFirstChild();
141             while (node != null) {
142                 // If a particular node is an element (could be HTML or XML), do two
143
// things: if it's the one we're looking for, count another matched // element; at any rate, traverse it's children as well.
144
if (node instanceof Element) {
145                     if (collectionMatch( (Element)node, null )) {
146                         ++length;
147                     }
148                     if (recurse()) {
149                         length += getLength( (Element)node );
150                     }
151                 }
152                 node = node.getNextSibling();
153             }
154         }
155         return length;
156     }
157
158
159     /**
160      * Recursive function returns the numbered element of a particular type that
161      * exist under the top level element. This is a recursive function and the
162      * top level element is passed along.
163      * <p>
164      * Note that this function must call itself with an index and get back both
165      * the element (if one was found) and the new index which is decremeneted
166      * for any like element found. Since integers are only passed by value, this
167      * function makes use of a separate class ({@link CollectionIndex}) to
168      * hold that index.
169      *
170      * @param topLevel Top level element from which to scan
171      * @param index The index of the item to retreive
172      * @return Number of elements
173      * @see CollectionIndex
174      */

175     private Node item( Element topLevel, CollectionIndex index ) {
176         Node node;
177         Node result;
178
179         synchronized (topLevel) {
180             // Traverse all the childs of the current element in the order they appear.
181
// Count from the index backwards until you reach matching element with an
182
// index of zero. Return that element.
183
node = topLevel.getFirstChild();
184             while (node != null) {
185                 // If a particular node is an element (could be HTML or XML), do two
186
// things: if it's the one we're looking for, decrease the index and
187
// if zero, return this node; at any rate, traverse it's children
188
// as well.
189
if (node instanceof Element) {
190                     if (collectionMatch( (Element)node, null )) {
191                         if (index.isZero()) {
192                             return node;
193                         }
194                         index.decrement();
195                     }
196                     if (recurse()) {
197                         result = item( (Element)node, index );
198                         if (result != null) {
199                             return result;
200                         }
201                     }
202                 }
203                 node = node.getNextSibling();
204             }
205         }
206         return null;
207     }
208
209
210     /**
211      * Recursive function returns an element of a particular type with the
212      * specified name (<TT>ID</TT> attribute).
213      *
214      * @param topLevel Top level element from which to scan
215      * @param name The named element to look for
216      * @return The first named element found
217      */

218     private Node namedItem( Element topLevel, String JavaDoc name ) {
219         Node node;
220         Node result;
221         synchronized (topLevel) {
222             // Traverse all the childs of the current element in the order they appear.
223
node = topLevel.getFirstChild();
224             while (node != null) {
225                 // If a particular node is an element (could be HTML or XML),
226
// do two things: if it's the one we're looking for, and the name
227
// (ID attribute) attribute is the one we're looking for, return
228
// this element; otherwise, traverse it's children.
229
if (node instanceof Element) {
230                     if (collectionMatch( (Element)node, name )) {
231                         return node;
232                     }
233                     if (recurse()) {
234                         result = namedItem( (Element)node, name );
235                         if (result != null) {
236                             return result;
237                         }
238                     }
239                 }
240
241                 node = node.getNextSibling();
242             }
243             return node;
244         }
245     }
246
247
248     /**
249      * Returns true if scanning methods should iterate through the collection.
250      * When looking for elements in the document, recursing is needed to traverse
251      * the full document tree. When looking inside a specific element (e.g. for a
252      * cell inside a row), recursing can lead to erroneous results.
253      *
254      * @return True if methods should recurse to traverse entire tree
255      */

256     protected boolean recurse() {
257         return true;
258     }
259
260
261     /**
262      * Determines if current element matches based on what we're looking for.
263      * The element is passed along with an optional identifier name. If the
264      * element is the one we're looking for, return true. If the name is also
265      * specified, the name must match the <TT>id</TT> attribute.
266      *
267      * @param elem The current element
268      * @param name The identifier name or null
269      * @return The element matches what we're looking for
270      */

271     protected boolean collectionMatch( Element elem, String JavaDoc name ) {
272         boolean match;
273         synchronized (elem) {
274             match = elem.getTagName().equals( _lookForTag );
275             if (match && name != null) {
276                 match = name.equals( elem.getAttribute( "id" ) );
277             }
278         }
279         return match;
280     }
281
282
283     /**
284      * Construct a new collection that retrieves element of the specific type
285      * (<TT>lookFor</TT>) from the specific document portion (<TT>topLevel</TT>).
286      *
287      * @param topLevel The element underneath which the collection exists
288      * @param lookFor Tag of element to look for
289      */

290     public CollectionImpl( Element topLevel, String JavaDoc lookFor ) {
291         if (topLevel == null) {
292             throw new NullPointerException JavaDoc( "Argument 'topLevel' is null." );
293         }
294         if (lookFor == null || lookFor.length() == 0) {
295             throw new NullPointerException JavaDoc( "Argument 'lookFor' is null or an empty string." );
296         }
297         _topLevel = topLevel;
298         _lookForTag = lookFor;
299     }
300
301
302     /**
303      * Hidden constructor used by derived classes that might have different
304      * _lookfor properties.
305      *
306      * @param topLevel The element underneath which the collection exists
307      */

308     public CollectionImpl( Element topLevel ) {
309         if (topLevel == null) {
310             throw new NullPointerException JavaDoc( "Argument 'topLevel' is null." );
311         }
312         _topLevel = topLevel;
313     }
314
315     /**
316      * This is the top level element underneath which the collection exists.
317      */

318     private Element _topLevel;
319     /**
320      * This is the element type that this collection deals with. It identifies
321      * the element's tag name, and only elements matching these tag names are
322      * counted or returned by this collection.
323      */

324     private String JavaDoc _lookForTag;
325
326 }
327
328
329 /**
330  * {@link CollectionImpl#item} must traverse down the tree and decrement the
331  * index until it matches an element who's index is zero. Since integers are
332  * passed by value, this class servers to pass the index into each recursion
333  * by reference. It encompasses all the operations that need be performed on
334  * the index, although direct access is possible.
335  *
336  * @see CollectionImpl#item
337  */

338 class CollectionIndex {
339
340
341     /**
342      * Returns the current index.
343      *
344      * @return Current index
345      */

346     int getIndex() {
347         return _index;
348     }
349
350
351     /**
352      * Decrements the index by one.
353      */

354     void decrement() {
355         --_index;
356     }
357
358
359     /**
360      * Returns true if index is zero (or negative).
361      *
362      * @return True if index is zero
363      */

364     boolean isZero() {
365         return _index <= 0;
366     }
367
368
369     /**
370      * Constructs a new index with the specified initial value. The index will
371      * then be decremeneted until it reaches zero.
372      *
373      * @param index The initial value
374      */

375     CollectionIndex( int index ) {
376         _index = index;
377     }
378
379     /**
380      * Holds the actual value that is passed by reference using this class.
381      */

382     private int _index;
383
384 }
385
Popular Tags