View Javadoc

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 }