001    package com.mockrunner.struts;
002    
003    import java.lang.reflect.Method;
004    import java.util.Arrays;
005    import java.util.HashSet;
006    import java.util.Iterator;
007    import java.util.Set;
008    
009    import net.sf.cglib.proxy.Enhancer;
010    import net.sf.cglib.proxy.MethodInterceptor;
011    import net.sf.cglib.proxy.MethodProxy;
012    
013    import com.mockrunner.util.common.MethodUtil;
014    
015    /**
016     * Helper class to generate CGLib proxies. Not meant for application use.
017     */
018    public class DynamicMockProxyGenerator
019    {
020        private Class proxiedClass;
021        private Object delegate;
022        private Class additionalInterface;
023        private Set methodsToIntercept;
024        private Set methodsToDuplicate;
025        
026        public DynamicMockProxyGenerator(Class proxiedClass, Object delegate, Method[] methodsToIntercept, Method[] methodsToDuplicate)
027        {
028            this(proxiedClass, delegate, methodsToIntercept, methodsToDuplicate, null);
029        }
030    
031        public DynamicMockProxyGenerator(Class proxiedClass, Object delegate, Method[] methodsToIntercept, Method[] methodsToDuplicate, Class additionalInterface)
032        {
033            this.proxiedClass = proxiedClass;
034            this.delegate = delegate;
035            this.additionalInterface = additionalInterface;
036            this.methodsToIntercept = new HashSet();
037            this.methodsToIntercept.addAll(Arrays.asList(methodsToIntercept));
038            this.methodsToDuplicate = new HashSet();
039            this.methodsToDuplicate.addAll(Arrays.asList(methodsToDuplicate));
040        }
041    
042        public Object createProxy()
043        {
044            Enhancer enhancer = new Enhancer();
045            enhancer.setSuperclass(proxiedClass);
046            if(null != additionalInterface)
047            {
048                enhancer.setInterfaces(new Class[] { additionalInterface });
049            }
050            Method[] targetInterceptMethods = getActualTargetMethods(delegate, methodsToIntercept);
051            Method[] targetDuplicateMethods = getActualTargetMethods(delegate, methodsToDuplicate);
052            enhancer.setCallback(new DelegatingInterceptor(delegate, targetInterceptMethods, targetDuplicateMethods));
053            return enhancer.create();
054        }
055        
056        private Method[] getActualTargetMethods(Object delegate, Set providedMethods)
057        {
058            Method[] methods = delegate.getClass().getMethods();
059            Set actualMethods = new HashSet();
060            Set tempProvidedMethods = new HashSet(providedMethods);
061            for(int ii = 0; ii < methods.length; ii++)
062            {
063                findAndAddMethod(tempProvidedMethods, methods[ii], actualMethods);
064            }
065            return (Method[])actualMethods.toArray(new Method[actualMethods.size()]);
066        }
067        
068        private void findAndAddMethod(Set providedMethods, Method currentMethod, Set actualMethods)
069        {
070            Iterator iterator = providedMethods.iterator();
071            while(iterator.hasNext())
072            {
073                Method currentMethodToIntercept = (Method)iterator.next();
074                if(MethodUtil.areMethodsEqual(currentMethod, currentMethodToIntercept))
075                {
076                    actualMethods.add(currentMethod);
077                    iterator.remove();
078                    return;
079                }
080            }
081        }
082        
083        private static class DelegatingInterceptor implements MethodInterceptor
084        { 
085            private Object delegate;
086            private Method[] methodsToIntercept;
087            private Method[] methodsToDuplicate;
088            
089            public DelegatingInterceptor(Object delegate, Method[] methodsToIntercept, Method[] methodsToDuplicate)
090            {
091                this.delegate = delegate;
092                this.methodsToIntercept = methodsToIntercept;
093                this.methodsToDuplicate = methodsToDuplicate;
094            }
095            
096            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
097            {
098                for(int ii = 0; ii < methodsToIntercept.length; ii++)
099                {
100                    if(MethodUtil.areMethodsEqual(method, methodsToIntercept[ii]))
101                    {
102                        return methodsToIntercept[ii].invoke(delegate, args);
103                    }
104                }
105                for(int ii = 0; ii < methodsToDuplicate.length; ii++)
106                {
107                    if(MethodUtil.areMethodsEqual(method, methodsToDuplicate[ii]))
108                    {
109                        methodsToDuplicate[ii].invoke(delegate, args);
110                    }
111                }
112                return proxy.invokeSuper(obj, args);
113            }
114        }
115    }