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: return alternate.invoke(bean, arguments);
073: } catch (IllegalAccessException ex) {
074: alternate.setAccessible(true);
075: return alternate.invoke(bean, arguments);
076: }
077: } else {
078: method.setAccessible(true);
079: return method.invoke(bean, arguments);
080: }
081: } catch (Exception e2) {
082: handleException(context, e2, alternate);
083: }
084: } catch (Exception e) {
085: handleException(context, e, method);
086: }
087: }
088: return null;
089: }
090:
091: /**
092: * Do nothing.
093: * @see org.apache.commons.betwixt.expression.Expression
094: */
095: public void update(Context context, String newValue) {
096: // do nothing
097: }
098:
099: /**
100: * Gets the method used to evaluate this expression.
101: * @return the method whose value (when invoked against the context's bean) will be used
102: * to evaluate this expression.
103: */
104: public Method getMethod() {
105: return method;
106: }
107:
108: /**
109: * Sets the method used to evaluate this expression
110: * @param method method whose value (when invoked against the context's bean) will be used
111: * to evaluate this expression
112: */
113: public void setMethod(Method method) {
114: this .method = method;
115: }
116:
117: // Implementation methods
118: //-------------------------------------------------------------------------
119:
120: /**
121: * Allows derived objects to create arguments for the method call
122: * @return {@link #NULL_ARGUMENTS}
123: */
124: protected Object[] getArguments() {
125: return NULL_ARGUMENTS;
126: }
127:
128: /** Tries to find an alternate method for the given type using interfaces
129: * which gets around the problem of inner classes,
130: * such as on Map.Entry implementations.
131: *
132: * @param type the Class whose methods are to be searched
133: * @param method the Method for which an alternative is to be search for
134: * @return the alternative Method, if one can be found. Otherwise null.
135: */
136: protected Method findAlternateMethod(Class type, Method method) {
137: // XXX
138: // Would it be better to use the standard reflection code in eg. lang
139: // since this code contains workarounds for common JVM bugs?
140: //
141: Class[] interfaces = type.getInterfaces();
142: if (interfaces != null) {
143: String name = method.getName();
144: for (int i = 0, size = interfaces.length; i < size; i++) {
145: Class otherType = interfaces[i];
146: //
147: // catch NoSuchMethodException so that all interfaces will be tried
148: try {
149: Method alternate = otherType.getMethod(name,
150: NULL_CLASSES);
151: if (alternate != null && alternate != method) {
152: return alternate;
153: }
154: } catch (NoSuchMethodException e) {
155: // swallow
156: }
157: }
158: }
159: return null;
160: }
161:
162: /**
163: * <p>Log error to context's logger.</p>
164: *
165: * <p>Allows derived objects to handle exceptions differently.</p>
166: *
167: * @param context the Context being evaluated when the exception occured
168: * @param e the exception to handle
169: * @since 0.8
170: */
171: protected void handleException(Context context, Exception e,
172: Method m) {
173: // use the context's logger to log the problem
174: context.getLog().error(
175: "[MethodExpression] Cannot evaluate method " + m, e);
176: }
177:
178: /**
179: * <p>Log error to context's logger.</p>
180: *
181: * <p>Allows derived objects to handle exceptions differently.</p>
182: *
183: * @param context the Context being evaluated when the exception occured
184: * @param e the exception to handle
185: */
186: protected void handleException(Context context, Exception e) {
187: // use the context's logger to log the problem
188: context.getLog().error(
189: "[MethodExpression] Cannot evaluate method ", e);
190: }
191:
192: /**
193: * Returns something useful for logging.
194: * @return something useful for logging
195: */
196: public String toString() {
197: return "MethodExpression [method=" + method + "]";
198: }
199: }
|