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 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 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; } }