code style improvements.
[utils] / system / general / src / main / java / org / wamblee / system / adapters / ConstructorConfiguration.java
1 /*
2  * Copyright 2005-2010 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                     }
125                     return Modifier.isPublic(aObject.getModifiers());
126                 }
127             });
128
129         for (Constructor ctor : checked) {
130             if (ctor.getParameterTypes().length > max) {
131                 max = ctor.getParameterTypes().length;
132             }
133         }
134
135         final int max2 = max;
136         List<Constructor<?>> ctors = checked;
137         List<Constructor<?>> longest = new ArrayList<Constructor<?>>();
138         CollectionFilter.filter(ctors, longest,
139             new Condition<Constructor<?>>() {
140                 @Override
141                 public boolean matches(Constructor<?> aObject) {
142                     return aObject.getParameterTypes().length == max2;
143                 }
144             });
145
146         if (longest.size() > 1) {
147             throw new SystemAssemblyException(
148                 "Greediest constructor cannot be uniquely determined");
149         }
150
151         constructor = longest.get(0);
152         resetValues();
153
154         return this;
155     }
156
157     public ParameterValues getParameters() {
158         getConstructor(); // initialize constructor if needed.
159
160         return values;
161     }
162
163     /**
164      * Resets the values.
165      */
166     private void resetValues() {
167         constructor.setAccessible(true);
168         values = new ParameterValues(constructor.getParameterTypes());
169     }
170
171     /**
172      * Creates the object in the given scope.
173      * 
174      * @param aScope
175      *            Scope containing required interfaces for this object.
176      * 
177      * @return object.
178      * 
179      */
180     public Object create(Scope aScope) {
181         Object[] valueArray = values.values(aScope);
182
183         try {
184             return getConstructor().newInstance(valueArray);
185         } catch (Exception e) {
186             throw new SystemAssemblyException("Could not construct object " +
187                 getConstructor() + " " + Arrays.asList(valueArray), e);
188         }
189     }
190
191     public List<RequiredInterface> getRequiredInterfaces() {
192         getConstructor(); // initialize constructor if needed.
193
194         return values.getRequiredInterfaces();
195     }
196
197     private Constructor getConstructor() {
198         if (constructor == null) {
199             greedy();
200         }
201
202         return constructor;
203     }
204 }