001    /*
002     * $Id: MetaMethod.java 4254 2006-11-23 20:38:19Z blackdrag $
003     *
004     * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005     *
006     * Redistribution and use of this software and associated documentation
007     * ("Software"), with or without modification, are permitted provided that the
008     * following conditions are met:
009     *  1. Redistributions of source code must retain copyright statements and
010     * notices. Redistributions must also contain a copy of this document.
011     *  2. Redistributions in binary form must reproduce the above copyright
012     * notice, this list of conditions and the following disclaimer in the
013     * documentation and/or other materials provided with the distribution.
014     *  3. The name "groovy" must not be used to endorse or promote products
015     * derived from this Software without prior written permission of The Codehaus.
016     * For written permission, please contact info@codehaus.org.
017     *  4. Products derived from this Software may not be called "groovy" nor may
018     * "groovy" appear in their names without prior written permission of The
019     * Codehaus. "groovy" is a registered trademark of The Codehaus.
020     *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
021     *
022     * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
023     * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
024     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
025     * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
026     * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
027     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
028     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
029     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
030     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
031     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
032     * DAMAGE.
033     *
034     */
035    package groovy.lang;
036    
037    import java.lang.reflect.Method;
038    import java.lang.reflect.Modifier;
039    import java.security.AccessController;
040    import java.security.PrivilegedAction;
041    import java.util.logging.Logger;
042    
043    import org.codehaus.groovy.runtime.InvokerHelper;
044    import org.codehaus.groovy.runtime.InvokerInvocationException;
045    import org.codehaus.groovy.runtime.MetaClassHelper;
046    import org.codehaus.groovy.runtime.Reflector;
047    
048    /**
049     * Represents a Method on a Java object a little like {@link java.lang.reflect.Method}
050     * except without using reflection to invoke the method
051     * 
052     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
053     * @version $Revision: 4254 $
054     */
055    public class MetaMethod implements Cloneable {
056    
057        private static final Logger log = Logger.getLogger(MetaMethod.class.getName());
058    
059        private String name;
060        private Class callClass;
061        private Class declaringClass;
062        private Class interfaceClass;
063        private Class[] parameterTypes;
064        private Class returnType;
065        private int modifiers;
066        private Reflector reflector;
067        private int methodIndex;
068        private Method method;
069    
070        public MetaMethod(String name, Class declaringClass, Class[] parameterTypes, Class returnType, int modifiers) {
071            this.name = name;
072            this.callClass = declaringClass;
073            this.declaringClass = declaringClass;
074            this.parameterTypes = parameterTypes;
075            this.returnType = returnType;
076            this.modifiers = modifiers;
077        }
078    
079        public MetaMethod(Method method) {
080            this(
081                method.getName(),
082                method.getDeclaringClass(),
083                method.getParameterTypes(),
084                method.getReturnType(),
085                method.getModifiers());
086            this.method = method;
087        }
088    
089        public MetaMethod(MetaMethod metaMethod) {
090            this(metaMethod.method);
091        }
092    
093        /**
094         * Checks that the given parameters are valid to call this method
095         * 
096         * @param arguments
097         * @throws IllegalArgumentException if the parameters are not valid
098         */
099        public void checkParameters(Class[] arguments) {
100            // lets check that the argument types are valid
101            if (!MetaClassHelper.isValidMethod(getParameterTypes(), arguments, false)) {
102                throw new IllegalArgumentException(
103                        "Parameters to method: "
104                        + getName()
105                        + " do not match types: "
106                        + InvokerHelper.toString(getParameterTypes())
107                        + " for arguments: "
108                        + InvokerHelper.toString(arguments));
109            }
110        }
111        
112        public Object invoke(Object object, Object[] arguments) {
113            try {
114                if (reflector != null) {
115                    return reflector.invoke(this, object, arguments);
116                } else {
117                    AccessController.doPrivileged(new PrivilegedAction() {
118                        public Object run() {
119                            method.setAccessible(true);
120                            return null;
121                        }
122                    });
123                    return method.invoke(object, arguments);
124                }
125            } catch (Exception e) {
126                throw new InvokerInvocationException(e);
127            }
128        }
129    
130        public Class getCallClass() {
131            return callClass;
132        }
133        
134        public void setCallClass(Class c) {
135            callClass=c;
136        }
137    
138        public int getMethodIndex() {
139            return methodIndex;
140        }
141    
142        public void setMethodIndex(int methodIndex) {
143            this.methodIndex = methodIndex;
144        }
145    
146        public int getModifiers() {
147            return modifiers;
148        }
149    
150        public String getName() {
151            return name;
152        }
153    
154        public Class[] getParameterTypes() {
155            return parameterTypes;
156        }
157    
158        public Class getReturnType() {
159            return returnType;
160        }
161    
162        public Reflector getReflector() {
163            return reflector;
164        }
165    
166        public void setReflector(Reflector reflector) {
167            this.reflector = reflector;
168        }
169    
170        public boolean isMethod(Method method) {
171            return name.equals(method.getName())
172                && modifiers == method.getModifiers()
173                && returnType.equals(method.getReturnType())
174                && equal(parameterTypes, method.getParameterTypes());
175        }
176    
177        protected boolean equal(Class[] a, Class[] b) {
178            if (a.length == b.length) {
179                for (int i = 0, size = a.length; i < size; i++) {
180                    if (!a[i].equals(b[i])) {
181                        return false;
182                    }
183                }
184                return true;
185            }
186            return false;
187        }
188    
189        public String toString() {
190            return super.toString()
191                + "[name: "
192                + name
193                + " params: "
194                + InvokerHelper.toString(parameterTypes)
195                + " returns: "
196                + returnType
197                + " owner: "
198                + callClass
199                + "]";
200        }
201    
202        public Object clone() {
203            try {
204                return super.clone();
205            }
206            catch (CloneNotSupportedException e) {
207                throw new GroovyRuntimeException("This should never happen", e);
208            }
209        }
210    
211        public boolean isStatic() {
212            return (modifiers & Modifier.STATIC) != 0;
213        }
214    
215        public boolean isPrivate() {
216            return (modifiers & Modifier.PRIVATE) != 0;
217        }
218    
219        public boolean isProtected() {
220            return (modifiers & Modifier.PROTECTED) != 0;
221        }
222    
223        public boolean isPublic() {
224            return (modifiers & Modifier.PUBLIC) != 0;
225        }
226    
227        /**
228         * @return true if the given method has the same name, parameters, return type
229         * and modifiers but may be defined on another type
230         */
231        public boolean isSame(MetaMethod method) {
232            return name.equals(method.getName())
233                && compatibleModifiers(modifiers, method.getModifiers())
234                && returnType.equals(method.getReturnType())
235                && equal(parameterTypes, method.getParameterTypes());
236        }
237    
238        protected boolean compatibleModifiers(int modifiersA, int modifiersB) {
239            int mask = Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC | Modifier.STATIC;
240            return (modifiersA & mask) == (modifiersB & mask);
241        }
242    
243        public Class getInterfaceClass() {
244            return interfaceClass;
245        }
246    
247        public void setInterfaceClass(Class interfaceClass) {
248            this.interfaceClass = interfaceClass;
249        }
250    
251        public boolean isCacheable() {
252            return true;
253        }
254        
255        public Class getDeclaringClass() {
256            return declaringClass;
257        }
258    }