From 07fd22914ba845be72c696f0078a072e903581a2 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 2 Aug 2016 21:29:41 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20ifPresent=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20MapValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/lib/MapValue.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/annimon/ownlang/lib/MapValue.java b/src/main/java/com/annimon/ownlang/lib/MapValue.java index 40ba519..5d3d33e 100644 --- a/src/main/java/com/annimon/ownlang/lib/MapValue.java +++ b/src/main/java/com/annimon/ownlang/lib/MapValue.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Objects; +import java.util.function.Consumer; /** * @@ -31,6 +32,18 @@ public class MapValue implements Value, Iterable> { this.map = map; } + public boolean ifPresent(String key, Consumer consumer) { + return ifPresent(new StringValue(key), consumer); + } + + public boolean ifPresent(Value key, Consumer consumer) { + if (map.containsKey(key)) { + consumer.accept(map.get(key)); + return true; + } + return false; + } + public ArrayValue toPairs() { final int size = map.size(); final ArrayValue result = new ArrayValue(size); From 148b6eff32e06c8c7a4d1583e7298a685cdb4ad1 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 2 Aug 2016 23:06:21 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=9A=D0=BE=D0=BD=D0=B2=D0=B5=D1=80=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=20=D0=BE=D0=B1=D1=8A=D0=B5?= =?UTF-8?q?=D0=BA=D1=82/=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D0=B0=20=D0=B2=20?= =?UTF-8?q?=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/lib/ValueUtils.java | 83 +++++++++++++++++++ .../lib/modules/functions/json_decode.java | 53 ++---------- .../lib/modules/functions/json_encode.java | 46 ++-------- 3 files changed, 100 insertions(+), 82 deletions(-) create mode 100644 src/main/java/com/annimon/ownlang/lib/ValueUtils.java diff --git a/src/main/java/com/annimon/ownlang/lib/ValueUtils.java b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java new file mode 100644 index 0000000..90feac5 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/ValueUtils.java @@ -0,0 +1,83 @@ +package com.annimon.ownlang.lib; + +import java.util.Iterator; +import java.util.Map; +import org.json.JSONArray; +import org.json.JSONObject; + +public final class ValueUtils { + + public static Object toObject(Value val) { + switch (val.type()) { + case Types.ARRAY: + return toObject((ArrayValue) val); + case Types.MAP: + return toObject((MapValue) val); + case Types.NUMBER: + return val.raw(); + case Types.STRING: + return val.asString(); + default: + return JSONObject.NULL; + } + } + + public static Object toObject(MapValue map) { + final JSONObject result = new JSONObject(); + for (Map.Entry entry : map) { + final String key = entry.getKey().asString(); + final Object value = toObject(entry.getValue()); + result.put(key, value); + } + return result; + } + + public static Object toObject(ArrayValue array) { + final JSONArray result = new JSONArray(); + for (Value value : array) { + result.put(toObject(value)); + } + return result; + } + + public static Value toValue(Object obj) { + if (obj instanceof JSONObject) { + return toValue((JSONObject) obj); + } + if (obj instanceof JSONArray) { + return toValue((JSONArray) obj); + } + if (obj instanceof String) { + return new StringValue((String) obj); + } + if (obj instanceof Number) { + return NumberValue.of(((Number) obj)); + } + if (obj instanceof Boolean) { + return NumberValue.fromBoolean((Boolean) obj); + } + // NULL or other + return NumberValue.ZERO; + } + + public static MapValue toValue(JSONObject json) { + final MapValue result = new MapValue(json.length()); + final Iterator it = json.keys(); + while(it.hasNext()) { + final String key = it.next(); + final Value value = toValue(json.get(key)); + result.set(new StringValue(key), value); + } + return result; + } + + public static ArrayValue toValue(JSONArray json) { + final int length = json.length(); + final ArrayValue result = new ArrayValue(length); + for (int i = 0; i < length; i++) { + final Value value = toValue(json.get(i)); + result.set(i, value); + } + return result; + } +} diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java index 5130a42..8bd415a 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_decode.java @@ -1,8 +1,12 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; -import java.util.Iterator; -import org.json.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import org.json.JSONException; +import org.json.JSONTokener; + public final class json_decode implements Function { @@ -12,50 +16,9 @@ public final class json_decode implements Function { try { final String jsonRaw = args[0].asString(); final Object root = new JSONTokener(jsonRaw).nextValue(); - return process(root); + return ValueUtils.toValue(root); } catch (JSONException ex) { throw new RuntimeException("Error while parsing json", ex); } } - - private Value process(Object obj) { - if (obj instanceof JSONObject) { - return process((JSONObject) obj); - } - if (obj instanceof JSONArray) { - return process((JSONArray) obj); - } - if (obj instanceof String) { - return new StringValue((String) obj); - } - if (obj instanceof Number) { - return NumberValue.of(((Number) obj)); - } - if (obj instanceof Boolean) { - return NumberValue.fromBoolean((Boolean) obj); - } - // NULL or other - return NumberValue.ZERO; - } - - private MapValue process(JSONObject json) { - final MapValue result = new MapValue(json.length()); - final Iterator it = json.keys(); - while(it.hasNext()) { - final String key = it.next(); - final Value value = process(json.get(key)); - result.set(new StringValue(key), value); - } - return result; - } - - private ArrayValue process(JSONArray json) { - final int length = json.length(); - final ArrayValue result = new ArrayValue(length); - for (int i = 0; i < length; i++) { - final Value value = process(json.get(i)); - result.set(i, value); - } - return result; - } } \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java index 33951c4..7907b14 100644 --- a/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java +++ b/src/main/java/com/annimon/ownlang/lib/modules/functions/json_encode.java @@ -1,8 +1,13 @@ package com.annimon.ownlang.lib.modules.functions; -import com.annimon.ownlang.lib.*; -import java.util.Map; -import org.json.*; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.Function; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.ValueUtils; +import org.json.JSONException; +import org.json.JSONObject; + public final class json_encode implements Function { @@ -10,44 +15,11 @@ public final class json_encode implements Function { public Value execute(Value... args) { Arguments.check(1, args.length); try { - final Object root = process(args[0]); + final Object root = ValueUtils.toObject(args[0]); final String jsonRaw = JSONObject.valueToString(root); return new StringValue(jsonRaw); } catch (JSONException ex) { throw new RuntimeException("Error while creating json", ex); } } - - private Object process(Value val) { - switch (val.type()) { - case Types.ARRAY: - return process((ArrayValue) val); - case Types.MAP: - return process((MapValue) val); - case Types.NUMBER: - return val.raw(); - case Types.STRING: - return val.asString(); - default: - return JSONObject.NULL; - } - } - - private Object process(MapValue map) { - final JSONObject result = new JSONObject(); - for (Map.Entry entry : map) { - final String key = entry.getKey().asString(); - final Object value = process(entry.getValue()); - result.put(key, value); - } - return result; - } - - private Object process(ArrayValue array) { - final JSONArray result = new JSONArray(); - for (Value value : array) { - result.put(process(value)); - } - return result; - } } \ No newline at end of file From fcd509efa8d4c103092cf2b5e3834e38d57d5594 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 2 Aug 2016 23:18:02 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20socket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +- .../annimon/ownlang/lib/modules/socket.java | 225 ++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/annimon/ownlang/lib/modules/socket.java diff --git a/build.gradle b/build.gradle index 02e8725..c7e17fd 100644 --- a/build.gradle +++ b/build.gradle @@ -53,7 +53,9 @@ task proguard(dependsOn: dist, type: proguard.gradle.ProGuardTask) { } dependencies { - compile 'com.squareup.okhttp3:okhttp:3.4.1' + compile ('io.socket:socket.io-client:0.7.0') { + exclude group: 'org.json', module: 'json' + } compile 'org.json:json:20160212' testCompile 'junit:junit:4.12' diff --git a/src/main/java/com/annimon/ownlang/lib/modules/socket.java b/src/main/java/com/annimon/ownlang/lib/modules/socket.java new file mode 100644 index 0000000..8781fe0 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/lib/modules/socket.java @@ -0,0 +1,225 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.annotations.ConstantInitializer; +import com.annimon.ownlang.exceptions.TypeException; +import com.annimon.ownlang.lib.*; +import io.socket.client.IO; +import io.socket.client.Socket; +import java.net.URISyntaxException; + +/** + * socket.io module. + * + * @author aNNiMON + */ +@ConstantInitializer +public final class socket implements Module { + + public static void initConstants() { + Variables.define("EVENT_CONNECT", new StringValue(Socket.EVENT_CONNECT)); + Variables.define("EVENT_CONNECTING", new StringValue(Socket.EVENT_CONNECTING)); + Variables.define("EVENT_CONNECT_ERROR", new StringValue(Socket.EVENT_CONNECT_ERROR)); + Variables.define("EVENT_CONNECT_TIMEOUT", new StringValue(Socket.EVENT_CONNECT_TIMEOUT)); + Variables.define("EVENT_DISCONNECT", new StringValue(Socket.EVENT_DISCONNECT)); + Variables.define("EVENT_ERROR", new StringValue(Socket.EVENT_ERROR)); + Variables.define("EVENT_MESSAGE", new StringValue(Socket.EVENT_MESSAGE)); + Variables.define("EVENT_PING", new StringValue(Socket.EVENT_PING)); + Variables.define("EVENT_PONG", new StringValue(Socket.EVENT_PONG)); + Variables.define("EVENT_RECONNECT", new StringValue(Socket.EVENT_RECONNECT)); + Variables.define("EVENT_RECONNECTING", new StringValue(Socket.EVENT_RECONNECTING)); + Variables.define("EVENT_RECONNECT_ATTEMPT", new StringValue(Socket.EVENT_RECONNECT_ATTEMPT)); + Variables.define("EVENT_RECONNECT_ERROR", new StringValue(Socket.EVENT_RECONNECT_ERROR)); + Variables.define("EVENT_RECONNECT_FAILED", new StringValue(Socket.EVENT_RECONNECT_FAILED)); + } + + @Override + public void init() { + initConstants(); + Functions.set("newSocket", socket::newSocket); + } + + private static Value newSocket(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + + try { + final String url = args[0].asString(); + if (args.length == 1) { + return new SocketValue(IO.socket(url)); + } + + if (args[1].type() != Types.MAP) { + throw new TypeException("Map expected in second argument"); + } + IO.Options options = parseOptions((MapValue) args[1]); + return new SocketValue(IO.socket(url, options)); + } catch (URISyntaxException ue) { + return NumberValue.MINUS_ONE; + } + } + + private static class SocketValue extends MapValue { + + private final Socket socket; + + public SocketValue(Socket socket) { + super(11); + this.socket = socket; + init(); + } + + private void init() { + set("close", this::close); + set("connect", this::connect); + set("connected", this::connected); + set("disconnect", this::disconnect); + set("emit", this::emit); + set("hasListeners", this::hasListeners); + set("id", this::id); + set("off", this::off); + set("on", this::on); + set("once", this::once); + set("open", this::open); + set("send", this::send); + } + + private Value close(Value... args) { + socket.close(); + return this; + } + + private Value connect(Value... args) { + socket.connect(); + return this; + } + + private Value connected(Value... args) { + return NumberValue.fromBoolean(socket.connected()); + } + + private Value disconnect(Value... args) { + socket.disconnect(); + return this; + } + + private Value hasListeners(Value... args) { + Arguments.check(1, args.length); + return NumberValue.fromBoolean( + socket.hasListeners(args[0].asString()) + ); + } + + private Value emit(Value... args) { + Arguments.checkOrOr(2, 3, args.length); + final String event = args[0].asString(); + final Value value = args[1]; + if (args.length == 3) { + // TODO ack + } + socket.emit(event, ValueUtils.toObject(value)); + return this; + } + + private Value id(Value... args) { + return new StringValue(socket.id()); + } + + private Value off(Value... args) { + Arguments.checkOrOr(0, 1, args.length); + if (args.length == 1) { + socket.off(args[0].asString()); + } else { + socket.off(); + } + return this; + } + + private Value on(Value... args) { + Arguments.check(2, args.length); + final String event = args[0].asString(); + final Function listener = ((FunctionValue) args[1]).getValue(); + socket.on(event, sArgs -> { + executeSocketListener(listener, sArgs); + }); + return this; + } + + private Value once(Value... args) { + Arguments.check(2, args.length); + final String event = args[0].asString(); + final Function listener = ((FunctionValue) args[1]).getValue(); + socket.once(event, sArgs -> { + executeSocketListener(listener, sArgs); + }); + return this; + } + + private Value open(Value... args) { + socket.open(); + return this; + } + + private Value send(Value... args) { + Arguments.check(1, args.length); + socket.send(ValueUtils.toObject(args[0])); + return this; + } + + private void executeSocketListener(Function listener, Object[] sArgs) { + if (sArgs == null) { + listener.execute(new ArrayValue(0)); + } else { + int size = sArgs.length; + final Value[] fArgs = new Value[size]; + for (int i = 0; i < size; i++) { + fArgs[i] = ValueUtils.toValue(sArgs[i]); + } + listener.execute(new ArrayValue(fArgs)); + } + } + } + + private static IO.Options parseOptions(MapValue map) { + final IO.Options result = new IO.Options(); + map.ifPresent("forceNew", v -> result.forceNew = v.asInt() != 0); + map.ifPresent("multiplex", v -> result.multiplex = v.asInt() != 0); + map.ifPresent("reconnection", v -> result.reconnection = v.asInt() != 0); + map.ifPresent("rememberUpgrade", v -> result.rememberUpgrade = v.asInt() != 0); + map.ifPresent("secure", v -> result.secure = v.asInt() != 0); + map.ifPresent("timestampRequests", v -> result.timestampRequests = v.asInt() != 0); + map.ifPresent("upgrade", v -> result.upgrade = v.asInt() != 0); + + map.ifPresent("policyPort", v -> result.policyPort = v.asInt()); + map.ifPresent("port", v -> result.port = v.asInt()); + map.ifPresent("reconnectionAttempts", v -> result.reconnectionAttempts = v.asInt()); + + map.ifPresent("reconnectionDelay", v -> result.reconnectionDelay = getNumber(v).longValue()); + map.ifPresent("reconnectionDelayMax", v -> result.reconnectionDelayMax = getNumber(v).longValue()); + map.ifPresent("timeout", v -> result.timeout = getNumber(v).longValue()); + + map.ifPresent("randomizationFactor", v -> result.randomizationFactor = v.asNumber()); + + map.ifPresent("host", v -> result.host = v.asString()); + map.ifPresent("hostname", v -> result.hostname = v.asString()); + map.ifPresent("path", v -> result.path = v.asString()); + map.ifPresent("query", v -> result.query = v.asString()); + map.ifPresent("timestampParam", v -> result.timestampParam = v.asString()); + + map.ifPresent("transports", v -> { + if (v.type() != Types.ARRAY) return; + + final ArrayValue arr = (ArrayValue) v; + final String[] values = new String[arr.size()]; + int index = 0; + for (Value value : arr) { + values[index++] = value.asString(); + } + result.transports = values; + }); + return result; + } + + private static Number getNumber(Value value) { + if (value.type() != Types.NUMBER) return value.asInt(); + return ((NumberValue) value).raw(); + } +}