001    /*
002     * $Id: MetaMethod.java,v 1.14 2004/07/10 03:31:36 bran Exp $
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.Reflector;
045    
046    /**
047     * Represents a Method on a Java object a little like {@link java.lang.reflect.Method}
048     * except without using reflection to invoke the method
049     * 
050     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
051     * @version $Revision: 1.14 $
052     */
053    public class MetaMethod implements Cloneable {
054    
055        private static final Logger log = Logger.getLogger(MetaMethod.class.getName());
056    
057        private String name;
058        private Class declaringClass;
059        private Class interfaceClass;
060        private Class[] parameterTypes;
061        private Class returnType;
062        private int modifiers;
063        private Reflector reflector;
064        private int methodIndex;
065        private Method method;
066    
067        public MetaMethod(String name, Class declaringClass, Class[] parameterTypes, Class returnType, int modifiers) {
068            this.name = name;
069            this.declaringClass = declaringClass;
070            this.parameterTypes = parameterTypes;
071            this.returnType = returnType;
072            this.modifiers = modifiers;
073        }
074    
075        public MetaMethod(Method method) {
076            this(
077                method.getName(),
078                method.getDeclaringClass(),
079                method.getParameterTypes(),
080                method.getReturnType(),
081                method.getModifiers());
082            this.method = method;
083        }
084    
085        public MetaMethod(MetaMethod metaMethod) {
086            this(metaMethod.method);
087        }
088    
089        /**
090         * Checks that the given parameters are valid to call this method
091         * 
092         * @param arguments
093         * @throws IllegalArgumentException if the parameters are not valid
094         */
095        public void checkParameters(Class[] arguments) {
096            // lets check that the argument types are valid
097            if (!MetaClass.isValidMethod(getParameterTypes(), arguments, false)) {
098                throw new IllegalArgumentException(
099                        "Parameters to method: "
100                        + getName()
101                        + " do not match types: "
102                        + InvokerHelper.toString(getParameterTypes())
103                        + " for arguments: "
104                        + InvokerHelper.toString(arguments));
105            }
106        }
107        
108        public Object invoke(Object object, Object[] arguments) throws Exception {
109            if (reflector != null) {
110                return reflector.invoke(this, object, arguments);
111            }
112            else {
113                AccessController.doPrivileged(new PrivilegedAction() {
114                                    public Object run() {
115                                        method.setAccessible(true);
116                                        return null;
117                                    }
118                            });
119                return method.invoke(object, arguments);
120            }
121        }
122    
123        public Class getDeclaringClass() {
124            return declaringClass;
125        }
126    
127        public int getMethodIndex() {
128            return methodIndex;
129        }
130    
131        public void setMethodIndex(int methodIndex) {
132            this.methodIndex = methodIndex;
133        }
134    
135        public int getModifiers() {
136            return modifiers;
137        }
138    
139        public String getName() {
140            return name;
141        }
142    
143        public Class[] getParameterTypes() {
144            return parameterTypes;
145        }
146    
147        public Class getReturnType() {
148            return returnType;
149        }
150    
151        public Reflector getReflector() {
152            return reflector;
153        }
154    
155        public void setReflector(Reflector reflector) {
156            this.reflector = reflector;
157        }
158    
159        public boolean isMethod(Method method) {
160            return name.equals(method.getName())
161                && modifiers == method.getModifiers()
162                && returnType.equals(method.getReturnType())
163                && equal(parameterTypes, method.getParameterTypes());
164        }
165    
166        protected boolean equal(Class[] a, Class[] b) {
167            if (a.length == b.length) {
168                for (int i = 0, size = a.length; i < size; i++) {
169                    if (!a[i].equals(b[i])) {
170                        return false;
171                    }
172                }
173                return true;
174            }
175            return false;
176        }
177    
178        public String toString() {
179            return super.toString()
180                + "[name: "
181                + name
182                + " params: "
183                + InvokerHelper.toString(parameterTypes)
184                + " returns: "
185                + returnType
186                + " owner: "
187                + declaringClass
188                + "]";
189        }
190    
191        public Object clone() {
192            try {
193                return super.clone();
194            }
195            catch (CloneNotSupportedException e) {
196                throw new GroovyRuntimeException("This should never happen", e);
197            }
198        }
199    
200        public boolean isStatic() {
201            return (modifiers & Modifier.STATIC) != 0;
202        }
203    
204        public boolean isPrivate() {
205            return (modifiers & Modifier.PRIVATE) != 0;
206        }
207    
208        public boolean isProtected() {
209            return (modifiers & Modifier.PROTECTED) != 0;
210        }
211    
212        public boolean isPublic() {
213            return (modifiers & Modifier.PUBLIC) != 0;
214        }
215    
216        /**
217         * @return true if the given method has the same name, parameters, return type
218         * and modifiers but may be defined on another type
219         */
220        public boolean isSame(MetaMethod method) {
221            return name.equals(method.getName())
222                && compatibleModifiers(modifiers, method.getModifiers())
223                && returnType.equals(method.getReturnType())
224                && equal(parameterTypes, method.getParameterTypes());
225        }
226    
227        protected boolean compatibleModifiers(int modifiersA, int modifiersB) {
228            int mask = Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC | Modifier.STATIC;
229            return (modifiersA & mask) == (modifiersB & mask);
230        }
231    
232        public Class getInterfaceClass() {
233            return interfaceClass;
234        }
235    
236        public void setInterfaceClass(Class interfaceClass) {
237            this.interfaceClass = interfaceClass;
238        }
239    
240        public boolean isCacheable() {
241            return true;
242        }
243    
244    }