KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > apache > html > dom > HTMLCollectionImpl


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999,2000 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Xerces" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57 package org.enhydra.apache.html.dom;
58
59
60 import org.w3c.dom.Element JavaDoc;
61 import org.w3c.dom.Node JavaDoc;
62 import org.w3c.dom.html.HTMLAnchorElement;
63 import org.w3c.dom.html.HTMLAppletElement;
64 import org.w3c.dom.html.HTMLAreaElement;
65 import org.w3c.dom.html.HTMLCollection;
66 import org.w3c.dom.html.HTMLElement;
67 import org.w3c.dom.html.HTMLFormElement;
68 import org.w3c.dom.html.HTMLImageElement;
69 import org.w3c.dom.html.HTMLObjectElement;
70 import org.w3c.dom.html.HTMLOptionElement;
71 import org.w3c.dom.html.HTMLTableCellElement;
72 import org.w3c.dom.html.HTMLTableRowElement;
73 import org.w3c.dom.html.HTMLTableSectionElement;
74
75
76 /**
77  * Implements {@link org.w3c.dom.html.HTMLCollection} to traverse any named
78  * elements on a {@link org.w3c.dom.html.HTMLDocument}. The elements type to
79  * look for is identified in the constructor by code. This collection is not
80  * optimized for traversing large trees.
81  * <p>
82  * The collection has to meet two requirements: it has to be live, and it has
83  * to traverse depth first and always return results in that order. As such,
84  * using an object container (such as {@link java.util.Vector}) is expensive on
85  * insert/remove operations. Instead, the collection has been implemented using
86  * three traversing functions. As a result, operations on large documents will
87  * result in traversal of the entire document tree and consume a considerable
88  * amount of time.
89  * <p>
90  * Note that synchronization on the traversed document cannot be achieved.
91  * The document itself cannot be locked, and locking each traversed node is
92  * likely to lead to a dead lock condition. Therefore, there is a chance of the
93  * document being changed as results are fetched; in all likelihood, the results
94  * might be out dated, but not erroneous.
95  *
96  *
97  * @version $Revision: 1.2 $ $Date: 2005/01/26 08:28:44 $
98  * @author <a HREF="mailto:arkin@exoffice.com">Assaf Arkin</a>
99  * @see org.w3c.dom.html.HTMLCollection
100  */

101 class HTMLCollectionImpl
102     implements HTMLCollection
103 {
104     
105
106     /**
107      * Request collection of all anchors in document: &lt;A&gt; elements that
108      * have a <code>name</code> attribute.
109      */

110     static final short ANCHOR = 1;
111     
112     
113     /**
114      * Request collection of all forms in document: &lt;FORM&gt; elements.
115      */

116     static final short FORM = 2;
117     
118     
119     /**
120      * Request collection of all images in document: &lt;IMAGE&gt; elements.
121      */

122     static final short IMAGE = 3;
123     
124     
125     /**
126      * Request collection of all Applets in document: &lt;APPLET&gt; and
127      * &lt;OBJECT&gt; elements (&lt;OBJECT&gt; must contain an Applet).
128      */

129     static final short APPLET = 4;
130     
131     
132     /**
133      * Request collection of all links in document: &lt;A&gt; and &lt;AREA&gt;
134      * elements (must have a <code>href</code> attribute).
135      */

136     static final short LINK = 5;
137     
138     
139     /**
140      * Request collection of all options in selection: &lt;OPTION&gt; elments in
141      * &lt;SELECT&gt; or &lt;OPTGROUP&gt;.
142      */

143     static final short OPTION = 6;
144     
145     
146     /**
147      * Request collection of all rows in table: &lt;TR&gt; elements in table or
148      * table section.
149      */

150     static final short ROW = 7;
151
152     
153     /**
154      * Request collection of all form elements: &lt;INPUT&gt;, &lt;BUTTON&gt;,
155      * &lt;SELECT&gt;, &lt;TEXT&gt; and &lt;TEXTAREA&gt; elements inside form
156      * &lt;FORM&gt;.
157      */

158     static final short ELEMENT = 8;
159     
160     
161     /**
162      * Request collection of all areas in map: &lt;AREA&gt; element in &lt;MAP&gt;
163      * (non recursive).
164      */

165     static final short AREA = -1;
166     
167
168     /**
169      * Request collection of all table bodies in table: &lt;TBODY&gt; element in
170      * table &lt;TABLE&gt; (non recursive).
171      */

172     static final short TBODY = -2;
173
174     
175     /**
176      * Request collection of all cells in row: &lt;TD&gt; elements in &lt;TR&gt;
177      * (non recursive).
178      */

179     static final short CELL = -3;
180
181     
182     /**
183      * Indicates what this collection is looking for. Holds one of the enumerated
184      * values and used by {@link #collectionMatch}. Set by the constructor and
185      * determine the collection's use for its life time.
186      */

187     private short _lookingFor;
188     
189     
190     /**
191      * This is the top level element underneath which the collection exists.
192      */

193     private Element JavaDoc _topLevel;
194
195
196     /**
197      * Construct a new collection that retrieves element of the specific type
198      * (<code>lookingFor</code>) from the specific document portion
199      * (<code>topLevel</code>).
200      *
201      * @param topLevel The element underneath which the collection exists
202      * @param lookingFor Code indicating what elements to look for
203      */

204     HTMLCollectionImpl( HTMLElement topLevel, short lookingFor )
205     {
206         if ( topLevel == null )
207             throw new NullPointerException JavaDoc( "HTM011 Argument 'topLevel' is null." );
208         _topLevel = topLevel;
209        _lookingFor = lookingFor;
210     }
211   
212   
213     /**
214      * Returns the length of the collection. This method might traverse the
215      * entire document tree.
216      *
217      * @return Length of the collection
218      */

219     public final int getLength()
220     {
221         // Call recursive function on top-level element.
222
return getLength( _topLevel );
223     }
224
225
226     /**
227      * Retrieves the indexed node from the collection. Nodes are numbered in
228      * tree order - depth-first traversal order. This method might traverse
229      * the entire document tree.
230      *
231      * @param index The index of the node to return
232      * @return The specified node or null if no such node found
233      */

234     public final Node JavaDoc item( int index )
235     {
236         if ( index < 0 )
237             throw new IllegalArgumentException JavaDoc( "HTM012 Argument 'index' is negative." );
238         // Call recursive function on top-level element.
239
return item( _topLevel, new CollectionIndex( index ) );
240     }
241     
242     
243     /**
244      * Retrieves the named node from the collection. The name is matched case
245      * sensitive against the <TT>id</TT> attribute of each element in the
246      * collection, returning the first match. The tree is traversed in
247      * depth-first order. This method might traverse the entire document tree.
248      *
249      * @param name The name of the node to return
250      * @return The specified node or null if no such node found
251      */

252     public final Node JavaDoc namedItem( String JavaDoc name )
253     {
254         if ( name == null )
255             throw new NullPointerException JavaDoc( "HTM013 Argument 'name' is null." );
256         // Call recursive function on top-level element.
257
return namedItem( _topLevel, name );
258     }
259     
260     
261     /**
262      * Recursive function returns the number of elements of a particular type
263      * that exist under the top level element. This is a recursive function
264      * and the top level element is passed along.
265      *
266      * @param topLevel Top level element from which to scan
267      * @return Number of elements
268      */

269     private int getLength( Element JavaDoc topLevel )
270     {
271         int length;
272         Node JavaDoc node;
273     
274         synchronized ( topLevel )
275         {
276             // Always count from zero and traverse all the childs of the
277
// current element in the order they appear.
278
length = 0;
279             node = topLevel.getFirstChild();
280             while ( node != null )
281             {
282                 // If a particular node is an element (could be HTML or XML),
283
// do two things: if it's the one we're looking for, count
284
// another matched element; at any rate, traverse it's
285
// children as well.
286
if ( node instanceof Element JavaDoc )
287                 {
288                     if ( collectionMatch( (Element JavaDoc) node, null ) )
289                         ++ length;
290                     else if ( recurse() )
291                         length += getLength( (Element JavaDoc) node );
292                 }
293                 node = node.getNextSibling();
294             }
295         }
296         return length;
297     }
298     
299         
300     /**
301      * Recursive function returns the numbered element of a particular type
302      * that exist under the top level element. This is a recursive function
303      * and the top level element is passed along.
304      * <p>
305      * Note that this function must call itself with an index and get back both
306      * the element (if one was found) and the new index which is decremeneted
307      * for any like element found. Since integers are only passed by value,
308      * this function makes use of a separate class ({@link CollectionIndex})
309      * to hold that index.
310      *
311      * @param topLevel Top level element from which to scan
312      * @param index The index of the item to retreive
313      * @return Number of elements
314      * @see CollectionIndex
315      */

316     private Node JavaDoc item( Element JavaDoc topLevel, CollectionIndex index )
317     {
318         Node JavaDoc node;
319         Node JavaDoc result;
320
321         synchronized ( topLevel )
322         {
323             // Traverse all the childs of the current element in the order
324
// they appear. Count from the index backwards until you reach
325
// matching element with an index of zero. Return that element.
326
node = topLevel.getFirstChild();
327             while ( node != null )
328             {
329                 // If a particular node is an element (could be HTML or XML),
330
// do two things: if it's the one we're looking for, decrease
331
// the index and if zero, return this node; at any rate,
332
// traverse it's children as well.
333
if ( node instanceof Element JavaDoc )
334                 {
335                     if ( collectionMatch( (Element JavaDoc) node, null ) )
336                     {
337                         if ( index.isZero() )
338                             return node;
339                         index.decrement();
340                     } else if ( recurse() )
341                     {
342                         result = item( (Element JavaDoc) node, index );
343                         if ( result != null )
344                             return result;
345                     }
346                 }
347                 node = node.getNextSibling();
348             }
349         }
350         return null;
351     }
352     
353     
354     /**
355      * Recursive function returns an element of a particular type with the
356      * specified name (<TT>id</TT> attribute).
357      *
358      * @param topLevel Top level element from which to scan
359      * @param name The named element to look for
360      * @return The first named element found
361      */

362     private Node JavaDoc namedItem( Element JavaDoc topLevel, String JavaDoc name )
363     {
364         Node JavaDoc node;
365         Node JavaDoc result;
366
367         synchronized ( topLevel )
368         {
369             // Traverse all the childs of the current element in the order
370
// they appear.
371
node = topLevel.getFirstChild();
372             while ( node != null )
373             {
374                 // If a particular node is an element (could be HTML or XML),
375
// do two things: if it's the one we're looking for, and the
376
// name (id attribute) attribute is the one we're looking for,
377
// return this element; otherwise, traverse it's children.
378
if ( node instanceof Element JavaDoc )
379                 {
380                     if ( collectionMatch( (Element JavaDoc) node, name ) )
381                         return node;
382                     else if ( recurse() )
383                     {
384                         result = namedItem( (Element JavaDoc) node, name );
385                         if ( result != null )
386                             return result;
387                     }
388                 }
389                 node = node.getNextSibling();
390             }
391             return node;
392         }
393     }
394     
395     
396     /**
397      * Returns true if scanning methods should iterate through the collection.
398      * When looking for elements in the document, recursing is needed to traverse
399      * the full document tree. When looking inside a specific element (e.g. for a
400      * cell inside a row), recursing can lead to erroneous results.
401      *
402      * @return True if methods should recurse to traverse entire tree
403      */

404     protected boolean recurse()
405     {
406         return _lookingFor > 0;
407     }
408     
409
410     /**
411      * Determines if current element matches based on what we're looking for.
412      * The element is passed along with an optional identifier name. If the
413      * element is the one we're looking for, return true. If the name is also
414      * specified, the name must match the <code>id</code> attribute
415      * (match <code>name</code> first for anchors).
416      *
417      * @param elem The current element
418      * @param name The identifier name or null
419      * @return The element matches what we're looking for
420      */

421     protected boolean collectionMatch( Element JavaDoc elem, String JavaDoc name )
422     {
423         boolean match;
424         
425         synchronized ( elem )
426         {
427             // Begin with no matching. Depending on what we're looking for,
428
// attempt to match based on the element type. This is the quickest
429
// way to match involving only a cast. Do the expensive string
430
// comparison later on.
431
match = false;
432             switch ( _lookingFor )
433             {
434             case ANCHOR:
435                 // Anchor is an <A> element with a 'name' attribute. Otherwise, it's
436
// just a link.
437
match = ( elem instanceof HTMLAnchorElement ) &&
438                         elem.getAttribute( "name" ).length() > 0;
439                 break;
440             case FORM:
441                 // Any <FORM> element.
442
match = ( elem instanceof HTMLFormElement );
443                 break;
444             case IMAGE:
445                 // Any <IMG> element. <OBJECT> elements with images are not returned.
446
match = ( elem instanceof HTMLImageElement );
447                 break;
448             case APPLET:
449                 // Any <APPLET> element, and any <OBJECT> element which represents an
450
// Applet. This is determined by 'codetype' attribute being
451
// 'application/java' or 'classid' attribute starting with 'java:'.
452
match = ( elem instanceof HTMLAppletElement ) ||
453                         ( elem instanceof HTMLObjectElement &&
454                           ( "application/java".equals( elem.getAttribute( "codetype" ) ) ||
455                             elem.getAttribute( "classid" ).startsWith( "java:" ) ) );
456                 break;
457             case ELEMENT:
458                 // All form elements implement HTMLFormControl for easy identification.
459
match = ( elem instanceof HTMLFormControl );
460                 break;
461             case LINK:
462                 // Any <A> element, and any <AREA> elements with an 'href' attribute.
463
match = ( ( elem instanceof HTMLAnchorElement ||
464                             elem instanceof HTMLAreaElement ) &&
465                           elem.getAttribute( "href" ).length() > 0 );
466                 break;
467             case AREA:
468                 // Any <AREA> element.
469
match = ( elem instanceof HTMLAreaElement );
470                 break;
471             case OPTION:
472                 // Any <OPTION> element.
473
match = ( elem instanceof HTMLOptionElement );
474                 break;
475             case ROW:
476                 // Any <TR> element.
477
match = ( elem instanceof HTMLTableRowElement );
478                 break;
479             case TBODY:
480                 // Any <TBODY> element (one of three table section types).
481
match = ( elem instanceof HTMLTableSectionElement &&
482                           elem.getTagName().equals( "tbody" ) );
483                 break;
484             case CELL:
485                 // Any <TD> element.
486
match = ( elem instanceof HTMLTableCellElement );
487                 break;
488             }
489         
490             // If element type was matched and a name was specified, must also match
491
// the name against either the 'id' or the 'name' attribute. The 'name'
492
// attribute is relevant only for <A> elements for backward compatibility.
493
if ( match && name != null )
494             {
495                 // If an anchor and 'name' attribute matches, return true. Otherwise,
496
// try 'id' attribute.
497
if ( elem instanceof HTMLAnchorElement &&
498                      name.equals( elem.getAttribute( "name" ) ) )
499                     return true;
500                 match = name.equals( elem.getAttribute( "id" ) );
501             }
502         }
503         return match;
504     }
505
506     
507 }
508
509
510 /**
511  * {@link CollectionImpl#item} must traverse down the tree and decrement the
512  * index until it matches an element who's index is zero. Since integers are
513  * passed by value, this class servers to pass the index into each recursion
514  * by reference. It encompasses all the operations that need be performed on
515  * the index, although direct access is possible.
516  *
517  * @see CollectionImpl#item
518  */

519 class CollectionIndex
520 {
521     
522     
523     /**
524      * Returns the current index.
525      *
526      * @return Current index
527      */

528     int getIndex()
529     {
530         return _index;
531     }
532     
533     
534     /**
535      * Decrements the index by one.
536      */

537     void decrement()
538     {
539         -- _index;
540     }
541     
542     
543     /**
544      * Returns true if index is zero (or negative).
545      *
546      * @return True if index is zero
547      */

548     boolean isZero()
549     {
550         return _index <= 0;
551     }
552     
553     
554     /**
555      * Constructs a new index with the specified initial value. The index will
556      * then be decremeneted until it reaches zero.
557      *
558      * @param index The initial value
559      */

560     CollectionIndex( int index )
561     {
562         _index = index;
563     }
564     
565     
566     /**
567      * Holds the actual value that is passed by reference using this class.
568      */

569     private int _index;
570     
571
572 }
573
Popular Tags