/*
* GNU LESSER GENERAL PUBLIC LICENSE Copyright (C) 2006 The Lobo Project
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Contact info: xamjadmin@users.sourceforge.net
*/
package org.cobra_grendel.js;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.cobra_grendel.util.Objects;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.WrappedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JavaFunctionObject extends ScriptableObject implements Function
{
private static final Logger LOGGER = LoggerFactory.getLogger(JavaFunctionObject.class);
/**
*
*/
private static final long serialVersionUID = 7988053493936296634L;
private final String className;
private final ArrayList methods = new ArrayList();
public JavaFunctionObject(final String name)
{
super();
className = name;
}
public void addMethod(final Method m)
{
methods.add(m);
}
@Override
public Object call(final Context cx, final Scriptable scope, final Scriptable thisObj, final Object[] args)
{
JavaObjectWrapper jcw = (JavaObjectWrapper) thisObj;
Method method = getBestMethod(args);
if (method == null)
{
throw new EvaluatorException("No method matching " + className + " with " + (args == null ? 0 : args.length) + " arguments.");
}
Class[] actualArgTypes = method.getParameterTypes();
int numParams = actualArgTypes.length;
Object[] actualArgs = args == null ? new Object[0] : new Object[numParams];
if (LOGGER.isInfoEnabled())
{
Object javaObject = jcw.getJavaObject();
LOGGER.info("call(): Calling method " + method.getName() + " on object " + javaObject + " of type " + getTypeName(javaObject));
}
JavaScript manager = JavaScript.getInstance();
for (int i = 0; i < numParams; i++)
{
Object arg = args[i];
Object actualArg = manager.getJavaObject(arg, actualArgTypes[i]);
if (LOGGER.isInfoEnabled())
{
LOGGER.info("call(): For method=" + method.getName() + ": Converted arg=" + arg + " (type=" + getTypeName(arg) + ") into actualArg=" + actualArg + ". Type expected by method is " + actualArgTypes[i].getName() + ".");
}
actualArgs[i] = actualArg;
}
try
{
Object raw = method.invoke(jcw.getJavaObject(), actualArgs);
return manager.getJavascriptObject(raw, scope);
}
catch (IllegalAccessException iae)
{
throw new IllegalStateException("Unable to call " + className + ".", iae);
}
catch (InvocationTargetException ite)
{
throw new WrappedException(new InvocationTargetException(ite.getCause(), "Unable to call " + className + " on " + jcw.getJavaObject() + "."));
}
catch (IllegalArgumentException iae)
{
StringBuffer argTypes = new StringBuffer();
for (int i = 0; i < actualArgs.length; i++)
{
if (i > 0)
{
argTypes.append(", ");
}
argTypes.append(actualArgs[i] == null ? "<null>" : actualArgs[i].getClass().getName());
}
throw new WrappedException(new IllegalArgumentException("Unable to call " + className + ". Argument types: " + argTypes + ".", iae));
}
}
@Override
public Scriptable construct(final Context cx, final Scriptable scope, final Object[] args)
{
throw new UnsupportedOperationException();
}
private Method getBestMethod(final Object[] args)
{
ArrayList methods = this.methods;
int size = methods.size();
int matchingNumParams = 0;
Method matchingMethod = null;
for (int i = 0; i < size; i++)
{
Method m = (Method) methods.get(i);
Class[] parameterTypes = m.getParameterTypes();
if (args == null)
{
if (parameterTypes == null || parameterTypes.length == 0)
{
return m;
}
}
else if (parameterTypes != null && args.length >= parameterTypes.length)
{
if (Objects.areAssignableTo(args, parameterTypes))
{
return m;
}
if (matchingMethod == null || parameterTypes.length > matchingNumParams)
{
matchingNumParams = parameterTypes.length;
matchingMethod = m;
}
}
}
if (size == 0)
{
throw new IllegalStateException("zero methods");
}
return matchingMethod;
}
@Override
public String getClassName()
{
return className;
}
@Override
public java.lang.Object getDefaultValue(final java.lang.Class hint)
{
if (LOGGER.isInfoEnabled())
{
LOGGER.info("getDefaultValue(): hint=" + hint + ",this=" + this);
}
if (hint == null || String.class.equals(hint))
{
return "function " + className;
}
else
{
return super.getDefaultValue(hint);
}
}
private String getTypeName(final Object object)
{
return object == null ? "[null]" : object.getClass().getName();
}
}