added Eclipse JDT based compilation

This commit is contained in:
Timo Westkämper 2012-07-30 23:38:06 +03:00
parent 92cbdaf867
commit 6bdaa71de4
10 changed files with 961 additions and 166 deletions

12
pom.xml
View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.mysema.codegen</groupId>
<artifactId>codegen</artifactId>
<version>0.5.1</version>
<version>0.5.2</version>
<name>Codegen</name>
<description>Code generation and compilation for Java</description>
<parent>
@ -20,6 +20,7 @@
<commons.collections.version>4.01</commons.collections.version>
<commons.lang.version>3.0.1</commons.lang.version>
<guava.version>11.0.2</guava.version>
<ecj.version>3.7.2</ecj.version>
</properties>
<dependencies>
@ -28,6 +29,12 @@
<artifactId>guava</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>${ecj.version}</version>
</dependency>
<!-- test -->
<dependency>
@ -47,7 +54,8 @@
<artifactId>validation-api</artifactId>
<version>1.0.CR3</version>
<scope>test</scope>
</dependency>
</dependency>
</dependencies>

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2012 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.codegen;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Type;
import com.mysema.codegen.model.TypeCategory;
/**
* @author tiwe
*
*/
public abstract class AbstractEvaluatorFactory implements EvaluatorFactory{
protected ClassLoader loader;
/**
* @param source
* @param projection
* @param names
* @param types
* @param id
* @param constants
* @throws IOException
*/
protected abstract void compile(String source, ClassType projection, String[] names, Type[] types,
String id, Map<String, Object> constants) throws IOException;
@Override
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 ClassType(TypeCategory.SIMPLE, classes[i]);
}
return createEvaluator(source, new ClassType(TypeCategory.SIMPLE, projectionType), names,
types, classes, constants);
}
/**
* Create a new Evaluator instance
*
* @param <T>
* projection type
* @param source
* expression in Java source code form
* @param projection
* type of the source expression
* @param names
* names of the arguments
* @param types
* types of the arguments
* @param constants
* @return
*/
@SuppressWarnings("unchecked")
@Override
public <T> Evaluator<T> createEvaluator(String source, ClassType projection, String[] names,
Type[] types, Class<?>[] classes, Map<String, Object> constants) {
try {
String id = toId(source, projection.getJavaClass(), types);
Class<?> clazz;
try {
clazz = loader.loadClass(id);
} catch (ClassNotFoundException e) {
compile(source, projection, names, types, id, constants);
// reload
clazz = loader.loadClass(id);
}
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());
}
Method method = clazz.getMethod("eval", classes);
return new MethodEvaluator<T>(method, object, (Class) projection.getJavaClass());
} catch (ClassNotFoundException e) {
throw new CodegenException(e);
} catch (SecurityException e) {
throw new CodegenException(e);
} catch (NoSuchMethodException e) {
throw new CodegenException(e);
} catch (NoSuchFieldException e) {
throw new CodegenException(e);
} catch (InstantiationException e) {
throw new CodegenException(e);
} catch (IOException e) {
throw new CodegenException(e);
} catch (IllegalAccessException e) {
throw new CodegenException(e);
}
}
protected String toId(String source, Class<?> returnType, Type... types) {
StringBuilder b = new StringBuilder("Q");
b.append("_").append(source.hashCode());
b.append("_").append(returnType.getName().hashCode());
for (Type type : types) {
b.append("_").append(type.getFullName().hashCode());
}
return b.toString().replace('-', '0');
}
}

View File

@ -0,0 +1,320 @@
/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.codegen;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.Compiler;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
import org.eclipse.jdt.internal.compiler.IProblemFactory;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Parameter;
import com.mysema.codegen.model.SimpleType;
import com.mysema.codegen.model.Type;
import com.mysema.codegen.model.TypeCategory;
import com.mysema.codegen.support.ClassUtils;
/**
* EvaluatorFactory is a factory implementation for creating Evaluator instances
*
* @author tiwe
*
*/
public class ECJEvaluatorFactory extends AbstractEvaluatorFactory {
private final MemFileManager fileManager;
private final ClassLoader parentClassLoader;
private final List<String> problemList = Lists.newArrayList();
private final CompilerOptions compilerOptions;
public static CompilerOptions getDefaultCompilerOptions() {
String javaSpecVersion = System.getProperty("java.specification.version");
Map<String, Object> settings = Maps.newHashMap();
settings.put(CompilerOptions.OPTION_Source, javaSpecVersion);
settings.put(CompilerOptions.OPTION_TargetPlatform, javaSpecVersion);
settings.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.IGNORE);
return new CompilerOptions(settings);
}
public ECJEvaluatorFactory(ClassLoader parent) {
this(parent, getDefaultCompilerOptions());
}
public ECJEvaluatorFactory(ClassLoader parent, CompilerOptions compilerOptions) {
this.parentClassLoader = parent;
this.fileManager = new MemFileManager(parent, new EclipseFileManager(Locale.getDefault(), Charset.defaultCharset()));
this.loader = fileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
this.compilerOptions = compilerOptions;
}
protected void compile(String source, ClassType projectionType, String[] names, Type[] types,
String id, Map<String, Object> constants) throws IOException {
// create source
StringWriter writer = new StringWriter();
JavaWriter javaw = new JavaWriter(writer);
SimpleType idType = new SimpleType(id, "", id);
javaw.beginClass(idType, null);
Parameter[] params = new Parameter[names.length];
for (int i = 0; i < params.length; i++) {
params[i] = new Parameter(names[i], types[i]);
}
for (Map.Entry<String, Object> entry : constants.entrySet()) {
Type type = new ClassType(TypeCategory.SIMPLE, ClassUtils.normalize(entry.getValue().getClass()));
javaw.publicField(type, entry.getKey());
}
if (constants.isEmpty()) {
javaw.beginStaticMethod(projectionType, "eval", params);
} else {
javaw.beginPublicMethod(projectionType, "eval", params);
}
javaw.append(source);
javaw.end();
javaw.end();
// compile
final char[] targetContents = writer.toString().toCharArray();
final String targetName = idType.getFullName();
final ICompilationUnit[] targetCompilationUnits = new ICompilationUnit[] { new ICompilationUnit() {
@Override
public char[] getContents() {
return targetContents;
}
@Override
public char[] getMainTypeName() {
int dot = targetName.lastIndexOf('.');
if (dot > 0)
return targetName.substring(dot + 1).toCharArray();
else
return targetName.toCharArray();
}
@Override
public char[][] getPackageName() {
StringTokenizer tok = new StringTokenizer(targetName, ".");
char[][] result = new char[tok.countTokens() - 1][];
for (int j = 0; j < result.length; j++) {
result[j] = tok.nextToken().toCharArray();
}
return result;
}
@Override
public char[] getFileName() {
return CharOperation.concat(targetName.toCharArray(), ".java".toCharArray());
}
} };
INameEnvironment env = new INameEnvironment() {
private String join(char[][] compoundName, char separator) {
if (compoundName == null) {
return "";
} else {
List<String> parts = Lists.newArrayListWithCapacity(compoundName.length);
for (char[] part: compoundName) {
parts.add(new String(part));
}
return Joiner.on(separator).join(parts);
}
}
@Override
public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
return findType(join(compoundTypeName, '.'));
}
@Override
public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
return findType(CharOperation.arrayConcat(packageName, typeName));
}
private boolean isClass(String result) {
if (Strings.isNullOrEmpty(result))
return false;
// if it's the class we're compiling, then of course it's a class
if (result.equals(targetName)) {
return true;
}
InputStream is = null;
try {
// if this is a class we've already compiled, it's a class
is = loader.getResourceAsStream(result);
if (is == null) {
// use our normal class loader now...
String resourceName = result.replace('.', '/') + ".class";
is = parentClassLoader.getResourceAsStream(resourceName);
if (is == null && !result.contains(".")) {
// we couldn't find the class, and it has no package; is it a core class?
is = parentClassLoader.getResourceAsStream("java/lang/" + resourceName);
}
}
if (is == null) {
return false; // if it's a class, we sure couldn't load it
} else {
return true; // we actually loaded the class, so it must be one
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {}
}
}
}
@Override
public boolean isPackage(char[][] parentPackageName, char[] packageName) {
// if the parent is a class, the child can't be a package
String parent = join(parentPackageName, '.');
if (isClass(parent))
return false;
// if the child is a class, it's not a package
String qualifiedName = (parent.isEmpty() ? "" : parent + ".") + new String(packageName);
return !isClass(qualifiedName);
}
@Override
public void cleanup() {
}
private NameEnvironmentAnswer findType(String className) {
String resourceName = className.replace('.', '/') + ".class";
InputStream is = null;
try {
// we're only asking ECJ to compile a single class; we shouldn't need this
if (className.equals(targetName)) {
return new NameEnvironmentAnswer(targetCompilationUnits[0], null);
}
is = loader.getResourceAsStream(resourceName);
if (is == null) {
is = parentClassLoader.getResourceAsStream(resourceName);
}
if (is != null) {
ClassFileReader cfr = new ClassFileReader(ByteStreams.toByteArray(is), className.toCharArray(), true);
return new NameEnvironmentAnswer(cfr, null);
} else {
return null;
}
} catch (ClassFormatException ex) {
throw new RuntimeException(ex);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {}
}
}
}
};
ICompilerRequestor requestor = new ICompilerRequestor() {
@Override
public void acceptResult(CompilationResult result) {
if (result.hasErrors()) {
for (CategorizedProblem problem: result.getProblems()) {
if (problem.isError()) {
problemList.add(problem.getMessage());
}
}
} else {
for (ClassFile clazz: result.getClassFiles()) {
try {
MemJavaFileObject jfo = (MemJavaFileObject) fileManager
.getJavaFileForOutput(StandardLocation.CLASS_OUTPUT,
new String(clazz.fileName()), JavaFileObject.Kind.CLASS, null);
OutputStream os = jfo.openOutputStream();
os.write(clazz.getBytes());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
}
};
problemList.clear();
IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.exitAfterAllProblems();
IProblemFactory problemFactory = new DefaultProblemFactory(Locale.getDefault());
try {
//Compiler compiler = new Compiler(env, policy, getCompilerOptions(), requestor, problemFactory, true);
Compiler compiler = new Compiler(env, policy, compilerOptions, requestor, problemFactory);
compiler.compile(targetCompilationUnits);
if (!problemList.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (String problem: problemList) {
sb.append("\t").append(problem).append("\n");
}
throw new CodegenException("Compilation of " + id + " failed:\n" + source + "\n" + sb.toString());
}
} catch (RuntimeException ex) {
// if we encountered an IOException, unbox and throw it;
// if we encountered a ClassFormatException, box it as an IOException and throw it
// otherwise, it's a legit RuntimeException,
// not one of our checked exceptions boxed as unchecked; just rethrow
Throwable cause = ex.getCause();
if (cause != null) {
if (cause instanceof IOException)
throw (IOException)cause;
else if (cause instanceof ClassFormatException)
throw new IOException(cause);
}
throw ex;
}
}
public CompilerOptions getCompilerOptions() {
return compilerOptions;
}
}

View File

@ -1,180 +1,38 @@
/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.mysema.codegen;
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.Method;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Type;
import java.util.Map;
import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Parameter;
import com.mysema.codegen.model.SimpleType;
import com.mysema.codegen.model.Type;
import com.mysema.codegen.model.TypeCategory;
import com.mysema.codegen.support.ClassUtils;
/**
* EvaluatorFactory is a factory implementation for creating Evaluator instances
*
* @author tiwe
*
*
* @author pgrant
*/
public class EvaluatorFactory {
public interface EvaluatorFactory {
private final MemFileManager fileManager;
private final String classpath;
private final List<String> compilationOptions;
private final JavaCompiler compiler;
private final ClassLoader loader;
public EvaluatorFactory(URLClassLoader parent) {
this(parent, ToolProvider.getSystemJavaCompiler());
}
public EvaluatorFactory(URLClassLoader parent, JavaCompiler compiler) {
this.fileManager = new MemFileManager(parent, compiler.getStandardFileManager(null, null, null));
this.compiler = compiler;
this.classpath = SimpleCompiler.getClassPath(parent);
this.loader = fileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
this.compilationOptions = Arrays.asList("-classpath", classpath, "-g:none");
}
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);
SimpleType idType = new SimpleType(id, "", id);
javaw.beginClass(idType, null);
Parameter[] params = new Parameter[names.length];
for (int i = 0; i < params.length; i++) {
params[i] = new Parameter(names[i], types[i]);
}
for (Map.Entry<String, Object> entry : constants.entrySet()) {
Type type = new ClassType(TypeCategory.SIMPLE, ClassUtils.normalize(entry.getValue().getClass()));
javaw.publicField(type, entry.getKey());
}
if (constants.isEmpty()) {
javaw.beginStaticMethod(projectionType, "eval", params);
} else {
javaw.beginPublicMethod(projectionType, "eval", params);
}
javaw.append(source);
javaw.end();
javaw.end();
// compile
SimpleJavaFileObject javaFileObject = new MemSourceFileObject(id, writer.toString());
Writer out = new StringWriter();
CompilationTask task = compiler.getTask(out, fileManager, null, compilationOptions, null,
Collections.singletonList(javaFileObject));
if (!task.call().booleanValue()) {
throw new CodegenException("Compilation of " + source + " failed.\n" + out.toString());
}
}
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 ClassType(TypeCategory.SIMPLE, classes[i]);
}
return createEvaluator(source, new ClassType(TypeCategory.SIMPLE, projectionType), names,
types, classes, constants);
}
<T> Evaluator<T> createEvaluator(String source, Class<? extends T> projectionType, String[] names, Class<?>[] classes, Map<String, Object> constants);
/**
* Create a new Evaluator instance
*
*
* @param <T>
* projection type
* projection type
* @param source
* expression in Java source code form
* expression in Java source code form
* @param projection
* type of the source expression
* type of the source expression
* @param names
* names of the arguments
* names of the arguments
* @param types
* types of the arguments
* types of the arguments
* @param constants
* @return
*/
@SuppressWarnings("unchecked")
public <T> Evaluator<T> createEvaluator(String source, ClassType projection, String[] names,
Type[] types, Class<?>[] classes, Map<String, Object> constants) {
try {
String id = toId(source, projection.getJavaClass(), types);
Class<?> clazz;
try {
clazz = loader.loadClass(id);
} catch (ClassNotFoundException e) {
compile(source, projection, names, types, id, constants);
// reload
clazz = loader.loadClass(id);
}
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());
}
Method method = clazz.getMethod("eval", classes);
return new MethodEvaluator<T>(method, object, (Class) projection.getJavaClass());
} catch (ClassNotFoundException e) {
throw new CodegenException(e);
} catch (SecurityException e) {
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, Type... types) {
StringBuilder b = new StringBuilder("Q");
b.append("_").append(source.hashCode());
b.append("_").append(returnType.getName().hashCode());
for (Type type : types) {
b.append("_").append(type.getFullName().hashCode());
}
return b.toString().replace('-', '0');
}
@SuppressWarnings(value = "unchecked")
<T> Evaluator<T> createEvaluator(String source, ClassType projection, String[] names, Type[] types, Class<?>[] classes, Map<String, Object> constants);
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.codegen;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
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.JavaCompiler.CompilationTask;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Parameter;
import com.mysema.codegen.model.SimpleType;
import com.mysema.codegen.model.Type;
import com.mysema.codegen.model.TypeCategory;
import com.mysema.codegen.support.ClassUtils;
/**
* JDKEvaluatorFactory is a factory implementation for creating Evaluator instances
*
* @author tiwe
*
*/
public class JDKEvaluatorFactory extends AbstractEvaluatorFactory {
private final MemFileManager fileManager;
private final String classpath;
private final List<String> compilationOptions;
private final JavaCompiler compiler;
public JDKEvaluatorFactory(URLClassLoader parent) {
this(parent, ToolProvider.getSystemJavaCompiler());
}
public JDKEvaluatorFactory(URLClassLoader parent, JavaCompiler compiler) {
this.fileManager = new MemFileManager(parent, compiler.getStandardFileManager(null, null, null));
this.compiler = compiler;
this.classpath = SimpleCompiler.getClassPath(parent);
this.loader = fileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
this.compilationOptions = Arrays.asList("-classpath", classpath, "-g:none");
}
protected void compile(String source, ClassType projectionType, String[] names, Type[] types,
String id, Map<String, Object> constants) throws IOException {
// create source
StringWriter writer = new StringWriter();
JavaWriter javaw = new JavaWriter(writer);
SimpleType idType = new SimpleType(id, "", id);
javaw.beginClass(idType, null);
Parameter[] params = new Parameter[names.length];
for (int i = 0; i < params.length; i++) {
params[i] = new Parameter(names[i], types[i]);
}
for (Map.Entry<String, Object> entry : constants.entrySet()) {
Type type = new ClassType(TypeCategory.SIMPLE, ClassUtils.normalize(entry.getValue().getClass()));
javaw.publicField(type, entry.getKey());
}
if (constants.isEmpty()) {
javaw.beginStaticMethod(projectionType, "eval", params);
} else {
javaw.beginPublicMethod(projectionType, "eval", params);
}
javaw.append(source);
javaw.end();
javaw.end();
// compile
SimpleJavaFileObject javaFileObject = new MemSourceFileObject(id, writer.toString());
Writer out = new StringWriter();
CompilationTask task = compiler.getTask(out, fileManager, null, compilationOptions, null,
Collections.singletonList(javaFileObject));
if (!task.call().booleanValue()) {
throw new CodegenException("Compilation of " + source + " failed.\n" + out.toString());
}
}
}

View File

@ -7,7 +7,6 @@ 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;
@ -18,11 +17,11 @@ import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Type;
import com.mysema.codegen.model.TypeCategory;
import com.mysema.codegen.model.Types;
import com.mysema.codegen.support.Cat;
public class ComplexEvaluationTest {
private EvaluatorFactory factory = new EvaluatorFactory((URLClassLoader) getClass()
.getClassLoader());
private EvaluatorFactory factory = new ECJEvaluatorFactory(getClass().getClassLoader());
@Test
@SuppressWarnings("unchecked")
@ -49,4 +48,116 @@ public class ComplexEvaluationTest {
assertEquals(Arrays.asList("2", "4"), evaluator.evaluate(a_, b_));
}
@Test
@SuppressWarnings("unchecked")
public void ComplexClassLoading() {
ClassType resultType = new ClassType(TypeCategory.LIST, List.class, Types.OBJECTS);
StringBuilder source = new StringBuilder();
source.append("java.util.List<Object[]> rv = new java.util.ArrayList<Object[]>();\n");
source.append("for (com.mysema.codegen.support.Cat cat : (java.util.List<com.mysema.codegen.support.Cat>)cat_){\n");
source.append("for (com.mysema.codegen.support.Cat otherCat : (java.util.List<com.mysema.codegen.support.Cat>)otherCat_){\n");
source.append("rv.add(new Object[]{cat,otherCat});\n");
source.append("}\n");
source.append("}\n");
source.append("return rv;\n");
Cat fuzzy = new Cat("fuzzy");
Cat spot = new Cat("spot");
Cat mittens = new Cat("mittens");
Cat sparkles = new Cat("sparkles");
List<Cat> a_ = Arrays.asList(fuzzy, spot);
List<Cat> b_ = Arrays.asList(mittens, sparkles);
ClassType argType = new ClassType(TypeCategory.LIST, List.class, new ClassType(Cat.class));
Evaluator<List> evaluator = factory.createEvaluator(source.toString(), resultType,
new String[] { "cat_", "otherCat_" }, new Type[] { argType, argType }, new Class[] {
List.class, List.class }, Collections.<String, Object> emptyMap());
Object[][] expResults = { {fuzzy, mittens}, {fuzzy, sparkles}, {spot, mittens}, {spot, sparkles} };
List<Object[]> result = evaluator.evaluate(a_, b_);
assertEquals(expResults.length, result.size());
for (int i = 0; i < expResults.length; i++) {
assertEquals(expResults[i].length, result.get(i).length);
for (int j = 0; j < expResults[i].length; j++) {
assertEquals(expResults[i][j], result.get(i)[j]);
}
}
}
@Test(expected=CodegenException.class)
@SuppressWarnings("unchecked")
public void ComplexClassLoadingFailure() {
ClassType resultType = new ClassType(TypeCategory.LIST, List.class, Types.STRING);
StringBuilder source = new StringBuilder();
source.append("java.util.List<String> rv = (java.util.List<String>) new java.util.ArrayList<Franklin>();\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_));
}
@Test
@SuppressWarnings("unchecked")
public void ComplexPrimitiveType() {
ClassType resultType = new ClassType(TypeCategory.LIST, List.class, Types.BOOLEAN);
StringBuilder source = new StringBuilder();
source.append("java.util.List<Boolean> rv = new java.util.ArrayList<Boolean>();\n");
source.append("for (boolean a : a_){\n");
source.append(" for (boolean b : b_){\n");
source.append(" if (a == 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<Boolean> a_ = Arrays.asList(true, true, true);
List<Boolean> b_ = Arrays.asList(false, false, true);
assertEquals(Arrays.asList(true, true, true), evaluator.evaluate(a_, b_));
}
@Test
@SuppressWarnings("unchecked")
public void ComplexEmbeddedClass() {
ClassType resultType = new ClassType(TypeCategory.LIST, List.class, Types.BOOLEAN);
StringBuilder source = new StringBuilder();
source.append("java.util.List<Boolean> rv = new java.util.ArrayList<Boolean>();\n");
source.append("for (boolean a : a_){\n");
source.append(" for (boolean b : b_){\n");
source.append(" if (a == b && new TestEmbedded().DO_RETURN()){\n");
source.append(" rv.add(a);\n");
source.append(" }\n");
source.append(" }\n");
source.append("}\n");
source.append("return rv;} private static class TestEmbedded { public TestEmbedded() {} public boolean DO_RETURN() { return true; } ");
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<Boolean> a_ = Arrays.asList(true, true, true);
List<Boolean> b_ = Arrays.asList(false, false, true);
assertEquals(Arrays.asList(true, true, true), evaluator.evaluate(a_, b_));
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.codegen;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
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;
import org.junit.Test;
public class ECJEvaluatorFactoryTest {
public static class TestEntity {
private final String name;
public TestEntity(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
private EvaluatorFactory factory;
private List<String> names = Arrays.asList("a", "b");
private List<Class<?>> ints = Arrays.<Class<?>> asList(int.class, int.class);
private List<Class<?>> strings = Arrays.<Class<?>> asList(String.class, String.class);
private List<Class<?>> string_int = Arrays.<Class<?>> asList(String.class, int.class);
@Before
public void setUp() throws IOException {
factory = new ECJEvaluatorFactory(getClass().getClassLoader());
}
@Test
public void Simple() {
for (String expr : Arrays.asList("a.equals(b)", "a.startsWith(b)", "a.equalsIgnoreCase(b)")) {
long start = System.currentTimeMillis();
evaluate(expr, boolean.class, names, strings, Arrays.asList("a", "b"),
Collections.<String, Object> emptyMap());
long duration = System.currentTimeMillis() - start;
System.err.println(expr + " took " + duration + "ms\n");
}
for (String expr : Arrays.asList("a != b", "a < b", "a > b", "a <= b", "a >= b")) {
long start = System.currentTimeMillis();
evaluate(expr, boolean.class, names, ints, Arrays.asList(0, 1),
Collections.<String, Object> emptyMap());
long duration = System.currentTimeMillis() - start;
System.err.println(expr + " took " + duration + "ms\n");
}
}
@Test
public void Results() {
// String + String
test("a + b", String.class, names, strings, Arrays.asList("Hello ", "World"), "Hello World");
// String + int
test("a.substring(b)", String.class, names, string_int,
Arrays.<Object> asList("Hello World", 6), "World");
// int + int
test("a + b", int.class, names, ints, Arrays.asList(1, 2), 3);
}
@Test
public void WithConstants() {
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 CustomType() {
test("a.getName()", String.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<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("return " + source + ";", projectionType,
names.toArray(new String[names.size()]), types.toArray(new Class[types.size()]),
constants);
return evaluator.evaluate(args.toArray());
}
}

View File

@ -19,7 +19,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class EvaluatorFactoryTest {
public class JDKEvaluatorFactoryTest {
public static class TestEntity {
@ -47,7 +47,7 @@ public class EvaluatorFactoryTest {
@Before
public void setUp() throws IOException {
factory = new EvaluatorFactory((URLClassLoader) getClass().getClassLoader());
factory = new JDKEvaluatorFactory((URLClassLoader) getClass().getClassLoader());
}
@Test

View File

@ -0,0 +1,159 @@
/*
* Copyright 2011, Mysema Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mysema.codegen.support;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class Cat {
private int breed;
private java.sql.Date dateField;
public enum Color {
BLUE, GREEN, BROWN
}
private Color eyecolor;
private List<Cat> kittens;
private Cat[] kittenArray;
private Map<String, Cat> kittensByName;
private Cat mate;
private String stringAsSimple;
private java.sql.Time timeField;
private String name;
public void setName(String name) {
this.name = name;
}
private Date birthdate;
private int id;
public Cat() {
this.kittensByName = Collections.emptyMap();
}
public Cat(String name) {
Cat kitten = new Cat();
this.kittens = Arrays.asList(kitten);
this.kittenArray = new Cat[]{kitten};
this.kittensByName = Collections.singletonMap("Kitty", kitten);
this.name = name;
}
public Cat(String name, String kittenName){
this(name);
kittens.get(0).setName(kittenName);
}
public Cat(String name, int id) {
this(name);
this.id = id;
}
public Cat(String name, int id, Date birthdate) {
this(name, id);
this.birthdate = new Date(birthdate.getTime());
this.dateField = new java.sql.Date(birthdate.getTime());
this.timeField = new java.sql.Time(birthdate.getTime());
}
public int getBreed() {
return breed;
}
public java.sql.Date getDateField() {
return dateField;
}
public Color getEyecolor() {
return eyecolor;
}
public List<Cat> getKittens() {
return kittens;
}
public Map<String, Cat> getKittensByName() {
return kittensByName;
}
public Cat getMate() {
return mate;
}
public String getStringAsSimple() {
return stringAsSimple;
}
public java.sql.Time getTimeField() {
return timeField;
}
public void setBreed(int breed) {
this.breed = breed;
}
public void setDateField(java.sql.Date dateField) {
this.dateField = new java.sql.Date(dateField.getTime());
}
public void setEyecolor(Color eyecolor) {
this.eyecolor = eyecolor;
}
public void setKittens(List<Cat> kittens) {
this.kittens = kittens;
}
public void setKittensByName(Map<String, Cat> kittensByName) {
this.kittensByName = kittensByName;
}
public void setMate(Cat mate) {
this.mate = mate;
}
public void setStringAsSimple(String stringAsSimple) {
this.stringAsSimple = stringAsSimple;
}
public void setTimeField(java.sql.Time timeField) {
this.timeField = timeField;
}
public Cat[] getKittenArray() {
return kittenArray;
}
public void setKittenArray(Cat[] kittenArray) {
this.kittenArray = kittenArray.clone();
}
public String toString() {
return name;
}
}

View File

@ -5,4 +5,5 @@ Bundle-ManifestVersion: 2
Import-Template:
javax.annotation.*;version="0",
javax.tools.*;version="0",
org.eclipse.jdt.*;version="3.7.2",
com.google.common.*;version="${guava.version}"