/* * 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: lobochief@users.sourceforge.net */ package com.nvarghese.beowulf.common.cobra.js; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; 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 com.nvarghese.beowulf.common.cobra.util.Objects; public class JavaFunctionObject extends ScriptableObject implements Function { private static final Logger logger = Logger.getLogger(JavaFunctionObject.class.getName()); private static final boolean loggableInfo = logger.isLoggable(Level.INFO); private final String className; private final transient ArrayList methods = new ArrayList(); public JavaFunctionObject(final String name) { super(); this.className = name; } public void addMethod(final Method m) { this.methods.add(m); } public String getClassName() { return this.className; } private String getTypeName(final Object object) { return object == null ? "[null]" : object.getClass().getName(); } 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; } public Object call(final Context cx, final Scriptable scope, final Scriptable thisObj, final Object[] args) { JavaObjectWrapper jcw = (JavaObjectWrapper) thisObj; Method method = this.getBestMethod(args); if (method == null) { throw new EvaluatorException("No method matching " + this.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]; boolean linfo = loggableInfo; if (linfo) { Object javaObject = jcw.getJavaObject(); logger.info("call(): Calling method " + method.getName() + " on object " + javaObject + " of type " + this.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 (linfo) { logger.info("call(): For method=" + method.getName() + ": Converted arg=" + arg + " (type=" + this.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 " + this.className + ".", iae); } catch (InvocationTargetException ite) { throw new WrappedException(new InvocationTargetException(ite.getCause(), "Unable to call " + this.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 " + this.className + ". Argument types: " + argTypes + ".", iae)); } } public java.lang.Object getDefaultValue(final java.lang.Class hint) { if (loggableInfo) { logger.info("getDefaultValue(): hint=" + hint + ",this=" + this); } if (hint == null || String.class.equals(hint)) { return "function " + this.className; } else { return super.getDefaultValue(hint); } } public Scriptable construct(final Context cx, final Scriptable scope, final Object[] args) { throw new UnsupportedOperationException(); } }