fitnesse.slim.StatementExecutor.java Source code

Java tutorial

Introduction

Here is the source code for fitnesse.slim.StatementExecutor.java

Source

// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.slim;

import fitnesse.slim.converters.MapEditor;
import fitnesse.slim.fixtureInteraction.DefaultInteraction;
import fitnesse.slim.fixtureInteraction.FixtureInteraction;
import org.apache.commons.lang.StringUtils;

import java.beans.PropertyEditorManager;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * This is the API for executing a SLIM statement. This class should not know
 * about the syntax of a SLIM statement.
 */

public class StatementExecutor implements StatementExecutorInterface {

    private static final String SLIM_HELPER_LIBRARY_INSTANCE_NAME = "SlimHelperLibrary";
    private Map<String, Object> instances = new HashMap<String, Object>();
    private List<Library> libraries = new ArrayList<Library>();

    private List<MethodExecutor> executorChain = new ArrayList<MethodExecutor>();

    private VariableStore variables = new VariableStore();
    private List<String> paths = new ArrayList<String>();

    private boolean stopRequested = false;
    private String lastActor;

    public StatementExecutor() {
        PropertyEditorManager.registerEditor(Map.class, MapEditor.class);

        executorChain.add(new FixtureMethodExecutor(instances));
        executorChain.add(new SystemUnderTestMethodExecutor(instances));
        executorChain.add(new LibraryMethodExecutor(libraries));

        addSlimHelperLibraryToLibraries();
    }

    private void addSlimHelperLibraryToLibraries() {
        SlimHelperLibrary slimHelperLibrary = new SlimHelperLibrary();
        slimHelperLibrary.setStatementExecutor(this);
        libraries.add(new Library(SLIM_HELPER_LIBRARY_INSTANCE_NAME, slimHelperLibrary));
    }

    public void setVariable(String name, Object value) {
        variables.setSymbol(name, new MethodExecutionResult(value, Object.class));
    }

    private void setVariable(String name, MethodExecutionResult value) {
        variables.setSymbol(name, value);
    }

    public Object addPath(String path) {
        paths.add(path);
        return "OK";
    }

    public Object getInstance(String instanceName) {
        Object instance = instances.get(instanceName);
        if (instance != null) {
            return instance;
        }

        for (Library library : libraries) {
            if (library.instanceName.equals(instanceName)) {
                return library.instance;
            }
        }
        throw new SlimError(String.format("message:<<NO_INSTANCE %s.>>", instanceName));
    }

    public Converter getConverter(Class<?> k) {
        return ConverterSupport.getConverter(k);
    }

    public Object create(String instanceName, String className, Object[] args) {
        try {
            if (hasStoredActor(className)) {
                addToInstancesOrLibrary(instanceName, getStoredActor(className));
            } else {
                String replacedClassName = variables.replaceSymbolsInString(className);
                Object instance = createInstanceOfConstructor(replacedClassName, replaceSymbols(args));
                addToInstancesOrLibrary(instanceName, instance);
            }
            return "OK";
        } catch (SlimError e) {
            return couldNotInvokeConstructorException(className, args);
        } catch (IllegalArgumentException e) {
            return couldNotInvokeConstructorException(className, args);
        } catch (Throwable e) {
            return exceptionToString(e);
        }
    }

    private void addToInstancesOrLibrary(String instanceName, Object instance) {
        if (isLibrary(instanceName)) {
            libraries.add(new Library(instanceName, instance));
        } else {
            setInstance(instanceName, instance);
        }
    }

    public void setInstance(String instanceName, Object instance) {
        instances.put(instanceName, instance);
    }

    private boolean hasStoredActor(String nameWithDollar) {
        if (!variables.containsValueFor(nameWithDollar)) {
            return false;
        }
        Object potentialActor = getStoredActor(nameWithDollar);
        return potentialActor != null && !(potentialActor instanceof String);
    }

    private Object getStoredActor(String nameWithDollar) {
        return variables.getStored(nameWithDollar);
    }

    private boolean isLibrary(String instanceName) {
        return instanceName.startsWith("library");
    }

    private String couldNotInvokeConstructorException(String className, Object[] args) {
        return exceptionToString(new SlimError(
                String.format("message:<<COULD_NOT_INVOKE_CONSTRUCTOR %s[%d]>>", className, args.length)));
    }

    private Object createInstanceOfConstructor(String className, Object[] args) throws IllegalArgumentException,
            InstantiationException, IllegalAccessException, InvocationTargetException {
        Class<?> k = searchPathsForClass(className);
        Constructor<?> constructor = getConstructor(k.getConstructors(), args);
        if (constructor == null)
            throw new SlimError(String.format("message:<<NO_CONSTRUCTOR %s>>", className));

        Object newInstance = newInstance(args, constructor);
        if (newInstance instanceof StatementExecutorConsumer) {
            ((StatementExecutorConsumer) newInstance).setStatementExecutor(this);
        }
        return newInstance;

    }

    private Object newInstance(Object[] args, Constructor<?> constructor)
            throws IllegalAccessException, InstantiationException, InvocationTargetException {
        Object[] initargs = ConverterSupport.convertArgs(args, constructor.getParameterTypes());

        FixtureInteraction interaction = SlimService.getInteractionClass().newInstance();
        return interaction.newInstance(constructor, initargs);
    }

    private Class<?> searchPathsForClass(String className) {
        Class<?> k = getClass(className);
        if (k != null)
            return k;
        List<String> reversedPaths = new ArrayList<String>(paths);
        Collections.reverse(reversedPaths);
        for (String path : reversedPaths) {
            k = getClass(path + "." + className);
            if (k != null)
                return k;
        }
        throw new SlimError(String.format("message:<<NO_CLASS %s>>", className));
    }

    private Class<?> getClass(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    private Constructor<?> getConstructor(Constructor<?>[] constructors, Object[] args) {
        for (Constructor<?> constructor : constructors) {
            Class<?> arguments[] = constructor.getParameterTypes();
            if (arguments.length == args.length)
                return constructor;
        }
        return null;
    }

    public Object call(String instanceName, String methodName, Object... args) {
        try {
            return getMethodExecutionResult(instanceName, methodName, args).returnValue();
        } catch (Throwable e) {
            return exceptionToString(e);
        }
    }

    private MethodExecutionResult getMethodExecutionResult(String instanceName, String methodName, Object... args)
            throws Throwable {
        MethodExecutionResults results = new MethodExecutionResults();
        for (int i = 0; i < executorChain.size(); i++) {
            MethodExecutionResult result = executorChain.get(i).execute(instanceName, methodName,
                    replaceSymbols(args));
            if (result.hasResult()) {
                return result;
            }
            results.add(result);
        }
        return results.getFirstResult();
    }

    public Object callAndAssign(String variable, String instanceName, String methodName, Object[] args) {
        try {
            MethodExecutionResult result = getMethodExecutionResult(instanceName, methodName, args);
            setVariable(variable, result);
            return result.returnValue();
        } catch (Throwable e) {
            return exceptionToString(e);
        }
    }

    private Object[] replaceSymbols(Object[] args) {
        return variables.replaceSymbols(args);
    }

    private String exceptionToString(Throwable exception) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter pw = new PrintWriter(stringWriter);
        exception.printStackTrace(pw);
        if (exception.getClass().toString().contains("StopTest")) {
            stopRequested = true;
            return SlimServer.EXCEPTION_STOP_TEST_TAG + stringWriter.toString();
        } else {
            return SlimServer.EXCEPTION_TAG + stringWriter.toString();
        }
    }

    public boolean stopHasBeenRequested() {
        return stopRequested;
    }

    public void reset() {
        stopRequested = false;
    }

}