added support for constants in Evaluator

This commit is contained in:
Timo Westkämper 2010-04-15 17:32:58 +00:00
parent 4c284540c4
commit d43c95d623
3 changed files with 71 additions and 14 deletions

View File

@ -5,7 +5,9 @@
*/
package com.mysema.codegen;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
@ -27,6 +29,18 @@ public final class ClassUtils {
return cl.getName().replace('$', '.');
}
}
public static Class<?> normalize(Class<?> clazz){
if (List.class.isAssignableFrom(clazz)){
return List.class;
}else if (Set.class.isAssignableFrom(clazz)){
return Set.class;
}else if (Collection.class.isAssignableFrom(clazz)){
return Collection.class;
}else{
return clazz;
}
}
private ClassUtils(){}
}

View File

@ -9,12 +9,14 @@ import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
@ -54,7 +56,7 @@ public class EvaluatorFactory {
}
private void compile(String source, Class<?> projectionType,
String[] names, Class<?>[] types, String id) throws IOException {
String[] names, Class<?>[] types, String id, Map<String,Object> constants) throws IOException {
// create source
StringWriter writer = new StringWriter();
JavaWriter javaw = new JavaWriter(writer);
@ -63,8 +65,17 @@ public class EvaluatorFactory {
for (int i = 0; i < params.length; i++) {
params[i] = ClassUtils.getName(types[i]) + " " + names[i];
}
for (Map.Entry<String,Object> entry : constants.entrySet()){
String className = ClassUtils.getName(ClassUtils.normalize(entry.getValue().getClass()));
javaw.publicField(className, entry.getKey());
}
javaw.beginStaticMethod(ClassUtils.getName(projectionType), "eval", params);
if (constants.isEmpty()){
javaw.beginStaticMethod(ClassUtils.getName(projectionType), "eval", params);
}else{
javaw.beginPublicMethod(ClassUtils.getName(projectionType), "eval", params);
}
javaw.line("return ", source, ";");
javaw.end();
javaw.end();
@ -94,10 +105,14 @@ public class EvaluatorFactory {
* @param projectionType type of the source expression
* @param names names of the arguments
* @param types types of the arguments
* @param constants
* @return
*/
public <T> Evaluator<T> createEvaluator(String source,
final Class<? extends T> projectionType, String[] names, Class<?>[] types) {
public <T> Evaluator<T> createEvaluator(
String source,
final Class<? extends T> projectionType,
String[] names, Class<?>[] types,
Map<String,Object> constants) {
try {
String id = toId(source, projectionType, types);
@ -105,10 +120,17 @@ public class EvaluatorFactory {
try{
clazz = loader.loadClass(id);
}catch(ClassNotFoundException e){
compile(source, projectionType, names, types, id);
compile(source, projectionType, names, types, id, constants);
// reload
clazz = loader.loadClass(id);
}
final Object object = !constants.isEmpty() ? clazz.newInstance() : null;
for (Map.Entry<String, Object> entry : constants.entrySet()){
Field field = clazz.getField(entry.getKey());
field.set(object, entry.getValue());
}
final Method method = clazz.getMethod("eval", types);
return new Evaluator<T>() {
@ -116,7 +138,7 @@ public class EvaluatorFactory {
@Override
public T evaluate(Object... args) {
try {
return (T) method.invoke(null, args);
return (T) method.invoke(object, args);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
@ -135,14 +157,20 @@ public class EvaluatorFactory {
throw new CodegenException(e);
} catch (NoSuchMethodException e) {
throw new CodegenException(e);
} catch (NoSuchFieldException e) {
throw new CodegenException(e);
} catch (UnsupportedEncodingException e) {
throw new CodegenException(e);
} catch (IOException e) {
throw new CodegenException(e);
} catch (InstantiationException e) {
throw new CodegenException(e);
} catch (IllegalAccessException e) {
throw new CodegenException(e);
}
}
protected String toId(String source, Class<?> returnType, Class<?>... types) {
StringBuilder b = new StringBuilder("Q");
b.append("_").append(source.hashCode());

View File

@ -5,11 +5,15 @@
*/
package com.mysema.codegen;
import static org.junit.Assert.*;
import java.io.IOException;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
@ -50,11 +54,11 @@ public class EvaluatorFactoryTest {
@Test
public void testSimple(){
for (String expr : Arrays.asList("a.equals(b)", "a.startsWith(b)", "a.equalsIgnoreCase(b)")){
test(expr, boolean.class, names, strings, Arrays.asList("a","b"));
evaluate(expr, boolean.class, names, strings, Arrays.asList("a","b"), Collections.<String,Object>emptyMap());
}
for (String expr : Arrays.asList("a != b", "a < b", "a > b", "a <= b", "a >= b")){
test(expr, boolean.class, names, ints, Arrays.asList(0,1));
evaluate(expr, boolean.class, names, ints, Arrays.asList(0,1), Collections.<String,Object>emptyMap());
}
}
@ -70,24 +74,35 @@ public class EvaluatorFactoryTest {
test("a + b", int.class, names, ints, Arrays.asList(1,2), 3);
}
@Test
public void testWithConstants(){
Map<String,Object> constants = new HashMap<String,Object>();
constants.put("x", "Hello World");
List<Class<?>> types = Arrays.<Class<?>>asList(String.class);
List<String> names = Arrays.asList("a");
assertEquals(Boolean.TRUE, evaluate("a.equals(x)", boolean.class, names, types, Arrays.asList("Hello World"), constants));
assertEquals(Boolean.FALSE, evaluate("a.equals(x)", boolean.class, names, types, Arrays.asList("Hello"), constants));
}
@Test
public void testCustomType(){
test("a.getName()", String.class,
Collections.singletonList("a"), Collections.singletonList(TestEntity.class),
Collections.singletonList("a"), Collections.<Class<?>>singletonList(TestEntity.class),
Arrays.asList(new TestEntity("Hello World")), "Hello World");
}
private void test(String source, Class<?> projectionType, List<String> names, List<? extends Class<?>> types, List<?> args, Object expectedResult){
Assert.assertEquals(expectedResult, test(source, projectionType, names, types, args));
private void test(String source, Class<?> projectionType, List<String> names, List<Class<?>> types, List<?> args, Object expectedResult){
Assert.assertEquals(expectedResult, evaluate(source, projectionType, names, types, args, Collections.<String,Object>emptyMap()));
}
private Object test(String source, Class<?> projectionType, List<String> names, List<? extends Class<?>> types, List<?> args) {
private Object evaluate(String source, Class<?> projectionType, List<String> names, List<Class<?>> types, List<?> args, Map<String,Object> constants) {
Evaluator<?> evaluator = factory.createEvaluator(
source,
projectionType,
names.toArray(new String[names.size()]),
types.toArray(new Class[types.size()]));
types.toArray(new Class[types.size()]),
constants);
return evaluator.evaluate(args.toArray());
}