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    }