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.ui.framework.ioc;
35
36 import info.magnolia.objectfactory.Components;
37
38 import java.lang.reflect.Constructor;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Objects;
44 import java.util.Optional;
45
46 import javax.inject.Inject;
47
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import com.google.common.collect.ArrayListMultimap;
52 import com.google.common.collect.ListMultimap;
53 import com.google.common.collect.Multimaps;
54 import com.google.inject.AbstractModule;
55 import com.google.inject.Binding;
56 import com.google.inject.Key;
57 import com.google.inject.Module;
58 import com.google.inject.TypeLiteral;
59 import com.google.inject.binder.ScopedBindingBuilder;
60 import com.google.inject.internal.Scoping;
61 import com.google.inject.spi.Element;
62 import com.google.inject.spi.Elements;
63 import com.google.inject.spi.LinkedKeyBinding;
64 import com.google.inject.spi.ProviderInstanceBinding;
65 import com.google.inject.spi.ProviderKeyBinding;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 final class DeflateUiContextDependentBindings extends AbstractModule {
107
108 private static final Logger log = LoggerFactory.getLogger(DeflateUiContextDependentBindings.class);
109
110 private final Module sourceModule;
111
112 DeflateUiContextDependentBindings(Module sourceModule) {
113 this.sourceModule = sourceModule;
114 }
115
116 @Override
117 @SuppressWarnings("unchecked")
118 protected void configure() {
119 final List<Element> sourceElements = Elements.getElements(sourceModule);
120 final List<Element> elementsToWrite = new ArrayList<>(sourceElements);
121
122
123 final ListMultimap<TypeLiteral, Binding> annotatedBindings = ArrayListMultimap.create();
124 GuiceSpi.getBindings(sourceElements).forEach(binding -> {
125 final Key<?> key = binding.getKey();
126 if (key.getAnnotation() instanceof UiContextAnnotation) {
127 annotatedBindings.put(key.getTypeLiteral(), binding);
128 }
129 });
130
131 Multimaps.asMap(annotatedBindings).forEach((typeLiteral, bindings) -> {
132
133
134 if (mayDeflateBindings(bindings)) {
135 elementsToWrite.removeAll(bindings);
136
137
138 final Scoping scope = GuiceSpi.resolveScope(bindings.get(0));
139 GuiceSpi.<ScopedBindingBuilder> inspect(bindings.get(0))
140
141 .onProviderKeyBinding(binding -> bind(typeLiteral).toProvider(binding.getProviderKey()))
142
143 .onProviderInstanceBinding(binding -> bind(typeLiteral).toProvider(binding.getUserSuppliedProvider()))
144
145
146
147 .onLinkedBinding(binding -> {
148 final Key linkedKey = binding.getLinkedKey();
149 final Key sourceKey = Key.get(typeLiteral);
150
151 if (linkedKey.equals(sourceKey)) {
152 return bind(typeLiteral);
153 } else {
154 return bind(typeLiteral).to(linkedKey);
155 }
156 })
157 .visit()
158 .ifPresent(scopedBindingBuilder -> {
159 if (!Scoping.UNSCOPED.equals(scope)) {
160 scope.applyTo(scopedBindingBuilder);
161 }
162 });
163
164 } else {
165
166 final UiContextApplyingProvider resolvingProvider = UiContextApplyingProvider.of(typeLiteral.getRawType());
167
168
169 bindings.forEach(binding -> GuiceSpi.<Void>inspect(binding)
170 .consumeLinkedBinding(linkedKeyBinding -> {
171
172 if (Objects.equals(typeLiteral.getRawType(), linkedKeyBinding.getLinkedKey().getTypeLiteral().getRawType())) {
173 elementsToWrite.remove(linkedKeyBinding);
174 bindNonAbstractType(linkedKeyBinding.getKey(), GuiceSpi.resolveScope(linkedKeyBinding));
175 }
176 })
177 .visit());
178 bind(typeLiteral).toProvider(resolvingProvider);
179 }
180 });
181
182 elementsToWrite.forEach(element -> element.applyTo(binder()));
183 }
184
185
186
187
188
189
190 @SuppressWarnings("unchecked")
191 private void bindNonAbstractType(Key key, Scoping scope) {
192 final Class type = key.getTypeLiteral().getRawType();
193
194
195 final Optional<Constructor> injectConstructor =
196 Arrays.stream(type.getConstructors())
197 .filter(constructor -> constructor.isAnnotationPresent(Inject.class))
198 .findFirst();
199
200 if (injectConstructor.isPresent()) {
201 scope.applyTo(bind(key).toConstructor(injectConstructor.get()));
202 } else {
203
204 scope.applyTo(bind(key).toProvider(() -> Components.newInstance(type)));
205 }
206 }
207
208
209
210
211
212
213 private boolean mayDeflateBindings(List<Binding> bindings) {
214 boolean allBindingsIdentical = true;
215
216 Binding currentBinding = bindings.get(0);
217 final Iterator<Binding> bindingIterator = bindings.subList(1, bindings.size()).iterator();
218 while (allBindingsIdentical && bindingIterator.hasNext()) {
219 final Binding<?> nextBinding = bindingIterator.next();
220 allBindingsIdentical = isSameBinding(nextBinding, currentBinding);
221 currentBinding = nextBinding;
222 }
223
224 if (!allBindingsIdentical) {
225 return false;
226 }
227
228
229
230
231
232 for (final Binding<?> binding : bindings) {
233 if (UiAnnotations.cast(binding.getKey().getAnnotation()).isGeneric()) {
234 return true;
235 }
236 }
237
238 return false;
239 }
240
241 private boolean isSameBinding(Binding currentBinding, Binding previousBinding) {
242 return Objects.equals(resolveTarget(currentBinding), resolveTarget(previousBinding)) &&
243 Objects.equals(GuiceSpi.resolveScope(currentBinding), GuiceSpi.resolveScope(previousBinding));
244 }
245
246 private Object resolveTarget(Binding<?> binding) {
247 return GuiceSpi.inspect(binding)
248 .onLinkedBinding(LinkedKeyBinding::getLinkedKey)
249 .onProviderKeyBinding(ProviderKeyBinding::getProviderKey)
250 .onProviderInstanceBinding(ProviderInstanceBinding::getUserSuppliedProvider)
251 .visit()
252 .orElseThrow(() -> new IllegalArgumentException("Unable ot resolve target key of the binding: " + binding));
253 }
254 }