001    package groovy.lang;
002    
003    import org.codehaus.groovy.runtime.InvokerHelper;
004    
005    import java.beans.IntrospectionException;
006    
007    /**
008     * As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs.
009     * It enriches MetaClass with the feature of making method invokations interceptable by
010     * an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing
011     * to add or withdraw this feature at runtime.
012     * See groovy/lang/InterceptorTest.groovy for details.
013     * @author Dierk Koenig
014     */
015    public class ProxyMetaClass extends MetaClass {
016    
017        protected MetaClass adaptee = null;
018        protected Interceptor interceptor = null;
019    
020        /**
021         * convenience factory method for the most usual case.
022         */
023        public static ProxyMetaClass getInstance(Class theClass) throws IntrospectionException {
024            MetaClassRegistry metaRegistry = InvokerHelper.getInstance().getMetaRegistry();
025            MetaClass meta = metaRegistry.getMetaClass(theClass);
026            return new ProxyMetaClass(metaRegistry, theClass, meta);
027        }
028        /**
029         * @param adaptee   the MetaClass to decorate with interceptability
030         */
031        public ProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) throws IntrospectionException {
032            super(registry, theClass);
033            this.adaptee = adaptee;
034            if (null == adaptee) throw new IllegalArgumentException("adaptee must not be null");
035        }
036    
037        /**
038         * Use the ProxyMetaClass for the given Closure.
039         * Cares for balanced register/unregister.
040         * @param closure piece of code to be executed with registered ProxyMetaClass
041         */
042        public void use(Closure closure){
043            registry.setMetaClass(theClass, this);
044            closure.call();
045            registry.setMetaClass(theClass, adaptee);
046        }
047    
048         /**
049         * Use the ProxyMetaClass for the given Closure.
050         * Cares for balanced setting/unsetting ProxyMetaClass.
051         * @param closure piece of code to be executed with ProxyMetaClass
052         */
053        public void use(GroovyObject object, Closure closure){
054            object.setMetaClass(this);
055            closure.call();
056            object.setMetaClass(adaptee);
057        }
058    
059        /**
060         * @return the interceptor in use or null if no interceptor is used
061         */
062        public Interceptor getInterceptor() {
063            return interceptor;
064        }
065    
066        /**
067         * @param interceptor may be null to reset any interception
068         */
069        public void setInterceptor(Interceptor interceptor) {
070            this.interceptor = interceptor;
071        }
072    
073        /**
074         * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
075         * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
076         * The method call is suppressed if Interceptor.doInvoke() returns false.
077         * See Interceptor for details.
078         */
079        public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
080            return doCall(object, methodName, arguments, new Callable(){
081                public Object call() {
082                    return adaptee.invokeMethod(object, methodName, arguments);
083                }
084            });
085        }
086        /**
087         * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
088         * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
089         * The method call is suppressed if Interceptor.doInvoke() returns false.
090         * See Interceptor for details.
091         */
092        public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
093            return doCall(object, methodName, arguments, new Callable(){
094                public Object call() {
095                    return adaptee.invokeStaticMethod(object, methodName, arguments);
096                }
097            });
098        }
099    
100        /**
101         * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
102         * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
103         * The method call is suppressed if Interceptor.doInvoke() returns false.
104         * See Interceptor for details.
105         */
106        public Object invokeConstructor(final Object[] arguments) {
107            return doCall(theClass, "ctor", arguments, new Callable(){
108                public Object call() {
109                    return adaptee.invokeConstructor(arguments);
110                }
111            });
112        }
113    
114        // since Java has no Closures...
115        private interface Callable{
116            Object call();
117        }
118        private Object doCall(Object object, String methodName, Object[] arguments, Callable howToInvoke) {
119            if (null == interceptor) {
120                return howToInvoke.call();
121            }
122            Object result = interceptor.beforeInvoke(object, methodName, arguments);
123            if (interceptor.doInvoke()) {
124                result = howToInvoke.call();
125            }
126            result = interceptor.afterInvoke(object, methodName, arguments, result);
127            return result;
128        }
129    }