com.gargoylesoftware.htmlunit.javascript.MethodWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of htmlunit Show documentation
Show all versions of htmlunit Show documentation
A headless browser intended for use in testing web-based applications.
/*
* Copyright (c) 2002-2020 Gargoyle Software Inc.
*
* 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 com.gargoylesoftware.htmlunit.javascript;
import java.lang.reflect.Method;
import org.apache.commons.lang3.ArrayUtils;
import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.Function;
import net.sourceforge.htmlunit.corejs.javascript.FunctionObject;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
/**
* Wraps a Java method to make it available as a JavaScript function
* (more flexible than Rhino's {@link FunctionObject}.
*
* @author Marc Guillemot
*/
public class MethodWrapper extends ScriptableObject implements Function {
private final Class> clazz_;
private final Method method_;
private final int[] jsTypeTags_;
/**
* Facility constructor to wrap a method without arguments.
* @param methodName the name of the method to wrap
* @param clazz the class declaring the method
* @throws NoSuchMethodException if the method is no found
*/
MethodWrapper(final String methodName, final Class> clazz) throws NoSuchMethodException {
this(methodName, clazz, ArrayUtils.EMPTY_CLASS_ARRAY);
}
/**
* Wraps a method as a JavaScript function.
* @param methodName the name of the method to wrap
* @param clazz the class declaring the method
* @param parameterTypes the types of the method's parameter
* @throws NoSuchMethodException if the method is no found
*/
MethodWrapper(final String methodName, final Class> clazz, final Class>[] parameterTypes)
throws NoSuchMethodException {
clazz_ = clazz;
method_ = clazz.getMethod(methodName, parameterTypes);
jsTypeTags_ = new int[parameterTypes.length];
int i = 0;
for (final Class> klass : parameterTypes) {
jsTypeTags_[i++] = FunctionObject.getTypeTag(klass);
}
}
/**
* @see net.sourceforge.htmlunit.corejs.javascript.ScriptableObject#getClassName()
* @return a name based on the method name
*/
@Override
public String getClassName() {
return "function " + method_.getName();
}
/**
* {@inheritDoc}
*/
@Override
public Object call(final Context context, final Scriptable scope, final Scriptable thisObj, final Object[] args) {
final Object javaResp;
if (thisObj instanceof ScriptableWrapper) {
final ScriptableWrapper wrapper = (ScriptableWrapper) thisObj;
final Object wrappedObject = wrapper.getWrappedObject();
if (clazz_.isInstance(wrappedObject)) {
// convert arguments
final Object[] javaArgs = convertJSArgsToJavaArgs(context, scope, args);
try {
javaResp = method_.invoke(wrappedObject, javaArgs);
}
catch (final Exception e) {
throw Context.reportRuntimeError("Exception calling wrapped function "
+ method_.getName() + ": " + e.getMessage());
}
}
else {
throw buildInvalidCallException(thisObj);
}
}
else {
throw buildInvalidCallException(thisObj);
}
final Object jsResp = Context.javaToJS(javaResp, ScriptableObject.getTopLevelScope(scope));
return jsResp;
}
private RuntimeException buildInvalidCallException(final Scriptable thisObj) {
return Context.reportRuntimeError("Function " + method_.getName()
+ " called on incompatible object: " + thisObj);
}
/**
* Converts JavaScript arguments to Java arguments.
* @param context the current context
* @param scope the current scope
* @param jsArgs the JavaScript arguments
* @return the java arguments
*/
Object[] convertJSArgsToJavaArgs(final Context context, final Scriptable scope, final Object[] jsArgs) {
if (jsArgs.length != jsTypeTags_.length) {
throw Context.reportRuntimeError("Bad number of parameters for function " + method_.getName()
+ ": expected " + jsTypeTags_.length + " got " + jsArgs.length);
}
final Object[] javaArgs = new Object[jsArgs.length];
int i = 0;
for (final Object object : jsArgs) {
javaArgs[i] = FunctionObject.convertArg(context, scope, object, jsTypeTags_[i++]);
}
return javaArgs;
}
/**
* {@inheritDoc}
*/
@Override
public Scriptable construct(final Context context, final Scriptable scope, final Object[] args) {
throw Context.reportRuntimeError("Function " + method_.getName() + " can't be used as a constructor");
}
}