/* * eXist Open Source Native XML Database * Copyright (C) 2001-2014 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ */ package org.exist.dom.persistent; import org.exist.numbering.NodeId; import org.exist.xquery.XPathException; import org.exist.xquery.value.Item; import org.exist.xquery.value.SequenceIterator; import java.util.Iterator; import java.util.Stack; public class AVLTreeNodeSet extends AbstractNodeSet { private Node root; private int size = 0; private int state = 0; /* (non-Javadoc) * @see org.exist.dom.persistent.NodeSet#iterate() */ @Override public SequenceIterator iterate() throws XPathException { return new InorderTraversal(); } @Override public SequenceIterator unorderedIterator() throws XPathException { return new InorderTraversal(); } @Override public void addAll(final NodeSet other) { for(final Iterator<NodeProxy> i = other.iterator(); i.hasNext(); ) { add(i.next()); } } @Override public int getLength() { return size; } //TODO : evaluate both semantics @Override public int getItemCount() { return size; } //TODO could we not just use itemAt(index) here or get(pos)? @Override public org.w3c.dom.Node item(final int pos) { final NodeProxy proxy = get(pos); return proxy == null ? null : proxy.getNode(); } @Override public NodeProxy get(final int pos) { return (NodeProxy) itemAt(pos); } @Override public final NodeProxy get(final NodeProxy p) { final Node n = searchData(p); return n == null ? null : n.getData(); } @Override public boolean isEmpty() { return size == 0; } @Override public boolean hasOne() { return size == 1; } @Override public Item itemAt(final int pos) { int i = 0; for(final Iterator<NodeProxy> it = iterator(); it.hasNext(); i++) { final NodeProxy p = it.next(); if(i == pos) { return p; } } return null; } @Override public final void add(final NodeProxy proxy) { if(proxy == null) { return; } setHasChanged(); if(root == null) { root = new Node(proxy); ++size; return; } Node tempNode = root; while(true) { final int c = tempNode.data.compareTo(proxy); if(c == 0) { return; } if(c > 0) { // inserts s into left subtree. if(tempNode.hasLeftChild()) { tempNode = tempNode.leftChild; continue; } final Node newNode = tempNode.addLeft(proxy); balance(newNode); ++size; return; } // inserts s to right subtree if(tempNode.hasRightChild()) { tempNode = tempNode.rightChild; continue; } final Node newNode = tempNode.addRight(proxy); balance(newNode); ++size; return; } } public Node getMinNode() { if(root == null) { return null; } Node tempNode = root; while(tempNode.hasLeftChild()) { tempNode = tempNode.getLeftChild(); } return tempNode; } public Node getMaxNode() { if(root == null) { return null; } Node tempNode = root; while(tempNode.hasRightChild()) { tempNode = tempNode.getRightChild(); } return tempNode; } private void balance(final Node node) { Node currentNode, currentParent; currentNode = node; currentParent = node.parent; while(currentNode != root) { final int h = currentParent.height; currentParent.setHeight(); if(h == currentParent.height) { return; } // Case 1 if(currentParent.balanced()) { currentNode = currentParent; currentParent = currentNode.parent; continue; } if(currentParent.leftHeight() - currentParent.rightHeight() == 2) { Node nodeA = currentParent, nodeB = nodeA.getLeftChild(), //nodeC = nodeB.getLeftChild(), nodeD = nodeB.getRightChild(); if(nodeB.leftHeight() > nodeB.rightHeight()) { // right rotation for Case 2 nodeA.addLeftChild(nodeD); if(nodeA != root) { if(nodeA.isLeftChild()) { nodeA.parent.addLeftChild(nodeB); } else { nodeA.parent.addRightChild(nodeB); } } else { root = nodeB; } nodeB.addRightChild(nodeA); nodeA.setHeight(); nodeB.setHeight(); currentNode = nodeB; currentParent = currentNode.parent; continue; } // Case 3 and Case 4 if(nodeD.hasLeftChild()) { nodeB.addRightChild(nodeD.getLeftChild()); } else { nodeB.removeRightChild(); } if(nodeD.hasRightChild()) { nodeA.addLeftChild(nodeD.getRightChild()); } else { nodeA.removeLeftChild(); } if(currentParent != root) { if(nodeA.isLeftChild()) { nodeA.parent.addLeftChild(nodeD); } else { nodeA.parent.addRightChild(nodeD); } } else { root = nodeD; } nodeD.addLeftChild(nodeB); nodeD.addRightChild(nodeA); nodeB.setHeight(); nodeA.setHeight(); nodeD.setHeight(); currentNode = nodeD; currentParent = currentNode.parent; continue; } if(currentParent.leftHeight() - currentParent.rightHeight() == -2) { final Node nodeA = currentParent; Node nodeB = nodeA.getRightChild(); Node nodeC = nodeB.getLeftChild(); //Node nodeD = nodeB.getRightChild(); if(nodeB.leftHeight() < nodeB.rightHeight()) { // left rotation for Case 2 nodeA.addRightChild(nodeC); if(nodeA != root) { if(nodeA.isLeftChild()) { nodeA.parent.addLeftChild(nodeB); } else { nodeA.parent.addRightChild(nodeB); } } else { root = nodeB; } nodeB.addLeftChild(nodeA); nodeA.setHeight(); nodeB.setHeight(); currentNode = nodeB; currentParent = currentNode.parent; continue; } // Case 3 and Case 4 if(nodeC.hasLeftChild()) { nodeA.addRightChild(nodeC.getLeftChild()); } else { nodeA.removeRightChild(); } if(nodeC.hasRightChild()) { nodeB.addLeftChild(nodeC.getRightChild()); } else { nodeB.removeLeftChild(); } if(nodeA != root) { if(nodeA.isLeftChild()) { nodeA.parent.addLeftChild(nodeC); } else { nodeA.parent.addRightChild(nodeC); } } else { root = nodeC; } nodeC.addLeftChild(nodeA); nodeC.addRightChild(nodeB); nodeB.setHeight(); nodeA.setHeight(); nodeC.setHeight(); currentNode = nodeC; currentParent = currentNode.parent; continue; } } } public final Node searchData(final NodeProxy proxy) { if(root == null) { return null; } Node tempNode = root; while(tempNode != null) { final int c = tempNode.data.compareTo(proxy); if(c == 0) { return tempNode; } if(c < 0) { tempNode = tempNode.rightChild; } else { tempNode = tempNode.leftChild; } } return null; } @Override public final NodeProxy get(final DocumentImpl doc, final NodeId nodeId) { if(root == null) { return null; } Node tempNode = root; int cmp; while(tempNode != null) { if(tempNode.data.getOwnerDocument().getDocId() == doc.getDocId()) { cmp = tempNode.data.getNodeId().compareTo(nodeId); if(cmp == 0) { return tempNode.data; } else if(cmp < 0) { tempNode = tempNode.rightChild; } else { tempNode = tempNode.leftChild; } } else if(tempNode.data.getOwnerDocument().getDocId() < doc.getDocId()) { tempNode = tempNode.rightChild; } else { tempNode = tempNode.leftChild; } } return null; } @Override public final boolean contains(final NodeProxy proxy) { return searchData(proxy) != null; } public void removeNode(final Node node) { --size; Node tempNode = node; while(tempNode.hasLeftChild() || tempNode.hasRightChild()) { if(tempNode.hasLeftChild()) { tempNode = tempNode.getLeftChild(); while(tempNode.hasRightChild()) { tempNode = tempNode.getRightChild(); } } else { tempNode = tempNode.getRightChild(); while(tempNode.hasLeftChild()) { tempNode = tempNode.getLeftChild(); } } node.setData(tempNode.getData()); } if(tempNode == root) { root = null; return; } final Node parent = tempNode.parent; if(tempNode.isLeftChild()) { parent.removeLeftChild(); if(parent.hasRightChild()) { balance(parent.getRightChild()); } else { balance(parent); } } else { parent.removeRightChild(); if(parent.hasLeftChild()) { balance(parent.getLeftChild()); } else { balance(parent); } } } @Override public NodeSetIterator iterator() { return new InorderTraversal(); } @Override public boolean hasChanged(final int previousState) { return state != previousState; } @Override public int getState() { return state; } @Override public boolean isCacheable() { return true; } private void setHasChanged() { state = (state == Integer.MAX_VALUE ? 0 : state + 1); } class InorderTraversal implements NodeSetIterator, SequenceIterator { private final Stack<Node> nodes = new Stack<>(); public InorderTraversal() { if(root != null) { Node tempNode = root; do { nodes.push(tempNode); tempNode = tempNode.leftChild; } while(tempNode != null); } } @Override public boolean hasNext() { if(nodes.size() == 0) { return false; } return true; } @Override public NodeProxy next() { if(nodes.isEmpty()) { return null; } final Node currentNode = nodes.peek(); nodes.pop(); if(currentNode.hasRightChild()) { Node tempNode = currentNode.rightChild; do { nodes.push(tempNode); tempNode = tempNode.leftChild; } while(tempNode != null); } return currentNode.getData(); } @Override public NodeProxy peekNode() { if(nodes.isEmpty()) { return null; } final Node currentNode = nodes.peek(); return currentNode.getData(); } @Override public void setPosition(final NodeProxy proxy) { final Node n = searchData(proxy); nodes.clear(); if(n != null) { Node tempNode = n; do { nodes.push(tempNode); tempNode = tempNode.leftChild; } while(tempNode != null); } } @Override public void remove() { throw new UnsupportedOperationException("remove is not supported on InorderTraversal"); } @Override public Item nextItem() { if(nodes.isEmpty()) { return null; } final Node currentNode = nodes.peek(); nodes.pop(); if(currentNode.hasRightChild()) { Node tempNode = currentNode.rightChild; do { nodes.push(tempNode); tempNode = tempNode.leftChild; } while(tempNode != null); } return currentNode.getData(); } } @Override public String toString() { return "AVLTree#" + super.toString(); } private static final class Node { private NodeProxy data; private Node parent; private Node leftChild; private Node rightChild; private int height; public Node(final NodeProxy data) { this.data = data; } public void setData(final NodeProxy data) { this.data = data; } public NodeProxy getData() { return data; } public boolean hasLeftChild() { return (leftChild != null); } public boolean hasRightChild() { return (rightChild != null); } public Node getLeftChild() { return leftChild; } public Node getRightChild() { return rightChild; } public boolean balanced() { return (Math.abs(leftHeight() - rightHeight()) <= 1); } public Node addLeft(final NodeProxy data) { final Node tempNode = new Node(data); this.leftChild = tempNode; tempNode.parent = this; return tempNode; } public Node addLeftChild(final Node node) { this.leftChild = node; if(node != null) { node.parent = this; } return node; } public Node addRight(final NodeProxy data) { final Node tempNode = new Node(data); this.rightChild = tempNode; tempNode.parent = this; return tempNode; } public Node addRightChild(final Node node) { this.rightChild = node; if(node != null) { node.parent = this; } return node; } public Node removeLeftChild() { final Node tempNode = leftChild; this.leftChild = null; return tempNode; } public Node removeRightChild() { final Node tempNode = rightChild; this.rightChild = null; return tempNode; } public int degree() { int i = 0; if(leftChild != null) { i++; } if(rightChild != null) { i++; } return i; } public void setHeight() { this.height = Math.max(leftHeight(), rightHeight()); } public boolean isLeftChild() { return (this == parent.leftChild); } public boolean isRightChild() { return (this == parent.rightChild); } public int leftHeight() { if(hasLeftChild()) { return (1 + leftChild.height); } return 0; } public int rightHeight() { if(hasRightChild()) { return (1 + rightChild.height); } return 0; } public int height() { return height; } } }