summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Clayton <alex.clayton@isode.com>2016-02-26 16:51:13 (GMT)
committerAlex Clayton <alex.clayton@isode.com>2016-03-09 16:44:17 (GMT)
commit8f112a856705b800d1a8797bec5d9396a9c00b34 (patch)
tree6468ad42172e1d1f68138a26679dbaf85b58e6b9
parent8fe752626726ca8d058ce437127a37d5d738a5eb (diff)
downloadstroke-8f112a856705b800d1a8797bec5d9396a9c00b34.zip
stroke-8f112a856705b800d1a8797bec5d9396a9c00b34.tar.bz2
Add Whiteboard Functionality
Add the Whiteboard classes to stroke. Test-information: Unit tests all pass. Change-Id: Id409c09d0fc1f82864e5d706c413b9d984a7db82
-rw-r--r--PortingProgress.txt16
-rw-r--r--src/com/isode/stroke/elements/DiscoInfo.java3
-rw-r--r--src/com/isode/stroke/elements/WhiteboardColor.java69
-rw-r--r--src/com/isode/stroke/elements/WhiteboardDeleteOperation.java33
-rw-r--r--src/com/isode/stroke/elements/WhiteboardElement.java30
-rw-r--r--src/com/isode/stroke/elements/WhiteboardElementVisitor.java21
-rw-r--r--src/com/isode/stroke/elements/WhiteboardEllipseElement.java71
-rw-r--r--src/com/isode/stroke/elements/WhiteboardFreehandPathElement.java65
-rw-r--r--src/com/isode/stroke/elements/WhiteboardInsertOperation.java33
-rw-r--r--src/com/isode/stroke/elements/WhiteboardLineElement.java64
-rw-r--r--src/com/isode/stroke/elements/WhiteboardOperation.java52
-rw-r--r--src/com/isode/stroke/elements/WhiteboardPayload.java67
-rw-r--r--src/com/isode/stroke/elements/WhiteboardPolygonElement.java70
-rw-r--r--src/com/isode/stroke/elements/WhiteboardRectElement.java71
-rw-r--r--src/com/isode/stroke/elements/WhiteboardTextElement.java61
-rw-r--r--src/com/isode/stroke/elements/WhiteboardUpdateOperation.java43
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java2
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/WhiteboardParser.java390
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/WhiteboardSerializer.java221
-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
-rw-r--r--test/com/isode/stroke/whiteboard/WhiteboardClientTest.java672
-rw-r--r--test/com/isode/stroke/whiteboard/WhiteboardServerTest.java146
29 files changed, 3119 insertions, 10 deletions
diff --git a/PortingProgress.txt b/PortingProgress.txt
index 0dcb71d..fca63cd 100644
--- a/PortingProgress.txt
+++ b/PortingProgress.txt
@@ -1,6 +1,6 @@
1Porting Progress from Swiften to Stroke. 1Porting Progress from Swiften to Stroke.
2This file indicates the porting progress from Swiften Library to Stroke. It indicates, upto which HEAD changes are ported to Stroke. Also indicates any remarks associated with it. 2This file indicates the porting progress from Swiften Library to Stroke. It indicates, upto which HEAD changes are ported to Stroke. Also indicates any remarks associated with it.
3To Be Ported: history, linklocal, WhiteBoard. 3To Be Ported: history, linklocal.
4 4
5----- 5-----
6Adhoc: 6Adhoc:
@@ -73,8 +73,6 @@ Elements:
73 73
74All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: 74All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for:
75 75
76WhiteBoard Functionalities -- Not Yet Ported!
77
78Individual Comments: 76Individual Comments:
79ToplevelElement -- Not Required in Stroke.Element does the work! 77ToplevelElement -- Not Required in Stroke.Element does the work!
80 78
@@ -157,8 +155,7 @@ Parser:
157All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: 155All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for:
158 156
159ExpatParser -- Not yet ported. Requires expact library. 157ExpatParser -- Not yet ported. Requires expact library.
160LibXMLParser -- Not yet ported. Requires libxml library. 158LibXMLParser -- Not yet ported. Requires libxml library.
161WhiteboardParser -- Not Yet Ported! Needs Whiteboard classes importing.
162 159
163----- 160-----
164Presence: 161Presence:
@@ -191,9 +188,7 @@ Windows Authenticator not needed!
191----- 188-----
192Serializer: 189Serializer:
193 190
194All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: 191All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39.
195
196WhiteboardSerializer -- Not Yet Ported! Needs whiteboard classes importing.
197 192
198----- 193-----
199Session: 194Session:
@@ -227,3 +222,8 @@ supported in java.
227VCards: 222VCards:
228 223
229All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39. 224All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39.
225
226-----
227Whiteboard:
228
229All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39.
diff --git a/src/com/isode/stroke/elements/DiscoInfo.java b/src/com/isode/stroke/elements/DiscoInfo.java
index 35786e7..78e43ff 100644
--- a/src/com/isode/stroke/elements/DiscoInfo.java
+++ b/src/com/isode/stroke/elements/DiscoInfo.java
@@ -3,7 +3,7 @@
3 * All rights reserved. 3 * All rights reserved.
4 */ 4 */
5/* 5/*
6 * Copyright (c) 2010-2015, Isode Limited, London, England. 6 * Copyright (c) 2010-2016, Isode Limited, London, England.
7 * All rights reserved. 7 * All rights reserved.
8 */ 8 */
9package com.isode.stroke.elements; 9package com.isode.stroke.elements;
@@ -33,6 +33,7 @@ public class DiscoInfo extends Payload {
33 public static final String JingleTransportsS5BFeature = "urn:xmpp:jingle:transports:s5b:1"; 33 public static final String JingleTransportsS5BFeature = "urn:xmpp:jingle:transports:s5b:1";
34 public static final String Bytestream = "http://jabber.org/protocol/bytestreams"; 34 public static final String Bytestream = "http://jabber.org/protocol/bytestreams";
35 public static final String MessageDeliveryReceiptsFeature = "urn:xmpp:receipts"; 35 public static final String MessageDeliveryReceiptsFeature = "urn:xmpp:receipts";
36 public static final String WhiteboardFeature = "http://swift.im/whiteboard";
36 37
37 public static class Identity implements Comparable<Identity> { 38 public static class Identity implements Comparable<Identity> {
38 private final String name_; 39 private final String name_;
diff --git a/src/com/isode/stroke/elements/WhiteboardColor.java b/src/com/isode/stroke/elements/WhiteboardColor.java
new file mode 100644
index 0000000..f40f618
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardColor.java
@@ -0,0 +1,69 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardColor {
13
14 private final int red_, green_, blue_;
15 private int alpha_;
16
17 public WhiteboardColor() {
18 this(0,0,0,255);
19 }
20
21 public WhiteboardColor(int red,int green,int blue) {
22 this(red,green,blue,255);
23 }
24
25 public WhiteboardColor(int red,int green,int blue,int alpha) {
26 red_ = red;
27 green_ = green;
28 blue_ = blue;
29 alpha_ = alpha;
30 }
31
32 public WhiteboardColor(String hex) {
33 alpha_ = 255;
34 int value = Integer.parseInt(hex.substring(1));
35 red_ = (value >> 16) & 0xFF;
36 green_ = (value >> 8) & 0xFF;
37 blue_ = value & 0xFF;
38 }
39
40 public String toHex() {
41 int value = (red_ << 16) + (green_ << 8) + blue_;
42 StringBuilder builder = new StringBuilder(Integer.toHexString(value));
43 while (builder.length() < 6) {
44 builder.insert(0, '0');
45 }
46 builder.insert(0, '#');
47 return builder.toString();
48 }
49
50 public int getRed() {
51 return red_;
52 }
53
54 public int getGreen() {
55 return green_;
56 }
57
58 public int getBlue() {
59 return blue_;
60 }
61
62 public int getAlpha() {
63 return alpha_;
64 }
65
66 public void setAlpha(int alpha) {
67 alpha_ = alpha;
68 }
69}
diff --git a/src/com/isode/stroke/elements/WhiteboardDeleteOperation.java b/src/com/isode/stroke/elements/WhiteboardDeleteOperation.java
new file mode 100644
index 0000000..87ab226
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardDeleteOperation.java
@@ -0,0 +1,33 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardDeleteOperation extends WhiteboardOperation {
13
14 private String elementID_ = "";
15
16 public WhiteboardDeleteOperation() {
17 // Empty Constructor
18 }
19
20 public WhiteboardDeleteOperation(WhiteboardDeleteOperation other) {
21 super(other);
22 this.elementID_ = other.elementID_;
23 }
24
25 public String getElementID() {
26 return elementID_;
27 }
28
29 public void setElementID(String id) {
30 elementID_ = id;
31 }
32
33}
diff --git a/src/com/isode/stroke/elements/WhiteboardElement.java b/src/com/isode/stroke/elements/WhiteboardElement.java
new file mode 100644
index 0000000..5e2912a
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardElement.java
@@ -0,0 +1,30 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public abstract class WhiteboardElement {
13
14 private String id_ = "";
15
16 public WhiteboardElement() {
17 // Empty Constructor
18 }
19
20 public abstract void accept(WhiteboardElementVisitor visitor);
21
22 public final String getID() {
23 return id_;
24 }
25
26 public final void setID(String id) {
27 id_ = id;
28 }
29
30}
diff --git a/src/com/isode/stroke/elements/WhiteboardElementVisitor.java b/src/com/isode/stroke/elements/WhiteboardElementVisitor.java
new file mode 100644
index 0000000..0853373
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardElementVisitor.java
@@ -0,0 +1,21 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public interface WhiteboardElementVisitor {
13
14 public void visit(WhiteboardLineElement element);
15 public void visit(WhiteboardFreehandPathElement element);
16 public void visit(WhiteboardRectElement element);
17 public void visit(WhiteboardPolygonElement element);
18 public void visit(WhiteboardTextElement element);
19 public void visit(WhiteboardEllipseElement element);
20
21}
diff --git a/src/com/isode/stroke/elements/WhiteboardEllipseElement.java b/src/com/isode/stroke/elements/WhiteboardEllipseElement.java
new file mode 100644
index 0000000..4cadc81
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardEllipseElement.java
@@ -0,0 +1,71 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardEllipseElement extends WhiteboardElement {
13
14 private final int cx_, cy_, rx_, ry_;
15 private WhiteboardColor penColor_;
16 private WhiteboardColor brushColor_;
17 private int penWidth_;
18
19 public WhiteboardEllipseElement(int cx, int cy, int rx, int ry) {
20 cx_ = cx;
21 cy_ = cy;
22 rx_ = rx;
23 ry_ = ry;
24 }
25
26 public int getCX() {
27 return cx_;
28 }
29
30 public int getCY() {
31 return cy_;
32 }
33
34 public int getRX() {
35 return rx_;
36 }
37
38 public int getRY() {
39 return ry_;
40 }
41
42 public WhiteboardColor getPenColor() {
43 return penColor_;
44 }
45
46 public void setPenColor(WhiteboardColor color) {
47 penColor_ = color;
48 }
49
50 public WhiteboardColor getBrushColor() {
51 return brushColor_;
52 }
53
54 public void setBrushColor(WhiteboardColor color) {
55 brushColor_ = color;
56 }
57
58 public int getPenWidth() {
59 return penWidth_;
60 }
61
62 public void setPenWidth(int penWidth) {
63 penWidth_ = penWidth;
64 }
65
66 @Override
67 public void accept(WhiteboardElementVisitor visitor) {
68 visitor.visit(this);
69 }
70
71}
diff --git a/src/com/isode/stroke/elements/WhiteboardFreehandPathElement.java b/src/com/isode/stroke/elements/WhiteboardFreehandPathElement.java
new file mode 100644
index 0000000..3885841
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardFreehandPathElement.java
@@ -0,0 +1,65 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.List;
15
16public class WhiteboardFreehandPathElement extends WhiteboardElement {
17
18 private List<Point> points_ = new ArrayList<Point>();
19 private WhiteboardColor color_ = new WhiteboardColor();
20 private int penWidth_ = 0;
21
22 public WhiteboardFreehandPathElement() {
23 // Empty Constructor
24 }
25
26 public void setPoints(Collection<? extends Point> points) {
27 points_.clear();
28 points_.addAll(points_);
29 }
30
31 public List<Point> getPoints() {
32 return new ArrayList<Point>(points_);
33 }
34
35 public WhiteboardColor getColor() {
36 return color_;
37 }
38
39 public void setColor(WhiteboardColor color) {
40 color_ = color;
41 }
42
43 public int getPenWidth() {
44 return penWidth_;
45 }
46
47 public void setPenWidth(int penWidth) {
48 penWidth_ = penWidth;
49 }
50
51 @Override
52 public void accept(WhiteboardElementVisitor visitor) {
53 visitor.visit(this);
54 }
55
56 public static class Point {
57 public final int x;
58 public final int y;
59 public Point(int x,int y) {
60 this.x = x;
61 this.y = y;
62 }
63 }
64
65}
diff --git a/src/com/isode/stroke/elements/WhiteboardInsertOperation.java b/src/com/isode/stroke/elements/WhiteboardInsertOperation.java
new file mode 100644
index 0000000..b4c9722
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardInsertOperation.java
@@ -0,0 +1,33 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardInsertOperation extends WhiteboardOperation {
13
14 private WhiteboardElement element_;
15
16 public WhiteboardInsertOperation() {
17 // Empty Constructor
18 }
19
20 public WhiteboardInsertOperation(WhiteboardInsertOperation other) {
21 super(other);
22 this.element_ = other.element_;
23 }
24
25 public WhiteboardElement getElement() {
26 return element_;
27 }
28
29 public void setElement(WhiteboardElement element) {
30 element_ = element;
31 }
32
33}
diff --git a/src/com/isode/stroke/elements/WhiteboardLineElement.java b/src/com/isode/stroke/elements/WhiteboardLineElement.java
new file mode 100644
index 0000000..df4ab9b
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardLineElement.java
@@ -0,0 +1,64 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardLineElement extends WhiteboardElement {
13
14 private final int x1_, x2_, y1_, y2_;
15
16 private WhiteboardColor color_ = new WhiteboardColor();
17
18 private int penWidth_ = 1;
19
20 public WhiteboardLineElement(int x1,int y1,int x2,int y2) {
21 x1_ = x1;
22 y1_ = y1;
23 x2_ = x2;
24 y2_ = y2;
25 }
26
27 public int x1() {
28 return x1_;
29 }
30
31 public int x2() {
32 return x2_;
33 }
34
35 public int y1() {
36 return y1_;
37 }
38
39 public int y2() {
40 return y2_;
41 }
42
43 public WhiteboardColor getColor() {
44 return color_;
45 }
46
47 public void setColor(WhiteboardColor color) {
48 color_ = color;
49 }
50
51 public int getPenWidth() {
52 return penWidth_;
53 }
54
55 public void setPenWidth(int penWidth) {
56 penWidth_ = penWidth;
57 }
58
59 @Override
60 public void accept(WhiteboardElementVisitor visitor) {
61 visitor.visit(this);
62 }
63
64}
diff --git a/src/com/isode/stroke/elements/WhiteboardOperation.java b/src/com/isode/stroke/elements/WhiteboardOperation.java
new file mode 100644
index 0000000..d980f7c
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardOperation.java
@@ -0,0 +1,52 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardOperation {
13
14 private String id_ = "";
15 private String parentID_ = "";
16 private int pos_ = 0;
17
18 public WhiteboardOperation() {
19 // Empty Constructor
20 }
21
22 public WhiteboardOperation(WhiteboardOperation other) {
23 this.id_ = other.id_;
24 this.parentID_ = other.parentID_;
25 this.pos_ = other.pos_;
26 }
27
28 public String getID() {
29 return id_;
30 }
31
32 public void setID(String id) {
33 id_ = id;
34 }
35
36 public String getParentID() {
37 return parentID_;
38 }
39
40 public void setParentID(String parentID) {
41 parentID_ = parentID;
42 }
43
44 public int getPos() {
45 return pos_;
46 }
47
48 public void setPos(int pos) {
49 pos_ = pos;
50 }
51
52}
diff --git a/src/com/isode/stroke/elements/WhiteboardPayload.java b/src/com/isode/stroke/elements/WhiteboardPayload.java
new file mode 100644
index 0000000..337f6bb
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardPayload.java
@@ -0,0 +1,67 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardPayload extends Payload {
13
14 private String data_;
15 private Type type_;
16 private WhiteboardElement element_;
17 private WhiteboardOperation operation_;
18
19 public enum Type {
20 UnknownType,
21 Data,
22 SessionRequest,
23 SessionAccept,
24 SessionTerminate;
25 }
26
27 public WhiteboardPayload() {
28 this(Type.Data);
29 }
30
31 public WhiteboardPayload(Type type) {
32 type_ = type;
33 }
34
35 public void setData(String data) {
36 data_ = data;
37 }
38
39 public String getData() {
40 return data_;
41 }
42
43 public Type getType() {
44 return type_;
45 }
46
47 public void setType(Type type) {
48 type_ = type;
49 }
50
51 public WhiteboardElement getElement() {
52 return element_;
53 }
54
55 public void setElement(WhiteboardElement element) {
56 element_ = element;
57 }
58
59 public WhiteboardOperation getOperation() {
60 return operation_;
61 }
62
63 public void setOperation(WhiteboardOperation operation) {
64 operation_ = operation;
65 }
66
67}
diff --git a/src/com/isode/stroke/elements/WhiteboardPolygonElement.java b/src/com/isode/stroke/elements/WhiteboardPolygonElement.java
new file mode 100644
index 0000000..3beb1c7
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardPolygonElement.java
@@ -0,0 +1,70 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.List;
15
16public class WhiteboardPolygonElement extends WhiteboardElement {
17
18 private final List<Point> points_ = new ArrayList<Point>();
19 private WhiteboardColor penColor_;
20 private WhiteboardColor brushColor_;
21 private int penWidth_ = 0;
22
23 public List<Point> getPoints() {
24 return new ArrayList<Point>(points_);
25 }
26
27 public void setPoints(Collection<? extends Point> points) {
28 points_.clear();
29 points_.addAll(points);
30 }
31
32 public WhiteboardColor getPenColor() {
33 return penColor_;
34 }
35
36 public void setPenColor(WhiteboardColor color) {
37 penColor_ = color;
38 }
39
40 public WhiteboardColor getBrushColor() {
41 return brushColor_;
42 }
43
44 public void setBrushColor(WhiteboardColor color) {
45 brushColor_ = color;
46 }
47
48 public int getPenWidth() {
49 return penWidth_;
50 }
51
52 public void setPenWidth(int penWidth) {
53 penWidth_ = penWidth;
54 }
55
56 @Override
57 public void accept(WhiteboardElementVisitor visitor) {
58 visitor.visit(this);
59 }
60
61 public static class Point {
62 public final int x;
63 public final int y;
64 public Point(int x,int y) {
65 this.x = x;
66 this.y = y;
67 }
68 }
69
70}
diff --git a/src/com/isode/stroke/elements/WhiteboardRectElement.java b/src/com/isode/stroke/elements/WhiteboardRectElement.java
new file mode 100644
index 0000000..205c8c1
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardRectElement.java
@@ -0,0 +1,71 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardRectElement extends WhiteboardElement {
13
14 private int x_, y_, width_, height_;
15 private WhiteboardColor penColor_;
16 private WhiteboardColor brushColor_;
17 private int penWidth_ = 1;
18
19 public WhiteboardRectElement(int x, int y, int width, int height) {
20 x_ = x;
21 y_ = y;
22 width_ = width;
23 height_ = height;
24 }
25
26 public int getX() {
27 return x_;
28 }
29
30 public int getY() {
31 return y_;
32 }
33
34 public int getWidth() {
35 return width_;
36 }
37
38 public int getHeight() {
39 return height_;
40 }
41
42 public WhiteboardColor getPenColor() {
43 return penColor_;
44 }
45
46 public void setPenColor(WhiteboardColor color) {
47 penColor_ = color;
48 }
49
50 public WhiteboardColor getBrushColor() {
51 return brushColor_;
52 }
53
54 public void setBrushColor(WhiteboardColor color) {
55 brushColor_ = color;
56 }
57
58 public int getPenWidth() {
59 return penWidth_;
60 }
61
62 public void setPenWidth(int penWidth) {
63 penWidth_ = penWidth;
64 }
65
66 @Override
67 public void accept(WhiteboardElementVisitor visitor) {
68 visitor.visit(this);
69 }
70
71}
diff --git a/src/com/isode/stroke/elements/WhiteboardTextElement.java b/src/com/isode/stroke/elements/WhiteboardTextElement.java
new file mode 100644
index 0000000..487aec0
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardTextElement.java
@@ -0,0 +1,61 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardTextElement extends WhiteboardElement {
13
14 private final int x_, y_;
15 private int size_;
16 private String text_;
17 private WhiteboardColor color_;
18
19 public WhiteboardTextElement(int x, int y) {
20 x_ = x;
21 y_ = y;
22 }
23
24 public void setText(String text) {
25 text_ = text;
26 }
27
28 public String getText() {
29 return text_;
30 }
31
32 public int getX() {
33 return x_;
34 }
35
36 public int getY() {
37 return y_;
38 }
39
40 public WhiteboardColor getColor() {
41 return color_;
42 }
43
44 public void setColor(WhiteboardColor color) {
45 color_ = color;
46 }
47
48 public int getSize() {
49 return size_;
50 }
51
52 public void setSize(int size) {
53 size_ = size;
54 }
55
56 @Override
57 public void accept(WhiteboardElementVisitor visitor) {
58 visitor.visit(this);
59 }
60
61}
diff --git a/src/com/isode/stroke/elements/WhiteboardUpdateOperation.java b/src/com/isode/stroke/elements/WhiteboardUpdateOperation.java
new file mode 100644
index 0000000..9ca5e32
--- /dev/null
+++ b/src/com/isode/stroke/elements/WhiteboardUpdateOperation.java
@@ -0,0 +1,43 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.elements;
11
12public class WhiteboardUpdateOperation extends WhiteboardOperation {
13
14 private WhiteboardElement element_;
15 private int newPos_ = 0;
16
17 public WhiteboardUpdateOperation() {
18 // Empty Constructor
19 }
20
21 public WhiteboardUpdateOperation(WhiteboardUpdateOperation other) {
22 super(other);
23 this.element_ = other.element_;
24 this.newPos_ = other.newPos_;
25 }
26
27 public WhiteboardElement getElement() {
28 return element_;
29 }
30
31 public void setElement(WhiteboardElement element) {
32 element_ = element;
33 }
34
35 public int getNewPos() {
36 return newPos_;
37 }
38
39 public void setNewPos(int newPos) {
40 newPos_ = newPos;
41 }
42
43}
diff --git a/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java b/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java
index 39dd9f7..e8d9898 100644
--- a/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java
+++ b/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java
@@ -64,7 +64,7 @@ public class FullPayloadParserFactoryCollection extends PayloadParserFactoryColl
64 addFactory(new GenericPayloadParserFactory<JingleFileTransferFileInfoParser>("file", JingleFileTransferFileInfoParser.class)); 64 addFactory(new GenericPayloadParserFactory<JingleFileTransferFileInfoParser>("file", JingleFileTransferFileInfoParser.class));
65 addFactory(new GenericPayloadParserFactory<JingleFileTransferHashParser>("checksum", JingleFileTransferHashParser.class)); 65 addFactory(new GenericPayloadParserFactory<JingleFileTransferHashParser>("checksum", JingleFileTransferHashParser.class));
66 addFactory(new GenericPayloadParserFactory<S5BProxyRequestParser>("query", "http://jabber.org/protocol/bytestreams",S5BProxyRequestParser.class)); 66 addFactory(new GenericPayloadParserFactory<S5BProxyRequestParser>("query", "http://jabber.org/protocol/bytestreams",S5BProxyRequestParser.class));
67 // addFactory(new GenericPayloadParserFactory<WhiteboardParser>("wb","http://swift.im/whiteboard",WhiteboardParser.class)); 67 addFactory(new GenericPayloadParserFactory<WhiteboardParser>("wb","http://swift.im/whiteboard",WhiteboardParser.class));
68 addFactory(new GenericPayloadParserFactory<UserLocationParser>("geoloc", "http://jabber.org/protocol/geoloc", UserLocationParser.class)); 68 addFactory(new GenericPayloadParserFactory<UserLocationParser>("geoloc", "http://jabber.org/protocol/geoloc", UserLocationParser.class));
69 addFactory(new GenericPayloadParserFactory<UserTuneParser>("tune", "http://jabber.org/protocol/tune", UserTuneParser.class)); 69 addFactory(new GenericPayloadParserFactory<UserTuneParser>("tune", "http://jabber.org/protocol/tune", UserTuneParser.class));
70 addFactory(new DeliveryReceiptParserFactory()); 70 addFactory(new DeliveryReceiptParserFactory());
diff --git a/src/com/isode/stroke/parser/payloadparsers/WhiteboardParser.java b/src/com/isode/stroke/parser/payloadparsers/WhiteboardParser.java
new file mode 100644
index 0000000..3cbe8d8
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/WhiteboardParser.java
@@ -0,0 +1,390 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.parser.payloadparsers;
11
12import java.util.ArrayList;
13import java.util.List;
14
15import com.isode.stroke.elements.WhiteboardColor;
16import com.isode.stroke.elements.WhiteboardDeleteOperation;
17import com.isode.stroke.elements.WhiteboardElement;
18import com.isode.stroke.elements.WhiteboardEllipseElement;
19import com.isode.stroke.elements.WhiteboardFreehandPathElement;
20import com.isode.stroke.elements.WhiteboardInsertOperation;
21import com.isode.stroke.elements.WhiteboardLineElement;
22import com.isode.stroke.elements.WhiteboardOperation;
23import com.isode.stroke.elements.WhiteboardPayload;
24import com.isode.stroke.elements.WhiteboardPolygonElement;
25import com.isode.stroke.elements.WhiteboardRectElement;
26import com.isode.stroke.elements.WhiteboardUpdateOperation;
27import com.isode.stroke.elements.WhiteboardPayload.Type;
28import com.isode.stroke.elements.WhiteboardTextElement;
29import com.isode.stroke.parser.AttributeMap;
30import com.isode.stroke.parser.GenericPayloadParser;
31
32public class WhiteboardParser extends GenericPayloadParser<WhiteboardPayload> {
33
34 private boolean actualIsText;
35 private int level_;
36 private String data_;
37 private WhiteboardElement wbElement;
38 private WhiteboardOperation operation;
39
40 public WhiteboardParser() {
41 super(new WhiteboardPayload());
42 }
43
44 @Override
45 public void handleStartElement(String element, String ns,
46 AttributeMap attributes) {
47 if (level_ == 0) {
48 getPayloadInternal().setType(stringToType(getAttributeOr(attributes, "type", "")));
49 }
50 else if (level_ == 1) {
51 String type = getAttributeOr(attributes, "type", "");
52 if (type.equals("insert")) {
53 operation = new WhiteboardInsertOperation();
54 }
55 else if (type.equals("update")) {
56 WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation();
57 String move = getAttributeOr(attributes, "newpos", "0");
58 updateOp.setNewPos(Integer.parseInt(move));
59 operation = updateOp;
60 }
61 else if (type.equals("delete")) {
62 WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation();
63 deleteOp.setElementID(getAttributeOr(attributes, "elementid", ""));
64 operation = deleteOp;
65 }
66 if (operation != null) {
67 operation.setID(getAttributeOr(attributes, "id", ""));
68 operation.setParentID(getAttributeOr(attributes, "parentid", ""));
69 try {
70 operation.setPos(getIntAttribute(attributes, "pos", 0));
71 } catch (NumberFormatException e) {
72 // Dont set pos
73 }
74 }
75
76 }
77 else if (level_ == 2) {
78 if ("line".equals(element)) {
79 int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
80 try {
81 x1 = getIntAttribute(attributes, "x1", 0);
82 y1 = getIntAttribute(attributes, "y1", 0);
83 x2 = getIntAttribute(attributes, "x2", 0);
84 y2 = getIntAttribute(attributes, "y2", 0);
85 } catch (NumberFormatException e) {
86 }
87 WhiteboardLineElement whiteboardElement = new WhiteboardLineElement(x1, y1, x2, y2);
88
89 WhiteboardColor color = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000"));
90 color.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1")));
91 whiteboardElement.setColor(color);
92
93 int penWidth = 1;
94 try {
95 penWidth = getIntAttribute(attributes, "stroke-width", 1);
96 } catch (NumberFormatException e) {
97 // Empty Catch
98 }
99
100 whiteboardElement.setPenWidth(penWidth);
101 whiteboardElement.setID(getAttributeOr(attributes,"id",""));
102 getPayloadInternal().setElement(whiteboardElement);
103 wbElement = whiteboardElement;
104 }
105 else if ("path".equals(element)) {
106 WhiteboardFreehandPathElement whiteboardElement = new WhiteboardFreehandPathElement();
107 String pathData = getAttributeOr(attributes, "d", "");
108 List<WhiteboardFreehandPathElement.Point> points =
109 new ArrayList<WhiteboardFreehandPathElement.Point>();
110 if (!pathData.isEmpty() && pathData.charAt(0) == 'M') {
111 try {
112 int pos = 1, npos;
113 int x, y;
114 if (pathData.charAt(pos) == ' ') {
115 pos++;
116 }
117 npos = pathData.indexOf(' ',pos);
118 x = Integer.parseInt(pathData.substring(pos, npos));
119 pos = npos+1;
120 npos = pathData.indexOf('L',pos);
121 y = Integer.parseInt(pathData.substring(pos,npos));
122 pos = npos+1;
123 if (pos < pathData.length() && pathData.charAt(pos) == ' ') {
124 pos++;
125 }
126 points.add(new WhiteboardFreehandPathElement.Point(x,y));
127 while (pos < pathData.length()) {
128 npos = pathData.indexOf(' ',pos);
129 x = Integer.parseInt(pathData.substring(pos, npos));
130 pos = npos+1;
131 npos = pathData.indexOf(' ',pos);
132 y = Integer.parseInt(pathData.substring(pos, npos));
133 pos = npos+1;
134 points.add(new WhiteboardFreehandPathElement.Point(x,y));
135 }
136 }
137 catch (NumberFormatException e) {
138 // Empty catch
139 }
140 }
141 whiteboardElement.setPoints(points);
142
143 int penWidth = 1;
144 try {
145 penWidth = getIntAttribute(attributes, "stroke-width", 1);
146 } catch (NumberFormatException e) {
147 // Empty Catch
148 }
149 whiteboardElement.setPenWidth(penWidth);
150
151 WhiteboardColor color = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000"));
152 color.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1")));
153 whiteboardElement.setColor(color);
154 whiteboardElement.setID(getAttributeOr(attributes,"id",""));
155 getPayloadInternal().setElement(whiteboardElement);
156 wbElement = whiteboardElement;
157 }
158 else if ("rect".equals(element)) {
159 int x = 0, y = 0, width = 0, height = 0;
160 try {
161 x = getIntAttribute(attributes, "x", 0);
162 y = getIntAttribute(attributes, "y", 0);
163 width = getIntAttribute(attributes, "width", 0);
164 height = getIntAttribute(attributes, "height", 0);
165 } catch (Exception e) {
166 // Empty Catch
167 }
168
169 WhiteboardRectElement whiteboardElement = new WhiteboardRectElement(x,y,width,height);
170
171 int penWidth = 1;
172 try {
173 penWidth = getIntAttribute(attributes, "stroke-width", 1);
174 } catch (NumberFormatException e) {
175 // Empty Catch Block
176 }
177 whiteboardElement.setPenWidth(penWidth);
178
179 WhiteboardColor penColor = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000"));
180 WhiteboardColor brushColor = new WhiteboardColor(getAttributeOr(attributes, "fill", "#000000"));
181 penColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1")));
182 brushColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "fill-opacity", "1")));
183 whiteboardElement.setPenColor(penColor);
184 whiteboardElement.setBrushColor(brushColor);;
185 whiteboardElement.setID(getAttributeOr(attributes, "id", ""));
186 getPayloadInternal().setElement(whiteboardElement);
187 wbElement = whiteboardElement;
188 }
189 else if ("polygon".equals(element)) {
190 WhiteboardPolygonElement whiteboardElement = new WhiteboardPolygonElement();
191
192 String pointsData = getAttributeOr(attributes, "points", "");
193 List<WhiteboardPolygonElement.Point> points = new ArrayList<WhiteboardPolygonElement.Point>();
194 int pos = 0;
195 int npos;
196 int x,y;
197 try {
198 while (pos < pointsData.length()) {
199 npos = pointsData.indexOf(',', pos);
200 if (npos == -1) {
201 break;
202 }
203 x = Integer.parseInt(pointsData.substring(pos, npos));
204 pos = npos+1;
205 npos = pointsData.indexOf(' ',pos);
206 if (npos == -1) {
207 npos = pointsData.length();
208 }
209 y = Integer.parseInt(pointsData.substring(pos,npos));
210 pos = npos+1;
211 points.add(new WhiteboardPolygonElement.Point(x,y));
212 }
213 } catch (NumberFormatException e) {
214 // Empty catch
215 }
216
217 whiteboardElement.setPoints(points);
218
219 int penWidth = 0;
220 try {
221 penWidth = getIntAttribute(attributes, "stroke-width", 1);
222 } catch (Exception e) {
223 // Empty catch
224 }
225
226 WhiteboardColor penColor = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000"));
227 WhiteboardColor brushColor = new WhiteboardColor(getAttributeOr(attributes, "fill", "#000000"));
228 penColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1")));
229 brushColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "fill-opacity", "1")));
230
231 whiteboardElement.setPenColor(penColor);
232 whiteboardElement.setBrushColor(brushColor);
233 whiteboardElement.setID(getAttributeOr(attributes, "id", ""));
234 getPayloadInternal().setElement(whiteboardElement);
235 wbElement = whiteboardElement;
236 }
237 else if ("text".equals(element)) {
238 int x = 0, y = 0;
239 try {
240 x = getIntAttribute(attributes, "x", 0);
241 y = getIntAttribute(attributes, "y", 0);
242 } catch (NumberFormatException e) {
243 // Empty Catch
244 }
245
246 WhiteboardTextElement whiteboardElement = new WhiteboardTextElement(x, y);
247
248 actualIsText = true;
249 WhiteboardColor color = new WhiteboardColor(getAttributeOr(attributes, "fill", "#000000"));
250 color.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1")));
251 whiteboardElement.setColor(color);
252
253 int fontSize = 1;
254 try {
255 fontSize = getIntAttribute(attributes, "font-size", 12);
256 } catch (NumberFormatException e) {
257 // Empty Catch
258 }
259
260 whiteboardElement.setSize(fontSize);
261 whiteboardElement.setID(getAttributeOr(attributes, "id", ""));
262 getPayloadInternal().setElement(whiteboardElement);
263 wbElement = whiteboardElement;
264 }
265 else if ("ellipse".equals(element)) {
266 int cx = 0, cy = 0, rx = 0, ry = 0;
267 try {
268 cx = getIntAttribute(attributes, "cx", 0);
269 cy = getIntAttribute(attributes, "cy", 0);
270 rx = getIntAttribute(attributes, "rx", 0);
271 ry = getIntAttribute(attributes, "ry", 0);
272 } catch (NumberFormatException e) {
273 // Empty Catch
274 }
275
276 WhiteboardEllipseElement whiteboardElement = new WhiteboardEllipseElement(cx, cy, rx, ry);
277
278 int penWidth = 1;
279 try {
280 penWidth = getIntAttribute(attributes, "stroke-width", 1);
281 } catch (NumberFormatException e) {
282 // Empty Catch
283 }
284 whiteboardElement.setPenWidth(penWidth);
285
286 WhiteboardColor penColor = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000"));
287 WhiteboardColor brushColor = new WhiteboardColor(getAttributeOr(attributes,"fill","#000000"));
288 penColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1")));
289 brushColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "fill-opacity", "1")));
290 whiteboardElement.setPenColor(penColor);
291 whiteboardElement.setBrushColor(brushColor);
292 whiteboardElement.setID(getAttributeOr(attributes, "id", ""));
293 getPayloadInternal().setElement(whiteboardElement);
294 wbElement = whiteboardElement;
295 }
296 }
297 ++level_;
298 }
299
300 public void handleEndElement(String element, String ns) {
301 --level_;
302 if (level_ == 0) {
303 getPayloadInternal().setData(data_);
304 } else if (level_ == 1) {
305 if (operation instanceof WhiteboardInsertOperation) {
306 WhiteboardInsertOperation insertOp = (WhiteboardInsertOperation) operation;
307 insertOp.setElement(wbElement);
308 }
309 if (operation instanceof WhiteboardUpdateOperation) {
310 WhiteboardUpdateOperation updateOp = (WhiteboardUpdateOperation) operation;
311 updateOp.setElement(wbElement);
312 }
313 getPayloadInternal().setOperation(operation);
314 } else if (level_ == 2) {
315 if (element == "text") {
316 actualIsText = false;
317 }
318 }
319
320 }
321
322 @Override
323 public void handleCharacterData(String data) {
324 if (level_ == 3 && actualIsText) {
325 WhiteboardTextElement element = (WhiteboardTextElement) getPayloadInternal().getElement();
326 element.setText(data);
327 }
328 }
329
330 private WhiteboardPayload.Type stringToType(String type) {
331 if (type == "data") {
332 return Type.Data;
333 } else if (type == "session-request") {
334 return Type.SessionRequest;
335 } else if (type == "session-accept") {
336 return Type.SessionAccept;
337 } else if (type == "session-terminate") {
338 return Type.SessionTerminate;
339 } else {
340 return Type.UnknownType;
341 }
342 }
343
344 private int opacityToAlpha(String opacity) {
345 int value = 255;
346 int location = opacity.indexOf('.');
347 if (location != -1 && opacity.length() > (3+location)) {
348 String stringValue = opacity.substring(location+1,location+3);
349 try {
350 value = Integer.parseInt(stringValue)*255/100;
351 } catch (NumberFormatException nfe) {
352 value = 255;
353 }
354 }
355 return value;
356 }
357
358 /**
359 * Gets the given attribute from a {@link AttributeMap} if it is set and none
360 * {@code null}, otherwise returns a default value.
361 * @param attributeMap An {@link AttributeMap}
362 * @param attribute The name of the attribute to get from the map.
363 * @param defaultValue Default value to return if the attribute is not set
364 * (or is set to {@code null}) in the {@link AttributeMap}
365 * @return The value of the attribute in the {@link AttributeMap} if it is
366 * none {@code null} or {@code defaultValue}
367 */
368 private String getAttributeOr(AttributeMap attributeMap,String attribute,String defaultValue) {
369 String value = attributeMap.getAttribute(attribute);
370 if (value == null) {
371 return defaultValue;
372 }
373 return value;
374 }
375
376 /**
377 * Gets an int value for a given attribute in an attirbute map, or a default value
378 * if that attribute is not set.
379 * @param attributeMap An {@link AttributeMap}
380 * @param attribute The name of the attribute to get from the map
381 * @param defaultValue The default value to return if the attribute is not set.
382 * @throws NumberFormatException if the attribute value can not be passed into an integer.
383 * @return The value of the attribute as an int or defaultValue if it was not set.
384 */
385 private int getIntAttribute(AttributeMap attributeMap,String attribute,int defaultValue) throws NumberFormatException {
386 String stringValue = getAttributeOr(attributeMap, attribute, String.valueOf(defaultValue));
387 return Integer.parseInt(stringValue);
388 }
389
390}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/WhiteboardSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/WhiteboardSerializer.java
new file mode 100644
index 0000000..b0f84f5
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/WhiteboardSerializer.java
@@ -0,0 +1,221 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.serializer.payloadserializers;
11
12import java.util.Iterator;
13import java.util.logging.Logger;
14
15import com.isode.stroke.elements.WhiteboardDeleteOperation;
16import com.isode.stroke.elements.WhiteboardElementVisitor;
17import com.isode.stroke.elements.WhiteboardEllipseElement;
18import com.isode.stroke.elements.WhiteboardFreehandPathElement;
19import com.isode.stroke.elements.WhiteboardInsertOperation;
20import com.isode.stroke.elements.WhiteboardLineElement;
21import com.isode.stroke.elements.WhiteboardOperation;
22import com.isode.stroke.elements.WhiteboardPayload;
23import com.isode.stroke.elements.WhiteboardPayload.Type;
24import com.isode.stroke.elements.WhiteboardPolygonElement;
25import com.isode.stroke.elements.WhiteboardRectElement;
26import com.isode.stroke.elements.WhiteboardTextElement;
27import com.isode.stroke.elements.WhiteboardUpdateOperation;
28import com.isode.stroke.serializer.GenericPayloadSerializer;
29import com.isode.stroke.serializer.xml.XMLElement;
30import com.isode.stroke.serializer.xml.XMLTextNode;
31
32public class WhiteboardSerializer extends GenericPayloadSerializer<WhiteboardPayload> {
33
34 private static class WhiteboardElementSerializingVisitor implements WhiteboardElementVisitor {
35
36 private XMLElement element;
37
38 @Override
39 public void visit(WhiteboardLineElement line) {
40 element = new XMLElement("line");
41 element.setAttribute("x1", String.valueOf(line.x1()));
42 element.setAttribute("y1", String.valueOf(line.y1()));
43 element.setAttribute("x2", String.valueOf(line.x2()));
44 element.setAttribute("y2", String.valueOf(line.y2()));
45 element.setAttribute("id", line.getID());
46 element.setAttribute("stroke", line.getColor().toHex());
47 element.setAttribute("stroke-width", String.valueOf(line.getPenWidth()));
48 element.setAttribute("opacity", alphaToOpacity(line.getColor().getAlpha()));
49 }
50
51 @Override
52 public void visit(WhiteboardFreehandPathElement path) {
53 element = new XMLElement("path");
54 element.setAttribute("id", path.getID());
55 element.setAttribute("stroke", path.getColor().toHex());
56 element.setAttribute("stroke-width", String.valueOf(path.getPenWidth()));
57 element.setAttribute("opacity", alphaToOpacity(path.getColor().getAlpha()));
58 StringBuilder pathDataBuilder = new StringBuilder();
59 if (!path.getPoints().isEmpty()) {
60 Iterator<WhiteboardFreehandPathElement.Point> it = path.getPoints().iterator();
61 WhiteboardFreehandPathElement.Point point = it.next();
62 pathDataBuilder.append('M');
63 pathDataBuilder.append(point.x);
64 pathDataBuilder.append(' ');
65 pathDataBuilder.append(point.y);
66 pathDataBuilder.append('L');
67 while (it.hasNext()) {
68 point = it.next();
69 pathDataBuilder.append(point.x);
70 pathDataBuilder.append(' ');
71 pathDataBuilder.append(point.y);
72 pathDataBuilder.append(' ');
73 }
74 }
75 element.setAttribute("d", pathDataBuilder.toString());
76 }
77
78 @Override
79 public void visit(WhiteboardRectElement rect) {
80 element = new XMLElement("rect");
81 element.setAttribute("x", String.valueOf(rect.getX()));
82 element.setAttribute("y", String.valueOf(rect.getY()));
83 element.setAttribute("width", String.valueOf(rect.getWidth()));
84 element.setAttribute("height", String.valueOf(rect.getHeight()));
85 element.setAttribute("id", rect.getID());
86 element.setAttribute("stroke", rect.getPenColor().toHex());
87 element.setAttribute("fill", rect.getBrushColor().toHex());;
88 element.setAttribute("stroke-width", String.valueOf(rect.getPenWidth()));
89 element.setAttribute("opacity", alphaToOpacity(rect.getPenColor().getAlpha()));
90 element.setAttribute("fill-opacity", alphaToOpacity(rect.getBrushColor().getAlpha()));
91 }
92
93 @Override
94 public void visit(WhiteboardPolygonElement polygon) {
95 element = new XMLElement("polygon");
96 element.setAttribute("id", polygon.getID());
97 element.setAttribute("stroke", polygon.getPenColor().toHex());
98 element.setAttribute("fill", polygon.getBrushColor().toHex());;
99 element.setAttribute("stroke-width", String.valueOf(polygon.getPenWidth()));
100 element.setAttribute("opacity", alphaToOpacity(polygon.getPenColor().getAlpha()));
101 element.setAttribute("fill-opacity", alphaToOpacity(polygon.getBrushColor().getAlpha()));
102 StringBuilder points = new StringBuilder();
103 for (WhiteboardPolygonElement.Point point : polygon.getPoints()) {
104 points.append(point.x);
105 points.append(',');
106 points.append(point.y);
107 points.append(' ');
108 }
109 element.setAttribute("points", points.toString());
110
111 }
112
113 @Override
114 public void visit(WhiteboardTextElement text) {
115 element = new XMLElement("text");
116 element.setAttribute("x", String.valueOf(text.getX()));
117 element.setAttribute("y", String.valueOf(text.getY()));
118 element.setAttribute("font-size", String.valueOf(text.getSize()));
119 element.setAttribute("id", text.getID());
120 element.setAttribute("fill", text.getColor().toHex());
121 element.setAttribute("opacity", alphaToOpacity(text.getColor().getAlpha()));
122 element.addNode(new XMLTextNode(text.getText()));
123
124 }
125
126 @Override
127 public void visit(WhiteboardEllipseElement ellipse) {
128 element = new XMLElement("ellipse");
129 element.setAttribute("cx", String.valueOf(ellipse.getCX()));
130 element.setAttribute("cy", String.valueOf(ellipse.getCY()));
131 element.setAttribute("rx", String.valueOf(ellipse.getRX()));
132 element.setAttribute("ry", String.valueOf(ellipse.getRY()));
133 element.setAttribute("id", ellipse.getID());
134 element.setAttribute("stroke", ellipse.getPenColor().toHex());
135 element.setAttribute("fill", ellipse.getBrushColor().toHex());;
136 element.setAttribute("stroke-width", String.valueOf(ellipse.getPenWidth()));
137 element.setAttribute("opacity", alphaToOpacity(ellipse.getPenColor().getAlpha()));
138 element.setAttribute("fill-opacity", alphaToOpacity(ellipse.getBrushColor().getAlpha()));
139 }
140
141 public XMLElement getResult() {
142 return element;
143 }
144
145 private String alphaToOpacity(int alpha) {
146 int opacity = 100*alpha/254;
147 if (opacity == 100) {
148 return "1";
149 } else {
150 return String.format(".%d", opacity);
151 }
152 }
153
154 }
155
156 private final Logger logger = Logger.getLogger(this.getClass().getName());
157
158 public WhiteboardSerializer() {
159 super(WhiteboardPayload.class);
160 }
161
162 @Override
163 protected String serializePayload(WhiteboardPayload payload) {
164 XMLElement element = new XMLElement("wb","http://swift.im/whiteboard");
165 if (payload.getType() == Type.Data) {
166 XMLElement operationNode = new XMLElement("operation");
167 WhiteboardElementSerializingVisitor visitor = new WhiteboardElementSerializingVisitor();
168 WhiteboardOperation operation = payload.getOperation();
169 if (operation instanceof WhiteboardInsertOperation) {
170 WhiteboardInsertOperation insertOp = (WhiteboardInsertOperation) operation;
171 operationNode.setAttribute("type", "insert");
172 operationNode.setAttribute("pos", String.valueOf(insertOp.getPos()));
173 operationNode.setAttribute("id", insertOp.getID());
174 operationNode.setAttribute("parentid", insertOp.getParentID());
175 insertOp.getElement().accept(visitor);
176 operationNode.addNode(operationNode);
177 }
178 if (operation instanceof WhiteboardUpdateOperation) {
179 WhiteboardUpdateOperation updateOp = (WhiteboardUpdateOperation) operation;
180 operationNode.setAttribute("type", "update");
181 operationNode.setAttribute("pos", String.valueOf(updateOp.getPos()));
182 operationNode.setAttribute("id", updateOp.getID());
183 operationNode.setAttribute("parentid", updateOp.getParentID());
184 operationNode.setAttribute("newpos", String.valueOf(updateOp.getNewPos()));
185 updateOp.getElement().accept(visitor);
186 operationNode.addNode(visitor.getResult());
187 }
188 if (operation instanceof WhiteboardDeleteOperation) {
189 WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation();
190 operationNode.setAttribute("type", "delete");
191 operationNode.setAttribute("pos", String.valueOf(deleteOp.getPos()));
192 operationNode.setAttribute("id", deleteOp.getID());
193 operationNode.setAttribute("parentid", deleteOp.getParentID());
194 operationNode.setAttribute("elementid", deleteOp.getElementID());
195 }
196 element.addNode(operationNode);
197 }
198 element.setAttribute("type", typeToString(payload.getType()));
199 return element.serialize();
200 }
201
202 private String typeToString(Type type) {
203 switch (type) {
204 case Data:
205 return "data";
206 case SessionAccept:
207 return "session-accept";
208 case SessionRequest:
209 return "session-request";
210 case SessionTerminate:
211 return "session-terminate";
212 case UnknownType:
213 logger.warning("Warning: Serializing unknown action value.");
214 return "";
215 default:
216 assert(false);
217 return "";
218 }
219 }
220
221}
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 @@
1package com.isode.stroke.whiteboard;
2
3import com.isode.stroke.elements.IQ;
4import com.isode.stroke.elements.WhiteboardOperation;
5import com.isode.stroke.elements.WhiteboardPayload;
6import com.isode.stroke.jid.JID;
7import com.isode.stroke.queries.GenericRequest;
8import com.isode.stroke.queries.IQRouter;
9
10public class IncomingWhiteboardSession extends WhiteboardSession {
11
12 private final WhiteboardClient client = new WhiteboardClient();
13
14 public IncomingWhiteboardSession(JID jid, IQRouter router) {
15 super(jid, router);
16 }
17
18 public void accept() {
19 WhiteboardPayload payload = new WhiteboardPayload(WhiteboardPayload.Type.SessionAccept);
20 GenericRequest<WhiteboardPayload> request =
21 new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
22 request.send();
23 onRequestAccepted.emit(toJID_);
24 }
25
26 @Override
27 public void sendOperation(WhiteboardOperation operation) {
28 operation.setID(idGenerator_.generateID());
29 operation.setParentID(lastOpID_);
30 lastOpID_ = operation.getID();
31
32 WhiteboardOperation result = client.handleLocalOperationReceived(operation);
33
34 if (result != null) {
35 WhiteboardPayload payload = new WhiteboardPayload();
36 payload.setOperation(result);
37 sendPayload(payload);
38 }
39 }
40
41 @Override
42 protected void handleIncomingOperation(WhiteboardOperation operation) {
43 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(operation);
44 if (pairResult.client != null) {
45 if (pairResult.client.getPos() != -1) {
46 onOperationReceived.emit(pairResult.client);
47 }
48 lastOpID_ = pairResult.client.getID();
49 }
50
51 if (pairResult.server != null) {
52 WhiteboardPayload payload = new WhiteboardPayload();
53 payload.setOperation(pairResult.server);
54 sendPayload(payload);
55 }
56 }
57
58
59
60}
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 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import com.isode.stroke.elements.ErrorPayload;
13import com.isode.stroke.elements.IQ;
14import com.isode.stroke.elements.WhiteboardOperation;
15import com.isode.stroke.elements.WhiteboardPayload;
16import com.isode.stroke.jid.JID;
17import com.isode.stroke.queries.GenericRequest;
18import com.isode.stroke.queries.IQRouter;
19import com.isode.stroke.signals.Slot2;
20
21public class OutgoingWhiteboardSession extends WhiteboardSession {
22
23 private final WhiteboardServer server = new WhiteboardServer();
24
25 public OutgoingWhiteboardSession(JID jid, IQRouter router) {
26 super(jid, router);
27 }
28
29 public void startSession() {
30
31 WhiteboardPayload payload = new WhiteboardPayload(WhiteboardPayload.Type.SessionRequest);
32 GenericRequest<WhiteboardPayload> request =
33 new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
34 request.onResponse.connect(new Slot2<WhiteboardPayload, ErrorPayload>() {
35
36 @Override
37 public void call(WhiteboardPayload payload, ErrorPayload error) {
38 handleRequestResponse(payload, error);
39 }
40
41 });
42 request.send();
43 }
44
45 private void handleRequestResponse(WhiteboardPayload payload,ErrorPayload error) {
46 if (error != null) {
47 onRequestRejected.emit(toJID_);
48 }
49 }
50
51 @Override
52 public void sendOperation(WhiteboardOperation operation) {
53 operation.setID(idGenerator_.generateID());
54 operation.setParentID(lastOpID_);
55 lastOpID_ = operation.getID();
56
57 server.handleLocalOperationReceived(operation);
58 WhiteboardPayload payload = new WhiteboardPayload();
59 payload.setOperation(operation);
60 sendPayload(payload);
61 }
62
63 @Override
64 protected void handleIncomingOperation(WhiteboardOperation operation) {
65 WhiteboardOperation op = server.handleClientOperationReceived(operation);
66 if (op.getPos() != -1) {
67 onOperationReceived.emit(op);
68 }
69 lastOpID_ = op.getID();
70
71 WhiteboardPayload payload = new WhiteboardPayload();
72 payload.setOperation(op);
73 sendPayload(payload);
74 }
75
76}
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 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import java.util.ArrayList;
13import java.util.List;
14
15import com.isode.stroke.elements.WhiteboardDeleteOperation;
16import com.isode.stroke.elements.WhiteboardInsertOperation;
17import com.isode.stroke.elements.WhiteboardOperation;
18import com.isode.stroke.elements.WhiteboardUpdateOperation;
19import com.isode.stroke.whiteboard.WhiteboardTransformer.Pair;
20
21public class WhiteboardClient {
22
23 private final List<WhiteboardOperation> localOperations_ = new ArrayList<WhiteboardOperation>();
24 private final List<WhiteboardOperation> serverOperations_ = new ArrayList<WhiteboardOperation>();
25 private final List<WhiteboardOperation> bridge_ = new ArrayList<WhiteboardOperation>();
26 private String lastSentOperationID_ = "";
27
28 public static class Result {
29 public final WhiteboardOperation client;
30 public final WhiteboardOperation server;
31 public Result(WhiteboardOperation client,WhiteboardOperation server) {
32 this.client = client;
33 this.server = server;
34 }
35 }
36
37 public WhiteboardOperation handleLocalOperationReceived(WhiteboardOperation operation) {
38 localOperations_.add(operation);
39
40 WhiteboardOperation op = null;
41 if (operation instanceof WhiteboardInsertOperation) {
42 op = new WhiteboardInsertOperation((WhiteboardInsertOperation) operation);
43 }
44 else if (operation instanceof WhiteboardUpdateOperation) {
45 op = new WhiteboardUpdateOperation((WhiteboardUpdateOperation) operation);
46 }
47 else if (operation instanceof WhiteboardDeleteOperation) {
48 op = new WhiteboardDeleteOperation((WhiteboardDeleteOperation) operation);
49 }
50
51 if (!bridge_.isEmpty()) {
52 WhiteboardOperation back = bridge_.get(bridge_.size()-1);
53 op.setParentID(back.getID());
54 }
55 bridge_.add(op);
56
57 if (lastSentOperationID_.isEmpty()) {
58 if (operation instanceof WhiteboardInsertOperation) {
59 op = new WhiteboardInsertOperation((WhiteboardInsertOperation) operation);
60 }
61 else if (operation instanceof WhiteboardUpdateOperation) {
62 op = new WhiteboardUpdateOperation((WhiteboardUpdateOperation) operation);
63 }
64 else if (operation instanceof WhiteboardDeleteOperation) {
65 op = new WhiteboardDeleteOperation((WhiteboardDeleteOperation) operation);
66 }
67
68 if (!serverOperations_.isEmpty()) {
69 WhiteboardOperation back = serverOperations_.get(serverOperations_.size()-1);
70 op.setParentID(back.getID());
71 }
72 lastSentOperationID_ = operation.getID();
73 return op;
74 }
75 else{
76 return null;
77 }
78 }
79
80 public Result handleServerOperationReceived(WhiteboardOperation operation) {
81 serverOperations_.add(operation);
82 WhiteboardOperation clientResult = null, serverResult = null;
83 if (localOperations_.size() == (serverOperations_.size()-1) ) {
84 localOperations_.add(operation);
85 clientResult = operation;
86 }
87 else if (lastSentOperationID_ == operation.getID()) {
88 //Client received confirmation about own operation and it sends next operation to server
89 if (!bridge_.isEmpty() && lastSentOperationID_ .equals(bridge_.get(0).getID())) {
90 bridge_.remove(0);
91 }
92
93 if (!bridge_.isEmpty() && lastSentOperationID_ .equals(bridge_.get(0).getParentID())) {
94 lastSentOperationID_ = bridge_.get(0).getID();
95 serverResult = bridge_.get(0);
96 }
97 if (serverResult == null) {
98 lastSentOperationID_ = "";
99 }
100 }
101 else if (!bridge_.isEmpty()) {
102 WhiteboardOperation temp;
103 Pair opPair = WhiteboardTransformer.transform(bridge_.get(0), operation);
104 temp = opPair.first;
105
106 bridge_.set(0, opPair.second);
107 String previousID = bridge_.get(0).getID();
108 for (int i = 1; i < bridge_.size(); ++i) {
109 opPair = WhiteboardTransformer.transform(bridge_.get(i), temp);
110 temp = opPair.first;
111 bridge_.set(i, opPair.second);
112 bridge_.get(i).setParentID(previousID);
113 previousID = bridge_.get(i).getID();
114 }
115
116 WhiteboardOperation localBack = localOperations_.get(localOperations_.size()-1);
117 temp.setParentID(localBack.getID());
118 localOperations_.add(temp);
119 clientResult = temp;
120 }
121
122 return new Result(clientResult, serverResult);
123 }
124
125 @Override
126 public String toString() {
127 StringBuilder builder = new StringBuilder();
128 builder.append("Client\n");
129 for (WhiteboardOperation op : localOperations_) {
130 builder.append(op.getID());
131 builder.append(' ');
132 builder.append(op.getPos());
133 builder.append('\n');
134 }
135 builder.append("Server\n");
136 for (WhiteboardOperation op : serverOperations_) {
137 builder.append(op.getID());
138 builder.append(' ');
139 builder.append(op.getPos());
140 builder.append('\n');
141 }
142 return builder.toString();
143 }
144
145 public void print() {
146 System.out.println(this);
147 }
148
149}
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 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import com.isode.stroke.elements.WhiteboardPayload;
13import com.isode.stroke.jid.JID;
14import com.isode.stroke.queries.IQRouter;
15import com.isode.stroke.queries.SetResponder;
16import com.isode.stroke.elements.ErrorPayload;
17
18public class WhiteboardResponder extends SetResponder<WhiteboardPayload> {
19
20 private final WhiteboardSessionManager sessionManager_;
21 private final IQRouter router_;
22
23 public WhiteboardResponder(WhiteboardSessionManager sessionManager,IQRouter router) {
24 super(new WhiteboardPayload(),router);
25 sessionManager_ = sessionManager;
26 router_ = router;
27 }
28
29 @Override
30 protected boolean handleSetRequest(JID from, JID to, String id,
31 WhiteboardPayload payload) {
32 if (payload.getType() == WhiteboardPayload.Type.SessionRequest) {
33 if (sessionManager_.getSession(from) != null) {
34 sendError(from, id, ErrorPayload.Condition.Conflict, ErrorPayload.Type.Cancel);
35 }
36 else {
37 sendResponse(from, id, null);
38 IncomingWhiteboardSession session = new IncomingWhiteboardSession(from, router_);
39 sessionManager_.handleIncomingSession(session);
40 }
41 }
42 else {
43 sendResponse(from, id, null);
44 WhiteboardSession session = sessionManager_.getSession(from);
45 if (session != null) {
46 session.handleIncomingAction(payload);
47 }
48 }
49 return true;
50 }
51
52}
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 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import java.util.ArrayList;
13import java.util.List;
14
15import com.isode.stroke.elements.WhiteboardOperation;
16
17public class WhiteboardServer {
18
19 private final List<WhiteboardOperation> operations_ = new ArrayList<WhiteboardOperation>();
20
21 public void handleLocalOperationReceived(WhiteboardOperation operation) {
22 operations_.add(operation);
23 }
24
25 public WhiteboardOperation handleClientOperationReceived(WhiteboardOperation newOperation) {
26
27 if (operations_.isEmpty() ||
28 newOperation.getParentID().equals(operations_.get(operations_.size()-1).getID())) {
29 operations_.add(newOperation);
30 return newOperation;
31 }
32 for (int i = (operations_.size()-1); i >= 0; i--) {
33 WhiteboardOperation operation = operations_.get(i);
34 while (newOperation.getParentID().equals(operation.getParentID())) {
35 WhiteboardTransformer.Pair tResult =
36 WhiteboardTransformer.transform(newOperation, operation);
37 if (i == (operations_.size()-1)) {
38 operations_.add(tResult.second);
39 return tResult.second;
40 }
41 else {
42 newOperation = tResult.second;
43 i++;
44 operation = operations_.get(i);
45 }
46 }
47 }
48 return null;
49 }
50
51 @Override
52 public String toString() {
53 StringBuilder builder = new StringBuilder();
54 builder.append("Server:\n");
55 for (WhiteboardOperation op : operations_) {
56 builder.append(op.getID());
57 builder.append(" '");
58 builder.append(op.getParentID());
59 builder.append("' ");
60 builder.append(op.getPos());
61 builder.append("\n");
62 }
63 return builder.toString();
64 }
65
66 public void print() {
67 System.out.println(this);
68 }
69
70}
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 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import com.isode.stroke.base.IDGenerator;
13import com.isode.stroke.elements.IQ;
14import com.isode.stroke.elements.WhiteboardElement;
15import com.isode.stroke.elements.WhiteboardOperation;
16import com.isode.stroke.elements.WhiteboardPayload;
17import com.isode.stroke.elements.WhiteboardPayload.Type;
18import com.isode.stroke.jid.JID;
19import com.isode.stroke.queries.GenericRequest;
20import com.isode.stroke.queries.IQRouter;
21import com.isode.stroke.signals.Signal1;
22
23public abstract class WhiteboardSession {
24
25 protected final JID toJID_;
26 protected final IQRouter router_;
27 protected String lastOpID_ = "";
28 protected final IDGenerator idGenerator_ = new IDGenerator();
29
30 public final Signal1<WhiteboardElement> onElementReceived = new Signal1<WhiteboardElement>();
31 public final Signal1<WhiteboardOperation> onOperationReceived = new Signal1<WhiteboardOperation>();
32 public final Signal1<JID> onSessionTerminated = new Signal1<JID>();
33 public final Signal1<JID> onRequestAccepted = new Signal1<JID>();
34 public final Signal1<JID> onRequestRejected = new Signal1<JID>();
35
36 public WhiteboardSession(JID jid,IQRouter router) {
37 toJID_ = jid;
38 router_ = router;
39 }
40
41 public void handleIncomingAction(WhiteboardPayload payload) {
42 switch(payload.getType()) {
43 case Data:
44 handleIncomingOperation(payload.getOperation());
45 return;
46 case SessionAccept:
47 onRequestAccepted.emit(toJID_);
48 return;
49 case SessionTerminate:
50 onSessionTerminated.emit(toJID_);
51 return;
52 case SessionRequest:
53 case UnknownType:
54 default:
55 return;
56 }
57 }
58 public void sendElement(WhiteboardElement element) {
59 WhiteboardPayload payload = new WhiteboardPayload();
60 payload.setElement(element);
61 GenericRequest<WhiteboardPayload> request =
62 new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
63 request.send();
64 }
65
66 public abstract void sendOperation(WhiteboardOperation operation);
67
68 public void cancel() {
69 if (router_.isAvailable()) {
70 WhiteboardPayload payload = new WhiteboardPayload(Type.SessionTerminate);
71 GenericRequest<WhiteboardPayload> request =
72 new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
73 request.send();
74 }
75 onSessionTerminated.emit(toJID_);
76 }
77
78 public JID getTo() {
79 return toJID_;
80 }
81
82 protected abstract void handleIncomingOperation(WhiteboardOperation operation);
83
84 protected final void sendPayload(WhiteboardPayload payload) {
85 GenericRequest<WhiteboardPayload> request = new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_);
86 request.send();
87 }
88
89}
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 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import java.util.ArrayList;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16
17import com.isode.stroke.client.StanzaChannel;
18import com.isode.stroke.disco.EntityCapsProvider;
19import com.isode.stroke.elements.DiscoInfo;
20import com.isode.stroke.elements.Presence;
21import com.isode.stroke.jid.JID;
22import com.isode.stroke.presence.PresenceOracle;
23import com.isode.stroke.queries.IQRouter;
24import com.isode.stroke.signals.Signal1;
25import com.isode.stroke.signals.Slot1;
26
27public class WhiteboardSessionManager {
28
29 private final Map<JID,WhiteboardSession> sessions_ = new HashMap<JID,WhiteboardSession>();
30 private final IQRouter router_;
31 private final StanzaChannel stanzaChannel_;
32 private final PresenceOracle presenceOracle_;
33 private final EntityCapsProvider capsProvider_;
34 private WhiteboardResponder responder;
35
36 public final Signal1<IncomingWhiteboardSession> onSessionRequest =
37 new Signal1<IncomingWhiteboardSession>();
38
39 public WhiteboardSessionManager(IQRouter router, StanzaChannel stanzaChannel,
40 PresenceOracle presenceOracle, EntityCapsProvider capsProvider) {
41 router_ = router;
42 stanzaChannel_ = stanzaChannel;
43 presenceOracle_ = presenceOracle;
44 capsProvider_ = capsProvider;
45 responder = new WhiteboardResponder(this, router_);
46 responder.start();
47 stanzaChannel_.onPresenceReceived.connect(new Slot1<Presence>() {
48
49 @Override
50 public void call(Presence presence) {
51 handlePresenceReceived(presence);
52 }
53
54 });
55 stanzaChannel_.onAvailableChanged.connect(new Slot1<Boolean>() {
56
57 @Override
58 public void call(Boolean p1) {
59 handleAvailableChanged(p1.booleanValue());
60 }
61
62 });
63 }
64
65 // Unlike in C++ we can't put this in a destructor to automatically be called when object is
66 // destroyed. Must be called manually.
67 public void stop() {
68 responder.stop();
69 }
70
71 public WhiteboardSession getSession(JID to) {
72 return sessions_.get(to);
73 }
74
75 public WhiteboardSession requestSession(JID to) {
76 WhiteboardSession session = getSession(to);
77 if (session == null) {
78 OutgoingWhiteboardSession outgoingSession = createOutgoingSession(to);
79 outgoingSession.startSession();
80 return outgoingSession;
81 } else {
82 return session;
83 }
84 }
85
86 private JID getFullJID(JID bareJID) {
87 JID fullReceipientJID = null;
88 int priority = Integer.MIN_VALUE;
89
90 //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
91 List<Presence> presences =
92 new ArrayList<Presence>(presenceOracle_.getAllPresence(bareJID));
93
94 //iterate over them
95 for (Presence pres : presences) {
96 if (pres.getPriority() > priority) {
97 // look up caps from the jid
98 DiscoInfo info = capsProvider_.getCaps(pres.getFrom());
99 if (info != null && info.hasFeature(DiscoInfo.WhiteboardFeature)) {
100 priority = pres.getPriority();
101 fullReceipientJID = pres.getFrom();
102 }
103 }
104 }
105
106 return fullReceipientJID;
107 }
108
109 private OutgoingWhiteboardSession createOutgoingSession(JID to) {
110 JID fullJID = to;
111 if (fullJID.isBare()) {
112 fullJID = getFullJID(fullJID);
113 }
114 OutgoingWhiteboardSession session = new OutgoingWhiteboardSession(fullJID, router_);
115 sessions_.put(fullJID, session);
116 session.onSessionTerminated.connect(new Slot1<JID>() {
117
118 @Override
119 public void call(JID jid) {
120 deleteSessionEntry(jid);
121 }
122
123 });
124 session.onRequestRejected.connect(new Slot1<JID>() {
125
126 @Override
127 public void call(JID jid) {
128 deleteSessionEntry(jid);
129 }
130
131 });
132 return session;
133 }
134
135 public void handleIncomingSession(IncomingWhiteboardSession session) {
136 sessions_.put(session.getTo(), session);
137 session.onSessionTerminated.connect(new Slot1<JID>() {
138
139 @Override
140 public void call(JID jid) {
141 deleteSessionEntry(jid);
142 }
143
144 });
145 onSessionRequest.emit(session);
146 }
147
148 private void handlePresenceReceived(Presence presence) {
149 if (!presence.isAvailable()) {
150 WhiteboardSession session = getSession(presence.getFrom());
151 if (session != null) {
152 session.cancel();
153 }
154 }
155 }
156 private void handleAvailableChanged(boolean available) {
157 if (!available) {
158 Map<JID,WhiteboardSession> sessionsCopy = new HashMap<JID,WhiteboardSession>(sessions_);
159 for (WhiteboardSession session : sessionsCopy.values()) {
160 session.cancel();
161 }
162 }
163 }
164
165 private void deleteSessionEntry(JID contact) {
166 sessions_.remove(contact);
167 }
168
169}
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 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import com.isode.stroke.elements.WhiteboardDeleteOperation;
13import com.isode.stroke.elements.WhiteboardInsertOperation;
14import com.isode.stroke.elements.WhiteboardOperation;
15import com.isode.stroke.elements.WhiteboardUpdateOperation;
16
17public final class WhiteboardTransformer {
18
19 public static final class Pair {
20 public final WhiteboardOperation first;
21 public final WhiteboardOperation second;
22 public Pair(WhiteboardOperation first,WhiteboardOperation second) {
23 this.first = first;
24 this.second = second;
25 }
26 public Pair() {
27 this(new WhiteboardOperation(),new WhiteboardOperation());
28 }
29 }
30
31 private WhiteboardTransformer() {
32 // Static class so constructor is private
33 }
34
35 public static Pair transform(WhiteboardOperation clientOp,WhiteboardOperation serverOp) {
36 if (clientOp instanceof WhiteboardInsertOperation) {
37 WhiteboardInsertOperation clientInsert = (WhiteboardInsertOperation) clientOp;
38 if (serverOp instanceof WhiteboardInsertOperation) {
39 WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp;
40 return transform(clientInsert, serverInsert);
41 }
42 else if (serverOp instanceof WhiteboardUpdateOperation) {
43 WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp;
44 return transform(clientInsert, serverUpdate);
45 }
46 else if (serverOp instanceof WhiteboardDeleteOperation) {
47 WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp;
48 return transform(clientInsert, serverDelete);
49 }
50 }
51 else if (clientOp instanceof WhiteboardUpdateOperation) {
52 WhiteboardUpdateOperation clientUpdate = (WhiteboardUpdateOperation) clientOp;
53 if (serverOp instanceof WhiteboardInsertOperation) {
54 WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp;
55 return transform(clientUpdate, serverInsert);
56 }
57 else if (serverOp instanceof WhiteboardUpdateOperation) {
58 WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp;
59 return transform(clientUpdate, serverUpdate);
60 }
61 else if (serverOp instanceof WhiteboardDeleteOperation) {
62 WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp;
63 return transform(clientUpdate, serverDelete);
64 }
65 }
66 else if (clientOp instanceof WhiteboardDeleteOperation) {
67 WhiteboardDeleteOperation clientDelete = (WhiteboardDeleteOperation) clientOp;
68 if (serverOp instanceof WhiteboardInsertOperation) {
69 WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp;
70 return transform(clientDelete, serverInsert);
71 }
72 else if (serverOp instanceof WhiteboardUpdateOperation) {
73 WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp;
74 return transform(clientDelete, serverUpdate);
75 }
76 else if (serverOp instanceof WhiteboardDeleteOperation) {
77 WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp;
78 return transform(clientDelete, serverDelete);
79 }
80 }
81 return new Pair();
82 }
83
84 public static Pair transform(WhiteboardInsertOperation clientOp,WhiteboardInsertOperation serverOp) {
85 WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp);
86 first.setParentID(clientOp.getID());
87 WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp);
88 second.setParentID(serverOp.getID());
89 if (clientOp.getPos() <= serverOp.getPos()) {
90 first.setPos(first.getPos()+1);
91 }
92 else {
93 second.setPos(second.getPos()+1);
94 }
95 return new Pair(first,second);
96 }
97
98 public static Pair transform(WhiteboardUpdateOperation clientOp,WhiteboardUpdateOperation serverOp) {
99
100 WhiteboardUpdateOperation first = new WhiteboardUpdateOperation(serverOp);
101 first.setParentID(clientOp.getID());
102
103 WhiteboardUpdateOperation second;
104 if (clientOp.getPos() == serverOp.getPos()) {
105 second = new WhiteboardUpdateOperation(serverOp);
106 second.setID(clientOp.getID());
107 second.setParentID(serverOp.getID());
108 }
109 else {
110 second = new WhiteboardUpdateOperation(clientOp);
111 second.setParentID(serverOp.getID());
112 }
113
114 if (clientOp.getPos() < serverOp.getPos() && clientOp.getNewPos() > serverOp.getPos()) {
115 first.setPos(first.getPos()-1);
116 if (clientOp.getNewPos() >= serverOp.getNewPos()) {
117 first.setNewPos(first.getNewPos()-1);
118 }
119 }
120 else if (clientOp.getNewPos() >= serverOp.getNewPos()) {
121 first.setNewPos(first.getNewPos()-1);
122 }
123 if (serverOp.getPos() < clientOp.getPos() && serverOp.getNewPos() > clientOp.getPos()) {
124 second.setPos(second.getPos()-1);
125 if (serverOp.getNewPos() >= clientOp.getNewPos()) {
126 second.setNewPos(second.getNewPos()-1);
127 }
128 }
129 else if (serverOp.getNewPos() >= clientOp.getNewPos()) {
130 second.setNewPos(second.getNewPos()-1);
131 }
132 return new Pair(first,second);
133 }
134
135 public static Pair transform(WhiteboardUpdateOperation clientOp,WhiteboardInsertOperation serverOp) {
136 WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp);
137 first.setParentID(clientOp.getID());
138 WhiteboardUpdateOperation second = new WhiteboardUpdateOperation(clientOp);
139 second.setParentID(serverOp.getID());
140 if (serverOp.getPos() <= clientOp.getPos()) {
141 second.setPos(second.getPos()+1);
142 }
143 return new Pair(first, second);
144 }
145
146 public static Pair transform(WhiteboardInsertOperation clientOp,WhiteboardUpdateOperation serverOp) {
147 WhiteboardUpdateOperation first = new WhiteboardUpdateOperation(serverOp);
148 first.setParentID(clientOp.getID());
149 WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp);
150 second.setParentID(serverOp.getID());
151 if (serverOp.getPos() >= clientOp.getPos()) {
152 first.setPos(first.getPos()+1);
153 }
154 return new Pair(first,second);
155 }
156
157 public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardDeleteOperation serverOp) {
158 WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp);
159 first.setParentID(clientOp.getID());
160 WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp);
161 second.setParentID(serverOp.getID());
162 if (clientOp.getPos() == -1) {
163 second.setPos(-1);
164 }
165 if (serverOp.getPos() == -1) {
166 first.setPos(-1);
167 }
168 if (clientOp.getPos() < serverOp.getPos()) {
169 first.setPos(first.getPos()-1);
170 }
171 else if (clientOp.getPos() > serverOp.getPos()) {
172 second.setPos(second.getPos()-1);
173 }
174 else {
175 first.setPos(-1);
176 second.setPos(-1);
177 }
178 return new Pair(first, second);
179 }
180
181 public static Pair transform(WhiteboardInsertOperation clientOp, WhiteboardDeleteOperation serverOp) {
182 WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp);
183 first.setParentID(clientOp.getID());
184 WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp);
185 second.setParentID(serverOp.getID());
186 if (clientOp.getPos() <= serverOp.getPos()) {
187 first.setPos(first.getPos()+1);
188 }
189 else if (serverOp.getPos() != -1) {
190 second.setPos(second.getPos()-1);
191 }
192 return new Pair(first, second);
193 }
194
195 public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardInsertOperation serverOp) {
196 WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp);
197 first.setParentID(clientOp.getID());
198 WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp);
199 second.setParentID(serverOp.getID());
200 if (serverOp.getPos() <= clientOp.getPos()) {
201 second.setPos(second.getPos()+1);
202 }
203 else if (clientOp.getPos() != -1) {
204 first.setPos(first.getPos()-1);
205 }
206 return new Pair(first, second);
207 }
208
209 public static Pair transform(WhiteboardUpdateOperation clientOp, WhiteboardDeleteOperation serverOp) {
210 WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp);
211 first.setParentID(clientOp.getID());
212 WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation(clientOp);;
213 WhiteboardOperation second = updateOp;
214 second.setParentID(serverOp.getID());
215 if (clientOp.getPos() == serverOp.getPos()) {
216 WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation();
217 second = deleteOp;
218 second.setPos(-1);
219 second.setID(clientOp.getID());
220 second.setParentID(serverOp.getID());
221 deleteOp.setElementID(serverOp.getElementID());
222 }
223 else if (clientOp.getPos() > serverOp.getPos() && clientOp.getNewPos() <= serverOp.getPos()) {
224 second.setPos(second.getPos()-1);
225 }
226 else if (clientOp.getPos() < serverOp.getPos() && clientOp.getNewPos() >= serverOp.getPos()) {
227 updateOp.setNewPos(updateOp.getNewPos()-1);
228 }
229 else if (clientOp.getPos() > serverOp.getPos()) {
230 second.setPos(second.getPos() - 1);
231 updateOp.setNewPos(updateOp.getNewPos()-1);
232 }
233 return new Pair(first, second);
234 }
235
236 public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardUpdateOperation serverOp) {
237 WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation(serverOp);
238 WhiteboardOperation first = updateOp;
239 first.setParentID(clientOp.getID());
240 WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp);
241 second.setParentID(serverOp.getID());
242 if (clientOp.getPos() == serverOp.getPos()) {
243 WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation();
244 first = deleteOp;
245 first.setPos(-1);
246 first.setID(serverOp.getID());
247 first.setParentID(clientOp.getID());
248 deleteOp.setElementID(clientOp.getElementID());
249 }
250 else if (clientOp.getPos() < serverOp.getPos() && clientOp.getPos() >= serverOp.getNewPos()) {
251 first.setPos(first.getPos()-1);
252 }
253 else if (clientOp.getPos() > serverOp.getPos() && clientOp.getPos() <= serverOp.getNewPos()) {
254 updateOp.setNewPos(updateOp.getNewPos()-1);
255 }
256 else if (clientOp.getPos() < serverOp.getPos()) {
257 first.setPos(first.getPos()-1);
258 updateOp.setNewPos(updateOp.getNewPos()-1);
259 }
260 return new Pair(first, second);
261 }
262
263
264}
diff --git a/test/com/isode/stroke/whiteboard/WhiteboardClientTest.java b/test/com/isode/stroke/whiteboard/WhiteboardClientTest.java
new file mode 100644
index 0000000..ae051de
--- /dev/null
+++ b/test/com/isode/stroke/whiteboard/WhiteboardClientTest.java
@@ -0,0 +1,672 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import static org.junit.Assert.assertEquals;
13import static org.junit.Assert.assertNull;
14
15import org.junit.Test;
16
17import com.isode.stroke.elements.WhiteboardDeleteOperation;
18import com.isode.stroke.elements.WhiteboardElement;
19import com.isode.stroke.elements.WhiteboardEllipseElement;
20import com.isode.stroke.elements.WhiteboardInsertOperation;
21import com.isode.stroke.elements.WhiteboardOperation;
22import com.isode.stroke.elements.WhiteboardUpdateOperation;
23
24
25/**
26 * Tests for {@link WhiteboardClient}
27 */
28public class WhiteboardClientTest {
29
30 /*!
31 * /\
32 * \/
33 * \
34 */
35 @Test
36 public void testSynchronize_simplestSync() {
37 WhiteboardClient client = new WhiteboardClient();
38 WhiteboardInsertOperation serverOp = createInsertOperation("0", "", 0);
39 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(serverOp);
40 assertEquals(serverOp, pairResult.client);
41 assertNull(pairResult.server);
42
43 //Client receives first local operation, because it's parented off "0" which exists
44 //in server history and client doesn't wait for any operation ack from server,
45 //so this operation could be send
46 WhiteboardInsertOperation clientOp = createInsertOperation("a", "0", 1);
47 WhiteboardEllipseElement aElement = new WhiteboardEllipseElement(0,0,0,0);
48 clientOp.setElement(aElement);
49 checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
50
51 //Client receives server operation parented off "0", which isn't last client operation
52 //so it have to be transformed against local operations and then transformed value can
53 //be returned to draw
54 serverOp = createInsertOperation("b", "0", 1);
55 WhiteboardEllipseElement bElement = new WhiteboardEllipseElement(0,0,0,0);
56 serverOp.setElement(bElement);
57 pairResult = client.handleServerOperationReceived(serverOp);
58 checkOperation(pairResult.client, "b", "a", 2, bElement);
59 assertNull(pairResult.server);
60
61 //Client receives confirmation from the server about processed "a" operation, it had to
62 //be transformed against "b" on the server side to receive operation parented off "b".
63 //There aren't any waiting operations to send to server, this operation should return
64 //nothing
65 serverOp = createInsertOperation("a", "b", 1);
66 pairResult = client.handleServerOperationReceived(serverOp);
67 assertNull(pairResult.client);
68 assertNull(pairResult.server);
69
70 //Client receives local operation, it doesn't have to be transformed against anything
71 //but operation returned to send to the server should be parented off last server
72 //operation, which is "b"
73 clientOp = createInsertOperation("c", "b", 3);
74 WhiteboardEllipseElement cElement = new WhiteboardEllipseElement(0,0,0,0);
75 clientOp.setElement(cElement);
76 checkOperation(client.handleLocalOperationReceived(clientOp), "c", "a", 3, cElement);
77
78 //Client receives confirmation from the server about processed "a" operation, it
79 //should be the same operation as it was sent because server didn't have to
80 //transform it
81 clientOp = createInsertOperation("c", "a", 3);
82 clientOp.setElement(cElement);
83 pairResult = client.handleServerOperationReceived(clientOp);
84 assertNull(pairResult.client);
85 assertNull(pairResult.server);
86
87 //Results:
88 //Client operations:
89 //ID pos
90 //0 0
91 //a 1
92 //b 2
93 //c 3
94 //
95 //Server operations:
96 //ID pos
97 //0 0
98 //b 1
99 //a 1
100 //c 3
101 //
102 //what gives 0abc on both sides
103 }
104
105 /*!
106 * /
107 * /
108 * \
109 */
110 @Test
111 public void testSynchronize_withoutTranslation() {
112 WhiteboardClient client = new WhiteboardClient();
113 WhiteboardInsertOperation serverOp = createInsertOperation("0", "", 0);
114 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(serverOp);
115 assertEquals(serverOp, pairResult.client);
116 assertNull(pairResult.server);
117
118 //Client receives first local operation, because it's parented off "0" which exists
119 //in server history and client doesn't wait for any operation ack from server,
120 //so this operation could be send
121 WhiteboardInsertOperation clientOp = createInsertOperation("c", "0", 1);
122 WhiteboardEllipseElement cElement = new WhiteboardEllipseElement(0,0,0,0);
123 clientOp.setElement(cElement);
124 checkOperation(client.handleLocalOperationReceived(clientOp), "c", "0", 1, cElement);
125
126 //Client receives second local operation, client didn't receive ack about previous
127 //operation from the server so it can't be send.
128 clientOp = createInsertOperation("d", "c", 2);
129 WhiteboardEllipseElement dElement = new WhiteboardEllipseElement(0,0,0,0);
130 clientOp.setElement(dElement);
131 assertNull(client.handleLocalOperationReceived(clientOp));
132
133 //Client receives confirmation about processing "c" operation, it should be the
134 //same as sent operation because it wasn't transformed, client could send now
135 //operation "d"
136 clientOp = createInsertOperation("c", "0", 1);
137 clientOp.setElement(cElement);
138 pairResult = client.handleServerOperationReceived(clientOp);
139 checkOperation(pairResult.server, "d", "c", 2, dElement);
140 assertNull(pairResult.client);
141
142 //Client receives confirmation about processing "d", it should be the same as
143 //sent operation. There aren't any operations in queue to send.
144 clientOp = createInsertOperation("d", "c", 2);
145 clientOp.setElement(dElement);
146 pairResult = client.handleServerOperationReceived(clientOp);
147 assertNull(pairResult.client);
148 assertNull(pairResult.server);
149
150 //Client receives new operation from server, it's parented off "d" which is at
151 //the end of local history so it doesn't have to be transformed, so operation
152 //to pass to window should be the same
153 serverOp = createInsertOperation("e", "d", 3);
154 WhiteboardEllipseElement eElement = new WhiteboardEllipseElement(0,0,0,0);
155 serverOp.setElement(eElement);
156 pairResult = client.handleServerOperationReceived(serverOp);
157 WhiteboardInsertOperation result = (WhiteboardInsertOperation) pairResult.client;
158 assertEquals(serverOp, pairResult.client);
159 assertNull(pairResult.server);
160
161
162 //Client operations:
163 //ID pos
164 //0 0
165 //c 1
166 //d 2
167 //e 3
168 //
169 //Server operations:
170 //ID pos
171 //0 0
172 //c 1
173 //d 2
174 //e 3
175 }
176
177 /*!
178 * /\
179 * / \
180 * \ /
181 * \/
182 */
183 @Test
184 public void testSynchronize_nonInterrupted() {
185 WhiteboardClient client = new WhiteboardClient();
186 WhiteboardInsertOperation serverOp = createInsertOperation("0", "", 0);
187 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(serverOp);
188 assertEquals(serverOp, pairResult.client);
189 assertEquals(null, pairResult.server);
190
191 //Client receives first local operation, because it's parented off "0" which exists
192 //in server history and client doesn't wait for any operation ack from server,
193 //so this operation could be send
194 WhiteboardInsertOperation clientOp = createInsertOperation("a", "0", 1);
195 WhiteboardEllipseElement aElement = new WhiteboardEllipseElement(0,0,0,0);
196 clientOp.setElement(aElement);
197 checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
198
199 //Client receives second local operation, client didn't receive ack about previous
200 //operation from the server so it can't be send.
201 clientOp = createInsertOperation("b", "a", 2);
202 WhiteboardEllipseElement bElement = new WhiteboardEllipseElement(0,0,0,0);
203 clientOp.setElement(bElement);
204 assertNull(client.handleLocalOperationReceived(clientOp));
205
206 //Client receives new operation from server, it should be transformed against
207 //"a" and "b" before adding to local operations history because it's parented off "0".
208 //Because client is waiting for ack of "a", there is no operation to send to server
209 serverOp = createInsertOperation("c", "0", 1);
210 WhiteboardEllipseElement cElement = new WhiteboardEllipseElement(0,0,0,0);
211 serverOp.setElement(cElement);
212 pairResult = client.handleServerOperationReceived(serverOp);
213 checkOperation(pairResult.client, "c", "b", 3, cElement);
214 assertNull(pairResult.server);
215
216 //Client receives new operation from server, it should be transformed against
217 //results of previous transformations, returned operation should be parented off
218 //"c" existing in local history.
219 //Because client is waiting for ack of "a", there is no operation to send to server
220 serverOp = createInsertOperation("d", "c", 2);
221 WhiteboardEllipseElement dElement = new WhiteboardEllipseElement(0,0,0,0);
222 serverOp.setElement(dElement);
223 pairResult = client.handleServerOperationReceived(serverOp);
224 checkOperation(pairResult.client, "d", "c", 4, dElement);
225 assertNull(pairResult.server);
226
227 //Client receives confirmation about processing "a", it should send next operation
228 //to server which is "b", but it should be version parented of transformed "a"
229 serverOp = createInsertOperation("a", "d", 1);
230 pairResult = client.handleServerOperationReceived(serverOp);
231 checkOperation(pairResult.server, "b", "a", 2, bElement);
232 assertNull(pairResult.client);
233
234
235 //Client receives confirmation about processing "b", there aren't any operations
236 //waiting so it should return nothing.
237 serverOp = createInsertOperation("b", "a", 2);
238 pairResult = client.handleServerOperationReceived(serverOp);
239 assertNull(pairResult.client);
240 assertNull(pairResult.server);
241
242 //Client operations:
243 //ID pos
244 //0 0
245 //a 1
246 //b 2
247 //c 3
248 //d 4
249 //
250 //Server operations:
251 //ID pos
252 //0 0
253 //c 1
254 //d 2
255 //a 1
256 //b 2
257 //
258 //what gives 0abcd on both sides.
259 }
260
261 /*!
262 * /\
263 * / \
264 * \ /
265 * / /
266 * \/
267 */
268 @Test
269 public void testSynchronize_clientInterruption() {
270 WhiteboardClient client = new WhiteboardClient();
271 WhiteboardInsertOperation serverOp = createInsertOperation("0", "", 0);
272 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(serverOp);
273 assertEquals(serverOp, pairResult.client);
274 assertNull(pairResult.server);
275
276 //Client receives first local operation, because it's parented off "0" which exists
277 //in server history and client doesn't wait for any operation ack from server,
278 //so this operation could be send
279 WhiteboardInsertOperation clientOp = createInsertOperation("a", "0", 1);
280 WhiteboardEllipseElement aElement = new WhiteboardEllipseElement(0,0,0,0);
281 clientOp.setElement(aElement);
282 checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
283
284 //Client receives second local operation, client didn't receive ack about previous
285 //operation from the server so it can't be send.
286 clientOp = createInsertOperation("b", "a", 2);
287 WhiteboardEllipseElement bElement = new WhiteboardEllipseElement(0,0,0,0);
288 clientOp.setElement(bElement);
289 assertNull(client.handleLocalOperationReceived(clientOp));
290
291 //Client receives new operation from server, it should be transformed against
292 //"a" and "b" before adding to local operations history because it's parented off "0".
293 //Because client is waiting for ack of "a", there is no operation to send to server
294 serverOp = createInsertOperation("c", "0", 1);
295 WhiteboardEllipseElement cElement = new WhiteboardEllipseElement(0,0,0,0);
296 serverOp.setElement(cElement);
297 pairResult = client.handleServerOperationReceived(serverOp);
298 checkOperation(pairResult.client, "c", "b", 3, cElement);
299 assertNull(pairResult.server);
300
301 //Client receives new local operation, client is still waiting for ack so, it
302 //should return nothing
303 clientOp = createInsertOperation("e", "a", 4);
304 WhiteboardEllipseElement eElement = new WhiteboardEllipseElement(0,0,0,0);
305 clientOp.setElement(eElement);
306 assertNull(client.handleLocalOperationReceived(clientOp));
307
308 //Client receives new server operation, to add it to local history it should be transformed
309 //against result of previous transformations and operation "e", returned operation should
310 //be parented off "e", which was last local operation
311 serverOp = createInsertOperation("d", "c", 2);
312 WhiteboardEllipseElement dElement = new WhiteboardEllipseElement(0,0,0,0);
313 serverOp.setElement(dElement);
314 pairResult = client.handleServerOperationReceived(serverOp);
315 checkOperation(pairResult.client, "d", "e", 5, dElement);
316 assertNull(pairResult.server);
317
318 //Client receives confirmation about processing "a", it had to be transformed against
319 //"c" and "d" and it is now parented off "d", returned value should be next operation
320 //which have to be send to server("b" parented off server version of "a").
321 serverOp = createInsertOperation("a", "d", 1);
322 pairResult = client.handleServerOperationReceived(serverOp);
323 checkOperation(pairResult.server, "b", "a", 2, bElement);
324 assertNull(pairResult.client);
325
326 //Client receives confirmation about processing "b", it is the same operation as sent because
327 //it didn't have to be transformed, returned value should be next operation
328 //which have to be send to server("e" parented off server version of "b").
329 serverOp = createInsertOperation("b", "a", 2);
330 pairResult = client.handleServerOperationReceived(serverOp);
331 checkOperation(pairResult.server, "e", "b", 4, eElement);
332 assertNull(pairResult.client);
333
334 //Client receives confirmation about processing "b", it is the same operation as sent because
335 //it didn't have to be transformed, there aren't any operations to send so this function returns
336 //nothing
337 serverOp = createInsertOperation("e", "b", 4);
338 pairResult = client.handleServerOperationReceived(serverOp);
339 assertNull(pairResult.client);
340 assertNull(pairResult.server);
341
342 //Result:
343 //Client operations:
344 //ID pos
345 //0 0
346 //a 1
347 //b 2
348 //c 3
349 //e 4
350 //d 5
351 //
352 //Server operations:
353 //0 0
354 //c 1
355 //d 2
356 //a 1
357 //b 2
358 //e 4
359 //what gives 0abced on both sides
360 }
361
362 /*!
363 * /\
364 * / /
365 * \ \
366 * \/
367 */
368 @Test
369 public void testSynchronize_serverInterruption() {
370 WhiteboardClient client = new WhiteboardClient();
371 WhiteboardInsertOperation serverOp = createInsertOperation("0", "", 0);
372 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(serverOp);
373 assertEquals(serverOp, pairResult.client);
374 assertNull(pairResult.server);
375
376 //Client receives first local operation, because it's parented off "0" which exists
377 //in server history and client doesn't wait for any operation ack from server,
378 //so this operation could be send
379 WhiteboardInsertOperation clientOp = createInsertOperation("a", "0", 1);
380 WhiteboardEllipseElement aElement = new WhiteboardEllipseElement(0,0,0,0);
381 clientOp.setElement(aElement);
382 checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
383
384 //Client receives second local operation, client didn't receive ack about previous
385 //operation from the server so it can't be send.
386 clientOp = createInsertOperation("b", "a", 2);
387 WhiteboardEllipseElement bElement = new WhiteboardEllipseElement(0,0,0,0);
388 clientOp.setElement(bElement);
389 assertNull(client.handleLocalOperationReceived(clientOp));
390
391 //Client receives new operation from server, it should be transformed against
392 //"a" and "b" before adding to local operations history because it's parented off "0".
393 //Because client is waiting for ack of "a", there is no operation to send to server
394 serverOp = createInsertOperation("c", "0", 1);
395 WhiteboardEllipseElement cElement = new WhiteboardEllipseElement(0,0,0,0);
396 serverOp.setElement(cElement);
397 pairResult = client.handleServerOperationReceived(serverOp);
398 checkOperation(pairResult.client, "c", "b", 3, cElement);
399 assertNull(pairResult.server);
400
401 //Client receives confirmation about processing "a", it had to be transformed against
402 //"c" and it is now parented off "c", returned value should be next operation
403 //which have to be send to server("b" parented off server version of "a").
404 serverOp = createInsertOperation("a", "c", 1);
405 serverOp.setElement(aElement);
406 pairResult = client.handleServerOperationReceived(serverOp);
407 checkOperation(pairResult.server, "b", "a", 2, bElement);
408 assertNull(pairResult.client);
409
410 //Client receives new server operation, to add it to local history it should be transformed
411 //against result of previous transformation(but only with transformation of "b"), returned
412 //operation should be parented off "c", which was last local operation
413 serverOp = createInsertOperation("d", "a", 3);
414 WhiteboardEllipseElement dElement = new WhiteboardEllipseElement(0,0,0,0);
415 serverOp.setElement(dElement);
416 pairResult = client.handleServerOperationReceived(serverOp);
417 checkOperation(pairResult.client, "d", "c", 4, dElement);
418 assertNull(pairResult.server);
419
420 //Client receives confirmation about processing "b", it had to be transformed against
421 //"d" because both operations was parented off server version of "a".
422 //there aren't any operations to send so this function returns nothing.
423 serverOp = createInsertOperation("b", "d", 2);
424 serverOp.setElement(bElement);
425 pairResult = client.handleServerOperationReceived(serverOp);
426 assertNull(pairResult.client);
427 assertNull(pairResult.server);
428
429 //Client operations:
430 //ID pos
431 //0 0
432 //a 1
433 //b 2
434 //c 3
435 //d 4
436 //
437 //Server operations:
438 //ID pos
439 //0 0
440 //c 1
441 //a 1
442 //d 3
443 //b 2
444 //
445 //what gives 0abcd on both sides
446 }
447
448 /*!
449 * /\
450 * / \
451 * \ /
452 * \/
453 */
454 @Test
455 public void testSynchronize_nonInterruptedMixOperations() {
456 WhiteboardClient client = new WhiteboardClient();
457 WhiteboardInsertOperation serverOp = createInsertOperation("0", "", 0);
458 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(serverOp);
459 assertEquals(serverOp, pairResult.client);
460 assertNull(pairResult.server);
461
462 //Client receives first local operation, because it's parented off "0" which exists
463 //in server history and client doesn't wait for any operation ack from server,
464 //so this operation could be send
465 WhiteboardInsertOperation clientOp = createInsertOperation("a", "0", 1);
466 WhiteboardEllipseElement aElement = new WhiteboardEllipseElement(0,0,0,0);
467 clientOp.setElement(aElement);
468 checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement);
469
470 //Client receives second local operation, client didn't receive ack about previous
471 //operation from the server so it can't be send.
472 WhiteboardUpdateOperation clientUpdateOp = createUpdateOperation("b", "a", 0);
473 WhiteboardEllipseElement bElement = new WhiteboardEllipseElement(0,0,0,0);
474 clientUpdateOp.setElement(bElement);
475 assertNull(client.handleLocalOperationReceived(clientUpdateOp));
476
477 //Client receives new operation from server, it should be transformed against
478 //"a" and "b" before adding to local operations history because it's parented off "0".
479 //Because client is waiting for ack of "a", there is no operation to send to server
480 WhiteboardUpdateOperation serverUpdateOp = createUpdateOperation("c", "0", 0);
481 WhiteboardEllipseElement cElement = new WhiteboardEllipseElement(0,0,0,0);
482 serverUpdateOp.setElement(cElement);
483 pairResult = client.handleServerOperationReceived(serverUpdateOp);
484 checkOperation(pairResult.client, "c", "b", 0, cElement);
485 assertNull(pairResult.server);
486
487 //Client receives new operation from server, it should be transformed against
488 //results of previous transformations, returned operation should be parented off
489 //"c" existing in local history.
490 //Because client is waiting for ack of "a", there is no operation to send to server
491 serverOp = createInsertOperation("d", "c", 1);
492 WhiteboardEllipseElement dElement = new WhiteboardEllipseElement(0,0,0,0);
493 serverOp.setElement(dElement);
494 pairResult = client.handleServerOperationReceived(serverOp);
495 checkOperation(pairResult.client, "d", "c", 2, dElement);
496 assertNull(pairResult.server);
497
498 //Client receives confirmation about processing "a", it should send next operation
499 //to server which is "b", but it should be version parented of transformed "a"
500 serverOp = createInsertOperation("a", "d", 1);
501 pairResult = client.handleServerOperationReceived(serverOp);
502 checkOperation(pairResult.server, "b", "a", 0, cElement);
503 assertNull(pairResult.client);
504
505
506 //Client receives confirmation about processing "b", there aren't any operations
507 //waiting so it should return nothing.
508 serverUpdateOp = createUpdateOperation("b", "a", 0);
509 pairResult = client.handleServerOperationReceived(serverUpdateOp);
510 assertNull(pairResult.client);
511 assertNull(pairResult.server);
512
513 //Client operations:
514 //ID pos
515 //0 0
516 //a 1
517 //b 2
518 //c 3
519 //d 4
520 //
521 //Server operations:
522 //ID pos
523 //0 0
524 //c 1
525 //d 2
526 //a 1
527 //b 2
528 //
529 //what gives 0abcd on both sides.
530 }
531
532 /*!
533 * /\
534 * / \
535 * \ /
536 * \/
537 */
538 @Test
539 public void testSynchronize_nonInterruptedMixOperations2() {
540 WhiteboardClient client = new WhiteboardClient();
541 WhiteboardInsertOperation serverOp = createInsertOperation("0", "", 0);
542 WhiteboardClient.Result pairResult = client.handleServerOperationReceived(serverOp);
543 assertEquals(serverOp, pairResult.client);
544 assertNull(pairResult.server);
545
546 serverOp = createInsertOperation("1", "0", 1);
547 pairResult = client.handleServerOperationReceived(serverOp);
548 assertEquals(serverOp, pairResult.client);
549 assertNull(pairResult.server);
550 //Client receives first local operation, because it's parented off "0" which exists
551 //in server history and client doesn't wait for any operation ack from server,
552 //so this operation could be send
553 WhiteboardInsertOperation clientOp;
554 WhiteboardUpdateOperation clientUpdateOp;
555 WhiteboardDeleteOperation clientDeleteOp;
556 clientUpdateOp = createUpdateOperation("a", "1", 0);
557 WhiteboardEllipseElement aElement = new WhiteboardEllipseElement(0,0,0,0);
558 clientUpdateOp.setElement(aElement);
559 checkOperation(client.handleLocalOperationReceived(clientUpdateOp), "a", "1", 0, aElement);
560
561 //Client receives second local operation, client didn't receive ack about previous
562 //operation from the server so it can't be send.
563 clientDeleteOp = createDeleteOperation("b", "a", 1);
564 assertNull(client.handleLocalOperationReceived(clientDeleteOp));
565
566 //Client receives new operation from server, it should be transformed against
567 //"a" and "b" before adding to local operations history because it's parented off "0".
568 //Because client is waiting for ack of "a", there is no operation to send to server
569 serverOp = createInsertOperation("c", "1", 2);
570 WhiteboardEllipseElement cElement = new WhiteboardEllipseElement(0,0,0,0);
571 serverOp.setElement(cElement);
572 pairResult = client.handleServerOperationReceived(serverOp);
573 checkOperation(pairResult.client, "c", "b", 1, cElement);
574 assertNull(pairResult.server);
575
576 //Client receives new operation from server, it should be transformed against
577 //results of previous transformations, returned operation should be parented off
578 //"c" existing in local history.
579 //Because client is waiting for ack of "a", there is no operation to send to server
580 WhiteboardUpdateOperation serverUpdateOp = createUpdateOperation("d", "c", 0);
581 WhiteboardEllipseElement dElement = new WhiteboardEllipseElement(0,0,0,0);
582 serverUpdateOp.setElement(dElement);
583 pairResult = client.handleServerOperationReceived(serverUpdateOp);
584 checkOperation(pairResult.client, "d", "c", 0, dElement);
585 assertNull(pairResult.server);
586
587 //Client receives confirmation about processing "a", it should send next operation
588 //to server which is "b", but it should be version parented of transformed "a"
589 serverUpdateOp = createUpdateOperation("a", "d", 0);
590 pairResult = client.handleServerOperationReceived(serverUpdateOp);
591 checkOperation(pairResult.server, "b", "a", 1);
592 assertNull(pairResult.client);
593
594
595 //Client receives confirmation about processing "b", there aren't any operations
596 //waiting so it should return nothing.
597 WhiteboardDeleteOperation serverDeleteOp = createDeleteOperation("b", "a", 0);
598 pairResult = client.handleServerOperationReceived(serverDeleteOp);
599 assertNull(pairResult.client);
600 assertNull(pairResult.server);
601
602 //Client operations:
603 //ID pos
604 //0 0
605 //a 1
606 //b 2
607 //c 3
608 //d 4
609 //
610 //Server operations:
611 //ID pos
612 //0 0
613 //c 1
614 //d 2
615 //a 1
616 //b 2
617 //
618 //what gives 0abcd on both sides.
619 }
620
621 private WhiteboardInsertOperation createInsertOperation(String id, String parent, int pos) {
622 WhiteboardInsertOperation operation = new WhiteboardInsertOperation();
623 operation.setParentID(parent);
624 operation.setID(id);
625 operation.setPos(pos);
626 return operation;
627 }
628
629 private WhiteboardUpdateOperation createUpdateOperation(String id, String parent, int pos) {
630 WhiteboardUpdateOperation operation = new WhiteboardUpdateOperation();
631 operation.setParentID(parent);
632 operation.setID(id);
633 operation.setPos(pos);
634 return operation;
635 }
636
637 private WhiteboardDeleteOperation createDeleteOperation(String id, String parent, int pos) {
638 WhiteboardDeleteOperation operation = new WhiteboardDeleteOperation();
639 operation.setParentID(parent);
640 operation.setID(id);
641 operation.setPos(pos);
642 return operation;
643 }
644
645 private void checkOperation(WhiteboardOperation operation, String id, String parent) {
646 checkOperation(operation,id,parent,-1);
647 }
648
649 private void checkOperation(WhiteboardOperation operation, String id, String parent, int pos) {
650 checkOperation(operation,id,parent,pos,null);
651 }
652
653 private void checkOperation(WhiteboardOperation operation, String id, String parent, int pos, WhiteboardElement element) {
654 assertEquals(id, operation.getID());
655 assertEquals(parent, operation.getParentID());
656 if (pos != -1) {
657 assertEquals(pos, operation.getPos());
658 }
659
660 if (element != null) {
661 if (operation instanceof WhiteboardInsertOperation) {
662 WhiteboardInsertOperation insertOp = (WhiteboardInsertOperation) operation;
663 assertEquals(element,insertOp.getElement());
664 }
665 else if (operation instanceof WhiteboardUpdateOperation) {
666 WhiteboardUpdateOperation updateOp = (WhiteboardUpdateOperation) operation;
667 assertEquals(element,updateOp.getElement());
668 }
669 }
670 }
671
672}
diff --git a/test/com/isode/stroke/whiteboard/WhiteboardServerTest.java b/test/com/isode/stroke/whiteboard/WhiteboardServerTest.java
new file mode 100644
index 0000000..c90d8c3
--- /dev/null
+++ b/test/com/isode/stroke/whiteboard/WhiteboardServerTest.java
@@ -0,0 +1,146 @@
1/* Copyright (c) 2016, Isode Limited, London, England.
2 * All rights reserved.
3 *
4 * Acquisition and use of this software and related materials for any
5 * purpose requires a written license agreement from Isode Limited,
6 * or a written license from an organisation licensed by Isode Limited
7 * to grant such a license.
8 *
9 */
10package com.isode.stroke.whiteboard;
11
12import static org.junit.Assert.assertEquals;
13import static org.junit.Assert.assertNotNull;
14import static org.junit.Assert.assertTrue;
15
16import org.junit.Test;
17
18import com.isode.stroke.elements.WhiteboardDeleteOperation;
19import com.isode.stroke.elements.WhiteboardEllipseElement;
20import com.isode.stroke.elements.WhiteboardInsertOperation;
21import com.isode.stroke.elements.WhiteboardOperation;
22import com.isode.stroke.elements.WhiteboardUpdateOperation;
23
24/**
25 * Tests for {@link WhiteboardServer}
26 */
27public class WhiteboardServerTest {
28
29 @Test
30 public void testSimpleOp() {
31 WhiteboardServer server = new WhiteboardServer();
32 WhiteboardInsertOperation firstOp = new WhiteboardInsertOperation();
33 firstOp.setID("0");
34 server.handleLocalOperationReceived(firstOp);
35 WhiteboardInsertOperation serverOp = new WhiteboardInsertOperation();
36 serverOp.setID("b");
37 serverOp.setParentID("0");
38 serverOp.setPos(1);
39 server.handleLocalOperationReceived(serverOp);
40 WhiteboardInsertOperation clientOp = new WhiteboardInsertOperation();
41 WhiteboardEllipseElement clientElement = new WhiteboardEllipseElement(0,0,0,0);
42 clientOp.setID("a");
43 clientOp.setParentID("0");
44 clientOp.setPos(1);
45 clientOp.setElement(clientElement);
46 WhiteboardOperation resultOp = server.handleClientOperationReceived(clientOp);
47 assertNotNull(resultOp);
48 assertTrue("resultOp is not a WhiteboardInsertOperation",
49 resultOp instanceof WhiteboardInsertOperation);
50 WhiteboardInsertOperation op = (WhiteboardInsertOperation) resultOp;
51 assertEquals("b",op.getParentID());
52 assertEquals("a",op.getID());
53 assertEquals(1,op.getPos());
54 assertEquals(clientElement,op.getElement());
55 }
56
57 @Test
58 public void testSimpleOp1() {
59 WhiteboardServer server = new WhiteboardServer();
60 WhiteboardInsertOperation firstOp = new WhiteboardInsertOperation();
61 firstOp.setID("0");
62 server.handleLocalOperationReceived(firstOp);
63 WhiteboardDeleteOperation serverOp = new WhiteboardDeleteOperation();
64 serverOp.setID("b");
65 serverOp.setParentID("0");
66 serverOp.setPos(1);
67 server.handleLocalOperationReceived(serverOp);
68 WhiteboardUpdateOperation clientOp = new WhiteboardUpdateOperation();
69 WhiteboardEllipseElement clientElement = new WhiteboardEllipseElement(0,0,0,0);
70 clientOp.setID("a");
71 clientOp.setParentID("0");
72 clientOp.setPos(1);
73 clientOp.setElement(clientElement);
74 WhiteboardOperation resultOp = server.handleClientOperationReceived(clientOp);
75 assertNotNull(resultOp);
76 assertTrue("resultOp is not a WhiteboardDeleteOperation",
77 resultOp instanceof WhiteboardDeleteOperation);
78 WhiteboardDeleteOperation op = (WhiteboardDeleteOperation) resultOp;
79 assertEquals("b", op.getParentID());
80 assertEquals("a", op.getID());
81 assertEquals(-1, op.getPos());
82 }
83
84 @Test
85 public void testSimpleOp2() {
86 WhiteboardServer server = new WhiteboardServer();
87 WhiteboardInsertOperation firstOp = new WhiteboardInsertOperation();
88 firstOp.setID("0");
89 server.handleLocalOperationReceived(firstOp);
90 WhiteboardUpdateOperation serverOp = new WhiteboardUpdateOperation();
91 serverOp.setID("b");
92 serverOp.setParentID("0");
93 serverOp.setPos(1);
94 server.handleLocalOperationReceived(serverOp);
95 WhiteboardDeleteOperation clientOp = new WhiteboardDeleteOperation();
96 clientOp.setID("a");
97 clientOp.setParentID("0");
98 clientOp.setPos(1);
99 WhiteboardOperation resultOp = server.handleClientOperationReceived(clientOp);
100 assertNotNull(resultOp);
101 assertTrue("resultOp is not a WhiteboardDeleteOperation",
102 resultOp instanceof WhiteboardDeleteOperation);
103 WhiteboardDeleteOperation op = (WhiteboardDeleteOperation) resultOp;
104 assertEquals("b", op.getParentID());
105 assertEquals("a", op.getID());
106 assertEquals(1, op.getPos());
107 }
108
109 @Test
110 public void testFewSimpleOps() {
111 WhiteboardServer server = new WhiteboardServer();
112 WhiteboardInsertOperation firstOp = new WhiteboardInsertOperation();
113 firstOp.setID("0");
114 server.handleLocalOperationReceived(firstOp);
115 WhiteboardInsertOperation serverOp = new WhiteboardInsertOperation();
116 serverOp.setID("a");
117 serverOp.setParentID("0");
118 serverOp.setPos(1);
119 server.handleLocalOperationReceived(serverOp);
120 serverOp = new WhiteboardInsertOperation();
121 serverOp.setID("b");
122 serverOp.setParentID("a");
123 serverOp.setPos(2);
124 server.handleLocalOperationReceived(serverOp);
125 serverOp = new WhiteboardInsertOperation();
126 serverOp.setID("c");
127 serverOp.setParentID("b");
128 serverOp.setPos(3);
129 server.handleLocalOperationReceived(serverOp);
130 WhiteboardInsertOperation clientOp = new WhiteboardInsertOperation();
131 WhiteboardEllipseElement clientElement = new WhiteboardEllipseElement(0,0,0,0);
132 clientOp.setID("d");
133 clientOp.setParentID("0");
134 clientOp.setPos(1);
135 clientOp.setElement(clientElement);
136 WhiteboardOperation resultOp = server.handleClientOperationReceived(clientOp);
137 assertNotNull(resultOp);
138 assertTrue("resultOp is not a WhiteboardInsertOperation",resultOp instanceof WhiteboardInsertOperation);
139 WhiteboardInsertOperation op = (WhiteboardInsertOperation) resultOp;
140 assertEquals("c", op.getParentID());
141 assertEquals("d", op.getID());
142 assertEquals(1, op.getPos());
143 assertEquals(clientElement, op.getElement());
144 }
145
146}