KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ditchnet > xml > dom > DomUtils


1 /*
2  * The contents of this file are subject to the GNU Lesser General Public
3  * License Version 2.1 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of
5  * the License at http://www.gnu.org/copyleft/lesser.html
6  *
7  * Software distributed under the License is distributed on an "AS
8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9  * implied. See the License for the specific language governing
10  * rights and limitations under the License.
11  *
12  * Developer:
13  * Todd Ditchendorf, todd@ditchnet.org
14  *
15  */

16
17 /**
18  * @author Todd Ditchendorf
19  * @since 2005-03-13
20  *
21  */

22 package org.ditchnet.xml.dom;
23
24 import java.util.List JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import org.w3c.dom.*;
28 import org.ditchnet.xml.Xhtml;
29
30 /**
31  * A Utility class that may not be instanciated (private constructor) that
32  * contains static utility methods that ease DOM traversal pain.
33  * @author Todd Ditchendorf
34  *
35  *
36  */

37 public class DomUtils {
38     
39     /**
40      * Private constructor -- class may not be instanciated.
41      */

42     private DomUtils() { }
43     
44     /**
45      * Test <code>Element</code> param to see if it's HTML <code>id</code>
46      * attribute matches <code>id</code>.
47      * @param target The Element to test.
48      * @param id Check target for this value in it's HTML
49      * <code>id</code> attribute.
50      */

51     public static boolean hasId(final Element target,final String JavaDoc id) {
52         return target.getAttribute(Xhtml.Attr.ID.toString()).equals(id);
53     }
54     
55     /**
56      * Test the <code>target</code> param to see if it's HTML <code>class</code>
57      * attribute contains <code>className</code>. Keep in mind that the HTML
58      * 4.01 spec allows an element's HTML <code>class</code> attribute value
59      * to consist of multiple space-separated values. This method takes that
60      * fact into account, and tests <code>target</code>'s HTML class attribute
61      * for the existence of <code>className</code> anywhere in the string.</p>
62      *
63      * <p>So for example, say you had an <code>org.w3c.dom.Element</code>
64      * reference to the following HTML element in a variable named
65      * <code>target</code>:</p>
66      * <p><code>&lt;div class="ditch-tab-pane ditch-focused"&gt;&lt;/div&gt;
67      * </code></p>
68      * <p>note that this attribute value is taking advantage of the
69      * ability to list multiple space separated values.</p>
70      * <p><code>DomUtils.hasClassName(target,"ditch-tab-pane");</code></p>
71      * <p>Would return <code>true</code>.</p>
72      * <p><code>DomUtils.hasClassName(target,"ditch-tab");</code></p>
73      * <p>Would return <code>false</code>.</p>
74      *
75      * @param target The Element to test.
76      * @param className Check target for this value in it's HTML
77      * <code>class</code> attribute.
78      */

79     //* @throws IllegalArgumentException
80
public static boolean hasClassName(final Element target,
81                                        String JavaDoc className) {
82         className = className.trim();
83         /*if (className.indexOf(" ") > -1) {
84             throw new IllegalArgumentException("Whitespace is not allowed " +
85                    "in the className arg. hasClassName() accepts only one " +
86                    "class name at a time");
87         }*/

88         String JavaDoc cn = target.getAttribute(Xhtml.Attr.CLASS.toString());
89         if (null == cn || 0 == cn.length()) {
90             return false;
91         }
92         cn = cn.trim();
93         if (cn.equals(className)) {
94             return true;
95         }
96         if (cn.indexOf(className + " ") > -1) {
97             return true;
98         }
99         if (_isLastOfMultpleClassNames(cn,className)) {
100             return true;
101         }
102         return false;
103     }
104     
105     private static boolean _isLastOfMultpleClassNames(final String JavaDoc all,
106                                               final String JavaDoc className) {
107         int spaceBefore = all.lastIndexOf(className)-1;
108         return all.endsWith(className) &&
109             all.substring(spaceBefore,spaceBefore+1).equals(" ");
110     }
111     
112     /**
113      * Tests to see if <code>target</code>'s <code>getNodeType()</code>
114      * method returns <code>Node.ELEMENT_NODE</code>.
115      *
116      */

117     public static boolean isElementNode(final Node target) {
118         return Node.ELEMENT_NODE == target.getNodeType();
119     }
120     
121     /**
122      * Search up the DOM tree for the first ancestor element of <code>
123      * target</code> with an HTML <code>class</code> attribute value of
124      * <code>className</code>.
125      */

126     public static Element getFirstAncestorByClassName(final Node target,
127                                                       final String JavaDoc className) {
128         Element parent = (Element)target;
129         while ((parent = (Element)parent.getParentNode()) != null) {
130             if (hasClassName(parent,className)) {
131                 return parent;
132             }
133         }
134         return null;
135     }
136     
137     /**
138      * Search up the DOM tree for the first ancestor element of <code>
139      * target</code> with an HTML <code>class</code> attribute value of
140      * <code>className</code>.
141      */

142     public static Element getFirstAncestorOrSelfByClassName(
143                                                     final Node target,
144                                                     final String JavaDoc className) {
145         Element parent = (Element)target;
146         do {
147             if (hasClassName(parent,className)) {
148                 return parent;
149             }
150         } while ((parent = (Element)parent.getParentNode()) != null);
151         return null;
152     }
153     
154     /**
155      * Search <code>target</code>'s children for the first element node
156      * with an HTML <code>class</code> attribute value of
157      * <code>className</code>.
158      */

159     public static Element getFirstChildByClassName(
160                                                 final Node target,
161                                                 final String JavaDoc className) {
162         Node childNode;
163         for (int i = 0; i < target.getChildNodes().getLength(); i++) {
164             childNode = target.getChildNodes().item(i);
165             if ( Node.ELEMENT_NODE != childNode.getNodeType() ) {
166                 continue;
167             }
168             if (hasClassName(((Element)childNode),className)) {
169                 return (Element)childNode;
170             }
171         }
172         return null;
173     }
174     
175     /**
176      * Retreives <code>target</code>'s first descendant element with an
177      * HTML <code>class</code> attribute value that includes <code>
178      * className</code> using a breadth-first algorithm.
179      */

180     public static Element getFirstDescendantByClassNameBreadthFirst(
181                                                 final Node target,
182                                                 final String JavaDoc className) {
183         Element result;
184         if ((result = getFirstChildByClassName(target,className)) != null) {
185             return result;
186         }
187         for (int i = 0; i < target.getChildNodes().getLength(); i++) {
188             result = getFirstDescendantByClassNameBreadthFirst(
189                                             target.getChildNodes().item(i),
190                                             className );
191             if (null != result) {
192                 return result;
193             }
194         }
195         return null;
196     }
197     
198     /**
199      * Retreives <code>target</code>'s first descendant element with an
200      * HTML <code>class</code> attribute value that includes <code>
201      * className</code> using a depth-first algorithm.
202      */

203     public static Element getFirstDescendantByClassNameDepthFirst(
204                                                   final Node target,
205                                                   final String JavaDoc className) {
206         Node child;
207         Element result;
208         for (int i = 0; i < target.getChildNodes().getLength(); i++) {
209             child = target.getChildNodes().item(i);
210             if (isElementNode(child) && hasClassName(((Element)child),className)) {
211                 return (Element)child;
212             }
213             result = getFirstDescendantByClassNameDepthFirst(
214                                                     target.getChildNodes().item(i),
215                                                     className );
216             if (null != result) {
217                 return result;
218             }
219         }
220         return null;
221     }
222     
223     /**
224      * Returns all child elements of <code>target</code> with HTML <code>
225      * class</code> attribute values that contain <code>className</code>
226      * as an array of type {@link java.util.List}. NOTE that unlike the
227      * algorithms specified in the DOM spec, a <code>NodeList</code> is NOT
228      * returned. This method searches only one level deep... <code>
229      * target</code>'s child nodes.
230      */

231     public static List JavaDoc getChildrenByClassName(final Node target,
232                                                    final String JavaDoc className) {
233         List JavaDoc result = new ArrayList JavaDoc();
234         Node child;
235         for (int i = 0; i < target.getChildNodes().getLength(); i++) {
236             child = target.getChildNodes().item(i);
237             if (isElementNode(child) && hasClassName(((Element)child),className)) {
238                 result.add(child);
239             }
240         }
241         return result;
242     }
243     
244     /**
245      * Returns all descendant elements of <code>target</code> with HTML <code>
246      * class</code> attribute values that contain <code>className</code>
247      * as an array of type {@link java.util.List}. NOTE that unlike the
248      * algorithms specified in the DOM spec, a <code>NodeList</code> is NOT
249      * returned. This method searched for all descendants of <code>target
250      * </code> meeting the criteria using a breadth-first algorithm.
251      */

252     public static List JavaDoc getDescendantsByClassName(final Node target,
253                                                 final String JavaDoc className) {
254         List JavaDoc result = new ArrayList JavaDoc();
255         result.addAll(getChildrenByClassName(target,className));
256         for (int i = 0; i < target.getChildNodes().getLength(); i++) {
257             result.addAll(getDescendantsByClassName(
258                                             target.getChildNodes().item(i),
259                                             className));
260         }
261         return result;
262     }
263     
264     
265     
266 }
267
Popular Tags