first commit
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package org.openautonomousconnection.luascript.events;
|
||||
|
||||
import org.luaj.vm2.LuaTable;
|
||||
import org.luaj.vm2.LuaValue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Coerces common Java values into Lua values.
|
||||
*/
|
||||
public final class JavaToLua {
|
||||
|
||||
private JavaToLua() { }
|
||||
|
||||
public static LuaValue coerce(Object v) {
|
||||
if (v == null) return LuaValue.NIL;
|
||||
if (v instanceof LuaValue lv) return lv;
|
||||
if (v instanceof String s) return LuaValue.valueOf(s);
|
||||
if (v instanceof Boolean b) return LuaValue.valueOf(b);
|
||||
if (v instanceof Integer i) return LuaValue.valueOf(i);
|
||||
if (v instanceof Long l) return LuaValue.valueOf(l);
|
||||
if (v instanceof Float f) return LuaValue.valueOf(f);
|
||||
if (v instanceof Double d) return LuaValue.valueOf(d);
|
||||
|
||||
if (v instanceof Map<?, ?> m) {
|
||||
LuaTable t = new LuaTable();
|
||||
for (Map.Entry<?, ?> e : m.entrySet()) {
|
||||
Object k = e.getKey();
|
||||
if (k == null) continue;
|
||||
t.set(String.valueOf(k), coerce(e.getValue()));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
if (v instanceof List<?> list) {
|
||||
LuaTable t = new LuaTable();
|
||||
int i = 1;
|
||||
for (Object o : list) {
|
||||
t.set(i++, coerce(o));
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
return LuaValue.valueOf(String.valueOf(v));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package org.openautonomousconnection.luascript.events;
|
||||
|
||||
import org.luaj.vm2.Globals;
|
||||
import org.luaj.vm2.LuaError;
|
||||
import org.luaj.vm2.LuaFunction;
|
||||
import org.luaj.vm2.LuaTable;
|
||||
import org.luaj.vm2.LuaValue;
|
||||
import org.luaj.vm2.Varargs;
|
||||
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
|
||||
import org.openautonomousconnection.luascript.security.LuaSecurityManager;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Stores bindings (elementId,eventName -> handlerPath) and dispatches events into Lua.
|
||||
*/
|
||||
public final class LuaEventDispatcher {
|
||||
|
||||
private final Globals globals;
|
||||
private final LuaSecurityManager securityManager;
|
||||
private final LuaExecutionPolicy policy;
|
||||
private final ConcurrentHashMap<String, String> bindings = new ConcurrentHashMap<>();
|
||||
|
||||
public LuaEventDispatcher(Globals globals, LuaSecurityManager securityManager, LuaExecutionPolicy policy) {
|
||||
this.globals = Objects.requireNonNull(globals, "globals");
|
||||
this.securityManager = Objects.requireNonNull(securityManager, "securityManager");
|
||||
this.policy = Objects.requireNonNull(policy, "policy");
|
||||
}
|
||||
|
||||
public void bind(String elementId, String eventName, String handlerPath) {
|
||||
Objects.requireNonNull(elementId, "elementId");
|
||||
Objects.requireNonNull(eventName, "eventName");
|
||||
Objects.requireNonNull(handlerPath, "handlerPath");
|
||||
|
||||
String k = key(elementId, UiEventRegistry.normalize(eventName));
|
||||
bindings.put(k, handlerPath);
|
||||
}
|
||||
|
||||
public void unbind(String elementId, String eventName) {
|
||||
Objects.requireNonNull(elementId, "elementId");
|
||||
Objects.requireNonNull(eventName, "eventName");
|
||||
bindings.remove(key(elementId, UiEventRegistry.normalize(eventName)));
|
||||
}
|
||||
|
||||
public boolean hasBinding(String elementId, String eventName) {
|
||||
return bindings.containsKey(key(elementId, UiEventRegistry.normalize(eventName)));
|
||||
}
|
||||
|
||||
public boolean dispatch(UiEvent event) {
|
||||
Objects.requireNonNull(event, "event");
|
||||
|
||||
String k = key(event.targetId(), UiEventRegistry.normalize(event.type()));
|
||||
String handlerPath = bindings.get(k);
|
||||
if (handlerPath == null) return false;
|
||||
|
||||
LuaValue fn = resolvePath(handlerPath);
|
||||
if (fn.isnil() || !fn.isfunction()) {
|
||||
throw new LuaError("Handler is not a function: " + handlerPath);
|
||||
}
|
||||
|
||||
LuaValue eventTable = toLuaEvent(event);
|
||||
Varargs args = LuaValue.varargsOf(new LuaValue[]{eventTable});
|
||||
|
||||
securityManager.callGuarded(globals, (LuaFunction) fn, args, policy);
|
||||
return true;
|
||||
}
|
||||
|
||||
private LuaValue resolvePath(String path) {
|
||||
String[] parts = path.split("\\.");
|
||||
LuaValue cur = globals;
|
||||
for (String p : parts) {
|
||||
String key = p.trim();
|
||||
if (key.isEmpty()) throw new LuaError("Invalid handler path: " + path);
|
||||
cur = cur.get(key);
|
||||
if (cur.isnil()) return LuaValue.NIL;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
private LuaValue toLuaEvent(UiEvent event) {
|
||||
LuaTable t = new LuaTable();
|
||||
t.set("target", LuaValue.valueOf(event.targetId()));
|
||||
t.set("type", LuaValue.valueOf(event.type()));
|
||||
|
||||
LuaTable d = new LuaTable();
|
||||
for (Map.Entry<String, Object> e : event.data().entrySet()) {
|
||||
String k = e.getKey();
|
||||
if (k == null) continue;
|
||||
d.set(k, JavaToLua.coerce(e.getValue()));
|
||||
}
|
||||
t.set("data", d);
|
||||
return t;
|
||||
}
|
||||
|
||||
private static String key(String elementId, String event) {
|
||||
return elementId + "\n" + event;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.openautonomousconnection.luascript.events;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Event passed from host into Lua.
|
||||
*/
|
||||
public final class UiEvent {
|
||||
|
||||
private final String targetId;
|
||||
private final String type;
|
||||
private final Map<String, Object> data;
|
||||
|
||||
public UiEvent(String targetId, String type, Map<String, Object> data) {
|
||||
this.targetId = Objects.requireNonNull(targetId, "targetId");
|
||||
this.type = Objects.requireNonNull(type, "type");
|
||||
this.data = (data == null) ? Collections.emptyMap() : Collections.unmodifiableMap(data);
|
||||
}
|
||||
|
||||
public String targetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Map<String, Object> data() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.openautonomousconnection.luascript.events;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Normalizes event names.
|
||||
*/
|
||||
public final class UiEventRegistry {
|
||||
|
||||
private UiEventRegistry() { }
|
||||
|
||||
public static String normalize(String eventName) {
|
||||
if (eventName == null) throw new IllegalArgumentException("eventName is null");
|
||||
String e = eventName.trim().toLowerCase(Locale.ROOT);
|
||||
if (e.isEmpty()) throw new IllegalArgumentException("eventName is empty");
|
||||
return e;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user