Removed DOCUMENT ME comments that were generated and applied source code
[utils] / system / general / src / main / java / org / wamblee / system / adapters / ConstructorConfiguration.java
1 /*
2  * Copyright 2008 the original author or authors.
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 org.wamblee.system.adapters;
17
18 import org.wamblee.collections.CollectionFilter;
19
20 import org.wamblee.conditions.Condition;
21
22 import org.wamblee.system.core.RequiredInterface;
23 import org.wamblee.system.core.Scope;
24 import org.wamblee.system.core.SystemAssemblyException;
25
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.Modifier;
28
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32
33 /**
34  * Class that allows configuration of the constructor to use. In particular, it
35  * provides:
36  * <ul>
37  * <li>Selection of a constructor using explicit selection
38  * {@link #select(Class...)} or using the most greedy constructor
39  * {@link #greedy()}.</li>
40  * <li>Selection of methods to invoke to inject other objects into the object.</li>
41  * <li>Selection of fields to set.</li>
42  * </ul>
43  */
44 public class ConstructorConfiguration {
45     private Class clazz;
46
47     private Constructor<?> constructor;
48
49     private ParameterValues values;
50
51     private boolean publicOnly;
52
53     /**
54      * Constructs the configuration. By default the public constructor with the
55      * most arguments will be used.
56      * 
57      * @param aClass
58      *            Class to construct.
59      */
60     public ConstructorConfiguration(Class aClass) {
61         clazz = aClass;
62         constructor = null;
63         publicOnly = true;
64     }
65
66     /**
67      * Sets whether or no non public constructors are also considered. Reset the
68      * choice of a constructor to its default.
69      * 
70      * @param aNonPublic
71      * 
72      * @return
73      */
74     public ConstructorConfiguration setNonPublic(boolean aNonPublic) {
75         publicOnly = !aNonPublic;
76         constructor = null;
77         values = null;
78
79         return this;
80     }
81
82     /**
83      * Selects an explicit constructor.
84      * 
85      * @return Return the injector to allow call chaining.
86      * 
87      */
88     public ConstructorConfiguration select(Class... aTypes) {
89         try {
90             constructor = clazz.getDeclaredConstructor(aTypes);
91         } catch (Exception e) {
92             throw new SystemAssemblyException(e.getMessage(), e);
93         }
94
95         resetValues();
96
97         return this;
98     }
99
100     /**
101      * Selects the greediest constructor.
102      * 
103      * @return The injector to allow call chaining.
104      * 
105      * @throws SystemAssemblyException
106      *             if the greediest constructor cannot be uniquely identified.
107      */
108     public ConstructorConfiguration greedy() {
109         Constructor<?>[] declared = clazz.getDeclaredConstructors();
110
111         if (declared.length == 0) {
112             throw new SystemAssemblyException("Class '" + clazz +
113                 " is an interface, primitive type, or array");
114         }
115
116         int max = -1;
117         List<Constructor<?>> checked = new ArrayList<Constructor<?>>();
118         CollectionFilter.filter(Arrays.asList(declared), checked,
119             new Condition<Constructor<?>>() {
120                 @Override
121                 public boolean matches(Constructor<?> aObject) {
122                     if (!publicOnly) {
123                         return true;
124                     } else {
125                         return Modifier.isPublic(aObject.getModifiers());
126                     }
127                 }
128             });
129
130         for (Constructor ctor : checked) {
131             if (ctor.getParameterTypes().length > max) {
132                 max = ctor.getParameterTypes().length;
133             }
134         }
135
136         final int max2 = max;
137         List<Constructor<?>> ctors = checked;
138         List<Constructor<?>> longest = new ArrayList<Constructor<?>>();
139         CollectionFilter.filter(ctors, longest,
140             new Condition<Constructor<?>>() {
141                 @Override
142                 public boolean matches(Constructor<?> aObject) {
143                     return aObject.getParameterTypes().length == max2;
144                 }
145             });
146
147         if (longest.size() > 1) {
148             throw new SystemAssemblyException(
149                 "Greediest constructor cannot be uniquely determined");
150         }
151
152         constructor = longest.get(0);
153         resetValues();
154
155         return this;
156     }
157
158     public ParameterValues getParameters() {
159         getConstructor(); // initialize constructor if needed.
160
161         return values;
162     }
163
164     /**
165      * Resets the values.
166      */
167     private void resetValues() {
168         constructor.setAccessible(true);
169         values = new ParameterValues(constructor.getParameterTypes());
170     }
171
172     /**
173      * Creates the object in the given scope.
174      * 
175      * @param aScope
176      *            Scope containing required interfaces for this object.
177      * 
178      * @return object.
179      * 
180      */
181     public Object create(Scope aScope) {
182         Object[] valueArray = values.values(aScope);
183
184         try {
185             return getConstructor().newInstance(valueArray);
186         } catch (Exception e) {
187             throw new SystemAssemblyException("Could not construct object " +
188                 getConstructor() + " " + Arrays.asList(valueArray), e);
189         }
190     }
191
192     public List<RequiredInterface> getRequiredInterfaces() {
193         getConstructor(); // initialize constructor if needed.
194
195         return values.getRequiredInterfaces();
196     }
197
198     private Constructor getConstructor() {
199         if (constructor == null) {
200             greedy();
201         }
202
203         return constructor;
204     }
205 }