001    /*
002     * $Id: MetaMethod.java,v 1.15 2005/06/30 16:12:05 blackdrag 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.15 $
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 void setDeclaringClass(Class c) {
128            declaringClass=c;
129        }
130    
131        public int getMethodIndex() {
132            return methodIndex;
133        }
134    
135        public void setMethodIndex(int methodIndex) {
136            this.methodIndex = methodIndex;
137        }
138    
139        public int getModifiers() {
140            return modifiers;
141        }
142    
143        public String getName() {
144            return name;
145        }
146    
147        public Class[] getParameterTypes() {
148            return parameterTypes;
149        }
150    
151        public Class getReturnType() {
152            return returnType;
153        }
154    
155        public Reflector getReflector() {
156            return reflector;
157        }
158    
159        public void setReflector(Reflector reflector) {
160            this.reflector = reflector;
161        }
162    
163        public boolean isMethod(Method method) {
164            return name.equals(method.getName())
165                && modifiers == method.getModifiers()
166                && returnType.equals(method.getReturnType())
167                && equal(parameterTypes, method.getParameterTypes());
168        }
169    
170        protected boolean equal(Class[] a, Class[] b) {
171            if (a.length == b.length) {
172                for (int i = 0, size = a.length; i < size; i++) {
173                    if (!a[i].equals(b[i])) {
174                        return false;
175                    }
176                }
177                return true;
178            }
179            return false;
180        }
181    
182        public String toString() {
183            return super.toString()
184                + "[name: "
185                + name
186                + " params: "
187                + InvokerHelper.toString(parameterTypes)
188                + " returns: "
189                + returnType
190                + " owner: "
191                + declaringClass
192                + "]";
193        }
194    
195        public Object clone() {
196            try {
197                return super.clone();
198            }
199            catch (CloneNotSupportedException e) {
200                throw new GroovyRuntimeException("This should never happen", e);
201            }
202        }
203    
204        public boolean isStatic() {
205            return (modifiers & Modifier.STATIC) != 0;
206        }
207    
208        public boolean isPrivate() {
209            return (modifiers & Modifier.PRIVATE) != 0;
210        }
211    
212        public boolean isProtected() {
213            return (modifiers & Modifier.PROTECTED) != 0;
214        }
215    
216        public boolean isPublic() {
217            return (modifiers & Modifier.PUBLIC) != 0;
218        }
219    
220        /**
221         * @return true if the given method has the same name, parameters, return type
222         * and modifiers but may be defined on another type
223         */
224        public boolean isSame(MetaMethod method) {
225            return name.equals(method.getName())
226                && compatibleModifiers(modifiers, method.getModifiers())
227                && returnType.equals(method.getReturnType())
228                && equal(parameterTypes, method.getParameterTypes());
229        }
230    
231        protected boolean compatibleModifiers(int modifiersA, int modifiersB) {
232            int mask = Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC | Modifier.STATIC;
233            return (modifiersA & mask) == (modifiersB & mask);
234        }
235    
236        public Class getInterfaceClass() {
237            return interfaceClass;
238        }
239    
240        public void setInterfaceClass(Class interfaceClass) {
241            this.interfaceClass = interfaceClass;
242        }
243    
244        public boolean isCacheable() {
245            return true;
246        }
247    
248    }