2725d0338fcae9026cf8f02754da8c2e5453f6f8
[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 public class ConstructorConfiguration {
32         private Class _class; 
33         private Constructor<?> _constructor;
34         private ParameterValues _values;
35         private boolean _publicOnly; 
36
37         /**
38          * Constructs the configuration. By default no constructor is selected and 
39          * one of {@link #select(Class...)} or 
40          * {@link #greedy()} must be called.  
41          * @param aClass Class to construct. 
42          */
43         public ConstructorConfiguration(Class aClass) {
44                 _class = aClass; 
45                 _constructor = null;  
46                 _publicOnly = true; 
47         }
48         
49         /**
50          * Sets whether or no non public constructors are also considered.
51          * Reset the choice of a constructor. 
52          * @param aNonPublic
53          * @return
54          */
55         public ConstructorConfiguration setNonPublic(boolean aNonPublic) { 
56                 _publicOnly = !aNonPublic;
57                 _constructor = null;
58                 _values = null;
59                 return this; 
60         }
61         
62         /**
63          * Selects an explicit constructor.
64          * @param aTypes Arguments of the constructor.
65          * @return Return the injector to allow call chaining. 
66          */
67         public ConstructorConfiguration select(Class... aTypes) {
68                 try {
69                         _constructor = _class.getDeclaredConstructor(aTypes); 
70                 } catch (Exception e) {
71                         throw new SystemAssemblyException(e.getMessage(), e);
72                 }
73                 resetValues(); 
74                 return this; 
75         }
76         
77         /**
78          * Selects the greediest constructor. 
79          * @return The injector to allow call chaining.
80          * @throws SystemAssemblyException if the greediest constructor cannot be uniquely 
81          *    identified.  
82          */
83         public ConstructorConfiguration greedy() { 
84                 Constructor<?>[] declared = _class.getDeclaredConstructors();
85                 if (declared.length == 0) {
86                         throw new SystemAssemblyException("Class '" + _class
87                                         + " is an interface, primitive type, or array");
88                 }
89                 int max = -1;
90                 List<Constructor<?>> checked = new ArrayList<Constructor<?>>();
91                 CollectionFilter.filter(Arrays.asList(declared), checked, 
92                         new Condition<Constructor<?>>() { 
93                         @Override
94                         public boolean matches(Constructor<?> aObject) {
95                                 if ( !_publicOnly ) { 
96                                         return true; 
97                                 } else { 
98                                         return Modifier.isPublic(aObject.getModifiers());
99                                 }
100                         }
101                 });
102                 for (Constructor ctor : checked) {
103                         if (ctor.getParameterTypes().length > max) {
104                                 max = ctor.getParameterTypes().length;
105                         }
106                 }
107                 final int max2 = max;
108                 List<Constructor<?>> ctors = checked;
109                 List<Constructor<?>> longest = new ArrayList<Constructor<?>>();
110                 CollectionFilter.filter(ctors, longest,
111                                 new Condition<Constructor<?>>() {
112                                         @Override
113                                         public boolean matches(Constructor<?> aObject) {
114                                                 return aObject.getParameterTypes().length == max2;
115                                         }
116                                 });
117                 if (longest.size() > 1) {
118                         throw new SystemAssemblyException(
119                                         "Greediest constructor cannot be uniquely determined");
120                 }
121             _constructor = longest.get(0);
122             resetValues();
123             return this; 
124         }
125         
126         public ParameterValues getParameters() {
127                 getConstructor(); // initialize constructor if needed.
128                 return _values; 
129         }
130
131         /**
132          * Resets the values. 
133          */
134         private void resetValues() {
135                 _constructor.setAccessible(true);
136                 _values = new ParameterValues(_constructor.getParameterTypes());        
137         }
138
139         /**
140          * Creates the object in the given scope. 
141          * @param aScope Scope containing required interfaces for this object. 
142          * @return object. 
143          */
144         public Object create(Scope aScope) {
145                 Object[] values = _values.values(aScope);
146                 try {
147                         return getConstructor().newInstance(values);
148                 } catch (Exception e) {
149                         throw new SystemAssemblyException("Could not construct object "
150                                         + getConstructor() + " " + Arrays.asList(values), e);
151                 }
152         }
153         
154         public List<RequiredInterface> getRequiredInterfaces() {
155                 getConstructor(); // initialize constructor if needed.
156                 return _values.getRequiredInterfaces(); 
157         }
158         
159         private Constructor getConstructor() { 
160                 if (_constructor == null ) { 
161                         greedy(); 
162                 }
163                 return _constructor; 
164         }
165 }