Author: cbrisson Date: Tue Apr 24 15:41:21 2012 New Revision: 1329799 URL: http://svn.apache.org/viewvc?rev=1329799&view=rev Log: commit Candid Dauth's patch for public fields uberspector (VELOCITY-12)
Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/PublicFieldExecutor.java velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SetPublicFieldExecutor.java velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassFieldMap.java velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectPublicFields.java Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCacheImpl.java Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/PublicFieldExecutor.java URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/PublicFieldExecutor.java?rev=1329799&view=auto ============================================================================== --- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/PublicFieldExecutor.java (added) +++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/PublicFieldExecutor.java Tue Apr 24 15:41:21 2012 @@ -0,0 +1,126 @@ +package org.apache.velocity.runtime.parser.node; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.lang.StringUtils; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.log.Log; +import org.apache.velocity.util.introspection.Introspector; + +/** + * Returns the value of a public field when executed. + */ +public class PublicFieldExecutor extends AbstractExecutor +{ + private final Introspector introspector; + + /** + * Field to be accessed + */ + private Field field = null; + + /** + * @param log + * @param introspector + * @param clazz + * @param property + * @since 1.5 + */ + public PublicFieldExecutor(final Log log, final Introspector introspector, + final Class clazz, final String property) + { + this.log = log; + this.introspector = introspector; + + // Don't allow passing in the empty string or null because + // it will either fail with a StringIndexOutOfBounds error + // or the introspector will get confused. + if (StringUtils.isNotEmpty(property)) + { + discover(clazz, property); + } + } + + public boolean isAlive() { + return getField() != null; + } + + /** + * @return The current field. + */ + public Field getField() + { + return field; + } + + /** + * @param field + */ + protected void setField(final Field field) + { + this.field = field; + } + + /** + * @return The current introspector. + * @since 1.5 + */ + protected Introspector getIntrospector() + { + return this.introspector; + } + + /** + * @param clazz + * @param property + */ + protected void discover(final Class clazz, final String property) + { + try + { + setField(introspector.getField(clazz, property)); + } + /** + * pass through application level runtime exceptions + */ + catch( RuntimeException e ) + { + throw e; + } + catch(Exception e) + { + String msg = "Exception while looking for public field '" + property; + log.error(msg, e); + throw new VelocityException(msg, e); + } + } + + /** + * @see org.apache.velocity.runtime.parser.node.AbstractExecutor#execute(java.lang.Object) + */ + public Object execute(Object o) + throws IllegalAccessException, InvocationTargetException + { + return isAlive() ? getField().get(o) : null; + } +} Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SetPublicFieldExecutor.java URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SetPublicFieldExecutor.java?rev=1329799&view=auto ============================================================================== --- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SetPublicFieldExecutor.java (added) +++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/SetPublicFieldExecutor.java Tue Apr 24 15:41:21 2012 @@ -0,0 +1,147 @@ +package org.apache.velocity.runtime.parser.node; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang.StringUtils; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.log.Log; +import org.apache.velocity.util.introspection.Introspector; + +/** + * Executor for setting public fields in objects + * + * @author <a href="mailto:henn...@apache.org">Henning P. Schmiedehausen</a> + * @author <a href="mailto:cda...@cdauth.eu">Candid Dauth</a> + */ +public class SetPublicFieldExecutor + extends SetExecutor +{ + private final Introspector introspector; + + /** + * Field to be accessed + */ + private Field field = null; + + /** + * @param log + * @param introspector + * @param clazz + * @param property + * @param arg + */ + public SetPublicFieldExecutor(final Log log, final Introspector introspector, + final Class clazz, final String property, final Object arg) + { + this.log = log; + this.introspector = introspector; + + // Don't allow passing in the empty string or null because + // it will either fail with a StringIndexOutOfBounds error + // or the introspector will get confused. + if (StringUtils.isNotEmpty(property)) + { + discover(clazz, property, arg); + } + } + + public boolean isAlive() { + return getField() != null; + } + + /** + * @return The current field. + */ + public Field getField() + { + return field; + } + + /** + * @param field + */ + protected void setField(final Field field) + { + this.field = field; + } + + /** + * @return The current introspector. + */ + protected Introspector getIntrospector() + { + return this.introspector; + } + + /** + * @param clazz + * @param property + * @param arg + */ + protected void discover(final Class clazz, final String property, final Object arg) + { + try + { + Field field = introspector.getField(clazz, property); + if(!Modifier.isFinal(field.getModifiers())) + { + setField(field); + } + } + /** + * pass through application level runtime exceptions + */ + catch( RuntimeException e ) + { + throw e; + } + catch(Exception e) + { + String msg = "Exception while looking for public field '" + property; + log.error(msg, e); + throw new VelocityException(msg, e); + } + } + + /** + * Execute method against context. + * @param o + * @param value + * @return The value of the invocation. + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public Object execute(final Object o, final Object value) + throws IllegalAccessException, InvocationTargetException + { + if (isAlive()) + { + Object oldValue = getField().get(o); + getField().set(o, value); + return oldValue; + } + else + return null; + } +} Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassFieldMap.java URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassFieldMap.java?rev=1329799&view=auto ============================================================================== --- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassFieldMap.java (added) +++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassFieldMap.java Tue Apr 24 15:41:21 2012 @@ -0,0 +1,176 @@ +package org.apache.velocity.util.introspection; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.velocity.runtime.log.Log; + +/** + * A cache of introspection information for a specific class instance. + * Keys {@link java.lang.reflect.Field} objects by the field names. + * + * @author <a href="mailto:jvan...@apache.org">Jason van Zyl</a> + * @author <a href="mailto:b...@werken.com">Bob McWhirter</a> + * @author <a href="mailto:szege...@freemail.hu">Attila Szegedi</a> + * @author <a href="mailto:ge...@optonline.net">Geir Magnusson Jr.</a> + * @author <a href="mailto:henn...@apache.org">Henning P. Schmiedehausen</a> + * @author Nathan Bubna + * @author <a href="mailto:cda...@cdauth.eu">Candid Dauth</a> + */ +public class ClassFieldMap +{ + /** Set true if you want to debug the reflection code */ + private static final boolean debugReflection = false; + + /** Class logger */ + private final Log log; + + /** + * Class passed into the constructor used to as + * the basis for the Field map. + */ + private final Class clazz; + + /** + * String --> Field map, the key is the field name + */ + private final Map fieldCache; + + /** + * Standard constructor + * @param clazz The class for which this ClassMap gets constructed. + */ + public ClassFieldMap(final Class clazz, final Log log) + { + this.clazz = clazz; + this.log = log; + + if (debugReflection && log.isDebugEnabled()) + { + log.debug("================================================================="); + log.debug("== Class: " + clazz); + } + + fieldCache = createFieldCache(); + + if (debugReflection && log.isDebugEnabled()) + { + log.debug("================================================================="); + } + } + + /** + * Returns the class object whose fields are cached by this map. + * + * @return The class object whose fields are cached by this map. + */ + public Class getCachedClass() + { + return clazz; + } + + /** + * Find a Field using the field name. + * + * @param name The field name to look up. + * @return A Field object representing the field to invoke or null. + */ + public Field findField(final String name) + { + return (Field)fieldCache.get(name); + } + + /** + * Populate the Map of direct hits. These + * are taken from all the public fields + * that our class, its parents and their implemented interfaces provide. + */ + private Map createFieldCache() + { + Map fieldCache = new ConcurrentHashMap(); + // + // Looks through all elements in the class hierarchy. + // + // We ignore all SecurityExceptions that might happen due to SecurityManager restrictions (prominently + // hit with Tomcat 5.5). + // Ah, the miracles of Java for(;;) ... + for (Class classToReflect = getCachedClass(); classToReflect != null ; classToReflect = classToReflect.getSuperclass()) + { + if (Modifier.isPublic(classToReflect.getModifiers())) + { + populateFieldCacheWith(fieldCache, classToReflect); + } + Class [] interfaces = classToReflect.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + { + populateFieldCacheWithInterface(fieldCache, interfaces[i]); + } + } + // return the already initialized cache + return fieldCache; + } + + /* recurses up interface heirarchy to get all super interfaces (VELOCITY-689) */ + private void populateFieldCacheWithInterface(Map fieldCache, Class iface) + { + if (Modifier.isPublic(iface.getModifiers())) + { + populateFieldCacheWith(fieldCache, iface); + } + Class[] supers = iface.getInterfaces(); + for (int i=0; i < supers.length; i++) + { + populateFieldCacheWithInterface(fieldCache, supers[i]); + } + } + + private void populateFieldCacheWith(Map fieldCache, Class classToReflect) + { + if (debugReflection && log.isDebugEnabled()) + { + log.debug("Reflecting " + classToReflect); + } + + try + { + Field[] fields = classToReflect.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) + { + int modifiers = fields[i].getModifiers(); + if (Modifier.isPublic(modifiers)) + { + fieldCache.put(fields[i].getName(), fields[i]); + } + } + } + catch (SecurityException se) // Everybody feels better with... + { + if (log.isDebugEnabled()) + { + log.debug("While accessing fields of " + classToReflect + ": ", se); + } + } + } + +} Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java?rev=1329799&r1=1329798&r2=1329799&view=diff ============================================================================== --- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java (original) +++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java Tue Apr 24 15:41:21 2012 @@ -19,6 +19,7 @@ package org.apache.velocity.util.introsp * under the License. */ +import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.velocity.runtime.log.Log; @@ -49,6 +50,7 @@ import org.apache.velocity.runtime.log.L * @author <a href="mailto:szege...@freemail.hu">Attila Szegedi</a> * @author <a href="mailto:paulo.gas...@krankikom.de">Paulo Gaspar</a> * @author <a href="mailto:henn...@apache.org">Henning P. Schmiedehausen</a> + * @author <a href="mailto:cda...@cdauth.eu">Candid Dauth</a> * @version $Id$ */ public abstract class IntrospectorBase @@ -106,6 +108,35 @@ public abstract class IntrospectorBase } /** + * Gets the field defined by <code>name</code>. + * + * @param c Class in which the method search is taking place + * @param name Name of the field being searched for + * + * @return The desired Field object. + * @throws IllegalArgumentException When the parameters passed in can not be used for introspection. + */ + public Field getField(final Class c, final String name) + throws IllegalArgumentException + { + if (c == null) + { + throw new IllegalArgumentException("class object is null!"); + } + + IntrospectorCache ic = getIntrospectorCache(); + + ClassFieldMap classFieldMap = ic.getFieldMap(c); + if (classFieldMap == null) + { + ic.put(c); + classFieldMap = ic.getFieldMap(c); + } + + return classFieldMap.findField(name); + } + + /** * Return the internal IntrospectorCache object. * * @return The internal IntrospectorCache object. Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java?rev=1329799&r1=1329798&r2=1329799&view=diff ============================================================================== --- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java (original) +++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java Tue Apr 24 15:41:21 2012 @@ -23,6 +23,7 @@ package org.apache.velocity.util.introsp * The introspector cache API definition. * * @author <a href="mailto:henn...@apache.org">Henning P. Schmiedehausen</a> + * @author <a href="mailto:cda...@cdauth.eu">Candid Dauth</a> * @version $Id$ * @since 1.5 */ @@ -44,6 +45,16 @@ public interface IntrospectorCache { ClassMap get(Class c); /** + * Lookup a given Class object in the cache. If it does not exist, + * check whether this is due to a class change and purge the caches + * eventually. + * + * @param c The class to look up. + * @return A ClassFieldMap object or null if it does not exist in the cache. + */ + ClassFieldMap getFieldMap(final Class c); + + /** * Creates a class map for specific class and registers it in the * cache. Also adds the qualified name to the name->class map * for later Classloader change detection. Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCacheImpl.java URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCacheImpl.java?rev=1329799&r1=1329798&r2=1329799&view=diff ============================================================================== --- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCacheImpl.java (original) +++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCacheImpl.java Tue Apr 24 15:41:21 2012 @@ -30,6 +30,7 @@ import org.apache.velocity.runtime.log.L * This is the internal introspector cache implementation. * * @author <a href="mailto:henn...@apache.org">Henning P. Schmiedehausen</a> + * @author <a href="mailto:cda...@cdauth.eu">Candid Dauth</a> * @version $Id$ * @since 1.5 */ @@ -50,6 +51,11 @@ public final class IntrospectorCacheImpl private final Map classMapCache = new HashMap(); /** + * Holds the field maps for the classes we know about. Map: Class --> ClassFieldMap object. + */ + private final Map classFieldMapCache = new HashMap(); + + /** * Keep the names of the classes in another map. This is needed for a multi-classloader environment where it is possible * to have Class 'Foo' loaded by a classloader and then get asked to introspect on 'Foo' from another class loader. While these * two Class objects have the same name, a <code>classMethodMaps.get(Foo.class)</code> will return null. For that case, we @@ -73,13 +79,14 @@ public final class IntrospectorCacheImpl synchronized (classMapCache) { classMapCache.clear(); + classFieldMapCache.clear(); classNameCache.clear(); log.debug(CACHEDUMP_MSG); } } /** - * Lookup a given Class object in the cache. If it does not exist, + * Lookup a given Class object in the cache. If it does not exist, * check whether this is due to a class change and purge the caches * eventually. * @@ -114,6 +121,41 @@ public final class IntrospectorCacheImpl } /** + * Lookup a given Class object in the cache. If it does not exist, + * check whether this is due to a class change and purge the caches + * eventually. + * + * @param c The class to look up. + * @return A ClassFieldMap object or null if it does not exist in the cache. + */ + public ClassFieldMap getFieldMap(final Class c) + { + if (c == null) + { + throw new IllegalArgumentException("class is null!"); + } + + ClassFieldMap classFieldMap = (ClassFieldMap)classFieldMapCache.get(c); + if (classFieldMap == null) + { + /* + * check to see if we have it by name. + * if so, then we have an object with the same + * name but loaded through a different class loader. + * In that case, we will just dump the cache to be sure. + */ + synchronized (classMapCache) + { + if (classNameCache.contains(c.getName())) + { + clear(); + } + } + } + return classFieldMap; + } + + /** * Creates a class map for specific class and registers it in the * cache. Also adds the qualified name to the name->class map * for later Classloader change detection. @@ -124,9 +166,11 @@ public final class IntrospectorCacheImpl public ClassMap put(final Class c) { final ClassMap classMap = new ClassMap(c, log); + final ClassFieldMap classFieldMap = new ClassFieldMap(c, log); synchronized (classMapCache) { classMapCache.put(c, classMap); + classFieldMapCache.put(c, classFieldMap); classNameCache.add(c.getName()); } return classMap; Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectPublicFields.java URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectPublicFields.java?rev=1329799&view=auto ============================================================================== --- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectPublicFields.java (added) +++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectPublicFields.java Tue Apr 24 15:41:21 2012 @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.velocity.util.introspection; + +import java.util.Iterator; +import org.apache.velocity.runtime.log.Log; +import org.apache.velocity.runtime.parser.node.PublicFieldExecutor; +import org.apache.velocity.runtime.parser.node.SetPublicFieldExecutor; +import org.apache.velocity.util.introspection.UberspectImpl.VelGetterImpl; +import org.apache.velocity.util.introspection.UberspectImpl.VelSetterImpl; + +/** + * Implementation of Uberspect to additionally provide access to public fields. + * + * @author <a href="mailto:ge...@optonline.net">Geir Magnusson Jr.</a> + * @author <a href="mailto:henn...@apache.org">Henning P. Schmiedehausen</a> + * @author <a href="mailto:cda...@cdauth.eu">Candid Dauth</a> + */ +public class UberspectPublicFields implements Uberspect, UberspectLoggable +{ + /** + * Our runtime logger. + */ + protected Log log; + + /** + * the default Velocity introspector + */ + protected Introspector introspector; + + /** + * init - generates the Introspector. As the setup code + * makes sure that the log gets set before this is called, + * we can initialize the Introspector using the log object. + */ + public void init() + { + introspector = new Introspector(log); + } + + /** + * Sets the runtime logger - this must be called before anything + * else. + * + * @param log The logger instance to use. + * @since 1.5 + */ + public void setLog(Log log) + { + this.log = log; + } + + /** + * Property getter + * @param obj + * @param identifier + * @param i + * @return A Velocity Getter Method. + * @throws Exception + */ + public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) + { + if (obj == null) + { + return null; + } + + Class claz = obj.getClass(); + + PublicFieldExecutor executor = new PublicFieldExecutor(log, introspector, claz, identifier); + + return (executor.isAlive()) ? new VelGetterImpl(executor) : null; + } + + /** + * Property setter + * @param obj + * @param identifier + * @param arg + * @param i + * @return A Velocity Setter method. + * @throws Exception + */ + public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) + { + if (obj == null) + { + return null; + } + + Class claz = obj.getClass(); + + SetPublicFieldExecutor executor = new SetPublicFieldExecutor(log, introspector, claz, identifier, arg); + + return (executor.isAlive()) ? new VelSetterImpl(executor) : null; + } + + public Iterator getIterator(Object obj, Info info) + { + return null; + } + + public VelMethod getMethod(Object obj, String method, Object[] args, Info info) + { + return null; + } + +}