/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.delta;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class SVNRangeTree {
private SVNRangeTreeNode myRoot = null;
private SVNRangeTreeNode myFreeTreeNodes;
private SVNRangeTreeNode myAllocatedTreeNodes;
private SVNRangeListNode myFreeListNodes;
public static class SVNRangeTreeNode {
public SVNRangeTreeNode(int offset, int limit, int target) {
this.offset = offset;
this.limit = limit;
this.targetOffset = target;
}
public String toString() {
String str = offset + ":" + limit + ":" + targetOffset;
return str;
}
public int offset;
public int limit;
public int targetOffset;
public SVNRangeTreeNode left;
public SVNRangeTreeNode right;
public SVNRangeTreeNode prev;
public SVNRangeTreeNode next;
public SVNRangeTreeNode nextFree;
}
private SVNRangeTreeNode allocateTreeNode(int offset, int limit, int target) {
if (myFreeTreeNodes == null) {
SVNRangeTreeNode node = new SVNRangeTreeNode(offset, limit, target);
node.nextFree = myAllocatedTreeNodes;
myAllocatedTreeNodes = node;
return node;
}
SVNRangeTreeNode node = myFreeTreeNodes;
myFreeTreeNodes = node.nextFree;
node.left = node.right = node.next = node.prev = null;
node.offset = offset;
node.limit = limit;
node.targetOffset = target;
// make it head of the allocated list.
node.nextFree = myAllocatedTreeNodes;
myAllocatedTreeNodes = node;
return node;
}
private void freeTreeNode(SVNRangeTreeNode node) {
if (node.next != null) {
node.next.prev = node.prev;
}
if (node.prev != null) {
node.prev.next = node.next;
}
node.next = null;
node.prev = null;
node.left = null;
node.right = null;
// remove if from the allocated list, it has to be there.
if (myAllocatedTreeNodes == node) {
myAllocatedTreeNodes = myAllocatedTreeNodes.nextFree;
} else {
SVNRangeTreeNode allocated = myAllocatedTreeNodes;
while(allocated.nextFree != node) {
allocated = allocated.nextFree;
}
allocated.nextFree = node.nextFree;
}
// make it head of the free nodes list.
node.nextFree = myFreeTreeNodes;
myFreeTreeNodes = node;
}
private SVNRangeListNode allocateListNode(int kind, int offset, int limit, int target) {
if (myFreeListNodes == null) {
return new SVNRangeListNode(kind, offset, limit, target);
}
SVNRangeListNode node = myFreeListNodes;
myFreeListNodes = node.next;
node.offset = offset;
node.limit = limit;
node.targetOffset = target;
node.kind = kind;
node.prev = node.next = null;
node.head = node;
return node;
}
public void disposeList(SVNRangeListNode head) {
SVNRangeListNode n = head;
while(head.next != null) {
head = head.next;
}
head.next = myFreeListNodes;
myFreeListNodes = n;
}
public void dispose() {
SVNRangeTreeNode node = myFreeTreeNodes;
if (node == null) {
myFreeTreeNodes = myAllocatedTreeNodes;
} else {
while(node.nextFree != null) {
node = node.nextFree;
}
node.nextFree = myAllocatedTreeNodes;
}
myAllocatedTreeNodes = null;
myRoot = null;
}
public static class SVNRangeListNode {
public static int FROM_SOURCE = 0;
public static int FROM_TARGET = 1;
public SVNRangeListNode(int kind, int offset, int limit, int target) {
this.kind = kind;
this.offset = offset;
this.limit = limit;
this.targetOffset = target;
this.head = this;
}
public SVNRangeListNode append(SVNRangeListNode node) {
this.next = node;
node.prev = this;
node.head = this.head;
return node;
}
public int kind;
public int offset;
public int limit;
public int targetOffset;
public SVNRangeListNode prev;
public SVNRangeListNode next;
public SVNRangeListNode head;
}
public SVNRangeListNode buildRangeList(int offset, int limit) throws SVNException {
SVNRangeListNode tail = null;
SVNRangeTreeNode node = myRoot;
while(offset < limit) {
if (node == null) {
return appendToRangeList(SVNRangeListNode.FROM_SOURCE, offset, limit, 0, tail);
}
if (offset < node.offset) {
if (limit <= node.offset) {
return appendToRangeList(SVNRangeListNode.FROM_SOURCE, offset, limit, 0, tail);
}
tail = appendToRangeList(SVNRangeListNode.FROM_SOURCE, offset, node.offset, 0, tail);
offset = node.offset;
} else {
if (offset >= node.limit) {
node = node.next;
} else {
int targetOffset = offset - node.offset + node.targetOffset;
if (limit <= node.limit) {
return appendToRangeList(SVNRangeListNode.FROM_TARGET, offset, limit, targetOffset, tail);
}
tail = appendToRangeList(SVNRangeListNode.FROM_TARGET, offset, node.limit, targetOffset, tail);
offset = node.limit;
node = node.next;
}
}
}
SVNErrorManager.assertionFailure(false, null, SVNLogType.DEFAULT);
return tail;
}
private SVNRangeListNode appendToRangeList(int kind, int offset, int limit, int tOffset, SVNRangeListNode tail) {
if (tail == null) {
return allocateListNode(kind, offset, limit, tOffset);
}
return tail.append(allocateListNode(kind, offset, limit, tOffset));
}
private SVNRangeTreeNode myScratchNode = new SVNRangeTreeNode(0,0,0);
public void splay(int offset) throws SVNException {
if (myRoot == null) {
return;
}
SVNRangeTreeNode root = myRoot;
SVNRangeTreeNode scratch = myScratchNode;
scratch.left = scratch.right = null;
SVNRangeTreeNode left = scratch;
SVNRangeTreeNode right = scratch;
while(true) {
if (offset < root.offset) {
if (root.left != null && offset < root.left.offset) {
SVNRangeTreeNode node = root.left;
root.left = node.right;
node.right = root;
root = node;
}
if (root.left == null) {
break;
}
right.left = root;
right = root;
root = root.left;
} else if (offset > root.offset) {
if (root.right != null && offset > root.right.offset) {
SVNRangeTreeNode node = root.right;
root.right = node.left;
node.left = root;
root = node;
}
if (root.right == null) {
break;
}
left.right = root;
left = root;
root = root.right;
} else {
break;
}
}
left.right = root.left;
right.left = root.right;
root.left = scratch.right;
root.right = scratch.left;
if (offset < root.offset && root.left != null) {
if (root.left.right == null) {
SVNRangeTreeNode node = root.left;
root.left = node.right;
SVNErrorManager.assertionFailure(root.left == null, null, SVNLogType.DEFAULT);
node.right = root;
root = node;
} else {
SVNRangeTreeNode nodePointer = root.left;
SVNRangeTreeNode nodeOwner = root;
boolean isLeft = true;
while(nodePointer.right != null) {
nodeOwner = nodePointer;
nodePointer = nodePointer.right;
isLeft = false;
}
right = root;
left = root.left;
root = nodePointer;
if (isLeft) {
nodeOwner.left = root.left;
} else {
nodeOwner.right = root.left;
}
SVNErrorManager.assertionFailure(root.right == null, null, SVNLogType.DEFAULT);
right.left = root.right;
root.left = left;
root.right = right;
}
}
myRoot = root;
SVNErrorManager.assertionFailure((offset >= root.offset) || (root.left == null && root.prev == null), null, SVNLogType.DEFAULT);
}
public void insert(int offset, int limit, int targetOffset) throws SVNException {
if (myRoot == null) {
myRoot = allocateTreeNode(offset, limit, targetOffset);
return;
}
if (offset == myRoot.offset && limit > myRoot.limit) {
myRoot.limit = limit;
myRoot.targetOffset = targetOffset;
cleanTree(limit);
} else if (offset > myRoot.offset && limit > myRoot.limit) {
boolean haveToInsertRange = myRoot.next == null ||
myRoot.limit < myRoot.next.offset ||
limit > myRoot.next.limit;
if (haveToInsertRange) {
if (myRoot.prev != null && myRoot.prev.limit > offset) {
myRoot.offset = offset;
myRoot.limit = limit;
myRoot.targetOffset = targetOffset;
} else {
SVNRangeTreeNode node = allocateTreeNode(offset, limit, targetOffset);
node.next = myRoot.next;
if (node.next != null) {
node.next.prev = node;
}
myRoot.next = node;
node.prev = myRoot;
node.right = myRoot.right;
myRoot.right = null;
node.left = myRoot;
myRoot = node;
}
cleanTree(limit);
}
} else if (offset < myRoot.offset) {
SVNErrorManager.assertionFailure(myRoot.left == null, null, SVNLogType.DEFAULT);
SVNRangeTreeNode node = allocateTreeNode(offset, limit, targetOffset);
node.left = node.prev = null;
node.right = node.next = myRoot;
myRoot = node.next.prev = node;
cleanTree(limit);
}
}
private void cleanTree(int limit) {
int topOffset = limit + 1;
SVNRangeTreeNode rightNode = myRoot.right;
SVNRangeTreeNode owner = myRoot;
if (rightNode == null) {
return;
}
boolean left = false;
while(rightNode != null) {
int offset = rightNode.right != null && rightNode.right.offset < topOffset ? rightNode.offset : topOffset;
if (rightNode.limit <= limit || (rightNode.offset < limit && offset < limit)) {
SVNRangeTreeNode rightRight = rightNode.right;
rightNode.right = null;
if (left) {
owner.left = rightRight;
} else {
owner.right = rightRight;
}
deleteSubtree(rightNode);
rightNode = left ? owner.left : owner.right;
} else {
topOffset = rightNode.offset;
owner = rightNode;
rightNode = rightNode.left;
left = true;
}
}
}
private void deleteSubtree(SVNRangeTreeNode node) {
if (node != null) {
deleteSubtree(node.left);
deleteSubtree(node.right);
freeTreeNode(node);
}
}
}