001    package org.codehaus.groovy.runtime;
002    
003    import java.lang.reflect.InvocationHandler;
004    import java.lang.reflect.InvocationTargetException;
005    import java.lang.reflect.Method;
006    
007    /**
008     * This class is a general adapter to map a call to an Java interface 
009     * to a given delegate.
010     * <p>
011     * @author Ben Yu
012     * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
013     */
014    public abstract class ConversionHandler implements InvocationHandler {
015        private Object delegate;
016        
017        /**
018         * Creates a ConversionHandler with an deleagte.
019         * @param delegate the delegate
020         * @throws IllegalArgumentException if the given delegate is null
021         */
022        public ConversionHandler(Object delegate) {
023            if (delegate==null) throw new IllegalArgumentException("delegate must not be null");
024            this.delegate = delegate;
025        }
026        
027        /**
028         * gets the delegate.
029         * @return the delegate
030         */
031        public Object getDelegate(){
032            return delegate;
033        }
034        
035        /**
036         * This method is a default implementation for the invoke method
037         * given in Invocationhandler. Any call to an method with an
038         * declaring class that is not Object is redirected to invokeCustom. 
039         * Methods like tostring, equals and hashcode are called on the class
040         * itself instead of the delegate. It is better to overwrite the 
041         * invokeCustom method where the Object related methods are filtered out.
042         * 
043         * @see #invokeCustom(Object, Method, Object[])
044         * @see InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
045         * 
046         * @param proxy the proxy
047         * @param method the method
048         * @param args the arguments
049         * @return the result of the invocation by method or delegate
050         * @throws Throwable any exception caused by the delegate or the method
051         */
052        public Object invoke(Object proxy, Method method, Object[] args)
053        throws Throwable {
054            if(!isObjectMethod(method)){
055                return invokeCustom(proxy,method,args);
056            }
057            try {
058                return method.invoke(this, args);
059            } catch (InvocationTargetException ite) {
060                throw ite.getTargetException();
061            }  
062        }
063        
064        /**
065         * This method is called for all Methods not defined on Object. 
066         * The delegate should be called here.
067         * 
068         * @param proxy the proxy
069         * @param method the method
070         * @param args the arguments
071         * @return the result of the invocation of the delegate
072         * @throws Throwable any exception causes by the delegate
073         * @see #invoke(Object, Method, Object[])
074         * @see InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
075         * 
076         */
077        public abstract Object invokeCustom(Object proxy, Method method, Object[] args) throws Throwable;
078        
079        /**
080         * Indicates whether some other object is "equal to" this one.
081         * The delegate is used if the class of the parameter and the
082         * current class are equal. In other cases the method will return 
083         * false. The exact class is here used, if inheritance is needed,
084         * this method must be overwritten. 
085         *        
086         * @see java.lang.Object#equals(java.lang.Object)
087         */
088        public boolean equals(Object obj) {
089            if (obj!=null && obj.getClass()==this.getClass()){
090                return (((ConversionHandler)obj).getDelegate()).equals(obj);
091            } else {
092                return false;
093            }
094        }
095    
096        /**
097         * Returns a hash code value for the delegate. 
098         * @see java.lang.Object#hashCode()
099         */
100        public int hashCode() {
101            return delegate.hashCode();
102        }
103        
104        /**
105         * Returns a String version of the delegate.
106         * @see java.lang.Object#toString()
107         */
108        public String toString() {
109            return delegate.toString();
110        }
111        
112        private static boolean isObjectMethod(Method mtd){
113            return mtd.getDeclaringClass().equals(Object.class);
114        }
115    }