content: /*
   * $Id: LuaJavaAPI.java,v 1.4 2006/12/22 14:06:40 thiago Exp $
   * Copyright (C) 2003-2007 Kepler Project.
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  package org.keplerproject.luajava;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  
  /**
   * Class that contains functions accessed by lua.
   *
   * @author Thiago Ponte
   */
  public final class LuaJavaAPI
  {
  
    private LuaJavaAPI()
    {
    }
  
    /**
     * Java implementation of the metamethod __index
     *
     * @param luaState int that indicates the state used
     * @param obj Object to be indexed
     * @param methodName the name of the method
     * @return number of returned objects
     */
    public static int objectIndex(int luaState, Object obj, String methodName)
            throws LuaException
    {
      LuaState L = LuaStateFactory.getExistingState(luaState);
  
      synchronized (L)
      {
        int top = L.getTop();
  
        Object[] objs = new Object[top - 1];
  
        Class clazz;
  
        if (obj instanceof Class)
        {
          clazz = (Class) obj;
        }
        else
        {
          clazz = obj.getClass();
        }
  
        Method[] methods = clazz.getMethods();
        Method method = null;
  
        // gets method and arguments
        for (int i = 0; i < methods.length; i++)
        {
          if (!methods[i].getName().equals(methodName))
  
            continue;
  
          Class[] parameters = methods[i].getParameterTypes();
          if (parameters.length != top - 1)
            continue;
  
          boolean okMethod = true;
  
          for (int j = 0; j < parameters.length; j++)
          {
            try
            {
              objs[j] = compareTypes(L, parameters[j], j + 2);
            }
            catch (Exception e)
            {
              okMethod = false;
              break;
            }
          }
  
          if (okMethod)
          {
            method = methods[i];
            break;
          }
  
        }
  
        // If method is null means there isn't one receiving the given arguments
        if (method == null)
        {
          throw new LuaException("Invalid method call. No such method.");
        }
  
        Object ret;
        try
        {
          if(Modifier.isPublic(method.getModifiers()))
          {
            method.setAccessible(true);
          }
  
          if (obj instanceof Class)
          {
            ret = method.invoke(null, objs);
          }
          else
          {
            ret = method.invoke(obj, objs);
          }
        }
        catch (Exception e)
        {
          throw new LuaException(e);
        }
  
        // Void function returns null
        if (ret == null)
        {
          return 0;
        }
  
        // push result
        L.pushObjectValue(ret);
  
        return 1;
      }
    }
  
    /**
     * Java function to be called when a java Class metamethod __index is called.
     * This function returns 1 if there is a field with searchName and 2 if there
     * is a method if the searchName
     *
     * @param luaState int that represents the state to be used
     * @param clazz class to be indexed
     * @param searchName name of the field or method to be accessed
     * @return number of returned objects
     * @throws LuaException
     */
    public static int classIndex(int luaState, Class clazz, String searchName)
            throws LuaException
    {
      synchronized (LuaStateFactory.getExistingState(luaState))
      {
        int res;
  
        res = checkField(luaState, clazz, searchName);
  
        if (res != 0)
        {
          return 1;
        }
  
        res = checkMethod(luaState, clazz, searchName);
  
        if (res != 0)
        {
          return 2;
        }
  
        return 0;
      }
    }
  
    /**
     * Pushes a new instance of a java Object of the type className
     *
     * @param luaState int that represents the state to be used
     * @param className name of the class
     * @return number of returned objects
     * @throws LuaException
     */
    public static int javaNewInstance(int luaState, String className)
            throws LuaException
    {
      LuaState L = LuaStateFactory.getExistingState(luaState);
  
      synchronized (L)
      {
        Class clazz;
        try
        {
          clazz = Class.forName(className);
        }
        catch (ClassNotFoundException e)
        {
          throw new LuaException(e);
        }
        Object ret = getObjInstance(L, clazz);
  
        L.pushJavaObject(ret);
  
        return 1;
      }
    }
  
    /**
     * javaNew returns a new instance of a given clazz
     *
     * @param luaState int that represents the state to be used
     * @param clazz class to be instanciated
     * @return number of returned objects
     * @throws LuaException
     */
    public static int javaNew(int luaState, Class clazz) throws LuaException
    {
      LuaState L = LuaStateFactory.getExistingState(luaState);
  
      synchronized (L)
      {
        Object ret = getObjInstance(L, clazz);
  
        L.pushJavaObject(ret);
  
        return 1;
      }
    }
  
    /**
     * Calls the static method <code>methodName</code> in class <code>className</code>
     * that receives a LuaState as first parameter.
     * @param luaState int that represents the state to be used
     * @param className name of the class that has the open library method
     * @param methodName method to open library
     * @return number of returned objects
     * @throws LuaException
     */
    public static int javaLoadLib(int luaState, String className, String methodName)
            throws LuaException
    {
      LuaState L = LuaStateFactory.getExistingState(luaState);
  
      synchronized (L)
      {
        Class clazz;
        try
        {
          clazz = Class.forName(className);
        }
        catch (ClassNotFoundException e)
        {
          throw new LuaException(e);
        }
  
        try
        {
          Method mt = clazz.getMethod(methodName, new Class[] {LuaState.class});
          Object obj = mt.invoke(null, new Object[] {L});
  
          if (obj != null && obj instanceof Integer)
          {
            return ((Integer) obj).intValue();
          }
          else
            return 0;
        }
        catch (Exception e)
        {
          throw new LuaException("Error on calling method. Library could not be loaded. " + e.getMessage());
        }
      }
    }
  
    private static Object getObjInstance(LuaState L, Class clazz)
            throws LuaException
    {
      synchronized (L)
      {
        int top = L.getTop();
  
        Object[] objs = new Object[top - 1];
  
        Constructor[] constructors = clazz.getConstructors();
        Constructor constructor = null;
  
        // gets method and arguments
        for (int i = 0; i < constructors.length; i++)
        {
          Class[] parameters = constructors[i].getParameterTypes();
          if (parameters.length != top - 1)
            continue;
  
          boolean okConstruc = true;
  
          for (int j = 0; j < parameters.length; j++)
          {
            try
            {
              objs[j] = compareTypes(L, parameters[j], j + 2);
            }
            catch (Exception e)
            {
              okConstruc = false;
              break;
            }
          }
  
          if (okConstruc)
          {
            constructor = constructors[i];
            break;
          }
  
        }
  
        // If method is null means there isn't one receiving the given arguments
        if (constructor == null)
        {
          throw new LuaException("Invalid method call. No such method.");
        }
  
        Object ret;
        try
        {
          ret = constructor.newInstance(objs);
        }
        catch (Exception e)
        {
          throw new LuaException(e);
        }
  
        if (ret == null)
        {
          throw new LuaException("Couldn't instantiate java Object");
        }
  
        return ret;
      }
    }
  
    /**
     * Checks if there is a field on the obj with the given name
     *
     * @param luaState int that represents the state to be used
     * @param obj object to be inspected
     * @param fieldName name of the field to be inpected
     * @return number of returned objects
     */
    public static int checkField(int luaState, Object obj, String fieldName)
            throws LuaException
    {
      LuaState L = LuaStateFactory.getExistingState(luaState);
  
      synchronized (L)
      {
        Field field = null;
        Class objClass;
  
        if (obj instanceof Class)
        {
          objClass = (Class) obj;
        }
        else
        {
          objClass = obj.getClass();
        }
  
        try
        {
          field = objClass.getField(fieldName);
        }
        catch (Exception e)
        {
          return 0;
        }
  
        if (field == null)
        {
          return 0;
        }
  
        Object ret = null;
        try
        {
          ret = field.get(obj);
        }
        catch (Exception e1)
        {
          return 0;
        }
  
        if (obj == null)
        {
          return 0;
        }
  
        L.pushObjectValue(ret);
  
        return 1;
      }
    }
  
    /**
     * Checks to see if there is a method with the given name.
     *
     * @param luaState int that represents the state to be used
     * @param obj object to be inspected
     * @param methodName name of the field to be inpected
     * @return number of returned objects
     */
    public static int checkMethod(int luaState, Object obj, String methodName)
    {
      LuaState L = LuaStateFactory.getExistingState(luaState);
  
      synchronized (L)
      {
        Class clazz;
  
        if (obj instanceof Class)
        {
          clazz = (Class) obj;
        }
        else
        {
          clazz = obj.getClass();
        }
  
        Method[] methods = clazz.getMethods();
  
        for (int i = 0; i < methods.length; i++)
        {
          if (methods[i].getName().equals(methodName))
            return 1;
        }
  
        return 0;
      }
    }
  
    /**
     * Function that creates an object proxy and pushes it into the stack
     *
     * @param luaState int that represents the state to be used
     * @param implem interfaces implemented separated by comma (<code>,</code>)
     * @return number of returned objects
     * @throws LuaException
     */
    public static int createProxyObject(int luaState, String implem)
            throws LuaException
    {
      LuaState L = LuaStateFactory.getExistingState(luaState);
  
      synchronized (L)
      {
        try
        {
          if (!(L.isTable(2)))
            throw new LuaException(
                    "Parameter is not a table. Can't create proxy.");
  
          LuaObject luaObj = L.getLuaObject(2);
  
          Object proxy = luaObj.createProxy(implem);
          L.pushJavaObject(proxy);
        }
        catch (Exception e)
        {
          throw new LuaException(e);
        }
  
        return 1;
      }
    }
  
    private static Object compareTypes(LuaState L, Class parameter, int idx)
            throws LuaException
    {
      boolean okType = true;
      Object obj = null;
  
      if (L.isBoolean(idx))
      {
        if (parameter.isPrimitive())
        {
          if (parameter != Boolean.TYPE)
          {
            okType = false;
          }
        }
        else if (!parameter.isAssignableFrom(Boolean.class))
        {
          okType = false;
        }
        obj = new Boolean(L.toBoolean(idx));
      }
      else if (L.type(idx) == LuaState.LUA_TSTRING)
      {
        if (!parameter.isAssignableFrom(String.class))
        {
          okType = false;
        }
        else
        {
          obj = L.toString(idx);
        }
      }
      else if (L.isFunction(idx))
      {
        if (!parameter.isAssignableFrom(LuaObject.class))
        {
          okType = false;
        }
        else
        {
          obj = L.getLuaObject(idx);
        }
      }
      else if (L.isTable(idx))
      {
        if (!parameter.isAssignableFrom(LuaObject.class))
        {
          okType = false;
        }
        else
        {
          obj = L.getLuaObject(idx);
        }
      }
      else if (L.type(idx) == LuaState.LUA_TNUMBER)
      {
        Double db = new Double(L.toNumber(idx));
  
        obj = LuaState.convertLuaNumber(db, parameter);
        if (obj == null)
        {
          okType = false;
        }
      }
      else if (L.isUserdata(idx))
      {
        if (L.isObject(idx))
        {
          Object userObj = L.getObjectFromUserdata(idx);
          if (!parameter.isAssignableFrom(userObj.getClass()))
          {
            okType = false;
          }
          else
          {
            obj = userObj;
          }
        }
        else
        {
          if (!parameter.isAssignableFrom(LuaObject.class))
          {
            okType = false;
          }
          else
          {
            obj = L.getLuaObject(idx);
          }
        }
      }
      else if (L.isNil(idx))
      {
        obj = null;
      }
      else
      {
        throw new LuaException("Invalid Parameters.");
      }
  
      if (!okType)
      {
        throw new LuaException("Invalid Parameter.");
      }
  
      return obj;
    }
  
  }