Package org.eclipse.ecf.docshare

Source Code of org.eclipse.ecf.docshare.DocShare

/****************************************************************************
* Copyright (c) 2007, 2009 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 - conflict resolution via operational transformations
*    Marcelo Mayworm - Adding sync API dependence
*    IBM Corporation - support for certain non-text editors
*****************************************************************************/

package org.eclipse.ecf.docshare;

import org.eclipse.ecf.internal.docshare.Messages;

import java.io.*;
import java.util.Collections;
import java.util.Map;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.Assert;
import org.eclipse.ecf.core.IContainer;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.identity.IDCreateException;
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.messages.*;
import org.eclipse.ecf.internal.docshare.*;
import org.eclipse.ecf.presence.IPresenceContainerAdapter;
import org.eclipse.ecf.presence.roster.*;
import org.eclipse.ecf.sync.*;
import org.eclipse.ecf.sync.doc.DocumentChangeMessage;
import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.source.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.*;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;

/**
* Represents a document sharing session between two participants.
* @since 2.1
*/
public class DocShare extends AbstractShare {

  public static class SelectionReceiver {

    private static final String SELECTION_ANNOTATION_ID = "org.eclipse.ecf.docshare.annotations.RemoteSelection"; //$NON-NLS-1$
    private static final String CURSOR_ANNOTATION_ID = "org.eclipse.ecf.docshare.annotations.RemoteCursor"; //$NON-NLS-1$

    /**
     * Annotation model of current document
     */
    private IAnnotationModel annotationModel;

    /**
     * Object to use as lock for changing in annotation model,
     * <code>null</code> if no model is provided.
     */
    private Object annotationModelLock;

    /**
     * Annotation for remote selection in annotationModel
     */
    private Annotation currentAnnotation;

    public SelectionReceiver(ITextEditor editor) {
      if (editor == null) {
        return;
      }
      IDocumentProvider documentProvider = editor.getDocumentProvider();
      if (documentProvider != null) {
        this.annotationModel = documentProvider.getAnnotationModel(editor.getEditorInput());
        if (this.annotationModel != null) {
          if (this.annotationModel instanceof ISynchronizable) {
            this.annotationModelLock = ((ISynchronizable) this.annotationModel).getLockObject();
          }
          if (this.annotationModelLock == null) {
            this.annotationModelLock = this;
          }
        }
      }
    }

    public void handleMessage(final SelectionMessage remoteMsg) {
      if (this.annotationModelLock == null) {
        return;
      }
      final Position newPosition = new Position(remoteMsg.getOffset(), remoteMsg.getLength());
      final Annotation newAnnotation = new Annotation(newPosition.getLength() > 0 ? SELECTION_ANNOTATION_ID : CURSOR_ANNOTATION_ID, false, Messages.DocShare_RemoteSelection);
      synchronized (this.annotationModelLock) {
        if (this.annotationModel != null) {
          // initial selection, create new
          if (this.currentAnnotation == null) {
            this.currentAnnotation = newAnnotation;
            this.annotationModel.addAnnotation(newAnnotation, newPosition);
            return;
          }
          // selection not changed, skip
          if (this.currentAnnotation.getType() == newAnnotation.getType()) {
            Position oldPosition = this.annotationModel.getPosition(this.currentAnnotation);
            if (oldPosition == null || newPosition.equals(oldPosition)) {
              return;
            }
          }
          // selection changed, replace annotation
          if (this.annotationModel instanceof IAnnotationModelExtension) {
            Annotation[] oldAnnotations = new Annotation[] {this.currentAnnotation};
            this.currentAnnotation = newAnnotation;
            Map newAnnotations = Collections.singletonMap(newAnnotation, newPosition);
            ((IAnnotationModelExtension) this.annotationModel).replaceAnnotations(oldAnnotations, newAnnotations);
          } else {
            this.annotationModel.removeAnnotation(this.currentAnnotation);
            this.annotationModel.addAnnotation(newAnnotation, newPosition);
          }
        }
      }
    }

    public void dispose() {
      if (this.annotationModelLock == null) {
        return;
      }
      synchronized (this.annotationModelLock) {
        if (this.annotationModel != null) {
          if (this.currentAnnotation != null) {
            this.annotationModel.removeAnnotation(this.currentAnnotation);
            this.currentAnnotation = null;
          }
          this.annotationModel = null;
        }
      }
    }

  }

  /**
   * The ID of the initiator
   */
  private ID initiatorID;
  /**
   * The ID of the receiver.
   */
  private ID receiverID;
  /**
   * Our ID
   */
  private ID ourID;
  /**
   * Text editor
   */
  private 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.
   */
  String startContent = null;
  /**
   * Object to use as lock for changing connected state of this docshare
   * instance
   */
  Object stateLock = new Object();

  /**
   * Strategy for maintaining consistency among session participants'
   * documents.
   */
  IModelSynchronizationStrategy syncStrategy;

  /**
   * Factory to returns the possible strategies
   */
  IDocumentSynchronizationStrategyFactory factory;

  /**
   * Handler for SelectionMessage (painting remote selection)
   */
  SelectionReceiver selectionReceiver;

  /**
   * 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.
   */
  public DocShare(IChannelContainerAdapter adapter) throws ECFException {
    super(adapter);
    factory = Activator.getDefault().getColaSynchronizationStrategyFactory();

  }

  IPartListener partListener = new IPartListener() {

    public void partActivated(IWorkbenchPart part) {
      // nothing to do
    }

    public void partBroughtToTop(IWorkbenchPart part) {
      // nothing to do
    }

    public void partClosed(IWorkbenchPart part) {
      ITextEditor textEditor = getTextEditor();
      if (textEditor != null && part.equals(textEditor.getSite().getPart())) {
        stopShare();
      }
    }

    public void partDeactivated(IWorkbenchPart part) {
      // nothing to do
    }

    public void partOpened(IWorkbenchPart part) {
      // nothing to do
    }

  };

  IRosterManager rosterManager;

  IRosterListener rosterListener = new IRosterListener() {
    public void handleRosterEntryAdd(IRosterEntry entry) {
      // nothing to do
    }

    public void handleRosterEntryRemove(IRosterEntry entry) {
      // nothing to do
    }

    public void handleRosterUpdate(IRoster roster, IRosterItem changedValue) {
      if (changedValue instanceof IRosterEntry) {
        ID changedID = ((IRosterEntry) changedValue).getUser().getID();
        ID oID = null;
        ID otherID = null;
        Shell shell = null;
        synchronized (stateLock) {
          oID = getOurID();
          otherID = getOtherID();
          IWorkbenchPartSite wps = getTextEditor().getSite();
          shell = wps.getShell();
        }
        if (oID != null && changedID.equals(oID)) {
          localStopShare();
          showStopShareMessage(shell, Messages.DocShare_STOP_SHARED_EDITOR_US);
        } else if (otherID != null && changedID.equals(otherID)) {
          localStopShare();
          showStopShareMessage(shell, Messages.DocShare_STOP_SHARED_EDITOR_REMOTE);
        }
      }
    }

  };

  /**
   * 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() || !isSharing()) {
        return;
      }
      // If the listener is not active, ignore input
      if (!Activator.getDefault().isListenerActive()) {
        // The local editor is being updated by a remote peer, so we do
        // not
        // wish to echo this change.
        return;
      }
      Trace.trace(Activator.PLUGIN_ID, NLS.bind("{0}.documentChanged[{1}]", DocShare.this, event)); //$NON-NLS-1$

      // SYNC API.  Here is entry point usage of sync API.  When a local document is changed by an editor,
      // this method will be called and the following code executed.  This code registers a DocumentChange
      // with the local syncStrategy instance via syncStrategy.registerLocalChange(IModelChange).
      // Model change messages returned from the registerLocalChange call are then sent (via ECF datashare channel)
      // to remote participant.
      IModelChangeMessage changeMessages[] = syncStrategy.registerLocalChange(new DocumentChangeMessage(event.getOffset(), event.getLength(), event.getText()));
      for (int i = 0; i < changeMessages.length; i++) {
        try {
          sendMessage(getOtherID(), changeMessages[i].serialize());
        } catch (final Exception e) {
          logError(Messages.DocShare_EXCEPTION_SEND_MESSAGE, e);
        }
      }
    }
  };

  ISelectionChangedListener selectionListener = new ISelectionChangedListener() {

    public void selectionChanged(final SelectionChangedEvent event) {
      // If the channel is gone, then no reason to handle this.
      if (getChannel() == null || !Activator.getDefault().isListenerActive()) {
        return;
      }
      // If the listener is not active, ignore input
      if (!Activator.getDefault().isListenerActive()) {
        // The local editor is being updated by a remote peer, so we do
        // not
        // wish to echo this change.
        return;
      }
      Trace.trace(Activator.PLUGIN_ID, NLS.bind("{0}.selectionChanged[{1}]", DocShare.this, event)); //$NON-NLS-1$

      if (!(event.getSelection() instanceof ITextSelection)) {
        return;
      }
      final ITextSelection textSelection = (ITextSelection) event.getSelection();
      final SelectionMessage msg = new SelectionMessage(textSelection.getOffset(), textSelection.getLength());

      try {
        sendMessage(getOtherID(), msg.serialize());
      } catch (final Exception e) {
        logError(Messages.DocShare_EXCEPTION_SEND_MESSAGE, e);
      }
    }

  };

  /**
   * 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 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);
    Assert.isNotNull(fName);
    Assert.isNotNull(editorPart);
    Display.getDefault().syncExec(new Runnable() {
      public void run() {
        try {
          // SYNC API.  Create the synchronization strategy instance
          syncStrategy = createSynchronizationStrategy(true);
          Assert.isNotNull(syncStrategy);

          // Get content from local document
          final String content = editorPart.getDocumentProvider().getDocument(editorPart.getEditorInput()).get();
          // Send start message with current content
          sendMessage(toID, new StartMessage(our, fName, toID, content, fileName).serialize());
          // Set local sharing start (to setup doc listener)
          localStartShare(getLocalRosterManager(), our, our, toID, editorPart);
        } catch (final Exception e) {
          logError(Messages.DocShare_ERROR_STARTING_EDITOR_TITLE, e);
          showErrorToUser(Messages.DocShare_ERROR_STARTING_EDITOR_TITLE, NLS.bind(Messages.DocShare_ERROR_STARTING_EDITOR_MESSAGE, e.getLocalizedMessage()));
        }
      }
    });
    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>.
   */
  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();
      syncStrategy = null;
    }
    localStopShare();
    Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "stopShare"); //$NON-NLS-1$
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.ecf.datashare.AbstractShare#handleMessage(org.eclipse.ecf.core.identity.ID,
   *      byte[])
   */
  protected void handleMessage(ID fromContainerID, byte[] data) {
    try {
      final IModelChangeMessage message = Message.deserialize(data);
      Assert.isNotNull(message);

      if (message instanceof DocumentChangeMessage) {
        handleUpdateMessage((DocumentChangeMessage) message);
      } else if (message instanceof SelectionMessage) {
        SelectionReceiver receiver = selectionReceiver;
        if (receiver != null) {
          receiver.handleMessage((SelectionMessage) message);
        }
      } else if (message instanceof StartMessage) {
        handleStartMessage((StartMessage) message);
      } else if (message instanceof StopMessage) {
        handleStopMessage((StopMessage) message);
      } else {
        throw new InvalidObjectException(NLS.bind(Messages.DocShare_EXCEPTION_INVALID_MESSAGE, message.getClass().getName()));
      }
    } catch (final Exception e) {
      logError(Messages.DocShare_EXCEPTION_HANDLE_MESSAGE, e);
    }
  }

  /**
   * 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.
   * @throws IDCreateException
   */
  protected void handleStartMessage(final StartMessage message) throws IDCreateException {
    final ID senderID = message.getSenderID();
    Assert.isNotNull(senderID);
    final String senderUsername = message.getSenderUsername();
    Assert.isNotNull(senderUsername);
    final ID our = message.getReceiverID();
    Assert.isNotNull(our);
    final String filename = message.getFilename();
    Assert.isNotNull(filename);
    final String documentContent = message.getDocumentContent();
    Assert.isNotNull(documentContent);

    //SYNC API. Create an instance of the synchronization strategy on the receiver
    syncStrategy = createSynchronizationStrategy(false);
    Assert.isNotNull(syncStrategy);

    // First synchronize on any state changes by getting stateLock
    synchronized (stateLock) {
      // If we are already sharing, or have non-null start content
      if (isSharing() || startContent != null) {
        sendStopMessage(senderID);
        // And we're done
        return;
      }
      // Otherwise set start content to the message-provided
      // documentContent
      startContent = documentContent;
    }
    // Then open UI and show text editor if appropriate
    Display.getDefault().asyncExec(new Runnable() {
      public void run() {
        try {
          // First, ask user if they want to receive the doc
          if (openReceiverDialog(senderID, senderUsername, filename)) {
            // If so, then we create a new DocShareEditorInput
            final DocShareEditorInput dsei = new DocShareEditorInput(getTempFileStore(senderUsername, filename, startContent), senderUsername, filename);
            // Then open up text editor
            final ITextEditor ep;
            IEditorPart editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().openEditor(dsei, getEditorIdForFileName(filename));
            // if it's not a text editor, offer it a chance to give us one
            if (editorPart != null && !(editorPart instanceof ITextEditor))
              ep = (ITextEditor) editorPart.getAdapter(ITextEditor.class);
            else
              ep = (ITextEditor) editorPart;
            // Then change our local state
            localStartShare(getLocalRosterManager(), our, senderID, our, ep);
          } else {
            // Send stop message to initiator
            sendStopMessage();
            // Then we stop the local share
            localStopShare();
          }
        } catch (final Exception e) {
          logError(Messages.DocShare_EXCEPTION_RECEIVING_MESSAGE_TITLE, e);
          showErrorToUser(Messages.DocShare_EXCEPTION_RECEIVING_MESSAGE_TITLE, NLS.bind(Messages.DocShare_EXCEPTION_RECEIVING_MESSAGE_MESSAGE, e.getLocalizedMessage()));
        }
      }
    });
  }

  IRosterManager getLocalRosterManager() {
    IContainer container = (IContainer) this.adapter.getAdapter(IContainer.class);
    if (container != null) {
      IPresenceContainerAdapter presenceContainerAdapter = (IPresenceContainerAdapter) container.getAdapter(IPresenceContainerAdapter.class);
      if (presenceContainerAdapter != null) {
        return presenceContainerAdapter.getRosterManager();
      }
    }
    return null;
  }

  void modifyStartContent(int offset, int length, String text) {
    final StringBuffer result = new StringBuffer(startContent.substring(0, offset));
    result.append(text);
    result.append(startContent.substring(offset + length));
    startContent = result.toString();
  }

  /**
   * This method called by the {@link #handleMessage(ID, byte[])} method if
   * the type of the message received is an update message.
   *
   * @param documentChangeMessage
   *            the UpdateMessage received.
   */
  protected void handleUpdateMessage(final DocumentChangeMessage documentChangeMessage) {
    synchronized (stateLock) {
      // If we're waiting on user to start then change the
      // startContent
      // directly
      if (startContent != null) {
        modifyStartContent(documentChangeMessage.getOffset(), documentChangeMessage.getLengthOfReplacedText(), documentChangeMessage.getText());
        // And we're done
        return;
      }
    }
    // Else replace in document directly
    Display.getDefault().asyncExec(new Runnable() {
      public void run() {
        try {
          Trace.entering(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_ENTERING, this.getClass(), "handleUpdateMessage", documentChangeMessage); //$NON-NLS-1$
          final IDocument document = getDocumentFromEditor();

          if (document != null) {
            Trace.trace(Activator.PLUGIN_ID, NLS.bind("{0}.handleUpdateMessage calling transformIncomingMessage", DocShare.this)); //$NON-NLS-1$

            // SYNC API.  Here a document change message has been received from remote via channel,
            // and is now passed to the syncStrategy for transformation.  The returned IModelChange[]
            // are then applied to the local document (after the synchronization strategy as transformed
            // them as necessary).
            IModelChange modelChanges[] = syncStrategy.transformRemoteChange(documentChangeMessage);

            // Make editor refuse input while we are applying changes
            setEditorToRefuseInput();

            for (int i = 0; i < modelChanges.length; i++) {
              // Apply each change to a model.  Clients may use this method
              // to apply the change to a model of appropriate type
              modelChanges[i].applyToModel(document);
            }
          }
        } catch (final Exception e) {
          logError(Messages.DocShare_EXCEPTION_RECEIVING_MESSAGE_TITLE, e);
          showErrorToUser(Messages.DocShare_EXCEPTION_RECEIVING_MESSAGE_TITLE, NLS.bind(Messages.DocShare_EXCEPTION_RECEIVING_MESSAGE_MESSAGE, e.getLocalizedMessage()));
        } finally {
          // Have editor accept input
          setEditorToAcceptInput();
          Trace.exiting(Activator.PLUGIN_ID, DocshareDebugOptions.METHODS_EXITING, this.getClass(), "handleUpdateMessage"); //$NON-NLS-1$
        }
      }
    });
  }

  /**
   * @param message
   */
  protected void handleStopMessage(StopMessage message) {
    if (isSharing()) {
      Shell s = editor.getSite().getShell();
      localStopShare();
      syncStrategy = null;
      showStopShareMessage(s, Messages.DocShare_REMOTE_USER_STOPPED);
    }
  }

  void setEditorToRefuseInput() {
    setEditorEditable(false);
    Activator.getDefault().setListenerActive(false);
  }

  void setEditorToAcceptInput() {
    setEditorEditable(true);
    Activator.getDefault().setListenerActive(true);
  }

  ITextEditor getEditor() {
    return editor;
  }

  IEditorInput getEditorInput() {
    synchronized (stateLock) {
      if (editor == null)
        return null;
      return editor.getEditorInput();
    }
  }

  boolean openReceiverDialog(ID fromID, String fromUsername, String fileName) {
    return MessageDialog.openQuestion(null, Messages.DocShare_EDITOR_SHARE_POPUP_TITLE, NLS.bind(Messages.DocShare_EDITOR_SHARE_POPUP_MESSAGE, fromUsername, fileName));
  }

  protected void handleDisconnectEvent(IChannelDisconnectEvent cde) {
    boolean weDisconnected = (ourID != null && ourID.equals(cde.getTargetID()));
    Shell shell = null;
    if (isSharing()) {
      shell = editor.getSite().getShell();
    }
    // Stop things and *then* notify user
    localStopShare();
    if (shell != null) {
      if (weDisconnected)
        showStopShareMessage(shell, Messages.DocShare_STOP_SHARED_EDITOR_US);
      else
        showStopShareMessage(shell, Messages.DocShare_STOP_SHARED_EDITOR_REMOTE);
    }
  }

  /**
   * @param shell must not be <code>null</code>
   * @param message message content for message dialog
   */
  void showStopShareMessage(final Shell shell, final String message) {
    Display display = shell.getDisplay();
    display.asyncExec(new Runnable() {
      public void run() {
        MessageDialog.openInformation(shell, Messages.DocShare_STOP_SHARED_EDITOR_TITLE, message);
      }
    });
  }

  IFileStore getTempFileStore(String fromUsername, String fileName, String content) throws IOException, CoreException {
    final IFileStore fileStore = EFS.getLocalFileSystem().fromLocalFile(File.createTempFile(fromUsername, fileName));
    final OutputStream outs = fileStore.openOutputStream(EFS.OVERWRITE, null);
    outs.write(content.getBytes());
    outs.close();
    return fileStore;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.ecf.datashare.AbstractShare#dispose()
   */
  public synchronized void dispose() {
    localStopShare();
    super.dispose();
  }

  void logError(IStatus status) {
    Activator.getDefault().getLog().log(status);
  }

  void showErrorToUser(String title, String message) {
    MessageDialog.openError(null, title, message);
  }

  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));
  }

  StyledText getTextControl() {
    synchronized (stateLock) {
      if (editor == null)
        return null;
      return (StyledText) editor.getAdapter(Control.class);
    }
  }

  void setEditorEditable(final boolean editable) {
    final StyledText textControl = getTextControl();
    if (textControl != null && !textControl.isDisposed()) {
      Display.getDefault().syncExec(new Runnable() {
        public void run() {
          textControl.setEditable(editable);
        }
      });
    }
  }

  String getEditorIdForFileName(String fileName) {
    final IWorkbench wb = PlatformUI.getWorkbench();
    final IEditorRegistry er = wb.getEditorRegistry();
    final IEditorDescriptor desc = er.getDefaultEditor(fileName);
    if (desc != null)
      return desc.getId();
    return EditorsUI.DEFAULT_TEXT_EDITOR_ID;
  }

  IDocument getDocumentFromEditor() {
    synchronized (stateLock) {
      ITextEditor textEditor = getTextEditor();
      if (textEditor == null)
        return null;
      final IDocumentProvider documentProvider = textEditor.getDocumentProvider();
      if (documentProvider == null)
        return null;
      return documentProvider.getDocument(textEditor.getEditorInput());
    }
  }

  void localStartShare(final IRosterManager rm, ID our, ID initiator, ID receiver, ITextEditor edt) {
    synchronized (stateLock) {
      localStopShare();
      this.rosterManager = rm;
      if (this.rosterManager != null) {
        this.rosterManager.addRosterListener(rosterListener);
      }
      this.ourID = our;
      this.initiatorID = initiator;
      this.receiverID = receiver;
      this.editor = edt;
      this.editor.getSite().getPage().addPartListener(partListener);
      final IDocument doc = getDocumentFromEditor();
      if (doc != null)
        doc.addDocumentListener(documentListener);
      if (this.editor != null) {
        ISelectionProvider selectionProvider = this.editor.getSelectionProvider();
        if (selectionProvider instanceof IPostSelectionProvider) {
          ((IPostSelectionProvider) selectionProvider).addPostSelectionChangedListener(selectionListener);
        }
        selectionReceiver = new SelectionReceiver(this.editor);
      }
    }

  }

  void localStopShare() {
    SelectionReceiver oldSelectionReceiver;
    synchronized (stateLock) {
      if (rosterManager != null)
        rosterManager.removeRosterListener(rosterListener);
      this.rosterManager = null;
      this.ourID = null;
      this.initiatorID = null;
      this.receiverID = null;
      this.startContent = null;
      final IDocument doc = getDocumentFromEditor();
      if (doc != null)
        doc.removeDocumentListener(documentListener);
      if (this.editor != null) {
        this.editor.getSite().getPage().removePartListener(partListener);

        ISelectionProvider selectionProvider = this.editor.getSelectionProvider();
        if (selectionProvider instanceof IPostSelectionProvider) {
          ((IPostSelectionProvider) selectionProvider).removePostSelectionChangedListener(selectionListener);
        }
      }
      oldSelectionReceiver = this.selectionReceiver;
      this.selectionReceiver = null;

      this.editor = null;
    }

    if (oldSelectionReceiver != null) {
      oldSelectionReceiver.dispose();
    }
  }

  void sendStopMessage() {
    sendStopMessage(getOtherID());
  }

  void sendStopMessage(ID other) {
    if (isSharing()) {
      try {
        super.sendMessage(other, new StopMessage().serialize());
      } catch (final Exception e) {
        logError(Messages.DocShare_EXCEPTION_SEND_MESSAGE, e);
      }
    }
  }

  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(syncStrategy).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
    return buf.toString();
  }

  IModelSynchronizationStrategy createSynchronizationStrategy(boolean isInitiator) {
    //Instantiate the service
    Assert.isNotNull(factory);
    return factory.createDocumentSynchronizationStrategy(getChannel().getID(), isInitiator);
  }

  public ID getInitiatorID() {
    return initiatorID;
  }

  public ID getReceiverID() {
    return receiverID;
  }

  public ID getOurID() {
    return ourID;
  }

  public ITextEditor getTextEditor() {
    return this.editor;
  }

  public boolean isSharing() {
    synchronized (stateLock) {
      return (this.editor != null);
    }
  }

  public ID getOtherID() {
    synchronized (stateLock) {
      if (isInitiator())
        return receiverID;
      return initiatorID;
    }
  }

  public boolean isInitiator() {
    synchronized (stateLock) {
      if (ourID == null || initiatorID == null || receiverID == null)
        return false;
      return ourID.equals(initiatorID);
    }
  }
}
TOP

Related Classes of org.eclipse.ecf.docshare.DocShare

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.