View Javadoc

1   /*
2    $Id: Invoker.java 4294 2006-12-02 19:31:27Z blackdrag $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package org.codehaus.groovy.runtime;
47  
48  import groovy.lang.Closure;
49  import groovy.lang.GroovyInterceptable;
50  import groovy.lang.GroovyObject;
51  import groovy.lang.GroovyRuntimeException;
52  import groovy.lang.MetaClass;
53  import groovy.lang.MetaClassRegistry;
54  import groovy.lang.MissingMethodException;
55  
56  import java.util.Map;
57  
58  /***
59   * A helper class to invoke methods or extract properties on arbitrary Java objects dynamically
60   *
61   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
62   * @version $Revision: 4294 $
63   */
64  public class Invoker {
65  
66      protected static final Object[] EMPTY_ARGUMENTS = {
67      };
68      protected static final Class[] EMPTY_TYPES = {
69      };
70  
71      public MetaClassRegistry getMetaRegistry() {
72          return metaRegistry;
73      }
74  
75      private MetaClassRegistry metaRegistry = new MetaClassRegistry();
76  
77      public MetaClass getMetaClass(Object object) {
78          return metaRegistry.getMetaClass(object.getClass());
79      }
80  
81      /***
82       * Invokes the given method on the object.
83       *
84       * @param object
85       * @param methodName
86       * @param arguments
87       * @return
88       */
89      public Object invokeMethod(Object object, String methodName, Object arguments) {
90          /*
91          System
92              .out
93              .println(
94                  "Invoker - Invoking method on object: "
95                      + object
96                      + " method: "
97                      + methodName
98                      + " arguments: "
99                      + 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 }