From b2e11d7f32db65a6be001dfdf74b74f16f4c9ec3 Mon Sep 17 00:00:00 2001
From: Mateusz Piekos <mateuszpiekos@gmail.com>
Date: Mon, 16 Jul 2012 14:14:05 +0200
Subject: Extended classes handling synchronization with update operation


diff --git a/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp b/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp
index 037eae2..5d4aef1 100644
--- a/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp
+++ b/Swiften/Parser/PayloadParsers/WhiteboardParser.cpp
@@ -13,6 +13,7 @@
 #include <Swiften/Whiteboard/Elements/WhiteboardFreehandPathElement.h>
 #include <Swiften/Whiteboard/Elements/Color.h>
 #include <Swiften/Whiteboard/Operations/WhiteboardInsertOperation.h>
+#include <Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h>
 #include <boost/optional.hpp>
 #include <boost/smart_ptr/make_shared.hpp>
 #include <boost/lexical_cast.hpp>
@@ -35,6 +36,15 @@ namespace Swift {
 				} catch (boost::bad_lexical_cast&) {
 				}
 				operation = insertOp;
+			} else if (type == "update") {
+				WhiteboardUpdateOperation::ref updateOp = boost::make_shared<WhiteboardUpdateOperation>();
+				try {
+					updateOp->setID(attributes.getAttributeValue("id").get_value_or(""));
+					updateOp->setParentID(attributes.getAttributeValue("parentid").get_value_or(""));
+					updateOp->setPos(boost::lexical_cast<int>(attributes.getAttributeValue("pos").get_value_or("0")));
+				} catch (boost::bad_lexical_cast&) {
+				}
+				operation = updateOp;
 			}
 		} else if (level_ == 2) {
 			if (element == "line") {
@@ -42,15 +52,21 @@ namespace Swift {
 				int y1 = 0;
 				int x2 = 0;
 				int y2 = 0;
+				int xShift = 0;
+				int yShift = 0;
 				try {
 					x1 = boost::lexical_cast<int>(attributes.getAttributeValue("x1").get_value_or("0"));
 					y1 = boost::lexical_cast<int>(attributes.getAttributeValue("y1").get_value_or("0"));
 					x2 = boost::lexical_cast<int>(attributes.getAttributeValue("x2").get_value_or("0"));
 					y2 = boost::lexical_cast<int>(attributes.getAttributeValue("y2").get_value_or("0"));
+					xShift = boost::lexical_cast<int>(attributes.getAttributeValue("xshift").get_value_or("0"));
+					yShift = boost::lexical_cast<int>(attributes.getAttributeValue("yshift").get_value_or("0"));
 				} catch (boost::bad_lexical_cast&) {
 				}
 				WhiteboardLineElement::ref whiteboardElement = boost::make_shared<WhiteboardLineElement>(x1, y1, x2, y2);
 
+				whiteboardElement->setShift(xShift, yShift);
+
 				Color color(attributes.getAttributeValue("stroke").get_value_or("#000000"));
 				color.setAlpha(opacityToAlpha(attributes.getAttributeValue("opacity").get_value_or("1")));
 				whiteboardElement->setColor(color);
@@ -253,6 +269,11 @@ namespace Swift {
 			if (insertOp) {
 				insertOp->setElement(wbElement);
 			}
+
+			WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(operation);
+			if (updateOp) {
+				updateOp->setElement(wbElement);
+			}
 			getPayloadInternal()->setOperation(operation);
 		} else if (level_ == 2) {
 			if (element == "text") {
diff --git a/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp b/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp
index 0cd4779..965939a 100644
--- a/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.cpp
@@ -10,6 +10,7 @@
 #include <boost/lexical_cast.hpp>
 #include <Swiften/Serializer/XML/XMLTextNode.h>
 #include <Swiften/Whiteboard/Operations/WhiteboardInsertOperation.h>
+#include <Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h>
 
 namespace Swift {
 	void WhiteboardElementSerializingVisitor::visit(WhiteboardLineElement& line) {
@@ -19,6 +20,8 @@ namespace Swift {
 			element->setAttribute("y1", boost::lexical_cast<std::string>(line.y1()));
 			element->setAttribute("x2", boost::lexical_cast<std::string>(line.x2()));
 			element->setAttribute("y2", boost::lexical_cast<std::string>(line.y2()));
+			element->setAttribute("xshift", boost::lexical_cast<std::string>(line.getXShift()));
+			element->setAttribute("yshift", boost::lexical_cast<std::string>(line.getYShift()));
 			element->setAttribute("id", line.getID());
 			element->setAttribute("stroke", line.getColor().toHex());
 			element->setAttribute("stroke-width", boost::lexical_cast<std::string>(line.getPenWidth()));
@@ -151,6 +154,19 @@ namespace Swift {
 				insertOp->getElement()->accept(visitor);
 				operationNode->addNode(visitor.getResult());
 			}
+			WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(payload->getOperation());
+			if (updateOp) {
+				try {
+					operationNode->setAttribute("type", "update");
+					operationNode->setAttribute("pos", boost::lexical_cast<std::string>(updateOp->getPos()));
+					operationNode->setAttribute("id", updateOp->getID());
+					operationNode->setAttribute("parentid", updateOp->getParentID());
+				} catch (boost::bad_lexical_cast&) {
+				}
+				updateOp->getElement()->accept(visitor);
+				operationNode->addNode(visitor.getResult());
+
+			}
 			element.addNode(operationNode);
 		}
 		element.setAttribute("type", typeToString(payload->getType()));
diff --git a/Swiften/Whiteboard/Elements/WhiteboardLineElement.h b/Swiften/Whiteboard/Elements/WhiteboardLineElement.h
index 20455b6..85e6302 100644
--- a/Swiften/Whiteboard/Elements/WhiteboardLineElement.h
+++ b/Swiften/Whiteboard/Elements/WhiteboardLineElement.h
@@ -37,6 +37,19 @@ namespace Swift {
 			return y2_;
 		}
 
+		int getXShift() const {
+			return xShift_;
+		}
+
+		int getYShift() const {
+			return yShift_;
+		}
+
+		void setShift(int x, int y) {
+			xShift_ = x;
+			yShift_ = y;
+		}
+
 		const Color& getColor() const {
 			return color_;
 		}
@@ -67,6 +80,7 @@ namespace Swift {
 
 	private:
 		int x1_, y1_, x2_, y2_;
+		int xShift_, yShift_;
 		Color color_;
 		int penWidth_;
 		std::string id_;
diff --git a/Swiften/Whiteboard/IncomingWhiteboardSession.cpp b/Swiften/Whiteboard/IncomingWhiteboardSession.cpp
index 60faa2e..42f5934 100644
--- a/Swiften/Whiteboard/IncomingWhiteboardSession.cpp
+++ b/Swiften/Whiteboard/IncomingWhiteboardSession.cpp
@@ -26,21 +26,21 @@ namespace Swift {
 
 	void IncomingWhiteboardSession::handleIncomingOperation(WhiteboardOperation::ref operation) {
 //		std::cout << "incoming pos: " << operation->getPos() << std::endl;
-		WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
-		if (insertOp) {
-			WhiteboardOperation::ref op = server.handleClientOperationReceived(insertOp);
+//		WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
+//		if (insertOp) {
+			WhiteboardOperation::ref op = server.handleClientOperationReceived(operation);
 			//std::cout << "in1: " << operation->getID() << " " << operation->getPos() <<  " " << operation->getParentID() << std::endl;
 			//std::cout << "in2: " << op->getID() << " " << op->getPos() <<  " " << op->getParentID() << std::endl;
 			onOperationReceived(op);
 			WhiteboardPayload::ref payload = boost::make_shared<WhiteboardPayload>();
 			payload->setOperation(op);
 			sendPayload(payload);
-		} else {
-			std::cout << "unknown operation" << std::endl;
-		}
+//		} else {
+//			std::cout << "unknown operation" << std::endl;
+//		}
 	}
 
-	void IncomingWhiteboardSession::handleSendOperationRequest(WhiteboardOperation::ref operation) {
+	void IncomingWhiteboardSession::sendOperation(WhiteboardOperation::ref operation) {
 		//std::cout << "out: " << operation->getID() << " " << operation->getPos() << " " << operation->getParentID()<< std::endl;
 		server.handleLocalOperationReceived(operation);
 		WhiteboardPayload::ref payload = boost::make_shared<WhiteboardPayload>();
diff --git a/Swiften/Whiteboard/IncomingWhiteboardSession.h b/Swiften/Whiteboard/IncomingWhiteboardSession.h
index 33c1a78..99d3e44 100644
--- a/Swiften/Whiteboard/IncomingWhiteboardSession.h
+++ b/Swiften/Whiteboard/IncomingWhiteboardSession.h
@@ -24,7 +24,7 @@ namespace Swift {
 
 	private:
 		void handleIncomingOperation(WhiteboardOperation::ref operation);
-		void handleSendOperationRequest(WhiteboardOperation::ref operation);
+		void sendOperation(WhiteboardOperation::ref operation);
 
 		WhiteboardServer server;
 	};
diff --git a/Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h b/Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h
new file mode 100644
index 0000000..7c4b088
--- /dev/null
+++ b/Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 Mateusz Piękos
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Whiteboard/Operations/WhiteboardOperation.h>
+
+#include <Swiften/Whiteboard/Elements/WhiteboardElement.h>
+
+namespace Swift {
+	class WhiteboardUpdateOperation : public WhiteboardOperation {
+	public:
+		typedef boost::shared_ptr<WhiteboardUpdateOperation> ref;
+	public:
+		~WhiteboardUpdateOperation() {
+		}
+
+		WhiteboardElement::ref getElement() const {
+			return element_;
+		}
+
+		void setElement(WhiteboardElement::ref element) {
+			element_ = element;
+		}
+
+	private:
+		WhiteboardElement::ref element_;
+	};
+}
diff --git a/Swiften/Whiteboard/OutgoingWhiteboardSession.cpp b/Swiften/Whiteboard/OutgoingWhiteboardSession.cpp
index da4d3cf..b3722ac 100644
--- a/Swiften/Whiteboard/OutgoingWhiteboardSession.cpp
+++ b/Swiften/Whiteboard/OutgoingWhiteboardSession.cpp
@@ -51,7 +51,7 @@ namespace Swift {
 		}
 	}
 
-	void OutgoingWhiteboardSession::handleSendOperationRequest(WhiteboardOperation::ref operation) {
+	void OutgoingWhiteboardSession::sendOperation(WhiteboardOperation::ref operation) {
 		//std::cout << "out1: " << operation->getID() << " " << operation->getPos() << " " << operation->getParentID() <<  std::endl;
 		WhiteboardOperation::ref result = client.handleLocalOperationReceived(operation);
 
diff --git a/Swiften/Whiteboard/OutgoingWhiteboardSession.h b/Swiften/Whiteboard/OutgoingWhiteboardSession.h
index fbd0f20..66b2d3f 100644
--- a/Swiften/Whiteboard/OutgoingWhiteboardSession.h
+++ b/Swiften/Whiteboard/OutgoingWhiteboardSession.h
@@ -25,7 +25,7 @@ namespace Swift {
 	private:
 		void handleRequestResponse(boost::shared_ptr<WhiteboardPayload> payload, ErrorPayload::ref error);
 		void handleIncomingOperation(WhiteboardOperation::ref operation);
-		void handleSendOperationRequest(WhiteboardOperation::ref operation);
+		void sendOperation(WhiteboardOperation::ref operation);
 
 		WhiteboardClient client;
 	};
diff --git a/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
index 1b89ae4..996d8a3 100644
--- a/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
+++ b/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp
@@ -11,6 +11,7 @@
 #include <boost/smart_ptr/make_shared.hpp>
 #include <Swiften/Whiteboard/WhiteboardClient.h>
 #include <Swiften/Whiteboard/Operations/WhiteboardInsertOperation.h>
+#include <Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h>
 #include <Swiften/Whiteboard/Elements/WhiteboardEllipseElement.h>
 
 using namespace Swift;
@@ -22,6 +23,7 @@ class WhiteboardClientTest : public CppUnit::TestFixture {
 	CPPUNIT_TEST(testSynchronize_nonInterrupted);
 	CPPUNIT_TEST(testSynchronize_clientInterruption);
 	CPPUNIT_TEST(testSynchronize_serverInterruption);
+	CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations);
 	CPPUNIT_TEST_SUITE_END();
 public:
 
@@ -481,6 +483,95 @@ public:
 
 	}
 
+	/*!
+	 *     /\
+	 *    /  \
+	 *    \  /
+	 *     \/
+	 */
+	void testSynchronize_nonInterruptedMixOperations() {
+		WhiteboardClient client;
+		WhiteboardInsertOperation::ref serverOp;
+		serverOp = createInsertOperation("0", "", 0);
+		WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp);
+		CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast<WhiteboardInsertOperation>(pairResult.client));
+		CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+		//Client receives first local operation, because it's parented off "0" which exists
+		//in server history and client doesn't wait for any operation ack from server,
+		//so this operation could be send
+		WhiteboardInsertOperation::ref clientOp;
+		clientOp = createInsertOperation("a", "0", 1);
+		clientOp->setOrigin(WhiteboardOperation::Local);
+		WhiteboardEllipseElement::ref aElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+		clientOp->setElement(aElement);
+		checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
+
+		//Client receives second local operation, client didn't receive ack about previous
+		//operation from the server so it can't be send.
+		WhiteboardUpdateOperation::ref clientUpdateOp = createUpdateOperation("b", "a", 0);
+		WhiteboardEllipseElement::ref bElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+		clientUpdateOp->setElement(bElement);
+		CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientUpdateOp));
+
+		//Client receives new operation from server, it should be transformed against
+		//"a" and "b" before adding to local operations history because it's parented off "0".
+		//Because client is waiting for ack of "a", there is no operation to send to server
+		WhiteboardUpdateOperation::ref serverUpdateOp = createUpdateOperation("c", "0", 0);
+		WhiteboardEllipseElement::ref cElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+		serverUpdateOp->setElement(cElement);
+		pairResult = client.handleServerOperationReceived(serverUpdateOp);
+		checkOperation(pairResult.client, "c", "b", 0, cElement);
+		CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+		//Client receives new operation from server, it should be transformed against
+		//results of previous transformations, returned operation should be parented off
+		//"c" existing in local history.
+		//Because client is waiting for ack of "a", there is no operation to send to server
+		serverOp = createInsertOperation("d", "c", 1);
+		serverOp->setOrigin(WhiteboardOperation::Other);
+		WhiteboardEllipseElement::ref dElement = boost::make_shared<WhiteboardEllipseElement>(0,0,0,0);
+		serverOp->setElement(dElement);
+		pairResult = client.handleServerOperationReceived(serverOp);
+		checkOperation(pairResult.client, "d", "c", 2, dElement);
+		CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+		//Client receives confirmation about processing "a", it should send next operation
+		//to server which is "b", but it should be version parented of transformed "a"
+		serverOp = createInsertOperation("a", "d", 1);
+		serverOp->setOrigin(WhiteboardOperation::Other);
+		pairResult = client.handleServerOperationReceived(serverOp);
+		checkOperation(pairResult.server, "b", "a", 0, cElement);
+		CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+
+
+		//Client receives confirmation about processing "b", there aren't any operations
+		//waiting so it should return nothing.
+		serverUpdateOp = createUpdateOperation("b", "a", 0);
+		serverUpdateOp->setOrigin(WhiteboardOperation::Other);
+		pairResult = client.handleServerOperationReceived(serverOp);
+		CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client);
+		CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server);
+
+		//Client operations:
+		//ID	pos
+		//0 	0
+		//a 	1
+		//b 	2
+		//c 	3
+		//d 	4
+		//
+		//Server operations:
+		//ID	pos
+		//0 	0
+		//c 	1
+		//d 	2
+		//a 	1
+		//b 	2
+		//
+		//what gives 0abcd on both sides.
+	}
+
 	WhiteboardInsertOperation::ref createInsertOperation(std::string id, std::string parent, int pos) {
 		WhiteboardInsertOperation::ref operation = boost::make_shared<WhiteboardInsertOperation>();
 		operation->setParentID(parent);
@@ -489,6 +580,14 @@ public:
 		return operation;
 	}
 
+	WhiteboardUpdateOperation::ref createUpdateOperation(std::string id, std::string parent, int pos) {
+		WhiteboardUpdateOperation::ref operation = boost::make_shared<WhiteboardUpdateOperation>();
+		operation->setParentID(parent);
+		operation->setID(id);
+		operation->setPos(pos);
+		return operation;
+	}
+
 	void checkOperation(WhiteboardOperation::ref operation, std::string id, std::string parent, int pos = -1, WhiteboardElement::ref element = WhiteboardElement::ref()) {
 		CPPUNIT_ASSERT_EQUAL(id, operation->getID());
 		CPPUNIT_ASSERT_EQUAL(parent, operation->getParentID());
@@ -497,7 +596,15 @@ public:
 		}
 
 		if (element) {
-			CPPUNIT_ASSERT_EQUAL(element, boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation)->getElement());
+			WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
+			if (insertOp) {
+				CPPUNIT_ASSERT_EQUAL(element, insertOp->getElement());
+			}
+
+			WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(operation);
+			if (updateOp) {
+				CPPUNIT_ASSERT_EQUAL(element, updateOp->getElement());
+			}
 		}
 	}
 };
diff --git a/Swiften/Whiteboard/WhiteboardClient.cpp b/Swiften/Whiteboard/WhiteboardClient.cpp
index 9610247..2dd9e45 100644
--- a/Swiften/Whiteboard/WhiteboardClient.cpp
+++ b/Swiften/Whiteboard/WhiteboardClient.cpp
@@ -12,7 +12,16 @@ namespace Swift {
 	WhiteboardOperation::ref WhiteboardClient::handleLocalOperationReceived(WhiteboardOperation::ref operation) {
 		localOperations_.push_back(operation);
 //		if (bridge_.size() > 0) {
-		WhiteboardOperation::ref op = boost::make_shared<WhiteboardInsertOperation>(*boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation).get());
+		WhiteboardOperation::ref op;
+		WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
+		if (insertOp) {
+			op = boost::make_shared<WhiteboardInsertOperation>(*insertOp.get());
+		}
+		WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(operation);
+		if (updateOp) {
+			op = boost::make_shared<WhiteboardUpdateOperation>(*updateOp.get());
+		}
+
 		if (bridge_.size() > 0) {
 			op->setParentID(bridge_.back()->getID());
 		}
@@ -20,7 +29,15 @@ namespace Swift {
 //		}
 		if (lastSentOperationID_.empty())
 		{
-			WhiteboardOperation::ref op = boost::make_shared<WhiteboardInsertOperation>(*boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation).get());
+			WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
+			if (insertOp) {
+				op = boost::make_shared<WhiteboardInsertOperation>(*insertOp.get());
+			}
+			WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(operation);
+			if (updateOp) {
+				op = boost::make_shared<WhiteboardUpdateOperation>(*updateOp.get());
+			}
+
 			if (serverOperations_.size() > 0) {
 				op->setParentID(serverOperations_.back()->getID());
 			}
@@ -62,23 +79,22 @@ namespace Swift {
 				result.server = bridge_.front();
 				result.server->setOrigin(WhiteboardOperation::Other);
 			}
-
 			if (!result.server) {
 				lastSentOperationID_.clear();
 			}
 		} else {
 			if (bridge_.size() > 0 && bridge_.front()->getParentID() == operation->getParentID()) {
 				std::list<WhiteboardOperation::ref>::iterator it = bridge_.begin();
-				std::pair<WhiteboardInsertOperation::ref, WhiteboardInsertOperation::ref> opPair;
-				WhiteboardInsertOperation::ref temp;
-				opPair = WhiteboardTransformer::transform(boost::dynamic_pointer_cast<WhiteboardInsertOperation>(*it), boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation));
+				std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> opPair;
+				WhiteboardOperation::ref temp;
+				opPair = WhiteboardTransformer::transform(*it, operation);
 				temp = opPair.first;
 
 				*it = opPair.second;
 				std::string previousID = (*it)->getID();
 				++it;
 				for (; it != bridge_.end(); ++it) {
-					opPair = WhiteboardTransformer::transform(boost::dynamic_pointer_cast<WhiteboardInsertOperation>(*it), temp);
+					opPair = WhiteboardTransformer::transform(*it, temp);
 					temp = opPair.first;
 					*it = opPair.second;
 					(*it)->setParentID(previousID);
@@ -90,7 +106,7 @@ namespace Swift {
 				result.client = temp;
 			} else {
 //doesn't get executed
-				std::list<WhiteboardOperation::ref>::reverse_iterator it;
+/*				std::list<WhiteboardOperation::ref>::reverse_iterator it;
 				std::pair<WhiteboardInsertOperation::ref, WhiteboardInsertOperation::ref> opPair;
 				WhiteboardInsertOperation::ref temp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
 				bool end = false;
@@ -113,8 +129,9 @@ namespace Swift {
 					}
 					if (end) {
 						break;
-					}
-				}
+						}*/
+			
+				   
 			}
 		}
 
diff --git a/Swiften/Whiteboard/WhiteboardServer.cpp b/Swiften/Whiteboard/WhiteboardServer.cpp
index 36dea87..e84ddb3 100644
--- a/Swiften/Whiteboard/WhiteboardServer.cpp
+++ b/Swiften/Whiteboard/WhiteboardServer.cpp
@@ -12,7 +12,7 @@ namespace Swift {
 		operations_.push_back(operation);
 	}
 
-	WhiteboardOperation::ref WhiteboardServer::handleClientOperationReceived(WhiteboardInsertOperation::ref newOperation) {
+	WhiteboardOperation::ref WhiteboardServer::handleClientOperationReceived(WhiteboardOperation::ref newOperation) {
 		std::list<WhiteboardOperation::ref>::reverse_iterator it;
 		if (operations_.size() == 0 || newOperation->getParentID() == operations_.back()->getID()) {
 			operations_.push_back(newOperation);
@@ -21,9 +21,9 @@ namespace Swift {
 		for (it = operations_.rbegin(); it != operations_.rend(); ++it) {
 			WhiteboardOperation::ref operation = *it;
 			while (newOperation->getParentID() == operation->getParentID()) {
-				WhiteboardInsertOperation::ref insertOperation = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
-				if (insertOperation) {
-					std::pair<WhiteboardInsertOperation::ref, WhiteboardInsertOperation::ref> tResult = WhiteboardTransformer::transform(newOperation, insertOperation);
+//				WhiteboardInsertOperation::ref insertOperation = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(operation);
+//				if (insertOperation) {
+					std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> tResult = WhiteboardTransformer::transform(newOperation, operation);
 
 					if (it == operations_.rbegin()) {
 						operations_.push_back(tResult.second);
@@ -33,10 +33,10 @@ namespace Swift {
 						--it;
 						operation = *it;
 					}
-				} else {
-					operations_.push_back(operation);
-					return *it;
-				}
+//				} else {
+//					operations_.push_back(operation);
+//					return *it;
+//				}
 			}
 		}
 		return WhiteboardOperation::ref();
diff --git a/Swiften/Whiteboard/WhiteboardServer.h b/Swiften/Whiteboard/WhiteboardServer.h
index 11ef64d..6ee4ac6 100644
--- a/Swiften/Whiteboard/WhiteboardServer.h
+++ b/Swiften/Whiteboard/WhiteboardServer.h
@@ -14,7 +14,7 @@ namespace Swift {
 	class WhiteboardServer {
 	public:
 		void handleLocalOperationReceived(WhiteboardOperation::ref operation);
-		WhiteboardOperation::ref handleClientOperationReceived(WhiteboardInsertOperation::ref operation);
+		WhiteboardOperation::ref handleClientOperationReceived(WhiteboardOperation::ref operation);
 
 	private:
 		std::list<WhiteboardOperation::ref> operations_;
diff --git a/Swiften/Whiteboard/WhiteboardSession.cpp b/Swiften/Whiteboard/WhiteboardSession.cpp
index 2828436..492aec5 100644
--- a/Swiften/Whiteboard/WhiteboardSession.cpp
+++ b/Swiften/Whiteboard/WhiteboardSession.cpp
@@ -44,14 +44,9 @@ namespace Swift {
 		request->send();
 	}
 
-	void WhiteboardSession::sendOperation(const WhiteboardOperation::ref operation) {
-/*		boost::shared_ptr<WhiteboardPayload> payload = boost::make_shared<WhiteboardPayload>();
-		//	payload->setElement(operation->getElement());
-		payload->setOperation(operation);
-		boost::shared_ptr<GenericRequest<WhiteboardPayload> > request = boost::make_shared<GenericRequest<WhiteboardPayload> >(IQ::Set, toJID_, payload, router_);
-		request->send();*/
+/*	void WhiteboardSession::sendOperation(const WhiteboardOperation::ref operation) {
 		handleSendOperationRequest(operation);
-	}
+	}*/
 
 	void WhiteboardSession::sendPayload(boost::shared_ptr<WhiteboardPayload> payload) {
 		boost::shared_ptr<GenericRequest<WhiteboardPayload> > request = boost::make_shared<GenericRequest<WhiteboardPayload> >(IQ::Set, toJID_, payload, router_);
diff --git a/Swiften/Whiteboard/WhiteboardSession.h b/Swiften/Whiteboard/WhiteboardSession.h
index 7fb03e6..1eb76a4 100644
--- a/Swiften/Whiteboard/WhiteboardSession.h
+++ b/Swiften/Whiteboard/WhiteboardSession.h
@@ -28,7 +28,7 @@ namespace Swift {
 		virtual ~WhiteboardSession();
 		void handleIncomingAction(boost::shared_ptr<WhiteboardPayload> payload);
 		void sendElement(const WhiteboardElement::ref element);
-		void sendOperation(WhiteboardOperation::ref operation);
+		virtual void sendOperation(WhiteboardOperation::ref operation) = 0;
 		void cancel();
 		const JID& getTo() const;
 		virtual std::string getClientID() const = 0;
@@ -43,7 +43,7 @@ namespace Swift {
 
 	private:
 		virtual void handleIncomingOperation(WhiteboardOperation::ref operation) = 0;
-		virtual void handleSendOperationRequest(WhiteboardOperation::ref operation) = 0;
+		//virtual void handleSendOperationRequest(WhiteboardOperation::ref operation) = 0;
 
 	protected:
 		void sendPayload(boost::shared_ptr<WhiteboardPayload> payload);
diff --git a/Swiften/Whiteboard/WhiteboardTransformer.cpp b/Swiften/Whiteboard/WhiteboardTransformer.cpp
index 32e9815..32b1183 100644
--- a/Swiften/Whiteboard/WhiteboardTransformer.cpp
+++ b/Swiften/Whiteboard/WhiteboardTransformer.cpp
@@ -8,7 +8,25 @@
 #include <boost/smart_ptr/make_shared.hpp>
 
 namespace Swift {
-	std::pair<WhiteboardInsertOperation::ref, WhiteboardInsertOperation::ref> WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) {
+	std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp) {
+		WhiteboardInsertOperation::ref clientInsert = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(clientOp);
+		WhiteboardInsertOperation::ref serverInsert = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(serverOp);
+		WhiteboardUpdateOperation::ref clientUpdate = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(clientOp);
+		WhiteboardUpdateOperation::ref serverUpdate = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(serverOp);
+		if (clientInsert && serverInsert) {
+			return transform(clientInsert, serverInsert);
+		} else if (clientUpdate && serverUpdate) {
+			return transform(clientUpdate, serverUpdate);
+		} else if (clientInsert && serverUpdate) {
+			return transform(clientInsert, serverUpdate);
+		} else if (clientUpdate && serverInsert) {
+			return transform(clientUpdate, serverInsert);
+		} else {
+			return std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref>();
+		}
+	}
+
+	std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) {
 		std::pair<WhiteboardInsertOperation::ref, WhiteboardInsertOperation::ref> result;
 		result.first = boost::make_shared<WhiteboardInsertOperation>(*serverOp.get());
 		result.first->setParentID(clientOp->getID());
@@ -17,4 +35,44 @@ namespace Swift {
 		result.second->setParentID(serverOp->getID());
 		return result;
 	}
+
+	std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp) {
+		std::pair<WhiteboardUpdateOperation::ref, WhiteboardUpdateOperation::ref> result;
+		result.first = boost::make_shared<WhiteboardUpdateOperation>(*serverOp.get());
+		result.first->setParentID(clientOp->getID());
+
+		if (clientOp->getPos() == serverOp->getPos()) {
+			result.second = boost::make_shared<WhiteboardUpdateOperation>(*serverOp.get());
+			result.second->setID(clientOp->getID());
+			result.second->setParentID(serverOp->getID());
+		} else {
+			result.second = boost::make_shared<WhiteboardUpdateOperation>(*clientOp.get());
+			result.second->setParentID(serverOp->getID());
+		}
+		return result;
+	}
+
+	std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) {
+		std::pair<WhiteboardInsertOperation::ref, WhiteboardUpdateOperation::ref> result;
+		result.first = boost::make_shared<WhiteboardInsertOperation>(*serverOp.get());
+		result.first->setParentID(clientOp->getID());
+		result.second = boost::make_shared<WhiteboardUpdateOperation>(*clientOp.get());
+		result.second->setParentID(serverOp->getID());
+		if (serverOp->getPos() <= clientOp->getPos()) {
+			result.second->setPos(result.second->getPos()+1);
+		}
+		return result;
+	}
+
+	std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp) {
+		std::pair<WhiteboardUpdateOperation::ref, WhiteboardInsertOperation::ref> result;
+		result.first = boost::make_shared<WhiteboardUpdateOperation>(*serverOp.get());
+		result.first->setParentID(clientOp->getID());
+		result.second = boost::make_shared<WhiteboardInsertOperation>(*clientOp.get());
+		result.second->setParentID(serverOp->getID());
+		if (serverOp->getPos() >= clientOp->getPos()) {
+			result.first->setPos(result.first->getPos()+1);
+		}
+		return result;
+	}
 }
diff --git a/Swiften/Whiteboard/WhiteboardTransformer.h b/Swiften/Whiteboard/WhiteboardTransformer.h
index 33086d8..0bab4d3 100644
--- a/Swiften/Whiteboard/WhiteboardTransformer.h
+++ b/Swiften/Whiteboard/WhiteboardTransformer.h
@@ -7,11 +7,16 @@
 #pragma once
 
 #include <Swiften/Whiteboard/Operations/WhiteboardInsertOperation.h>
+#include <Swiften/Whiteboard/Operations/WhiteboardUpdateOperation.h>
 #include <utility>
 
 namespace Swift {
 	class WhiteboardTransformer {
 	public:
-		static std::pair<WhiteboardInsertOperation::ref, WhiteboardInsertOperation::ref> transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp);
+static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp);
+		static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp);
+		static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp);
+		static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp);
+		static std::pair<WhiteboardOperation::ref, WhiteboardOperation::ref> transform(WhiteboardInsertOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp);
 	};
 }
-- 
cgit v0.10.2-6-g49f6