Package erjang.m.java

Source Code of erjang.m.java.JavaObject$Mapper

/**
*
*/
package erjang.m.java;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import erjang.codegen.EFunCG;
import junit.extensions.TestDecorator;

import kilim.Mailbox;
import kilim.Pausable;
import erjang.EAtom;
import erjang.EBinary;
import erjang.EBitString;
import erjang.ECons;
import erjang.EDouble;
import erjang.EFun;
import erjang.EFunHandler;
import erjang.EInteger;
import erjang.EList;
import erjang.ENil;
import erjang.ENumber;
import erjang.EObject;
import erjang.EProc;
import erjang.EPseudoTerm;
import erjang.ERT;
import erjang.ESeq;
import erjang.ESmall;
import erjang.EString;
import erjang.ETuple;
import erjang.ETuple2;
import erjang.ErlangError;
import erjang.ErlangException;
import erjang.driver.IO;
import erjang.m.erlang.ErlProc;

public class JavaObject extends EPseudoTerm {

  public JavaObject testJavaObject() {
    return this;
  }

  static final EAtom am_none = EAtom.intern("none");
  static final EAtom am_java_object = EAtom.intern("java_object");
  static final EAtom am_badaccess = EAtom.intern("badaccess");
  static final EAtom am_badfun = EAtom.intern("badfun");
  static final EAtom am_null_pointer_exception = EAtom
      .intern("null_pointer_exception");
  protected static final EAtom am_erlang = EAtom.intern("erlang");
  protected static final EAtom am_apply = EAtom.intern("apply");

  final Object real_object;
  final EProc owner;
 
  public Object realObject() {
    return real_object;
  }

  @Override
  public int hashCode() {
    return real_object.hashCode();
  }
  //TODO: equality operators

  @Override
  public EBinary testBinary() {
    if (real_object instanceof byte[]) {
      return new EBinary((byte[]) real_object);
    }
    if (real_object instanceof String) {
      return new EBinary(((String) real_object).getBytes(IO.UTF8));
    }
    return null;
  }

  @Override
  public EBitString testBitString() {
    EBinary bi;
    if ((bi=testBinary()) != null) {
      return bi;
    }
   
    if (testCons() == null
        && testNumber() == null
        && testTuple() == null
        && testAtom() == null
        && testHandle() == null
        )
      return EBinary.EMPTY;
   
    return null;
   
  }
 
  @Override
  public ENil testNil() {
    if (real_object == null)
      return ERT.NIL;
    if (testSeq() == ERT.NIL)
      return ERT.NIL;
    return null;
  }

  @Override
  public EFun testFunction() {
    EFun fun;

    if ((fun = testFunction2(0)) != null) {
      return fun;
    }

    if ((fun = testFunction2(1)) != null) {
      return fun;
    }

    return null;
  }

  @Override
  public EFun testFunction2(int nargs) {

    /** a java.lang.Runnable can be used as a function of 0 arguments */
    if ((nargs == 0) && (real_object instanceof Runnable)) {
      final Runnable r = (Runnable) real_object;
      return EFunCG.get_fun_with_handler("java.lang.Runnable", "run", 0,
                    new EFunHandler() {
                        @Override
                        public EObject invoke(EProc proc, EObject[] args)
                                throws Pausable {
                            if (proc != owner)
                                throw new ErlangError(ERT.am_badfun, args);

                            r.run();
                            return ERT.am_ok;
                        }
                    }, getClass().getClassLoader());

    }

    /*
     * a java.util.Map can be used as a function with 1 argument to get a
     * value
     */
    if ((nargs == 1) && (real_object instanceof java.util.Map<?, ?>)) {
      final java.util.Map<?, ?> r = (java.util.Map<?, ?>) real_object;
      return EFunCG.get_fun_with_handler("java.util.Map", "get", 0,
        new EFunHandler() {
          @Override
          public EObject invoke(EProc self, EObject[] args)
              throws Pausable {
            if (self != owner)
              throw new ErlangError(ERT.am_badfun, args);

            Object key = JavaObject.unbox(self, Object.class, args[0]);
            if (r.containsKey(key)) {
              return new ETuple2(args[0], JavaObject.box(self, r
                  .get(key)));
            } else {
              return am_none;
            }
          }
        }, getClass().getClassLoader());

    }

    return null;
  }
 
  @Override
  public ECons testCons() {
    return testSeq();
  }

  @Override
  public ECons testNonEmptyList() {
    ESeq seq = testSeq();
    if (seq == null || seq.isNil())
      return null;
    return seq;
  }

  @Override
  public EAtom testBoolean() {
    if (real_object == Boolean.TRUE)
      return ERT.TRUE;
    if (real_object == Boolean.FALSE)
      return ERT.FALSE;
    return null;
  }

  @Override
  public EString testString() {
    if (real_object instanceof String) {
      return EString.fromString((String) real_object);
    }

    ESeq seq;
    if ((seq = testSeq()) != null) {
      return seq.testString();
    }

    return null;
  }

  @Override
  public EAtom testAtom() {
    if (real_object instanceof String) {
      String s = (String) real_object;
      return EAtom.existing_atom(s);
    }
    if (real_object instanceof Class) {
      return EAtom.intern(((Class<?>) real_object).getName());
    }
    return null;
  }

  @SuppressWarnings("unchecked")
  @Override
  public ESeq testSeq() {
    if (real_object instanceof CharSequence) {
      return JavaCharSeq.box(owner, (CharSequence) real_object, 0);
    }

    if (real_object instanceof Iterable<?>) {
      Iterable<?> it = (Iterable<?>) real_object;
      return JavaIterator.box(owner, it.iterator());
    }

    if (real_object instanceof Iterator<?>) {
      return JavaIterator.box(owner, ((Iterator<?>) real_object));
    }

    if (real_object instanceof Map<?, ?>) {
      return JavaMapIterator.box(owner, ((Map) real_object).entrySet()
          .iterator());
    }

    if (real_object != null && real_object.getClass().isArray()) {
      return JavaArray.box(owner, real_object, 0);
    }

    return null;
  }

  @Override
  public ESmall testSmall() {
    EInteger iv;
    if ((iv = testInteger()) != null) {
      return iv.testSmall();
    }

    return null;
  }
 
  @Override
  public ENumber testNumber() {

    EInteger i;
    if ((i = testInteger()) != null)
        return i;

    EDouble d;
    if ((d = testFloat()) != null)
        return d;

    return null;
  }

  @Override
  public EInteger testInteger() {

    if (real_object instanceof BigInteger) {
      return ERT.box(((BigInteger) real_object));
    }

    if (real_object instanceof Integer) {
      return ERT.box(((Integer) real_object).intValue());
    }

    if (real_object instanceof Short) {
      return ERT.box(((Short) real_object).shortValue());
    }

    if (real_object instanceof Character) {
      return ERT.box(((Character) real_object).charValue());
    }

    if (real_object instanceof Byte) {
      return ERT.box(((Byte) real_object).byteValue());
    }

    if (real_object instanceof Long) {
      return ERT.box(((Long) real_object).longValue());
    }

    return null;
  }

  @Override
  public EDouble testFloat() {
    if (real_object instanceof Double) {
      return new EDouble(((Double) real_object).doubleValue());
    }
    return null;
  }

  public JavaObject(EProc self, Object object) {
    real_object = object;
    owner = self;
  }

  public String toString() {
    return String.valueOf(real_object);
  };

  public static EObject box(EProc self, Object object) {
    if (object instanceof EObject) {
      return (EObject) object;
    }
    return new JavaObject(self, object);
  }

  public static Object[] convert_args(EProc self, Class<?>[] arg_types,
      ESeq arg_seq) throws IllegalArgumentException {
    Object[] out = new Object[arg_types.length];

    ESeq as_iter = arg_seq;
    for (int i = 0; i < arg_types.length; i++) {
      out[i] = JavaObject.unbox(self, arg_types[i], as_iter.head());
      as_iter = as_iter.tail();
    }

    return out;
  }

  public static Object[] convert_args(EProc self, Class<?>[] arg_types,
      EObject[] args) throws IllegalArgumentException {
    Object[] out = new Object[arg_types.length];

    for (int i = 0; i < arg_types.length; i++) {
      out[i] = JavaObject.unbox(self, arg_types[i], args[i]);
    }

    return out;
  }

  /** this logic is used to map an EObject back to a Java object */
  static Object unbox(final EProc self, Class<?> type, EObject val)
      throws IllegalArgumentException {
    if (val instanceof JavaObject) {
      JavaObject jo = (JavaObject) val;
      if (type.isInstance(jo.real_object)) {
        return jo.real_object;
      } else {
        throw new IllegalArgumentException("cannot convert "
            + val.getClass().getName() + " to " + type);
      }
    }

    if (type == Object.class || type.isInstance(val)) {
      // TODO: give each erlang term a "natural" conversion to
      // java.lang.Object
      return val;
    }

    if (type == Void.class || type == void.class) {
      return null;
    }
   
    /** unbox to array type */
    if (type.isArray()) {
      ESeq seq;
      if ((seq = val.testSeq()) != null) {
        int length = seq.length();
        Class<?> componentType = type.getComponentType();
        Object arr = Array.newInstance(componentType, length);

        int index = 0;
        while (!seq.isNil()) {
          Object value = JavaObject.unbox(self, componentType, seq
              .head());
          Array.set(arr, index++, value);
          seq = seq.tail();
        }

        return arr;
      }

      ETuple tup;
      if ((tup = val.testTuple()) != null) {
        int length = tup.arity();
        Class<?> componentType = type.getComponentType();
        Object arr = Array.newInstance(componentType, length);

        for (int index = 0; index < length; index++) {
          Object value = JavaObject.unbox(self, componentType, tup
              .elm(index + 1));
          Array.set(arr, index, value);
        }

        return arr;

      }
    }

    Mapper mapper = type_mapper.get(type);
    if (mapper != null) {
      return mapper.map(val);
    }

    final EFun ifun;
    if (type.isInterface() && (ifun = val.testFunction2(3)) != null) {

      final ClassLoader loader = JavaObject.class
          .getClassLoader();
      return
      java.lang.reflect.Proxy.newProxyInstance(loader, new Class[] { type }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, final Method method, final Object[] args)
                throws Throwable {
             
              final Mailbox<Object> reply = new Mailbox<Object>(1);
             
              EFun job = EFunCG.get_fun_with_handler("erlang", "apply", 0, new EFunHandler() {
                @Override
                public EObject invoke(EProc proc, EObject[] _) throws Pausable {

                  EObject aa = JavaObject.box(proc, args);
                  EObject at = JavaObject.box(proc, method.getParameterTypes());

                  EObject[] call_args = new EObject[] {
                    EAtom.intern(method.getName()), at, aa
                  };

                  EObject result = ifun.invoke(proc, call_args);

                  Object jresult = JavaObject.unbox(proc,
                      method.getReturnType(),
                      result);

                  if (method.getReturnType() == void.class) {
                    reply.put(ERT.am_ok);
                  } else {
                    reply.putjresult );
                  }

                  return ERT.am_ok;
                }
              }, loader);

              EProc proc = new EProc(self.group_leader(),
                  am_erlang, am_apply, EList.make(job, ERT.NIL));
              ERT.run(proc);
             
              return reply.getb();
            }
          });
    }

    throw new IllegalArgumentException("cannot convert "
        + val.getClass().getName() + " to " + type);
  }

  static Map<Class<?>, Mapper> type_mapper = new HashMap<Class<?>, Mapper>();

  interface Mapper {
    Object map(EObject val);
  }

  static {
    Mapper string_mapper = new Mapper() {
      public Object map(EObject val) {

        EString str;
        if ((str = val.testString()) != null)
          return str.stringValue();

        EAtom am;
        if ((am = val.testAtom()) != null)
          return am.getName();
       
        EBinary bi;
        if ((bi = val.testBinary()) != null) {
          return new String(bi.toByteArray(), IO.UTF8);
        }

        throw new IllegalArgumentException();
      }
    };
    type_mapper.put(String.class, string_mapper);
    type_mapper.put(CharSequence.class, string_mapper);

    Mapper byte_mapper = new Mapper() {

      public Object map(EObject val) {
        ESmall num;
        // special case: allow converting values -127 ... 155.
        if ((num = val.testSmall()) != null
            && num.value >= Byte.MIN_VALUE && num.value <= 255) {
          return new Byte((byte) num.intValue());
        }
        throw new IllegalArgumentException("cannot convert " + val
            + " to byte");
      }
    };
    type_mapper.put(Byte.class, byte_mapper);
    type_mapper.put(byte.class, byte_mapper);

    Mapper short_mapper = new Mapper() {

      public Object map(EObject val) {
        ESmall num;
        if ((num = val.testSmall()) != null
            && num.value >= Short.MIN_VALUE
            && num.value <= Short.MAX_VALUE) {
          return new Short((byte) num.intValue());
        }
        throw new IllegalArgumentException("cannot convert " + val
            + " to short");
      }
    };
    type_mapper.put(Short.class, short_mapper);
    type_mapper.put(short.class, short_mapper);

    Mapper int_mapper = new Mapper() {

      public Object map(EObject val) {
        ESmall num;
        if ((num = val.testSmall()) != null) {
          return new Integer(num.intValue());
        }
        throw new IllegalArgumentException("cannot convert " + val
            + " to int");
      }
    };
    type_mapper.put(Integer.class, int_mapper);
    type_mapper.put(int.class, int_mapper);

    Mapper long_mapper = new Mapper() {

      public Object map(EObject val) {
        EInteger num;
        if ((num = val.testInteger()) != null) {
          return new Long(num.longValue());
        }
        throw new IllegalArgumentException("cannot convert " + val
            + " to long");
      }
    };
    type_mapper.put(Long.class, long_mapper);
    type_mapper.put(long.class, long_mapper);

    Mapper double_mapper = new Mapper() {

      public Object map(EObject val) {
        ENumber num;
        if ((num = val.testNumber()) != null) {
          return new Double(num.doubleValue());
        }
        throw new IllegalArgumentException("cannot convert " + val
            + " to double");
      }
    };
    type_mapper.put(Double.class, double_mapper);
    type_mapper.put(double.class, double_mapper);

    Mapper float_mapper = new Mapper() {

      public Object map(EObject val) {
        ENumber num;
        if ((num = val.testNumber()) != null) {
          return new Float(num.doubleValue());
        }
        throw new IllegalArgumentException("cannot convert " + val
            + " to float");
      }
    };
    type_mapper.put(Float.class, float_mapper);
    type_mapper.put(float.class, float_mapper);

    Mapper bool_mapper = new Mapper() {

      public Object map(EObject val) {

        EAtom bool;
        if ((bool = val.testBoolean()) != null) {
          if (bool == ERT.TRUE)
            return Boolean.TRUE;
          if (bool == ERT.FALSE)
            return Boolean.FALSE;
        }

        throw new IllegalArgumentException();
      }
    };
    type_mapper.put(boolean.class, bool_mapper);
    type_mapper.put(Boolean.class, bool_mapper);
  }

  /**
   * return a fun that will call a Java method; used when calling
   * Object:doThis(a,b,c)
   */
  public EFun resolve_fun(final EAtom f, final int arity) {

    // TODO: we can make this much much faster!

    return EFunCG.get_fun_with_handler("java.lang.Object", f.toString(), arity,

    new EFunHandler() {
      @Override
      public EObject invoke(EProc proc, EObject[] args) throws Pausable {

        if (real_object == null) {
          throw new ErlangError(am_null_pointer_exception);
        }

        Method[] methods = real_object.getClass().getMethods();

        return choose_and_invoke_method(proc, real_object, f, args,
            methods, false);
      }
    }, JavaObject.class.getClassLoader());

  }

  public static EObject choose_and_invoke_method(EProc self, Object target,
      final EAtom method_name, EObject[] args, Method[] methods,
      boolean static_only) {
    for (int i = 0; i < methods.length; i++) {
      // TODO: handle reflective invocation of Pausable methods

      Method m = methods[i];
      int modifier = m.getModifiers();
      boolean is_static = (Modifier.STATIC & modifier) == Modifier.STATIC;
      if (is_static != static_only)
        continue;

      if (!m.getName().equals(method_name.getName()))
        continue;
      Class<?>[] pt = m.getParameterTypes();
      if (pt.length != args.length)
        continue;
      Object[] a;
      try {
        a = convert_args(self, pt, args);
      } catch (IllegalArgumentException e) {
        // TODO: make this a null check
        continue;
      }

      // point-of-no-return
      Object result;
      try {

        result = m.invoke(target, a);
      } catch (IllegalArgumentException e) {
        throw ERT.badarg(args);
      } catch (IllegalAccessException e) {
        throw new ErlangError(am_badaccess, args);
      } catch (InvocationTargetException e) {
        Throwable te = e.getTargetException();
        if (te instanceof ErlangException) {
          throw (ErlangException) te;
        } else {
          ETuple reason = ETuple.make(EAtom.intern(te.getClass()
              .getName()), EString.fromString(te.getMessage()));
          throw new ErlangError(reason, args);
        }
      }

      if (m.getReturnType() == Void.TYPE) {
        return ERT.am_ok;
      }

      return JavaObject.box(self, result);
    }

    throw new ErlangError(am_badfun, args);
  }

  public static EObject choose_and_invoke_constructor(EProc self,
      EObject[] args, Constructor<?>[] cons) {
    for (int i = 0; i < cons.length; i++) {
      // TODO: handle reflective invocation of Pausable methods

      Constructor<?> m = cons[i];

      Class<?>[] pt = m.getParameterTypes();
      if (pt.length != args.length)
        continue;
      Object[] a;
      try {
        a = convert_args(self, pt, args);
      } catch (IllegalArgumentException e) {
        // TODO: make this a null check
        continue;
      }

      // point-of-no-return
      Object result;
      try {

        result = m.newInstance(a);
      } catch (InstantiationException e) {
        throw ERT.badarg(args);
      } catch (IllegalArgumentException e) {
        throw ERT.badarg(args);
      } catch (IllegalAccessException e) {
        throw new ErlangError(am_badaccess, args);
      } catch (InvocationTargetException e) {
        Throwable te = e.getTargetException();
        if (te instanceof ErlangException) {
          throw (ErlangException) te;
        } else {
          ETuple reason = ETuple.make(EAtom.intern(te.getClass()
              .getName()), EString.fromString(te.getMessage()));
          throw new ErlangError(reason, args);
        }
      }

      return JavaObject.box(self, result);
    }

    throw new ErlangError(am_badfun, args);
  }
}
TOP

Related Classes of erjang.m.java.JavaObject$Mapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.