001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.betwixt.expression; 018 019 import java.lang.reflect.Method; 020 021 /** <p><code>MethodExpression</code> evaluates a method on the current bean context.</p> 022 * 023 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 024 * @version $Revision: 471234 $ 025 */ 026 public class MethodExpression implements Expression { 027 028 /** null arguments */ 029 protected static Object[] NULL_ARGUMENTS; 030 /** null classes */ 031 protected static Class[] NULL_CLASSES; 032 033 /** The method to call on the bean */ 034 private Method method; 035 036 /** Base constructor */ 037 public MethodExpression() { 038 } 039 040 /** 041 * Convenience constructor sets method property 042 * @param method the Method whose return value when invoked on the bean 043 * will the value of this expression 044 */ 045 public MethodExpression(Method method) { 046 this.method = method; 047 } 048 049 /** 050 * Evaluate by calling the read method on the current bean 051 * 052 * @param context the context against which this expression will be evaluated 053 * @return the value returned by the method when it's invoked on the context's bean, 054 * so long as the method can be invoked. 055 * Otherwise, null. 056 */ 057 public Object evaluate(Context context) { 058 Object bean = context.getBean(); 059 if ( bean != null ) { 060 Object[] arguments = getArguments(); 061 try { 062 return method.invoke( bean, arguments ); 063 064 } catch (IllegalAccessException e) { 065 // lets try use another method with the same name 066 Method alternate = null; 067 try { 068 Class type = bean.getClass(); 069 alternate = findAlternateMethod( type, method ); 070 if ( alternate != null ) { 071 try 072 { 073 return alternate.invoke( bean, arguments ); 074 } catch (IllegalAccessException ex) { 075 alternate.setAccessible(true); 076 return alternate.invoke( bean, arguments ); 077 } 078 } 079 else 080 { 081 method.setAccessible(true); 082 return method.invoke( bean, arguments ); 083 } 084 } catch (Exception e2) { 085 handleException(context, e2, alternate); 086 } 087 } catch (Exception e) { 088 handleException(context, e, method); 089 } 090 } 091 return null; 092 } 093 094 /** 095 * Do nothing. 096 * @see org.apache.commons.betwixt.expression.Expression 097 */ 098 public void update(Context context, String newValue) { 099 // do nothing 100 } 101 102 /** 103 * Gets the method used to evaluate this expression. 104 * @return the method whose value (when invoked against the context's bean) will be used 105 * to evaluate this expression. 106 */ 107 public Method getMethod() { 108 return method; 109 } 110 111 /** 112 * Sets the method used to evaluate this expression 113 * @param method method whose value (when invoked against the context's bean) will be used 114 * to evaluate this expression 115 */ 116 public void setMethod(Method method) { 117 this.method = method; 118 } 119 120 // Implementation methods 121 //------------------------------------------------------------------------- 122 123 /** 124 * Allows derived objects to create arguments for the method call 125 * @return {@link #NULL_ARGUMENTS} 126 */ 127 protected Object[] getArguments() { 128 return NULL_ARGUMENTS; 129 } 130 131 /** Tries to find an alternate method for the given type using interfaces 132 * which gets around the problem of inner classes, 133 * such as on Map.Entry implementations. 134 * 135 * @param type the Class whose methods are to be searched 136 * @param method the Method for which an alternative is to be search for 137 * @return the alternative Method, if one can be found. Otherwise null. 138 */ 139 protected Method findAlternateMethod( 140 Class type, 141 Method method ) { 142 // XXX 143 // Would it be better to use the standard reflection code in eg. lang 144 // since this code contains workarounds for common JVM bugs? 145 // 146 Class[] interfaces = type.getInterfaces(); 147 if ( interfaces != null ) { 148 String name = method.getName(); 149 for ( int i = 0, size = interfaces.length; i < size; i++ ) { 150 Class otherType = interfaces[i]; 151 // 152 // catch NoSuchMethodException so that all interfaces will be tried 153 try { 154 Method alternate = otherType.getMethod( name, NULL_CLASSES ); 155 if ( alternate != null && alternate != method ) { 156 return alternate; 157 } 158 } catch (NoSuchMethodException e) { 159 // swallow 160 } 161 } 162 } 163 return null; 164 } 165 166 /** 167 * <p>Log error to context's logger.</p> 168 * 169 * <p>Allows derived objects to handle exceptions differently.</p> 170 * 171 * @param context the Context being evaluated when the exception occured 172 * @param e the exception to handle 173 * @since 0.8 174 */ 175 protected void handleException(Context context, Exception e, Method m) { 176 // use the context's logger to log the problem 177 context.getLog().error("[MethodExpression] Cannot evaluate method " + m, e); 178 } 179 180 /** 181 * <p>Log error to context's logger.</p> 182 * 183 * <p>Allows derived objects to handle exceptions differently.</p> 184 * 185 * @param context the Context being evaluated when the exception occured 186 * @param e the exception to handle 187 */ 188 protected void handleException(Context context, Exception e) { 189 // use the context's logger to log the problem 190 context.getLog().error("[MethodExpression] Cannot evaluate method ", e); 191 } 192 193 /** 194 * Returns something useful for logging. 195 * @return something useful for logging 196 */ 197 public String toString() { 198 return "MethodExpression [method=" + method + "]"; 199 } 200 }