From fab883348a59975ef97753a2bd5f65f1ff06c885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Thu, 3 Jun 2010 16:48:35 +0000 Subject: [PATCH] added generic type system updated version to 0.1.3 --- pom.xml | 2 +- .../java/com/mysema/codegen/ClassUtils.java | 24 ++++++++ .../com/mysema/codegen/EvaluatorFactory.java | 48 ++++++++++------ src/main/java/com/mysema/codegen/Type.java | 57 +++++++++++++++++++ .../mysema/codegen/ComplexEvaluationTest.java | 45 +++++++++++++++ .../mysema/codegen/EvaluatorFactoryTest.java | 11 ++-- 6 files changed, 163 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/mysema/codegen/Type.java create mode 100644 src/test/java/com/mysema/codegen/ComplexEvaluationTest.java diff --git a/pom.xml b/pom.xml index d45bafea9..5ebfb2439 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.mysema.codegen codegen - 0.1.2-SNAPSHOT + 0.1.3 Codegen Code generation and compilation for Java diff --git a/src/main/java/com/mysema/codegen/ClassUtils.java b/src/main/java/com/mysema/codegen/ClassUtils.java index 27445c32e..b5581dae3 100644 --- a/src/main/java/com/mysema/codegen/ClassUtils.java +++ b/src/main/java/com/mysema/codegen/ClassUtils.java @@ -31,6 +31,30 @@ public final class ClassUtils { } } + public static String getName(Type cl){ + return getName(cl, Collections.singleton(Object.class.getPackage()), Collections.>emptySet()); + } + + public static String getName(Type type, Set packages, Set> classes){ + if (type.getParameters().isEmpty()){ + return getName(type.getJavaClass(), packages, classes); + }else{ + StringBuilder builder = new StringBuilder(); + builder.append(getName(type.getJavaClass(), packages, classes)); + builder.append("<"); + boolean first = true; + for (Type parameter : type.getParameters()){ + builder.append(getName(parameter, packages, classes)); + if (!first){ + builder.append(","); + } + first = false; + } + builder.append(">"); + return builder.toString(); + } + } + public static Class normalize(Class clazz){ if (List.class.isAssignableFrom(clazz)){ return List.class; diff --git a/src/main/java/com/mysema/codegen/EvaluatorFactory.java b/src/main/java/com/mysema/codegen/EvaluatorFactory.java index 8dcfeda65..d1c379fd4 100644 --- a/src/main/java/com/mysema/codegen/EvaluatorFactory.java +++ b/src/main/java/com/mysema/codegen/EvaluatorFactory.java @@ -54,8 +54,8 @@ public class EvaluatorFactory { this.compilationOptions = Arrays.asList("-classpath", classpath, "-g:none"); } - private void compile(String source, Class projectionType, - String[] names, Class[] types, String id, Map constants) throws IOException { + private void compile(String source, Type projectionType, + String[] names, Type[] types, String id, Map constants) throws IOException { // create source StringWriter writer = new StringWriter(); JavaWriter javaw = new JavaWriter(writer); @@ -75,7 +75,7 @@ public class EvaluatorFactory { }else{ javaw.beginPublicMethod(ClassUtils.getName(projectionType), "eval", params); } - javaw.line("return ", source, ";"); + javaw.append(source); javaw.end(); javaw.end(); @@ -95,34 +95,46 @@ public class EvaluatorFactory { } } + + @SuppressWarnings("unchecked") + public Evaluator createEvaluator( + String source, + Class projectionType, + String[] names, + Class[] classes, + Map constants) { + Type[] types = new Type[classes.length]; + for (int i = 0; i < types.length; i++){ + types[i] = new Type(classes[i]); + } + return createEvaluator(source, new Type(projectionType), names, types, classes, constants); + } /** * Create a new Evaluator instance * * @param projection type * @param source expression in Java source code form - * @param projectionType type of the source expression + * @param projection type of the source expression * @param names names of the arguments * @param types types of the arguments * @param constants * @return */ public Evaluator createEvaluator( - String source, - // TODO : support for generic projection types - Class projectionType, - String[] names, - // TODO : support for generic argument type - Class[] types, - Map constants) { - + String source, + Type projection, + String[] names, + Type[] types, + Class[] classes, + Map constants) { try { - String id = toId(source, projectionType, types); + String id = toId(source, projection, types); Class clazz; try{ clazz = loader.loadClass(id); }catch(ClassNotFoundException e){ - compile(source, projectionType, names, types, id, constants); + compile(source, projection, names, types, id, constants); // reload clazz = loader.loadClass(id); } @@ -134,8 +146,8 @@ public class EvaluatorFactory { field.set(object, entry.getValue()); } - Method method = clazz.getMethod("eval", types); - return new MethodEvaluator(method, object, projectionType); + Method method = clazz.getMethod("eval", classes); + return new MethodEvaluator(method, object, projection.getJavaClass()); } catch (ClassNotFoundException e) { throw new CodegenException(e); } catch (SecurityException e) { @@ -156,11 +168,11 @@ public class EvaluatorFactory { } - protected String toId(String source, Class returnType, Class... types) { + protected String toId(String source, Type returnType, Type... types) { StringBuilder b = new StringBuilder("Q"); b.append("_").append(source.hashCode()); b.append("_").append(returnType.getName().hashCode()); - for (Class type : types) { + for (Type type : types) { b.append("_").append(type.getName().hashCode()); } return b.toString().replace('-', '0'); diff --git a/src/main/java/com/mysema/codegen/Type.java b/src/main/java/com/mysema/codegen/Type.java new file mode 100644 index 000000000..2db342dab --- /dev/null +++ b/src/main/java/com/mysema/codegen/Type.java @@ -0,0 +1,57 @@ +package com.mysema.codegen; + +import java.util.Arrays; +import java.util.List; + +/** + * Type represents a generic type used in code generation + * + * @author tiwe + * + * @param + */ +public class Type { + + private final Class javaClass; + + private final List> parameters; + + public Type(Class javaClass, List> parameters) { + this.javaClass = javaClass; + this.parameters = parameters; + } + + public Type(Class clazz, Type... parameters) { + this(clazz, Arrays.asList(parameters)); + } + + public Class getJavaClass() { + return javaClass; + } + + public List> getParameters() { + return parameters; + } + + public String getName() { + return javaClass.getName(); + } + + @Override + public int hashCode(){ + return javaClass.hashCode(); + } + + @Override + public boolean equals(Object o){ + if (o == this){ + return true; + }else if (o instanceof Type){ + Type t = (Type)o; + return t.javaClass.equals(javaClass) && t.parameters.equals(parameters); + }else{ + return false; + } + } + +} diff --git a/src/test/java/com/mysema/codegen/ComplexEvaluationTest.java b/src/test/java/com/mysema/codegen/ComplexEvaluationTest.java new file mode 100644 index 000000000..84aaf5ab5 --- /dev/null +++ b/src/test/java/com/mysema/codegen/ComplexEvaluationTest.java @@ -0,0 +1,45 @@ +package com.mysema.codegen; + +import static org.junit.Assert.*; + +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +public class ComplexEvaluationTest { + + private EvaluatorFactory factory = new EvaluatorFactory((URLClassLoader) getClass().getClassLoader()); + + @Test + @SuppressWarnings("unchecked") + public void testComplex(){Type stringType = new Type(String.class); + Type resultType = new Type(List.class, stringType); + StringBuilder source = new StringBuilder(); + source.append("java.util.List rv = new java.util.ArrayList();\n"); + source.append("for (String a : a_){\n"); + source.append(" for (String b : b_){\n"); + source.append(" if (a.equals(b)){\n"); + source.append(" rv.add(a);\n"); + source.append(" }\n"); + source.append(" }\n"); + source.append("}\n"); + source.append("return rv;"); + + Evaluator evaluator = factory.createEvaluator( + source.toString(), + resultType, + new String[]{"a_","b_"}, + new Type[]{resultType, resultType}, + new Class[]{List.class,List.class}, + Collections.emptyMap()); + + List a_ = Arrays.asList("1","2","3","4"); + List b_ = Arrays.asList("2","4","6","8"); + + assertEquals(Arrays.asList("2","4"), evaluator.evaluate(a_, b_)); + } + +} diff --git a/src/test/java/com/mysema/codegen/EvaluatorFactoryTest.java b/src/test/java/com/mysema/codegen/EvaluatorFactoryTest.java index cc77d5fea..2d0970251 100644 --- a/src/test/java/com/mysema/codegen/EvaluatorFactoryTest.java +++ b/src/test/java/com/mysema/codegen/EvaluatorFactoryTest.java @@ -5,7 +5,7 @@ */ package com.mysema.codegen; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import java.io.IOException; import java.net.URLClassLoader; @@ -94,22 +94,23 @@ public class EvaluatorFactoryTest { public void testCustomType(){ test("a.getName()", String.class, Collections.singletonList("a"), Collections.>singletonList(TestEntity.class), - Arrays.asList(new TestEntity("Hello World")), "Hello World"); - + Arrays.asList(new TestEntity("Hello World")), "Hello World"); } - + private void test(String source, Class projectionType, List names, List> types, List args, Object expectedResult){ Assert.assertEquals(expectedResult, evaluate(source, projectionType, names, types, args, Collections.emptyMap())); } private Object evaluate(String source, Class projectionType, List names, List> types, List args, Map constants) { Evaluator evaluator = factory.createEvaluator( - source, + "return " + source + ";", projectionType, names.toArray(new String[names.size()]), types.toArray(new Class[types.size()]), constants); return evaluator.evaluate(args.toArray()); } + + }