diff options
author | Alex Clayton <alex.clayton@isode.com> | 2016-02-26 16:51:13 (GMT) |
---|---|---|
committer | Alex Clayton <alex.clayton@isode.com> | 2016-03-09 16:44:17 (GMT) |
commit | 8f112a856705b800d1a8797bec5d9396a9c00b34 (patch) | |
tree | 6468ad42172e1d1f68138a26679dbaf85b58e6b9 /src/com/isode/stroke/whiteboard | |
parent | 8fe752626726ca8d058ce437127a37d5d738a5eb (diff) | |
download | stroke-8f112a856705b800d1a8797bec5d9396a9c00b34.zip stroke-8f112a856705b800d1a8797bec5d9396a9c00b34.tar.bz2 |
Add Whiteboard Functionality
Add the Whiteboard classes to stroke.
Test-information:
Unit tests all pass.
Change-Id: Id409c09d0fc1f82864e5d706c413b9d984a7db82
Diffstat (limited to 'src/com/isode/stroke/whiteboard')
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); + } + + +} |