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 }