    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.jdom;
    019
    020import java.util.Collections;
    021import java.util.List;
    022
    023import org.apache.commons.jxpath.ri.compiler.NodeTest;
    024import org.apache.commons.jxpath.ri.model.NodeIterator;
    025import org.apache.commons.jxpath.ri.model.NodePointer;
    026import org.jdom.Document;
    027import org.jdom.Element;
    028
    029/**
    030 * An iterator of children of a JDOM Node.
    031 */
    032public class JDOMNodeIterator implements NodeIterator {
    033
    034    private final NodePointer parent;
    035    private final NodeTest nodeTest;
    036    private final boolean reverse;
    037    private int position;
    038    private int index;
    039    private List children;
    040    private Object child;
    041
    042    /**
    043     * Constructs a new JDOMNodeIterator.
    044     *
    045     * @param parent    pointer
    046     * @param nodeTest  test
    047     * @param reverse   whether to iterate in reverse
    048     * @param startWith starting pointer
    049     */
    050    public JDOMNodeIterator(final NodePointer parent, final NodeTest nodeTest, final boolean reverse, final NodePointer startWith) {
    051        this.parent = parent;
    052        if (startWith != null) {
    053            this.child = startWith.getNode();
    054        }
    055        // TBD: optimize me for different node tests
    056        final Object node = parent.getNode();
    057        if (node instanceof Document) {
    058            this.children = ((Document) node).getContent();
    059        } else if (node instanceof Element) {
    060            this.children = ((Element) node).getContent();
    061        } else {
    062            this.children = Collections.EMPTY_LIST;
    063        }
    064        this.nodeTest = nodeTest;
    065        this.reverse = reverse;
    066    }
    067
    068    @Override
    069    public NodePointer getNodePointer() {
    070        if (child == null) {
    071            if (!setPosition(1)) {
    072                return null;
    073            }
    074            position = 0;
    075        }
    076        return new JDOMNodePointer(parent, child);
    077    }
    078
    079    @Override
    080    public int getPosition() {
    081        return position;
    082    }
    083
    084    /**
    085     * Iterate to next pointer.
    086     *
    087     * @return whether valid
    088     */
    089    private boolean next() {
    090        position++;
    091        if (!reverse) {
    092            if (position == 1) {
    093                index = 0;
    094                if (child != null) {
    095                    index = children.indexOf(child) + 1;
    096                }
    097            } else {
    098                index++;
    099            }
    100            for (; index < children.size(); index++) {
    101                child = children.get(index);
    102                if (testChild()) {
    103                    return true;
    104                }
    105            }
    106            return false;
    107        }
    108        if (position == 1) {
    109            index = children.size() - 1;
    110            if (child != null) {
    111                index = children.indexOf(child) - 1;
    112            }
    113        } else {
    114            index--;
    115        }
    116        for (; index >= 0; index--) {
    117            child = children.get(index);
    118            if (testChild()) {
    119                return true;
    120            }
    121        }
    122        return false;
    123    }
    124
    125    /**
    126     * This is actually never invoked during the normal evaluation of xpaths - an iterator is always going forward, never backwards. So, this is implemented
    127     * only for completeness and perhaps for those who use these iterators outside of XPath evaluation.
    128     *
    129     * @return boolean
    130     */
    131    private boolean previous() {
    132        position--;
    133        if (!reverse) {
    134            while (--index >= 0) {
    135                child = children.get(index);
    136                if (testChild()) {
    137                    return true;
    138                }
    139            }
    140        } else {
    141            for (; index < children.size(); index++) {
    142                child = children.get(index);
    143                if (testChild()) {
    144                    return true;
    145                }
    146            }
    147        }
    148        return false;
    149    }
    150
    151    @Override
    152    public boolean setPosition(final int position) {
    153        while (this.position < position) {
    154            if (!next()) {
    155                return false;
    156            }
    157        }
    158        while (this.position > position) {
    159            if (!previous()) {
    160                return false;
    161            }
    162        }
    163        return true;
    164    }
    165
    166    /**
    167     * Test a child node.
    168     *
    169     * @return whether test passes.
    170     */
    171    private boolean testChild() {
    172        return JDOMNodePointer.testNode(parent, child, nodeTest);
    173    }
    174}