blob: 19bd205b2c7fcf2a95095486924afbda2e76b2a8 [file] [log] [blame]
/****************************************************************************
* Copyright (c) 2008 Mustafa K. Isik and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Mustafa K. Isik - initial API and implementation
*****************************************************************************/
package org.eclipse.ecf.internal.provisional.docshare.cola;
import org.eclipse.ecf.internal.provisional.docshare.messages.UpdateMessage;
import org.eclipse.ecf.core.util.Trace;
import org.eclipse.ecf.internal.docshare.Activator;
import org.eclipse.ecf.internal.docshare.DocshareDebugOptions;
public class ColaDeletion implements TransformationStrategy {
private static final long serialVersionUID = -7430435392915553959L;
private static ColaDeletion INSTANCE;
private ColaDeletion() {
// default constructor is private to enforce singleton property via
// static factory method
}
public static TransformationStrategy getInstance() {
if (INSTANCE == null) {
INSTANCE = new ColaDeletion();
}
return INSTANCE;
}
public ColaUpdateMessage getOperationalTransform(ColaUpdateMessage remoteIncomingMsg, ColaUpdateMessage localAppliedMsg, boolean localMsgHighPrio) {
Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "getOperationalTransform", new Object[] {remoteIncomingMsg, localAppliedMsg, new Boolean(localMsgHighPrio)}); //$NON-NLS-1$
ColaUpdateMessage remoteTransformedMsg = remoteIncomingMsg;
if (localAppliedMsg.isDeletion()) {
final int noOpLength = 0;
if (remoteTransformedMsg.getOffset() < localAppliedMsg.getOffset()) {
if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < localAppliedMsg.getOffset()) {
//no overlap - remote OK as is, local needs modification
localAppliedMsg.setOffset(localAppliedMsg.getOffset() - remoteTransformedMsg.getLengthOfReplacedText());
} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//remote del at lower index reaches into locally applied del, local del reaches further out
//--> shorten remote del appropriately, move and shorten local del left
remoteTransformedMsg.setLengthOfReplacedText((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) - localAppliedMsg.getOffset());
localAppliedMsg.setLengthOfReplacedText((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) - localAppliedMsg.getOffset());
localAppliedMsg.setOffset(remoteTransformedMsg.getOffset());//TODO verify!
} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) >= (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//remote del at lower index, remote del fully extends over local del
//--> shorten remote by local.lengthOfReplacedText, make local no-op
remoteTransformedMsg.setLengthOfReplacedText(remoteTransformedMsg.getLengthOfReplacedText() - localAppliedMsg.getLengthOfReplacedText());
//TODO verify whether this is equivalent to setting no-op, otherwise declare a noop boolean field for ColaDeletions
localAppliedMsg.setOffset(remoteTransformedMsg.getOffset());
localAppliedMsg.setLengthOfReplacedText(noOpLength);
}
} else if (remoteTransformedMsg.getOffset() == localAppliedMsg.getOffset()) {
if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//start indices are equal, remote is shorter --> make remote no-op
remoteTransformedMsg.setLengthOfReplacedText(noOpLength);
//--> shorten localOp to only delete non-overlapping region
localAppliedMsg.setLengthOfReplacedText(localAppliedMsg.getLengthOfReplacedText() - remoteTransformedMsg.getLengthOfReplacedText());
} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) == (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//same endIndex, i.e. same deletion
//--> make remote op be no-op
remoteTransformedMsg.setLengthOfReplacedText(noOpLength);
//--> make local op appear as no-op
localAppliedMsg.setLengthOfReplacedText(noOpLength);
} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) > (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//remote del extends over local del
//-->shorten remote del by length of local del, index/offset does not need to be updated
remoteTransformedMsg.setLengthOfReplacedText(remoteTransformedMsg.getLengthOfReplacedText() - localAppliedMsg.getLengthOfReplacedText());
//-->make local del appear as no-op
localAppliedMsg.setLengthOfReplacedText(noOpLength);
}
} else if (remoteTransformedMsg.getOffset() > localAppliedMsg.getOffset()) {
if (remoteTransformedMsg.getOffset() > (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//move remote deletion left by length of local deletion
remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() - localAppliedMsg.getLengthOfReplacedText());
} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//remote is fully contained in/overlapping with local del
//--> remote is no-op
remoteTransformedMsg.setLengthOfReplacedText(noOpLength);
//-->local needs to be shortened by length of remote
localAppliedMsg.setLengthOfReplacedText(localAppliedMsg.getLengthOfReplacedText() - remoteTransformedMsg.getLengthOfReplacedText());
} else if (remoteTransformedMsg.getOffset() < (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
//remote del starts within local del, but extends further
//-->shorten remote by overlap and move left to index of local del
remoteTransformedMsg.setLengthOfReplacedText(remoteTransformedMsg.getLengthOfReplacedText() - (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText()) - remoteTransformedMsg.getOffset());
remoteTransformedMsg.setOffset(localAppliedMsg.getOffset());
//-->shorten local applied message
localAppliedMsg.setLengthOfReplacedText(localAppliedMsg.getLengthOfReplacedText() - (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText()) - remoteTransformedMsg.getOffset());
}
}
} else if (localAppliedMsg.isInsertion()) {
if (remoteTransformedMsg.getOffset() < localAppliedMsg.getOffset()) {
if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) < localAppliedMsg.getOffset()) {
//remote remains unchanged, deletion happens fully before local insertion
//local insertion needs to be moved left by full length of deletion
localAppliedMsg.setOffset(localAppliedMsg.getOffset() - remoteTransformedMsg.getLengthOfReplacedText());
} else if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLengthOfReplacedText()) >= localAppliedMsg.getOffset()) { //TODO optimize away, "if" just here for clarity, "else" would be enough
//remote deletion reaches into local insertion and potentially over it
//remote deletion needs to be split apart
UpdateMessage deletionFirstMsg = new UpdateMessage(remoteTransformedMsg.getOffset(), localAppliedMsg.getOffset() - remoteTransformedMsg.getOffset(), remoteTransformedMsg.getText());
ColaUpdateMessage deletionFirstPart = new ColaUpdateMessage(deletionFirstMsg, remoteTransformedMsg.getLocalOperationsCount(), remoteTransformedMsg.getRemoteOperationsCount());
remoteTransformedMsg.addToSplitUpRepresentation(deletionFirstPart);
UpdateMessage deletionSecondMsg = new UpdateMessage(localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfInsertedText(), remoteTransformedMsg.getLengthOfReplacedText() - deletionFirstPart.getLengthOfReplacedText(), remoteTransformedMsg.getText());
ColaUpdateMessage deletionSecondPart = new ColaUpdateMessage(deletionSecondMsg, remoteTransformedMsg.getLocalOperationsCount(), remoteTransformedMsg.getRemoteOperationsCount());
remoteTransformedMsg.addToSplitUpRepresentation(deletionSecondPart);
remoteTransformedMsg.setSplitUp(true);
//local insertion needs to be moved left by overlap
localAppliedMsg.setOffset(remoteTransformedMsg.getOffset());
}
} else if (remoteTransformedMsg.getOffset() >= localAppliedMsg.getOffset()) {
//remote del needs to be moved right by full length of insertion
remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() + localAppliedMsg.getLengthOfInsertedText());
}
}
Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", null); //$NON-NLS-1$
return remoteTransformedMsg;
}
}