001    /*
002     * $Id: DOMCategory.java 4201 2006-11-05 10:23:50Z paulk $version Apr 25, 2004 5:18:30 PM $user Exp $
003     * 
004     * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
005     * 
006     * Redistribution and use of this software and associated documentation
007     * ("Software"), with or without modification, are permitted provided that the
008     * following conditions are met: 1. Redistributions of source code must retain
009     * copyright statements and notices. Redistributions must also contain a copy of
010     * this document. 2. Redistributions in binary form must reproduce the above
011     * copyright notice, this list of conditions and the following disclaimer in the
012     * documentation and/or other materials provided with the distribution. 3. The
013     * name "groovy" must not be used to endorse or promote products derived from
014     * this Software without prior written permission of The Codehaus. For written
015     * permission, please contact info@codehaus.org. 4. Products derived from this
016     * Software may not be called "groovy" nor may "groovy" appear in their names
017     * without prior written permission of The Codehaus. "groovy" is a registered
018     * trademark of The Codehaus. 5. Due credit should be given to The Codehaus -
019     * http://groovy.codehaus.org/
020     * 
021     * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
022     * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
024     * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
025     * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
026     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
027     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
028     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
029     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
030     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031     *  
032     */
033    package groovy.xml.dom;
034    
035    import org.w3c.dom.*;
036    import org.codehaus.groovy.runtime.DefaultGroovyMethods;
037    
038    import java.util.Iterator;
039    import java.util.List;
040    import java.util.ArrayList;
041    import java.util.Collection;
042    
043    /**
044     * @author sam
045     * @author paulk
046     */
047    public class DOMCategory {
048    
049        private static boolean trimWhitespace = true;
050    
051        public static Object get(Object o, String elementName) {
052            if (o instanceof Element) {
053                return get((Element) o, elementName);
054            }
055            if (o instanceof NodeList) {
056                return get((NodeList) o, elementName);
057            }
058            if (o instanceof NamedNodeMap) {
059                return get((NamedNodeMap) o, elementName);
060            }
061            return null;
062        }
063    
064        private static Object get(Element element, String elementName) {
065            return getAt(element, elementName);
066        }
067    
068        private static Object get(NodeList nodeList, String elementName) {
069            return getAt(nodeList, elementName);
070        }
071    
072        private static Object get(NamedNodeMap nodeMap, String elementName) {
073            return getAt(nodeMap, elementName);
074        }
075    
076        private static Object getAt(Element element, String elementName) {
077            if ("..".equals(elementName)) {
078                return parent(element);
079            }
080            if ("**".equals(elementName)) {
081                return depthFirst(element);
082            }
083            if (elementName.startsWith("@")) {
084                return element.getAttribute(elementName.substring(1));
085            }
086            return getChildElements(element, elementName);
087        }
088    
089        private static Object getAt(NodeList nodeList, String elementName) {
090            List results = new ArrayList();
091            for (int i = 0; i < nodeList.getLength(); i++) {
092                Node node = nodeList.item(i);
093                if (node instanceof Element) {
094                    addResult(results, get(node, elementName));
095                }
096            }
097            if (elementName.startsWith("@")) {
098                return results;
099            }
100            return new NodeListsHolder(results);
101        }
102    
103        public static NamedNodeMap attributes(Element element) {
104            return element.getAttributes();
105        }
106    
107        private static String getAt(NamedNodeMap namedNodeMap, String elementName) {
108            Attr a = (Attr) namedNodeMap.getNamedItem(elementName);
109            return a.getValue();
110        }
111    
112        public static int size(NamedNodeMap namedNodeMap) {
113            return namedNodeMap.getLength();
114        }
115    
116        public static Node getAt(Element element, int i) {
117            if (hasChildElements(element, "*")) {
118                NodeList nodeList = getChildElements(element, "*");
119                return nodeList.item(i);
120            }
121            return null;
122        }
123    
124        public static Node getAt(NodeList nodeList, int i) {
125            if (i >= 0 && i < nodeList.getLength()) {
126                return nodeList.item(i);
127            }
128            return null;
129        }
130    
131        public static String name(Element element) {
132            return element.getNodeName();
133        }
134    
135        public static Node parent(Node node) {
136            return node.getParentNode();
137        }
138    
139        public static String text(Object o) {
140            if (o instanceof Element) {
141                return text((Element) o);
142            }
143            if (o instanceof Node) {
144                Node n = (Node) o;
145                if (n.getNodeType() == Node.TEXT_NODE) {
146                    return n.getNodeValue();
147                }
148            }
149            if (o instanceof NodeList) {
150                return text((NodeList) o);
151            }
152            return null;
153        }
154    
155        private static String text(Element element) {
156            if (!element.hasChildNodes()) {
157                return "";
158            }
159            if (element.getFirstChild().getNodeType() != Node.TEXT_NODE) {
160                return "";
161            }
162            return element.getFirstChild().getNodeValue();
163        }
164    
165        private static String text(NodeList nodeList) {
166            StringBuffer sb = new StringBuffer();
167            for (int i = 0; i < nodeList.getLength(); i++) {
168                sb.append(text(nodeList.item(i)));
169            }
170            return sb.toString();
171        }
172    
173        public static List list(NodeList self) {
174            List answer = new ArrayList();
175            Iterator it = DefaultGroovyMethods.iterator(self);
176            while (it.hasNext()) {
177                answer.add(it.next());
178            }
179            return answer;
180        }
181    
182        public static NodeList depthFirst(Element self) {
183            List result = new ArrayList();
184            result.add(createNodeList(self));
185            result.add(self.getElementsByTagName("*"));
186            return new NodeListsHolder(result);
187        }
188    
189        private static NodeList createNodeList(Element self) {
190            List first = new ArrayList();
191            first.add(self);
192            return new NodesHolder(first);
193        }
194    
195        public static NodeList breadthFirst(Element self) {
196            List result = new ArrayList();
197            NodeList thisLevel = createNodeList(self);
198            while (thisLevel.getLength() > 0) {
199                result.add(thisLevel);
200                thisLevel = getNextLevel(thisLevel);
201            }
202            return new NodeListsHolder(result);
203        }
204    
205        private static NodeList getNextLevel(NodeList thisLevel) {
206            List result = new ArrayList();
207            for (int i = 0; i < thisLevel.getLength(); i++) {
208                Node n = thisLevel.item(i);
209                if (n instanceof Element) {
210                    result.add(getChildElements((Element) n, "*"));
211                }
212            }
213            return new NodeListsHolder(result);
214        }
215    
216        public static NodeList children(Element self) {
217            return getChildElements(self, "*");
218        }
219    
220        private static boolean hasChildElements(Element self, String elementName) {
221            return getChildElements(self, elementName).getLength() > 0;
222        }
223    
224        private static NodeList getChildElements(Element self, String elementName) {
225            List result = new ArrayList();
226            NodeList nodeList = self.getChildNodes();
227            for (int i = 0; i < nodeList.getLength(); i++) {
228                Node node = nodeList.item(i);
229                if (node.getNodeType() == Node.ELEMENT_NODE) {
230                    Element child = (Element) node;
231                    if ("*".equals(elementName) || child.getTagName().equals(elementName)) {
232                        result.add(child);
233                    }
234                } else if (node.getNodeType() == Node.TEXT_NODE) {
235                    String value = node.getNodeValue();
236                    if (trimWhitespace) {
237                        value = value.trim();
238                    }
239                    if ("*".equals(elementName) && value.length() > 0) {
240                        node.setNodeValue(value);
241                        result.add(node);
242                    }
243                }
244            }
245            return new NodesHolder(result);
246        }
247    
248        public static String toString(Object o) {
249            if (o instanceof Node) {
250                if (((Node) o).getNodeType() == Node.TEXT_NODE) {
251                    return ((Node) o).getNodeValue();
252                }
253            }
254            if (o instanceof NodeList) {
255                return toString((NodeList) o);
256            }
257            return o.toString();
258        }
259    
260        private static String toString(NodeList self) {
261            StringBuffer sb = new StringBuffer();
262            sb.append("[");
263            Iterator it = DefaultGroovyMethods.iterator(self);
264            while (it.hasNext()) {
265                if (sb.length() > 1) sb.append(", ");
266                sb.append(it.next().toString());
267            }
268            sb.append("]");
269            return sb.toString();
270        }
271    
272        public static int size(NodeList self) {
273            return self.getLength();
274        }
275    
276        public static boolean isEmpty(NodeList self) {
277            return size(self) == 0;
278        }
279    
280        private static void addResult(List results, Object result) {
281            if (result != null) {
282                if (result instanceof Collection) {
283                    results.addAll((Collection) result);
284                } else {
285                    results.add(result);
286                }
287            }
288        }
289    
290        private static class NodeListsHolder implements NodeList {
291            private List nodeLists;
292    
293            private NodeListsHolder(List nodeLists) {
294                this.nodeLists = nodeLists;
295            }
296    
297            public int getLength() {
298                int length = 0;
299                for (int i = 0; i < nodeLists.size(); i++) {
300                    NodeList nl = (NodeList) nodeLists.get(i);
301                    length += nl.getLength();
302                }
303                return length;
304            }
305    
306            public Node item(int index) {
307                int relativeIndex = index;
308                for (int i = 0; i < nodeLists.size(); i++) {
309                    NodeList nl = (NodeList) nodeLists.get(i);
310                    if (relativeIndex < nl.getLength()) {
311                        return nl.item(relativeIndex);
312                    }
313                    relativeIndex -= nl.getLength();
314                }
315                return null;
316            }
317    
318            public String toString() {
319                return DOMCategory.toString(this);
320            }
321        }
322    
323        private static class NodesHolder implements NodeList {
324            private List nodes;
325    
326            private NodesHolder(List nodes) {
327                this.nodes = nodes;
328            }
329    
330            public int getLength() {
331                return nodes.size();
332            }
333    
334            public Node item(int index) {
335                if (index < 0 || index >= getLength()) {
336                    return null;
337                }
338                return (Node) nodes.get(index);
339            }
340        }
341    }