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