mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 08:44:20 +03:00
Merge socket-module into latest
This commit is contained in:
commit
da36a3417c
@ -53,7 +53,9 @@ task proguard(dependsOn: dist, type: proguard.gradle.ProGuardTask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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'
|
compile 'org.json:json:20160212'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
|
@ -5,6 +5,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -31,6 +32,18 @@ public class MapValue implements Value, Iterable<Map.Entry<Value, Value>> {
|
|||||||
this.map = map;
|
this.map = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean ifPresent(String key, Consumer<Value> consumer) {
|
||||||
|
return ifPresent(new StringValue(key), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ifPresent(Value key, Consumer<Value> consumer) {
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
consumer.accept(map.get(key));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public ArrayValue toPairs() {
|
public ArrayValue toPairs() {
|
||||||
final int size = map.size();
|
final int size = map.size();
|
||||||
final ArrayValue result = new ArrayValue(size);
|
final ArrayValue result = new ArrayValue(size);
|
||||||
|
83
src/main/java/com/annimon/ownlang/lib/ValueUtils.java
Normal file
83
src/main/java/com/annimon/ownlang/lib/ValueUtils.java
Normal file
@ -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<Value, Value> 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<String> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,12 @@
|
|||||||
package com.annimon.ownlang.lib.modules.functions;
|
package com.annimon.ownlang.lib.modules.functions;
|
||||||
|
|
||||||
import com.annimon.ownlang.lib.*;
|
import com.annimon.ownlang.lib.Arguments;
|
||||||
import java.util.Iterator;
|
import com.annimon.ownlang.lib.Function;
|
||||||
import org.json.*;
|
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 {
|
public final class json_decode implements Function {
|
||||||
|
|
||||||
@ -12,50 +16,9 @@ public final class json_decode implements Function {
|
|||||||
try {
|
try {
|
||||||
final String jsonRaw = args[0].asString();
|
final String jsonRaw = args[0].asString();
|
||||||
final Object root = new JSONTokener(jsonRaw).nextValue();
|
final Object root = new JSONTokener(jsonRaw).nextValue();
|
||||||
return process(root);
|
return ValueUtils.toValue(root);
|
||||||
} catch (JSONException ex) {
|
} catch (JSONException ex) {
|
||||||
throw new RuntimeException("Error while parsing json", 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<String> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,8 +1,13 @@
|
|||||||
package com.annimon.ownlang.lib.modules.functions;
|
package com.annimon.ownlang.lib.modules.functions;
|
||||||
|
|
||||||
import com.annimon.ownlang.lib.*;
|
import com.annimon.ownlang.lib.Arguments;
|
||||||
import java.util.Map;
|
import com.annimon.ownlang.lib.Function;
|
||||||
import org.json.*;
|
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 {
|
public final class json_encode implements Function {
|
||||||
|
|
||||||
@ -10,44 +15,11 @@ public final class json_encode implements Function {
|
|||||||
public Value execute(Value... args) {
|
public Value execute(Value... args) {
|
||||||
Arguments.check(1, args.length);
|
Arguments.check(1, args.length);
|
||||||
try {
|
try {
|
||||||
final Object root = process(args[0]);
|
final Object root = ValueUtils.toObject(args[0]);
|
||||||
final String jsonRaw = JSONObject.valueToString(root);
|
final String jsonRaw = JSONObject.valueToString(root);
|
||||||
return new StringValue(jsonRaw);
|
return new StringValue(jsonRaw);
|
||||||
} catch (JSONException ex) {
|
} catch (JSONException ex) {
|
||||||
throw new RuntimeException("Error while creating json", 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<Value, Value> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
225
src/main/java/com/annimon/ownlang/lib/modules/socket.java
Normal file
225
src/main/java/com/annimon/ownlang/lib/modules/socket.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user