/**
* 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 crdt.tree.fctree;
import collect.OrderedNode;
import crdt.CRDTMessage;
import crdt.OperationBasedMessagesBag;
import crdt.OperationBasedOneMessage;
import crdt.PreconditionException;
import crdt.tree.fctree.Operations.Add;
import crdt.tree.fctree.Operations.Del;
import crdt.tree.fctree.Operations.Nop;
import crdt.tree.fctree.policy.PostAction;
import crdt.tree.orderedtree.CRDTOrderedTree;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
/**
*
* @param <T>
* @author Stephane Martin <stephane.martin@loria.fr>
*/
public abstract class FCTree<T> extends CRDTOrderedTree<T> {
PostAction postAction = null;
boolean removeEntireSubtree =false; //remove entire subtree on local delete
FCNode root;
HashMap<FCIdentifier, FCNode> map = new HashMap<FCIdentifier, FCNode>();
//HashMap<FCIdentifier, FCNodeGf> idToCycle = new HashMap<FCIdentifier, FCNodeGf>();
FCIdFactory idFactory = new FCIdFactory();
FCPositionFactory positionFactory = new FCPositionFactory();
/**
* Add a node at end of path, in position p and return a message to others
*
* @param path
* @param p
* @param element
* @return
* @throws PreconditionException
*/
@Override
public CRDTMessage add(List<Integer> path, int p, T element) throws PreconditionException {
FCNode node = root.getNodeFromPath(path);
FCNode gnode = node.getChild(p - 1);
FCNode lnode = node.getChild(p);
FCIdentifier id = this.idFactory.createId();
Add add = new Add(element, positionFactory.createBetweenNode(gnode, lnode, id), node.getId(), id);
add.apply(node, this);
return new OperationBasedOneMessage(add);
}
/**
* del a node at end of path and return a message to others
*
* @param path
* @return
* @throws PreconditionException
*/
@Override
public CRDTMessage remove(List<Integer> path) throws PreconditionException {
FCNode node = root.getNodeFromPath(path);
if (node.getId().getReplicaNumber() < 0) {
return new OperationBasedOneMessage(new Nop(this.idFactory.createId()));
}
if (removeEntireSubtree) {
return delNode(node);
} else {
Del del = new Del(this.idFactory.createId(), node.getId());
del.apply(node, this);
return new OperationBasedOneMessage(del);
}
}
private OperationBasedMessagesBag delNode(FCNode node) {
OperationBasedMessagesBag ret = new OperationBasedMessagesBag();
List<FCNode<T>> child=new LinkedList(node.getElements());
for (FCNode<T> n : child) {
ret.addMessage(delNode(n));
}
Del del = new Del(this.idFactory.createId(), node.getId());
del.apply(node, this);
ret.addMessage(new OperationBasedOneMessage(del));
return ret;
}
@Override
public CRDTMessage rename(List<Integer> path, T newValue){
return new OperationBasedOneMessage(new Nop(this.idFactory.createId()));
}
@Override
public CRDTMessage move(List<Integer> from, List<Integer> to, int p) {
return new OperationBasedOneMessage(new Nop(this.idFactory.createId()));
}
/**
* search node by identifier
*
* @param id
* @return node identified by ID
*/
public FCNode<T> getNodeById(FCIdentifier id) {
return map.get(id);
}
/**
* Apply one remote message from others
*
* @param op
*/
@Override
public void applyOneRemote(CRDTMessage op) {
applyOneRemote((FCOperation) ((OperationBasedOneMessage) op).getOperation());
}
/**
* Apply one remote message from others
*
* @param op
*/
public void applyOneRemote(FCOperation<T> op) {
/* System.out.println("op : "+op);
System.out.println("before"+root);*/
op.apply(this);
//System.out.println("after"+root);
}
/**
* return the lookup (the root)
*
* @return
*/
@Override
public OrderedNode<T> lookup() {
return root;
}
/**
* Return the map of identifier -> fcnodes
*
* @return
*/
public HashMap<FCIdentifier, FCNode> getMap() {
return map;
}
/**
* Set replicat number
*
* @param replica
*/
@Override
public void setReplicaNumber(int replica) {
super.setReplicaNumber(replica);
idFactory.setReplicaNumber(replica);
}
/**
* Constructor for an tree with a root identified by site : -1 nbop : 0
*
*/
public FCTree() {
this(false);
}
/**
*
* @param action Action trigger after add/del/move operation
* @param removeEntireTree Remove entire subtree on local remove
*/
public FCTree(PostAction action,boolean removeEntireTree) {
this.removeEntireSubtree = removeEntireTree;
this.postAction = action;
if (postAction != null) {
postAction.setTree(this);
}
}
/**
*
* @param removeEntireTree Remove entire subtree on local remove
*/
public FCTree(boolean removeEntireTree) {
//FCIdentifier idroot = new FCIdentifier(-1, 0);
//root = new FCNodeGf(root, null, null, idroot);
//map.put(idroot, root);
this.removeEntireSubtree = removeEntireTree;
}
public FCTree(PostAction action) {
this(action,false);
}
@Override
public String toString() {
return "FCTree" + idFactory.getReplica() + "{" + root + '}';
}
public PostAction getPostAction() {
return postAction;
}
public FCNode getRoot() {
return root;
}
public FCPositionFactory getPositionFactory() {
return positionFactory;
}
public FCIdFactory getIdFactory() {
return idFactory;
}
public boolean isRemoveEntireSubtree() {
return removeEntireSubtree;
}
public void setRemoveEntireSubtree(boolean removeEntireSubtree) {
this.removeEntireSubtree = removeEntireSubtree;
}
}