diff options
author | slewis | 2008-06-12 04:48:33 +0000 |
---|---|---|
committer | slewis | 2008-06-12 04:48:33 +0000 |
commit | 23288f5a4c654ec841cab11b5a17eff9deca4c5b (patch) | |
tree | 00a6de5bc6a39b8f31aa415aac1b30516b3b201e | |
parent | ad7e97c8fab8d7e44334da9d194d4726bd73ab9d (diff) | |
download | org.eclipse.ecf-23288f5a4c654ec841cab11b5a17eff9deca4c5b.tar.gz org.eclipse.ecf-23288f5a4c654ec841cab11b5a17eff9deca4c5b.tar.xz org.eclipse.ecf-23288f5a4c654ec841cab11b5a17eff9deca4c5b.zip |
Checkins from Mustafa Isik to address https://bugs.eclipse.org/bugs/show_bug.cgi?id=207530
20 files changed, 529 insertions, 279 deletions
diff --git a/framework/bundles/org.eclipse.ecf.docshare/.options b/framework/bundles/org.eclipse.ecf.docshare/.options new file mode 100644 index 000000000..e96177b5a --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.docshare/.options @@ -0,0 +1,16 @@ +# Debugging options for the org.eclipse.ecf.docshare plug-in + +# Turn on general debugging for the org.eclipse.ecf.docshare plug-in +org.eclipse.ecf.docshare/debug=true +org.eclipse.ecf.docshare/debug/filter = * +org.eclipse.ecf.docshare/debug/flag = true + +# Trace when exceptions are caught +org.eclipse.ecf.docshare/debug/exceptions/catching=false +# Trace when exceptions are thrown +org.eclipse.ecf.docshare/debug/exceptions/throwing=false + +# Trace when methods are entered +org.eclipse.ecf.docshare/debug/methods/entering=false +# Trace when methods are exited +org.eclipse.ecf.docshare/debug/methods/exiting=false diff --git a/framework/bundles/org.eclipse.ecf.docshare/about.html b/framework/bundles/org.eclipse.ecf.docshare/about.html index 4c79781a5..6a60a262f 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/about.html +++ b/framework/bundles/org.eclipse.ecf.docshare/about.html @@ -8,7 +8,7 @@ <body lang="EN-US"> <h2>About This Content</h2> -<p>June 25, 2008</p> +<p>June 29, 2007</p> <h3>License</h3> <p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise diff --git a/framework/bundles/org.eclipse.ecf.docshare/plugin.properties b/framework/bundles/org.eclipse.ecf.docshare/plugin.properties index 6ff3fa21d..918864ace 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/plugin.properties +++ b/framework/bundles/org.eclipse.ecf.docshare/plugin.properties @@ -1,10 +1,10 @@ -############################################################################ -# Copyright (c) 2007 Composent Inc., IBM Corp. 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 -# -############################################################################ +/**************************************************************************** +* Copyright (c) 2007 Composent Inc., IBM Corp. 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 +* +*****************************************************************************/ plugin.provider = Eclipse.org -plugin.name = ECF DocShare Plugin +plugin.name = ECF DocShare Plugin
\ No newline at end of file diff --git a/framework/bundles/org.eclipse.ecf.docshare/plugin.xml b/framework/bundles/org.eclipse.ecf.docshare/plugin.xml index e55d1d234..c5cacf536 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/plugin.xml +++ b/framework/bundles/org.eclipse.ecf.docshare/plugin.xml @@ -13,7 +13,7 @@ locationURI="popup:#CompilationUnitEditorContext"> <dynamic class="org.eclipse.ecf.docshare.menu.DocShareRosterMenuContributionItem" - id="org.eclipse.ecf.docshare.compilationuniteditorcontribution"> + id="org.eclipse.ecf.editorshare.dynamic1"> </dynamic> </menuContribution> </extension> @@ -24,20 +24,10 @@ locationURI="popup:#TextEditorContext"> <dynamic class="org.eclipse.ecf.docshare.menu.DocShareRosterMenuContributionItem" - id="org.eclipse.ecf.docshare.texteditorcontribution"> + id="org.eclipse.ecf.editorshare.dynamic2"> </dynamic> </menuContribution> </extension> - <extension - point="org.eclipse.ui.menus"> - <menuContribution - locationURI="popup:org.eclipse.wst.sse.ui.StructuredTextEditor.EditorContext?after=additions"> - <dynamic - class="org.eclipse.ecf.docshare.menu.DocShareRosterMenuContributionItem" - id="org.eclipse.ecf.editorshare.structuredtexteditorcontribution"> - </dynamic> - </menuContribution> - </extension> </plugin> diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/DocShare.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/DocShare.java index f615471c3..b21f7e26d 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/DocShare.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/DocShare.java @@ -19,16 +19,15 @@ import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.Assert; import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.core.util.ECFException; +import org.eclipse.ecf.core.util.Trace; import org.eclipse.ecf.datashare.AbstractShare; import org.eclipse.ecf.datashare.IChannelContainerAdapter; import org.eclipse.ecf.datashare.events.IChannelDisconnectEvent; +import org.eclipse.ecf.docshare.cola.ColaSynchronizer; import org.eclipse.ecf.docshare.messages.*; -import org.eclipse.ecf.internal.docshare.Activator; -import org.eclipse.ecf.internal.docshare.Messages; +import org.eclipse.ecf.internal.docshare.*; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.*; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Control; @@ -42,7 +41,7 @@ import org.eclipse.ui.texteditor.ITextEditor; * Represents a document sharing session between two participants. */ /** - * + * */ public class DocShare extends AbstractShare { @@ -51,7 +50,7 @@ public class DocShare extends AbstractShare { */ ID initiatorID; /** - * The ID of the receiver. + * The ID of the receiver. */ ID receiverID; /** @@ -63,26 +62,36 @@ public class DocShare extends AbstractShare { */ ITextEditor editor; /** - * Content that we have received via start message, before user has responded - * to question about whether or not to display in editor. Should be null - * at all other times. + * Content that we have received via start message, before user has + * responded to question about whether or not to display in editor. Should + * be null at all other times. */ String startContent = null; /** - * Object to use as lock for changing connected state of this docshare instance + * Object to use as lock for changing connected state of this docshare + * instance */ Object stateLock = new Object(); /** - * The document listener is the listener for changes to the *local* copy of the - * IDocument. This listener is responsible for sending document update messages when - * notified. + * Strategy for maintaining consistency among session participants' + * documents. + */ + // TODO provide for a user-interactive selection mechanism + SynchronizationStrategy sync; + + /** + * The document listener is the listener for changes to the *local* copy of + * the IDocument. This listener is responsible for sending document update + * messages when notified. */ IDocumentListener documentListener = new IDocumentListener() { + public void documentAboutToBeChanged(DocumentEvent event) { // nothing to do } + // handling of LOCAL OPERATION application public void documentChanged(DocumentEvent event) { // If the channel is gone, then no reason to handle this. if (getChannel() == null || !Activator.getDefault().isListenerActive()) { @@ -90,20 +99,26 @@ public class DocShare extends AbstractShare { } // If the listener is not active, ignore input if (!Activator.getDefault().isListenerActive()) { - //The local editor is being updated by an remote peer, so we do not - //wish to echo this change. + // The local editor is being updated by a remote peer, so we do + // not + // wish to echo this change. return; } - // Otherwise end update message - sendUpdateMessage(event); + Trace.trace(Activator.PLUGIN_ID, NLS.bind("{0}.documentChanged[{1}]", DocShare.this, event)); //$NON-NLS-1$ + UpdateMessage msg = new UpdateMessage(event.getOffset(), event.getLength(), event.getText()); + UpdateMessage colaMsg = sync.registerOutgoingMessage(msg); + sendUpdateMsg(colaMsg); } }; /** * Create a document sharing session instance. * - * @param adapter the {@link IChannelContainerAdapter} to use to create this document sharing session. - * @throws ECFException if the channel cannot be created. + * @param adapter + * the {@link IChannelContainerAdapter} to use to create this + * document sharing session. + * @throws ECFException + * if the channel cannot be created. */ public DocShare(IChannelContainerAdapter adapter) throws ECFException { super(adapter); @@ -148,17 +163,23 @@ public class DocShare extends AbstractShare { } /** - * Start sharing an editor's contents between two participants. This will send a request to start sharing - * with the target identified by the <code>toID</code> parameter. The remote receiver will be displayed a - * message dialog, and given the option to start editor sharing, or not. + * Start sharing an editor's contents between two participants. This will + * send a request to start sharing with the target identified by the + * <code>toID</code> parameter. The remote receiver will be displayed a + * message dialog, and given the option to start editor sharing, or not. * - * @param our the ID associated with the initiator. Must not be <code>null</code>. - * @param fromName a name to present to the receiver. If <code>null, our.getName() will be used. + * @param our + * the ID associated with the initiator. Must not be + * <code>null</code>. + * @param fromName + * a name to present to the receiver. If + * <code>null, our.getName() will be used. * @param toID the ID of the intended receiver. Must not be <code>null</code>. * @param fileName the file name of the file to be shared (with suffix type extension). Must not be <code>null</code>. * @param editorPart the text editor currently showing the contents of this editor. Must not be <code>null</code>. */ public void startShare(final ID our, final String fromName, final ID toID, final String fileName, final ITextEditor editorPart) { + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, DocShare.class, "startShare", new Object[] {our, fromName, toID, fileName, editorPart}); //$NON-NLS-1$ Assert.isNotNull(our); final String fName = (fromName == null) ? our.getName() : fromName; Assert.isNotNull(toID); @@ -179,53 +200,32 @@ public class DocShare extends AbstractShare { } } }); + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, DocShare.class, "startShare"); //$NON-NLS-1$ } /** - * Stop editor sharing. Message only sent if we are currently engaged in an editor sharing session - * ({@link #isSharing()} returns <code>true</code>. + * Stop editor sharing. Message only sent if we are currently engaged in an + * editor sharing session ({@link #isSharing()} returns <code>true</code>. */ public void stopShare() { + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "stopShare"); //$NON-NLS-1$ if (isSharing()) { // send stop message to other sendStopMessage(); } localStopShare(); - } - - public void sendSelection() { - ITextEditor textEditor = getTextEditor(); - if (textEditor != null) { - ISelectionProvider selectionProvider = textEditor.getSelectionProvider(); - ISelection selection = selectionProvider.getSelection(); - if (selection instanceof ITextSelection) { - ITextSelection textSelection = (ITextSelection) selection; - try { - send(getOtherID(), new SelectionMessage(textSelection.getOffset(), textSelection.getLength(), textSelection.getStartLine(), textSelection.getEndLine())); - } catch (Exception e) { - logError(Messages.DocShare_SELECTION_SEND_ERROR_TITLE, e); - showErrorToUser(Messages.DocShare_SELECTION_SEND_ERROR_TITLE, Messages.DocShare_SELECTION_SEND_ERROR_MESSAGE); - } - } - } - } - - protected void handleSelectionMessage(final SelectionMessage selectionMessage) { - Display.getDefault().syncExec(new Runnable() { - public void run() { - ITextEditor textEditor = getTextEditor(); - if (textEditor != null) - textEditor.selectAndReveal(selectionMessage.getOffset(), selectionMessage.getLength()); - } - }); + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "stopShare"); //$NON-NLS-1$ } void send(ID toID, Message message) throws Exception { super.sendMessage(toID, message.serialize()); } - /* (non-Javadoc) - * @see org.eclipse.ecf.datashare.AbstractShare#handleMessage(org.eclipse.ecf.core.identity.ID, byte[]) + /* + * (non-Javadoc) + * + * @see org.eclipse.ecf.datashare.AbstractShare#handleMessage(org.eclipse.ecf.core.identity.ID, + * byte[]) */ protected void handleMessage(ID fromContainerID, byte[] data) { try { @@ -237,8 +237,6 @@ public class DocShare extends AbstractShare { handleUpdateMessage((UpdateMessage) message); } else if (message instanceof StopMessage) { handleStopMessage((StopMessage) message); - } else if (message instanceof SelectionMessage) { - handleSelectionMessage((SelectionMessage) message); } else { throw new InvalidObjectException(NLS.bind(Messages.DocShare_EXCEPTION_INVALID_MESSAGE, message.getClass().getName())); } @@ -248,9 +246,12 @@ public class DocShare extends AbstractShare { } /** - * This method called by the {@link #handleMessage(ID, byte[])} method if the type of the message received is a start message (sent - * by remote party via {@link #startShare(ID, String, ID, String, ITextEditor)}. - * @param message the UpdateMessage received. + * This method called by the {@link #handleMessage(ID, byte[])} method if + * the type of the message received is a start message (sent by remote party + * via {@link #startShare(ID, String, ID, String, ITextEditor)}. + * + * @param message + * the UpdateMessage received. */ protected void handleStartMessage(final StartMessage message) { final ID senderID = message.getSenderID(); @@ -271,7 +272,8 @@ public class DocShare extends AbstractShare { // And we're done return; } - // Otherwise set start content to the message-provided documentContent + // Otherwise set start content to the message-provided + // documentContent startContent = documentContent; } // Then open UI and show text editor if appropriate @@ -308,20 +310,19 @@ public class DocShare extends AbstractShare { } /** - * This method called by the {@link #handleMessage(ID, byte[])} method if the type of the message received is an update message. - * @param message the UpdateMessage received. + * This method called by the {@link #handleMessage(ID, byte[])} method if + * the type of the message received is an update message. + * + * @param remoteMsg + * the UpdateMessage received. */ - protected void handleUpdateMessage(final UpdateMessage message) { - final int offset = message.getOffset(); - Assert.isTrue(offset > -1); - final int length = message.getLength(); - Assert.isTrue(length > -1); - final String text = message.getText(); - Assert.isTrue(text != null); + protected void handleUpdateMessage(final UpdateMessage remoteMsg) { synchronized (stateLock) { - // If we're waiting on user to start then change the startContent directly + // If we're waiting on user to start then change the + // startContent + // directly if (startContent != null) { - modifyStartContent(offset, length, text); + modifyStartContent(remoteMsg.getOffset(), remoteMsg.getLength(), remoteMsg.getText()); // And we're done return; } @@ -330,11 +331,22 @@ public class DocShare extends AbstractShare { Display.getDefault().asyncExec(new Runnable() { public void run() { try { + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "handleUpdateMessage", remoteMsg); //$NON-NLS-1$ final IDocument document = getDocumentFromEditor(); + if (document != null) { - // We setup editor to not take input while we are changing document + // transparent concurrency/sync'ing algorithm delegation + // The idea here is to be transparent with the sync'ing + // strategy. + Trace.trace(Activator.PLUGIN_ID, NLS.bind("{0}.handleUpdateMessage calling transformIncomingMessage", DocShare.this)); //$NON-NLS-1$ + UpdateMessage msgForLocalApplication = sync.transformIncomingMessage(remoteMsg); + + // if (localState.equalsIgnoreCase(remoteState)) { + // We setup editor to not take input while we are + // changing document setEditorToRefuseInput(); - document.replace(offset, length, text); + + document.replace(msgForLocalApplication.getOffset(), msgForLocalApplication.getLength(), msgForLocalApplication.getText()); } } catch (final Exception e) { logError(Messages.DocShare_EXCEPTION_RECEIVING_MESSAGE_TITLE, e); @@ -342,6 +354,7 @@ public class DocShare extends AbstractShare { } finally { // Have editor accept input setEditorToAcceptInput(); + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "handleUpdateMessage"); //$NON-NLS-1$ } } }); @@ -391,7 +404,9 @@ public class DocShare extends AbstractShare { return fileStore; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.eclipse.ecf.datashare.AbstractShare#dispose() */ public synchronized void dispose() { @@ -408,6 +423,7 @@ public class DocShare extends AbstractShare { } void logError(String exceptionString, Throwable e) { + Trace.catching(Activator.PLUGIN_ID, DocshareDebugOptions.EXCEPTIONS_CATCHING, this.getClass(), exceptionString, e); Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, exceptionString, e)); } @@ -461,6 +477,11 @@ public class DocShare extends AbstractShare { if (doc != null) doc.addDocumentListener(documentListener); } + // used to have the ColaSynchronizer.getInstanceFor(...) call here ... + // TODO needs to be moved to a more appropriate spot, where ColaSynch'er + // does not blow up + // sync = IdentityMapping.getInstance(); + sync = ColaSynchronizer.getInstanceFor(this); } void localStopShare() { @@ -474,12 +495,16 @@ public class DocShare extends AbstractShare { doc.removeDocumentListener(documentListener); this.editor = null; } + // clean up if necessary + // TODO abstract this to work for SynchronizationStrategy + ColaSynchronizer.cleanUpFor(this); + sync = null; } - void sendUpdateMessage(DocumentEvent event) { + void sendUpdateMsg(UpdateMessage msg) { if (isSharing()) { try { - send(getOtherID(), new UpdateMessage(event.getOffset(), event.getLength(), event.getText())); + send(getOtherID(), msg); } catch (final Exception e) { logError(Messages.DocShare_EXCEPTION_SEND_MESSAGE, e); } @@ -500,4 +525,11 @@ public class DocShare extends AbstractShare { } } + public String toString() { + StringBuffer buf = new StringBuffer("DocShare["); //$NON-NLS-1$ + buf.append("ourID=").append(ourID).append(";initiatorID=").append(initiatorID).append(";receiverID=").append(receiverID); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + buf.append(";strategy=").append(sync).append("]"); //$NON-NLS-1$ //$NON-NLS-2$ + return buf.toString(); + } + } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/IdentityMapping.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/IdentityMapping.java index e8f643456..771a2e4d8 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/IdentityMapping.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/IdentityMapping.java @@ -6,7 +6,7 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare; @@ -32,4 +32,9 @@ public class IdentityMapping implements SynchronizationStrategy { return remoteMsg; } + public String toString() { + StringBuffer buf = new StringBuffer("IdentityMapping[]"); //$NON-NLS-1$ + return buf.toString(); + } + } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/SynchronizationStrategy.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/SynchronizationStrategy.java index 4cd9b20fe..4252af871 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/SynchronizationStrategy.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/SynchronizationStrategy.java @@ -6,7 +6,7 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare; diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaDeletion.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaDeletion.java index d8e3df9a6..bd331b880 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaDeletion.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaDeletion.java @@ -6,21 +6,108 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare.cola; +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 { - public ColaUpdateMessage getForOwner(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied) { - // TODO Auto-generated method stub - return null; + 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 ColaUpdateMessage getForParticipant(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied) { - // TODO Auto-generated method stub - return null; + 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$ + + // this transformation handles an incoming remote deletion that has to + // be transformed against a locally operation + ColaUpdateMessage remoteTransformedMsg = remoteIncomingMsg; + + if (localAppliedMsg.isInsertion()) { + // something has been inserted at a lower or same index --> move + // deletion right + if (localAppliedMsg.getOffset() <= remoteTransformedMsg.getOffset()) { + remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() + localAppliedMsg.getText().length()); + } + } else if (localAppliedMsg.isDeletion()) { + if (remoteTransformedMsg.getOffset() > localAppliedMsg.getOffset()) { + // move to remote deletion to the left + // check for overlap + if (remoteTransformedMsg.getOffset() < (localAppliedMsg.getOffset() + localAppliedMsg.getLength())) { + // partial overlap on the right side of local op + if ((remoteTransformedMsg.getOffset() + remoteTransformedMsg.getLength()) > (localAppliedMsg.getOffset() + localAppliedMsg.getLength())) { + // the case that some part of the beginning of the + // incoming + // remote del lies within the already executed deletion + // and extends beyond the already applied del + // shorten the remote deletion, cut off the redundant + // part + // at the beginning of rem del + remoteTransformedMsg.setLength(remoteTransformedMsg.getLength() - ((localAppliedMsg.getOffset() + localAppliedMsg.getLength()) - remoteTransformedMsg.getOffset())); + // move shortened remote del offset to correct pos. + remoteTransformedMsg.setOffset(localAppliedMsg.getOffset()); + } else { // full overlap, remote op fully contained + // within local op + // case remote deletion is fully within already applied + // deletion, i.e. don't do anything + remoteTransformedMsg.setLength(0); // TODO check + remoteTransformedMsg.setOffset(0);// TODO check - + // should resolve + // nullpointerexc + // whether this is + // enough to make + // this a no-op + } + } else { // no overlap + // deletion is fully after the already applied deletion + remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() - localAppliedMsg.getLength()); + } + // if incoming deletion is at a lower or equal index + } else if (remoteTransformedMsg.getOffset() <= localAppliedMsg.getOffset()) { + // check for overlap + if ((remoteTransformedMsg.getOffset() + remoteIncomingMsg.getLength()) > localAppliedMsg.getOffset()) { + // case remote op reaches into or even over the local op + if ((remoteTransformedMsg.getOffset() + remoteIncomingMsg.getLength()) <= (localAppliedMsg.getOffset() + localAppliedMsg.getLength())) { + // case remote op does not reach over local op, i.e. + // shorten remote op by overlap + remoteTransformedMsg.setLength(remoteTransformedMsg.getLength() - ((remoteIncomingMsg.getOffset() + remoteTransformedMsg.getLength()) - localAppliedMsg.getOffset()));// same + // as + // remoteOffset + // - localOffset + } else { + // case remote op reaches over, i.e. cut out length of + // local del-op + remoteTransformedMsg.setLength(remoteTransformedMsg.getLength() - localAppliedMsg.getLength()); + } + + } + // no need to do anything if there is no overlap for del-op at + // lower index vs. local del-op + } + } + + remoteTransformedMsg.remoteOperationsCount += 1; + localAppliedMsg.remoteOperationsCount += 1; + + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", null); //$NON-NLS-1$ + + return null; + } } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaInsertion.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaInsertion.java index a5c135072..5dedd0e80 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaInsertion.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaInsertion.java @@ -6,26 +6,88 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare.cola; +import org.eclipse.ecf.core.util.Trace; +import org.eclipse.ecf.internal.docshare.Activator; +import org.eclipse.ecf.internal.docshare.DocshareDebugOptions; + public class ColaInsertion implements TransformationStrategy { - public ColaUpdateMessage getForOwner(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied) { - // i.e. this strategy belongs to an operation/msg coming from a - // participant-->lesser prio - // remote is to be properly transformed - if (toBeTransformed.getOffset() > alreadyApplied.getOffset() && toBeTransformed.getOffset() < (alreadyApplied.getOffset() + alreadyApplied.getText().length())) { - // the modification - } - return null; + private static final long serialVersionUID = 5192625383622519749L; + private static ColaInsertion INSTANCE; + + private ColaInsertion() { + // default constructor is private to enforce singleton property via + // static factory method } - public ColaUpdateMessage getForParticipant(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied) { - // TODO Auto-generated method stub - return null; + public static TransformationStrategy getInstance() { + if (INSTANCE == null) { + INSTANCE = new ColaInsertion(); + } + 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.isInsertion()) { + // check for full on collision - this is comparable to equal + // insertion + // pos. with single chars + if ((remoteTransformedMsg.getOffset() >= localAppliedMsg.getOffset()) && (remoteTransformedMsg.getOffset() < (localAppliedMsg.getOffset() + localAppliedMsg.getText().length()))) { + // determine what to modify and how + + if (localMsgHighPrio) { + + int localMsgEndIndex = localAppliedMsg.getOffset() + localAppliedMsg.getText().length(); + remoteTransformedMsg.setOffset(localMsgEndIndex); + + } else { + // localMsg if of lesser prio + // update both operations accordingly + + remoteTransformedMsg.setOffset(localAppliedMsg.getOffset()); + + // TODO is this necessary? I think so ... + /* + * appliedLocalMsg.setOffset(appliedLocalMsg.getOffset() + + * transformedRemote.getText().length()); + */ + } + + } else if (remoteTransformedMsg.getOffset() < localAppliedMsg.getOffset()) { + + /* + * appliedLocalMsg.setOffset(appliedLocalMsg.getOffset() + + * transformedRemote.getText().length()); + */ + + } else if (remoteTransformedMsg.getOffset() > localAppliedMsg.getOffset()) { + + remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() + localAppliedMsg.getText().length()); + } + } else if (localAppliedMsg.isDeletion()) { + // TODO determine which cases are interesting to a remote insertion + // when running into a local, already applied deletion: + // the following seems to be the only case of relevance here + if (localAppliedMsg.getOffset() < remoteTransformedMsg.getOffset()) { + // move remote insertion to the left + remoteTransformedMsg.setOffset(remoteTransformedMsg.getOffset() - localAppliedMsg.getLength()); + } + } + + remoteTransformedMsg.remoteOperationsCount += 1; + localAppliedMsg.remoteOperationsCount += 1; + + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", remoteTransformedMsg); //$NON-NLS-1$ + return remoteTransformedMsg; + } } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaReplacement.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaReplacement.java index fe5c47a1e..13f48de2f 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaReplacement.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaReplacement.java @@ -6,20 +6,39 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare.cola; +import org.eclipse.ecf.core.util.Trace; +import org.eclipse.ecf.internal.docshare.Activator; +import org.eclipse.ecf.internal.docshare.DocshareDebugOptions; + public class ColaReplacement implements TransformationStrategy { - public ColaUpdateMessage getForOwner(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied) { - // TODO Auto-generated method stub - return null; + private static final long serialVersionUID = -7295023855308474804L; + private static ColaReplacement INSTANCE; + + private ColaReplacement() { + // default constructor is private to enforce singleton property via + // static factory method } - public ColaUpdateMessage getForParticipant(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied) { + public static TransformationStrategy getInstance() { + if (INSTANCE == null) { + INSTANCE = new ColaReplacement(); + } + return INSTANCE; + } + + public ColaUpdateMessage getOperationalTransform(ColaUpdateMessage remoteMsg, ColaUpdateMessage appliedLocalMsg, boolean localMsgHighPrio) { + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "getOperationalTransform", new Object[] {remoteMsg, appliedLocalMsg, new Boolean(localMsgHighPrio)}); //$NON-NLS-1$ + // TODO Auto-generated method stub + + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "getOperationalTransform", null); //$NON-NLS-1$ + return null; } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaSynchronizer.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaSynchronizer.java index 29ab8963c..bf3239860 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaSynchronizer.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaSynchronizer.java @@ -6,99 +6,122 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare.cola; import java.util.*; -import org.eclipse.ecf.core.identity.ID; +import org.eclipse.core.runtime.Assert; +import org.eclipse.ecf.core.util.Trace; +import org.eclipse.ecf.docshare.DocShare; import org.eclipse.ecf.docshare.SynchronizationStrategy; import org.eclipse.ecf.docshare.messages.UpdateMessage; import org.eclipse.ecf.internal.docshare.Activator; +import org.eclipse.ecf.internal.docshare.DocshareDebugOptions; +import org.eclipse.osgi.util.NLS; public class ColaSynchronizer implements SynchronizationStrategy { - private List unacknowledgedLocalOperations; + // <ColaUpdateMessage> + private final LinkedList unacknowledgedLocalOperations; private final boolean isInitiator; - private double localOperationsCount; - private double remoteOperationsCount; + private long localOperationsCount; + private long remoteOperationsCount; + // <DocShare, ColaSynchronizer> private static Map sessionStrategies = new HashMap(); - private ColaSynchronizer(ID docshareID) { - this.isInitiator = Activator.getDefault().getDocShare(docshareID).isInitiator(); + private ColaSynchronizer(DocShare docshare) { + this.isInitiator = docshare.isInitiator(); unacknowledgedLocalOperations = new LinkedList(); localOperationsCount = 0; remoteOperationsCount = 0; } - public static ColaSynchronizer getInstanceFor(ID docshareID) { - if (sessionStrategies.get(docshareID) == null) { - sessionStrategies.put(docshareID, new ColaSynchronizer(docshareID)); + public static ColaSynchronizer getInstanceFor(DocShare docshare) { + if (sessionStrategies.get(docshare) == null) { + sessionStrategies.put(docshare, new ColaSynchronizer(docshare)); } - return (ColaSynchronizer) sessionStrategies.get(docshareID); + return (ColaSynchronizer) sessionStrategies.get(docshare); } - public static void cleanUpFor(ID docshareID) { - sessionStrategies.remove(docshareID); + public static void cleanUpFor(DocShare docshare) { + sessionStrategies.remove(docshare); } public UpdateMessage registerOutgoingMessage(UpdateMessage localMsg) { - ColaUpdateMessage colaMsg = new ColaUpdateMessage(localMsg, localOperationsCount, remoteOperationsCount); + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "registerOutgoingMessage", localMsg); //$NON-NLS-1$ + final ColaUpdateMessage colaMsg = new ColaUpdateMessage(localMsg, localOperationsCount, remoteOperationsCount); unacknowledgedLocalOperations.add(colaMsg); localOperationsCount++; + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "registerOutgoingMessage", colaMsg); //$NON-NLS-1$ return colaMsg; } /** * Handles proper transformation of incoming <code>ColaUpdateMessage</code>s. * Returned <code>UpdateMessage</code>s can be applied directly to the - * shared document. The method implements the concurreny algorithm described + * shared document. The method implements the concurrency algorithm described * in <code>http://wiki.eclipse.org/RT_Shared_Editing</code> + * @param remoteMsg + * @return UpdateMessage */ public UpdateMessage transformIncomingMessage(final UpdateMessage remoteMsg) { if (!(remoteMsg instanceof ColaUpdateMessage)) { throw new IllegalArgumentException("UpdateMessage is incompatible with Cola SynchronizationStrategy"); //$NON-NLS-1$ } + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "transformIncomingMessage", remoteMsg); //$NON-NLS-1$ ColaUpdateMessage transformedRemote = (ColaUpdateMessage) remoteMsg; + remoteOperationsCount++; // TODO this is where the concurrency algorithm is executed if (!unacknowledgedLocalOperations.isEmpty()) { // remove operations from queue that have been implicitly // acknowledged as received on the remote site by the reception of // this message - Iterator queueIterator = unacknowledgedLocalOperations.iterator(); - ColaUpdateMessage localOperation = (ColaUpdateMessage) queueIterator.next(); - while (!unacknowledgedLocalOperations.isEmpty() && transformedRemote.getRemoteOperationsCount() > localOperation.getLocalOperationsCount()) { - queueIterator.remove(); - if (queueIterator.hasNext()) { - localOperation = (ColaUpdateMessage) queueIterator.next(); + for (final Iterator it = unacknowledgedLocalOperations.iterator(); it.hasNext();) { + ColaUpdateMessage unackedLocalOp = (ColaUpdateMessage) 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 + } + // at this point the queue has been freed of operations that // don't require to be transformed against + + // TODO this is where the BUG is - I am not adapting/modifying the + // queued up operations!!! 2008-06-08 if (!unacknowledgedLocalOperations.isEmpty()) { - Iterator queueModIterator = unacknowledgedLocalOperations.iterator(); - while (queueModIterator.hasNext()) { + ColaUpdateMessage localOp = (ColaUpdateMessage) unacknowledgedLocalOperations.getFirst(); + Assert.isTrue(transformedRemote.getRemoteOperationsCount() == localOp.localOperationsCount); + for (final Iterator it = unacknowledgedLocalOperations.iterator(); it.hasNext();) { // returns new instance // clarify operation preference, owner/docshare initiator // consistently comes first - if (this.isInitiator) { - transformedRemote = transformedRemote.transformForApplicationAtOwnerAgainst(localOperation); - } else { - transformedRemote = transformedRemote.transformForApplicationAtParticipantAgainst(localOperation); - } - localOperation = (ColaUpdateMessage) queueModIterator.next(); - } - // TODO unsure whether this is needed or not, need to test - // transform against last element in the queue - if (this.isInitiator) { - transformedRemote = transformedRemote.transformForApplicationAtOwnerAgainst(localOperation); - } else { - transformedRemote = transformedRemote.transformForApplicationAtParticipantAgainst(localOperation); + + transformedRemote = transformedRemote.transformAgainst((ColaUpdateMessage) it.next(), isInitiator); + } } + } + Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "transformIncomingMessage", transformedRemote); //$NON-NLS-1$ return transformedRemote; + } + public String toString() { + StringBuffer buf = new StringBuffer("ColaSynchronizer"); //$NON-NLS-1$ + return buf.toString(); } }
\ No newline at end of file diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaUpdateMessage.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaUpdateMessage.java index 8bbbcccd8..56ecf3fce 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaUpdateMessage.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/ColaUpdateMessage.java @@ -6,12 +6,15 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare.cola; +import org.eclipse.ecf.core.util.Trace; import org.eclipse.ecf.docshare.messages.UpdateMessage; +import org.eclipse.ecf.internal.docshare.Activator; +import org.eclipse.ecf.internal.docshare.DocshareDebugOptions; public class ColaUpdateMessage extends UpdateMessage { @@ -19,30 +22,38 @@ public class ColaUpdateMessage extends UpdateMessage { // TODO encapsulate in a new ColaOpOriginationState and re-implement equals, // hashCode, i.e. make comparable - final double localOperationsCount; - final double remoteOperationsCount; + final long localOperationsCount; + long remoteOperationsCount; final TransformationStrategy trafoStrat; - public ColaUpdateMessage(UpdateMessage msg, double localOperationsCount, double remoteOperationsCount) { + public ColaUpdateMessage(UpdateMessage msg, long localOperationsCount, long remoteOperationsCount) { super(msg.getOffset(), msg.getLength(), msg.getText()); this.localOperationsCount = localOperationsCount; this.remoteOperationsCount = remoteOperationsCount; if (super.getLength() == 0) { // this is neither a replacement, nor a deletion - trafoStrat = new ColaInsertion(); + trafoStrat = ColaInsertion.getInstance(); } else { if (super.getText().length() == 0) { // something has been replaced, nothing inserted, must be a // deletion - trafoStrat = new ColaDeletion(); + trafoStrat = ColaDeletion.getInstance(); } else { // something has been replaced with some new input, has to be a // replacement op - trafoStrat = new ColaReplacement(); + trafoStrat = ColaReplacement.getInstance(); } } } + public boolean isInsertion() { + return (this.trafoStrat instanceof ColaInsertion); + } + + public boolean isDeletion() { + return (this.trafoStrat instanceof ColaDeletion); + } + public double getLocalOperationsCount() { return this.localOperationsCount; } @@ -51,37 +62,19 @@ public class ColaUpdateMessage extends UpdateMessage { return this.remoteOperationsCount; } - /** - * The receiver of this message transforms it for local application. - * - * The transformation assumes that this operation is to be transformed - * against queued document owner operations which have a higher modification - * priority. that is when in direct index conflict are applied to lower - * index positions. This <code>ColaUpdateMessage</code> is transformed to - * be applied to the next appropriate higher document index position. - * - * @param msg - * queued up operation of local editing site - * @return message suitable to be transformed against next queued up - * operation - */ - public ColaUpdateMessage transformForApplicationAtOwnerAgainst(ColaUpdateMessage msg) { - // case this is the operation of lesser insertion priority - ColaUpdateMessage transformedMsg = trafoStrat.getForOwner(this, msg); - return transformedMsg; - } - - public ColaUpdateMessage transformForApplicationAtParticipantAgainst(ColaUpdateMessage msg) { - // case this is the operation of higher insertion priority - ColaUpdateMessage transformedMsg = trafoStrat.getForParticipant(this, msg); + public ColaUpdateMessage transformAgainst(ColaUpdateMessage localMsg, boolean localMsgHighPrio) { + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "transformAgainst", localMsg); //$NON-NLS-1$ + ColaUpdateMessage transformedMsg = trafoStrat.getOperationalTransform(this, localMsg, localMsgHighPrio); + Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "transformAgainst", transformedMsg); //$NON-NLS-1$ return transformedMsg; } public String toString() { - StringBuffer sb = new StringBuffer(super.toString()); - sb.append(";").append("originationCount.local=").append(this.localOperationsCount).append(";"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ - sb.append("originationCount.remote=").append(this.remoteOperationsCount); //$NON-NLS-1$ - return sb.toString(); + StringBuffer buf = new StringBuffer("ColaUpdateMessage["); //$NON-NLS-1$ + buf.append("text=").append(getText()).append(";offset=").append(getOffset()); //$NON-NLS-1$ //$NON-NLS-2$ + buf.append(";length=").append(getLength()).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(); } - } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/TransformationStrategy.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/TransformationStrategy.java index 966f014b1..90b5affec 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/TransformationStrategy.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/cola/TransformationStrategy.java @@ -6,14 +6,15 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Mustafa K. Isik - conflict resolution via operational transformations + * Mustafa K. Isik - initial API and implementation *****************************************************************************/ package org.eclipse.ecf.docshare.cola; -public interface TransformationStrategy { +import java.io.Serializable; - ColaUpdateMessage getForOwner(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied); +public interface TransformationStrategy extends Serializable { - ColaUpdateMessage getForParticipant(ColaUpdateMessage toBeTransformed, ColaUpdateMessage alreadyApplied); + ColaUpdateMessage getOperationalTransform(ColaUpdateMessage remoteIncomingMsg, + ColaUpdateMessage localAppliedMsg, boolean localMsgHighPrio); } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/menu/DocShareRosterMenuContributionItem.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/menu/DocShareRosterMenuContributionItem.java index 53fe54d39..066b7cee2 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/menu/DocShareRosterMenuContributionItem.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/menu/DocShareRosterMenuContributionItem.java @@ -1,17 +1,8 @@ -/**************************************************************************** - * 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.docshare.menu; -import java.util.*; +import java.util.Iterator; +import java.util.List; + import org.eclipse.ecf.core.IContainer; import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.docshare.DocShare; @@ -22,9 +13,18 @@ import org.eclipse.ecf.presence.roster.IRoster; import org.eclipse.ecf.presence.roster.IRosterEntry; import org.eclipse.ecf.presence.ui.menu.AbstractRosterMenuContributionItem; import org.eclipse.ecf.presence.ui.menu.AbstractRosterMenuHandler; -import org.eclipse.jface.action.*; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; import org.eclipse.osgi.util.NLS; -import org.eclipse.ui.*; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.ITextEditor; public class DocShareRosterMenuContributionItem extends AbstractRosterMenuContributionItem { @@ -75,7 +75,7 @@ public class DocShareRosterMenuContributionItem extends AbstractRosterMenuContri final IPresenceContainerAdapter pca = (IPresenceContainerAdapter) i.next(); final DocShare docShare = getDocShareForPresenceContainerAdapter(pca); if (docShare != null && docShare.isSharing() && docShare.getTextEditor().equals(editorPart)) { - return getMenuContributionsDuringShare(docShare); + return getMenuContributionForStopShare(pca.getRosterManager().getRoster(), docShare, docShare.getOtherID()); } } return super.getContributionItems(); @@ -102,26 +102,15 @@ public class DocShareRosterMenuContributionItem extends AbstractRosterMenuContri return new IContributionItem[] {menuManager}; } - protected IContributionItem[] getMenuContributionsDuringShare(final DocShare docShare) { - List items = new ArrayList(); - if (docShare.isInitiator()) { - items.add(new Separator()); - } - final IAction sendSelection = new Action() { - public void run() { - docShare.sendSelection(); - } - }; - sendSelection.setText(NLS.bind(Messages.DocShareRosterMenuContributionItem_SELECTION_SEND_EDITOR_MENU_TEXT, trimIDNameForMenu(docShare.getOtherID()))); - items.add(new ActionContributionItem(sendSelection)); + protected IContributionItem[] getMenuContributionForStopShare(IRoster roster, final DocShare docShare, final ID otherID) { final IAction stopEditorShare = new Action() { public void run() { docShare.stopShare(); } }; - stopEditorShare.setText(NLS.bind(Messages.DocShareRosterMenuContributionItem_STOP_SHARE_EDITOR_MENU_TEXT, trimIDNameForMenu(docShare.getOtherID()))); - items.add(new ActionContributionItem(stopEditorShare)); - return (IContributionItem[]) items.toArray(new IContributionItem[] {}); + stopEditorShare.setText(NLS.bind(Messages.DocShareRosterMenuContributionItem_STOP_SHARE_EDITOR_MENU_TEXT, trimIDNameForMenu(otherID))); + stopEditorShare.setImageDescriptor(getTopMenuImageDescriptor()); + return new IContributionItem[] {new Separator(), new ActionContributionItem(stopEditorShare)}; } protected AbstractRosterMenuHandler createRosterEntryHandler(IRosterEntry rosterEntry) { diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/messages/UpdateMessage.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/messages/UpdateMessage.java index a2790470c..4fc1772c0 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/messages/UpdateMessage.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/docshare/messages/UpdateMessage.java @@ -13,13 +13,13 @@ package org.eclipse.ecf.docshare.messages; /** - * + * */ public class UpdateMessage extends Message { private static final long serialVersionUID = -3195542805471664496L; - String text; + final String text; int offset; int length; @@ -29,15 +29,42 @@ public class UpdateMessage extends Message { 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 getLength() { return length; } + public void setLength(int length) { + this.length = length; + } + public String getText() { return text; } + + public String toString() { + StringBuffer buf = new StringBuffer("UpdateMessage["); //$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(); + } + } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Activator.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Activator.java index bdc6211bb..a39d9daf1 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Activator.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Activator.java @@ -1,17 +1,7 @@ -/**************************************************************************** - * 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.docshare; import java.util.Hashtable; + import org.eclipse.ecf.core.IContainerManager; import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.core.util.ECFException; diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/DocshareDebugOptions.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/DocshareDebugOptions.java new file mode 100644 index 000000000..01d366c05 --- /dev/null +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/DocshareDebugOptions.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.docshare; + +public interface DocshareDebugOptions { + + 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.docshare/src/org/eclipse/ecf/internal/docshare/ECFStart.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/ECFStart.java index e2fa0850c..f5240650e 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/ECFStart.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/ECFStart.java @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 2008 Composent, Inc. and others. + * Copyright (c) 2007 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 @@ -18,27 +18,26 @@ import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.core.start.IECFStart; import org.eclipse.ecf.core.util.ECFException; import org.eclipse.ecf.datashare.IChannelContainerAdapter; -import org.eclipse.ecf.docshare.DocShare; import org.eclipse.osgi.util.NLS; public class ECFStart implements IECFStart { IContainerListener containerListener = new IContainerListener() { - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.eclipse.ecf.core.IContainerListener#handleEvent(org.eclipse.ecf.core.events.IContainerEvent) */ public void handleEvent(IContainerEvent event) { - Activator activator = Activator.getDefault(); - if (activator == null) - return; - final IContainerManager containerManager = activator.getContainerManager(); + final IContainerManager containerManager = Activator.getDefault().getContainerManager(); if (containerManager == null) return; IContainer container = containerManager.getContainer(event.getLocalContainerID()); if (container == null) return; - if (event instanceof IContainerConnectedEvent || event instanceof IContainerDisconnectedEvent || event instanceof IContainerEjectedEvent) { + if (event instanceof IContainerConnectedEvent + || event instanceof IContainerDisconnectedEvent) { // connected IChannelContainerAdapter cca = (IChannelContainerAdapter) container.getAdapter(IChannelContainerAdapter.class); if (cca == null) @@ -46,16 +45,17 @@ public class ECFStart implements IECFStart { ID containerID = container.getID(); if (event instanceof IContainerConnectedEvent) { try { - activator.addDocShare(containerID, cca); + Activator.getDefault().addDocShare(containerID, cca); } catch (ECFException e) { - activator.getLog().log(new Status(IStatus.INFO, Activator.PLUGIN_ID, IStatus.INFO, NLS.bind(Messages.ECFStart_ERROR_DOCUMENT_SHARE_NOT_CREATED, container.getID()), null)); - } - } else if (event instanceof IContainerDisconnectedEvent || event instanceof IContainerEjectedEvent) { - // disconnected, so remove docshare for given container, and remove associated channel - DocShare docShare = Activator.getDefault().removeDocShare(containerID); - if (docShare != null) { - docShare.dispose(); + Activator.getDefault().getLog().log( + new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.WARNING, + NLS.bind( + Messages.ECFStart_ERROR_DOCUMENT_SHARE_NOT_CREATED, + container.getID()), null)); } + } else if (event instanceof IContainerDisconnectedEvent) { + // disconnected + Activator.getDefault().removeDocShare(containerID); } } else if (event instanceof IContainerDisposeEvent) { containerManager.removeListener(containerManagerListener); @@ -67,7 +67,9 @@ public class ECFStart implements IECFStart { IContainerManagerListener containerManagerListener = new IContainerManagerListener() { - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.eclipse.ecf.core.IContainerManagerListener#containerAdded(org.eclipse.ecf.core.IContainer) */ public void containerAdded(IContainer container) { @@ -77,7 +79,9 @@ public class ECFStart implements IECFStart { container.addListener(containerListener); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.eclipse.ecf.core.IContainerManagerListener#containerRemoved(org.eclipse.ecf.core.IContainer) */ public void containerRemoved(IContainer container) { @@ -85,13 +89,16 @@ public class ECFStart implements IECFStart { } }; - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.eclipse.ecf.core.start.IECFStart#run(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus run(IProgressMonitor monitor) { final IContainerManager containerManager = Activator.getDefault().getContainerManager(); if (containerManager == null) - return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.WARNING, Messages.ECFStart_ERROR_CONTAINER_MANAGER_NOT_AVAILABLE, null); + return new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.WARNING, + Messages.ECFStart_ERROR_CONTAINER_MANAGER_NOT_AVAILABLE, null); containerManager.addListener(containerManagerListener); return Status.OK_STATUS; } diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Messages.java b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Messages.java index 40ea115ee..3c57da218 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Messages.java +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/Messages.java @@ -27,9 +27,6 @@ public class Messages extends NLS { public static String DocShare_EXCEPTION_RECEIVING_MESSAGE_MESSAGE; public static String DocShare_EXCEPTION_RECEIVING_MESSAGE_TITLE; public static String DocShare_EXCEPTION_SEND_MESSAGE; - public static String DocShare_SELECTION_SEND_ERROR_MESSAGE; - public static String DocShare_SELECTION_SEND_ERROR_TITLE; - public static String DocShareRosterMenuContributionItem_SELECTION_SEND_EDITOR_MENU_TEXT; public static String DocShareRosterMenuContributionItem_SHARE_EDITOR_MENU_TEXT; public static String DocShareRosterMenuContributionItem_STOP_SHARE_EDITOR_MENU_TEXT; public static String DocShareRosterMenuHandler_ERROR_ALREADY_PARTICIPATING; diff --git a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/messages.properties b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/messages.properties index 89908cf67..4376d08c8 100644 --- a/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/messages.properties +++ b/framework/bundles/org.eclipse.ecf.docshare/src/org/eclipse/ecf/internal/docshare/messages.properties @@ -1,11 +1,3 @@ -################################################################################ -# Copyright (c) 2008 Composent, Inc., Mustafa 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 -# -################################################################################ DocShare_EDITOR_SHARE_POPUP_MESSAGE=Editor share request from {0} for {1}.\n\nDo you want to accept and open editor? DocShare_EDITOR_SHARE_POPUP_TITLE=Editor Share DocShare_ERROR_STARTING_EDITOR_MESSAGE=Error Starting Editor Sharing {0} @@ -15,11 +7,8 @@ DocShare_EXCEPTION_INVALID_MESSAGE=message is invalid type {0} DocShare_EXCEPTION_RECEIVING_MESSAGE_MESSAGE=Exception handling start message {0} DocShare_EXCEPTION_RECEIVING_MESSAGE_TITLE=Exception receiving message DocShare_EXCEPTION_SEND_MESSAGE=send message error -DocShare_SELECTION_SEND_ERROR_MESSAGE=The selection send failed. See error log for more information -DocShare_SELECTION_SEND_ERROR_TITLE=Selection send error -DocShareRosterMenuContributionItem_SELECTION_SEND_EDITOR_MENU_TEXT=Send Editor Selection to {0} DocShareRosterMenuContributionItem_SHARE_EDITOR_MENU_TEXT=Share Editor With -DocShareRosterMenuContributionItem_STOP_SHARE_EDITOR_MENU_TEXT=Stop Editor Sharing with {0} +DocShareRosterMenuContributionItem_STOP_SHARE_EDITOR_MENU_TEXT=Stop Sharing Editor with {0} DocShareRosterMenuHandler_ERROR_ALREADY_PARTICIPATING=Already participating in editor sharing DocShareRosterMenuHandler_ERROR_EDITOR_ALREADY_SHARING=Editor is already sharing DocShareRosterMenuHandler_ERROR_NO_DOCUMENT_CONTENT=No document content |