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