001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.jxpath.ri.model.jdom;
018    
019    import java.util.Collections;
020    import java.util.List;
021    
022    import org.apache.commons.jxpath.ri.compiler.NodeTest;
023    import org.apache.commons.jxpath.ri.model.NodeIterator;
024    import org.apache.commons.jxpath.ri.model.NodePointer;
025    import org.jdom.Document;
026    import org.jdom.Element;
027    
028    /**
029     * An iterator of children of a JDOM Node.
030     *
031     * @author Dmitri Plotnikov
032     * @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
033     */
034    public class JDOMNodeIterator implements NodeIterator {
035        private NodePointer parent;
036        private NodeTest nodeTest;
037    
038        private boolean reverse;
039        private int position = 0;
040        private int index = 0;
041        private List children;
042        private Object child;
043    
044        /**
045         * Create a new JDOMNodeIterator.
046         * @param parent pointer
047         * @param nodeTest test
048         * @param reverse whether to iterate in reverse
049         * @param startWith starting pointer
050         */
051        public JDOMNodeIterator(
052                NodePointer parent, NodeTest nodeTest,
053                boolean reverse, NodePointer startWith) {
054            this.parent = parent;
055            if (startWith != null) {
056                this.child = startWith.getNode();
057            }
058            // TBD: optimize me for different node tests
059            Object node = parent.getNode();
060            if (node instanceof Document) {
061                this.children = ((Document) node).getContent();
062            }
063            else if (node instanceof Element) {
064                this.children = ((Element) node).getContent();
065            }
066            else {
067                this.children = Collections.EMPTY_LIST;
068            }
069            this.nodeTest = nodeTest;
070            this.reverse = reverse;
071        }
072    
073        public NodePointer getNodePointer() {
074            if (child == null) {
075                if (!setPosition(1)) {
076                    return null;
077                }
078                position = 0;
079            }
080    
081            return new JDOMNodePointer(parent, child);
082        }
083    
084        public int getPosition() {
085            return position;
086        }
087    
088        public boolean setPosition(int position) {
089            while (this.position < position) {
090                if (!next()) {
091                    return false;
092                }
093            }
094            while (this.position > position) {
095                if (!previous()) {
096                    return false;
097                }
098            }
099            return true;
100        }
101    
102        /**
103         * This is actually never invoked during the normal evaluation
104         * of xpaths - an iterator is always going forward, never backwards.
105         * So, this is implemented only for completeness and perhaps for
106         * those who use these iterators outside of XPath evaluation.
107         * @return boolean
108         */
109        private boolean previous() {
110            position--;
111            if (!reverse) {
112                while (--index >= 0) {
113                    child = children.get(index);
114                    if (testChild()) {
115                        return true;
116                    }
117                }
118            }
119            else {
120                for (; index < children.size(); index++) {
121                    child = children.get(index);
122                    if (testChild()) {
123                        return true;
124                    }
125                }
126            }
127            return false;
128        }
129    
130        /**
131         * Iterate to next pointer.
132         * @return whether valid
133         */
134        private boolean next() {
135            position++;
136            if (!reverse) {
137                if (position == 1) {
138                    index = 0;
139                    if (child != null) {
140                        index = children.indexOf(child) + 1;
141                    }
142                }
143                else {
144                    index++;
145                }
146                for (; index < children.size(); index++) {
147                    child = children.get(index);
148                    if (testChild()) {
149                        return true;
150                    }
151                }
152                return false;
153            }
154            else {
155                if (position == 1) {
156                    index = children.size() - 1;
157                    if (child != null) {
158                        index = children.indexOf(child) - 1;
159                    }
160                }
161                else {
162                    index--;
163                }
164                for (; index >= 0; index--) {
165                    child = children.get(index);
166                    if (testChild()) {
167                        return true;
168                    }
169                }
170                return false;
171            }
172        }
173    
174        /**
175         * Test a child node.
176         * @return whether test passes.
177         */
178        private boolean testChild() {
179            return JDOMNodePointer.testNode(parent, child, nodeTest);
180        }
181    }