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 static java.util.stream.Collectors.joining;
37
38 import java.lang.reflect.Constructor;
39 import java.lang.reflect.InvocationTargetException;
40 import java.lang.reflect.Modifier;
41 import java.util.Arrays;
42 import java.util.stream.IntStream;
43 import java.util.stream.Stream;
44
45 import javax.inject.Inject;
46
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50
51
52
53
54
55
56
57 public class ObjectManufacturer {
58
59 private static final Logger log = LoggerFactory.getLogger(ObjectManufacturer.class);
60
61
62
63
64
65
66
67
68 public Object newInstance(Class<?> clazz, ParameterResolver... parameterResolvers) {
69 final ClassFactory cf = Classes.getClassFactory();
70 if (cf instanceof ReloadableClassFactory) {
71 try {
72 clazz = ((ReloadableClassFactory) cf).reload(clazz);
73 } catch (ClassNotFoundException e) {
74 throw new MgnlInstantiationException(e);
75 }
76 }
77
78 Constructor<?>[] constructors = clazz.getDeclaredConstructors();
79
80 Constructor<?> selectedConstructor = null;
81 for (Constructor<?> constructor : constructors) {
82 if (constructor.isAnnotationPresent(Inject.class)) {
83 if (selectedConstructor != null) {
84 throw new MgnlInstantiationException("Only one constructor can use @Inject [" + clazz + "]");
85 }
86 selectedConstructor = constructor;
87 }
88 }
89 if (selectedConstructor != null) {
90 selectedConstructor.setAccessible(true);
91 Object[] parameters = resolveParameters(selectedConstructor, parameterResolvers);
92 if (parameters == null) {
93 String params = getUnresolvableParameters(selectedConstructor, parameterResolvers)
94 .map(param -> String.format("%s at %d", param.getParameterType().getSimpleName(), param.getParameterIndex()))
95 .collect(joining(", ", "[", "]"));
96
97 throw new MgnlInstantiationException("Unable to resolve parameters " + params + " of ctor [" + constructorToString(selectedConstructor) + "]");
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 | IllegalAccessException | InvocationTargetException e) {
131 throw new MgnlInstantiationException("Failed to create an instance with c-tor [" + constructorToString(constructor) + "]", e);
132 }
133 }
134
135 private Object[] resolveParameters(Constructor<?> constructor, ParameterResolver[] parameterResolvers) {
136 Object[] parameters = new Object[constructor.getParameterTypes().length];
137 for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
138 ParameterInfoeterInfo">ParameterInfo constructorParameter = new ParameterInfo(constructor, parameterIndex);
139 try {
140 Object parameter = resolveParameter(constructorParameter, parameterResolvers);
141 if (parameter == ParameterResolver.UNRESOLVED) {
142 return null;
143 }
144 parameters[parameterIndex] = parameter;
145 } catch (Exception e) {
146 throw new MgnlInstantiationException("Failed to resolve param [" + constructorParameter.getParameterIndex() + "] of type [" + constructorParameter.getParameterType() + "]", e);
147 }
148 }
149 return parameters;
150 }
151
152 private Stream<ParameterInfo> getUnresolvableParameters(Constructor<?> constructor, ParameterResolver[] parameterResolvers) {
153 return IntStream.range(0, constructor.getParameterTypes().length)
154 .mapToObj(idx -> new ParameterInfo(constructor, idx))
155 .filter(param -> resolveParameter(param, parameterResolvers) == ParameterResolver.UNRESOLVED);
156 }
157
158 private Object resolveParameter(ParameterInfo constructorParameter, ParameterResolver[] parameterResolvers) {
159 for (ParameterResolver parameterResolver : parameterResolvers) {
160 Object parameter = parameterResolver.resolveParameter(constructorParameter);
161 if (parameter != ParameterResolver.UNRESOLVED) {
162 return parameter;
163 }
164 }
165 log.debug("Failed to resolve {}. parameter {} in constructor of {}. Will use other constructor (if available).", (constructorParameter.getParameterIndex() + 1), constructorParameter.getParameterType(), constructorParameter.getDeclaringClass());
166 return ParameterResolver.UNRESOLVED;
167 }
168
169 private String constructorToString(Constructor ctor) {
170 return ctor.getDeclaringClass().getSimpleName() +
171 Arrays.stream(ctor.getParameterTypes()).map(Class::getSimpleName).collect(joining(", ", "(", ")"));
172 }
173 }