package alma.logoot.logootengine;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Random;
import alma.logoot.logootengine.diff_match_patch.Diff;
/**
* Classe utilitaire comprenant les fonctions de manipulation des identifiants,
* notament les algos p61-63.
*
* @author R5A
*
*/
public class LogootEngine implements ILogootEngine {
String oldText;
ArrayList<LogootIdContainer> idTable;
LogootIdentifier id = new LogootIdentifier(0, -1, 0);
private String getOldText() {
if (oldText == null)
oldText = "";
return oldText;
}
private void setOldText(String oldText) {
this.oldText = oldText;
}
public ArrayList<LogootIdContainer> getIdTable() {
if (idTable == null) {
idTable = new ArrayList<LogootIdContainer>();
LogootIdContainer first = new LogootIdContainer();
first.add(new LogootIdentifier(1, 0, 0));
LogootIdContainer last = new LogootIdContainer();
last.add(new LogootIdentifier(LogootConf.BASE, 0, 0));
idTable.add(first);
idTable.add(last);
}
return idTable;
}
private LogootIdentifier getId() {
return id;
}
/**
*
* @param p
* premier identifiant de position
* @param q
* second identifiant de position
* @param N
* nombre d'identifiant souhaites
* @param rep
* identifiant de la replique
* @return N identifiants pour la replique s entre p et q
*/
public ArrayList<LogootIdContainer> generateLineIdentier(
LogootIdContainer p, LogootIdContainer q, int N,
LogootIdentifier rep) {
BigInteger MAXINT = new BigInteger(Integer.MAX_VALUE + "");
ArrayList<LogootIdContainer> list = new ArrayList<LogootIdContainer>();
int index = 0;
int interval = 0;
while (interval < N) {
index++;
BigInteger intervalB = prefix(q, index).subtract(prefix(p, index));
intervalB = intervalB.subtract(new BigInteger("1"));
if ((intervalB).compareTo(MAXINT) != -1)
interval = Integer.MAX_VALUE;
else
interval = intervalB.intValue();
}
int step;
if (LogootConf.USEBOUNDARY) {
step = Math.min(LogootConf.BOUNDARY, interval / N);
} else {
step = interval / N;
}
BigInteger stepB = new BigInteger(step + "");
BigInteger r = prefix(p, index);
Random random = new Random();
for (int j = 0; j < N; j++) {
BigInteger rand;
if (step == 1) {
rand = r.add(new BigInteger("1"));
} else {
rand = r.add(new BigInteger((random.nextInt(step - 1) + 1) + ""));
}
list.add(constructIdentifier(rand, p, q, rep));
p = list.get(list.size() - 1);
r = r.add(stepB);
}
return list;
}
/**
*
* @param r
* liste d'identifiants concatenes
* @param p
* premier identifiant de position
* @param q
* second identifiant de position
* @param rep
* defini horloge et id de la replique pour laquelle on genere la
* position
* @return l'identifiant pour la replique definit par rep(id+horloge)
*/
public LogootIdContainer constructIdentifier(BigInteger r,
LogootIdContainer p, LogootIdContainer q, LogootIdentifier rep) {
// TODO : Ici, la fonction risque de prendre des identifiants a la fois
// dans p et dans q.
LogootIdContainer result = new LogootIdContainer();
LinkedList<Integer> prefix = prefixToList(r);
int index = 0;
for (int i : prefix) {
LogootIdentifier triplet = new LogootIdentifier();
triplet.setDigit(i);
if (index < p.size() && i == p.get(index).getDigit()) {
triplet.setClock(p.get(index).getClock());
triplet.setIdentifier(p.get(index).getIdentifier());
} else if (index < q.size() && i == q.get(index).getDigit()) {
triplet.setClock(q.get(index).getClock());
triplet.setIdentifier(q.get(index).getIdentifier());
} else {
rep.setClock(rep.getClock() + 1);
triplet.setClock(rep.getClock());
triplet.setIdentifier(rep.getIdentifier());
}
index++;
result.add(triplet);
}
return result;
}
/**
*
* @param id
* Identifiant de caractere
* @param n
* Nombre de triplet a prendre en compte
* @return les n identifiants dans la base concatenes.
*/
public BigInteger prefix(LogootIdContainer id, int n) {
String result = "";
int size = new Integer(LogootConf.BASE - 1).toString().length();
for (int i = 0; i < n; i++) {
String s = "";
if (i < id.size()) {
s = String.valueOf(id.get(i).getDigit());
}
while (s.length() < size)
s = "0" + s;
result += s;
}
return new BigInteger(result);
}
private LinkedList<Integer> prefixToList(BigInteger prefix) {
LinkedList<Integer> result = new LinkedList<Integer>();
String ts = String.valueOf(prefix.toString());
int size = new Integer(LogootConf.BASE - 1).toString().length();
int endIndex = ts.length();
int beginIndex = Math.max(0, endIndex - size);
String cs = ts.substring(beginIndex, endIndex);
result.addLast(Integer.parseInt(cs));
while (beginIndex != 0) {
endIndex -= cs.length();
beginIndex = Math.max(0, endIndex - size);
cs = ts.substring(beginIndex, endIndex);
result.addFirst(Integer.parseInt(cs));
}
return result;
}
@Override
public String generatePatch(String text) {
// Initialization by making a diff between the old text and the new one.
diff_match_patch diffEngine = new diff_match_patch();
LinkedList<Diff> diff = diffEngine.diff_main(getOldText(), text, false);
setOldText(text);
int index = 0;
Collection<IOperation> patch = new ArrayList<IOperation>();
// For each difference, we need to add or delete some positions.
for (Diff d : diff) {
if (d.operation == alma.logoot.logootengine.diff_match_patch.Operation.EQUAL) {
index += d.text.length();
} else if (d.operation == alma.logoot.logootengine.diff_match_patch.Operation.INSERT) {
LogootIdContainer p = getIdTable().get(index);
LogootIdContainer q = getIdTable().get(index + 1);
ArrayList<LogootIdContainer> idList = generateLineIdentier(p,
q, d.text.length(), getId());
// Mise a jour idTable
getIdTable().addAll(index + 1, idList);
// Creation operations
int i = 0;
for (LogootIdContainer lic : idList) {
IOperation op = new Operation("i", lic, d.text.charAt(i));
patch.add(op);
i++;
}
index += d.text.length();
} else { // DELETE
for (int i = 0; i < d.text.length(); i++) {
LogootIdContainer position = getIdTable().get(index + 1);
getIdTable().remove(position);
IOperation op = new Operation("d", position);
patch.add(op);
}
}
}
// TODO : serialization
// return serializeToJson(person);
return patch.toString();
}
@Override
public String deliver(String patch) {
ArrayList<IOperation> patched = new ArrayList<IOperation>();
try {
patch = patch.split("^[\\[]{2}")[1];
patch = patch.split("[\\]]{2}$")[0];
String[] splited = patch.split("[\\]],[ ][\\[]");
for (int i = 0; i < splited.length; i++) {
patched.add(new Operation(splited[i]));
}
} catch (Exception e) {
System.err.println("LogootEngine : Deserialization error.");
}
System.out.println("L'objet apres serialization : "+patched.getClass().getName()+ " "+ patched);
// Operation o = (Operation) patched.get(0);
// o.getPosition().get(o.getPosition().size()-1).getIdentifier();
// if (o.getPosition().get(o.getPosition().size()-1).getIdentifier().equals(id.getIdentifier())){
// System.out.println("C'est moi je ne dois pas ecrire huhu.");
// return null;
// }
for (IOperation op : patched)
deliver(op);
return getOldText();
}
private void deliver(IOperation op) {
// TODO : FAIRE UNE VERIFICATION SUR LID, VERIFIER SI CE NEST PAS LE MEME QUE CELUI DU CLIENT
// ( sinon probleme dans la table des ids. )
Operation o = (Operation) op;
if (o.isIns()) {
int index = -Collections
.binarySearch(getIdTable(), o.getPosition()) - 1;
StringBuffer sb = new StringBuffer(getOldText());
sb.insert(index - 1, o.getContent());
setOldText(sb.toString());
getIdTable().add(index, o.getPosition());
} else {
int index = Collections.binarySearch(getIdTable(), o.getPosition());
if (index > 0) {
StringBuffer sb = new StringBuffer(getOldText());
sb.deleteCharAt(index - 1);
setOldText(sb.toString());
getIdTable().remove(index);
}
}
}
@Override
public void setId(Integer id) {
System.out.println("LogootEngine - Reception d'un id : " + id);
this.id.setIdentifier(id);
}
}