Package javarepl

Source Code of javarepl.Evaluator

package javarepl;

import com.googlecode.totallylazy.*;
import com.googlecode.totallylazy.annotations.multimethod;
import javarepl.expressions.*;
import javarepl.expressions.Value;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.regex.MatchResult;

import static com.googlecode.totallylazy.Either.left;
import static com.googlecode.totallylazy.Either.right;
import static com.googlecode.totallylazy.Files.*;
import static com.googlecode.totallylazy.Option.*;
import static com.googlecode.totallylazy.Predicates.equalTo;
import static com.googlecode.totallylazy.Predicates.where;
import static com.googlecode.totallylazy.Sequences.empty;
import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.totallylazy.predicates.Not.not;
import static java.io.File.pathSeparator;
import static javarepl.Evaluation.evaluation;
import static javarepl.EvaluationClassLoader.evaluationClassLoader;
import static javarepl.EvaluationContext.evaluationContext;
import static javarepl.Result.functions.value;
import static javarepl.Result.noResult;
import static javarepl.Utils.randomIdentifier;
import static javarepl.Utils.urlAsFilePath;
import static javarepl.expressions.Patterns.*;
import static javarepl.rendering.EvaluationClassRenderer.renderExpressionClass;
import static javarepl.rendering.EvaluationClassRenderer.renderMethodSignatureDetection;
import static javarepl.rendering.ExpressionSourceRenderer.renderExpressionSource;
import static javarepl.rendering.ExpressionTokenRenderer.EXPRESSION_TOKEN;
import static javax.tools.ToolProvider.getSystemJavaCompiler;

public class Evaluator {

    private EvaluationClassLoader classLoader;
    private EvaluationContext context;

    public Evaluator() {
        initializeEvaluator(evaluationContext());
    }

    private Evaluator(EvaluationContext context) {
        initializeEvaluator(context);
    }

    public Either<Throwable, Evaluation> evaluate(final String expr) {
        return parseExpression(expr).flatMap(
                new Mapper<Expression, Either<Throwable, Evaluation>>() {
                    public Either<Throwable, Evaluation> call(Expression expression) throws Exception {
                        Either<Throwable, Evaluation> resultForValue = evaluate(expression);
                        if (resultForValue.isLeft() && resultForValue.left() instanceof ExpressionCompilationException && expression instanceof Value) {
                            Either<Throwable, Evaluation> resultForStatement = evaluate(new Statement(expr));
                            return resultForStatement.isLeft() && resultForStatement.left() instanceof ExpressionCompilationException
                                    ? Left.<Throwable, Evaluation>left(new ExpressionCompilationException(sequence(resultForStatement.left().getMessage(), resultForValue.left().getMessage()).unique().toString("\n\n")))
                                    : resultForStatement;
                        }

                        return resultForValue;
                    }
                });
    }

    public Either<Throwable, Expression> parseExpression(String expression) {
        if (isValidImport(expression))
            return createImport(expression);

        if (isValidType(expression))
            return createTypeExpression(expression);

        if (isValidMethod(expression))
            return createMethodExpression(expression);

        if (isValidAssignmentWithType(expression))
            return createAssignmentWithType(expression);

        if (isValidAssignment(expression))
            return createAssignmentExpression(expression);

        return createValueExpression(expression);
    }

    public void addResults(Sequence<Result> result) {
        context = context.addResults(result);
    }

    private Either<Throwable, Expression> createImport(String expression) {
        return right((Expression) new Import(expression, importPattern.match(expression).group(1)));
    }

    private Either<Throwable, Expression> createAssignmentWithType(String expression) {
        try {
            MatchResult match = Patterns.assignmentWithTypeNamePattern.match(expression);
            java.lang.reflect.Method declaredMethod = detectMethod(match.group(1) + " " + randomIdentifier("method") + "(){}");
            return right((Expression) new AssignmentWithType(expression, declaredMethod.getGenericReturnType(), match.group(2), match.group(3)));
        } catch (Exception e) {
            return left(Utils.unwrapException(e));
        }
    }

    private Either<Throwable, Expression> createAssignmentExpression(String expression) {
        MatchResult match = Patterns.assignmentPattern.match(expression);
        return right((Expression) new Assignment(expression, match.group(1), match.group(2)));
    }

    private Either<Throwable, Expression> createTypeExpression(String expression) {
        MatchResult match = typePattern.match(expression);
        return right((Expression) new Type(expression, option(match.group(1)), match.group(2)));
    }

    private Either<Throwable, Expression> createValueExpression(String expression) {
        return right((Expression) new Value(expression));
    }

    public Option<String> lastSource() {
        return context.lastSource();
    }

    public Sequence<Result> results() {
        return context.results();
    }

    public Option<Result> result(String name) {
        return context.result(name);
    }

    public <T extends Expression> Sequence<T> expressionsOfType(Class<T> type) {
        return context.expressionsOfType(type);
    }

    public Sequence<Expression> expressions() {
        return context.expressions();
    }

    public EvaluationContext context() {
        return context;
    }

    public void reset() {
        clearOutputDirectory();
        initializeEvaluator(evaluationContext());
    }

    private void initializeEvaluator(EvaluationContext evaluationContext) {
        context = evaluationContext;
        classLoader = evaluationClassLoader(context.outputDirectory());
    }

    public final Either<Throwable, Evaluation> tryEvaluate(String expression) {
        Evaluator localEvaluator = new Evaluator(context);
        return localEvaluator.evaluate(expression);
    }

    public final Option<java.lang.reflect.Type> typeOfExpression(String expression) {
        Either<? extends Throwable, Evaluation> evaluation = tryEvaluate(expression);

        Option<java.lang.reflect.Type> expressionType = none();
        if (evaluation.isRight()) {
            Option<Result> result = evaluation.right().result();
            if (!result.isEmpty()) {
                expressionType = some(result.get().type());
            }
        }

        return expressionType;
    }

    public final Option<Class> classFrom(String expression) {
        try {
            return some(detectClass(expression));
        } catch (Throwable e) {
            return none();
        }
    }

    public void clearOutputDirectory() {
        deleteFiles(context.outputDirectory());
        delete(context.outputDirectory());
    }

    public void addClasspathUrl(URL classpathUrl) {
        classLoader.addURL(classpathUrl);
    }

    public File outputDirectory() {
        return context.outputDirectory();
    }

    @multimethod
    private Either<Throwable, Evaluation> evaluate(Expression expression) {
        return new multi() {
        }.<Either<Throwable, Evaluation>>methodOption(expression).getOrElse(evaluateExpression(expression));
    }

    @multimethod
    private Either<Throwable, Evaluation> evaluate(Type expression) {
        if (getSystemJavaCompiler() == null) {
            return left((Throwable) new FileNotFoundException("Java compiler not found." +
                    "This can occur when JavaREPL was run with JRE instead of JDK or JDK is not configured correctly."));
        }

        try {
            File outputPath = directory(context.outputDirectory(), expression.typePackage().getOrElse("").replace('.', File.separatorChar));
            File outputJavaFile = file(outputPath, expression.type() + ".java");

            String sources = renderExpressionClass(context, expression.type(), expression)
                    .replace(EXPRESSION_TOKEN, renderExpressionSource(expression));

            Files.write(sources.getBytes(), outputJavaFile);
            compile(outputJavaFile);

            classLoader.loadClass(expression.canonicalName());

            context = context.addExpression(expression).lastSource(sources);

            return right(evaluation(expression, noResult()));
        } catch (Exception e) {
            return left(Utils.unwrapException(e));
        }
    }

    private Either<Throwable, Expression> createMethodExpression(String expression) {
        try {
            java.lang.reflect.Method declaredMethod = detectMethod(expression);
            return right((Expression) new Method(expression, declaredMethod.getGenericReturnType(), declaredMethod.getName(), sequence(declaredMethod.getParameterTypes())));
        } catch (Exception e) {
            return left(Utils.unwrapException(e));
        }
    }

    private java.lang.reflect.Method detectMethod(String expression) throws Exception {
        final String className = randomIdentifier("Method");
        final File outputJavaFile = file(context.outputDirectory(), className + ".java");

        final String sources = renderMethodSignatureDetection(context, className, expression);
        Files.write(sources.getBytes(), outputJavaFile);

        compile(outputJavaFile);

        Class<?> expressionClass = classLoader.loadClass(className);

        return expressionClass.getDeclaredMethods()[0];
    }

    private Class detectClass(String expression) throws Exception {
        final String className = randomIdentifier("Class");
        final File outputJavaFile = file(context.outputDirectory(), className + ".java");

        final String sources = renderMethodSignatureDetection(context, className, "public " + expression + " detectClass(){return null;}");
        Files.write(sources.getBytes(), outputJavaFile);

        compile(outputJavaFile);

        Class expressionClass = classLoader.loadClass(className);

        return expressionClass.getDeclaredMethods()[0].getReturnType();
    }

    private Either<Throwable, Evaluation> evaluateExpression(final Expression expression) {
        final String className = randomIdentifier("Evaluation");

        try {
            EvaluationContext newContext = context.removeExpressionWithKey(expression.key());

            File outputJavaFile = file(context.outputDirectory(), className + ".java");
            final String sources = renderExpressionClass(newContext, className, expression)
                    .replace(EXPRESSION_TOKEN, renderExpressionSource(expression));

            Files.write(sources.getBytes(), outputJavaFile);

            compile(outputJavaFile);

            Class<?> expressionClass = classLoader.loadClass(className);
            Constructor<?> constructor = expressionClass.getDeclaredConstructor(EvaluationContext.class);

            final Object expressionInstance = constructor.newInstance(newContext);

            java.lang.reflect.Method method = expressionClass.getMethod("evaluate");
            Object resultObject = method.invoke(expressionInstance);

            Sequence<Result> modifiedResults = modifiedResults(expressionInstance);

            if (resultObject != null || !method.getReturnType().equals(void.class)) {
                Result result = Result.result(nextResultKeyFor(expression), resultObject, typeFor(expression));
                context = newContext.addExpression(expression).addResults(modifiedResults.append(result)).lastSource(sources);
                return right(evaluation(expression, some(result)));
            } else {
                context = newContext.addExpression(expression).addResults(modifiedResults).lastSource(sources);
                return right(evaluation(expression, noResult()));
            }
        } catch (Throwable e) {
            return left(Utils.unwrapException(e));
        }
    }

    private Sequence<Result> modifiedResults(final Object expressionInstance) {
        return sequence(expressionInstance.getClass().getDeclaredFields())
                .reduceLeft(new Reducer<Field, Sequence<Result>>() {
                    public Sequence<Result> call(Sequence<Result> results, Field field) throws Exception {
                        Option<Result> result = result(field.getName()).filter(where(value(), not(equalTo(field.get(expressionInstance)))));

                        if (result.isEmpty())
                            return results;

                        return results.append(Result.result(field.getName(), field.get(expressionInstance)));

                    }

                    public Sequence<Result> identity() {
                        return empty(Result.class);
                    }
                });
    }

    private String nextResultKeyFor(Expression expression) {
        return (expression instanceof Value)
                ? context.nextResultKey()
                : expression.key();
    }

    private Option<java.lang.reflect.Type> typeFor(Expression expression) {
        return (expression instanceof AssignmentWithType)
                ? Option.some(((AssignmentWithType) expression).type())
                : Option.<java.lang.reflect.Type>none();
    }

    private void compile(File file) throws Exception {
        String classpath =
                sequence(System.getProperty("java.class.path"))
                        .join(sequence(classLoader.getURLs()).map(urlAsFilePath()))
                        .toString(pathSeparator);
        JavaCompiler compiler = getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sequence(file));
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, sequence("-cp", classpath), null, compilationUnits);

        try {
            if (!task.call())
                throw new ExpressionCompilationException(file, diagnostics.getDiagnostics());
        } finally {
            fileManager.close();
        }
    }
}
TOP

Related Classes of javarepl.Evaluator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.