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 }