int offset = 0;
boolean resolvesConflict = false;
while (remoteOpMutations.hasNext()) {
final Mutation rm = remoteOpMutations.next();
final Mutation lm = localOpMutations.next();
final int rmIdx = rm.getPosition();
final int diff = rmIdx - lm.getPosition();
final TransactionLog transactionLog = entity.getTransactionLog();
if (diff < 0) {
if (rm.getType() == MutationType.Delete) {
if (rm.getPosition() + rm.length() > lm.getPosition()) {
if (lm.getType() == MutationType.Insert) {
// uh-oh.. our local insert is inside the range of this remote delete ... move the insert
// to the beginning of the delete range to resolve this.
final LogQuery effectiveStateForRevision = transactionLog.getEffectiveStateForRevision(localOp.getRevision());
final State rewind = effectiveStateForRevision.getEffectiveState();
localOp.removeFromCanonHistory();
transactionLog.markDirty();
final Mutation mutation = lm.newBasedOn(rm.getPosition());
mutation.apply(rewind);
final OTOperation localOnlyOperation
= createLocalOnlyOperation(engine, remoteOp.getAgentId(), singletonList(mutation), entity, localOp.getRevision(), OpPair.of(remoteOp, localOp));
transactionLog.insertLog(localOp.getRevision(), localOnlyOperation);
entity.getState().syncStateFrom(rewind);
transformedMutations.add(rm.newBasedOn(mutation.getPosition() + lm.length()));
continue;
}
else if (lm.getType() == MutationType.Delete) {
final int truncate = lm.getPosition() - rm.getPosition();
final Mutation mutation = rm.newBasedOn(rm.getPosition(), truncate);
transformedMutations.add(mutation);
resolvesConflict = true;
continue;
}
}
}
if (rm.getType() != MutationType.Noop) {
transformedMutations.add(rm);
}
}
else if (diff == 0) {
if (remoteOp.getRevision() != localOp.getRevision() && !remoteOp.isResolvedConflict()) {
transformedMutations.add(rm);
}
else {
switch (rm.getType()) {
case Insert:
if (!remoteWins && lm.getType() == MutationType.Insert) {
offset += lm.length();
}
resolvesConflict = true;
break;
case Delete:
if (lm.getType() == MutationType.Insert) {
offset += lm.length();
resolvesConflict = true;
}
break;
}
if (resolvesConflict) {
transformedMutations.add(adjustMutationToIndex(rmIdx + offset, rm));
}
}
}
else if (diff > 0) {
if (lm.getType() != MutationType.Noop && !localOp.isResolvedConflict()) {
switch (rm.getType()) {
case Insert:
if (lm.getType() == MutationType.Insert) {
offset += lm.length();
}
if (lm.getType() == MutationType.Delete) {
if (remoteWins && (lm.getPosition() + lm.length()) > rm.getPosition()) {
final LogQuery effectiveStateForRevision = transactionLog.getEffectiveStateForRevision(localOp.getRevision());
final State rewind
= effectiveStateForRevision.getEffectiveState();
final Mutation mutation = rm.newBasedOn(lm.getPosition());
//transactionLog.pruneFromOperation(localOp);
localOp.removeFromCanonHistory();
transactionLog.markDirty();
mutation.apply(rewind);
entity.getState().syncStateFrom(rewind);
transactionLog.insertLog(remoteOp.getRevision(),
createLocalOnlyOperation(engine, remoteOp.getAgentId(), singletonList(mutation), entity, remoteOp.getRevision(), OpPair.of(remoteOp, localOp)));
transformedMutations.add(lm.newBasedOn(mutation.getPosition() + rm.length()));
continue;
}
offset -= lm.length();
}
break;
case Delete:
if (lm.getType() == MutationType.Insert) {
offset += lm.length();
}
if (lm.getType() == MutationType.Delete) {
if (remoteWins && (lm.getPosition() + lm.length()) > rm.getPosition()) {
final LogQuery effectiveStateForRevision = transactionLog.getEffectiveStateForRevision(localOp.getRevision());
final State rewind
= effectiveStateForRevision.getEffectiveState();
rm.apply(rewind);
transactionLog.insertLog(localOp.getRevision(),
remoteOp);
localOp.removeFromCanonHistory();
entity.getState().syncStateFrom(rewind);
final int truncate = rm.getPosition() - lm.getPosition();
final Mutation mutation = lm.newBasedOn(lm.getPosition(), truncate);
transformedMutations.add(mutation);
resolvesConflict = true;
continue;