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