View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.di.impl;
17  
18  import java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.List;
21  
22  import net.sf.jguiraffe.di.Dependency;
23  import net.sf.jguiraffe.di.DependencyProvider;
24  import net.sf.jguiraffe.di.InjectionException;
25  
26  /**
27   * <p>
28   * A class that represents a method invocation.
29   * </p>
30   * <p>
31   * This class stores all data, which is needed for invoking a method; i.e. the
32   * method name, the target class, optional information about the data types of
33   * the method parameters, and the current parameter values to be passed to the
34   * method.
35   * </p>
36   * <p>
37   * Once initialized, an instance is immutable. So it can easily be shared
38   * between multiple components and threads without having to care about
39   * synchronization issues. The {@code invoke()} method actually executes
40   * the corresponding method.
41   * </p>
42   *
43   * @author Oliver Heger
44   * @version $Id: MethodInvocation.java 207 2012-02-09 07:30:13Z oheger $
45   */
46  public class MethodInvocation extends Invocation implements Invokable
47  {
48      /** Stores the target dependency for this invocation. */
49      private final Dependency targetDependency;
50  
51      /** Stores the name of the method to invoke. */
52      private final String methodName;
53  
54      /** Stores a flag whether this is a static method invocation. */
55      private final boolean staticInvocation;
56  
57      /**
58       * Creates a new instance of {@code MethodInvocation} for non-static
59       * method invocations. This constructor sets the target class to <b>null</b>,
60       * so that it can only be derived from the target instance (which prohibits
61       * static method invocations).
62       *
63       * @param methodName the name of the method to be invoked (must not be
64       * <b>null</b>)
65       * @param paramTypes an array with the parameter types
66       * @param paramValues the current parameter values (defined as
67       * {@code Dependency} objects); this array must not contain <b>null</b>
68       * elements
69       * @throws IllegalArgumentException if the length of the parameter types
70       * array does not match the length of the parameter values array, or if the
71       * values array contains <b>null</b> elements, or if the method name is
72       * undefined
73       */
74      public MethodInvocation(String methodName, ClassDescription[] paramTypes,
75              Dependency... paramValues)
76      {
77          this(null, methodName, paramTypes, paramValues);
78      }
79  
80      /**
81       * Creates a new instance of {@code MethodInvocation} and initializes
82       * it with information about the method to invoke and the target class.
83       *
84       * @param targetClass the class, on which the method is to be invoked
85       * @param methodName the name of the method to be invoked (must not be
86       * <b>null</b>)
87       * @param paramTypes an array with the parameter types
88       * @param paramValues the current parameter values (defined as
89       * {@code Dependency} objects); this array must not contain <b>null</b>
90       * elements
91       * @throws IllegalArgumentException if the length of the parameter types
92       * array does not match the length of the parameter values array, or if the
93       * values array contains <b>null</b> elements, or if the method name is
94       * undefined
95       * @see Invocation#Invocation(Class, Class[], Dependency...)
96       */
97      public MethodInvocation(ClassDescription targetClass, String methodName,
98              ClassDescription[] paramTypes, Dependency... paramValues)
99      {
100         this(targetClass, methodName, false, paramTypes, paramValues);
101     }
102 
103     /**
104      * Creates a new instance of {@code MethodInvocation} and initializes most
105      * of the properties. This constructor is appropriate for static or
106      * non-static invocations which are performed on the target object passed to
107      * the {@code invoke()} method.
108      *
109      * @param targetClass the class, on which the method is to be invoked
110      * @param methodName the name of the method to be invoked (must not be
111      * <b>null</b>)
112      * @param isStatic determines whether a static method is to be invoked
113      * @param paramTypes an array with the parameter types
114      * @param paramValues the current parameter values (defined as
115      * {@code Dependency} objects); this array must not contain <b>null</b>
116      * elements
117      * @throws IllegalArgumentException if the length of the parameter types
118      * array does not match the length of the parameter values array, or if the
119      * values array contains <b>null</b> elements, or if the method name is
120      * undefined, or if the static flag is <b>true</b>, but no target class is
121      * defined
122      * @see Invocation#Invocation(Class, Class[], Dependency...)
123      */
124     public MethodInvocation(ClassDescription targetClass, String methodName,
125             boolean isStatic, ClassDescription[] paramTypes,
126             Dependency... paramValues)
127     {
128         this(targetClass, null, methodName, isStatic, paramTypes, paramValues);
129     }
130 
131     /**
132      * Creates a new instance of {@code MethodInvocation} and fully initializes
133      * it. This constructor takes all information required for arbitrary method
134      * invocations. It is especially possible to define a dependency for the
135      * target object. If set, this dependency is resolved during invocation; a
136      * target object is then ignored. Refer to the base class for a detailed
137      * explanation of the arguments.
138      *
139      * @param targetClass the class, on which the method is to be invoked
140      * @param targetDep an optional {@code Dependency} to the target bean on
141      *        which the method should be invoked
142      * @param methodName the name of the method to be invoked (must not be
143      *        <b>null</b>)
144      * @param isStatic determines whether a static method is to be invoked
145      * @param paramTypes an array with the parameter types
146      * @param paramValues the current parameter values (defined as
147      *        {@code Dependency} objects); this array must not contain
148      *        <b>null</b> elements
149      * @throws IllegalArgumentException if the length of the parameter types
150      *         array does not match the length of the parameter values array, or
151      *         if the values array contains <b>null</b> elements, or if the
152      *         method name is undefined, or if the static flag is <b>true</b>,
153      *         but no target class is defined
154      * @see Invocation#Invocation(Class, Class[], Dependency...)
155      * @since 1.1
156      */
157     public MethodInvocation(ClassDescription targetClass, Dependency targetDep,
158             String methodName, boolean isStatic, ClassDescription[] paramTypes,
159             Dependency... paramValues)
160     {
161         super(targetClass, paramTypes, paramValues);
162         if (methodName == null)
163         {
164             throw new IllegalArgumentException("Method name must not be null!");
165         }
166         if (targetClass == null && isStatic)
167         {
168             throw new IllegalArgumentException(
169                     "Need a target class for a static invocation!");
170         }
171 
172         targetDependency = targetDep;
173         this.methodName = methodName;
174         staticInvocation = isStatic;
175     }
176 
177     /**
178      * Returns the name of the method to be invoked.
179      *
180      * @return the method name
181      */
182     public String getMethodName()
183     {
184         return methodName;
185     }
186 
187     /**
188      * Returns the static flag. This flag indicates whether a static method is
189      * to be invoked.
190      *
191      * @return the static invocation flag
192      */
193     public boolean isStaticInvocation()
194     {
195         return staticInvocation;
196     }
197 
198     /**
199      * Returns the target {@code Dependency} of this {@code MethodInvocation}.
200      * This dependency defines the bean on which the method is to be invoked. If
201      * there is no target dependency, result is <b>null</b>.
202      *
203      * @return the target {@code Dependency}
204      * @since 1.1
205      */
206     public Dependency getTargetDependency()
207     {
208         return targetDependency;
209     }
210 
211     /**
212      * {@inheritDoc} This implementation adds the dependency to the invocation
213      * target if it exists.
214      */
215     @Override
216     public List<Dependency> getParameterDependencies()
217     {
218         List<Dependency> deps = super.getParameterDependencies();
219         Dependency depTarget = getTargetDependency();
220         if (depTarget == null)
221         {
222             return deps;
223         }
224 
225         List<Dependency> result = new ArrayList<Dependency>(deps.size() + 1);
226         result.addAll(deps);
227         result.add(depTarget);
228         return Collections.unmodifiableList(result);
229     }
230 
231     /**
232      * Invokes the corresponding method on the specified target instance. The
233      * method's result is returned. The behavior of this method depends on the
234      * {@link #isStaticInvocation()} flag. If it is set, a passed in target
235      * object is ignored and a static method invocation on the target class is
236      * performed. Otherwise, if a non <b>null</b> target object is passed in,
237      * the target class is derived from this instance (an eventually set target
238      * class is ignored).
239      *
240      * @param depProvider the dependency provider for resolving the parameters
241      *        (must not be <b>null</b>)
242      * @param target the target instance, on which to invoke the method
243      * @return the method's return value
244      * @throws InjectionException in case of an error
245      * @throws IllegalArgumentException if the dependency provider is
246      *         <b>null</b>
247      */
248     public Object invoke(DependencyProvider depProvider, Object target)
249     {
250         Object targetObject = resolveTarget(depProvider, target);
251         if (targetObject == null && getTargetClass() == null)
252         {
253             throw new InjectionException(
254                     "Target class and instance must not both be null!");
255         }
256 
257         Object[] values = getResolvedParameters(depProvider);
258         if (isStaticInvocation() || targetObject == null)
259         {
260             return depProvider.getInvocationHelper().invokeStaticMethod(
261                     getTargetClass().getTargetClass(depProvider),
262                     getMethodName(), getParameterClasses(depProvider), values);
263         }
264         else
265         {
266             return depProvider.getInvocationHelper().invokeInstanceMethod(
267                     targetObject, getMethodName(),
268                     getParameterClasses(depProvider), values);
269         }
270     }
271 
272     /**
273      * Creates a string with additional information about this invocation. This
274      * implementation will output the method name.
275      *
276      * @param buf the target buffer
277      */
278     @Override
279     protected void invocationInfoToString(StringBuilder buf)
280     {
281         super.invocationInfoToString(buf);
282         if (getTargetClass() != null)
283         {
284             buf.append('.');
285         }
286         buf.append(getMethodName());
287     }
288 
289     /**
290      * Resolves the target object of this method invocation. If a target
291      * dependency is set, it is resolved, and the resulting object is used as
292      * target.
293      *
294      * @param depProvider the {@code DependencyProvider}
295      * @param target the target object passed to {@code invoke()}
296      * @return the target object of the invocation
297      * @throws IllegalArgumentException if the {@code DependencyProvider} is
298      *         <b>null</b>
299      */
300     private Object resolveTarget(DependencyProvider depProvider, Object target)
301     {
302         Dependency dep = getTargetDependency();
303         if (dep == null)
304         {
305             return target;
306         }
307 
308         checkDependencyProvider(depProvider);
309         return depProvider.getDependentBean(dep);
310     }
311 }