All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.commons.xml.RangeIterator Maven / Gradle / Ivy

The newest version!
/*
 * © Copyright IBM Corp. 2012
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at:
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 * implied. See the License for the specific language governing 
 * permissions and limitations under the License.
 */

package com.ibm.commons.xml;

import java.util.*;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ranges.Range;

/**
 *      
 * Iterator over the objects in a Range.
 * 
 * @author dloverin
 * Jun 9, 2006
 * 
 */
public class RangeIterator implements ListIterator {

    private Node _currentNode;  // hold place for traversal
    private int _currentOffset; // current offset in container of _currentNode
    private Node _commonAncestor; // derived from _range
    private Range _range;
    private Map _descentPath;       // descent from the common ancestor to the starting container
    private int _currentIndex;      // index of iterator
    private boolean _firstChild;    // if true start at first child, not common ancestor
    
    /**
     * NOT READY FOR USE YET. PLEASE DO NOT USE!
     * 
     * Create a new iterator that will return each node encounters while
     * walking the dom tree from the beginning of the range to the end of
     * the range. 
     * The walk of the range begins at the common ancestor of the range endpoints
     * and continues in a depth first traversal of the tree.
     * 
     * Parent containers are included in the iteration after reaching the last child in
     * a container.
     * 
     * @param range
     */
    public RangeIterator(Range range) {
        this(range, false);
    }
    
    
    /**
     * Same iterator with an option to start the iterator at the range's first node.
     * @param range - range must be normalized.
     * @param firstChild - if true start the the range's first child instead of
     *              the common ancestor.
     */
    public RangeIterator(Range range, boolean firstChild) {
        if (range == null || range.getStartContainer() == null ||
            range.getEndContainer() == null) {
            throw new NullPointerException();
        }
        
        _range = range;
        _firstChild = firstChild;
        initIterator();
    }
    
    public boolean hasNext() {
        
        if (_commonAncestor == null) {
            return false;
        }
        
        if (_currentNode == null) {
            return true;
        }
        
        if (isAtEndOfRange(_currentNode, _currentOffset)) {
            return false;
        }
        
        return true;
    }

    public Object next() {
        
        if (_commonAncestor == null) {
            throw new NoSuchElementException();
        }
 
        if (_currentNode == null) {
            if (_firstChild) {
                _currentNode = _range.getStartContainer();
                if (_currentNode.hasChildNodes()) {
                    _currentNode = _currentNode.getChildNodes().item(_range.getStartOffset());
                    _currentOffset = 0;
                }
                else {
                    _currentOffset = _range.getStartOffset();
                }
            }
            else {
                _currentNode = _commonAncestor;
                _currentOffset = ((Integer)_descentPath.get(_commonAncestor)).intValue();
            }

            _currentIndex++;
            return _currentNode;
        }
        
        if (isAtEndOfRange(_currentNode, _currentOffset)) {
            throw new NoSuchElementException();
        }
   
        if (_currentNode.hasChildNodes()) {
            Integer offsetInt = (Integer)_descentPath.get(_currentNode);
            if (offsetInt == null) {
                _currentNode = _currentNode.getFirstChild();
                _currentIndex++;
                _currentOffset = 0;
                return _currentNode;
            }
            else {
                Node tempNode = _currentNode.getChildNodes().item(offsetInt.intValue());
                if (tempNode != null) {
                    _currentNode = tempNode;
                    _currentIndex++;
                    offsetInt = (Integer)_descentPath.get(_currentNode);
                    _currentOffset = (offsetInt == null) ? 0 : offsetInt.intValue();
                    return _currentNode;
                }
            }
        }

        // when we are at the end of the sibling for a node,
        // go to the parent and get the next sibing in the parent.
        Node nextNode = _currentNode.getNextSibling();
        _currentOffset++;
        while (nextNode == null) {
            _currentNode = _currentNode.getParentNode();
            
            if (_currentNode == _commonAncestor) { 
                throw new NoSuchElementException();
            }

            nextNode = _currentNode.getNextSibling();
            _currentOffset = 0;
        }
        
        _currentNode = nextNode;
        _currentIndex++;
        
        return _currentNode;
    }

     
    /**
     * currently not supported
     */
    public void remove() {
        throw new UnsupportedOperationException();
    }
    
    /**
     * init state of this iterator
     *
     */
    private void initIterator() {
        _currentIndex = 0;
        
        // init the common ascestor
        _commonAncestor = _range.getCommonAncestorContainer();
        
        // this can hapend when an element has been removed from the model
        // but the changes have not been flushed.
        if (_commonAncestor == null) {
            return;
        }
        
        // create a map of nodes to child index to create a path
        // to descend the tree from the common ancestor to the 
        // starting container
        _descentPath = new HashMap();
        Node node = _range.getStartContainer();
        int offset = _range.getStartOffset();
        Node ancestorParent = _commonAncestor.getParentNode();
        while (node != ancestorParent) {
            _descentPath.put(node, Integer.valueOf(offset));
            offset = getOffsetInContainer(node);
            node = node.getParentNode();
        }
        
    }
    
    /**
     * Helper method to get the offset of a given node with respect to its
     * parent.
     * 
     * @param node -
     *            may not be null
     * @return int - zero-based offset of node in its container or -1 if the
     *         node as no container.
     */
    public static int getOffsetInContainer(Node node) {
        Node parent = node.getParentNode();
        if (parent != null) {
            NodeList children = parent.getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                Node child = (Node) children.item(i);
                if (child == node) {
                    return i;
                }
            }
            throw new IllegalStateException(); // how could the child not be found?
        }

        return -1;
    }


    /**
     * NOT SUPPORTED
     * @param arg0
     */
    public void add(Object arg0) {
        throw new UnsupportedOperationException();
    }


    public boolean hasPrevious() {
        return _currentIndex == 0 ? true : false;
    }


    public int nextIndex() {
        return _currentIndex + 1;
    }


    public Object previous() {
        if (_currentIndex == 0) {
            throw new NoSuchElementException();
        }
        
        // return the current node
        Node retNode = _currentNode;
        
        // update the position of the current node for the next operation
        Node prev = _currentNode.getPreviousSibling();
        if (prev == null) {
            _currentNode = _currentNode.getParentNode();
        }
        else {
            // get the last child of this node if it has children
            if (prev.hasChildNodes()) {
                NodeList children = prev.getChildNodes();
                _currentNode = children.item(children.getLength() - 1);
            }
            else {
                _currentNode = prev;
            }

        }
        
        _currentIndex--;
        
        return retNode;
    }


    public int previousIndex() {
        return _currentIndex - 1;
    }


    /**
     * NOT SUPPORTED
     * 
     * @param arg0
     */
    public void set(Object arg0) {
        throw new UnsupportedOperationException();        
    }   

    
    /**
     * Test if node is at the end of the range of this iterator.
     * @param node
     * @return true node is the last node in the range.
     */
    private boolean isAtEndOfRange(Node node, int offset) {

        // don't compare offsets on the text node since we are jumping node to node.
        if (node.getNodeType() == Node.TEXT_NODE && node == _range.getEndContainer()) {
            return true;
        }
        
        int result = DOMUtil.compareLocations(node, offset, _range.getEndContainer(), _range.getEndOffset());
        if (result >= 0) {
            return true;
        }
 
        Node currentNode = node;
        Node endNode = _range.getEndContainer();

        if (currentNode.hasChildNodes()) {
            return false;
        }
        
        // check if all the parents between the current node and the end node are at their last child.
        // if so then we are done.
        Node parent = currentNode.getParentNode();
        while (parent != null) {
            NodeList children = parent.getChildNodes();
            if (children.item(children.getLength() - 1) != currentNode) {
                break;
            }
            
            if (parent == endNode) {
                return true;    // found no nodes to iterate to
            }
            
            currentNode = parent;
            parent = currentNode.getParentNode();
        }
        
        return false;

    }
    
    
    
    /**
     * Map a node and offset into an actual node.
     * 
     * @param node
     * @param offset
     * @return
     */
//    private Node getEffectiveNode(Node node, int offset) {
//        if (node.hasChildNodes()) {
//            NodeList children = node.getChildNodes();
//            if (offset == children.getLength()) {
//                offset--;
//            }
//            node = children.item(offset);
//        }
//        
//        return node;
//    }
//    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy