From 38a9a76d687e731347eb05acfcbd7fb2e2c45aad Mon Sep 17 00:00:00 2001
From: slewis
Date: Thu, 2 Oct 2008 00:12:20 +0000
Subject: Initial checkin of o.e.e.sync plugin in runtime area
---
framework/bundles/org.eclipse.ecf.sync/.classpath | 7 +
framework/bundles/org.eclipse.ecf.sync/.options | 16 ++
framework/bundles/org.eclipse.ecf.sync/.project | 34 ++++
.../.settings/org.eclipse.jdt.core.prefs | 7 +
.../.settings/org.eclipse.pde.core.prefs | 4 +
.../org.eclipse.ecf.sync/META-INF/MANIFEST.MF | 20 ++
framework/bundles/org.eclipse.ecf.sync/about.html | 28 +++
.../bundles/org.eclipse.ecf.sync/build.properties | 10 +
.../bundles/org.eclipse.ecf.sync/plugin.properties | 12 ++
.../org/eclipse/ecf/internal/sync/Activator.java | 63 ++++++
.../ecf/internal/sync/SyncDebugOptions.java | 23 +++
.../cola/ColaDeletionTransformationStrategy.java | 148 ++++++++++++++
.../sync/doc/cola/ColaDocumentChangeMessage.java | 113 +++++++++++
.../cola/ColaInsertionTransformationStategy.java | 105 ++++++++++
.../cola/ColaReplacementTransformationStategy.java | 44 +++++
.../sync/doc/cola/ColaSynchronizationStrategy.java | 220 +++++++++++++++++++++
.../cola/ColaSynchronizationStrategyFactory.java | 39 ++++
.../sync/doc/cola/ColaTransformationStrategy.java | 19 ++
.../identity/IdentitySynchronizationStrategy.java | 46 +++++
.../IdentitySynchronizationStrategyFactory.java | 38 ++++
.../org/eclipse/ecf/sync/IServiceConstants.java | 21 ++
.../org/eclipse/ecf/sync/doc/IDocumentChange.java | 37 ++++
.../ecf/sync/doc/IDocumentChangeMessage.java | 23 +++
.../sync/doc/IDocumentSynchronizationStrategy.java | 58 ++++++
.../IDocumentSynchronizationStrategyFactory.java | 39 ++++
.../ecf/sync/doc/SerializationException.java | 58 ++++++
.../sync/doc/messages/DocumentChangeMessage.java | 109 ++++++++++
27 files changed, 1341 insertions(+)
create mode 100644 framework/bundles/org.eclipse.ecf.sync/.classpath
create mode 100644 framework/bundles/org.eclipse.ecf.sync/.options
create mode 100644 framework/bundles/org.eclipse.ecf.sync/.project
create mode 100644 framework/bundles/org.eclipse.ecf.sync/.settings/org.eclipse.jdt.core.prefs
create mode 100644 framework/bundles/org.eclipse.ecf.sync/.settings/org.eclipse.pde.core.prefs
create mode 100644 framework/bundles/org.eclipse.ecf.sync/META-INF/MANIFEST.MF
create mode 100644 framework/bundles/org.eclipse.ecf.sync/about.html
create mode 100644 framework/bundles/org.eclipse.ecf.sync/build.properties
create mode 100644 framework/bundles/org.eclipse.ecf.sync/plugin.properties
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/Activator.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/SyncDebugOptions.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDeletionTransformationStrategy.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDocumentChangeMessage.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaInsertionTransformationStategy.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaReplacementTransformationStategy.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategy.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategyFactory.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaTransformationStrategy.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategy.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategyFactory.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/IServiceConstants.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChange.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChangeMessage.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategy.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategyFactory.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/SerializationException.java
create mode 100644 framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/messages/DocumentChangeMessage.java
(limited to 'framework/bundles/org.eclipse.ecf.sync')
diff --git a/framework/bundles/org.eclipse.ecf.sync/.classpath b/framework/bundles/org.eclipse.ecf.sync/.classpath
new file mode 100644
index 000000000..2fbb7a23e
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/.classpath
@@ -0,0 +1,7 @@
+
+
June 29, 2007
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.
+ +If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.
+ + + \ No newline at end of file diff --git a/framework/bundles/org.eclipse.ecf.sync/build.properties b/framework/bundles/org.eclipse.ecf.sync/build.properties new file mode 100644 index 000000000..fad931173 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.sync/build.properties @@ -0,0 +1,10 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + about.html,\ + plugin.properties +src.includes = META-INF/,\ + about.html,\ + plugin.properties,\ + src/ diff --git a/framework/bundles/org.eclipse.ecf.sync/plugin.properties b/framework/bundles/org.eclipse.ecf.sync/plugin.properties new file mode 100644 index 000000000..636946c9d --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.sync/plugin.properties @@ -0,0 +1,12 @@ +################################################################################ +# Copyright (c) 2008 Composent, Inc. 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: +# Composent, Inc. - initial API and implementation +################################################################################ +plugin.provider = Eclipse.org +plugin.name = ECF Synchronization API \ No newline at end of file diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/Activator.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/Activator.java new file mode 100644 index 000000000..3f74dee68 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/Activator.java @@ -0,0 +1,63 @@ +package org.eclipse.ecf.internal.sync; + +import java.util.Dictionary; +import java.util.Properties; + +import org.eclipse.ecf.internal.sync.doc.cola.ColaSynchronizationStrategyFactory; +import org.eclipse.ecf.internal.sync.doc.identity.IdentitySynchronizationStrategyFactory; +import org.eclipse.ecf.sync.IServiceConstants; +import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +public class Activator implements BundleActivator { + + public static final String PLUGIN_ID = "org.eclipse.ecf.sync"; + + private static Activator bundle; + private ServiceRegistration colaServiceRegistration; + private ServiceRegistration identityServiceRegistration; + private BundleContext context; + + public static Activator getDefault() { + return bundle; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext ctxt) throws Exception { + bundle = this; + this.context = ctxt; + // Register identity synchronizer service + final Dictionary identityServiceProps = new Properties(); + identityServiceProps.put(IServiceConstants.SYNCSTRATEGY_TYPE_PROPERTY, IdentitySynchronizationStrategyFactory.SYNCHSTRATEGY_TYPE); + identityServiceProps.put(IServiceConstants.SYNCSTRATEGY_PROVIDER_PROPETY, IdentitySynchronizationStrategyFactory.SYNCHSTRATEGY_PROVIDER); + identityServiceRegistration = this.context.registerService(IDocumentSynchronizationStrategyFactory.class.getName(), new IdentitySynchronizationStrategyFactory(), identityServiceProps); + // Register cola synchronizer service + final Dictionary colaServiceProps = new Properties(); + colaServiceProps.put(IServiceConstants.SYNCSTRATEGY_TYPE_PROPERTY, ColaSynchronizationStrategyFactory.SYNCHSTRATEGY_TYPE); + colaServiceProps.put(IServiceConstants.SYNCSTRATEGY_PROVIDER_PROPETY, ColaSynchronizationStrategyFactory.SYNCHSTRATEGY_PROVIDER); + colaServiceRegistration = this.context.registerService(IDocumentSynchronizationStrategyFactory.class.getName(), new ColaSynchronizationStrategyFactory(), colaServiceProps); + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + if (colaServiceRegistration != null) { + colaServiceRegistration.unregister(); + colaServiceRegistration = null; + } + if (identityServiceRegistration != null) { + identityServiceRegistration.unregister(); + identityServiceRegistration = null; + } + this.context = null; + bundle = null; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/SyncDebugOptions.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/SyncDebugOptions.java new file mode 100644 index 000000000..cb8dc9038 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/SyncDebugOptions.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2008 Composent, Inc. 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: Composent, Inc. - initial API and implementation + ******************************************************************************/ +package org.eclipse.ecf.internal.sync; + +public interface SyncDebugOptions { + + public static final String DEBUG = Activator.PLUGIN_ID + "/debug"; //$NON-NLS-1$ + + public static final String EXCEPTIONS_CATCHING = DEBUG + "/exceptions/catching"; //$NON-NLS-1$ + + public static final String EXCEPTIONS_THROWING = DEBUG + "/exceptions/throwing"; //$NON-NLS-1$ + + public static final String METHODS_ENTERING = DEBUG + "/methods/entering"; //$NON-NLS-1$ + + public static final String METHODS_EXITING = DEBUG + "/methods/exiting"; //$NON-NLS-1$ + +} diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDeletionTransformationStrategy.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDeletionTransformationStrategy.java new file mode 100644 index 000000000..22eaa757f --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDeletionTransformationStrategy.java @@ -0,0 +1,148 @@ +/**************************************************************************** + * 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.sync.doc.cola; + +import org.eclipse.ecf.core.util.Trace; +import org.eclipse.ecf.internal.sync.Activator; +import org.eclipse.ecf.internal.sync.SyncDebugOptions; +import org.eclipse.ecf.sync.doc.messages.DocumentChangeMessage; + +public class ColaDeletionTransformationStrategy implements ColaTransformationStrategy { + + private static final long serialVersionUID = -7430435392915553959L; + private static ColaDeletionTransformationStrategy INSTANCE; + + private ColaDeletionTransformationStrategy() { + // default constructor is private to enforce singleton property via + // static factory method + } + + public static ColaTransformationStrategy getInstance() { + if (INSTANCE == null) { + INSTANCE = new ColaDeletionTransformationStrategy(); + } + return INSTANCE; + } + + public ColaDocumentChangeMessage getOperationalTransform(ColaDocumentChangeMessage remoteIncomingMsg, ColaDocumentChangeMessage localAppliedMsg, boolean localMsgHighPrio) { + + Trace.entering(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_ENTERING, this.getClass(), "getOperationalTransform", new Object[] {remoteIncomingMsg, localAppliedMsg, new Boolean(localMsgHighPrio)}); //$NON-NLS-1$ + + final ColaDocumentChangeMessage 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 + final DocumentChangeMessage deletionFirstMsg = new DocumentChangeMessage(remoteTransformedMsg.getOffset(), localAppliedMsg.getOffset() - remoteTransformedMsg.getOffset(), remoteTransformedMsg.getText()); + final ColaDocumentChangeMessage deletionFirstPart = new ColaDocumentChangeMessage(deletionFirstMsg, remoteTransformedMsg.getLocalOperationsCount(), remoteTransformedMsg.getRemoteOperationsCount()); + remoteTransformedMsg.addToSplitUpRepresentation(deletionFirstPart); + + final DocumentChangeMessage deletionSecondMsg = new DocumentChangeMessage(localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfInsertedText(), remoteTransformedMsg.getLengthOfReplacedText() - deletionFirstPart.getLengthOfReplacedText(), remoteTransformedMsg.getText()); + final ColaDocumentChangeMessage deletionSecondPart = new ColaDocumentChangeMessage(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, SyncDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", null); //$NON-NLS-1$ + + return remoteTransformedMsg; + } + +} diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDocumentChangeMessage.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDocumentChangeMessage.java new file mode 100644 index 000000000..4ab9c577f --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaDocumentChangeMessage.java @@ -0,0 +1,113 @@ +/**************************************************************************** + * 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.sync.doc.cola; + +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.ecf.core.util.Trace; +import org.eclipse.ecf.internal.sync.Activator; +import org.eclipse.ecf.internal.sync.SyncDebugOptions; +import org.eclipse.ecf.sync.doc.messages.DocumentChangeMessage; + +public class ColaDocumentChangeMessage extends DocumentChangeMessage { + + private static final long serialVersionUID = 2038025022180647210L; + + // TODO encapsulate in a new ColaOpOriginationState and re-implement equals, + // hashCode, i.e. make comparable + private final long localOperationsCount; + private final long remoteOperationsCount; + private final ColaTransformationStrategy trafoStrat; + private boolean splitUp; + private List splitUpRepresentation; + + public ColaDocumentChangeMessage(DocumentChangeMessage msg, long localOperationsCount, long remoteOperationsCount) { + super(msg.getOffset(), msg.getLengthOfReplacedText(), msg.getText()); + this.localOperationsCount = localOperationsCount; + this.remoteOperationsCount = remoteOperationsCount; + this.splitUp = false; + this.splitUpRepresentation = new LinkedList(); + if (super.getLengthOfReplacedText() == 0) { + // this is neither a replacement, nor a deletion + trafoStrat = ColaInsertionTransformationStategy.getInstance(); + } else { + if (super.getText().length() == 0) { + // something has been replaced, nothing inserted, must be a + // deletion + trafoStrat = ColaDeletionTransformationStrategy.getInstance(); + } else { + // something has been replaced with some new input, has to be a + // replacement op + trafoStrat = ColaReplacementTransformationStategy.getInstance(); + //TODO this has not been implemented yet + //throw new IllegalArgumentException("Replacement Handling not implemented yet! Known Bug."); + } + } + } + + public boolean isInsertion() { + return (this.trafoStrat instanceof ColaInsertionTransformationStategy); + } + + public boolean isDeletion() { + return (this.trafoStrat instanceof ColaDeletionTransformationStrategy); + } + + public boolean isReplacement() { + return (this.trafoStrat instanceof ColaReplacementTransformationStategy); + } + + public long getLocalOperationsCount() { + return this.localOperationsCount; + } + + public long getRemoteOperationsCount() { + return this.remoteOperationsCount; + } + + public ColaDocumentChangeMessage transformAgainst(ColaDocumentChangeMessage localMsg, boolean localMsgHighPrio) { + Trace.entering(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_ENTERING, this.getClass(), "transformAgainst", localMsg); //$NON-NLS-1$ + final ColaDocumentChangeMessage transformedMsg = trafoStrat.getOperationalTransform(this, localMsg, localMsgHighPrio); + Trace.entering(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_EXITING, this.getClass(), "transformAgainst", transformedMsg); //$NON-NLS-1$ + return transformedMsg; + } + + public String toString() { + final StringBuffer buf = new StringBuffer("ColaDocumentChangeMessage["); //$NON-NLS-1$ + buf.append("text=").append(getText()).append(";offset=").append(getOffset()); //$NON-NLS-1$ //$NON-NLS-2$ + buf.append(";length=").append(getLengthOfReplacedText()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ + buf.append(";operationsCount[local=").append(getLocalOperationsCount()); //$NON-NLS-1$ + buf.append(";remote=").append(getRemoteOperationsCount()).append("]]"); //$NON-NLS-1$//$NON-NLS-2$ + return buf.toString(); + } + + public void setSplitUp(boolean toBeSplitUp) { + this.splitUp = toBeSplitUp; + } + + public boolean isSplitUp() { + return splitUp; + } + + public void setSplitUpRepresentation(List splitUpRepresentation) { + this.splitUpRepresentation = splitUpRepresentation; + } + + public List getSplitUpRepresentation() { + return splitUpRepresentation; + } + + public void addToSplitUpRepresentation(ColaDocumentChangeMessage splitUpRepresentationPart) { + this.splitUpRepresentation.add(splitUpRepresentationPart); + } +} diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaInsertionTransformationStategy.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaInsertionTransformationStategy.java new file mode 100644 index 000000000..e91f728bd --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaInsertionTransformationStategy.java @@ -0,0 +1,105 @@ +/**************************************************************************** + * 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.sync.doc.cola; + +import org.eclipse.ecf.core.util.Trace; +import org.eclipse.ecf.internal.sync.Activator; +import org.eclipse.ecf.internal.sync.SyncDebugOptions; +import org.eclipse.ecf.sync.doc.messages.DocumentChangeMessage; + +public class ColaInsertionTransformationStategy implements ColaTransformationStrategy { + + private static final long serialVersionUID = 5192625383622519749L; + private static ColaInsertionTransformationStategy INSTANCE; + + private ColaInsertionTransformationStategy() { + // default constructor is private to enforce singleton property via + // static factory method + } + + public static ColaTransformationStrategy getInstance() { + if (INSTANCE == null) { + INSTANCE = new ColaInsertionTransformationStategy(); + } + return INSTANCE; + } + + /** + * Resolves two conflictingColaDocumentChangeMessage
s by applying an appropriate operational transform.
+ *
+ * If necessary, modifies localAppliedMsg
as well to reflect knowledge of remoteIncomingMsg
+ * in case more conflicting/further diverging remote messages arrive.
+ *
+ * @param remoteIncomingMsg message originating from remote site, generated on same document state as localAppliedMsg
+ * @param localAppliedMsg message already applied to local document, generation state corresponds to that of remoteIncomingMsg
+ * @param localMsgHighPrio determines insertion preference for same offsets, if true localAppliedMsg comes first
+ * @return operational transform of remote message, not conflicting with applied local message
+ */
+ public ColaDocumentChangeMessage getOperationalTransform(ColaDocumentChangeMessage remoteIncomingMsg, ColaDocumentChangeMessage localAppliedMsg, boolean localMsgHighPrio) {
+
+ Trace.entering(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_ENTERING, this.getClass(), "getOperationalTransform", new Object[] {remoteIncomingMsg, localAppliedMsg, new Boolean(localMsgHighPrio)}); //$NON-NLS-1$
+
+ final ColaDocumentChangeMessage remoteTransformedMsg = remoteIncomingMsg;
+
+ if (localAppliedMsg.isInsertion()) {
+
+ if (remoteTransformedMsg.getOffset() < localAppliedMsg.getOffset()) {
+ //coopt(remote(low),local(high)) --> (remote(low),local(low + high))
+ localAppliedMsg.setOffset(localAppliedMsg.getOffset() + remoteTransformedMsg.getOffset());
+ } else if (remoteTransformedMsg.getOffset() == localAppliedMsg.getOffset()) {
+ //coopt(remote(same),local(same))
+ if (localMsgHighPrio) {
+ //at owner --> (remote(high),local(same))
+ remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() + localAppliedMsg.getText().length());
+ } else {
+ //at participant --> (remote(same),local(high))
+ localAppliedMsg.setOffset(localAppliedMsg.getOffset() + remoteTransformedMsg.getText().length());
+ }
+ } else if (remoteTransformedMsg.getOffset() > localAppliedMsg.getOffset()) {
+ //coopt(remote(high),local(low)) --> (remote(low + high),local(low))
+ remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() + localAppliedMsg.getText().length());
+ }
+
+ } else if (localAppliedMsg.isDeletion()) {
+
+ if (remoteTransformedMsg.getOffset() <= localAppliedMsg.getOffset()) {
+
+ localAppliedMsg.setOffset(localAppliedMsg.getOffset() + remoteTransformedMsg.getLengthOfInsertedText());
+
+ } else if (remoteTransformedMsg.getOffset() > localAppliedMsg.getOffset()) {
+
+ if (remoteTransformedMsg.getOffset() > (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
+
+ remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() - localAppliedMsg.getLengthOfReplacedText());
+ } else if (remoteTransformedMsg.getOffset() <= (localAppliedMsg.getOffset() + localAppliedMsg.getLengthOfReplacedText())) {
+
+ //TODO test this ^#$%^#$ case
+ final DocumentChangeMessage deletionFirstMessage = new DocumentChangeMessage(localAppliedMsg.getOffset(), remoteTransformedMsg.getOffset() - localAppliedMsg.getOffset(), localAppliedMsg.getText());
+ final ColaDocumentChangeMessage deletionFirstPart = new ColaDocumentChangeMessage(deletionFirstMessage, localAppliedMsg.getLocalOperationsCount(), localAppliedMsg.getRemoteOperationsCount());
+ localAppliedMsg.addToSplitUpRepresentation(deletionFirstPart);
+
+ final DocumentChangeMessage deletionSecondMessage = new DocumentChangeMessage(localAppliedMsg.getOffset() + remoteTransformedMsg.getLengthOfInsertedText(), localAppliedMsg.getLengthOfReplacedText() - deletionFirstPart.getLengthOfReplacedText(), localAppliedMsg.getText());
+ final ColaDocumentChangeMessage deletionSecondPart = new ColaDocumentChangeMessage(deletionSecondMessage, localAppliedMsg.getLocalOperationsCount(), localAppliedMsg.getRemoteOperationsCount());
+ localAppliedMsg.addToSplitUpRepresentation(deletionSecondPart);
+
+ localAppliedMsg.setSplitUp(true);
+
+ remoteTransformedMsg.setOffset(localAppliedMsg.getOffset());
+
+ }
+ }
+ }
+
+ Trace.exiting(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", remoteTransformedMsg); //$NON-NLS-1$
+ return remoteTransformedMsg;
+ }
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaReplacementTransformationStategy.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaReplacementTransformationStategy.java
new file mode 100644
index 000000000..370c68f00
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaReplacementTransformationStategy.java
@@ -0,0 +1,44 @@
+/****************************************************************************
+ * 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.sync.doc.cola;
+
+import org.eclipse.ecf.core.util.Trace;
+import org.eclipse.ecf.internal.sync.Activator;
+import org.eclipse.ecf.internal.sync.SyncDebugOptions;
+
+//TODO make this to be something like a marker interface, does not need to be a class
+public class ColaReplacementTransformationStategy implements ColaTransformationStrategy {
+
+ private static final long serialVersionUID = -7295023855308474804L;
+ private static ColaReplacementTransformationStategy INSTANCE;
+
+ private ColaReplacementTransformationStategy() {
+ // default constructor is private to enforce singleton property via
+ // static factory method
+ }
+
+ public static ColaTransformationStrategy getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new ColaReplacementTransformationStategy();
+ }
+ return INSTANCE;
+ }
+
+ public ColaDocumentChangeMessage getOperationalTransform(ColaDocumentChangeMessage remoteMsg, ColaDocumentChangeMessage appliedLocalMsg, boolean localMsgHighPrio) {
+ Trace.entering(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_ENTERING, this.getClass(), "getOperationalTransform", new Object[] {remoteMsg, appliedLocalMsg, new Boolean(localMsgHighPrio)}); //$NON-NLS-1$
+
+ Trace.exiting(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", null); //$NON-NLS-1$
+
+ return null;
+ }
+
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategy.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategy.java
new file mode 100644
index 000000000..96d3a44df
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategy.java
@@ -0,0 +1,220 @@
+/****************************************************************************
+ * 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.sync.doc.cola;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.ecf.core.identity.ID;
+import org.eclipse.ecf.core.util.Trace;
+import org.eclipse.ecf.internal.sync.Activator;
+import org.eclipse.ecf.internal.sync.SyncDebugOptions;
+import org.eclipse.ecf.sync.doc.IDocumentChange;
+import org.eclipse.ecf.sync.doc.IDocumentChangeMessage;
+import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy;
+import org.eclipse.ecf.sync.doc.SerializationException;
+import org.eclipse.ecf.sync.doc.messages.DocumentChangeMessage;
+import org.eclipse.osgi.util.NLS;
+
+public class ColaSynchronizationStrategy implements IDocumentSynchronizationStrategy {
+
+ // ColaDocumentChangeMessage
s.
+ * Returned DocumentChangeMessage
s can be applied directly to the
+ * shared document. The method implements the concurrency algorithm described
+ * in http://wiki.eclipse.org/RT_Shared_Editing
+ * @param remoteMsg
+ * @return List contains DocumentChangeMessage
s ready for sequential application to document
+ */
+ public List transformIncomingMessage(final DocumentChangeMessage remoteMsg) {
+ if (!(remoteMsg instanceof ColaDocumentChangeMessage)) {
+ throw new IllegalArgumentException("DocumentChangeMessage is incompatible with Cola SynchronizationStrategy"); //$NON-NLS-1$
+ }
+ Trace.entering(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_ENTERING, this.getClass(), "transformIncomingMessage", remoteMsg); //$NON-NLS-1$
+ ColaDocumentChangeMessage transformedRemote = (ColaDocumentChangeMessage) remoteMsg;
+
+ final List transformedRemotes = new LinkedList();
+ transformedRemotes.add(transformedRemote);
+
+ remoteOperationsCount++;
+ //this is where the concurrency algorithm is executed
+ if (!unacknowledgedLocalOperations.isEmpty()) {//Do not remove this. It is necessary. The following iterator does not suffice.
+ // remove operations from queue that have been implicitly
+ // acknowledged as received on the remote site by the reception of
+ // this message
+ for (final Iterator it = unacknowledgedLocalOperations.iterator(); it.hasNext();) {
+ final ColaDocumentChangeMessage unackedLocalOp = (ColaDocumentChangeMessage) it.next();
+ if (transformedRemote.getRemoteOperationsCount() > unackedLocalOp.getLocalOperationsCount()) {
+ Trace.trace(Activator.PLUGIN_ID, NLS.bind("transformIncomingMessage.removing {0}", unackedLocalOp)); //$NON-NLS-1$
+ it.remove();
+ } else {
+ // the unackowledgedLocalOperations queue is ordered and
+ // sorted
+ // due to sequential insertion of local ops, thus once a
+ // local op with a higher
+ // or equal local op count (i.e. remote op count from the
+ // remote operation's view)
+ // is reached, we can abandon the check for the remaining
+ // queue items
+ Trace.trace(Activator.PLUGIN_ID, "breaking out of unackedLocalOperations loop"); //$NON-NLS-1$
+ break;// exits for-loop
+ }
+ }
+
+ // at this point the queue has been freed of operations that
+ // don't require to be transformed against
+
+ if (!unacknowledgedLocalOperations.isEmpty()) {
+ ColaDocumentChangeMessage localOp = (ColaDocumentChangeMessage) unacknowledgedLocalOperations.getFirst();
+ Assert.isTrue(transformedRemote.getRemoteOperationsCount() == localOp.getLocalOperationsCount());
+
+ for (final ListIterator unackOpsListIt = unacknowledgedLocalOperations.listIterator(); unackOpsListIt.hasNext();) {
+ for (final ListIterator trafoRemotesIt = transformedRemotes.listIterator(); trafoRemotesIt.hasNext();) {
+ // returns new instance
+ // clarify operation preference, owner/docshare initiator
+ // consistently comes first
+ localOp = (ColaDocumentChangeMessage) unackOpsListIt.next();
+ transformedRemote = (ColaDocumentChangeMessage) trafoRemotesIt.next();
+ transformedRemote = transformedRemote.transformAgainst(localOp, isInitiator);
+
+ if (transformedRemote.isSplitUp()) {
+ //currently this only happens for a remote deletion that needs to be transformed against a locally applied insertion
+ //attention: before applying a list of deletions to docshare, the indices need to be updated/finalized one last time
+ //since deletions can only be applied sequentially and every deletion is going to change the underlying document and the
+ //respective indices!
+ trafoRemotesIt.remove();
+ for (final Iterator splitUpIterator = transformedRemote.getSplitUpRepresentation().iterator(); splitUpIterator.hasNext();) {
+ trafoRemotesIt.add(splitUpIterator.next());
+ }
+ //according to the ListIterator documentation it seems so as if the following line is unnecessary,
+ //as a call to next() after the last removal and additions will return what it would have returned anyway
+ //trafoRemotesIt.next();//TODO not sure about the need for this - I want to jump over the two inserted ops and reach the end of this iterator
+ }
+
+ //TODO check whether or not this collection shuffling does what it is supposed to, i.e. remove current localop in unack list and add split up representation instead
+ if (localOp.isSplitUp()) {
+ //local operation has been split up during operational transform --> remove current version and add new versions plus jump over those
+ unackOpsListIt.remove();
+ for (final Iterator splitUpOpIterator = localOp.getSplitUpRepresentation().iterator(); splitUpOpIterator.hasNext();) {
+ unackOpsListIt.add(splitUpOpIterator.next());
+ }
+ //according to the ListIterator documentation it seems so as if the following line is unnecessary,
+ //as a call to next() after the last removal and additions will return what it would have returned anyway
+ //unackOpsListIt.next();//TODO check whether or not this does jump over both inserted operations that replaced the current unack op
+ }//end split up localop handling
+ }//transformedRemotes List iteration
+ }
+ }
+
+ }
+ Trace.exiting(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_EXITING, this.getClass(), "transformIncomingMessage", transformedRemote); //$NON-NLS-1$
+
+ //TODO find a cleaner and more OO way of cleaning up the list if it contains multiple deletions:
+ if (transformedRemotes.size() > 1) {
+ final ColaDocumentChangeMessage firstOp = (ColaDocumentChangeMessage) transformedRemotes.get(0);
+ if (firstOp.isDeletion()) {
+ //this means all operations in the return list must also be deletions, i.e. modify virtual/optimistic offset for sequential application to document
+ final ListIterator deletionFinalizerIt = transformedRemotes.listIterator();
+ ColaDocumentChangeMessage previousDel = (ColaDocumentChangeMessage) deletionFinalizerIt.next();//jump over first del-op does not need modification, we know this is OK because of previous size check;
+ ColaDocumentChangeMessage currentDel;
+
+ for (; deletionFinalizerIt.hasNext();) {
+ currentDel = (ColaDocumentChangeMessage) deletionFinalizerIt.next();
+ currentDel.setOffset(currentDel.getOffset() - previousDel.getLengthOfReplacedText());
+ previousDel = currentDel;
+ }
+ }
+ }
+
+ return transformedRemotes;
+ }
+
+ public String toString() {
+ final StringBuffer buf = new StringBuffer("ColaSynchronizationStrategy"); //$NON-NLS-1$
+ return buf.toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy#registerLocalChange(org.eclipse.ecf.sync.doc.IDocumentChange)
+ */
+ public IDocumentChangeMessage[] registerLocalChange(IDocumentChange localChange) {
+ Trace.entering(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_ENTERING, this.getClass(), "registerLocalChange", localChange); //$NON-NLS-1$
+ final ColaDocumentChangeMessage colaMsg = new ColaDocumentChangeMessage(new DocumentChangeMessage(localChange.getOffset(), localChange.getLengthOfReplacedText(), localChange.getText()), localOperationsCount, remoteOperationsCount);
+ if (!colaMsg.isReplacement()) {
+ unacknowledgedLocalOperations.add(colaMsg);
+ localOperationsCount++;
+ }
+ Trace.exiting(Activator.PLUGIN_ID, SyncDebugOptions.METHODS_EXITING, this.getClass(), "registerLocalChange", colaMsg); //$NON-NLS-1$
+ return new IDocumentChangeMessage[] {colaMsg};
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy#toDocumentChangeMessage(byte[])
+ */
+ public IDocumentChange deserializeToDocumentChange(byte[] bytes) throws SerializationException {
+ return DocumentChangeMessage.deserialize(bytes);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy#transformRemoteChange(org.eclipse.ecf.sync.doc.IDocumentChangeMessage)
+ */
+ public IDocumentChange[] transformRemoteChange(IDocumentChange remoteChange) {
+ if (!(remoteChange instanceof DocumentChangeMessage))
+ return new IDocumentChange[] {};
+ final DocumentChangeMessage m = (DocumentChangeMessage) remoteChange;
+ final List l = this.transformIncomingMessage(m);
+ return (IDocumentChange[]) l.toArray(new IDocumentChange[] {});
+ }
+}
\ No newline at end of file
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategyFactory.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategyFactory.java
new file mode 100644
index 000000000..6ebbe0fab
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaSynchronizationStrategyFactory.java
@@ -0,0 +1,39 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.internal.sync.doc.cola;
+
+import org.eclipse.ecf.core.identity.ID;
+import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy;
+import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory;
+
+/**
+ *
+ */
+public class ColaSynchronizationStrategyFactory implements IDocumentSynchronizationStrategyFactory {
+
+ public static final String SYNCHSTRATEGY_PROVIDER = "org.eclipse.ecf.internal.sync.doc.cola";
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory#disposeSynchronizationStragety(org.eclipse.ecf.core.identity.ID)
+ */
+ public void disposeSynchronizationStragety(ID uniqueID) {
+ ColaSynchronizationStrategy.cleanUpFor(uniqueID);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory#getSyncronizationStrategy(org.eclipse.ecf.core.identity.ID, boolean)
+ */
+ public IDocumentSynchronizationStrategy getSyncronizationStrategy(ID uniqueID, boolean isInitiator) {
+ return ColaSynchronizationStrategy.getInstanceFor(uniqueID, isInitiator);
+ }
+
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaTransformationStrategy.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaTransformationStrategy.java
new file mode 100644
index 000000000..64542cfe7
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/cola/ColaTransformationStrategy.java
@@ -0,0 +1,19 @@
+/****************************************************************************
+ * 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.sync.doc.cola;
+
+import java.io.Serializable;
+
+public interface ColaTransformationStrategy extends Serializable {
+
+ ColaDocumentChangeMessage getOperationalTransform(ColaDocumentChangeMessage remoteIncomingMsg, ColaDocumentChangeMessage localAppliedMsg, boolean localMsgHighPrio);
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategy.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategy.java
new file mode 100644
index 000000000..796e0cd78
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategy.java
@@ -0,0 +1,46 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.internal.sync.doc.identity;
+
+import org.eclipse.ecf.sync.doc.IDocumentChange;
+import org.eclipse.ecf.sync.doc.IDocumentChangeMessage;
+import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy;
+import org.eclipse.ecf.sync.doc.SerializationException;
+import org.eclipse.ecf.sync.doc.messages.DocumentChangeMessage;
+
+/**
+ *
+ */
+public class IdentitySynchronizationStrategy implements IDocumentSynchronizationStrategy {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy#deserializeToDocumentChange(byte[])
+ */
+ public IDocumentChange deserializeToDocumentChange(byte[] bytes) throws SerializationException {
+ return DocumentChangeMessage.deserialize(bytes);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy#registerLocalChange(org.eclipse.ecf.sync.doc.IDocumentChange)
+ */
+ public IDocumentChangeMessage[] registerLocalChange(IDocumentChange localChange) {
+ return new IDocumentChangeMessage[] {new DocumentChangeMessage(localChange.getOffset(), localChange.getLengthOfReplacedText(), localChange.getText())};
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy#transformRemoteChange(org.eclipse.ecf.sync.doc.IDocumentChange)
+ */
+ public IDocumentChange[] transformRemoteChange(IDocumentChange remoteChange) {
+ return new IDocumentChange[] {remoteChange};
+ }
+
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategyFactory.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategyFactory.java
new file mode 100644
index 000000000..534e797ba
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/internal/sync/doc/identity/IdentitySynchronizationStrategyFactory.java
@@ -0,0 +1,38 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.internal.sync.doc.identity;
+
+import org.eclipse.ecf.core.identity.ID;
+import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategy;
+import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory;
+
+/**
+ *
+ */
+public class IdentitySynchronizationStrategyFactory implements IDocumentSynchronizationStrategyFactory {
+
+ public static final String SYNCHSTRATEGY_PROVIDER = "org.eclipse.ecf.sync.doc.identity";
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory#disposeSynchronizationStragety(org.eclipse.ecf.core.identity.ID)
+ */
+ public void disposeSynchronizationStragety(ID uniqueID) {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory#getSyncronizationStrategy(org.eclipse.ecf.core.identity.ID, boolean)
+ */
+ public IDocumentSynchronizationStrategy getSyncronizationStrategy(ID uniqueID, boolean isInitiator) {
+ return new IdentitySynchronizationStrategy();
+ }
+
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/IServiceConstants.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/IServiceConstants.java
new file mode 100644
index 000000000..f71468a6c
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/IServiceConstants.java
@@ -0,0 +1,21 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.sync;
+
+/**
+ *
+ */
+public interface IServiceConstants {
+ public static final String SYNCSTRATEGY_TYPE_PROPERTY = "org.eclipse.ecf.type";
+ public static final String SYNCSTRATEGY_PROVIDER_PROPETY = "org.eclipse.ecf.sync.provider";
+
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChange.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChange.java
new file mode 100644
index 000000000..8743b7c59
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChange.java
@@ -0,0 +1,37 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.sync.doc;
+
+/**
+ * Local document change. Instances of this class represent
+ * local changes to a replicated document.
+ */
+public interface IDocumentChange {
+ /**
+ * Get offset in document where change has or will occur.
+ * @return int the offset
+ */
+ public int getOffset();
+
+ /**
+ * Get length of text that was replaced.
+ * @return length of replaced text
+ */
+ public int getLengthOfReplacedText();
+
+ /**
+ * Get the new text.
+ * @return String text. Will not return null
, but
+ * may return empty string.
+ */
+ public String getText();
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChangeMessage.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChangeMessage.java
new file mode 100644
index 000000000..d3d2d8cd1
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentChangeMessage.java
@@ -0,0 +1,23 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.sync.doc;
+
+/**
+ * Document change message. Instances of this interface
+ * may be serialized to a byte [] so that they can be
+ * communicated to remote processes.
+ */
+public interface IDocumentChangeMessage {
+
+ public byte[] toByteArray() throws SerializationException;
+
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategy.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategy.java
new file mode 100644
index 000000000..776e66861
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategy.java
@@ -0,0 +1,58 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.sync.doc;
+
+/**
+ * Document synchronization strategy. Instances implementing this interface
+ * expose the ability for clients to synchronize combinations of local
+ * and remote documents.
+ *
+ * Owners that wish to synchronize local and remote changes
+ * should call {@link #registerLocalChange(IDocumentChange)} when local
+ * changes occur, and then serialize returned {@link IDocumentChangeMessage}s and
+ * deliver the change message to remotes. When remote change messages are received,
+ * they should first be deserized via {@link #deserializeToDocumentChange(byte[])}, and then
+ * passed to {@link #transformRemoteChange(IDocumentChange)} to transform
+ * the change so that when the returned IDocumentChanges are applied to the local
+ * document its state will be consistent with other client(s).
+ */
+public interface IDocumentSynchronizationStrategy {
+
+ /**
+ * Register local document change with document synchronization strategy. This method
+ * should be synchronously called when a local change has
+ * been made to the underlying document.
+ * @param localChange the IDocumentChange made to the local document
+ * @return IDocumentChangeMessage[] an array of document change message to be
+ * delivered to remote participants.
+ */
+ public IDocumentChangeMessage[] registerLocalChange(IDocumentChange localChange);
+
+ /**
+ * Transform remote document change into a set of local document changes to
+ * be synchronously applied to the local document.
+ * @param remoteChange the remote document change instance to
+ * be transformed by this synchronization strategy.
+ * @return IDocumentChange[] to apply to local document
+ */
+ public IDocumentChange[] transformRemoteChange(IDocumentChange remoteChange);
+
+ /**
+ * Deserialization of given byte array to concrete instance of
+ * IDocumentChange object to represent local change to be applied
+ *
+ * @param bytes the bytes to be deserialized
+ * @return IDocumentChange instance from bytes. Will not be null
.
+ * @throws SerializationException thrown if some problem deserializing given bytes.
+ */
+ public IDocumentChange deserializeToDocumentChange(byte[] bytes) throws SerializationException;
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategyFactory.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategyFactory.java
new file mode 100644
index 000000000..f0f1967f2
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/IDocumentSynchronizationStrategyFactory.java
@@ -0,0 +1,39 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.sync.doc;
+
+import org.eclipse.ecf.core.identity.ID;
+
+/**
+ * Factory for creating {@link IDocumentSynchronizationStrategy} instances for
+ * a uniquely identified entity. This interface is exposed as a service and
+ * provides an entry point for clients.
+ */
+public interface IDocumentSynchronizationStrategyFactory {
+
+ public static final String SYNCHSTRATEGY_TYPE = "org.eclipse.ecf.sync.doc";
+
+ /**
+ * Get an IDocumentSynchronizationStrategy for a unique ID. Should not be null
.
+ * @param uniqueID the uniqueID to identify the client of the {@link IDocumentSynchronizationStrategy}.
+ * @param isInitiator whether the client is the initiator of the
+ * shared editing, or the receiver.
+ * @return IDocumentSynchronizationStrategy for the given uniqueID.
+ */
+ public IDocumentSynchronizationStrategy getSyncronizationStrategy(ID uniqueID, boolean isInitiator);
+
+ /**
+ * Clean up the synchronization strategy caching for a given uniqueID. Should not be null
.
+ * @param uniqueID the ID of the
+ */
+ public void disposeSynchronizationStragety(ID uniqueID);
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/SerializationException.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/SerializationException.java
new file mode 100644
index 000000000..e477717b7
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/SerializationException.java
@@ -0,0 +1,58 @@
+/****************************************************************************
+ * Copyright (c) 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ *****************************************************************************/
+
+package org.eclipse.ecf.sync.doc;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ecf.core.util.ECFException;
+import org.eclipse.ecf.internal.sync.Activator;
+
+/**
+ *
+ */
+public class SerializationException extends ECFException {
+
+ private static final long serialVersionUID = -8702959540799683251L;
+
+ /**
+ * @param message
+ * message associated with exception
+ */
+ public SerializationException(String message) {
+ this(message, null);
+ }
+
+ /**
+ * @param cause
+ * the cause of the new exception
+ */
+ public SerializationException(Throwable cause) {
+ this(null, cause);
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public SerializationException(String message, Throwable cause) {
+ this(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, ((message == null) ? "" : message), cause)); //$NON-NLS-1$
+ }
+
+ /**
+ * @param status
+ * the status for th
+ */
+ public SerializationException(IStatus status) {
+ super(status);
+ }
+
+}
diff --git a/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/messages/DocumentChangeMessage.java b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/messages/DocumentChangeMessage.java
new file mode 100644
index 000000000..ae75fa90c
--- /dev/null
+++ b/framework/bundles/org.eclipse.ecf.sync/src/org/eclipse/ecf/sync/doc/messages/DocumentChangeMessage.java
@@ -0,0 +1,109 @@
+/****************************************************************************
+ * Copyright (c) 2007, 2008 Composent, Inc. 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:
+ * Composent, Inc. - initial API and implementation
+ * Mustafa K. Isik
+ *****************************************************************************/
+
+package org.eclipse.ecf.sync.doc.messages;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.eclipse.ecf.sync.doc.IDocumentChange;
+import org.eclipse.ecf.sync.doc.IDocumentChangeMessage;
+import org.eclipse.ecf.sync.doc.SerializationException;
+
+/**
+ *
+ */
+public class DocumentChangeMessage implements IDocumentChange, IDocumentChangeMessage {
+
+ private static final long serialVersionUID = -3195542805471664496L;
+
+ public static DocumentChangeMessage deserialize(byte[] bytes) throws SerializationException {
+ try {
+ final ByteArrayInputStream bins = new ByteArrayInputStream(bytes);
+ final ObjectInputStream oins = new ObjectInputStream(bins);
+ return (DocumentChangeMessage) oins.readObject();
+ } catch (final Exception e) {
+ throw new SerializationException("Exception deserializing DocumentChangeMessage", e);
+ }
+ }
+
+ final String text;
+ int offset;
+ int length;
+
+ public DocumentChangeMessage(int offset, int length, String text) {
+ this.offset = offset;
+ this.length = length;
+ this.text = text;
+ }
+
+ /**
+ * Returns the modification index of the operation resembled by this
+ * message.
+ *
+ * @return modification index
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int updatedOffset) {
+ this.offset = updatedOffset;
+ }
+
+ /**
+ * Returns the length of replaced text.
+ *
+ * @return length of replaced text
+ */
+ public int getLengthOfReplacedText() {
+ return length;
+ }
+
+ public void setLengthOfReplacedText(int length) {
+ this.length = length;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public int getLengthOfInsertedText() {
+ return this.text.length();
+ }
+
+ public String toString() {
+ final StringBuffer buf = new StringBuffer("DocumentChangeMessage["); //$NON-NLS-1$
+ buf.append("text=").append(text).append(";offset=").append(offset); //$NON-NLS-1$ //$NON-NLS-2$
+ buf.append(";length=").append(length).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
+ return buf.toString();
+ }
+
+ private byte[] serialize() throws IOException {
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ final ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(this);
+ return bos.toByteArray();
+ }
+
+ public byte[] toByteArray() throws SerializationException {
+ try {
+ return serialize();
+ } catch (final IOException e) {
+ throw new SerializationException("Exception serializing DocumentChangeMessage", e);
+ }
+ }
+
+}
--
cgit v1.2.3