package jbenchmarker.rgaTreeSplit;
import jbenchmarker.core.Document;
import jbenchmarker.core.SequenceOperation;
import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
import crdt.Operation;
public class RgaSDocument<T> implements Document {
private HashMap<RgaSS3Vector, RgaSNode> hash;
private RgaSNode head;
private RgaSTree root;
private int size = 0;
public RgaSDocument() {
super();
head = new RgaSNode();
hash=(new HashMap<RgaSS3Vector, RgaSNode>());
}
/* Methods to apply each type of remote operations:
*
* apply
* remoteInsert
* remoteDelete:
* remoteSplit
*/
public void apply(Operation op) {
RgaSOperation rgaop = (RgaSOperation) op;
if (rgaop.getType() == SequenceOperation.OpType.delete) {
remoteDelete(rgaop);
} else {
remoteInsert(rgaop);
}
}
private void remoteInsert(RgaSOperation op) {
RgaSNode newnd = new RgaSNode(op.getS3vtms(), op.getContent());
RgaSNode node, next=null;
RgaSS3Vector s3v = op.getS3vtms();
RgaSNode nodeTree = null;
if (op.getS3vpos() == null) {
node = head;
} else {
int offsetAbs = op.getOffset1()+op.getS3vpos().getOffset();
node = hash.get(op.getS3vpos());
node = findGoodNode(node, offsetAbs);
remoteSplit(node, offsetAbs);
}
next = node.getNext();
while (next!=null) {
if (s3v.compareTo(next.getKey()) == RgaSS3Vector.AFTER) {
break;
}
node = next;
next = next.getNext();
}
nodeTree=node.getNextVisible();
insertInLocalTree( nodeTree,newnd);
newnd.setNext(next);
node.setNext(newnd);
hash.put(op.getS3vtms(), newnd);
size+=newnd.size();
}
private void remoteDelete(RgaSOperation op) {
int offsetAbs1 = op.getOffset1()+op.getS3vpos().getOffset()-1;
int offsetRel1 = op.getOffset1()-1;
int offsetAbs2 = op.getOffset2()+op.getS3vpos().getOffset();
int offsetRel2 = op.getOffset2();
RgaSNode node = hash.get(op.getS3vpos());
node=findGoodNode(node, offsetAbs1);
if (offsetRel1>0){
remoteSplit(node,offsetAbs1);
node=node.getLink();
}
while (node.getOffset() + node.size() < offsetAbs2){
if (node.isVisible()){
size-=node.size();
deleteInLocalTree(node);
}
node.makeTombstone();
node=node.getLink();
}
if (offsetRel2>0){
remoteSplit(node,offsetAbs2);
if (node.isVisible()){
size-=node.size();
deleteInLocalTree(node);
}
node.makeTombstone();
}
}
public void remoteSplit(RgaSNode node, int offsetAbs) {
RgaSNode end=null;
if (offsetAbs-node.getOffset()>0 && node.size()-offsetAbs+node.getOffset()>0){
List<T> a= null;
List<T> b = null;
if (node.isVisible()){
a = node.getContent().subList(0,offsetAbs-node.getOffset());
b = node.getContent().subList(offsetAbs-node.getOffset(),node.size());
}
end = new RgaSNode(node.clone(), b, offsetAbs);
end.setSize(node.size()-offsetAbs+node.getOffset());
end.setNext(node.getNext());
node.setContent(a);
node.setSize(offsetAbs-node.getOffset());
node.setNext(end);
node.setLink(end);
hash.put(node.getKey(), node);
hash.put(end.getKey(), end);
if (node.isVisible()){
RgaSTree treeEnd = new RgaSTree(end, null, node.getTree().getRightSon());
node.getTree().setRoot(node);
node.getTree().setRightSon(treeEnd);
}
}
}
/* Methods to apply each type of local operations:
*
* findPosInLocalTree
* insertInLocalTree
* deleteInLocalTree
*/
public Position findPosInLocalTree(int pos){
RgaSTree tree = root;
if (pos<=0 || root == null){
return new Position(null, 0);
}
else if (pos>=this.viewLength()){
tree=findMostRight(tree,0);
return new Position(tree.getRoot(), tree.getRoot().size());
}
else {
while (!(tree.size()-tree.getRightSize()-tree.getRoot().size()< pos && pos <= tree.size()-tree.getRightSize())){
if (pos<=tree.size()-tree.getRightSize()-tree.getRoot().size()){
tree=tree.getLeftSon();
}
else {
pos-=tree.getLeftSize()+tree.getRoot().size();
tree=tree.getRightSon();
}
}
return new Position(tree.getRoot(), pos-(tree.size()-tree.getRightSize()-tree.getRoot().size()));
}
}
public void insertInLocalTree(RgaSNode nodePos, RgaSNode newnd){
RgaSTree tree = (nodePos== null) ? null : nodePos.getTree();
RgaSTree newTree = new RgaSTree(newnd, null, null);
if (root==null || (nodePos!=null && nodePos.equals(head))){
if (root==null) root=newTree;
else if (root.getLeftSon()==null) root.setLeftSon(newTree);
else findMostRight(root.getLeftSon(), 0).setRightSon(newTree);
} else if (nodePos==null){
findMostRight(root, 0).setRightSon(newTree);
} else {
if (tree.getLeftSon()== null) tree.setLeftSon(newTree);
else findMostRight(tree.getLeftSon(),0).setRightSon(newTree);
}
newTree=newTree.getFather();
while (newTree!=null){ // add the size of the inserted node in all fathers and grandfathers
newTree.setSize(newTree.size()+newnd.size());
newTree=newTree.getFather();
}
}
public void deleteInLocalTree(RgaSNode nodeDel){
RgaSTree tree = nodeDel.getTree(), father = null;
boolean isRoot = (tree.equals(this.root)) ? true : false;
boolean hasRightSon = (tree.getRightSon()!=null) ? true: false;
boolean hasLeftSon = (tree.getLeftSon()!=null) ? true: false;
boolean isLeaf = (!hasRightSon && !hasLeftSon) ? true: false;
boolean isLeftSon = false;
if (!isRoot){
father = tree.getFather();
if (father.getLeftSon()==null);
else if (father.getLeftSon().equals(tree)) isLeftSon = true;
}
if (isRoot){ // if the tree is the root, so...
if (isLeaf) root = null;
else if (hasLeftSon && !hasRightSon) root=root.getLeftSon();
else if (!hasLeftSon && hasRightSon) root=root.getRightSon();
else {
findMostLeft(tree.getRightSon(), root.getLeftSon().size()).setLeftSon(root.getLeftSon());
root=root.getRightSon();
}
} else if (isLeaf){ // else if it is a leaf, so...
if (isLeftSon)father.setLeftSon(null);
else father.setRightSon(null);
} else { // else ...
if (!hasRightSon){
if (isLeftSon) father.setLeftSon(tree.getLeftSon());
else father.setRightSon(tree.getLeftSon());
} else if (!hasLeftSon){
if (isLeftSon) father.setLeftSon(tree.getRightSon());
else father.setRightSon(tree.getRightSon());
} else {
RgaSTree tree2 = findMostLeft(tree.getRightSon(), tree.getLeftSon().size());
tree2.setLeftSon(tree.getLeftSon());
if (isLeftSon) father.setLeftSon(tree.getRightSon());
else father.setRightSon(tree.getRightSon());
}
}
while (father!=null){ // soutract the size of the deleted node in all fathers and grandfathers
father.setSize(father.size()-nodeDel.size());
father=father.getFather();
}
}
/* Methods to display the view of the document
*
* view(): normal view of the document, without separator between each node
* viewWithSeparator(): view with separators between each node for debugging
* treeView(): normal view of the local tree, without separator between each node
* treeViewWithSeparator(): view of the local tree with separators between each node for debugging
*/
@Override
public String view() {
return treeView(new StringBuilder(),root);
}
/*@Override
public String view() {
StringBuilder s = new StringBuilder();
RgaSNode node = head.getNext();
while (node != null) {
if (node.isVisible() && node.getContent()!=null) {
for (int i=0; i<node.size();i++)
s.append(node.getContent().get(i));
}
node = node.getNext();
}
return s.toString();
}*/
public String viewWithSeparator() {
StringBuilder s = new StringBuilder();
StringBuilder a = new StringBuilder();
RgaSNode node = head.getNext();
while (node != null) {
if (node.isVisible() && node.getContent()!=null) {
a = new StringBuilder();
for (int i=0; i<node.size();i++){
a.append(node.getContent().get(i));
}
s.append("->|"+a+"|");
}
node = node.getNext();
}
return s.toString();
}
public void treeViewWithSeparator(RgaSTree tree, int profondeur){
if (tree!=null){
if (tree.getLeftSon()!=null) treeViewWithSeparator(tree.getLeftSon(),profondeur + 1);
for (int i=0; i < profondeur; i++){
System.out.print(" ");
}
System.out.println("-->"+ tree.getRoot().getContentAsString()+", " +tree.size());
if (tree.getRightSon()!=null) treeViewWithSeparator(tree.getRightSon(),profondeur + 1);
}
}
public String treeView(StringBuilder buf,RgaSTree tree){
if (tree!=null){
if (tree.getLeftSon()!=null) treeView(buf,tree.getLeftSon());
buf.append(tree.getRoot().getContentAsString());
if (tree.getRightSon()!=null) treeView(buf,tree.getRightSon());
}
return buf.toString();
}
/*
*
* other methods used in local and remote operations
*/
public RgaSNode findGoodNode(RgaSNode target, int off){
while (target.getOffset() + target.size() < off){
target=target.getLink();
}
return target;
}
public RgaSTree findMostLeft(RgaSTree tree, int i){
while (tree.getLeftSon()!=null){
tree.setSize(tree.size()+i);
tree=tree.getLeftSon();
}
tree.setSize(tree.size()+i);
return tree;
}
public RgaSTree findMostRight(RgaSTree tree, int i){
while (tree.getRightSon()!=null){
tree.setSize(tree.size()+i);
tree=tree.getRightSon();
}
tree.setSize(tree.size()+i);
return tree;
}
protected class Position {
protected RgaSNode node;
protected int offset;
public Position(RgaSNode n, int offset) {
this.node= n;
this.offset = offset;
}
public String toString(){
return "[" + node + "," + offset +"]";
}
}
public Position getPosition(RgaSNode node, int start){
Position pos;
int i = node.size();
while (node != null && i <= start ) {
node = node.getNextVisible();
if (node!=null) {
i+=node.size();
}
}
if (node!=null){
return pos = new Position(node, node.size() - i + start);
} else {
return pos = new Position(null, 0);
}
}
public HashMap<RgaSS3Vector, RgaSNode> getHash() {
return hash;
}
public RgaSNode getHead(){
return head;
}
@Override
public int viewLength() {
return size;
}
public RgaSTree getRoot() {
return root;
}
}