package org.codemap.communication; import java.util.Collection; import org.codemap.CodemapCore; import org.codemap.MapSelection; import org.codemap.communication.messages.Message; import org.codemap.communication.messages.SelectionMessage; import org.codemap.communication.messages.StartMessage; import org.codemap.communication.messages.StopMessage; import org.codemap.communication.util.EditorPartListener; import org.codemap.util.Log; import org.eclipse.core.runtime.Assert; import org.eclipse.ecf.core.IContainer; import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.core.util.ECFException; import org.eclipse.ecf.datashare.AbstractShare; import org.eclipse.ecf.datashare.IChannelContainerAdapter; import org.eclipse.ecf.datashare.events.IChannelDisconnectEvent; import org.eclipse.ecf.presence.IPresenceContainerAdapter; import org.eclipse.ecf.presence.roster.IRoster; import org.eclipse.ecf.presence.roster.IRosterEntry; import org.eclipse.ecf.presence.roster.IRosterItem; import org.eclipse.ecf.presence.roster.IRosterListener; import org.eclipse.ecf.presence.roster.IRosterManager; import org.eclipse.ecf.sync.IModelSynchronizationStrategy; import org.eclipse.ecf.sync.SerializationException; import org.eclipse.ecf.sync.doc.IDocumentSynchronizationStrategyFactory; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchPage; public class SelectionShare extends AbstractShare { IRosterListener rosterListener = new IRosterListener() { public void handleRosterEntryAdd(IRosterEntry entry) { // nothing to do } public void handleRosterEntryRemove(IRosterEntry entry) { // nothing to do } /** * handle changes to the roster. * AFAIK: this might be a local or a remote disconnect. */ public void handleRosterUpdate(IRoster roster, IRosterItem changedValue) { if (!(changedValue instanceof IRosterEntry)) return; IRosterEntry rosterEntry = (IRosterEntry) changedValue; System.out.println(rosterEntry); ID changedID = rosterEntry.getUser().getID(); String message = null; synchronized (SelectionShare.this) { if (changedID.equals(ourID)) { localStopShare(); message = "Sharing aborted locally."; } else if (changedID.equals(remoteID)) { localStopShare(); message = "Sharing aborted remotely."; } } if (message != null) showStopShareMessage(message); } }; private IModelSynchronizationStrategy syncStrategy; private IDocumentSynchronizationStrategyFactory factory; private IRosterManager rosterManager; private ID ourID; private ID remoteID; private EditorPartListener listener; public SelectionShare(IChannelContainerAdapter adapter) throws ECFException { super(adapter); factory = ECFContribution.getDefault().getColaSynchronizationStrategyFactory(); } @Override protected void handleMessage(ID fromContainerID, byte[] data) { Message message; try { message = Message.deserialize(data); Assert.isNotNull(message); message.applyOn(this); } catch (SerializationException e) { Log.error(e); } } @Override protected void handleDisconnectEvent(IChannelDisconnectEvent cde) { boolean weDisconnected = (ourID != null && ourID.equals(cde.getTargetID())); boolean wasSharing = isSharing(); // Stop things and *then* notify user localStopShare(); if (wasSharing) { // TODO: use some more verbose messages (check whole file ...) if (weDisconnected) showStopShareMessage("We stopped sharing."); else showStopShareMessage("Remote stopped sharing."); } } protected boolean openReceiverDialog(ID sender) { return MessageDialog.openQuestion(null, "Share your Selection.", "Do you want to share your Codemap selection with " + sender.getName() + "?"); } protected void showStopShareMessage(final String message) { final Display display = Display.getDefault(); display.asyncExec(new Runnable() { public void run() { MessageDialog.openInformation(display.getActiveShell(), "Stopped sharing.", message); } }); } /** * Called when we are on the remote side. */ public synchronized void handleStartRequest(final Message message) { remoteID = message.senderID; Assert.isNotNull(remoteID); ourID = message.receiverID; Assert.isNotNull(ourID); // execute async to avoid nested monitor in callbacks Display.getDefault().asyncExec(new Runnable(){ @Override public void run() { synchronized(SelectionShare.this) { if (openReceiverDialog(message.senderID)) { // SYNC API. Create an instance of the synchronization strategy on // the receiver syncStrategy = createSynchronizationStrategy(false); Assert.isNotNull(syncStrategy); addEditorListener(); } else { sendStopMessage(); localStopShare(); } } } }); } /** * Add a listener that notifies us when the files are opened/closed in an * editor. */ private void addEditorListener() { // needs to run in an UI Thread to have access to the workbench. if (listener == null) { listener = new EditorPartListener(SelectionShare.this); } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { // needs to run in an UI Thread to have access to the workbench. IWorkbenchPage page = CodemapCore.getPlugin().getWorkbench().getActiveWorkbenchWindow().getActivePage(); page.addPartListener(listener); } }); } public synchronized boolean isSharing() { return this.remoteID != null; } public synchronized void startShare(final ID our, final ID remote) { Assert.isNotNull(our); ourID = our; Assert.isNotNull(remote); remoteID = remote; syncStrategy = createSynchronizationStrategy(true); Assert.isNotNull(syncStrategy); sendMessageToRemote(new StartMessage(ourID, remoteID)); localStartShare(getLocalRosterManager()); } private void localStartShare(IRosterManager localRosterManager) { this.rosterManager = localRosterManager; if (this.rosterManager != null) { this.rosterManager.addRosterListener(rosterListener); } addEditorListener(); } private 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; } private IModelSynchronizationStrategy createSynchronizationStrategy(boolean isInitiator) { // Instantiate the service Assert.isNotNull(factory); return factory.createDocumentSynchronizationStrategy(getChannel().getID(), isInitiator); } public synchronized void onLocalSelectionChanged(Collection<String> selection) { sendMessageToRemote(new SelectionMessage(ourID, remoteID, selection)); } public synchronized String getRemoteName() { if (remoteID == null) return ""; return remoteID.getName(); } public synchronized void stopShare() { sendStopMessage(); localStopShare(); } private void sendStopMessage() { sendMessageToRemote(new StopMessage(remoteID, ourID)); } private void localStopShare() { getSelection().clear(); ourID = null; remoteID = null; syncStrategy = null; } private void sendMessageToRemote(Message message) { try { sendMessage(remoteID, message.serialize()); } catch (SerializationException e) { Log.error(e); } catch (ECFException e) { Log.error(e); } } public synchronized void handleRemoteStop(StopMessage stopMessage) { getSelection().clear(); ourID = null; remoteID = null; syncStrategy = null; removeEditorListener(); showStopShareMessage("The remote User stopped sharing."); } private void removeEditorListener() { if (listener == null) return; Display.getDefault().syncExec(new Runnable() { @Override public void run() { IWorkbenchPage page = CodemapCore.getPlugin().getWorkbench().getActiveWorkbenchWindow().getActivePage(); page.removePartListener(listener); } }); } public synchronized void handleRemoteSelection(Collection<String> selection) { getSelection().replaceAll(selection); } private MapSelection getSelection() { return ECFContribution.getDefault().getCommunicationSelection(); } }