001 /*
002 $Id: Invoker.java 4294 2006-12-02 19:31:27Z 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
008 that the following conditions are met:
009
010 1. Redistributions of source code must retain copyright
011 statements and notices. Redistributions must also contain a
012 copy of this document.
013
014 2. Redistributions in binary form must reproduce the
015 above copyright notice, this list of conditions and the
016 following disclaimer in the documentation and/or other
017 materials provided with the distribution.
018
019 3. The name "groovy" must not be used to endorse or promote
020 products derived from this Software without prior written
021 permission of The Codehaus. For written permission,
022 please contact info@codehaus.org.
023
024 4. Products derived from this Software may not be called "groovy"
025 nor may "groovy" appear in their names without prior written
026 permission of The Codehaus. "groovy" is a registered
027 trademark of The Codehaus.
028
029 5. Due credit should be given to The Codehaus -
030 http://groovy.codehaus.org/
031
032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043 OF THE POSSIBILITY OF SUCH DAMAGE.
044
045 */
046 package org.codehaus.groovy.runtime;
047
048 import groovy.lang.Closure;
049 import groovy.lang.GroovyInterceptable;
050 import groovy.lang.GroovyObject;
051 import groovy.lang.GroovyRuntimeException;
052 import groovy.lang.MetaClass;
053 import groovy.lang.MetaClassRegistry;
054 import groovy.lang.MissingMethodException;
055
056 import java.util.Map;
057
058 /**
059 * A helper class to invoke methods or extract properties on arbitrary Java objects dynamically
060 *
061 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
062 * @version $Revision: 4294 $
063 */
064 public class Invoker {
065
066 protected static final Object[] EMPTY_ARGUMENTS = {
067 };
068 protected static final Class[] EMPTY_TYPES = {
069 };
070
071 public MetaClassRegistry getMetaRegistry() {
072 return metaRegistry;
073 }
074
075 private MetaClassRegistry metaRegistry = new MetaClassRegistry();
076
077 public MetaClass getMetaClass(Object object) {
078 return metaRegistry.getMetaClass(object.getClass());
079 }
080
081 /**
082 * Invokes the given method on the object.
083 *
084 * @param object
085 * @param methodName
086 * @param arguments
087 * @return
088 */
089 public Object invokeMethod(Object object, String methodName, Object arguments) {
090 /*
091 System
092 .out
093 .println(
094 "Invoker - Invoking method on object: "
095 + object
096 + " method: "
097 + methodName
098 + " arguments: "
099 + InvokerHelper.toString(arguments));
100 */
101
102 if (object == null) {
103 object = NullObject.getNullObject();
104 //throw new NullPointerException("Cannot invoke method " + methodName + "() on null object");
105 }
106
107 // if the object is a Class, call a static method from that class
108 if (object instanceof Class) {
109 Class theClass = (Class) object;
110 MetaClass metaClass = metaRegistry.getMetaClass(theClass);
111 return metaClass.invokeStaticMethod(object, methodName, asArray(arguments));
112 }
113 else // it's an instance
114 {
115 // if it's not an object implementing GroovyObject (thus not builder, nor a closure)
116 if (!(object instanceof GroovyObject)) {
117 Class theClass = object.getClass();
118 MetaClass metaClass = metaRegistry.getMetaClass(theClass);
119 return metaClass.invokeMethod(object, methodName, asArray(arguments));
120 }
121 // it's an object implementing GroovyObject
122 else {
123 GroovyObject groovy = (GroovyObject) object;
124 try {
125 // if it's a pure interceptable object (even intercepting toString(), clone(), ...)
126 if (groovy instanceof GroovyInterceptable) {
127 return groovy.invokeMethod(methodName, asArray(arguments));
128 }
129 //else if there's a statically typed method or a GDK method
130 else {
131 return groovy.getMetaClass().invokeMethod(object, methodName, asArray(arguments));
132 }
133 } catch (MissingMethodException e) {
134 if (e.getMethod().equals(methodName) && object.getClass() == e.getType()) {
135 // in case there's nothing else, invoke the object's own invokeMethod()
136 return groovy.invokeMethod(methodName, asArray(arguments));
137 } else {
138 throw e;
139 }
140 }
141 }
142 }
143 }
144
145 public Object invokeSuperMethod(Object object, String methodName, Object arguments) {
146 if (object == null) {
147 throw new NullPointerException("Cannot invoke method " + methodName + "() on null object");
148 }
149
150 Class theClass = object.getClass();
151
152 MetaClass metaClass = metaRegistry.getMetaClass(theClass.getSuperclass());
153 return metaClass.invokeMethod(object, methodName, asArray(arguments));
154 }
155
156 public Object invokeStaticMethod(Class type, String method, Object arguments) {
157 MetaClass metaClass = metaRegistry.getMetaClass(type);
158 return metaClass.invokeStaticMethod(type, method, asArray(arguments));
159 }
160
161 public Object invokeConstructorOf(Class type, Object arguments) {
162 MetaClass metaClass = metaRegistry.getMetaClass(type);
163 return metaClass.invokeConstructor(asArray(arguments));
164 }
165
166 /**
167 * Converts the given object into an array; if its an array then just
168 * cast otherwise wrap it in an array
169 */
170 public Object[] asArray(Object arguments) {
171 if (arguments == null) {
172 return EMPTY_ARGUMENTS;
173 } else if (arguments instanceof Object[]) {
174 return (Object[]) arguments;
175 } else {
176 return new Object[]{arguments};
177 }
178 }
179
180 /**
181 * Looks up the given property of the given object
182 */
183 public Object getProperty(Object object, String property) {
184 if (object == null) {
185 throw new NullPointerException("Cannot get property: " + property + " on null object");
186 }
187 else if (object instanceof GroovyObject) {
188 GroovyObject pogo = (GroovyObject) object;
189 return pogo.getProperty(property);
190 }
191 else if (object instanceof Map) {
192 Map map = (Map) object;
193 return map.get(property);
194 }
195 else if (object instanceof Class) {
196 Class c = (Class) object;
197 return metaRegistry.getMetaClass(c).getProperty(object, property);
198 }
199 else {
200 return metaRegistry.getMetaClass(object.getClass()).getProperty(object, property);
201 }
202 }
203
204 /**
205 * Sets the property on the given object
206 */
207 public void setProperty(Object object, String property, Object newValue) {
208 if (object == null) {
209 throw new GroovyRuntimeException("Cannot set property on null object");
210 }
211 else if (object instanceof GroovyObject) {
212 GroovyObject pogo = (GroovyObject) object;
213 pogo.setProperty(property, newValue);
214 }
215 else if (object instanceof Map) {
216 Map map = (Map) object;
217 map.put(property, newValue);
218 }
219 else {
220 if (object instanceof Class)
221 metaRegistry.getMetaClass((Class) object).setProperty((Class) object, property, newValue);
222 else
223 metaRegistry.getMetaClass(object.getClass()).setProperty(object, property, newValue);
224 }
225 }
226
227 /**
228 * Looks up the given attribute (field) on the given object
229 */
230 public Object getAttribute(Object object, String attribute) {
231 if (object == null) {
232 throw new NullPointerException("Cannot get attribute: " + attribute + " on null object");
233
234 /**
235 } else if (object instanceof GroovyObject) {
236 GroovyObject pogo = (GroovyObject) object;
237 return pogo.getAttribute(attribute);
238 } else if (object instanceof Map) {
239 Map map = (Map) object;
240 return map.get(attribute);
241 */
242 }
243 else {
244 if (object instanceof Class) {
245 return metaRegistry.getMetaClass((Class) object).getAttribute(object, attribute);
246 } else if (object instanceof GroovyObject) {
247 return ((GroovyObject)object).getMetaClass().getAttribute(object, attribute);
248 } else {
249 return metaRegistry.getMetaClass(object.getClass()).getAttribute(object, attribute);
250 }
251 }
252 }
253
254 /**
255 * Sets the given attribute (field) on the given object
256 */
257 public void setAttribute(Object object, String attribute, Object newValue) {
258 if (object == null) {
259 throw new GroovyRuntimeException("Cannot set attribute on null object");
260 /*
261 } else if (object instanceof GroovyObject) {
262 GroovyObject pogo = (GroovyObject) object;
263 pogo.setProperty(attribute, newValue);
264 } else if (object instanceof Map) {
265 Map map = (Map) object;
266 map.put(attribute, newValue);
267 */
268 }
269 else {
270 if (object instanceof Class) {
271 metaRegistry.getMetaClass((Class) object).setAttribute(object, attribute, newValue);
272 } else if (object instanceof GroovyObject) {
273 ((GroovyObject)object).getMetaClass().setAttribute(object, attribute, newValue);
274 } else {
275 metaRegistry.getMetaClass(object.getClass()).setAttribute(object, attribute, newValue);
276 }
277 }
278 }
279
280 /**
281 * Returns the method pointer for the given object name
282 */
283 public Closure getMethodPointer(Object object, String methodName) {
284 if (object == null) {
285 throw new NullPointerException("Cannot access method pointer for '" + methodName + "' on null object");
286 }
287 return MetaClassHelper.getMethodPointer(object, methodName);
288 }
289
290 public void removeMetaClass(Class clazz) {
291 getMetaRegistry().removeMetaClass(clazz);
292 }
293 }