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 }