1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.betwixt.expression; 18 19 import java.lang.reflect.Method; 20 21 /** <p><code>MethodExpression</code> evaluates a method on the current bean context.</p> 22 * 23 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 24 * @version $Revision: 471234 $ 25 */ 26 public class MethodExpression implements Expression { 27 28 /** null arguments */ 29 protected static Object[] NULL_ARGUMENTS; 30 /** null classes */ 31 protected static Class[] NULL_CLASSES; 32 33 /** The method to call on the bean */ 34 private Method method; 35 36 /** Base constructor */ 37 public MethodExpression() { 38 } 39 40 /** 41 * Convenience constructor sets method property 42 * @param method the Method whose return value when invoked on the bean 43 * will the value of this expression 44 */ 45 public MethodExpression(Method method) { 46 this.method = method; 47 } 48 49 /** 50 * Evaluate by calling the read method on the current bean 51 * 52 * @param context the context against which this expression will be evaluated 53 * @return the value returned by the method when it's invoked on the context's bean, 54 * so long as the method can be invoked. 55 * Otherwise, null. 56 */ 57 public Object evaluate(Context context) { 58 Object bean = context.getBean(); 59 if ( bean != null ) { 60 Object[] arguments = getArguments(); 61 try { 62 return method.invoke( bean, arguments ); 63 64 } catch (IllegalAccessException e) { 65 // lets try use another method with the same name 66 Method alternate = null; 67 try { 68 Class type = bean.getClass(); 69 alternate = findAlternateMethod( type, method ); 70 if ( alternate != null ) { 71 try 72 { 73 return alternate.invoke( bean, arguments ); 74 } catch (IllegalAccessException ex) { 75 alternate.setAccessible(true); 76 return alternate.invoke( bean, arguments ); 77 } 78 } 79 else 80 { 81 method.setAccessible(true); 82 return method.invoke( bean, arguments ); 83 } 84 } catch (Exception e2) { 85 handleException(context, e2, alternate); 86 } 87 } catch (Exception e) { 88 handleException(context, e, method); 89 } 90 } 91 return null; 92 } 93 94 /** 95 * Do nothing. 96 * @see org.apache.commons.betwixt.expression.Expression 97 */ 98 public void update(Context context, String newValue) { 99 // 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 }