1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.objectfactory;
35
36 import java.lang.reflect.Constructor;
37 import java.lang.reflect.InvocationTargetException;
38 import java.lang.reflect.Modifier;
39
40 import javax.inject.Inject;
41
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45
46
47
48
49
50
51
52 public class ObjectManufacturer {
53
54 private static final Logger log = LoggerFactory.getLogger(ObjectManufacturer.class);
55
56
57
58
59
60
61
62
63 public Object newInstance(Class<?> clazz, ParameterResolver... parameterResolvers) {
64 final ClassFactory cf = Classes.getClassFactory();
65 if (cf instanceof ReloadableClassFactory) {
66 try {
67 clazz = ((ReloadableClassFactory) cf).reload(clazz);
68 } catch (ClassNotFoundException e) {
69 throw new MgnlInstantiationException(e);
70 }
71 }
72
73 Constructor<?>[] constructors = clazz.getDeclaredConstructors();
74
75 Constructor<?> selectedConstructor = null;
76 for (Constructor<?> constructor : constructors) {
77 if (constructor.isAnnotationPresent(Inject.class)) {
78 if (selectedConstructor != null) {
79 throw new MgnlInstantiationException("Only one constructor can use @Inject [" + clazz + "]");
80 }
81 selectedConstructor = constructor;
82 }
83 }
84 if (selectedConstructor != null) {
85 selectedConstructor.setAccessible(true);
86 Object[] parameters = resolveParameters(selectedConstructor, parameterResolvers);
87 if (parameters == null) {
88 StringBuilder params = new StringBuilder();
89 int index = 1;
90 for (Object param : getUnresolvableParameters(selectedConstructor, parameterResolvers)) {
91 if (param != null) {
92 params.append(index).append(index == 1 ? "st" : index == 2 ? "nd" : index == 3 ? "rd" : "th").append(" parameter which is of type ").append(((ParameterInfo) param).getParameterType().getName()).append(", ");
93 }
94 index++;
95 }
96
97 throw new MgnlInstantiationException("Unable to resolve parameters for constructor " + selectedConstructor + (params.length() > 2 ? (". Unresolved parameter(s) are: " + params.toString().substring(0, params.length() - 2)) : ""));
98 }
99 return newInstance(selectedConstructor, parameters);
100 }
101
102
103 int bestScore = -1;
104 Object[] bestParameters = null;
105 for (Constructor<?> constructor : constructors) {
106 if (!Modifier.isPublic(constructor.getModifiers())) {
107 continue;
108 }
109 int score = constructor.getParameterTypes().length;
110 if (score < bestScore) {
111 continue;
112 }
113 Object[] parameters = resolveParameters(constructor, parameterResolvers);
114 if (parameters == null) {
115 continue;
116 }
117 selectedConstructor = constructor;
118 bestScore = score;
119 bestParameters = parameters;
120 }
121 if (selectedConstructor != null) {
122 return newInstance(selectedConstructor, bestParameters);
123 }
124 throw new MgnlInstantiationException("No suitable constructor found for class [" + clazz + "]");
125 }
126
127 private Object newInstance(Constructor constructor, Object[] parameters) {
128 try {
129 return constructor.newInstance(parameters);
130 } catch (InstantiationException e) {
131 throw new MgnlInstantiationException(e);
132 } catch (IllegalAccessException e) {
133 throw new MgnlInstantiationException(e);
134 } catch (InvocationTargetException e) {
135 throw new MgnlInstantiationException(e);
136 }
137 }
138
139 private Object[] resolveParameters(Constructor<?> constructor, ParameterResolver[] parameterResolvers) {
140 Object[] parameters = new Object[constructor.getParameterTypes().length];
141 for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
142 ParameterInfo constructorParameter = new ParameterInfo(constructor, parameterIndex);
143 Object parameter = resolveParameter(constructorParameter, parameterResolvers);
144 if (parameter == ParameterResolver.UNRESOLVED) {
145 return null;
146 }
147 parameters[parameterIndex] = parameter;
148 }
149 return parameters;
150 }
151
152 private Object[] getUnresolvableParameters(Constructor<?> constructor, ParameterResolver[] parameterResolvers) {
153 Object[] parameters = new Object[constructor.getParameterTypes().length];
154 for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
155 ParameterInfo constructorParameter = new ParameterInfo(constructor, parameterIndex);
156 Object parameter = resolveParameter(constructorParameter, parameterResolvers);
157 if (parameter == ParameterResolver.UNRESOLVED) {
158 parameters[parameterIndex] = constructorParameter;
159 }
160 }
161 return parameters;
162 }
163
164 private Object resolveParameter(ParameterInfo constructorParameter, ParameterResolver[] parameterResolvers) {
165 for (ParameterResolver parameterResolver : parameterResolvers) {
166 Object parameter = parameterResolver.resolveParameter(constructorParameter);
167 if (parameter != ParameterResolver.UNRESOLVED) {
168 return parameter;
169 }
170 }
171 log.debug("Failed to resolve {}. parameter {} in constructor of {}. Will use other constructor (if available).", (constructorParameter.getParameterIndex() + 1), constructorParameter.getParameterType(), constructorParameter.getDeclaringClass());
172 return ParameterResolver.UNRESOLVED;
173 }
174
175 }