net.sandius.rembulan.lib.luajava.LuaJavaLib Maven / Gradle / Ivy
Show all versions of rembulan-luajava-compat Show documentation
/*
* Copyright 2016 Miroslav Janíček
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sandius.rembulan.lib.luajava;
import net.sandius.rembulan.LuaRuntimeException;
import net.sandius.rembulan.Table;
import net.sandius.rembulan.TableFactory;
import net.sandius.rembulan.impl.UnimplementedFunction;
import net.sandius.rembulan.lib.Lib;
import net.sandius.rembulan.lib.impl.AbstractLibFunction;
import net.sandius.rembulan.lib.impl.ArgumentIterator;
import net.sandius.rembulan.runtime.ExecutionContext;
import net.sandius.rembulan.runtime.ResolvedControlThrowable;
import java.lang.reflect.InvocationTargetException;
public class LuaJavaLib extends Lib {
@Override
public String name() {
return "luajava";
}
@Override
public Table toTable(TableFactory tableFactory) {
Table t = tableFactory.newTable();
t.rawset("newInstance", NewInstance.INSTANCE);
t.rawset("bindClass", BindClass.INSTANCE);
t.rawset("new", New.INSTANCE);
t.rawset("createProxy", new UnimplementedFunction("luajava.createProxy"));
t.rawset("loadLib", new UnimplementedFunction("luajava.loadLib"));
return t;
}
/**
* {@code newInstance(className, ...)}
*
* This function creates a new Java object, and returns a Lua object that is a reference
* to the actual Java object. You can access this object with the regular syntax used
* to access object oriented functions in Lua objects.
*
* The first parameter is the name of the class to be instantiated. The other parameters
* are passed to the Java Class constructor.
*
* Example:
*
*
* obj = luajava.newInstance("java.lang.Object")
* -- obj is now a reference to the new object
* -- created and any of its methods can be accessed.
*
* -- this creates a string tokenizer to the "a,b,c,d"
* -- string using "," as the token separator.
* strTk = luajava.newInstance("java.util.StringTokenizer",
* "a,b,c,d", ",")
* while strTk:hasMoreTokens() do
* print(strTk:nextToken())
* end
*
*
* The code above should print the following on the screen:
*
*
* a
* b
* c
* d
*
*/
static class NewInstance extends AbstractLibFunction {
static final NewInstance INSTANCE = new NewInstance();
@Override
protected String name() {
return "newInstance";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args)
throws ResolvedControlThrowable {
String className = args.nextString().toString();
Object[] ctorArgs = args.getTail();
final ObjectWrapper instance;
try {
instance = ObjectWrapper.newInstance(className, ctorArgs);
}
catch (ClassNotFoundException | MethodSelectionException | IllegalAccessException
| InstantiationException | InvocationTargetException ex) {
throw new LuaRuntimeException(ex);
}
context.getReturnBuffer().setTo(instance);
}
}
/**
* {@code bindClass(className)}
*
* This function retrieves a Java class corresponding to {@code className}.
* The returned object can be used to access static fields and methods of the corresponding
* class.
*
* Example:
*
*
* sys = luajava.bindClass("java.lang.System")
* print ( sys:currentTimeMillis() )
*
* -- this prints the time returned by the function.
*
*/
static class BindClass extends AbstractLibFunction {
static final BindClass INSTANCE = new BindClass();
@Override
protected String name() {
return "bindClass";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
String className = args.nextString().toString();
final ClassWrapper wrapper;
try {
wrapper = ClassWrapper.of(className);
}
catch (ClassNotFoundException ex) {
throw new LuaRuntimeException(ex);
}
context.getReturnBuffer().setTo(wrapper);
}
}
/**
* {@code new(javaClass)}
*
* This function receives a java.lang.Class and returns a new instance of this class.
*
* {@code new} works just like {@code newInstance}, but the first argument is an instance of the
* class.
*
* Example:
*
*
* str = luajava.bindClass("java.lang.String")
* strInstance = luajava.new(str)
*
*/
static class New extends AbstractLibFunction {
static final New INSTANCE = new New();
@Override
protected String name() {
return "new";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
ClassWrapper classWrapper = args.nextUserdata(ClassWrapper.staticTypeName(), ClassWrapper.class);
final ObjectWrapper instance;
try {
instance = ObjectWrapper.newInstance(classWrapper.get(), new Object[] { });
}
catch (MethodSelectionException | IllegalAccessException | InstantiationException
| InvocationTargetException ex) {
throw new LuaRuntimeException(ex);
}
context.getReturnBuffer().setTo(instance);
}
}
/**
* {@code createProxy(interfaceNames, luaObject)}
*
* We can also, instead of creating a Java object to be manipulated by Lua, create a Lua
* object that will be manipulated by Java. We can do that in LuaJava by creating a proxy to
* that object. This is done by the {@code createProxy} function.
*
* The function {@code createProxy} returns a java Object reference that can be used as
* an implementation of the given interface.
*
* {@code createProxy} receives a string that contains the names of the interfaces to
* be implemented, separated by a comma ({@code ,}), and a Lua object that is the interface
* implementation.
*
* Example:
*
*
* button = luajava.newInstance("java.awt.Button", "execute")
* button_cb = {}
* function button_cb.actionPerformed(ev)
* -- ...
* end
*
* buttonProxy = luajava.createProxy("java.awt.ActionListener",
* button_cb)
*
* button:addActionListener(buttonProxy)
*
*
* We can use Lua scripts to write implementations only for Java interfaces.
*/
static class CreateProxy extends AbstractLibFunction {
static final CreateProxy INSTANCE = new CreateProxy();
@Override
protected String name() {
return "createProxy";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
throw new UnsupportedOperationException("not implemented: " + name()); // TODO
}
}
/**
* {@code loadLib(className, methodName)}
*
* loadLib is a function that has a use similar to Lua's {@code loadlib} function.
* The purpose of this function is to allow users to write libraries in Java and then load
* them into Lua.
*
* What {@code loadLib} does is call a static function in a given class and execute
* a given method, which should receive {@code LuaState} as parameter. If this function
* returns a integer, LuaJava takes it as the number of parameters returned by the the
* function, otherwise nothing is returned.
*
* The following Lua example can access the global {@code eg} created by the Java class
* {@code test.LoadLibExample}:
*
*
* luajava.loadLib("test.LoadLibExample", "open")
* eg.example(3)
*
*
* And this Java example implements the method {@code example}:
*
*
* public static int open(LuaState L) throws LuaException {
* L.newTable();
* L.pushValue(-1);
* L.setGlobal("eg");
*
* L.pushString("example");
*
* L.pushJavaFunction(new JavaFunction(L) {
* //
* // Example for loadLib.
* // Prints the time and the first parameter, if any.
* //
* public int execute() throws LuaException {
* System.out.println(new Date().toString());
*
* if (L.getTop() > 1) {
* System.out.println(getParam(2));
* }
*
* return 0;
* }
* });
*
* L.setTable(-3);
*
* return 1;
* }
*
*/
static class LoadLib extends AbstractLibFunction {
static final LoadLib INSTANCE = new LoadLib();
@Override
protected String name() {
return "loadLib";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
throw new UnsupportedOperationException("not implemented: " + name()); // TODO
}
}
}