All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mysema.codegen.EvaluatorFactory Maven / Gradle / Ivy

/*
 * Copyright (c) 2010 Mysema Ltd.
 * All rights reserved.
 * 
 */
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 java.util.Map;

import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;


/**
 * EvaluatorFactory is a factory implementation for creating Evaluator instances
 * 
 * @author tiwe
 * 
 */
public class EvaluatorFactory {

    private final MemFileManager fileManager;
    
    private final String classpath;

    private final List 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 constants) throws IOException {
        // create source
        StringWriter writer = new StringWriter();
        JavaWriter javaw = new JavaWriter(writer);
        javaw.beginClass(id, null);
        String[] params = new String[names.length];
        for (int i = 0; i < params.length; i++) {
            params[i] = ClassUtils.getName(types[i]) + " " + names[i];
        }
        
        for (Map.Entry entry : constants.entrySet()){
            String className = ClassUtils.getName(ClassUtils.normalize(entry.getValue().getClass()));
            javaw.publicField(className, entry.getKey());
        }

        if (constants.isEmpty()){
            javaw.beginStaticMethod(ClassUtils.getName(projectionType), "eval", params);    
        }else{
            javaw.beginPublicMethod(ClassUtils.getName(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());
        }

    }
    
    @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 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,
                Type projection, 
                String[] names, 
                Type[] types,  
                Class[] classes,
                Map constants) {
        try {
            String id = toId(source, projection, 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 entry : constants.entrySet()){
                Field field = clazz.getField(entry.getKey());
                field.set(object, entry.getValue());
            }

            Method method = clazz.getMethod("eval", classes);
            return new MethodEvaluator(method, object, 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, Type 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.getName().hashCode());
        }
        return b.toString().replace('-', '0');
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy