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.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 }