From c20e4edf64bcd596b41dc4bc6227e2a6e446c719 Mon Sep 17 00:00:00 2001 From: Mikhail Bobrutskov Date: Thu, 6 Jun 2024 23:02:40 +0200 Subject: [PATCH] added ClassExpressionLookup to access classes declared later in the script --- .../evaluation/ClassExpressionLookup.java | 42 ++++++++++++ .../wizzardo/tools/evaluation/EvalTools.java | 8 ++- .../tools/evaluation/EvalToolsTest.java | 65 +++++++++++++++++-- 3 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/ClassExpressionLookup.java diff --git a/modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/ClassExpressionLookup.java b/modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/ClassExpressionLookup.java new file mode 100644 index 00000000..b9cd19e2 --- /dev/null +++ b/modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/ClassExpressionLookup.java @@ -0,0 +1,42 @@ +package com.wizzardo.tools.evaluation; + +import com.wizzardo.tools.misc.Unchecked; +import java.util.Map; + +public class ClassExpressionLookup extends Expression { + + protected final String className; + protected final String key; + protected ClassExpression ce; + + public ClassExpressionLookup(String className, EvaluationContext context) { + super(context); + this.className = className; + this.key = "class " + className; + } + + @Override + public void setVariable(Variable v) { + } + + @Override + public Expression clone() { + return this; + } + + @Override + protected Object doExecute(Map model) { + if (ce == null) { + ce = (ClassExpression) model.get(key); + } + if (ce == null) { + Unchecked.rethrow(new ClassNotFoundException("Can not find class '" + className + "'")); + } + return ce; + } + + @Override + public String toString() { + return className; + } +} diff --git a/modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/EvalTools.java b/modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/EvalTools.java index d02ab35b..48086f15 100644 --- a/modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/EvalTools.java +++ b/modules/tools-evaluation/src/main/java/com/wizzardo/tools/evaluation/EvalTools.java @@ -5,7 +5,6 @@ package com.wizzardo.tools.evaluation; import com.wizzardo.tools.misc.Pair; -import com.wizzardo.tools.misc.Unchecked; import java.lang.reflect.*; import java.util.*; @@ -1938,8 +1937,11 @@ public static Expression prepare(String exp, int from, int to, EvaluationContext methodName = CONSTRUCTOR; from = start = expressionPart.start + m.end(); } - } else - Unchecked.rethrow(new ClassNotFoundException("Can not find class '" + className + "'")); + } else { + thatObject = new ClassExpressionLookup(className, model); + methodName = CONSTRUCTOR; + from = start = expressionPart.start + m.end(); + } } } } diff --git a/src/test/java/com/wizzardo/tools/evaluation/EvalToolsTest.java b/src/test/java/com/wizzardo/tools/evaluation/EvalToolsTest.java index 74196ee2..806a1020 100644 --- a/src/test/java/com/wizzardo/tools/evaluation/EvalToolsTest.java +++ b/src/test/java/com/wizzardo/tools/evaluation/EvalToolsTest.java @@ -12,9 +12,7 @@ import java.awt.*; import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.lang.reflect.*; import java.util.*; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -1096,7 +1094,7 @@ public void test_imports_1() { checkException(new Runnable() { @Override public void run() { - EvalTools.prepare(s); + EvalTools.prepare(s).get(model); } }, ClassNotFoundException.class, "Can not find class 'AtomicInteger'"); @@ -2221,6 +2219,65 @@ public void test_generate_class_2() throws InstantiationException, IllegalAccess Assert.assertEquals(true, flagField.get(instance)); } + @Test + public void test_class_expression_lookup() throws InstantiationException, IllegalAccessException, InvocationTargetException { + Map model = new HashMap(); + + Expression e = EvalTools.prepare("" + + "class SimpleClass {\n" + + " public String get(){\n" + + " return new InternalClass().value+\"\";" + + " }\n" + + " static class InternalClass {\n" + + " int value = 1;" + + " }\n" + + "}\n" + + ""); + + Object result = e.get(new HashMap<>()); + + Assert.assertNotNull(result); + Assert.assertTrue(result instanceof ClassExpression); + Class aClass = ((ClassExpression) result).getJavaClass(); + Assert.assertEquals("SimpleClass", aClass.getSimpleName()); + Method[] methods = ((Class) aClass).getDeclaredMethods(); + Assert.assertTrue(methods.length >= 1); + Method getMethod = Arrays.stream(methods).filter(it -> it.getName().equals("get")).findFirst().orElse(null); + Assert.assertNotNull(getMethod); + Object instance = ((ClassExpression) result).newInstance(new Object[0]); + Assert.assertEquals("1", getMethod.invoke(instance)); + } + + @Test + public void test_class_expression_lookup_2() throws InstantiationException, IllegalAccessException, InvocationTargetException { + Map model = new HashMap(); + + Expression e = EvalTools.prepare("" + + "class SimpleClass {\n" + + " public String get(){\n" + + " return new InternalClass(2).value+\"\";" + + " }\n" + + " static class InternalClass {\n" + + " int value = 1;" + + " InternalClass(int v){this.value=v;}" + + " }\n" + + "}\n" + + ""); + + Object result = e.get(new HashMap<>()); + + Assert.assertNotNull(result); + Assert.assertTrue(result instanceof ClassExpression); + Class aClass = ((ClassExpression) result).getJavaClass(); + Assert.assertEquals("SimpleClass", aClass.getSimpleName()); + Method[] methods = ((Class) aClass).getDeclaredMethods(); + Assert.assertTrue(methods.length >= 1); + Method getMethod = Arrays.stream(methods).filter(it -> it.getName().equals("get")).findFirst().orElse(null); + Assert.assertNotNull(getMethod); + Object instance = ((ClassExpression) result).newInstance(new Object[0]); + Assert.assertEquals("2", getMethod.invoke(instance)); + } + @Test public void test_line_number() { Map model = new HashMap();