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 }