added generic type system

updated version to 0.1.3
This commit is contained in:
Timo Westkämper 2010-06-03 16:48:35 +00:00
parent 326c0825f3
commit fab883348a
6 changed files with 163 additions and 24 deletions

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.mysema.codegen</groupId>
<artifactId>codegen</artifactId>
<version>0.1.2-SNAPSHOT</version>
<version>0.1.3</version>
<name>Codegen</name>
<description>Code generation and compilation for Java</description>
<parent>

View File

@ -31,6 +31,30 @@ public final class ClassUtils {
}
}
public static String getName(Type<?> cl){
return getName(cl, Collections.singleton(Object.class.getPackage()), Collections.<Class<?>>emptySet());
}
public static String getName(Type<?> type, Set<Package> packages, Set<Class<?>> 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;

View File

@ -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<String,Object> constants) throws IOException {
private void compile(String source, Type<?> projectionType,
String[] names, Type<?>[] types, String id, Map<String,Object> 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 <T> Evaluator<T> createEvaluator(
String source,
Class<? extends T> projectionType,
String[] names,
Class<?>[] classes,
Map<String,Object> 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 <T> 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 <T> Evaluator<T> createEvaluator(
String source,
// TODO : support for generic projection types
Class<? extends T> projectionType,
String[] names,
// TODO : support for generic argument type
Class<?>[] types,
Map<String,Object> constants) {
String source,
Type<? extends T> projection,
String[] names,
Type<?>[] types,
Class<?>[] classes,
Map<String,Object> 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<T>(method, object, projectionType);
Method method = clazz.getMethod("eval", classes);
return new MethodEvaluator<T>(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');

View File

@ -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 <T>
*/
public class Type<T> {
private final Class<T> javaClass;
private final List<Type<?>> parameters;
public Type(Class<T> javaClass, List<Type<?>> parameters) {
this.javaClass = javaClass;
this.parameters = parameters;
}
public Type(Class<T> clazz, Type<?>... parameters) {
this(clazz, Arrays.asList(parameters));
}
public Class<T> getJavaClass() {
return javaClass;
}
public List<Type<?>> 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;
}
}
}

View File

@ -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<String> stringType = new Type<String>(String.class);
Type<List> resultType = new Type<List>(List.class, stringType);
StringBuilder source = new StringBuilder();
source.append("java.util.List<String> rv = new java.util.ArrayList<String>();\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<List> evaluator = factory.createEvaluator(
source.toString(),
resultType,
new String[]{"a_","b_"},
new Type[]{resultType, resultType},
new Class[]{List.class,List.class},
Collections.<String,Object>emptyMap());
List<String> a_ = Arrays.asList("1","2","3","4");
List<String> b_ = Arrays.asList("2","4","6","8");
assertEquals(Arrays.asList("2","4"), evaluator.evaluate(a_, b_));
}
}

View File

@ -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.<Class<?>>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<String> names, List<Class<?>> types, List<?> args, Object expectedResult){
Assert.assertEquals(expectedResult, evaluate(source, projectionType, names, types, args, Collections.<String,Object>emptyMap()));
}
private Object evaluate(String source, Class<?> projectionType, List<String> names, List<Class<?>> types, List<?> args, Map<String,Object> 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());
}
}