/**
* 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.woot.wooth;
import crdt.Factory;
import java.util.Map;
import jbenchmarker.core.Document;
import crdt.Operation;
import jbenchmarker.core.SequenceOperation;
import jbenchmarker.woot.WootIdentifier;
import jbenchmarker.woot.WootOperation;
import jbenchmarker.woot.WootPosition;
/**
* A WOOTH document. Linked list and hash table to retrieve elements.
* @author urso
*/
public class WootHashDocument<T> implements Document, Factory<Document> {
final protected LinkedNode<T> first;
final protected Map<WootIdentifier, LinkedNode<T>> map = new java.util.HashMap<WootIdentifier, LinkedNode<T>>();
protected int size = 0;
private int clock = 0;
private int replicaNumber;
public WootHashDocument() {
LinkedNode<T> end = new WootHashNode<T>(WootIdentifier.IE, null, false, null, 0);
this.first = new WootHashNode<T>(WootIdentifier.IB, null, false, end, 0);
this.map.put(WootIdentifier.IB, first);
this.map.put(WootIdentifier.IE, end);
}
public WootHashDocument(LinkedNode<T> first, LinkedNode<T> end) {
this.first = first;
this.map.put(WootIdentifier.IB, first);
this.map.put(WootIdentifier.IE, end);
}
@Override
public String view() {
StringBuilder s = new StringBuilder();
for (LinkedNode<T> w = first; w != null; w = w.getNext()) {
if (w.isVisible()) {
s.append(w.getContent());
}
}
return s.toString();
}
public T find(WootIdentifier id) {
return map.get(id).getContent();
}
public boolean has(WootIdentifier id) {
for (LinkedNode<T> w = first; w != null; w = w.getNext()) {
if (w.isVisible()) {
return true;
}
}
return false;
}
@Override
public void apply(Operation op) {
WootOperation<T> wop = (WootOperation<T>) op;
if (wop.getType() == SequenceOperation.OpType.delete) {
del(wop.getId());
} else {
add(wop.getId(), wop.getContent(), wop.getIp(), wop.getIn());
}
}
protected void add(WootIdentifier id, T content, WootIdentifier ip, WootIdentifier in) {
LinkedNode<T> wp = map.get(ip);
LinkedNode<T> wn = map.get(in);
LinkedNode<T> w = newNode(id, content, true, null, Math.max(wp.getDegree(), wn.getDegree()) + 1);
insertBetween(w, wp, wn);
map.put(id, w);
++size;
}
protected void del(WootIdentifier id) {
setVisible(id, false);
}
protected void setVisible(WootIdentifier id, boolean b) {
LinkedNode<T> e = map.get(id);
if (!b && e.isVisible()) {
--size;
} else if (b && !e.isVisible()) {
++size;
}
((WootHashNode) e).setVisible(b);
}
/**
* pth visible character
*/
public LinkedNode<T> getVisible(int p) {
int j = -1;
LinkedNode<T> w = first;
while (j < p) {
if (w.isVisible()) {
j++;
}
if (j < p) {
w = w.getNext();
}
}
return w;
}
/**
* next visible character starting from v model position.
*/
public LinkedNode<T> nextVisible(LinkedNode<T> v) {
v = v.getNext();
while (!v.isVisible()) {
v = v.getNext();
}
return v;
}
/**
* Previous character of pth visible character. 0 for 0th
*/
public LinkedNode<T> getPrevious(int p) {
if (p == 0) {
return first;
}
return getVisible(p - 1);
}
/**
* Next character of pth visible characterstarting from v model position. IE
* for last visible.
*/
public LinkedNode<T> getNext(LinkedNode<T> v) {
v = v.getNext();
while (!v.isVisible() && v.getNext() != null) {
v = v.getNext();
}
return v;
}
public WootOperation delete(SequenceOperation o, WootIdentifier id) {
return new WootOperation(SequenceOperation.OpType.delete, id, null);
}
public WootOperation insert(SequenceOperation o, WootIdentifier ip, WootIdentifier in, T content) {
return new WootOperation(SequenceOperation.OpType.insert,
new WootPosition(nextIdentifier(), ip, in), content);
}
private void insertBetween(LinkedNode<T> wn, LinkedNode<T> ip, LinkedNode<T> in) {
if (in == ip.getNext()) {
wn.setNext(in);
ip.setNext(wn);
} else {
LinkedNode<T> e = ip.getNext().getNext();
int dMin = ip.getNext().getDegree();
while (e != in) {
if (e.getDegree() < dMin) {
dMin = e.getDegree();
}
e = e.getNext();
}
e = ip.getNext();
while (e != in) {
if (e.getDegree() == dMin) {
if (e.getId().compareTo(wn.getId()) < 0) {
ip = e;
} else {
in = e;
}
}
if (e != in) {
e = e.getNext();
}
}
insertBetween(wn, ip, in);
}
}
protected LinkedNode<T> get(WootIdentifier id) {
return map.get(id);
}
LinkedNode<T> getFirst() {
return first;
}
@Override
public int viewLength() {
return size;
}
protected WootIdentifier nextIdentifier() {
clock++;
return new WootIdentifier(this.replicaNumber, clock);
}
public void setReplicaNumber(int replicaNumber) {
this.replicaNumber = replicaNumber;
}
public int getReplicaNumber() {
return replicaNumber;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final WootHashDocument<T> other = (WootHashDocument<T>) obj;
if (this.first != other.first && (this.first == null || !this.first.equals(other.first))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + (this.first != null ? this.first.hashCode() : 0);
return hash;
}
protected LinkedNode<T> newNode(WootIdentifier id, T content, boolean visible, LinkedNode<T> next, int degree) {
return new WootHashNode<T>(id, content, visible, next, degree);
}
@Override
public Document create() {
return new WootHashDocument();
}
}