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 public class ObjectManufacturer {
50
51 private static final Logger log = LoggerFactory.getLogger(ObjectManufacturer.class);
52
53
54
55
56
57
58
59
60 public Object newInstance(Class<?> clazz, ParameterResolver... parameterResolvers) {
61
62 Constructor<?>[] constructors = clazz.getDeclaredConstructors();
63
64 Constructor<?> selectedConstructor = null;
65 for (Constructor<?> constructor : constructors) {
66 if (constructor.isAnnotationPresent(Inject.class)) {
67 if (selectedConstructor != null) {
68 throw new MgnlInstantiationException("Only one constructor can use @Inject [" + clazz + "]");
69 }
70 selectedConstructor = constructor;
71 }
72 }
73 if (selectedConstructor != null) {
74 selectedConstructor.setAccessible(true);
75 Object[] parameters = resolveParameters(selectedConstructor, parameterResolvers);
76 if (parameters == null) {
77 StringBuilder params = new StringBuilder();
78 int index = 1;
79 for (Object param : getUnresolvableParameters(selectedConstructor, parameterResolvers)) {
80 if (param != null) {
81 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(", ");
82 }
83 index++;
84 }
85
86 throw new MgnlInstantiationException("Unable to resolve parameters for constructor " + selectedConstructor + (params.length() > 2 ? (". Unresolved parameter(s) are: " + params.toString().substring(0, params.length() - 2)) : ""));
87 }
88 return newInstance(selectedConstructor, parameters);
89 }
90
91
92 int bestScore = -1;
93 Object[] bestParameters = null;
94 for (Constructor<?> constructor : constructors) {
95 if (!Modifier.isPublic(constructor.getModifiers())) {
96 continue;
97 }
98 int score = constructor.getParameterTypes().length;
99 if (score < bestScore) {
100 continue;
101 }
102 Object[] parameters = resolveParameters(constructor, parameterResolvers);
103 if (parameters == null) {
104 continue;
105 }
106 selectedConstructor = constructor;
107 bestScore = score;
108 bestParameters = parameters;
109 }
110 if (selectedConstructor != null) {
111 return newInstance(selectedConstructor, bestParameters);
112 }
113 throw new MgnlInstantiationException("No suitable constructor found for class [" + clazz + "]");
114 }
115
116 private Object newInstance(Constructor constructor, Object[] parameters) {
117 try {
118 return constructor.newInstance(parameters);
119 } catch (InstantiationException e) {
120 throw new MgnlInstantiationException(e);
121 } catch (IllegalAccessException e) {
122 throw new MgnlInstantiationException(e);
123 } catch (InvocationTargetException e) {
124 throw new MgnlInstantiationException(e);
125 }
126 }
127
128 private Object[] resolveParameters(Constructor<?> constructor, ParameterResolver[] parameterResolvers) {
129 Object[] parameters = new Object[constructor.getParameterTypes().length];
130 for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
131 ParameterInfo constructorParameter = new ParameterInfo(constructor, parameterIndex);
132 Object parameter = resolveParameter(constructorParameter, parameterResolvers);
133 if (parameter == ParameterResolver.UNRESOLVED) {
134 return null;
135 }
136 parameters[parameterIndex] = parameter;
137 }
138 return parameters;
139 }
140
141 private Object[] getUnresolvableParameters(Constructor<?> constructor, ParameterResolver[] parameterResolvers) {
142 Object[] parameters = new Object[constructor.getParameterTypes().length];
143 for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
144 ParameterInfo constructorParameter = new ParameterInfo(constructor, parameterIndex);
145 Object parameter = resolveParameter(constructorParameter, parameterResolvers);
146 if (parameter == ParameterResolver.UNRESOLVED) {
147 parameters[parameterIndex] = constructorParameter;
148 }
149 }
150 return parameters;
151 }
152
153 private Object resolveParameter(ParameterInfo constructorParameter, ParameterResolver[] parameterResolvers) {
154 for (ParameterResolver parameterResolver : parameterResolvers) {
155 Object parameter = parameterResolver.resolveParameter(constructorParameter);
156 if (parameter != ParameterResolver.UNRESOLVED) {
157 return parameter;
158 }
159 }
160 log.debug("Failed to resolve {}. parameter {} in constructor of {}. Will use other constructor (if available).", (constructorParameter.getParameterIndex() + 1), constructorParameter.getParameterType(), constructorParameter.getDeclaringClass());
161 return ParameterResolver.UNRESOLVED;
162 }
163
164 }