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.Arrays;
19 import java.util.List;
20
21 import net.sf.jguiraffe.di.Dependency;
22 import net.sf.jguiraffe.di.DependencyProvider;
23
24 /**
25 * <p>
26 * A base class for (method or constructor) invocations.
27 * </p>
28 * <p>
29 * This class allows the definition of important data that is required for
30 * invoking a method using reflection. Especially the parameter types and the
31 * parameter values can be specified. The parameter values are provided as
32 * {@link Dependency} objects, so they can refer to other beans defined in a
33 * {@code BeanStore}.
34 * </p>
35 * <p>
36 * The main use case for {@code Invocation} objects is the creation and
37 * initialization of beans performed by the dependency injection framework: At
38 * first a bean has to be created by invoking one of its constructors. After
39 * that some initialization methods may be called. In both cases the parameters
40 * for the calls have to be specified (which can be either constant values or
41 * references to other beans).
42 * </p>
43 * <p>
44 * This base provides common functionality related to the management of the
45 * invocation parameters. There will be concrete sub classes implementing
46 * specific invocations of methods or constructors.
47 * </p>
48 *
49 * @author Oliver Heger
50 * @version $Id: Invocation.java 207 2012-02-09 07:30:13Z oheger $
51 */
52 public class Invocation
53 {
54 /** Constant for the initial string buffer size. */
55 private static final int BUF_SIZE = 128;
56
57 /** Stores the parameter types. */
58 private final ClassDescription[] parameterTypes;
59
60 /** Stores a list with the dependencies for the parameters. */
61 private final List<Dependency> parameterDependencies;
62
63 /** Stores the target class of the invocation. */
64 private final ClassDescription targetClass;
65
66 /**
67 * Creates a new instance of {@code Invocation} and initializes it
68 * with information about the call parameters. To perform an invocation the
69 * class has to know the current parameter values and (at least partly) the
70 * data types of these values. From this information the signature of the
71 * method to call is derived. The array with the parameter types can have
72 * <b>null</b> elements if the corresponding parameter types are unknown;
73 * it can even be <b>null</b> at all if no information about data types is
74 * available. If it is not <b>null</b>, its length must be the same as the
75 * length of the array with the parameter values.
76 *
77 * @param targetClass description of the class, on which the method is to be
78 * invoked
79 * @param paramTypes an array with the parameter type descriptions
80 * @param paramValues the current parameter values (defined as
81 * {@code Dependency} objects); this array must not contain <b>null</b>
82 * elements
83 * @throws IllegalArgumentException if the length of the parameter types
84 * array does not match the length of the parameter values array or if the
85 * values array contains <b>null</b> elements
86 */
87 protected Invocation(ClassDescription targetClass,
88 ClassDescription[] paramTypes, Dependency... paramValues)
89 {
90 parameterTypes = initParameterTypes(paramTypes, paramValues);
91 parameterDependencies = initDependencies(paramValues);
92 this.targetClass = targetClass;
93 }
94
95 /**
96 * Returns the target class of this invocation. This is the class, on which
97 * the method or constructor is to be invoked. A target class may not be
98 * required for all cases, e.g. for non-static method invocations it can be
99 * determined from the target instance.
100 *
101 * @return the target class of the invocation
102 */
103 public ClassDescription getTargetClass()
104 {
105 return targetClass;
106 }
107
108 /**
109 * Returns an array with the types of the parameters of the invocation. Note
110 * that the array returned here is never <b>null</b>, even if <b>null</b>
111 * was passed to the constructor. The returned array may contain <b>null</b>
112 * elements for parameters where the type information is lacking.
113 *
114 * @return an array with the parameter types
115 */
116 public ClassDescription[] getParameterTypes()
117 {
118 return parameterTypes.clone();
119 }
120
121 /**
122 * Returns the {@code Dependency} objects defining the current
123 * parameter values.
124 *
125 * @return a list with the {@code Dependency} objects for the
126 * parameter values
127 */
128 public List<Dependency> getParameterDependencies()
129 {
130 return parameterDependencies;
131 }
132
133 /**
134 * Returns a flag whether the data types of all method parameters are known.
135 * If this is the case, the signature of the method to be called can be
136 * exactly specified. Otherwise the signature has to be derived from the
137 * data types of the current parameters.
138 *
139 * @return a flag whether information about the parameter types is complete
140 */
141 public boolean isTypeInfoComplete()
142 {
143 for (ClassDescription type : parameterTypes)
144 {
145 if (type == null)
146 {
147 return false;
148 }
149 }
150
151 return true;
152 }
153
154 /**
155 * Returns an array with the resolved parameters. This method iterates over
156 * the parameter dependencies and tries to resolve them using the specified
157 * {@code DependencyProvider}. An array with the resulting beans is
158 * returned. If no parameters are specified (i.e. for invocations of methods
159 * that do not have arguments), the return value is <b>null</b>.
160 *
161 * @param depProvider the dependency provider (must not be <b>null</b>)
162 * @return an array with the resolved parameter values (can be <b>null</b>)
163 * @throws net.sf.jguiraffe.di.InjectionException if a dependency cannot be
164 * resolved
165 * @throws IllegalArgumentException if the passed in dependency provider is
166 * <b>null</b>
167 */
168 public Object[] getResolvedParameters(DependencyProvider depProvider)
169 {
170 if (parameterDependencies.isEmpty())
171 {
172 return null;
173 }
174 checkDependencyProvider(depProvider);
175
176 Object[] values = new Object[parameterDependencies.size()];
177 int idx = 0;
178 for (Dependency d : parameterDependencies)
179 {
180 values[idx++] = depProvider.getDependentBean(d);
181 }
182 return values;
183 }
184
185 /**
186 * Returns an array with the concrete parameter classes. This method
187 * converts the internally stored {@code ClassDescription} objects into
188 * {@code Class} objects.
189 *
190 * @param depProvider the dependency provider for resolving the classes
191 * @return an array with the parameter classes
192 * @throws net.sf.jguiraffe.di.InjectionException if a class cannot be
193 * resolved
194 */
195 public Class<?>[] getParameterClasses(DependencyProvider depProvider)
196 {
197 ClassDescription[] descs = parameterTypes;
198 Class<?>[] result = new Class<?>[descs.length];
199 for (int i = 0; i < descs.length; i++)
200 {
201 if (descs[i] != null)
202 {
203 result[i] = descs[i].getTargetClass(depProvider);
204 }
205 }
206
207 return result;
208 }
209
210 /**
211 * Returns a string representation of this object. The returned string will
212 * contain information about this invocation (including the class name, the
213 * target class name, the parameters, and further information provided by
214 * sub classes). This implementation will print the concrete class name,
215 * followed by an opening square bracket. Then
216 * {@code invocationInfoToString()}, and
217 * {@code parametersToString()} are called. Finally a closing square
218 * bracket is output.
219 *
220 * @return a string for this object
221 */
222 @Override
223 public String toString()
224 {
225 StringBuilder buf = new StringBuilder(BUF_SIZE);
226 buf.append(getClass().getName());
227 buf.append('@').append(System.identityHashCode(this));
228 buf.append("[ ");
229 invocationInfoToString(buf);
230 parametersToString(buf);
231 buf.append(']');
232 return buf.toString();
233 }
234
235 /**
236 * Creates a string with additional information about this invocation. This
237 * method is called by the default {@code toString()} implementation.
238 * It adds the target class to the buffer if it is defined.
239 *
240 * @param buf the target buffer
241 */
242 protected void invocationInfoToString(StringBuilder buf)
243 {
244 if (getTargetClass() != null)
245 {
246 buf.append(getTargetClass());
247 }
248 }
249
250 /**
251 * Creates a string representation of the current parameter values. This
252 * implementation iterates over all parameter dependencies and invokes their
253 * {@code toString()} method. It is called by the
254 * {@code toString()} method.
255 *
256 * @param buf the target buffer
257 */
258 protected void parametersToString(StringBuilder buf)
259 {
260 buf.append('(');
261 boolean first = true;
262 for (Dependency d : parameterDependencies)
263 {
264 if (first)
265 {
266 first = false;
267 }
268 else
269 {
270 buf.append(", ");
271 }
272 buf.append(d);
273 }
274 buf.append(')');
275 }
276
277 /**
278 * Checks whether a valid {@code DependencyProvider} has been specified.
279 *
280 * @param depProvider the provider to be checked
281 * @throws IllegalArgumentException if the {@code DependencyProvider} is
282 * undefined
283 * @since 1.1
284 */
285 protected static void checkDependencyProvider(DependencyProvider depProvider)
286 {
287 if (depProvider == null)
288 {
289 throw new IllegalArgumentException(
290 "Dependency provider must not be null!");
291 }
292 }
293
294 /**
295 * Initializes the array with the parameter types. <b>null</b> may have
296 * been passed in, which has to be converted to a valid value.
297 *
298 * @param types the array with the parameter types
299 * @param deps the parameter dependencies
300 * @return the final array with the types of the parameters
301 * @throws IllegalArgumentException if the lengths of the arrays are
302 * incompatible
303 */
304 private ClassDescription[] initParameterTypes(ClassDescription[] types,
305 Dependency... deps)
306 {
307 if (types == null)
308 {
309 return new ClassDescription[deps.length];
310 }
311 else
312 {
313 if (types.length != deps.length)
314 {
315 throw new IllegalArgumentException(
316 "If the parameter types array is defined, "
317 + "its length must be the same as the values array!");
318 }
319
320 ClassDescription[] result = new ClassDescription[types.length];
321 System.arraycopy(types, 0, result, 0, types.length);
322 return result;
323 }
324 }
325
326 /**
327 * Initializes the list with the dependencies based on the constructor
328 * argument. The dependencies are also checked for validity.
329 *
330 * @param deps the dependencies
331 * @return the list with the dependencies
332 * @throws IllegalArgumentException if an undefined dependency is specified
333 */
334 private List<Dependency> initDependencies(Dependency... deps)
335 {
336 for (Dependency d : deps)
337 {
338 if (d == null)
339 {
340 throw new IllegalArgumentException(
341 "Parameter dependency must not be null!");
342 }
343 }
344 return Arrays.asList(deps);
345 }
346 }