diff options
author | Alex Clayton <alex.clayton@isode.com> | 2016-02-26 16:51:13 (GMT) |
---|---|---|
committer | Alex Clayton <alex.clayton@isode.com> | 2016-03-09 16:44:17 (GMT) |
commit | 8f112a856705b800d1a8797bec5d9396a9c00b34 (patch) | |
tree | 6468ad42172e1d1f68138a26679dbaf85b58e6b9 /src/com | |
parent | 8fe752626726ca8d058ce437127a37d5d738a5eb (diff) | |
download | stroke-8f112a856705b800d1a8797bec5d9396a9c00b34.zip stroke-8f112a856705b800d1a8797bec5d9396a9c00b34.tar.bz2 |
Add Whiteboard Functionality
Add the Whiteboard classes to stroke.
Test-information:
Unit tests all pass.
Change-Id: Id409c09d0fc1f82864e5d706c413b9d984a7db82
Diffstat (limited to 'src/com')
26 files changed, 2293 insertions, 2 deletions
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 @@ * All rights reserved. */ /* - * Copyright (c) 2010-2015, Isode Limited, London, England. + * Copyright (c) 2010-2016, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.elements; @@ -33,6 +33,7 @@ public class DiscoInfo extends Payload { public static final String JingleTransportsS5BFeature = "urn:xmpp:jingle:transports:s5b:1"; public static final String Bytestream = "http://jabber.org/protocol/bytestreams"; public static final String MessageDeliveryReceiptsFeature = "urn:xmpp:receipts"; + public static final String WhiteboardFeature = "http://swift.im/whiteboard"; public static class Identity implements Comparable<Identity> { 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardColor { + + private final int red_, green_, blue_; + private int alpha_; + + public WhiteboardColor() { + this(0,0,0,255); + } + + public WhiteboardColor(int red,int green,int blue) { + this(red,green,blue,255); + } + + public WhiteboardColor(int red,int green,int blue,int alpha) { + red_ = red; + green_ = green; + blue_ = blue; + alpha_ = alpha; + } + + public WhiteboardColor(String hex) { + alpha_ = 255; + int value = Integer.parseInt(hex.substring(1)); + red_ = (value >> 16) & 0xFF; + green_ = (value >> 8) & 0xFF; + blue_ = value & 0xFF; + } + + public String toHex() { + int value = (red_ << 16) + (green_ << 8) + blue_; + StringBuilder builder = new StringBuilder(Integer.toHexString(value)); + while (builder.length() < 6) { + builder.insert(0, '0'); + } + builder.insert(0, '#'); + return builder.toString(); + } + + public int getRed() { + return red_; + } + + public int getGreen() { + return green_; + } + + public int getBlue() { + return blue_; + } + + public int getAlpha() { + return alpha_; + } + + public void setAlpha(int alpha) { + alpha_ = alpha; + } +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardDeleteOperation extends WhiteboardOperation { + + private String elementID_ = ""; + + public WhiteboardDeleteOperation() { + // Empty Constructor + } + + public WhiteboardDeleteOperation(WhiteboardDeleteOperation other) { + super(other); + this.elementID_ = other.elementID_; + } + + public String getElementID() { + return elementID_; + } + + public void setElementID(String id) { + elementID_ = id; + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public abstract class WhiteboardElement { + + private String id_ = ""; + + public WhiteboardElement() { + // Empty Constructor + } + + public abstract void accept(WhiteboardElementVisitor visitor); + + public final String getID() { + return id_; + } + + public final void setID(String id) { + id_ = id; + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public interface WhiteboardElementVisitor { + + public void visit(WhiteboardLineElement element); + public void visit(WhiteboardFreehandPathElement element); + public void visit(WhiteboardRectElement element); + public void visit(WhiteboardPolygonElement element); + public void visit(WhiteboardTextElement element); + public void visit(WhiteboardEllipseElement element); + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardEllipseElement extends WhiteboardElement { + + private final int cx_, cy_, rx_, ry_; + private WhiteboardColor penColor_; + private WhiteboardColor brushColor_; + private int penWidth_; + + public WhiteboardEllipseElement(int cx, int cy, int rx, int ry) { + cx_ = cx; + cy_ = cy; + rx_ = rx; + ry_ = ry; + } + + public int getCX() { + return cx_; + } + + public int getCY() { + return cy_; + } + + public int getRX() { + return rx_; + } + + public int getRY() { + return ry_; + } + + public WhiteboardColor getPenColor() { + return penColor_; + } + + public void setPenColor(WhiteboardColor color) { + penColor_ = color; + } + + public WhiteboardColor getBrushColor() { + return brushColor_; + } + + public void setBrushColor(WhiteboardColor color) { + brushColor_ = color; + } + + public int getPenWidth() { + return penWidth_; + } + + public void setPenWidth(int penWidth) { + penWidth_ = penWidth; + } + + @Override + public void accept(WhiteboardElementVisitor visitor) { + visitor.visit(this); + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class WhiteboardFreehandPathElement extends WhiteboardElement { + + private List<Point> points_ = new ArrayList<Point>(); + private WhiteboardColor color_ = new WhiteboardColor(); + private int penWidth_ = 0; + + public WhiteboardFreehandPathElement() { + // Empty Constructor + } + + public void setPoints(Collection<? extends Point> points) { + points_.clear(); + points_.addAll(points_); + } + + public List<Point> getPoints() { + return new ArrayList<Point>(points_); + } + + public WhiteboardColor getColor() { + return color_; + } + + public void setColor(WhiteboardColor color) { + color_ = color; + } + + public int getPenWidth() { + return penWidth_; + } + + public void setPenWidth(int penWidth) { + penWidth_ = penWidth; + } + + @Override + public void accept(WhiteboardElementVisitor visitor) { + visitor.visit(this); + } + + public static class Point { + public final int x; + public final int y; + public Point(int x,int y) { + this.x = x; + this.y = y; + } + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardInsertOperation extends WhiteboardOperation { + + private WhiteboardElement element_; + + public WhiteboardInsertOperation() { + // Empty Constructor + } + + public WhiteboardInsertOperation(WhiteboardInsertOperation other) { + super(other); + this.element_ = other.element_; + } + + public WhiteboardElement getElement() { + return element_; + } + + public void setElement(WhiteboardElement element) { + element_ = element; + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardLineElement extends WhiteboardElement { + + private final int x1_, x2_, y1_, y2_; + + private WhiteboardColor color_ = new WhiteboardColor(); + + private int penWidth_ = 1; + + public WhiteboardLineElement(int x1,int y1,int x2,int y2) { + x1_ = x1; + y1_ = y1; + x2_ = x2; + y2_ = y2; + } + + public int x1() { + return x1_; + } + + public int x2() { + return x2_; + } + + public int y1() { + return y1_; + } + + public int y2() { + return y2_; + } + + public WhiteboardColor getColor() { + return color_; + } + + public void setColor(WhiteboardColor color) { + color_ = color; + } + + public int getPenWidth() { + return penWidth_; + } + + public void setPenWidth(int penWidth) { + penWidth_ = penWidth; + } + + @Override + public void accept(WhiteboardElementVisitor visitor) { + visitor.visit(this); + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardOperation { + + private String id_ = ""; + private String parentID_ = ""; + private int pos_ = 0; + + public WhiteboardOperation() { + // Empty Constructor + } + + public WhiteboardOperation(WhiteboardOperation other) { + this.id_ = other.id_; + this.parentID_ = other.parentID_; + this.pos_ = other.pos_; + } + + public String getID() { + return id_; + } + + public void setID(String id) { + id_ = id; + } + + public String getParentID() { + return parentID_; + } + + public void setParentID(String parentID) { + parentID_ = parentID; + } + + public int getPos() { + return pos_; + } + + public void setPos(int pos) { + pos_ = pos; + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardPayload extends Payload { + + private String data_; + private Type type_; + private WhiteboardElement element_; + private WhiteboardOperation operation_; + + public enum Type { + UnknownType, + Data, + SessionRequest, + SessionAccept, + SessionTerminate; + } + + public WhiteboardPayload() { + this(Type.Data); + } + + public WhiteboardPayload(Type type) { + type_ = type; + } + + public void setData(String data) { + data_ = data; + } + + public String getData() { + return data_; + } + + public Type getType() { + return type_; + } + + public void setType(Type type) { + type_ = type; + } + + public WhiteboardElement getElement() { + return element_; + } + + public void setElement(WhiteboardElement element) { + element_ = element; + } + + public WhiteboardOperation getOperation() { + return operation_; + } + + public void setOperation(WhiteboardOperation operation) { + operation_ = operation; + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class WhiteboardPolygonElement extends WhiteboardElement { + + private final List<Point> points_ = new ArrayList<Point>(); + private WhiteboardColor penColor_; + private WhiteboardColor brushColor_; + private int penWidth_ = 0; + + public List<Point> getPoints() { + return new ArrayList<Point>(points_); + } + + public void setPoints(Collection<? extends Point> points) { + points_.clear(); + points_.addAll(points); + } + + public WhiteboardColor getPenColor() { + return penColor_; + } + + public void setPenColor(WhiteboardColor color) { + penColor_ = color; + } + + public WhiteboardColor getBrushColor() { + return brushColor_; + } + + public void setBrushColor(WhiteboardColor color) { + brushColor_ = color; + } + + public int getPenWidth() { + return penWidth_; + } + + public void setPenWidth(int penWidth) { + penWidth_ = penWidth; + } + + @Override + public void accept(WhiteboardElementVisitor visitor) { + visitor.visit(this); + } + + public static class Point { + public final int x; + public final int y; + public Point(int x,int y) { + this.x = x; + this.y = y; + } + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardRectElement extends WhiteboardElement { + + private int x_, y_, width_, height_; + private WhiteboardColor penColor_; + private WhiteboardColor brushColor_; + private int penWidth_ = 1; + + public WhiteboardRectElement(int x, int y, int width, int height) { + x_ = x; + y_ = y; + width_ = width; + height_ = height; + } + + public int getX() { + return x_; + } + + public int getY() { + return y_; + } + + public int getWidth() { + return width_; + } + + public int getHeight() { + return height_; + } + + public WhiteboardColor getPenColor() { + return penColor_; + } + + public void setPenColor(WhiteboardColor color) { + penColor_ = color; + } + + public WhiteboardColor getBrushColor() { + return brushColor_; + } + + public void setBrushColor(WhiteboardColor color) { + brushColor_ = color; + } + + public int getPenWidth() { + return penWidth_; + } + + public void setPenWidth(int penWidth) { + penWidth_ = penWidth; + } + + @Override + public void accept(WhiteboardElementVisitor visitor) { + visitor.visit(this); + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardTextElement extends WhiteboardElement { + + private final int x_, y_; + private int size_; + private String text_; + private WhiteboardColor color_; + + public WhiteboardTextElement(int x, int y) { + x_ = x; + y_ = y; + } + + public void setText(String text) { + text_ = text; + } + + public String getText() { + return text_; + } + + public int getX() { + return x_; + } + + public int getY() { + return y_; + } + + public WhiteboardColor getColor() { + return color_; + } + + public void setColor(WhiteboardColor color) { + color_ = color; + } + + public int getSize() { + return size_; + } + + public void setSize(int size) { + size_ = size; + } + + @Override + public void accept(WhiteboardElementVisitor visitor) { + visitor.visit(this); + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.elements; + +public class WhiteboardUpdateOperation extends WhiteboardOperation { + + private WhiteboardElement element_; + private int newPos_ = 0; + + public WhiteboardUpdateOperation() { + // Empty Constructor + } + + public WhiteboardUpdateOperation(WhiteboardUpdateOperation other) { + super(other); + this.element_ = other.element_; + this.newPos_ = other.newPos_; + } + + public WhiteboardElement getElement() { + return element_; + } + + public void setElement(WhiteboardElement element) { + element_ = element; + } + + public int getNewPos() { + return newPos_; + } + + public void setNewPos(int newPos) { + newPos_ = newPos; + } + +} 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 addFactory(new GenericPayloadParserFactory<JingleFileTransferFileInfoParser>("file", JingleFileTransferFileInfoParser.class)); addFactory(new GenericPayloadParserFactory<JingleFileTransferHashParser>("checksum", JingleFileTransferHashParser.class)); addFactory(new GenericPayloadParserFactory<S5BProxyRequestParser>("query", "http://jabber.org/protocol/bytestreams",S5BProxyRequestParser.class)); - // addFactory(new GenericPayloadParserFactory<WhiteboardParser>("wb","http://swift.im/whiteboard",WhiteboardParser.class)); + addFactory(new GenericPayloadParserFactory<WhiteboardParser>("wb","http://swift.im/whiteboard",WhiteboardParser.class)); addFactory(new GenericPayloadParserFactory<UserLocationParser>("geoloc", "http://jabber.org/protocol/geoloc", UserLocationParser.class)); addFactory(new GenericPayloadParserFactory<UserTuneParser>("tune", "http://jabber.org/protocol/tune", UserTuneParser.class)); 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.parser.payloadparsers; + +import java.util.ArrayList; +import java.util.List; + +import com.isode.stroke.elements.WhiteboardColor; +import com.isode.stroke.elements.WhiteboardDeleteOperation; +import com.isode.stroke.elements.WhiteboardElement; +import com.isode.stroke.elements.WhiteboardEllipseElement; +import com.isode.stroke.elements.WhiteboardFreehandPathElement; +import com.isode.stroke.elements.WhiteboardInsertOperation; +import com.isode.stroke.elements.WhiteboardLineElement; +import com.isode.stroke.elements.WhiteboardOperation; +import com.isode.stroke.elements.WhiteboardPayload; +import com.isode.stroke.elements.WhiteboardPolygonElement; +import com.isode.stroke.elements.WhiteboardRectElement; +import com.isode.stroke.elements.WhiteboardUpdateOperation; +import com.isode.stroke.elements.WhiteboardPayload.Type; +import com.isode.stroke.elements.WhiteboardTextElement; +import com.isode.stroke.parser.AttributeMap; +import com.isode.stroke.parser.GenericPayloadParser; + +public class WhiteboardParser extends GenericPayloadParser<WhiteboardPayload> { + + private boolean actualIsText; + private int level_; + private String data_; + private WhiteboardElement wbElement; + private WhiteboardOperation operation; + + public WhiteboardParser() { + super(new WhiteboardPayload()); + } + + @Override + public void handleStartElement(String element, String ns, + AttributeMap attributes) { + if (level_ == 0) { + getPayloadInternal().setType(stringToType(getAttributeOr(attributes, "type", ""))); + } + else if (level_ == 1) { + String type = getAttributeOr(attributes, "type", ""); + if (type.equals("insert")) { + operation = new WhiteboardInsertOperation(); + } + else if (type.equals("update")) { + WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation(); + String move = getAttributeOr(attributes, "newpos", "0"); + updateOp.setNewPos(Integer.parseInt(move)); + operation = updateOp; + } + else if (type.equals("delete")) { + WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation(); + deleteOp.setElementID(getAttributeOr(attributes, "elementid", "")); + operation = deleteOp; + } + if (operation != null) { + operation.setID(getAttributeOr(attributes, "id", "")); + operation.setParentID(getAttributeOr(attributes, "parentid", "")); + try { + operation.setPos(getIntAttribute(attributes, "pos", 0)); + } catch (NumberFormatException e) { + // Dont set pos + } + } + + } + else if (level_ == 2) { + if ("line".equals(element)) { + int x1 = 0, y1 = 0, x2 = 0, y2 = 0; + try { + x1 = getIntAttribute(attributes, "x1", 0); + y1 = getIntAttribute(attributes, "y1", 0); + x2 = getIntAttribute(attributes, "x2", 0); + y2 = getIntAttribute(attributes, "y2", 0); + } catch (NumberFormatException e) { + } + WhiteboardLineElement whiteboardElement = new WhiteboardLineElement(x1, y1, x2, y2); + + WhiteboardColor color = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000")); + color.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1"))); + whiteboardElement.setColor(color); + + int penWidth = 1; + try { + penWidth = getIntAttribute(attributes, "stroke-width", 1); + } catch (NumberFormatException e) { + // Empty Catch + } + + whiteboardElement.setPenWidth(penWidth); + whiteboardElement.setID(getAttributeOr(attributes,"id","")); + getPayloadInternal().setElement(whiteboardElement); + wbElement = whiteboardElement; + } + else if ("path".equals(element)) { + WhiteboardFreehandPathElement whiteboardElement = new WhiteboardFreehandPathElement(); + String pathData = getAttributeOr(attributes, "d", ""); + List<WhiteboardFreehandPathElement.Point> points = + new ArrayList<WhiteboardFreehandPathElement.Point>(); + if (!pathData.isEmpty() && pathData.charAt(0) == 'M') { + try { + int pos = 1, npos; + int x, y; + if (pathData.charAt(pos) == ' ') { + pos++; + } + npos = pathData.indexOf(' ',pos); + x = Integer.parseInt(pathData.substring(pos, npos)); + pos = npos+1; + npos = pathData.indexOf('L',pos); + y = Integer.parseInt(pathData.substring(pos,npos)); + pos = npos+1; + if (pos < pathData.length() && pathData.charAt(pos) == ' ') { + pos++; + } + points.add(new WhiteboardFreehandPathElement.Point(x,y)); + while (pos < pathData.length()) { + npos = pathData.indexOf(' ',pos); + x = Integer.parseInt(pathData.substring(pos, npos)); + pos = npos+1; + npos = pathData.indexOf(' ',pos); + y = Integer.parseInt(pathData.substring(pos, npos)); + pos = npos+1; + points.add(new WhiteboardFreehandPathElement.Point(x,y)); + } + } + catch (NumberFormatException e) { + // Empty catch + } + } + whiteboardElement.setPoints(points); + + int penWidth = 1; + try { + penWidth = getIntAttribute(attributes, "stroke-width", 1); + } catch (NumberFormatException e) { + // Empty Catch + } + whiteboardElement.setPenWidth(penWidth); + + WhiteboardColor color = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000")); + color.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1"))); + whiteboardElement.setColor(color); + whiteboardElement.setID(getAttributeOr(attributes,"id","")); + getPayloadInternal().setElement(whiteboardElement); + wbElement = whiteboardElement; + } + else if ("rect".equals(element)) { + int x = 0, y = 0, width = 0, height = 0; + try { + x = getIntAttribute(attributes, "x", 0); + y = getIntAttribute(attributes, "y", 0); + width = getIntAttribute(attributes, "width", 0); + height = getIntAttribute(attributes, "height", 0); + } catch (Exception e) { + // Empty Catch + } + + WhiteboardRectElement whiteboardElement = new WhiteboardRectElement(x,y,width,height); + + int penWidth = 1; + try { + penWidth = getIntAttribute(attributes, "stroke-width", 1); + } catch (NumberFormatException e) { + // Empty Catch Block + } + whiteboardElement.setPenWidth(penWidth); + + WhiteboardColor penColor = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000")); + WhiteboardColor brushColor = new WhiteboardColor(getAttributeOr(attributes, "fill", "#000000")); + penColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1"))); + brushColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "fill-opacity", "1"))); + whiteboardElement.setPenColor(penColor); + whiteboardElement.setBrushColor(brushColor);; + whiteboardElement.setID(getAttributeOr(attributes, "id", "")); + getPayloadInternal().setElement(whiteboardElement); + wbElement = whiteboardElement; + } + else if ("polygon".equals(element)) { + WhiteboardPolygonElement whiteboardElement = new WhiteboardPolygonElement(); + + String pointsData = getAttributeOr(attributes, "points", ""); + List<WhiteboardPolygonElement.Point> points = new ArrayList<WhiteboardPolygonElement.Point>(); + int pos = 0; + int npos; + int x,y; + try { + while (pos < pointsData.length()) { + npos = pointsData.indexOf(',', pos); + if (npos == -1) { + break; + } + x = Integer.parseInt(pointsData.substring(pos, npos)); + pos = npos+1; + npos = pointsData.indexOf(' ',pos); + if (npos == -1) { + npos = pointsData.length(); + } + y = Integer.parseInt(pointsData.substring(pos,npos)); + pos = npos+1; + points.add(new WhiteboardPolygonElement.Point(x,y)); + } + } catch (NumberFormatException e) { + // Empty catch + } + + whiteboardElement.setPoints(points); + + int penWidth = 0; + try { + penWidth = getIntAttribute(attributes, "stroke-width", 1); + } catch (Exception e) { + // Empty catch + } + + WhiteboardColor penColor = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000")); + WhiteboardColor brushColor = new WhiteboardColor(getAttributeOr(attributes, "fill", "#000000")); + penColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1"))); + brushColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "fill-opacity", "1"))); + + whiteboardElement.setPenColor(penColor); + whiteboardElement.setBrushColor(brushColor); + whiteboardElement.setID(getAttributeOr(attributes, "id", "")); + getPayloadInternal().setElement(whiteboardElement); + wbElement = whiteboardElement; + } + else if ("text".equals(element)) { + int x = 0, y = 0; + try { + x = getIntAttribute(attributes, "x", 0); + y = getIntAttribute(attributes, "y", 0); + } catch (NumberFormatException e) { + // Empty Catch + } + + WhiteboardTextElement whiteboardElement = new WhiteboardTextElement(x, y); + + actualIsText = true; + WhiteboardColor color = new WhiteboardColor(getAttributeOr(attributes, "fill", "#000000")); + color.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1"))); + whiteboardElement.setColor(color); + + int fontSize = 1; + try { + fontSize = getIntAttribute(attributes, "font-size", 12); + } catch (NumberFormatException e) { + // Empty Catch + } + + whiteboardElement.setSize(fontSize); + whiteboardElement.setID(getAttributeOr(attributes, "id", "")); + getPayloadInternal().setElement(whiteboardElement); + wbElement = whiteboardElement; + } + else if ("ellipse".equals(element)) { + int cx = 0, cy = 0, rx = 0, ry = 0; + try { + cx = getIntAttribute(attributes, "cx", 0); + cy = getIntAttribute(attributes, "cy", 0); + rx = getIntAttribute(attributes, "rx", 0); + ry = getIntAttribute(attributes, "ry", 0); + } catch (NumberFormatException e) { + // Empty Catch + } + + WhiteboardEllipseElement whiteboardElement = new WhiteboardEllipseElement(cx, cy, rx, ry); + + int penWidth = 1; + try { + penWidth = getIntAttribute(attributes, "stroke-width", 1); + } catch (NumberFormatException e) { + // Empty Catch + } + whiteboardElement.setPenWidth(penWidth); + + WhiteboardColor penColor = new WhiteboardColor(getAttributeOr(attributes, "stroke", "#000000")); + WhiteboardColor brushColor = new WhiteboardColor(getAttributeOr(attributes,"fill","#000000")); + penColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "opacity", "1"))); + brushColor.setAlpha(opacityToAlpha(getAttributeOr(attributes, "fill-opacity", "1"))); + whiteboardElement.setPenColor(penColor); + whiteboardElement.setBrushColor(brushColor); + whiteboardElement.setID(getAttributeOr(attributes, "id", "")); + getPayloadInternal().setElement(whiteboardElement); + wbElement = whiteboardElement; + } + } + ++level_; + } + + public void handleEndElement(String element, String ns) { + --level_; + if (level_ == 0) { + getPayloadInternal().setData(data_); + } else if (level_ == 1) { + if (operation instanceof WhiteboardInsertOperation) { + WhiteboardInsertOperation insertOp = (WhiteboardInsertOperation) operation; + insertOp.setElement(wbElement); + } + if (operation instanceof WhiteboardUpdateOperation) { + WhiteboardUpdateOperation updateOp = (WhiteboardUpdateOperation) operation; + updateOp.setElement(wbElement); + } + getPayloadInternal().setOperation(operation); + } else if (level_ == 2) { + if (element == "text") { + actualIsText = false; + } + } + + } + + @Override + public void handleCharacterData(String data) { + if (level_ == 3 && actualIsText) { + WhiteboardTextElement element = (WhiteboardTextElement) getPayloadInternal().getElement(); + element.setText(data); + } + } + + private WhiteboardPayload.Type stringToType(String type) { + if (type == "data") { + return Type.Data; + } else if (type == "session-request") { + return Type.SessionRequest; + } else if (type == "session-accept") { + return Type.SessionAccept; + } else if (type == "session-terminate") { + return Type.SessionTerminate; + } else { + return Type.UnknownType; + } + } + + private int opacityToAlpha(String opacity) { + int value = 255; + int location = opacity.indexOf('.'); + if (location != -1 && opacity.length() > (3+location)) { + String stringValue = opacity.substring(location+1,location+3); + try { + value = Integer.parseInt(stringValue)*255/100; + } catch (NumberFormatException nfe) { + value = 255; + } + } + return value; + } + + /** + * Gets the given attribute from a {@link AttributeMap} if it is set and none + * {@code null}, otherwise returns a default value. + * @param attributeMap An {@link AttributeMap} + * @param attribute The name of the attribute to get from the map. + * @param defaultValue Default value to return if the attribute is not set + * (or is set to {@code null}) in the {@link AttributeMap} + * @return The value of the attribute in the {@link AttributeMap} if it is + * none {@code null} or {@code defaultValue} + */ + private String getAttributeOr(AttributeMap attributeMap,String attribute,String defaultValue) { + String value = attributeMap.getAttribute(attribute); + if (value == null) { + return defaultValue; + } + return value; + } + + /** + * Gets an int value for a given attribute in an attirbute map, or a default value + * if that attribute is not set. + * @param attributeMap An {@link AttributeMap} + * @param attribute The name of the attribute to get from the map + * @param defaultValue The default value to return if the attribute is not set. + * @throws NumberFormatException if the attribute value can not be passed into an integer. + * @return The value of the attribute as an int or defaultValue if it was not set. + */ + private int getIntAttribute(AttributeMap attributeMap,String attribute,int defaultValue) throws NumberFormatException { + String stringValue = getAttributeOr(attributeMap, attribute, String.valueOf(defaultValue)); + return Integer.parseInt(stringValue); + } + +} 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 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.serializer.payloadserializers; + +import java.util.Iterator; +import java.util.logging.Logger; + +import com.isode.stroke.elements.WhiteboardDeleteOperation; +import com.isode.stroke.elements.WhiteboardElementVisitor; +import com.isode.stroke.elements.WhiteboardEllipseElement; +import com.isode.stroke.elements.WhiteboardFreehandPathElement; +import com.isode.stroke.elements.WhiteboardInsertOperation; +import com.isode.stroke.elements.WhiteboardLineElement; +import com.isode.stroke.elements.WhiteboardOperation; +import com.isode.stroke.elements.WhiteboardPayload; +import com.isode.stroke.elements.WhiteboardPayload.Type; +import com.isode.stroke.elements.WhiteboardPolygonElement; +import com.isode.stroke.elements.WhiteboardRectElement; +import com.isode.stroke.elements.WhiteboardTextElement; +import com.isode.stroke.elements.WhiteboardUpdateOperation; +import com.isode.stroke.serializer.GenericPayloadSerializer; +import com.isode.stroke.serializer.xml.XMLElement; +import com.isode.stroke.serializer.xml.XMLTextNode; + +public class WhiteboardSerializer extends GenericPayloadSerializer<WhiteboardPayload> { + + private static class WhiteboardElementSerializingVisitor implements WhiteboardElementVisitor { + + private XMLElement element; + + @Override + public void visit(WhiteboardLineElement line) { + element = new XMLElement("line"); + element.setAttribute("x1", String.valueOf(line.x1())); + element.setAttribute("y1", String.valueOf(line.y1())); + element.setAttribute("x2", String.valueOf(line.x2())); + element.setAttribute("y2", String.valueOf(line.y2())); + element.setAttribute("id", line.getID()); + element.setAttribute("stroke", line.getColor().toHex()); + element.setAttribute("stroke-width", String.valueOf(line.getPenWidth())); + element.setAttribute("opacity", alphaToOpacity(line.getColor().getAlpha())); + } + + @Override + public void visit(WhiteboardFreehandPathElement path) { + element = new XMLElement("path"); + element.setAttribute("id", path.getID()); + element.setAttribute("stroke", path.getColor().toHex()); + element.setAttribute("stroke-width", String.valueOf(path.getPenWidth())); + element.setAttribute("opacity", alphaToOpacity(path.getColor().getAlpha())); + StringBuilder pathDataBuilder = new StringBuilder(); + if (!path.getPoints().isEmpty()) { + Iterator<WhiteboardFreehandPathElement.Point> it = path.getPoints().iterator(); + WhiteboardFreehandPathElement.Point point = it.next(); + pathDataBuilder.append('M'); + pathDataBuilder.append(point.x); + pathDataBuilder.append(' '); + pathDataBuilder.append(point.y); + pathDataBuilder.append('L'); + while (it.hasNext()) { + point = it.next(); + pathDataBuilder.append(point.x); + pathDataBuilder.append(' '); + pathDataBuilder.append(point.y); + pathDataBuilder.append(' '); + } + } + element.setAttribute("d", pathDataBuilder.toString()); + } + + @Override + public void visit(WhiteboardRectElement rect) { + element = new XMLElement("rect"); + element.setAttribute("x", String.valueOf(rect.getX())); + element.setAttribute("y", String.valueOf(rect.getY())); + element.setAttribute("width", String.valueOf(rect.getWidth())); + element.setAttribute("height", String.valueOf(rect.getHeight())); + element.setAttribute("id", rect.getID()); + element.setAttribute("stroke", rect.getPenColor().toHex()); + element.setAttribute("fill", rect.getBrushColor().toHex());; + element.setAttribute("stroke-width", String.valueOf(rect.getPenWidth())); + element.setAttribute("opacity", alphaToOpacity(rect.getPenColor().getAlpha())); + element.setAttribute("fill-opacity", alphaToOpacity(rect.getBrushColor().getAlpha())); + } + + @Override + public void visit(WhiteboardPolygonElement polygon) { + element = new XMLElement("polygon"); + element.setAttribute("id", polygon.getID()); + element.setAttribute("stroke", polygon.getPenColor().toHex()); + element.setAttribute("fill", polygon.getBrushColor().toHex());; + element.setAttribute("stroke-width", String.valueOf(polygon.getPenWidth())); + element.setAttribute("opacity", alphaToOpacity(polygon.getPenColor().getAlpha())); + element.setAttribute("fill-opacity", alphaToOpacity(polygon.getBrushColor().getAlpha())); + StringBuilder points = new StringBuilder(); + for (WhiteboardPolygonElement.Point point : polygon.getPoints()) { + points.append(point.x); + points.append(','); + points.append(point.y); + points.append(' '); + } + element.setAttribute("points", points.toString()); + + } + + @Override + public void visit(WhiteboardTextElement text) { + element = new XMLElement("text"); + element.setAttribute("x", String.valueOf(text.getX())); + element.setAttribute("y", String.valueOf(text.getY())); + element.setAttribute("font-size", String.valueOf(text.getSize())); + element.setAttribute("id", text.getID()); + element.setAttribute("fill", text.getColor().toHex()); + element.setAttribute("opacity", alphaToOpacity(text.getColor().getAlpha())); + element.addNode(new XMLTextNode(text.getText())); + + } + + @Override + public void visit(WhiteboardEllipseElement ellipse) { + element = new XMLElement("ellipse"); + element.setAttribute("cx", String.valueOf(ellipse.getCX())); + element.setAttribute("cy", String.valueOf(ellipse.getCY())); + element.setAttribute("rx", String.valueOf(ellipse.getRX())); + element.setAttribute("ry", String.valueOf(ellipse.getRY())); + element.setAttribute("id", ellipse.getID()); + element.setAttribute("stroke", ellipse.getPenColor().toHex()); + element.setAttribute("fill", ellipse.getBrushColor().toHex());; + element.setAttribute("stroke-width", String.valueOf(ellipse.getPenWidth())); + element.setAttribute("opacity", alphaToOpacity(ellipse.getPenColor().getAlpha())); + element.setAttribute("fill-opacity", alphaToOpacity(ellipse.getBrushColor().getAlpha())); + } + + public XMLElement getResult() { + return element; + } + + private String alphaToOpacity(int alpha) { + int opacity = 100*alpha/254; + if (opacity == 100) { + return "1"; + } else { + return String.format(".%d", opacity); + } + } + + } + + private final Logger logger = Logger.getLogger(this.getClass().getName()); + + public WhiteboardSerializer() { + super(WhiteboardPayload.class); + } + + @Override + protected String serializePayload(WhiteboardPayload payload) { + XMLElement element = new XMLElement("wb","http://swift.im/whiteboard"); + if (payload.getType() == Type.Data) { + XMLElement operationNode = new XMLElement("operation"); + WhiteboardElementSerializingVisitor visitor = new WhiteboardElementSerializingVisitor(); + WhiteboardOperation operation = payload.getOperation(); + if (operation instanceof WhiteboardInsertOperation) { + WhiteboardInsertOperation insertOp = (WhiteboardInsertOperation) operation; + operationNode.setAttribute("type", "insert"); + operationNode.setAttribute("pos", String.valueOf(insertOp.getPos())); + operationNode.setAttribute("id", insertOp.getID()); + operationNode.setAttribute("parentid", insertOp.getParentID()); + insertOp.getElement().accept(visitor); + operationNode.addNode(operationNode); + } + if (operation instanceof WhiteboardUpdateOperation) { + WhiteboardUpdateOperation updateOp = (WhiteboardUpdateOperation) operation; + operationNode.setAttribute("type", "update"); + operationNode.setAttribute("pos", String.valueOf(updateOp.getPos())); + operationNode.setAttribute("id", updateOp.getID()); + operationNode.setAttribute("parentid", updateOp.getParentID()); + operationNode.setAttribute("newpos", String.valueOf(updateOp.getNewPos())); + updateOp.getElement().accept(visitor); + operationNode.addNode(visitor.getResult()); + } + if (operation instanceof WhiteboardDeleteOperation) { + WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation(); + operationNode.setAttribute("type", "delete"); + operationNode.setAttribute("pos", String.valueOf(deleteOp.getPos())); + operationNode.setAttribute("id", deleteOp.getID()); + operationNode.setAttribute("parentid", deleteOp.getParentID()); + operationNode.setAttribute("elementid", deleteOp.getElementID()); + } + element.addNode(operationNode); + } + element.setAttribute("type", typeToString(payload.getType())); + return element.serialize(); + } + + private String typeToString(Type type) { + switch (type) { + case Data: + return "data"; + case SessionAccept: + return "session-accept"; + case SessionRequest: + return "session-request"; + case SessionTerminate: + return "session-terminate"; + case UnknownType: + logger.warning("Warning: Serializing unknown action value."); + return ""; + default: + assert(false); + return ""; + } + } + +} diff --git a/src/com/isode/stroke/whiteboard/IncomingWhiteboardSession.java b/src/com/isode/stroke/whiteboard/IncomingWhiteboardSession.java new file mode 100644 index 0000000..0ed9a01 --- /dev/null +++ b/src/com/isode/stroke/whiteboard/IncomingWhiteboardSession.java @@ -0,0 +1,60 @@ +package com.isode.stroke.whiteboard; + +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.WhiteboardOperation; +import com.isode.stroke.elements.WhiteboardPayload; +import com.isode.stroke.jid.JID; +import com.isode.stroke.queries.GenericRequest; +import com.isode.stroke.queries.IQRouter; + +public class IncomingWhiteboardSession extends WhiteboardSession { + + private final WhiteboardClient client = new WhiteboardClient(); + + public IncomingWhiteboardSession(JID jid, IQRouter router) { + super(jid, router); + } + + public void accept() { + WhiteboardPayload payload = new WhiteboardPayload(WhiteboardPayload.Type.SessionAccept); + GenericRequest<WhiteboardPayload> request = + new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_); + request.send(); + onRequestAccepted.emit(toJID_); + } + + @Override + public void sendOperation(WhiteboardOperation operation) { + operation.setID(idGenerator_.generateID()); + operation.setParentID(lastOpID_); + lastOpID_ = operation.getID(); + + WhiteboardOperation result = client.handleLocalOperationReceived(operation); + + if (result != null) { + WhiteboardPayload payload = new WhiteboardPayload(); + payload.setOperation(result); + sendPayload(payload); + } + } + + @Override + protected void handleIncomingOperation(WhiteboardOperation operation) { + WhiteboardClient.Result pairResult = client.handleServerOperationReceived(operation); + if (pairResult.client != null) { + if (pairResult.client.getPos() != -1) { + onOperationReceived.emit(pairResult.client); + } + lastOpID_ = pairResult.client.getID(); + } + + if (pairResult.server != null) { + WhiteboardPayload payload = new WhiteboardPayload(); + payload.setOperation(pairResult.server); + sendPayload(payload); + } + } + + + +} diff --git a/src/com/isode/stroke/whiteboard/OutgoingWhiteboardSession.java b/src/com/isode/stroke/whiteboard/OutgoingWhiteboardSession.java new file mode 100644 index 0000000..3a79a4c --- /dev/null +++ b/src/com/isode/stroke/whiteboard/OutgoingWhiteboardSession.java @@ -0,0 +1,76 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.whiteboard; + +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.WhiteboardOperation; +import com.isode.stroke.elements.WhiteboardPayload; +import com.isode.stroke.jid.JID; +import com.isode.stroke.queries.GenericRequest; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.signals.Slot2; + +public class OutgoingWhiteboardSession extends WhiteboardSession { + + private final WhiteboardServer server = new WhiteboardServer(); + + public OutgoingWhiteboardSession(JID jid, IQRouter router) { + super(jid, router); + } + + public void startSession() { + + WhiteboardPayload payload = new WhiteboardPayload(WhiteboardPayload.Type.SessionRequest); + GenericRequest<WhiteboardPayload> request = + new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_); + request.onResponse.connect(new Slot2<WhiteboardPayload, ErrorPayload>() { + + @Override + public void call(WhiteboardPayload payload, ErrorPayload error) { + handleRequestResponse(payload, error); + } + + }); + request.send(); + } + + private void handleRequestResponse(WhiteboardPayload payload,ErrorPayload error) { + if (error != null) { + onRequestRejected.emit(toJID_); + } + } + + @Override + public void sendOperation(WhiteboardOperation operation) { + operation.setID(idGenerator_.generateID()); + operation.setParentID(lastOpID_); + lastOpID_ = operation.getID(); + + server.handleLocalOperationReceived(operation); + WhiteboardPayload payload = new WhiteboardPayload(); + payload.setOperation(operation); + sendPayload(payload); + } + + @Override + protected void handleIncomingOperation(WhiteboardOperation operation) { + WhiteboardOperation op = server.handleClientOperationReceived(operation); + if (op.getPos() != -1) { + onOperationReceived.emit(op); + } + lastOpID_ = op.getID(); + + WhiteboardPayload payload = new WhiteboardPayload(); + payload.setOperation(op); + sendPayload(payload); + } + +} diff --git a/src/com/isode/stroke/whiteboard/WhiteboardClient.java b/src/com/isode/stroke/whiteboard/WhiteboardClient.java new file mode 100644 index 0000000..7b7bb10 --- /dev/null +++ b/src/com/isode/stroke/whiteboard/WhiteboardClient.java @@ -0,0 +1,149 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.whiteboard; + +import java.util.ArrayList; +import java.util.List; + +import com.isode.stroke.elements.WhiteboardDeleteOperation; +import com.isode.stroke.elements.WhiteboardInsertOperation; +import com.isode.stroke.elements.WhiteboardOperation; +import com.isode.stroke.elements.WhiteboardUpdateOperation; +import com.isode.stroke.whiteboard.WhiteboardTransformer.Pair; + +public class WhiteboardClient { + + private final List<WhiteboardOperation> localOperations_ = new ArrayList<WhiteboardOperation>(); + private final List<WhiteboardOperation> serverOperations_ = new ArrayList<WhiteboardOperation>(); + private final List<WhiteboardOperation> bridge_ = new ArrayList<WhiteboardOperation>(); + private String lastSentOperationID_ = ""; + + public static class Result { + public final WhiteboardOperation client; + public final WhiteboardOperation server; + public Result(WhiteboardOperation client,WhiteboardOperation server) { + this.client = client; + this.server = server; + } + } + + public WhiteboardOperation handleLocalOperationReceived(WhiteboardOperation operation) { + localOperations_.add(operation); + + WhiteboardOperation op = null; + if (operation instanceof WhiteboardInsertOperation) { + op = new WhiteboardInsertOperation((WhiteboardInsertOperation) operation); + } + else if (operation instanceof WhiteboardUpdateOperation) { + op = new WhiteboardUpdateOperation((WhiteboardUpdateOperation) operation); + } + else if (operation instanceof WhiteboardDeleteOperation) { + op = new WhiteboardDeleteOperation((WhiteboardDeleteOperation) operation); + } + + if (!bridge_.isEmpty()) { + WhiteboardOperation back = bridge_.get(bridge_.size()-1); + op.setParentID(back.getID()); + } + bridge_.add(op); + + if (lastSentOperationID_.isEmpty()) { + if (operation instanceof WhiteboardInsertOperation) { + op = new WhiteboardInsertOperation((WhiteboardInsertOperation) operation); + } + else if (operation instanceof WhiteboardUpdateOperation) { + op = new WhiteboardUpdateOperation((WhiteboardUpdateOperation) operation); + } + else if (operation instanceof WhiteboardDeleteOperation) { + op = new WhiteboardDeleteOperation((WhiteboardDeleteOperation) operation); + } + + if (!serverOperations_.isEmpty()) { + WhiteboardOperation back = serverOperations_.get(serverOperations_.size()-1); + op.setParentID(back.getID()); + } + lastSentOperationID_ = operation.getID(); + return op; + } + else{ + return null; + } + } + + public Result handleServerOperationReceived(WhiteboardOperation operation) { + serverOperations_.add(operation); + WhiteboardOperation clientResult = null, serverResult = null; + if (localOperations_.size() == (serverOperations_.size()-1) ) { + localOperations_.add(operation); + clientResult = operation; + } + else if (lastSentOperationID_ == operation.getID()) { + //Client received confirmation about own operation and it sends next operation to server + if (!bridge_.isEmpty() && lastSentOperationID_ .equals(bridge_.get(0).getID())) { + bridge_.remove(0); + } + + if (!bridge_.isEmpty() && lastSentOperationID_ .equals(bridge_.get(0).getParentID())) { + lastSentOperationID_ = bridge_.get(0).getID(); + serverResult = bridge_.get(0); + } + if (serverResult == null) { + lastSentOperationID_ = ""; + } + } + else if (!bridge_.isEmpty()) { + WhiteboardOperation temp; + Pair opPair = WhiteboardTransformer.transform(bridge_.get(0), operation); + temp = opPair.first; + + bridge_.set(0, opPair.second); + String previousID = bridge_.get(0).getID(); + for (int i = 1; i < bridge_.size(); ++i) { + opPair = WhiteboardTransformer.transform(bridge_.get(i), temp); + temp = opPair.first; + bridge_.set(i, opPair.second); + bridge_.get(i).setParentID(previousID); + previousID = bridge_.get(i).getID(); + } + + WhiteboardOperation localBack = localOperations_.get(localOperations_.size()-1); + temp.setParentID(localBack.getID()); + localOperations_.add(temp); + clientResult = temp; + } + + return new Result(clientResult, serverResult); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Client\n"); + for (WhiteboardOperation op : localOperations_) { + builder.append(op.getID()); + builder.append(' '); + builder.append(op.getPos()); + builder.append('\n'); + } + builder.append("Server\n"); + for (WhiteboardOperation op : serverOperations_) { + builder.append(op.getID()); + builder.append(' '); + builder.append(op.getPos()); + builder.append('\n'); + } + return builder.toString(); + } + + public void print() { + System.out.println(this); + } + +} diff --git a/src/com/isode/stroke/whiteboard/WhiteboardResponder.java b/src/com/isode/stroke/whiteboard/WhiteboardResponder.java new file mode 100644 index 0000000..6f80115 --- /dev/null +++ b/src/com/isode/stroke/whiteboard/WhiteboardResponder.java @@ -0,0 +1,52 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.whiteboard; + +import com.isode.stroke.elements.WhiteboardPayload; +import com.isode.stroke.jid.JID; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.queries.SetResponder; +import com.isode.stroke.elements.ErrorPayload; + +public class WhiteboardResponder extends SetResponder<WhiteboardPayload> { + + private final WhiteboardSessionManager sessionManager_; + private final IQRouter router_; + + public WhiteboardResponder(WhiteboardSessionManager sessionManager,IQRouter router) { + super(new WhiteboardPayload(),router); + sessionManager_ = sessionManager; + router_ = router; + } + + @Override + protected boolean handleSetRequest(JID from, JID to, String id, + WhiteboardPayload payload) { + if (payload.getType() == WhiteboardPayload.Type.SessionRequest) { + if (sessionManager_.getSession(from) != null) { + sendError(from, id, ErrorPayload.Condition.Conflict, ErrorPayload.Type.Cancel); + } + else { + sendResponse(from, id, null); + IncomingWhiteboardSession session = new IncomingWhiteboardSession(from, router_); + sessionManager_.handleIncomingSession(session); + } + } + else { + sendResponse(from, id, null); + WhiteboardSession session = sessionManager_.getSession(from); + if (session != null) { + session.handleIncomingAction(payload); + } + } + return true; + } + +} diff --git a/src/com/isode/stroke/whiteboard/WhiteboardServer.java b/src/com/isode/stroke/whiteboard/WhiteboardServer.java new file mode 100644 index 0000000..7685671 --- /dev/null +++ b/src/com/isode/stroke/whiteboard/WhiteboardServer.java @@ -0,0 +1,70 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.whiteboard; + +import java.util.ArrayList; +import java.util.List; + +import com.isode.stroke.elements.WhiteboardOperation; + +public class WhiteboardServer { + + private final List<WhiteboardOperation> operations_ = new ArrayList<WhiteboardOperation>(); + + public void handleLocalOperationReceived(WhiteboardOperation operation) { + operations_.add(operation); + } + + public WhiteboardOperation handleClientOperationReceived(WhiteboardOperation newOperation) { + + if (operations_.isEmpty() || + newOperation.getParentID().equals(operations_.get(operations_.size()-1).getID())) { + operations_.add(newOperation); + return newOperation; + } + for (int i = (operations_.size()-1); i >= 0; i--) { + WhiteboardOperation operation = operations_.get(i); + while (newOperation.getParentID().equals(operation.getParentID())) { + WhiteboardTransformer.Pair tResult = + WhiteboardTransformer.transform(newOperation, operation); + if (i == (operations_.size()-1)) { + operations_.add(tResult.second); + return tResult.second; + } + else { + newOperation = tResult.second; + i++; + operation = operations_.get(i); + } + } + } + return null; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Server:\n"); + for (WhiteboardOperation op : operations_) { + builder.append(op.getID()); + builder.append(" '"); + builder.append(op.getParentID()); + builder.append("' "); + builder.append(op.getPos()); + builder.append("\n"); + } + return builder.toString(); + } + + public void print() { + System.out.println(this); + } + +} diff --git a/src/com/isode/stroke/whiteboard/WhiteboardSession.java b/src/com/isode/stroke/whiteboard/WhiteboardSession.java new file mode 100644 index 0000000..cc1957e --- /dev/null +++ b/src/com/isode/stroke/whiteboard/WhiteboardSession.java @@ -0,0 +1,89 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.whiteboard; + +import com.isode.stroke.base.IDGenerator; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.WhiteboardElement; +import com.isode.stroke.elements.WhiteboardOperation; +import com.isode.stroke.elements.WhiteboardPayload; +import com.isode.stroke.elements.WhiteboardPayload.Type; +import com.isode.stroke.jid.JID; +import com.isode.stroke.queries.GenericRequest; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.signals.Signal1; + +public abstract class WhiteboardSession { + + protected final JID toJID_; + protected final IQRouter router_; + protected String lastOpID_ = ""; + protected final IDGenerator idGenerator_ = new IDGenerator(); + + public final Signal1<WhiteboardElement> onElementReceived = new Signal1<WhiteboardElement>(); + public final Signal1<WhiteboardOperation> onOperationReceived = new Signal1<WhiteboardOperation>(); + public final Signal1<JID> onSessionTerminated = new Signal1<JID>(); + public final Signal1<JID> onRequestAccepted = new Signal1<JID>(); + public final Signal1<JID> onRequestRejected = new Signal1<JID>(); + + public WhiteboardSession(JID jid,IQRouter router) { + toJID_ = jid; + router_ = router; + } + + public void handleIncomingAction(WhiteboardPayload payload) { + switch(payload.getType()) { + case Data: + handleIncomingOperation(payload.getOperation()); + return; + case SessionAccept: + onRequestAccepted.emit(toJID_); + return; + case SessionTerminate: + onSessionTerminated.emit(toJID_); + return; + case SessionRequest: + case UnknownType: + default: + return; + } + } + public void sendElement(WhiteboardElement element) { + WhiteboardPayload payload = new WhiteboardPayload(); + payload.setElement(element); + GenericRequest<WhiteboardPayload> request = + new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_); + request.send(); + } + + public abstract void sendOperation(WhiteboardOperation operation); + + public void cancel() { + if (router_.isAvailable()) { + WhiteboardPayload payload = new WhiteboardPayload(Type.SessionTerminate); + GenericRequest<WhiteboardPayload> request = + new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_); + request.send(); + } + onSessionTerminated.emit(toJID_); + } + + public JID getTo() { + return toJID_; + } + + protected abstract void handleIncomingOperation(WhiteboardOperation operation); + + protected final void sendPayload(WhiteboardPayload payload) { + GenericRequest<WhiteboardPayload> request = new GenericRequest<WhiteboardPayload>(IQ.Type.Set, toJID_, payload, router_); + request.send(); + } + +} diff --git a/src/com/isode/stroke/whiteboard/WhiteboardSessionManager.java b/src/com/isode/stroke/whiteboard/WhiteboardSessionManager.java new file mode 100644 index 0000000..d797303 --- /dev/null +++ b/src/com/isode/stroke/whiteboard/WhiteboardSessionManager.java @@ -0,0 +1,169 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.whiteboard; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.isode.stroke.client.StanzaChannel; +import com.isode.stroke.disco.EntityCapsProvider; +import com.isode.stroke.elements.DiscoInfo; +import com.isode.stroke.elements.Presence; +import com.isode.stroke.jid.JID; +import com.isode.stroke.presence.PresenceOracle; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.signals.Signal1; +import com.isode.stroke.signals.Slot1; + +public class WhiteboardSessionManager { + + private final Map<JID,WhiteboardSession> sessions_ = new HashMap<JID,WhiteboardSession>(); + private final IQRouter router_; + private final StanzaChannel stanzaChannel_; + private final PresenceOracle presenceOracle_; + private final EntityCapsProvider capsProvider_; + private WhiteboardResponder responder; + + public final Signal1<IncomingWhiteboardSession> onSessionRequest = + new Signal1<IncomingWhiteboardSession>(); + + public WhiteboardSessionManager(IQRouter router, StanzaChannel stanzaChannel, + PresenceOracle presenceOracle, EntityCapsProvider capsProvider) { + router_ = router; + stanzaChannel_ = stanzaChannel; + presenceOracle_ = presenceOracle; + capsProvider_ = capsProvider; + responder = new WhiteboardResponder(this, router_); + responder.start(); + stanzaChannel_.onPresenceReceived.connect(new Slot1<Presence>() { + + @Override + public void call(Presence presence) { + handlePresenceReceived(presence); + } + + }); + stanzaChannel_.onAvailableChanged.connect(new Slot1<Boolean>() { + + @Override + public void call(Boolean p1) { + handleAvailableChanged(p1.booleanValue()); + } + + }); + } + + // Unlike in C++ we can't put this in a destructor to automatically be called when object is + // destroyed. Must be called manually. + public void stop() { + responder.stop(); + } + + public WhiteboardSession getSession(JID to) { + return sessions_.get(to); + } + + public WhiteboardSession requestSession(JID to) { + WhiteboardSession session = getSession(to); + if (session == null) { + OutgoingWhiteboardSession outgoingSession = createOutgoingSession(to); + outgoingSession.startSession(); + return outgoingSession; + } else { + return session; + } + } + + private JID getFullJID(JID bareJID) { + JID fullReceipientJID = null; + int priority = Integer.MIN_VALUE; + + //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11 + List<Presence> presences = + new ArrayList<Presence>(presenceOracle_.getAllPresence(bareJID)); + + //iterate over them + for (Presence pres : presences) { + if (pres.getPriority() > priority) { + // look up caps from the jid + DiscoInfo info = capsProvider_.getCaps(pres.getFrom()); + if (info != null && info.hasFeature(DiscoInfo.WhiteboardFeature)) { + priority = pres.getPriority(); + fullReceipientJID = pres.getFrom(); + } + } + } + + return fullReceipientJID; + } + + private OutgoingWhiteboardSession createOutgoingSession(JID to) { + JID fullJID = to; + if (fullJID.isBare()) { + fullJID = getFullJID(fullJID); + } + OutgoingWhiteboardSession session = new OutgoingWhiteboardSession(fullJID, router_); + sessions_.put(fullJID, session); + session.onSessionTerminated.connect(new Slot1<JID>() { + + @Override + public void call(JID jid) { + deleteSessionEntry(jid); + } + + }); + session.onRequestRejected.connect(new Slot1<JID>() { + + @Override + public void call(JID jid) { + deleteSessionEntry(jid); + } + + }); + return session; + } + + public void handleIncomingSession(IncomingWhiteboardSession session) { + sessions_.put(session.getTo(), session); + session.onSessionTerminated.connect(new Slot1<JID>() { + + @Override + public void call(JID jid) { + deleteSessionEntry(jid); + } + + }); + onSessionRequest.emit(session); + } + + private void handlePresenceReceived(Presence presence) { + if (!presence.isAvailable()) { + WhiteboardSession session = getSession(presence.getFrom()); + if (session != null) { + session.cancel(); + } + } + } + private void handleAvailableChanged(boolean available) { + if (!available) { + Map<JID,WhiteboardSession> sessionsCopy = new HashMap<JID,WhiteboardSession>(sessions_); + for (WhiteboardSession session : sessionsCopy.values()) { + session.cancel(); + } + } + } + + private void deleteSessionEntry(JID contact) { + sessions_.remove(contact); + } + +} diff --git a/src/com/isode/stroke/whiteboard/WhiteboardTransformer.java b/src/com/isode/stroke/whiteboard/WhiteboardTransformer.java new file mode 100644 index 0000000..4d20498 --- /dev/null +++ b/src/com/isode/stroke/whiteboard/WhiteboardTransformer.java @@ -0,0 +1,264 @@ +/* Copyright (c) 2016, Isode Limited, London, England. + * All rights reserved. + * + * Acquisition and use of this software and related materials for any + * purpose requires a written license agreement from Isode Limited, + * or a written license from an organisation licensed by Isode Limited + * to grant such a license. + * + */ +package com.isode.stroke.whiteboard; + +import com.isode.stroke.elements.WhiteboardDeleteOperation; +import com.isode.stroke.elements.WhiteboardInsertOperation; +import com.isode.stroke.elements.WhiteboardOperation; +import com.isode.stroke.elements.WhiteboardUpdateOperation; + +public final class WhiteboardTransformer { + + public static final class Pair { + public final WhiteboardOperation first; + public final WhiteboardOperation second; + public Pair(WhiteboardOperation first,WhiteboardOperation second) { + this.first = first; + this.second = second; + } + public Pair() { + this(new WhiteboardOperation(),new WhiteboardOperation()); + } + } + + private WhiteboardTransformer() { + // Static class so constructor is private + } + + public static Pair transform(WhiteboardOperation clientOp,WhiteboardOperation serverOp) { + if (clientOp instanceof WhiteboardInsertOperation) { + WhiteboardInsertOperation clientInsert = (WhiteboardInsertOperation) clientOp; + if (serverOp instanceof WhiteboardInsertOperation) { + WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp; + return transform(clientInsert, serverInsert); + } + else if (serverOp instanceof WhiteboardUpdateOperation) { + WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp; + return transform(clientInsert, serverUpdate); + } + else if (serverOp instanceof WhiteboardDeleteOperation) { + WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp; + return transform(clientInsert, serverDelete); + } + } + else if (clientOp instanceof WhiteboardUpdateOperation) { + WhiteboardUpdateOperation clientUpdate = (WhiteboardUpdateOperation) clientOp; + if (serverOp instanceof WhiteboardInsertOperation) { + WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp; + return transform(clientUpdate, serverInsert); + } + else if (serverOp instanceof WhiteboardUpdateOperation) { + WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp; + return transform(clientUpdate, serverUpdate); + } + else if (serverOp instanceof WhiteboardDeleteOperation) { + WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp; + return transform(clientUpdate, serverDelete); + } + } + else if (clientOp instanceof WhiteboardDeleteOperation) { + WhiteboardDeleteOperation clientDelete = (WhiteboardDeleteOperation) clientOp; + if (serverOp instanceof WhiteboardInsertOperation) { + WhiteboardInsertOperation serverInsert = (WhiteboardInsertOperation) serverOp; + return transform(clientDelete, serverInsert); + } + else if (serverOp instanceof WhiteboardUpdateOperation) { + WhiteboardUpdateOperation serverUpdate = (WhiteboardUpdateOperation) serverOp; + return transform(clientDelete, serverUpdate); + } + else if (serverOp instanceof WhiteboardDeleteOperation) { + WhiteboardDeleteOperation serverDelete = (WhiteboardDeleteOperation) serverOp; + return transform(clientDelete, serverDelete); + } + } + return new Pair(); + } + + public static Pair transform(WhiteboardInsertOperation clientOp,WhiteboardInsertOperation serverOp) { + WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp); + first.setParentID(clientOp.getID()); + WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp); + second.setParentID(serverOp.getID()); + if (clientOp.getPos() <= serverOp.getPos()) { + first.setPos(first.getPos()+1); + } + else { + second.setPos(second.getPos()+1); + } + return new Pair(first,second); + } + + public static Pair transform(WhiteboardUpdateOperation clientOp,WhiteboardUpdateOperation serverOp) { + + WhiteboardUpdateOperation first = new WhiteboardUpdateOperation(serverOp); + first.setParentID(clientOp.getID()); + + WhiteboardUpdateOperation second; + if (clientOp.getPos() == serverOp.getPos()) { + second = new WhiteboardUpdateOperation(serverOp); + second.setID(clientOp.getID()); + second.setParentID(serverOp.getID()); + } + else { + second = new WhiteboardUpdateOperation(clientOp); + second.setParentID(serverOp.getID()); + } + + if (clientOp.getPos() < serverOp.getPos() && clientOp.getNewPos() > serverOp.getPos()) { + first.setPos(first.getPos()-1); + if (clientOp.getNewPos() >= serverOp.getNewPos()) { + first.setNewPos(first.getNewPos()-1); + } + } + else if (clientOp.getNewPos() >= serverOp.getNewPos()) { + first.setNewPos(first.getNewPos()-1); + } + if (serverOp.getPos() < clientOp.getPos() && serverOp.getNewPos() > clientOp.getPos()) { + second.setPos(second.getPos()-1); + if (serverOp.getNewPos() >= clientOp.getNewPos()) { + second.setNewPos(second.getNewPos()-1); + } + } + else if (serverOp.getNewPos() >= clientOp.getNewPos()) { + second.setNewPos(second.getNewPos()-1); + } + return new Pair(first,second); + } + + public static Pair transform(WhiteboardUpdateOperation clientOp,WhiteboardInsertOperation serverOp) { + WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp); + first.setParentID(clientOp.getID()); + WhiteboardUpdateOperation second = new WhiteboardUpdateOperation(clientOp); + second.setParentID(serverOp.getID()); + if (serverOp.getPos() <= clientOp.getPos()) { + second.setPos(second.getPos()+1); + } + return new Pair(first, second); + } + + public static Pair transform(WhiteboardInsertOperation clientOp,WhiteboardUpdateOperation serverOp) { + WhiteboardUpdateOperation first = new WhiteboardUpdateOperation(serverOp); + first.setParentID(clientOp.getID()); + WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp); + second.setParentID(serverOp.getID()); + if (serverOp.getPos() >= clientOp.getPos()) { + first.setPos(first.getPos()+1); + } + return new Pair(first,second); + } + + public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardDeleteOperation serverOp) { + WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp); + first.setParentID(clientOp.getID()); + WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp); + second.setParentID(serverOp.getID()); + if (clientOp.getPos() == -1) { + second.setPos(-1); + } + if (serverOp.getPos() == -1) { + first.setPos(-1); + } + if (clientOp.getPos() < serverOp.getPos()) { + first.setPos(first.getPos()-1); + } + else if (clientOp.getPos() > serverOp.getPos()) { + second.setPos(second.getPos()-1); + } + else { + first.setPos(-1); + second.setPos(-1); + } + return new Pair(first, second); + } + + public static Pair transform(WhiteboardInsertOperation clientOp, WhiteboardDeleteOperation serverOp) { + WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp); + first.setParentID(clientOp.getID()); + WhiteboardInsertOperation second = new WhiteboardInsertOperation(clientOp); + second.setParentID(serverOp.getID()); + if (clientOp.getPos() <= serverOp.getPos()) { + first.setPos(first.getPos()+1); + } + else if (serverOp.getPos() != -1) { + second.setPos(second.getPos()-1); + } + return new Pair(first, second); + } + + public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardInsertOperation serverOp) { + WhiteboardInsertOperation first = new WhiteboardInsertOperation(serverOp); + first.setParentID(clientOp.getID()); + WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp); + second.setParentID(serverOp.getID()); + if (serverOp.getPos() <= clientOp.getPos()) { + second.setPos(second.getPos()+1); + } + else if (clientOp.getPos() != -1) { + first.setPos(first.getPos()-1); + } + return new Pair(first, second); + } + + public static Pair transform(WhiteboardUpdateOperation clientOp, WhiteboardDeleteOperation serverOp) { + WhiteboardDeleteOperation first = new WhiteboardDeleteOperation(serverOp); + first.setParentID(clientOp.getID()); + WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation(clientOp);; + WhiteboardOperation second = updateOp; + second.setParentID(serverOp.getID()); + if (clientOp.getPos() == serverOp.getPos()) { + WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation(); + second = deleteOp; + second.setPos(-1); + second.setID(clientOp.getID()); + second.setParentID(serverOp.getID()); + deleteOp.setElementID(serverOp.getElementID()); + } + else if (clientOp.getPos() > serverOp.getPos() && clientOp.getNewPos() <= serverOp.getPos()) { + second.setPos(second.getPos()-1); + } + else if (clientOp.getPos() < serverOp.getPos() && clientOp.getNewPos() >= serverOp.getPos()) { + updateOp.setNewPos(updateOp.getNewPos()-1); + } + else if (clientOp.getPos() > serverOp.getPos()) { + second.setPos(second.getPos() - 1); + updateOp.setNewPos(updateOp.getNewPos()-1); + } + return new Pair(first, second); + } + + public static Pair transform(WhiteboardDeleteOperation clientOp, WhiteboardUpdateOperation serverOp) { + WhiteboardUpdateOperation updateOp = new WhiteboardUpdateOperation(serverOp); + WhiteboardOperation first = updateOp; + first.setParentID(clientOp.getID()); + WhiteboardDeleteOperation second = new WhiteboardDeleteOperation(clientOp); + second.setParentID(serverOp.getID()); + if (clientOp.getPos() == serverOp.getPos()) { + WhiteboardDeleteOperation deleteOp = new WhiteboardDeleteOperation(); + first = deleteOp; + first.setPos(-1); + first.setID(serverOp.getID()); + first.setParentID(clientOp.getID()); + deleteOp.setElementID(clientOp.getElementID()); + } + else if (clientOp.getPos() < serverOp.getPos() && clientOp.getPos() >= serverOp.getNewPos()) { + first.setPos(first.getPos()-1); + } + else if (clientOp.getPos() > serverOp.getPos() && clientOp.getPos() <= serverOp.getNewPos()) { + updateOp.setNewPos(updateOp.getNewPos()-1); + } + else if (clientOp.getPos() < serverOp.getPos()) { + first.setPos(first.getPos()-1); + updateOp.setNewPos(updateOp.getNewPos()-1); + } + return new Pair(first, second); + } + + +} |