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 }