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
018package org.apache.commons.jxpath.ri.model.dom;
019
020import org.apache.commons.jxpath.ri.compiler.NodeTest;
021import org.apache.commons.jxpath.ri.model.NodeIterator;
022import org.apache.commons.jxpath.ri.model.NodePointer;
023import org.w3c.dom.Node;
024
025/**
026 * An iterator of children of a DOM Node.
027 */
028public class DOMNodeIterator implements NodeIterator {
029
030    private final NodePointer parent;
031    private final NodeTest nodeTest;
032    private final Node node;
033    private Node child;
034    private final boolean reverse;
035    private int position;
036
037    /**
038     * Constructs a new DOMNodeIterator.
039     *
040     * @param parent    parent pointer
041     * @param nodeTest  test
042     * @param reverse   whether to iterate in reverse
043     * @param startWith starting pointer
044     */
045    public DOMNodeIterator(final NodePointer parent, final NodeTest nodeTest, final boolean reverse, final NodePointer startWith) {
046        this.parent = parent;
047        this.node = (Node) parent.getNode();
048        if (startWith != null) {
049            this.child = (Node) startWith.getNode();
050        }
051        this.nodeTest = nodeTest;
052        this.reverse = reverse;
053    }
054
055    @Override
056    public NodePointer getNodePointer() {
057        if (position == 0) {
058            setPosition(1);
059        }
060        return child == null ? null : new DOMNodePointer(parent, child);
061    }
062
063    @Override
064    public int getPosition() {
065        return position;
066    }
067
068    /**
069     * Sets the next position.
070     *
071     * @return whether valid
072     */
073    private boolean next() {
074        position++;
075        if (!reverse) {
076            if (position == 1 && child == null) {
077                child = node.getFirstChild();
078            } else {
079                child = child.getNextSibling();
080            }
081            while (child != null && !testChild()) {
082                child = child.getNextSibling();
083            }
084        } else {
085            if (position == 1 && child == null) {
086                child = node.getLastChild();
087            } else {
088                child = child.getPreviousSibling();
089            }
090            while (child != null && !testChild()) {
091                child = child.getPreviousSibling();
092            }
093        }
094        return child != null;
095    }
096
097    /**
098     * Sets the previous position.
099     *
100     * @return whether valid
101     */
102    private boolean previous() {
103        position--;
104        if (!reverse) {
105            if (position == 0) {
106                child = null;
107            } else if (child == null) {
108                child = node.getLastChild();
109            } else {
110                child = child.getPreviousSibling();
111            }
112            while (child != null && !testChild()) {
113                child = child.getPreviousSibling();
114            }
115        } else {
116            child = child.getNextSibling();
117            while (child != null && !testChild()) {
118                child = child.getNextSibling();
119            }
120        }
121        return child != null;
122    }
123
124    @Override
125    public boolean setPosition(final int position) {
126        while (this.position < position) {
127            if (!next()) {
128                return false;
129            }
130        }
131        while (this.position > position) {
132            if (!previous()) {
133                return false;
134            }
135        }
136        return true;
137    }
138
139    /**
140     * Test child.
141     *
142     * @return result of the test
143     */
144    private boolean testChild() {
145        return DOMNodePointer.testNode(child, nodeTest);
146    }
147}