public interface Interceptor extends Serializable
{
    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    /**
     * Intercept the specified {@link Invocation}.
     * 
     * @param invocation
     * @return return value of the method
     * @throws Throwable
     */
    Object intercept(Invocation invocation) throws Throwable;
} public interface Invocation
{
    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    /**
     * Returns the arguments being passed to this method invocation. Changes in the elements of this array will be
     * propagated to the recipient of this invocation.
     * 
     * @return the arguments being passed to this method invocation
     */
    Object[] getArguments();

    /**
     * Returns the method being called.
     * 
     * @return the method being called
     */
    Method getMethod();

    /**
     * Returns the proxy object on which this invocation was invoked.
     * 
     * @return the proxy object on which this invocation was invoked
     */
    Object getProxy();

    /**
     * Called in order to let the invocation proceed.
     * 
     * @return the return value of the invocation
     * @throws Throwable
     *             any exception or error that was thrown as a result of this invocation
     */
    Object proceed() throws Throwable;
} public interface Invoker extends Serializable
{
    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    /**
     * "Invokes" the method. Implementation should throw a {@link org.apache.commons.proxy2.exception.InvokerException}
     * if problems arise while trying to invoke the method.
     * 
     * @param proxy
     *            the proxy2 object
     * @param method
     *            the method being invoked
     * @param arguments
     *            the arguments
     * @return the return value
     * @throws Throwable
     *             thrown by the implementation
     */
    Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable;
} public interface ProxyFactory
{
    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    /**
     * Learn whether this {@link ProxyFactory} is capable of creating a proxy for the specified set of classes.
     * 
     * @param proxyClasses
     *            the proxy2 classes
     * @return boolean
     */
    boolean canProxy(Class<?>... proxyClasses);

    /**
     * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>. The proxy will be
     * generated using the current thread's "context class loader."
     * 
     * @param delegateProvider
     *            the delegate provider
     * @param proxyClasses
     *            the interfaces that the proxy should implement
     * @return a proxy which delegates to the object provided by the target object provider
     */
    <T> T createDelegatorProxy(ObjectProvider<?> delegateProvider, Class<?>... proxyClasses);

    /**
     * Creates a proxy which delegates to the object provided by <code>delegateProvider</code>.
     * 
     * @param classLoader
     *            the class loader to use when generating the proxy
     * @param delegateProvider
     *            the delegate provider
     * @param proxyClasses
     *            the interfaces that the proxy should implement
     * @return a proxy which delegates to the object provided by the target <code>delegateProvider>
     */
    <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> delegateProvider, Class<?>... proxyClasses);

    /**
     * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
     * <code>target</code> object. The proxy will be generated using the current thread's "context class loader."
     * 
     * @param target
     *            the target object
     * @param interceptor
     *            the method interceptor
     * @param proxyClasses
     *            the interfaces that the proxy should implement
     * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
     *         <code>target</code> object.
     */
    <T> T createInterceptorProxy(Object target, Interceptor interceptor, Class<?>... proxyClasses);

    /**
     * Creates a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
     * <code>target</code> object.
     * 
     * @param classLoader
     *            the class loader to use when generating the proxy
     * @param target
     *            the target object
     * @param interceptor
     *            the method interceptor
     * @param proxyClasses
     *            the interfaces that the proxy should implement.
     * @return a proxy which passes through a {@link Interceptor interceptor} before eventually reaching the
     *         <code>target</code> object.
     */
    <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
            Class<?>... proxyClasses);

    /**
     * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations. The proxy will be
     * generated using the current thread's "context class loader."
     * 
     * @param invoker
     *            the invoker
     * @param proxyClasses
     *            the interfaces that the proxy should implement
     * @return a proxy which uses the provided {@link Invoker} to handle all method invocations
     */
    <T> T createInvokerProxy(Invoker invoker, Class<?>... proxyClasses);

    /**
     * Creates a proxy which uses the provided {@link Invoker} to handle all method invocations.
     * 
     * @param classLoader
     *            the class loader to use when generating the proxy
     * @param invoker
     *            the invoker
     * @param proxyClasses
     *            the interfaces that the proxy should implement
     * @return a proxy which uses the provided {@link Invoker} to handle all method invocations
     */
    <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class<?>... proxyClasses);
} public final class ProxyUtils
{
    //******************************************************************************************************************
    // Fields
    //******************************************************************************************************************

    public static final Object[] EMPTY_ARGUMENTS = ArrayUtils.EMPTY_OBJECT_ARRAY;
    public static final Class<?>[] EMPTY_ARGUMENT_TYPES = ArrayUtils.EMPTY_CLASS_ARRAY;
    private static final Map<Class<?>, Class<?>> WRAPPER_CLASS_MAP;
    private static final Map<Class<?>, Object> NULL_VALUE_MAP;

    //******************************************************************************************************************
    // Static Methods
    //******************************************************************************************************************

    static
    {
        final Map<Class<?>, Class<?>> wrappers = new HashMap<Class<?>, Class<?>>();
        wrappers.put(Integer.TYPE, Integer.class);
        wrappers.put(Character.TYPE, Character.class);
        wrappers.put(Boolean.TYPE, Boolean.class);
        wrappers.put(Short.TYPE, Short.class);
        wrappers.put(Long.TYPE, Long.class);
        wrappers.put(Float.TYPE, Float.class);
        wrappers.put(Double.TYPE, Double.class);
        wrappers.put(Byte.TYPE, Byte.class);
        WRAPPER_CLASS_MAP = Collections.unmodifiableMap(wrappers);

        final Map<Class<?>, Object> nullValues = new HashMap<Class<?>, Object>();
        nullValues.put(Integer.TYPE, Integer.valueOf(0));
        nullValues.put(Long.TYPE, Long.valueOf(0));
        nullValues.put(Short.TYPE, Short.valueOf((short) 0));
        nullValues.put(Byte.TYPE, Byte.valueOf((byte) 0));
        nullValues.put(Float.TYPE, Float.valueOf(0.0f));
        nullValues.put(Double.TYPE, Double.valueOf(0.0));
        nullValues.put(Character.TYPE, Character.valueOf((char) 0));
        nullValues.put(Boolean.TYPE, Boolean.FALSE);
        NULL_VALUE_MAP = Collections.unmodifiableMap(nullValues);
    }

    /**
     * <p>
     * Gets an array of {@link Class} objects representing all interfaces implemented by the given class and its
     * superclasses.
     * </p>
     * <p/>
     * <p>
     * The order is determined by looking through each interface in turn as declared in the source file and following
     * its hierarchy up. Then each superclass is considered in the same way. Later duplicates are ignored, so the order
     * is maintained.
     * </p>
     * <p/>
     * <b>Note</b>: Implementation of this method was "borrowed" from <a href="http://commons.apache.org/lang/">Apache
     * Commons Lang</a> to avoid a dependency.
     * </p>
     * 
     * @param cls
     *            the class to look up, may be <code>null</code>
     * @return an array of {@link Class} objects representing all interfaces implemented by the given class and its
     *         superclasses or <code>null</code> if input class is null.
     */
    public static Class<?>[] getAllInterfaces(Class<?> cls)
    {
        return cls == null ? null : ClassUtils.getAllInterfaces(cls).toArray(ArrayUtils.EMPTY_CLASS_ARRAY);
    }

    /**
     * Returns the class name as you would expect to see it in Java code.
     * <p/>
     * <b>Examples:</b>
     * <ul>
     * <li>getJavaClassName( Object[].class ) == "Object[]"</li>
     * <li>getJavaClassName( Object[][].class ) == "Object[][]"</li>
     * <li>getJavaClassName( Integer.TYPE ) == "int"</li>
     * </p>
     * 
     * @param clazz
     *            the class
     * @return the class' name as you would expect to see it in Java code
     */
    public static String getJavaClassName(Class<?> clazz)
    {
        if (clazz.isArray())
        {
            return getJavaClassName(clazz.getComponentType()) + "[]";
        }
        return clazz.getName();
    }

    /**
     * Returns the wrapper class for the given primitive type.
     * 
     * @param primitiveType
     *            the primitive type
     * @return the wrapper class
     */
    public static Class<?> getWrapperClass(Class<?> primitiveType)
    {
        return WRAPPER_CLASS_MAP.get(primitiveType);
    }

    /**
     * Returns the proper "null value" as specified by the Java language.
     * 
     * @param type
     *            the type
     * @return the null value
     */
    public static <T> T nullValue(Class<T> type)
    {
        @SuppressWarnings("unchecked") // map only contains matching type/value entries
        final T result = (T) NULL_VALUE_MAP.get(type);
        return result;
    }

    /**
     * Learn whether the specified method is/overrides {@link Object#equals(Object)}.
     * 
     * @param method
     *            to compare
     * @return <code>true</code> for a method with signature <code>equals(Object)</code>
     */
    public static boolean isEqualsMethod(Method method)
    {
        return "equals".equals(method.getName()) && method.getParameterTypes().length == 1
                && Object.class.equals(method.getParameterTypes()[0]);
    }

    /**
     * Learn whether the specified method is/overrides {@link Object#hashCode()}.
     * 
     * @param method
     *            to compare
     * @return true for a method with signature <code>hashCode()</code>
     */
    public static boolean isHashCode(Method method)
    {
        return "hashCode".equals(method.getName()) && method.getParameterTypes().length == 0;
    }

    /**
     * Get a {@link ProxyFactory} that delegates to discoverable {@link ProxyFactory} service providers.
     * 
     * @return {@link ProxyFactory}
     */
    public static ProxyFactory proxyFactory()
    {
        return DefaultProxyFactory.INSTANCE;
    }

    private ProxyUtils()
    {
        // Hiding constructor in utility class!
    }
} public class SwitchInterceptor implements Interceptor, Serializable
{
    //******************************************************************************************************************
    // Fields
    //******************************************************************************************************************

    private static final long serialVersionUID = 1L;

    private final List<Pair<InvocationMatcher, Interceptor>> cases
        = new CopyOnWriteArrayList<Pair<InvocationMatcher, Interceptor>>();

    //******************************************************************************************************************
    // Constructors
    //******************************************************************************************************************

    public SwitchInterceptor()
    {
    }

    //******************************************************************************************************************
    // Interceptor Implementation
    //******************************************************************************************************************

    @Override
    public Object intercept(Invocation invocation) throws Throwable
    {
        for (Pair<InvocationMatcher, Interceptor> currentCase : cases)
        {
            if (currentCase.getLeft().matches(invocation))
            {
                return currentCase.getRight().intercept(invocation);
            }
        }
        return invocation.proceed();
    }

    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    public CaseBuilder when(InvocationMatcher matcher)
    {
        return new CaseBuilder(matcher);
    }

    //******************************************************************************************************************
    // Inner Classes
    //******************************************************************************************************************

    public class CaseBuilder
    {
        private final InvocationMatcher matcher;

        private CaseBuilder(InvocationMatcher matcher)
        {
            this.matcher = matcher;
        }

        public SwitchInterceptor then(Interceptor interceptor)
        {
            cases.add(new ImmutablePair<InvocationMatcher, Interceptor>(matcher, interceptor));
            return SwitchInterceptor.this;
        }
    }
} public interface ArgumentMatcher<T>
{
    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    boolean matches(T argument);
} public interface InvocationMatcher
{
    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    boolean matches(Invocation invocation);
} public class NullInvoker implements Invoker, Serializable
{
    /** Serialization version */
    private static final long serialVersionUID = 1L;

    /**
     * Statically available instance.
     */
    public static final NullInvoker INSTANCE = new NullInvoker();

    //******************************************************************************************************************
    // Invoker Implementation
    //******************************************************************************************************************

    /**
     * {@inheritDoc}
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        final Class<?> returnType = method.getReturnType();
        return ProxyUtils.nullValue(returnType);
    }
} public class RecordedInvocation
{
    //******************************************************************************************************************
    // Fields
    //******************************************************************************************************************

    private final Method invokedMethod;
    private final Object[] arguments;

    //******************************************************************************************************************
    // Constructors
    //******************************************************************************************************************

    /**
     * Create a new RecordedInvocation instance.
     * 
     * @param invokedMethod
     * @param arguments
     */
    public RecordedInvocation(Method invokedMethod, Object[] arguments)
    {
        this.invokedMethod = invokedMethod;
        this.arguments = ArrayUtils.nullToEmpty(ArrayUtils.clone(arguments));
    }

    //******************************************************************************************************************
    // Getter/Setter Methods
    //******************************************************************************************************************

    /**
     * Get the invokedMethod.
     * 
     * @return Method
     */
    public Method getInvokedMethod()
    {
        return invokedMethod;
    }

    //******************************************************************************************************************
    // Canonical Methods
    //******************************************************************************************************************

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
        StringBuilder buffer = new StringBuilder();
        buffer.append(invokedMethod.getDeclaringClass().getName());
        buffer.append(".");
        buffer.append(invokedMethod.getName());
        buffer.append("(");
        int count = arguments.length;
        for (int i = 0; i < count; i++)
        {
            Object arg = arguments[i];
            if (i > 0)
            {
                buffer.append(", ");
            }
            convert(buffer, arg);
        }
        buffer.append(")");
        return buffer.toString();
    }

    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    /**
     * Add a string representation of <code>input</code> to <code>buffer</code>.
     * 
     * @param buffer
     * @param input
     */
    protected void convert(StringBuilder buffer, Object input)
    {
        if (input == null)
        {
            buffer.append("<null>");
            return;
        }

        // Primitive types, and non-object arrays
        // use toString().
        if (!(input instanceof Object[]))
        {
            buffer.append(input.toString());
            return;
        }
        buffer.append("(");
        buffer.append(ProxyUtils.getJavaClassName(input.getClass()));
        buffer.append("){");
        Object[] array = (Object[]) input;
        int count = array.length;
        for (int i = 0; i < count; i++)
        {
            if (i > 0)
            {
                buffer.append(", ");
            }
            // We use convert() again, because it could be a multi-dimensional array
            // where each element must be converted.
            convert(buffer, array[i]);
        }
        buffer.append("}");
    }

    /**
     * Get the arguments.
     * 
     * @return Object[]
     */
    public Object[] getArguments()
    {
        return ArrayUtils.clone(arguments);
    }
}
import org.apache.commons.proxy2.interceptor.SwitchInterceptor;
import org.apache.commons.proxy2.interceptor.matcher.ArgumentMatcher;
import org.apache.commons.proxy2.interceptor.matcher.InvocationMatcher;
import org.apache.commons.proxy2.invoker.NullInvoker;
import org.apache.commons.proxy2.invoker.RecordedInvocation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.proxy2.Interceptor;
import org.apache.commons.proxy2.Invocation;
import org.apache.commons.proxy2.Invoker;
import org.apache.commons.proxy2.ProxyFactory;
import org.apache.commons.proxy2.ProxyUtils;
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.commons.proxy2.stub;



class TrainingContext
{
    //******************************************************************************************************************
    // Fields
    //******************************************************************************************************************

    private static final ThreadLocal<TrainingContext> TRAINING_CONTEXT = new ThreadLocal<TrainingContext>();

    private final ProxyFactory proxyFactory;

    private final Deque<TrainingContextFrame<?>> frameDeque = new LinkedList<TrainingContextFrame<?>>();

    private final TrainingContext resume;

    //******************************************************************************************************************
    // Static Methods
    //******************************************************************************************************************

    static TrainingContext current()
    {
        return TRAINING_CONTEXT.get();
    }

    static synchronized TrainingContext join(ProxyFactory proxyFactory)
    {
        final TrainingContext context = new TrainingContext(proxyFactory);
        TRAINING_CONTEXT.set(context);
        return context;
    }

    //******************************************************************************************************************
    // Constructors
    //******************************************************************************************************************

    private TrainingContext(ProxyFactory proxyFactory)
    {
        this.proxyFactory = proxyFactory;
        this.resume = current();
    }

    //******************************************************************************************************************
    // Other Methods
    //******************************************************************************************************************

    void part()
    {
        synchronized (TRAINING_CONTEXT)
        {
            if (resume == null)
            {
                TRAINING_CONTEXT.remove();
            }
            else
            {
                TRAINING_CONTEXT.set(resume);
            }
        }
    }

    private TrainingContextFrame<?> peek()
    {
        return frameDeque.peek();
    }

    <T> T pop()
    {
        return pop(NullInvoker.INSTANCE);
    }

    <T> T pop(Invoker invoker)
    {
        final TrainingContextFrame<?> frame = frameDeque.pop();
        return proxyFactory.createInterceptorProxy(proxyFactory.createInvokerProxy(invoker, frame.type),
                frame.stubInterceptor, frame.type);
    }

    <T> T push(Class<T> type)
    {
        return push(type, new SwitchInterceptor());
    }

    <T> T push(Class<T> type, SwitchInterceptor switchInterceptor)
    {
        TrainingContextFrame<T> frame = new TrainingContextFrame<T>(type, switchInterceptor);
        Invoker invoker = new TrainingInvoker(frame);
        frameDeque.push(frame);
        return proxyFactory.createInvokerProxy(invoker, type);
    }

    void record(ArgumentMatcher<?> argumentMatcher)
    {
        peek().argumentMatchers.add(argumentMatcher);
    }

    void then(Interceptor interceptor)
    {
        peek().then(interceptor);
    }

    //******************************************************************************************************************
    // Inner Classes
    //******************************************************************************************************************

    private static final class ExactArgumentsMatcher implements InvocationMatcher
    {
        private final RecordedInvocation recordedInvocation;

        private ExactArgumentsMatcher(RecordedInvocation recordedInvocation)
        {
            this.recordedInvocation = recordedInvocation;
        }

        @Override
        public boolean matches(Invocation invocation)