summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/isode/stroke/whiteboard')
-rw-r--r--src/com/isode/stroke/whiteboard/IncomingWhiteboardSession.java60
-rw-r--r--src/com/isode/stroke/whiteboard/OutgoingWhiteboardSession.java76
-rw-r--r--src/com/isode/stroke/whiteboard/WhiteboardClient.java149
-rw-r--r--src/com/isode/stroke/whiteboard/WhiteboardResponder.java52
-rw-r--r--src/com/isode/stroke/whiteboard/WhiteboardServer.java70
-rw-r--r--src/com/isode/stroke/whiteboard/WhiteboardSession.java89
-rw-r--r--src/com/isode/stroke/whiteboard/WhiteboardSessionManager.java169
-rw-r--r--src/com/isode/stroke/whiteboard/WhiteboardTransformer.java264
8 files changed, 929 insertions, 0 deletions
diff --git a/src/com/isode/stroke/whiteboard/IncomingWhiteboardSession.java b/src/com/isode/stroke/whiteboard/IncomingWhiteboardSession.java
new file mode 100644
index 0000000..0ed9a01
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/IncomingWhiteboardSession.java
@@ -0,0 +1,60 @@
+package com.isode.stroke.whiteboard;
+
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.WhiteboardOperation;
+import com.isode.stroke.elements.WhiteboardPayload;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+
+public class IncomingWhiteboardSession extends WhiteboardSession {
+
+ private final WhiteboardClient client = new WhiteboardClient();
+
+ public IncomingWhiteboardSession(JID jid, IQRouter router) {
+ super(jid, router);
+ }
+
+ public void accept() {
+ WhiteboardPayload payload = new WhiteboardPayload(WhiteboardPayload.Type.SessionAccept);
+ GenericRequest<WhiteboardPayload> request =
+ new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
+ request.send();
+ onRequestAccepted.emit(toJID_);
+ }
+
+ @Override
+ public void sendOperation(WhiteboardOperation operation) {
+ operation.setID(idGenerator_.generateID());
+ operation.setParentID(lastOpID_);
+ lastOpID_ = operation.getID();
+
+ WhiteboardOperation result = client.handleLocalOperationReceived(operation);
+
+ if (result != null) {
+ WhiteboardPayload payload = new WhiteboardPayload();
+ payload.setOperation(result);
+ sendPayload(payload);
+ }
+ }
+
+ @Override
+ protected void handleIncomingOperation(WhiteboardOperation operation) {
+ WhiteboardClient.Result pairResult = client.handleServerOperationReceived(operation);
+ if (pairResult.client != null) {
+ if (pairResult.client.getPos() != -1) {
+ onOperationReceived.emit(pairResult.client);
+ }
+ lastOpID_ = pairResult.client.getID();
+ }
+
+ if (pairResult.server != null) {
+ WhiteboardPayload payload = new WhiteboardPayload();
+ payload.setOperation(pairResult.server);
+ sendPayload(payload);
+ }
+ }
+
+
+
+}
diff --git a/src/com/isode/stroke/whiteboard/OutgoingWhiteboardSession.java b/src/com/isode/stroke/whiteboard/OutgoingWhiteboardSession.java
new file mode 100644
index 0000000..3a79a4c
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/OutgoingWhiteboardSession.java
@@ -0,0 +1,76 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.whiteboard;
+
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.WhiteboardOperation;
+import com.isode.stroke.elements.WhiteboardPayload;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Slot2;
+
+public class OutgoingWhiteboardSession extends WhiteboardSession {
+
+ private final WhiteboardServer server = new WhiteboardServer();
+
+ public OutgoingWhiteboardSession(JID jid, IQRouter router) {
+ super(jid, router);
+ }
+
+ public void startSession() {
+
+ WhiteboardPayload payload = new WhiteboardPayload(WhiteboardPayload.Type.SessionRequest);
+ GenericRequest<WhiteboardPayload> request =
+ new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
+ request.onResponse.connect(new Slot2<WhiteboardPayload, ErrorPayload>() {
+
+ @Override
+ public void call(WhiteboardPayload payload, ErrorPayload error) {
+ handleRequestResponse(payload, error);
+ }
+
+ });
+ request.send();
+ }
+
+ private void handleRequestResponse(WhiteboardPayload payload,ErrorPayload error) {
+ if (error != null) {
+ onRequestRejected.emit(toJID_);
+ }
+ }
+
+ @Override
+ public void sendOperation(WhiteboardOperation operation) {
+ operation.setID(idGenerator_.generateID());
+ operation.setParentID(lastOpID_);
+ lastOpID_ = operation.getID();
+
+ server.handleLocalOperationReceived(operation);
+ WhiteboardPayload payload = new WhiteboardPayload();
+ payload.setOperation(operation);
+ sendPayload(payload);
+ }
+
+ @Override
+ protected void handleIncomingOperation(WhiteboardOperation operation) {
+ WhiteboardOperation op = server.handleClientOperationReceived(operation);
+ if (op.getPos() != -1) {
+ onOperationReceived.emit(op);
+ }
+ lastOpID_ = op.getID();
+
+ WhiteboardPayload payload = new WhiteboardPayload();
+ payload.setOperation(op);
+ sendPayload(payload);
+ }
+
+}
diff --git a/src/com/isode/stroke/whiteboard/WhiteboardClient.java b/src/com/isode/stroke/whiteboard/WhiteboardClient.java
new file mode 100644
index 0000000..7b7bb10
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/WhiteboardClient.java
@@ -0,0 +1,149 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.whiteboard;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.isode.stroke.elements.WhiteboardDeleteOperation;
+import com.isode.stroke.elements.WhiteboardInsertOperation;
+import com.isode.stroke.elements.WhiteboardOperation;
+import com.isode.stroke.elements.WhiteboardUpdateOperation;
+import com.isode.stroke.whiteboard.WhiteboardTransformer.Pair;
+
+public class WhiteboardClient {
+
+ private final List<WhiteboardOperation> localOperations_ = new ArrayList<WhiteboardOperation>();
+ private final List<WhiteboardOperation> serverOperations_ = new ArrayList<WhiteboardOperation>();
+ private final List<WhiteboardOperation> bridge_ = new ArrayList<WhiteboardOperation>();
+ private String lastSentOperationID_ = "";
+
+ public static class Result {
+ public final WhiteboardOperation client;
+ public final WhiteboardOperation server;
+ public Result(WhiteboardOperation client,WhiteboardOperation server) {
+ this.client = client;
+ this.server = server;
+ }
+ }
+
+ public WhiteboardOperation handleLocalOperationReceived(WhiteboardOperation operation) {
+ localOperations_.add(operation);
+
+ WhiteboardOperation op = null;
+ if (operation instanceof WhiteboardInsertOperation) {
+ op = new WhiteboardInsertOperation((WhiteboardInsertOperation) operation);
+ }
+ else if (operation instanceof WhiteboardUpdateOperation) {
+ op = new WhiteboardUpdateOperation((WhiteboardUpdateOperation) operation);
+ }
+ else if (operation instanceof WhiteboardDeleteOperation) {
+ op = new WhiteboardDeleteOperation((WhiteboardDeleteOperation) operation);
+ }
+
+ if (!bridge_.isEmpty()) {
+ WhiteboardOperation back = bridge_.get(bridge_.size()-1);
+ op.setParentID(back.getID());
+ }
+ bridge_.add(op);
+
+ if (lastSentOperationID_.isEmpty()) {
+ if (operation instanceof WhiteboardInsertOperation) {
+ op = new WhiteboardInsertOperation((WhiteboardInsertOperation) operation);
+ }
+ else if (operation instanceof WhiteboardUpdateOperation) {
+ op = new WhiteboardUpdateOperation((WhiteboardUpdateOperation) operation);
+ }
+ else if (operation instanceof WhiteboardDeleteOperation) {
+ op = new WhiteboardDeleteOperation((WhiteboardDeleteOperation) operation);
+ }
+
+ if (!serverOperations_.isEmpty()) {
+ WhiteboardOperation back = serverOperations_.get(serverOperations_.size()-1);
+ op.setParentID(back.getID());
+ }
+ lastSentOperationID_ = operation.getID();
+ return op;
+ }
+ else{
+ return null;
+ }
+ }
+
+ public Result handleServerOperationReceived(WhiteboardOperation operation) {
+ serverOperations_.add(operation);
+ WhiteboardOperation clientResult = null, serverResult = null;
+ if (localOperations_.size() == (serverOperations_.size()-1) ) {
+ localOperations_.add(operation);
+ clientResult = operation;
+ }
+ else if (lastSentOperationID_ == operation.getID()) {
+ //Client received confirmation about own operation and it sends next operation to server
+ if (!bridge_.isEmpty() && lastSentOperationID_ .equals(bridge_.get(0).getID())) {
+ bridge_.remove(0);
+ }
+
+ if (!bridge_.isEmpty() && lastSentOperationID_ .equals(bridge_.get(0).getParentID())) {
+ lastSentOperationID_ = bridge_.get(0).getID();
+ serverResult = bridge_.get(0);
+ }
+ if (serverResult == null) {
+ lastSentOperationID_ = "";
+ }
+ }
+ else if (!bridge_.isEmpty()) {
+ WhiteboardOperation temp;
+ Pair opPair = WhiteboardTransformer.transform(bridge_.get(0), operation);
+ temp = opPair.first;
+
+ bridge_.set(0, opPair.second);
+ String previousID = bridge_.get(0).getID();
+ for (int i = 1; i < bridge_.size(); ++i) {
+ opPair = WhiteboardTransformer.transform(bridge_.get(i), temp);
+ temp = opPair.first;
+ bridge_.set(i, opPair.second);
+ bridge_.get(i).setParentID(previousID);
+ previousID = bridge_.get(i).getID();
+ }
+
+ WhiteboardOperation localBack = localOperations_.get(localOperations_.size()-1);
+ temp.setParentID(localBack.getID());
+ localOperations_.add(temp);
+ clientResult = temp;
+ }
+
+ return new Result(clientResult, serverResult);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Client\n");
+ for (WhiteboardOperation op : localOperations_) {
+ builder.append(op.getID());
+ builder.append(' ');
+ builder.append(op.getPos());
+ builder.append('\n');
+ }
+ builder.append("Server\n");
+ for (WhiteboardOperation op : serverOperations_) {
+ builder.append(op.getID());
+ builder.append(' ');
+ builder.append(op.getPos());
+ builder.append('\n');
+ }
+ return builder.toString();
+ }
+
+ public void print() {
+ System.out.println(this);
+ }
+
+}
diff --git a/src/com/isode/stroke/whiteboard/WhiteboardResponder.java b/src/com/isode/stroke/whiteboard/WhiteboardResponder.java
new file mode 100644
index 0000000..6f80115
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/WhiteboardResponder.java
@@ -0,0 +1,52 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.whiteboard;
+
+import com.isode.stroke.elements.WhiteboardPayload;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.queries.SetResponder;
+import com.isode.stroke.elements.ErrorPayload;
+
+public class WhiteboardResponder extends SetResponder<WhiteboardPayload> {
+
+ private final WhiteboardSessionManager sessionManager_;
+ private final IQRouter router_;
+
+ public WhiteboardResponder(WhiteboardSessionManager sessionManager,IQRouter router) {
+ super(new WhiteboardPayload(),router);
+ sessionManager_ = sessionManager;
+ router_ = router;
+ }
+
+ @Override
+ protected boolean handleSetRequest(JID from, JID to, String id,
+ WhiteboardPayload payload) {
+ if (payload.getType() == WhiteboardPayload.Type.SessionRequest) {
+ if (sessionManager_.getSession(from) != null) {
+ sendError(from, id, ErrorPayload.Condition.Conflict, ErrorPayload.Type.Cancel);
+ }
+ else {
+ sendResponse(from, id, null);
+ IncomingWhiteboardSession session = new IncomingWhiteboardSession(from, router_);
+ sessionManager_.handleIncomingSession(session);
+ }
+ }
+ else {
+ sendResponse(from, id, null);
+ WhiteboardSession session = sessionManager_.getSession(from);
+ if (session != null) {
+ session.handleIncomingAction(payload);
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/com/isode/stroke/whiteboard/WhiteboardServer.java b/src/com/isode/stroke/whiteboard/WhiteboardServer.java
new file mode 100644
index 0000000..7685671
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/WhiteboardServer.java
@@ -0,0 +1,70 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.whiteboard;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.isode.stroke.elements.WhiteboardOperation;
+
+public class WhiteboardServer {
+
+ private final List<WhiteboardOperation> operations_ = new ArrayList<WhiteboardOperation>();
+
+ public void handleLocalOperationReceived(WhiteboardOperation operation) {
+ operations_.add(operation);
+ }
+
+ public WhiteboardOperation handleClientOperationReceived(WhiteboardOperation newOperation) {
+
+ if (operations_.isEmpty() ||
+ newOperation.getParentID().equals(operations_.get(operations_.size()-1).getID())) {
+ operations_.add(newOperation);
+ return newOperation;
+ }
+ for (int i = (operations_.size()-1); i >= 0; i--) {
+ WhiteboardOperation operation = operations_.get(i);
+ while (newOperation.getParentID().equals(operation.getParentID())) {
+ WhiteboardTransformer.Pair tResult =
+ WhiteboardTransformer.transform(newOperation, operation);
+ if (i == (operations_.size()-1)) {
+ operations_.add(tResult.second);
+ return tResult.second;
+ }
+ else {
+ newOperation = tResult.second;
+ i++;
+ operation = operations_.get(i);
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Server:\n");
+ for (WhiteboardOperation op : operations_) {
+ builder.append(op.getID());
+ builder.append(" '");
+ builder.append(op.getParentID());
+ builder.append("' ");
+ builder.append(op.getPos());
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+
+ public void print() {
+ System.out.println(this);
+ }
+
+}
diff --git a/src/com/isode/stroke/whiteboard/WhiteboardSession.java b/src/com/isode/stroke/whiteboard/WhiteboardSession.java
new file mode 100644
index 0000000..cc1957e
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/WhiteboardSession.java
@@ -0,0 +1,89 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.whiteboard;
+
+import com.isode.stroke.base.IDGenerator;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.WhiteboardElement;
+import com.isode.stroke.elements.WhiteboardOperation;
+import com.isode.stroke.elements.WhiteboardPayload;
+import com.isode.stroke.elements.WhiteboardPayload.Type;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Signal1;
+
+public abstract class WhiteboardSession {
+
+ protected final JID toJID_;
+ protected final IQRouter router_;
+ protected String lastOpID_ = "";
+ protected final IDGenerator idGenerator_ = new IDGenerator();
+
+ public final Signal1<WhiteboardElement> onElementReceived = new Signal1<WhiteboardElement>();
+ public final Signal1<WhiteboardOperation> onOperationReceived = new Signal1<WhiteboardOperation>();
+ public final Signal1<JID> onSessionTerminated = new Signal1<JID>();
+ public final Signal1<JID> onRequestAccepted = new Signal1<JID>();
+ public final Signal1<JID> onRequestRejected = new Signal1<JID>();
+
+ public WhiteboardSession(JID jid,IQRouter router) {
+ toJID_ = jid;
+ router_ = router;
+ }
+
+ public void handleIncomingAction(WhiteboardPayload payload) {
+ switch(payload.getType()) {
+ case Data:
+ handleIncomingOperation(payload.getOperation());
+ return;
+ case SessionAccept:
+ onRequestAccepted.emit(toJID_);
+ return;
+ case SessionTerminate:
+ onSessionTerminated.emit(toJID_);
+ return;
+ case SessionRequest:
+ case UnknownType:
+ default:
+ return;
+ }
+ }
+ public void sendElement(WhiteboardElement element) {
+ WhiteboardPayload payload = new WhiteboardPayload();
+ payload.setElement(element);
+ GenericRequest<WhiteboardPayload> request =
+ new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
+ request.send();
+ }
+
+ public abstract void sendOperation(WhiteboardOperation operation);
+
+ public void cancel() {
+ if (router_.isAvailable()) {
+ WhiteboardPayload payload = new WhiteboardPayload(Type.SessionTerminate);
+ GenericRequest<WhiteboardPayload> request =
+ new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
+ request.send();
+ }
+ onSessionTerminated.emit(toJID_);
+ }
+
+ public JID getTo() {
+ return toJID_;
+ }
+
+ protected abstract void handleIncomingOperation(WhiteboardOperation operation);
+
+ protected final void sendPayload(WhiteboardPayload payload) {
+ GenericRequest<WhiteboardPayload> request = new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
+ request.send();
+ }
+
+}
diff --git a/src/com/isode/stroke/whiteboard/WhiteboardSessionManager.java b/src/com/isode/stroke/whiteboard/WhiteboardSessionManager.java
new file mode 100644
index 0000000..d797303
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/WhiteboardSessionManager.java
@@ -0,0 +1,169 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.whiteboard;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.disco.EntityCapsProvider;
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.presence.PresenceOracle;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Slot1;
+
+public class WhiteboardSessionManager {
+
+ private final Map<JID,WhiteboardSession> sessions_ = new HashMap<JID,WhiteboardSession>();
+ private final IQRouter router_;
+ private final StanzaChannel stanzaChannel_;
+ private final PresenceOracle presenceOracle_;
+ private final EntityCapsProvider capsProvider_;
+ private WhiteboardResponder responder;
+
+ public final Signal1<IncomingWhiteboardSession> onSessionRequest =
+ new Signal1<IncomingWhiteboardSession>();
+
+ public WhiteboardSessionManager(IQRouter router, StanzaChannel stanzaChannel,
+ PresenceOracle presenceOracle, EntityCapsProvider capsProvider) {
+ router_ = router;
+ stanzaChannel_ = stanzaChannel;
+ presenceOracle_ = presenceOracle;
+ capsProvider_ = capsProvider;
+ responder = new WhiteboardResponder(this, router_);
+ responder.start();
+ stanzaChannel_.onPresenceReceived.connect(new Slot1<Presence>() {
+
+ @Override
+ public void call(Presence presence) {
+ handlePresenceReceived(presence);
+ }
+
+ });
+ stanzaChannel_.onAvailableChanged.connect(new Slot1<Boolean>() {
+
+ @Override
+ public void call(Boolean p1) {
+ handleAvailableChanged(p1.booleanValue());
+ }
+
+ });
+ }
+
+ // Unlike in C++ we can't put this in a destructor to automatically be called when object is
+ // destroyed. Must be called manually.
+ public void stop() {
+ responder.stop();
+ }
+
+ public WhiteboardSession getSession(JID to) {
+ return sessions_.get(to);
+ }
+
+ public WhiteboardSession requestSession(JID to) {
+ WhiteboardSession session = getSession(to);
+ if (session == null) {
+ OutgoingWhiteboardSession outgoingSession = createOutgoingSession(to);
+ outgoingSession.startSession();
+ return outgoingSession;
+ } else {
+ return session;
+ }
+ }
+
+ private JID getFullJID(JID bareJID) {
+ JID fullReceipientJID = null;
+ int priority = Integer.MIN_VALUE;
+
+ //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
+ List<Presence> presences =
+ new ArrayList<Presence>(presenceOracle_.getAllPresence(bareJID));
+
+ //iterate over them
+ for (Presence pres : presences) {
+ if (pres.getPriority() > priority) {
+ // look up caps from the jid
+ DiscoInfo info = capsProvider_.getCaps(pres.getFrom());
+ if (info != null && info.hasFeature(DiscoInfo.WhiteboardFeature)) {
+ priority = pres.getPriority();
+ fullReceipientJID = pres.getFrom();
+ }
+ }
+ }
+
+ return fullReceipientJID;
+ }
+
+ private OutgoingWhiteboardSession createOutgoingSession(JID to) {
+ JID fullJID = to;
+ if (fullJID.isBare()) {
+ fullJID = getFullJID(fullJID);
+ }
+ OutgoingWhiteboardSession session = new OutgoingWhiteboardSession(fullJID, router_);
+ sessions_.put(fullJID, session);
+ session.onSessionTerminated.connect(new Slot1<JID>() {
+
+ @Override
+ public void call(JID jid) {
+ deleteSessionEntry(jid);
+ }
+
+ });
+ session.onRequestRejected.connect(new Slot1<JID>() {
+
+ @Override
+ public void call(JID jid) {
+ deleteSessionEntry(jid);
+ }
+
+ });
+ return session;
+ }
+
+ public void handleIncomingSession(IncomingWhiteboardSession session) {
+ sessions_.put(session.getTo(), session);
+ session.onSessionTerminated.connect(new Slot1<JID>() {
+
+ @Override
+ public void call(JID jid) {
+ deleteSessionEntry(jid);
+ }
+
+ });
+ onSessionRequest.emit(session);
+ }
+
+ private void handlePresenceReceived(Presence presence) {
+ if (!presence.isAvailable()) {
+ WhiteboardSession session = getSession(presence.getFrom());
+ if (session != null) {
+ session.cancel();
+ }
+ }
+ }
+ private void handleAvailableChanged(boolean available) {
+ if (!available) {
+ Map<JID,WhiteboardSession> sessionsCopy = new HashMap<JID,WhiteboardSession>(sessions_);
+ for (WhiteboardSession session : sessionsCopy.values()) {
+ session.cancel();
+ }
+ }
+ }
+
+ private void deleteSessionEntry(JID contact) {
+ sessions_.remove(contact);
+ }
+
+}
diff --git a/src/com/isode/stroke/whiteboard/WhiteboardTransformer.java b/src/com/isode/stroke/whiteboard/WhiteboardTransformer.java
new file mode 100644
index 0000000..4d20498
--- /dev/null
+++ b/src/com/isode/stroke/whiteboard/WhiteboardTransformer.java
@@ -0,0 +1,264 @@
+/* Copyright (c) 2016, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written license agreement from Isode Limited,
+ * or a written license from an organisation licensed by Isode Limited
+ * to grant such a license.
+ *
+ */
+package com.isode.stroke.whiteboard;
+
+import com.isode.stroke.elements.WhiteboardDeleteOperation;
+import com.isode.stroke.elements.WhiteboardInsertOperation;
+import com.isode.stroke.elements.WhiteboardOperation;
+import com.isode.stroke.elements.WhiteboardUpdateOperation;
+
+public final class WhiteboardTransformer {
+
+ public static final class Pair {
+ public final WhiteboardOperation first;
+ public final WhiteboardOperation second;
+ public Pair(WhiteboardOperation first,WhiteboardOperation second) {
+ this.first = first;
+ this.second = second;
+ }
+ public Pair() {
+ this(new WhiteboardOperation(),new WhiteboardOperation());
+ }
+ }
+
+ private WhiteboardTransformer() {
+ // Static class so constructor is private
+ }
+
+ public static Pair transform(WhiteboardOperation clientOp,WhiteboardOperation serverOp) {
+ if (clientOp instanceof WhiteboardInsertOperation) {
+ WhiteboardInsertOperation clientInsert = (WhiteboardInsertOperation) clientOp;
+ if (serverOp instanceof WhiteboardInsertOperation) {
+ WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp;
+ return transform(clientInsert, serverInsert);
+ }
+ else if (serverOp instanceof WhiteboardUpdateOperation) {
+ WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp;
+ return transform(clientInsert, serverUpdate);
+ }
+ else if (serverOp instanceof WhiteboardDeleteOperation) {
+ WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp;
+ return transform(clientInsert, serverDelete);
+ }
+ }
+ else if (clientOp instanceof WhiteboardUpdateOperation) {
+ WhiteboardUpdateOperation clientUpdate = (WhiteboardUpdateOperation) clientOp;
+ if (serverOp instanceof WhiteboardInsertOperation) {
+ WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp;
+ return transform(clientUpdate, serverInsert);
+ }
+ else if (serverOp instanceof WhiteboardUpdateOperation) {
+ WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp;
+ return transform(clientUpdate, serverUpdate);
+ }
+ else if (serverOp instanceof WhiteboardDeleteOperation) {
+ WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp;
+ return transform(clientUpdate, serverDelete);
+ }
+ }
+ else if (clientOp instanceof WhiteboardDeleteOperation) {
+ WhiteboardDeleteOperation clientDelete = (WhiteboardDeleteOperation) clientOp;
+ if (serverOp instanceof WhiteboardInsertOperation) {
+ WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp;
+ return transform(clientDelete, serverInsert);
+ }
+ else if (serverOp instanceof WhiteboardUpdateOperation) {
+ WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp;
+ return transform(clientDelete, serverUpdate);
+ }
+ else if (serverOp instanceof WhiteboardDeleteOperation) {
+ WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp;
+ return transform(clientDelete, serverDelete);
+ }
+ }
+ return new Pair();
+ }
+
+ public static Pair transform(WhiteboardInsertOperation clientOp,WhiteboardInsertOperation serverOp) {
+ WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp);
+ first.setParentID(clientOp.getID());
+ WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ if (clientOp.getPos() <= serverOp.getPos()) {
+ first.setPos(first.getPos()+1);
+ }
+ else {
+ second.setPos(second.getPos()+1);
+ }
+ return new Pair(first,second);
+ }
+
+ public static Pair transform(WhiteboardUpdateOperation clientOp,WhiteboardUpdateOperation serverOp) {
+
+ WhiteboardUpdateOperation first = new WhiteboardUpdateOperation(serverOp);
+ first.setParentID(clientOp.getID());
+
+ WhiteboardUpdateOperation second;
+ if (clientOp.getPos() == serverOp.getPos()) {
+ second = new WhiteboardUpdateOperation(serverOp);
+ second.setID(clientOp.getID());
+ second.setParentID(serverOp.getID());
+ }
+ else {
+ second = new WhiteboardUpdateOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ }
+
+ if (clientOp.getPos() < serverOp.getPos() && clientOp.getNewPos() > serverOp.getPos()) {
+ first.setPos(first.getPos()-1);
+ if (clientOp.getNewPos() >= serverOp.getNewPos()) {
+ first.setNewPos(first.getNewPos()-1);
+ }
+ }
+ else if (clientOp.getNewPos() >= serverOp.getNewPos()) {
+ first.setNewPos(first.getNewPos()-1);
+ }
+ if (serverOp.getPos() < clientOp.getPos() && serverOp.getNewPos() > clientOp.getPos()) {
+ second.setPos(second.getPos()-1);
+ if (serverOp.getNewPos() >= clientOp.getNewPos()) {
+ second.setNewPos(second.getNewPos()-1);
+ }
+ }
+ else if (serverOp.getNewPos() >= clientOp.getNewPos()) {
+ second.setNewPos(second.getNewPos()-1);
+ }
+ return new Pair(first,second);
+ }
+
+ public static Pair transform(WhiteboardUpdateOperation clientOp,WhiteboardInsertOperation serverOp) {
+ WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp);
+ first.setParentID(clientOp.getID());
+ WhiteboardUpdateOperation second = new WhiteboardUpdateOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ if (serverOp.getPos() <= clientOp.getPos()) {
+ second.setPos(second.getPos()+1);
+ }
+ return new Pair(first, second);
+ }
+
+ public static Pair transform(WhiteboardInsertOperation clientOp,WhiteboardUpdateOperation serverOp) {
+ WhiteboardUpdateOperation first = new WhiteboardUpdateOperation(serverOp);
+ first.setParentID(clientOp.getID());
+ WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ if (serverOp.getPos() >= clientOp.getPos()) {
+ first.setPos(first.getPos()+1);
+ }
+ return new Pair(first,second);
+ }
+
+ public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardDeleteOperation serverOp) {
+ WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp);
+ first.setParentID(clientOp.getID());
+ WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ if (clientOp.getPos() == -1) {
+ second.setPos(-1);
+ }
+ if (serverOp.getPos() == -1) {
+ first.setPos(-1);
+ }
+ if (clientOp.getPos() < serverOp.getPos()) {
+ first.setPos(first.getPos()-1);
+ }
+ else if (clientOp.getPos() > serverOp.getPos()) {
+ second.setPos(second.getPos()-1);
+ }
+ else {
+ first.setPos(-1);
+ second.setPos(-1);
+ }
+ return new Pair(first, second);
+ }
+
+ public static Pair transform(WhiteboardInsertOperation clientOp, WhiteboardDeleteOperation serverOp) {
+ WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp);
+ first.setParentID(clientOp.getID());
+ WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ if (clientOp.getPos() <= serverOp.getPos()) {
+ first.setPos(first.getPos()+1);
+ }
+ else if (serverOp.getPos() != -1) {
+ second.setPos(second.getPos()-1);
+ }
+ return new Pair(first, second);
+ }
+
+ public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardInsertOperation serverOp) {
+ WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp);
+ first.setParentID(clientOp.getID());
+ WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ if (serverOp.getPos() <= clientOp.getPos()) {
+ second.setPos(second.getPos()+1);
+ }
+ else if (clientOp.getPos() != -1) {
+ first.setPos(first.getPos()-1);
+ }
+ return new Pair(first, second);
+ }
+
+ public static Pair transform(WhiteboardUpdateOperation clientOp, WhiteboardDeleteOperation serverOp) {
+ WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp);
+ first.setParentID(clientOp.getID());
+ WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation(clientOp);;
+ WhiteboardOperation second = updateOp;
+ second.setParentID(serverOp.getID());
+ if (clientOp.getPos() == serverOp.getPos()) {
+ WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation();
+ second = deleteOp;
+ second.setPos(-1);
+ second.setID(clientOp.getID());
+ second.setParentID(serverOp.getID());
+ deleteOp.setElementID(serverOp.getElementID());
+ }
+ else if (clientOp.getPos() > serverOp.getPos() && clientOp.getNewPos() <= serverOp.getPos()) {
+ second.setPos(second.getPos()-1);
+ }
+ else if (clientOp.getPos() < serverOp.getPos() && clientOp.getNewPos() >= serverOp.getPos()) {
+ updateOp.setNewPos(updateOp.getNewPos()-1);
+ }
+ else if (clientOp.getPos() > serverOp.getPos()) {
+ second.setPos(second.getPos() - 1);
+ updateOp.setNewPos(updateOp.getNewPos()-1);
+ }
+ return new Pair(first, second);
+ }
+
+ public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardUpdateOperation serverOp) {
+ WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation(serverOp);
+ WhiteboardOperation first = updateOp;
+ first.setParentID(clientOp.getID());
+ WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp);
+ second.setParentID(serverOp.getID());
+ if (clientOp.getPos() == serverOp.getPos()) {
+ WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation();
+ first = deleteOp;
+ first.setPos(-1);
+ first.setID(serverOp.getID());
+ first.setParentID(clientOp.getID());
+ deleteOp.setElementID(clientOp.getElementID());
+ }
+ else if (clientOp.getPos() < serverOp.getPos() && clientOp.getPos() >= serverOp.getNewPos()) {
+ first.setPos(first.getPos()-1);
+ }
+ else if (clientOp.getPos() > serverOp.getPos() && clientOp.getPos() <= serverOp.getNewPos()) {
+ updateOp.setNewPos(updateOp.getNewPos()-1);
+ }
+ else if (clientOp.getPos() < serverOp.getPos()) {
+ first.setPos(first.getPos()-1);
+ updateOp.setNewPos(updateOp.getNewPos()-1);
+ }
+ return new Pair(first, second);
+ }
+
+
+}