org.mozilla.javascript.InterfaceAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
The Rhino JavaScript Engine for Java
The newest version!
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;
import java.lang.reflect.Method;
/**
* Adapter to use JS function as implementation of Java interfaces with
* single method or multiple methods with the same signature.
*/
public class InterfaceAdapter
{
private final Object proxyHelper;
/**
* Make glue object implementing interface cl that will
* call the supplied JS function when called.
* Only interfaces were all methods have the same signature is supported.
*
* @return The glue object or null if cl is not interface or
* has methods with different signatures.
*/
static Object create(Context cx, Class> cl, ScriptableObject object)
{
if (!cl.isInterface()) throw new IllegalArgumentException();
Scriptable topScope = ScriptRuntime.getTopCallScope(cx);
ClassCache cache = ClassCache.get(topScope);
InterfaceAdapter adapter;
adapter = (InterfaceAdapter)cache.getInterfaceAdapter(cl);
ContextFactory cf = cx.getFactory();
if (adapter == null) {
Method[] methods = cl.getMethods();
if ( object instanceof Callable) {
// Check if interface can be implemented by a single function.
// We allow this if the interface has only one method or multiple
// methods with the same name (in which case they'd result in
// the same function to be invoked anyway).
int length = methods.length;
if (length == 0) {
throw Context.reportRuntimeError1(
"msg.no.empty.interface.conversion", cl.getName());
}
if (length > 1) {
String methodName = methods[0].getName();
for (int i = 1; i < length; i++) {
if (!methodName.equals(methods[i].getName())) {
throw Context.reportRuntimeError1(
"msg.no.function.interface.conversion",
cl.getName());
}
}
}
}
adapter = new InterfaceAdapter(cf, cl);
cache.cacheInterfaceAdapter(cl, adapter);
}
return VMBridge.instance.newInterfaceProxy(
adapter.proxyHelper, cf, adapter, object, topScope);
}
private InterfaceAdapter(ContextFactory cf, Class> cl)
{
this.proxyHelper
= VMBridge.instance.getInterfaceProxyHelper(
cf, new Class[] { cl });
}
public Object invoke(ContextFactory cf,
final Object target,
final Scriptable topScope,
final Object thisObject,
final Method method,
final Object[] args)
{
ContextAction action = new ContextAction() {
public Object run(Context cx)
{
return invokeImpl(cx, target, topScope, thisObject, method, args);
}
};
return cf.call(action);
}
Object invokeImpl(Context cx,
Object target,
Scriptable topScope,
Object thisObject,
Method method,
Object[] args)
{
Callable function;
if (target instanceof Callable) {
function = (Callable)target;
} else {
Scriptable s = (Scriptable)target;
String methodName = method.getName();
Object value = ScriptableObject.getProperty(s, methodName);
if (value == ScriptableObject.NOT_FOUND) {
// We really should throw an error here, but for the sake of
// compatibility with JavaAdapter we silently ignore undefined
// methods.
Context.reportWarning(ScriptRuntime.getMessage1(
"msg.undefined.function.interface", methodName));
Class> resultType = method.getReturnType();
if (resultType == Void.TYPE) {
return null;
} else {
return Context.jsToJava(null, resultType);
}
}
if (!(value instanceof Callable)) {
throw Context.reportRuntimeError1(
"msg.not.function.interface",methodName);
}
function = (Callable)value;
}
WrapFactory wf = cx.getWrapFactory();
if (args == null) {
args = ScriptRuntime.emptyArgs;
} else {
for (int i = 0, N = args.length; i != N; ++i) {
Object arg = args[i];
// neutralize wrap factory java primitive wrap feature
if (!(arg instanceof String || arg instanceof Number
|| arg instanceof Boolean)) {
args[i] = wf.wrap(cx, topScope, arg, null);
}
}
}
Scriptable thisObj = wf.wrapAsJavaObject(cx, topScope, thisObject, null);
Object result = function.call(cx, topScope, thisObj, args);
Class> javaResultType = method.getReturnType();
if (javaResultType == Void.TYPE) {
result = null;
} else {
result = Context.jsToJava(result, javaResultType);
}
return result;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy