/**
* Replication Benchmarker
* https://github.com/score-team/replication-benchmarker/
* Copyright (C) 2013 LORIA / Inria / SCORE Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package jbenchmarker.rgaTreeList;
import collect.TreeList;
import jbenchmarker.core.Document;
import jbenchmarker.core.SequenceOperation;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import crdt.Operation;
/**
*
* @author Roh
*/
public class RGADocument<T> implements Document {
private HashMap<RGAS4Vector, RGANode<T>> hash;
private RGANode head;
private TreeList list;
public RGADocument() {
super();
head = new RGANode();
hash = new HashMap<RGAS4Vector, RGANode<T>>();
list = new TreeList();
}
@Override
public String view() {
StringBuilder s = new StringBuilder();
for (Object n : list) {
s.append(((RGANode)n).getContent());
}
return s.toString();
}
public void apply(Operation op) {
RGAOperation rgaop = (RGAOperation) op;
if (rgaop.getType() == SequenceOperation.OpType.delete) {
boolean wasVisible = remoteDelete(rgaop);
if (wasVisible) {
list.remove(list.indexOf(hash.get(rgaop.getS4VPos())));
hash.get(rgaop.getS4VPos()).setTree(null);
}
} else {
RGANode prev;
if (rgaop.getS4VPos() == null) {
prev = head;
} else {
prev = hash.get(rgaop.getS4VPos());
}
List<RGANode<T>> news = new LinkedList<RGANode<T>>();
RGANode node = prev;
RGAS4Vector v = rgaop.getS4VTms();
for (Object e : rgaop.getBlock()) {
node = remoteInsert(node, v, (T)e);
news.add(node);
v = v.follower();
}
RGANode next = node.getNextVisible();
int index = (next==null) ? list.size() : list.indexOf(next);
list.addAll(index, news);
}
}
public RGANode remoteInsert(RGANode prev, RGAS4Vector s4v, T content) {
RGANode newnd = new RGANode(s4v, content);
RGANode next;
if (prev == null) {
throw new NoSuchElementException("RemoteInsert");
}
next = prev.getNext();
while (next != null) {
if (s4v.compareTo(next.getKey()) == RGAS4Vector.AFTER) {
break;
}
prev = next;
next = next.getNext();
}
newnd.setNext(next);
prev.setNext(newnd);
hash.put(s4v, newnd);
return newnd;
}
public boolean remoteDelete(RGAOperation op) {
RGANode node = hash.get(op.getS4VPos());
if (node == null) {
throw new NoSuchElementException("Cannot find" + op.getS4VPos());
}
boolean wasVisible= node.isVisible();
node.makeTombstone();
return wasVisible;
}
void removeLocal(int p, int offset) {
for (int i=0; i<offset; i++){
((RGANode)list.get(p)).setTree(null);;
list.remove(p);
}
}
void addLocal(int i, List<RGANode<T>> ln) {
list.addAll(i, ln);
}
public RGAS4Vector getVisibleS4V(int v) {
RGANode node = getVisibleNode(v);
if (node == null) {
throw new NoSuchElementException("getVisibleS4V");
}
return node.getKey();
}
public RGANode getVisibleNode(int v) {
if (v==0 || list.isEmpty()) return head;
else return (RGANode) list.get(v-1);
}
@Override
public int viewLength() {
return list.size();
}
}