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

nl.hsac.fitnesse.slim.interaction.FixtureFactory Maven / Gradle / Ivy

There is a newer version: 5.3.17
Show newest version
package nl.hsac.fitnesse.slim.interaction;

import fitnesse.slim.fixtureInteraction.DefaultInteraction;
import fitnesse.slim.fixtureInteraction.FixtureInteraction;
import fitnesse.slim.fixtureInteraction.InteractionAwareFixture;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * Factory to create fixture instances that behave like the way they do when Slim invokes them.
 * This means #aroundSlimInvoke() is applied around their normal methods, so that they can be used by
 * 'normal' Java classes and not just the wiki.
 * It is not exactly the same as Slim does, as instance created by this factory do not have #aroundSlimInvoke() applied
 * to calls to 'final' methods. This is a limitation of the technology used.
 */
public class FixtureFactory {
    private static final Map, Factory> FACTORIES = new HashMap, Factory>();
    private FixtureInteraction interaction = null;

    /**
     * @return interaction that will be passed to #aroundSlimInvoke().
     */
    public FixtureInteraction getInteraction() {
        if (interaction == null) {
            interaction = new DefaultInteraction();
        }
        return interaction;
    }

    /**
     * @param interaction interaction to pass to #aroundSlimInvoke().
     */
    public void setInteraction(FixtureInteraction interaction) {
        this.interaction = interaction;
    }

    /**
     * Creates new instance of fixture.
     * @param clazz class to instantiate.
     * @param  type to create.
     * @return instance of clazz (subclass, actually) that will have #aroundSlimInvoke() invoked on each method call.
     */
    public  T create(Class clazz) {
        return create(clazz, null, null);
    }

    /**
     * Creates new instance of fixture.
     * @param clazz class to instantiate.
     * @param constructorArg argument to pass to constructor of clazz.
     * @param  type to create.
     * @return instance of clazz (subclass, actually) that will have #aroundSlimInvoke() invoked on each method call.
     */
    public  T create(Class clazz, int constructorArg) {
        return create(clazz, new Class[] {int.class}, new Object[] {constructorArg});
    }

    /**
     * Creates new instance of fixture.
     * @param clazz class to instantiate.
     * @param constructorArg argument to pass to constructor of clazz.
     * @param  type to create.
     * @return instance of clazz (subclass, actually) that will have #aroundSlimInvoke() invoked on each method call.
     */
    public  T create(Class clazz, long constructorArg) {
        return create(clazz, new Class[] {long.class}, new Object[] {constructorArg});
    }

    /**
     * Creates new instance of fixture.
     * @param clazz class to instantiate.
     * @param constructorArg argument to pass to constructor of clazz.
     * @param  type to create.
     * @return instance of clazz (subclass, actually) that will have #aroundSlimInvoke() invoked on each method call.
     */
    public  T create(Class clazz, double constructorArg) {
        return create(clazz, new Class[] {double.class}, new Object[] {constructorArg});
    }

    /**
     * Creates new instance of fixture.
     * @param clazz class to instantiate.
     * @param constructorArg argument to pass to constructor of clazz.
     * @param  type to create.
     * @return instance of clazz (subclass, actually) that will have #aroundSlimInvoke() invoked on each method call.
     */
    public  T create(Class clazz, boolean constructorArg) {
        return create(clazz, new Class[] {boolean.class}, new Object[] {constructorArg});
    }

    /**
     * Creates new instance of fixture.
     * @param clazz class to instantiate.
     * @param constructorArgs arguments to pass to constructor of clazz.
     * @param  type to create.
     * @return instance of clazz (subclass, actually) that will have #aroundSlimInvoke() invoked on each method call.
     */
    public  T create(Class clazz, Object... constructorArgs) {
        T result;
        if (constructorArgs != null && constructorArgs.length > 0) {
            Class[] types = new Class[constructorArgs.length];
            for (int i = 0; i < constructorArgs.length; i++) {
                types[i] = constructorArgs[i].getClass();
            }
            result = create(clazz, types, constructorArgs);
        } else {
            result = create(clazz, null, null);
        }
        return result;
    }

    /**
     * Creates new instance of fixture.
     * @param clazz class to instantiate.
     * @param constructorTypes types of arguments used to determine which constructor to use.
     * @param constructorArgs arguments to pass to constructor of clazz.
     * @param  type to create.
     * @return instance of clazz (subclass, actually) that will have #aroundSlimInvoke() invoked on each method call.
     */
    public  T create(Class clazz, Class[] constructorTypes, Object[] constructorArgs) {
        MethodInterceptor callback = createCallback();

        T result;
        if (FACTORIES.containsKey(clazz)) {
            Factory factory = FACTORIES.get(clazz);
            result = createUsingFactory(callback, factory, constructorTypes, constructorArgs);
        } else {
            result = createFirst(callback, clazz, constructorTypes, constructorArgs);
            FACTORIES.put(clazz, (Factory) result);
        }
        return result;
    }

    protected  T createUsingFactory(Callback callback, Factory factory, Class[] constructorTypes, Object[] constructorArgs) {
        Callback[] callbacks = new Callback[] { callback };

        T result;
        if (constructorArgs != null && constructorArgs.length > 0) {
            result = (T) factory.newInstance(constructorTypes, constructorArgs, callbacks);
        } else {
            result = (T) factory.newInstance(callbacks);
        }
        return result;
    }

    protected  T createFirst(Callback callback, Class clazz, Class[] constructorTypes, Object[] constructorArgs) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(callback);

        T result;
        if (constructorArgs != null && constructorArgs.length > 0) {
            result = (T) enhancer.create(constructorTypes, constructorArgs);
        } else {
            result = (T) enhancer.create();
        }
        return result;
    }

    protected MethodInterceptor createCallback() {
        FixtureInteraction nestedInteraction = getInteraction();
        return new LikeSlimInteraction(nestedInteraction);
    }

    protected static class LikeSlimInteraction implements MethodInterceptor {
        private final FixtureInteraction interaction;
        private boolean aroundInvoked = false;

        public LikeSlimInteraction(FixtureInteraction fixtureInteraction) {
            interaction = fixtureInteraction;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            if (!aroundInvoked
                    && Modifier.isPublic(method.getModifiers())
                    && !method.getDeclaringClass().equals(Object.class)
                    && !"aroundSlimInvoke".equals(method.getName())) {
                aroundInvoked = true;
                try {
                    return ((InteractionAwareFixture) obj).aroundSlimInvoke(interaction, method, args);
                } finally {
                    aroundInvoked = false;
                }
            } else {
                return proxy.invokeSuper(obj, args);
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy