Добавлен модуль java

This commit is contained in:
Victor 2016-07-04 22:01:42 +03:00
parent a6efd831e0
commit 3b90e544a7
4 changed files with 548 additions and 1 deletions

View File

@ -7,7 +7,7 @@ import java.util.Objects;
*
* @author aNNiMON
*/
public final class FunctionValue implements Value {
public class FunctionValue implements Value {
public static final FunctionValue EMPTY = new FunctionValue(args -> NumberValue.ZERO);

View File

@ -0,0 +1,459 @@
package com.annimon.ownlang.lib.modules;
import com.annimon.ownlang.lib.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Java interoperability module.
*
* @author aNNiMON
*/
public final class java implements Module {
private static final Value NULL = new NullValue();
@Override
public void init() {
Variables.define("null", NULL);
Variables.define("boolean.class", new ClassValue(boolean.class));
Variables.define("boolean[].class", new ClassValue(boolean[].class));
Variables.define("boolean[][].class", new ClassValue(boolean[][].class));
Variables.define("byte.class", new ClassValue(byte.class));
Variables.define("byte[].class", new ClassValue(byte[].class));
Variables.define("byte[][].class", new ClassValue(byte[][].class));
Variables.define("short.class", new ClassValue(short.class));
Variables.define("short[].class", new ClassValue(short[].class));
Variables.define("short[][].class", new ClassValue(short[][].class));
Variables.define("char.class", new ClassValue(char.class));
Variables.define("char[].class", new ClassValue(char[].class));
Variables.define("char[][].class", new ClassValue(char[][].class));
Variables.define("int.class", new ClassValue(int.class));
Variables.define("int[].class", new ClassValue(int[].class));
Variables.define("int[][].class", new ClassValue(int[][].class));
Variables.define("long.class", new ClassValue(long.class));
Variables.define("long[].class", new ClassValue(long[].class));
Variables.define("long[][].class", new ClassValue(long[][].class));
Variables.define("float.class", new ClassValue(float.class));
Variables.define("float[].class", new ClassValue(float[].class));
Variables.define("float[][].class", new ClassValue(float[][].class));
Variables.define("double.class", new ClassValue(double.class));
Variables.define("double[].class", new ClassValue(double[].class));
Variables.define("double[][].class", new ClassValue(double[][].class));
Variables.define("String.class", new ClassValue(String.class));
Variables.define("String[].class", new ClassValue(String[].class));
Variables.define("String[][].class", new ClassValue(String[][].class));
Variables.define("Object.class", new ClassValue(Object.class));
Variables.define("Object[].class", new ClassValue(Object[].class));
Variables.define("Object[][].class", new ClassValue(Object[][].class));
Functions.set("isNull", this::isNull);
Functions.set("newClass", this::newClass);
Functions.set("toObject", this::toObject);
Functions.set("toValue", this::toValue);
}
//<editor-fold defaultstate="collapsed" desc="Values">
private static class NullValue implements Value {
@Override
public Object raw() {
return null;
}
@Override
public int asInt() {
return 0;
}
@Override
public double asNumber() {
return 0;
}
@Override
public String asString() {
return "null";
}
@Override
public int type() {
return 482862660;
}
@Override
public int compareTo(Value o) {
if (o.raw() == null) return 0;
return -1;
}
@Override
public String toString() {
return asString();
}
}
private static class ClassValue extends MapValue {
public static Value classOrNull(Class<?> clazz) {
if (clazz == null) return NULL;
return new ClassValue(clazz);
}
private final Class<?> clazz;
public ClassValue(Class<?> clazz) {
super(25);
this.clazz = clazz;
init(clazz);
}
private void init(Class<?> clazz) {
set(new StringValue("isAnnotation"), NumberValue.fromBoolean(clazz.isAnnotation()));
set(new StringValue("isAnonymousClass"), NumberValue.fromBoolean(clazz.isAnonymousClass()));
set(new StringValue("isArray"), NumberValue.fromBoolean(clazz.isArray()));
set(new StringValue("isEnum"), NumberValue.fromBoolean(clazz.isEnum()));
set(new StringValue("isInterface"), NumberValue.fromBoolean(clazz.isInterface()));
set(new StringValue("isLocalClass"), NumberValue.fromBoolean(clazz.isLocalClass()));
set(new StringValue("isMemberClass"), NumberValue.fromBoolean(clazz.isMemberClass()));
set(new StringValue("isPrimitive"), NumberValue.fromBoolean(clazz.isPrimitive()));
set(new StringValue("isSynthetic"), NumberValue.fromBoolean(clazz.isSynthetic()));
set(new StringValue("modifiers"), NumberValue.of(clazz.getModifiers()));
set(new StringValue("canonicalName"), new StringValue(clazz.getCanonicalName()));
set(new StringValue("name"), new StringValue(clazz.getName()));
set(new StringValue("simpleName"), new StringValue(clazz.getSimpleName()));
set(new StringValue("typeName"), new StringValue(clazz.getTypeName()));
set(new StringValue("genericString"), new StringValue(clazz.toGenericString()));
set(new StringValue("getComponentType"), new FunctionValue(v -> classOrNull(clazz.getComponentType()) ));
set(new StringValue("getDeclaringClass"), new FunctionValue(v -> classOrNull(clazz.getDeclaringClass()) ));
set(new StringValue("getEnclosingClass"), new FunctionValue(v -> classOrNull(clazz.getEnclosingClass()) ));
set(new StringValue("getSuperclass"), new FunctionValue(v -> new ClassValue(clazz.getSuperclass()) ));
set(new StringValue("getClasses"), new FunctionValue(v -> array(clazz.getClasses()) ));
set(new StringValue("getDeclaredClasses"), new FunctionValue(v -> array(clazz.getDeclaredClasses()) ));
set(new StringValue("getInterfaces"), new FunctionValue(v -> array(clazz.getInterfaces()) ));
set(new StringValue("asSubclass"), new FunctionValue(this::asSubclass));
set(new StringValue("isAssignableFrom"), new FunctionValue(this::isAssignableFrom));
set(new StringValue("new"), new FunctionValue(this::newInstance));
set(new StringValue("cast"), new FunctionValue(this::cast));
}
private Value asSubclass(Value... args) {
Arguments.check(1, args.length);
return new ClassValue(clazz.asSubclass( ((ClassValue)args[0]).clazz ));
}
private Value isAssignableFrom(Value... args) {
Arguments.check(1, args.length);
return NumberValue.fromBoolean(clazz.isAssignableFrom( ((ClassValue)args[0]).clazz ));
}
private Value newInstance(Value... args) {
try {
return new ObjectValue(clazz.newInstance());
} catch (InstantiationException | IllegalAccessException ex) {
return NULL;
}
}
private Value cast(Value... args) {
Arguments.check(1, args.length);
return objectToValue(clazz, clazz.cast(((ObjectValue)args[0]).object));
}
@Override
public String toString() {
return "ClassValue " + clazz.toString();
}
}
private static class ObjectValue extends MapValue {
public static Value objectOrNull(Object object) {
if (object == null) return NULL;
return new ObjectValue(object);
}
private final Object object;
public ObjectValue(Object object) {
super(2);
this.object = object;
}
@Override
public boolean containsKey(Value key) {
return getValue(key.asString()) != null;
}
@Override
public Value get(Value key) {
return getValue(key.asString());
}
private Value getValue(String key) {
// Trying to get field
try {
final Field field = object.getClass().getField(key);
return objectToValue(field.getType(), field.get(object));
} catch (NoSuchFieldException | SecurityException |
IllegalArgumentException | IllegalAccessException ex) {
// ignore and go to the next step
}
// Trying to invoke method
try {
final Method[] allMethods = object.getClass().getMethods();
final List<Method> methods = new ArrayList<>();
for (Method method : allMethods) {
if (method.getName().equals(key)) {
methods.add(method);
}
}
if (methods.size() == 0) {
return FunctionValue.EMPTY;
}
return new FunctionValue(methodsToFunction(methods));
} catch (SecurityException ex) {
// ignore and go to the next step
}
return NULL;
}
private Function methodsToFunction(List<Method> methods) {
return (args) -> {
for (Method method : methods) {
if (method.getParameterCount() != args.length) continue;
if (!isMatch(args, method.getParameterTypes())) continue;
try {
final Object result = method.invoke(object, valuesToObjects(args));
if (method.getReturnType() != void.class) {
return objectToValue(result);
}
return NumberValue.ONE;
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
// skip
}
}
return null;
};
}
private boolean isMatch(Value[] args, Class<?>[] types) {
for (int i = 0; i < args.length; i++) {
final Value arg = args[i];
final Class<?> clazz = types[i];
if (arg == NULL) continue;
if (unboxed(clazz).isAssignableFrom(unboxed(valueToObject(arg).getClass()))) {
continue;
}
return false;
}
return true;
}
@Override
public String asString() {
return object.toString();
}
@Override
public String toString() {
return "ObjectValue " + asString();
}
private Class<?> unboxed(Class<?> clazz) {
if (clazz == null) return null;
if (clazz.isPrimitive()) {
if (int.class == clazz) return Integer.class;
if (boolean.class == clazz) return Boolean.class;
if (double.class == clazz) return Double.class;
if (float.class == clazz) return Float.class;
if (long.class == clazz) return Long.class;
if (byte.class == clazz) return Byte.class;
if (char.class == clazz) return Character.class;
if (short.class == clazz) return Short.class;
if (void.class == clazz) return Void.class;
}
return clazz;
}
}
//</editor-fold>
private Value isNull(Value... args) {
Arguments.checkAtLeast(1, args.length);
for (Value arg : args) {
if (arg.raw() == null) return NumberValue.ONE;
}
return NumberValue.ZERO;
}
private Value newClass(Value... args) {
Arguments.check(1, args.length);
final String className = args[0].asString();
try {
return new ClassValue(Class.forName(className));
} catch (ClassNotFoundException ce) {
return NULL;
}
}
private Value toObject(Value... args) {
Arguments.check(1, args.length);
if (args[0] == NULL) return NULL;
return new ObjectValue(valueToObject(args[0]));
}
private Value toValue(Value... args) {
Arguments.check(1, args.length);
if (args[0] instanceof ObjectValue) {
return objectToValue( ((ObjectValue) args[0]).object );
}
return NULL;
}
//<editor-fold defaultstate="collapsed" desc="Helpers">
private static ArrayValue array(Class<?>[] classes) {
final ArrayValue result = new ArrayValue(classes.length);
for (int i = 0; i < classes.length; i++) {
result.set(i, ClassValue.classOrNull(classes[i]));
}
return result;
}
private static Value objectToValue(Object o) {
if (o == null) return NULL;
return objectToValue(o.getClass(), o);
}
private static Value objectToValue(Class<?> clazz, Object o) {
if (o == null | o == NULL) return NULL;
if (clazz.isPrimitive()) {
if (int.class.isAssignableFrom(clazz))
return NumberValue.of((int) o);
if (boolean.class.isAssignableFrom(clazz))
return NumberValue.fromBoolean((boolean) o);
if (double.class.isAssignableFrom(clazz))
return NumberValue.of((double) o);
if (float.class.isAssignableFrom(clazz))
return NumberValue.of((float) o);
if (long.class.isAssignableFrom(clazz))
return NumberValue.of((long) o);
if (byte.class.isAssignableFrom(clazz))
return NumberValue.of((byte) o);
if (char.class.isAssignableFrom(clazz))
return NumberValue.of((char) o);
if (short.class.isAssignableFrom(clazz))
return NumberValue.of((short) o);
}
if (Number.class.isAssignableFrom(clazz)) {
return NumberValue.of((Number) o);
}
if (String.class.isAssignableFrom(clazz)) {
return new StringValue((String) o);
}
if (CharSequence.class.isAssignableFrom(clazz)) {
return new StringValue( ((CharSequence) o).toString() );
}
if (o instanceof Value) {
return (Value) o;
}
if (clazz.isArray()) {
final int length = Array.getLength(o);
final ArrayValue result = new ArrayValue(length);
final Class<?> componentType = clazz.getComponentType();
int i = 0;
if (boolean.class.isAssignableFrom(componentType)) {
for (boolean element : (boolean[]) o) {
result.set(i++, NumberValue.fromBoolean(element));
}
} else if (byte.class.isAssignableFrom(componentType)) {
for (byte element : (byte[]) o) {
result.set(i++, NumberValue.of(element));
}
} else if (char.class.isAssignableFrom(componentType)) {
for (char element : (char[]) o) {
result.set(i++, NumberValue.of(element));
}
} else if (double.class.isAssignableFrom(componentType)) {
for (double element : (double[]) o) {
result.set(i++, NumberValue.of(element));
}
} else if (float.class.isAssignableFrom(componentType)) {
for (float element : (float[]) o) {
result.set(i++, NumberValue.of(element));
}
} else if (int.class.isAssignableFrom(componentType)) {
for (int element : (int[]) o) {
result.set(i++, NumberValue.of(element));
}
} else if (long.class.isAssignableFrom(componentType)) {
for (long element : (long[]) o) {
result.set(i++, NumberValue.of(element));
}
} else if (short.class.isAssignableFrom(componentType)) {
for (short element : (short[]) o) {
result.set(i++, NumberValue.of(element));
}
} else {
for (Object element : (Object[]) o) {
result.set(i++, objectToValue(element));
}
}
return result;
}
final Class<?> componentType = clazz.getComponentType();
if (componentType != null) {
return objectToValue(componentType, o);
}
return new ObjectValue(o);
}
private static Object[] valuesToObjects(Value[] args) {
Object[] result = new Object[args.length];
for (int i = 0; i < args.length; i++) {
result[i] = valueToObject(args[i]);
}
return result;
}
private static Object valueToObject(Value value) {
if (value == NULL) return null;
switch (value.type()) {
case Types.NUMBER:
return value.raw();
case Types.STRING:
return value.asString();
case Types.ARRAY: {
final ArrayValue array = (ArrayValue) value;
final int size = array.size();
final Object[] result = new Object[size];
for (int i = 0; i < size; i++) {
result[i] = valueToObject(array.get(i));
}
return result;
}
}
if (value instanceof ObjectValue) {
return ((ObjectValue) value).object;
}
if (value instanceof ClassValue) {
return ((ClassValue) value).clazz;
}
return value.raw();
}
//</editor-fold>
}

39
test/interop/Data.java Normal file
View File

@ -0,0 +1,39 @@
package interop;
public final class Data {
public final int intValue = 3228;
public final int[] intArrayValue = {1, 2, 3};
public final Object nullObject = null;
public final String stringValue = "str";
public final String[] stringArrayValue = {"abc", "test"};
public final Object[] objectArray = new Object[] {intValue, intArrayValue, nullObject, stringValue, stringArrayValue};
public final Object compoundObject = objectArray;
public void method() {
System.out.println("method");
}
public String methodWithResult() {
return "result";
}
private int value;
private String text;
public void set(int value) {
this.value = value;
}
public void set(String text) {
this.text = text;
}
public int getValue() {
return value;
}
public String getText() {
return text;
}
}

View File

@ -0,0 +1,49 @@
use "std"
use "java"
def testCheckNull() {
assertTrue(isNull(null))
assertFalse(isNull(`byte[].class`))
assertTrue(null == null)
}
def testFieldAccess() {
data = createObject()
assertEquals(3228, data.intValue)
assertEquals([1, 2, 3], data.intArrayValue)
assertTrue(isNull(data.nullObject))
assertEquals("str", data.stringValue)
assertEquals(["abc", "test"], data.stringArrayValue)
assertObjectArray(data.objectArray)
}
def testCast() {
data = createObject()
assertObjectArray( `Object[].class`.cast(data.compoundObject) )
}
def testInvokeMethod() {
data = createObject()
data.method()
assertEquals("result", data.methodWithResult())
}
def testInvokeMethodSameName() {
data = createObject()
data.set(1)
data.set("text")
assertEquals(1, data.getValue())
assertEquals("text", data.getText())
}
def createObject() {
dataClass = newClass("interop.Data")
return dataClass.new()
}
def assertObjectArray(obj) {
assertEquals(5, length(obj))
assertEquals([3228, [1, 2, 3], null, "str", ["abc", "test"]], obj)
}