diff options
| author | Alex Clayton <alex.clayton@isode.com> | 2016-02-26 16:51:13 (GMT) |
|---|---|---|
| committer | Alex Clayton <alex.clayton@isode.com> | 2016-03-09 16:44:17 (GMT) |
| commit | 8f112a856705b800d1a8797bec5d9396a9c00b34 (patch) | |
| tree | 6468ad42172e1d1f68138a26679dbaf85b58e6b9 | |
| parent | 8fe752626726ca8d058ce437127a37d5d738a5eb (diff) | |
| download | stroke-8f112a856705b800d1a8797bec5d9396a9c00b34.zip stroke-8f112a856705b800d1a8797bec5d9396a9c00b34.tar.bz2 | |
Add Whiteboard Functionality
Add the Whiteboard classes to stroke.
Test-information:
Unit tests all pass.
Change-Id: Id409c09d0fc1f82864e5d706c413b9d984a7db82
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 @@ | |||
| 1 | Porting Progress from Swiften to Stroke. | 1 | Porting Progress from Swiften to Stroke. |
| 2 | This 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. | 2 | This 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. |
| 3 | To Be Ported: history, linklocal, WhiteBoard. | 3 | To Be Ported: history, linklocal. |
| 4 | 4 | ||
| 5 | ----- | 5 | ----- |
| 6 | Adhoc: | 6 | Adhoc: |
| @@ -73,8 +73,6 @@ Elements: | |||
| 73 | 73 | ||
| 74 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: | 74 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: |
| 75 | 75 | ||
| 76 | WhiteBoard Functionalities -- Not Yet Ported! | ||
| 77 | |||
| 78 | Individual Comments: | 76 | Individual Comments: |
| 79 | ToplevelElement -- Not Required in Stroke.Element does the work! | 77 | ToplevelElement -- Not Required in Stroke.Element does the work! |
| 80 | 78 | ||
| @@ -157,8 +155,7 @@ Parser: | |||
| 157 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: | 155 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: |
| 158 | 156 | ||
| 159 | ExpatParser -- Not yet ported. Requires expact library. | 157 | ExpatParser -- Not yet ported. Requires expact library. |
| 160 | LibXMLParser -- Not yet ported. Requires libxml library. | 158 | LibXMLParser -- Not yet ported. Requires libxml library. |
| 161 | WhiteboardParser -- Not Yet Ported! Needs Whiteboard classes importing. | ||
| 162 | 159 | ||
| 163 | ----- | 160 | ----- |
| 164 | Presence: | 161 | Presence: |
| @@ -191,9 +188,7 @@ Windows Authenticator not needed! | |||
| 191 | ----- | 188 | ----- |
| 192 | Serializer: | 189 | Serializer: |
| 193 | 190 | ||
| 194 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39 except for: | 191 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39. |
| 195 | |||
| 196 | WhiteboardSerializer -- Not Yet Ported! Needs whiteboard classes importing. | ||
| 197 | 192 | ||
| 198 | ----- | 193 | ----- |
| 199 | Session: | 194 | Session: |
| @@ -227,3 +222,8 @@ supported in java. | |||
| 227 | VCards: | 222 | VCards: |
| 228 | 223 | ||
| 229 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39. | 224 | All files ported to 6ca201d0b48f4273e24dd7bff17c4a46eeaddf39. |
| 225 | |||
| 226 | ----- | ||
| 227 | Whiteboard: | ||
| 228 | |||
| 229 | All 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 | */ |
| 9 | package com.isode.stroke.elements; | 9 | package 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.Collection; | ||
| 14 | import java.util.List; | ||
| 15 | |||
| 16 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.Collection; | ||
| 14 | import java.util.List; | ||
| 15 | |||
| 16 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.elements; | ||
| 11 | |||
| 12 | public 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 | */ | ||
| 10 | package com.isode.stroke.parser.payloadparsers; | ||
| 11 | |||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.List; | ||
| 14 | |||
| 15 | import com.isode.stroke.elements.WhiteboardColor; | ||
| 16 | import com.isode.stroke.elements.WhiteboardDeleteOperation; | ||
| 17 | import com.isode.stroke.elements.WhiteboardElement; | ||
| 18 | import com.isode.stroke.elements.WhiteboardEllipseElement; | ||
| 19 | import com.isode.stroke.elements.WhiteboardFreehandPathElement; | ||
| 20 | import com.isode.stroke.elements.WhiteboardInsertOperation; | ||
| 21 | import com.isode.stroke.elements.WhiteboardLineElement; | ||
| 22 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 23 | import com.isode.stroke.elements.WhiteboardPayload; | ||
| 24 | import com.isode.stroke.elements.WhiteboardPolygonElement; | ||
| 25 | import com.isode.stroke.elements.WhiteboardRectElement; | ||
| 26 | import com.isode.stroke.elements.WhiteboardUpdateOperation; | ||
| 27 | import com.isode.stroke.elements.WhiteboardPayload.Type; | ||
| 28 | import com.isode.stroke.elements.WhiteboardTextElement; | ||
| 29 | import com.isode.stroke.parser.AttributeMap; | ||
| 30 | import com.isode.stroke.parser.GenericPayloadParser; | ||
| 31 | |||
| 32 | public 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 | */ | ||
| 10 | package com.isode.stroke.serializer.payloadserializers; | ||
| 11 | |||
| 12 | import java.util.Iterator; | ||
| 13 | import java.util.logging.Logger; | ||
| 14 | |||
| 15 | import com.isode.stroke.elements.WhiteboardDeleteOperation; | ||
| 16 | import com.isode.stroke.elements.WhiteboardElementVisitor; | ||
| 17 | import com.isode.stroke.elements.WhiteboardEllipseElement; | ||
| 18 | import com.isode.stroke.elements.WhiteboardFreehandPathElement; | ||
| 19 | import com.isode.stroke.elements.WhiteboardInsertOperation; | ||
| 20 | import com.isode.stroke.elements.WhiteboardLineElement; | ||
| 21 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 22 | import com.isode.stroke.elements.WhiteboardPayload; | ||
| 23 | import com.isode.stroke.elements.WhiteboardPayload.Type; | ||
| 24 | import com.isode.stroke.elements.WhiteboardPolygonElement; | ||
| 25 | import com.isode.stroke.elements.WhiteboardRectElement; | ||
| 26 | import com.isode.stroke.elements.WhiteboardTextElement; | ||
| 27 | import com.isode.stroke.elements.WhiteboardUpdateOperation; | ||
| 28 | import com.isode.stroke.serializer.GenericPayloadSerializer; | ||
| 29 | import com.isode.stroke.serializer.xml.XMLElement; | ||
| 30 | import com.isode.stroke.serializer.xml.XMLTextNode; | ||
| 31 | |||
| 32 | public 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 @@ | |||
| 1 | package com.isode.stroke.whiteboard; | ||
| 2 | |||
| 3 | import com.isode.stroke.elements.IQ; | ||
| 4 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 5 | import com.isode.stroke.elements.WhiteboardPayload; | ||
| 6 | import com.isode.stroke.jid.JID; | ||
| 7 | import com.isode.stroke.queries.GenericRequest; | ||
| 8 | import com.isode.stroke.queries.IQRouter; | ||
| 9 | |||
| 10 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import com.isode.stroke.elements.ErrorPayload; | ||
| 13 | import com.isode.stroke.elements.IQ; | ||
| 14 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 15 | import com.isode.stroke.elements.WhiteboardPayload; | ||
| 16 | import com.isode.stroke.jid.JID; | ||
| 17 | import com.isode.stroke.queries.GenericRequest; | ||
| 18 | import com.isode.stroke.queries.IQRouter; | ||
| 19 | import com.isode.stroke.signals.Slot2; | ||
| 20 | |||
| 21 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.List; | ||
| 14 | |||
| 15 | import com.isode.stroke.elements.WhiteboardDeleteOperation; | ||
| 16 | import com.isode.stroke.elements.WhiteboardInsertOperation; | ||
| 17 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 18 | import com.isode.stroke.elements.WhiteboardUpdateOperation; | ||
| 19 | import com.isode.stroke.whiteboard.WhiteboardTransformer.Pair; | ||
| 20 | |||
| 21 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import com.isode.stroke.elements.WhiteboardPayload; | ||
| 13 | import com.isode.stroke.jid.JID; | ||
| 14 | import com.isode.stroke.queries.IQRouter; | ||
| 15 | import com.isode.stroke.queries.SetResponder; | ||
| 16 | import com.isode.stroke.elements.ErrorPayload; | ||
| 17 | |||
| 18 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.List; | ||
| 14 | |||
| 15 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 16 | |||
| 17 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import com.isode.stroke.base.IDGenerator; | ||
| 13 | import com.isode.stroke.elements.IQ; | ||
| 14 | import com.isode.stroke.elements.WhiteboardElement; | ||
| 15 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 16 | import com.isode.stroke.elements.WhiteboardPayload; | ||
| 17 | import com.isode.stroke.elements.WhiteboardPayload.Type; | ||
| 18 | import com.isode.stroke.jid.JID; | ||
| 19 | import com.isode.stroke.queries.GenericRequest; | ||
| 20 | import com.isode.stroke.queries.IQRouter; | ||
| 21 | import com.isode.stroke.signals.Signal1; | ||
| 22 | |||
| 23 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import java.util.ArrayList; | ||
| 13 | import java.util.HashMap; | ||
| 14 | import java.util.List; | ||
| 15 | import java.util.Map; | ||
| 16 | |||
| 17 | import com.isode.stroke.client.StanzaChannel; | ||
| 18 | import com.isode.stroke.disco.EntityCapsProvider; | ||
| 19 | import com.isode.stroke.elements.DiscoInfo; | ||
| 20 | import com.isode.stroke.elements.Presence; | ||
| 21 | import com.isode.stroke.jid.JID; | ||
| 22 | import com.isode.stroke.presence.PresenceOracle; | ||
| 23 | import com.isode.stroke.queries.IQRouter; | ||
| 24 | import com.isode.stroke.signals.Signal1; | ||
| 25 | import com.isode.stroke.signals.Slot1; | ||
| 26 | |||
| 27 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import com.isode.stroke.elements.WhiteboardDeleteOperation; | ||
| 13 | import com.isode.stroke.elements.WhiteboardInsertOperation; | ||
| 14 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 15 | import com.isode.stroke.elements.WhiteboardUpdateOperation; | ||
| 16 | |||
| 17 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import static org.junit.Assert.assertEquals; | ||
| 13 | import static org.junit.Assert.assertNull; | ||
| 14 | |||
| 15 | import org.junit.Test; | ||
| 16 | |||
| 17 | import com.isode.stroke.elements.WhiteboardDeleteOperation; | ||
| 18 | import com.isode.stroke.elements.WhiteboardElement; | ||
| 19 | import com.isode.stroke.elements.WhiteboardEllipseElement; | ||
| 20 | import com.isode.stroke.elements.WhiteboardInsertOperation; | ||
| 21 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 22 | import com.isode.stroke.elements.WhiteboardUpdateOperation; | ||
| 23 | |||
| 24 | |||
| 25 | /** | ||
| 26 | * Tests for {@link WhiteboardClient} | ||
| 27 | */ | ||
| 28 | public 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 | */ | ||
| 10 | package com.isode.stroke.whiteboard; | ||
| 11 | |||
| 12 | import static org.junit.Assert.assertEquals; | ||
| 13 | import static org.junit.Assert.assertNotNull; | ||
| 14 | import static org.junit.Assert.assertTrue; | ||
| 15 | |||
| 16 | import org.junit.Test; | ||
| 17 | |||
| 18 | import com.isode.stroke.elements.WhiteboardDeleteOperation; | ||
| 19 | import com.isode.stroke.elements.WhiteboardEllipseElement; | ||
| 20 | import com.isode.stroke.elements.WhiteboardInsertOperation; | ||
| 21 | import com.isode.stroke.elements.WhiteboardOperation; | ||
| 22 | import com.isode.stroke.elements.WhiteboardUpdateOperation; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Tests for {@link WhiteboardServer} | ||
| 26 | */ | ||
| 27 | public 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 | } | ||
Swift