blob: 7cd671705989b8a98e3049174927b0dead155cea [file] [log] [blame]
Pierre De Ropfaca2892016-01-31 23:27:05 +00001package org.apache.felix.dm.lambda.impl;
2
3import java.util.ArrayList;
4import java.util.Dictionary;
5import java.util.HashMap;
6import java.util.List;
7import java.util.Map;
8import java.util.Objects;
Pierre De Rop11527502016-02-18 21:07:16 +00009import java.util.function.Function;
Pierre De Ropfaca2892016-01-31 23:27:05 +000010import java.util.stream.Stream;
11
12import org.apache.felix.dm.BundleDependency;
13import org.apache.felix.dm.Component;
14import org.apache.felix.dm.DependencyManager;
15import org.apache.felix.dm.lambda.BundleDependencyBuilder;
16import org.apache.felix.dm.lambda.callbacks.CbBundle;
Pierre De Rop11527502016-02-18 21:07:16 +000017import org.apache.felix.dm.lambda.callbacks.CbBundleComponent;
18import org.apache.felix.dm.lambda.callbacks.InstanceCbBundle;
19import org.apache.felix.dm.lambda.callbacks.InstanceCbBundleComponent;
Pierre De Ropfaca2892016-01-31 23:27:05 +000020import org.osgi.framework.Bundle;
21
22@SuppressWarnings("unchecked")
23public class BundleDependencyBuilderImpl implements BundleDependencyBuilder {
24 private String m_added;
25 private String m_changed;
26 private String m_removed;
27 private Object m_instance;
28 private boolean m_autoConfig = true;
29 private boolean m_autoConfigInvoked = false;
30 private boolean m_required = true;
31 private Bundle m_bundle;
32 private String m_filter;
33 private int m_stateMask = -1;
34 private boolean m_propagate;
35 private Object m_propagateInstance;
36 private String m_propagateMethod;
Pierre De Rop11527502016-02-18 21:07:16 +000037 private Function<Bundle, Dictionary<?, ?>> m_propagateCallback;
Pierre De Ropfaca2892016-01-31 23:27:05 +000038 private final Component m_component;
39
40 enum Cb {
41 ADD,
42 CHG,
43 REM
44 };
45
46 private final Map<Cb, List<MethodRef<Object>>> m_refs = new HashMap<>();
47
48 @FunctionalInterface
49 interface MethodRef<I> {
50 public void accept(I instance, Component c, Bundle bundle);
51 }
52
53 /**
54 * Class used to call a supplier that returns Propagated properties
55 */
56 private class Propagate {
57 @SuppressWarnings("unused")
Pierre De Rop11527502016-02-18 21:07:16 +000058 Dictionary<?, ?> propagate(Bundle bundle) {
59 return m_propagateCallback.apply(bundle);
Pierre De Ropfaca2892016-01-31 23:27:05 +000060 }
61 }
62
63 public BundleDependencyBuilderImpl (Component component) {
64 m_component = component;
Pierre De Rop11527502016-02-18 21:07:16 +000065 m_required = Helpers.isDependencyRequiredByDefault(component);
Pierre De Ropfaca2892016-01-31 23:27:05 +000066 }
67
68 @Override
69 public BundleDependencyBuilder autoConfig(boolean autoConfig) {
70 m_autoConfig = autoConfig;
71 m_autoConfigInvoked = true;
72 return this;
73 }
74
75 @Override
76 public BundleDependencyBuilder autoConfig() {
77 autoConfig(true);
78 return this;
79 }
80
81 @Override
82 public BundleDependencyBuilder required(boolean required) {
83 m_required = required;
84 return this;
85 }
86
87 @Override
88 public BundleDependencyBuilder required() {
89 required(true);
90 return this;
91 }
92
93 @Override
94 public BundleDependencyBuilder bundle(Bundle bundle) {
95 m_bundle = bundle;
96 return this;
97 }
98
99 @Override
100 public BundleDependencyBuilder filter(String filter) throws IllegalArgumentException {
101 m_filter = filter;
102 return this;
103 }
104
105 @Override
106 public BundleDependencyBuilder mask(int mask) {
107 m_stateMask = mask;
108 return this;
109 }
110
111 @Override
112 public BundleDependencyBuilder propagate(boolean propagate) {
113 m_propagate = propagate;
114 return this;
115 }
116
117 @Override
118 public BundleDependencyBuilder propagate() {
119 propagate(true);
120 return this;
121 }
122
123 @Override
124 public BundleDependencyBuilder propagate(Object instance, String method) {
Pierre De Rop11527502016-02-18 21:07:16 +0000125 if (m_propagateCallback != null || m_propagate) throw new IllegalStateException("Propagate callback already set.");
Pierre De Ropfaca2892016-01-31 23:27:05 +0000126 Objects.nonNull(method);
127 Objects.nonNull(instance);
128 m_propagateInstance = instance;
129 m_propagateMethod = method;
130 return this;
131 }
132
133 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000134 public BundleDependencyBuilder propagate(Function<Bundle, Dictionary<?, ?>> instance) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000135 if (m_propagateInstance != null || m_propagate) throw new IllegalStateException("Propagate callback already set.");
Pierre De Rop11527502016-02-18 21:07:16 +0000136 m_propagateCallback = instance;
Pierre De Ropfaca2892016-01-31 23:27:05 +0000137 return this;
138 }
Pierre De Ropfaca2892016-01-31 23:27:05 +0000139
140 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000141 public BundleDependencyBuilder callbackInstance(Object callbackInstance) {
142 m_instance = callbackInstance;
143 return this;
144 }
Pierre De Ropfaca2892016-01-31 23:27:05 +0000145
Pierre De Rop11527502016-02-18 21:07:16 +0000146 @Override
147 public BundleDependencyBuilder add(String add) {
148 callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000149 return this;
150 }
151
Pierre De Rop11527502016-02-18 21:07:16 +0000152 @Override
153 public BundleDependencyBuilder change(String change) {
154 callbacks(null, change, null);
155 return this;
156 }
157
158 @Override
159 public BundleDependencyBuilder remove(String remove) {
160 callbacks(null, null, remove);
161 return this;
162 }
163
164 private BundleDependencyBuilder callbacks(String added, String changed, String removed) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000165 requiresNoMethodRefs();
Pierre De Ropfaca2892016-01-31 23:27:05 +0000166 m_added = added != null ? added : m_added;
167 m_changed = changed != null ? changed : m_changed;
168 m_removed = removed != null ? removed : m_removed;
169 if (! m_autoConfigInvoked) m_autoConfig = false;
170 return this;
171 }
172
173 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000174 public <T> BundleDependencyBuilder add(CbBundle<T> add) {
175 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000176 }
177
178 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000179 public <T> BundleDependencyBuilder change(CbBundle<T> change) {
180 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000181 }
182
183 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000184 public <T> BundleDependencyBuilder remove(CbBundle<T> remove) {
185 return callbacks(null, null, remove);
186 }
187
188 private <T> BundleDependencyBuilder callbacks(CbBundle<T> add, CbBundle<T> change, CbBundle<T> remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000189 if (add != null) {
190 setComponentCallbackRef(Cb.ADD, Helpers.getLambdaArgType(add, 0), (inst, component, bundle) -> add.accept ((T) inst, bundle));
191 }
192 if (change != null) {
193 setComponentCallbackRef(Cb.CHG, Helpers.getLambdaArgType(change, 0), (inst, component, bundle) -> change.accept ((T) inst, bundle));
194 }
195 if (remove != null) {
196 setComponentCallbackRef(Cb.REM, Helpers.getLambdaArgType(remove, 0), (inst, component, bundle) -> remove.accept ((T) inst, bundle));
197 }
198 return this;
199 }
200
201 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000202 public <T> BundleDependencyBuilder add(CbBundleComponent<T> add) {
203 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000204 }
205
206 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000207 public <T> BundleDependencyBuilder change(CbBundleComponent<T> change) {
208 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000209 }
210
211 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000212 public <T> BundleDependencyBuilder remove(CbBundleComponent<T> remove) {
213 return callbacks(null, null, remove);
214 }
215
216 private <T> BundleDependencyBuilder callbacks(CbBundleComponent<T> add, CbBundleComponent<T> change, CbBundleComponent<T> remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000217 if (add != null) {
Pierre De Rop11527502016-02-18 21:07:16 +0000218 setComponentCallbackRef(Cb.ADD, Helpers.getLambdaArgType(add, 0), (inst, component, bundle) -> add.accept ((T) inst, bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000219 }
220 if (change != null) {
Pierre De Rop11527502016-02-18 21:07:16 +0000221 setComponentCallbackRef(Cb.CHG, Helpers.getLambdaArgType(change, 0), (inst, component, bundle) -> change.accept ((T) inst, bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000222 }
223 if (remove != null) {
Pierre De Rop11527502016-02-18 21:07:16 +0000224 setComponentCallbackRef(Cb.REM, Helpers.getLambdaArgType(remove, 0), (inst, component, bundle) -> remove.accept ((T) inst, bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000225 }
226 return this;
227 }
228
229 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000230 public BundleDependencyBuilder add(InstanceCbBundle add) {
231 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000232 }
233
234 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000235 public BundleDependencyBuilder change(InstanceCbBundle change) {
236 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000237 }
238
239 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000240 public BundleDependencyBuilder remove(InstanceCbBundle remove) {
241 return callbacks(null, null, remove);
242 }
243
244 private BundleDependencyBuilder callbacks(InstanceCbBundle add, InstanceCbBundle change, InstanceCbBundle remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000245 if (add != null) setInstanceCallbackRef(Cb.ADD, (inst, component, bundle) -> add.accept(bundle));
246 if (change != null) setInstanceCallbackRef(Cb.CHG, (inst, component, bundle) -> change.accept(bundle));
247 if (remove != null) setInstanceCallbackRef(Cb.REM, (inst, component, bundle) -> remove.accept(bundle));
248 return this;
249 }
250
251 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000252 public BundleDependencyBuilder add(InstanceCbBundleComponent add) {
253 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000254 }
255
256 @Override
Pierre De Rop11527502016-02-18 21:07:16 +0000257 public BundleDependencyBuilder change(InstanceCbBundleComponent add) {
258 return callbacks(add, null, null);
259 }
260
261 @Override
262 public BundleDependencyBuilder remove(InstanceCbBundleComponent remove) {
263 return callbacks(null, null, remove);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000264 }
265
Pierre De Rop11527502016-02-18 21:07:16 +0000266 private BundleDependencyBuilder callbacks(InstanceCbBundleComponent add, InstanceCbBundleComponent change, InstanceCbBundleComponent remove) {
267 if (add != null) setInstanceCallbackRef(Cb.ADD, (inst, component, bundle) -> add.accept(bundle, component));
268 if (change != null) setInstanceCallbackRef(Cb.CHG, (inst, component, bundle) -> change.accept(bundle, component));
269 if (remove != null) setInstanceCallbackRef(Cb.REM, (inst, component, bundle) -> remove.accept(bundle, component));
Pierre De Ropfaca2892016-01-31 23:27:05 +0000270 return this;
271 }
272
273 @Override
274 public BundleDependency build() {
275 DependencyManager dm = m_component.getDependencyManager();
276
277 BundleDependency dep = dm.createBundleDependency();
278 dep.setRequired(m_required);
279
280 if (m_filter != null) {
281 dep.setFilter(m_filter);
282 }
283
284 if (m_bundle != null) {
285 dep.setBundle(m_bundle);
286 }
287
288 if (m_stateMask != -1) {
289 dep.setStateMask(m_stateMask);
290 }
291
292 if (m_propagate) {
293 dep.setPropagate(true);
294 } else if (m_propagateInstance != null) {
295 dep.setPropagate(m_propagateInstance, m_propagateMethod);
Pierre De Rop11527502016-02-18 21:07:16 +0000296 } else if (m_propagateCallback != null) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000297 dep.setPropagate(new Propagate(), "propagate");
298 }
299
300 if (m_added != null || m_changed != null || m_removed != null) {
301 dep.setCallbacks(m_instance, m_added, m_changed, m_removed);
302 } else if (m_refs.size() > 0) {
303 Object cb = createCallbackInstance();
304 dep.setCallbacks(cb, "add", "change", "remove");
305 }
306
307 dep.setAutoConfig(m_autoConfig);
308 return dep;
309 }
310
311 private <T> BundleDependencyBuilder setInstanceCallbackRef(Cb cbType, MethodRef<T> ref) {
312 requiresNoStringCallbacks();
313 if (! m_autoConfigInvoked) m_autoConfig = false;
314 List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>());
315 list.add((instance, component, bundle) -> ref.accept(null, component, bundle));
316 return this;
317 }
318
319 private <T> BundleDependencyBuilder setComponentCallbackRef(Cb cbType, Class<T> type, MethodRef<T> ref) {
320 requiresNoStringCallbacks();
321 if (! m_autoConfigInvoked) m_autoConfig = false;
322 List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>());
323 list.add((instance, component, bundle) -> {
324 Object componentImpl = Stream.of(component.getInstances())
325 .filter(impl -> Helpers.getClass(impl).equals(type))
326 .findFirst()
327 .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes."));
328 ref.accept((T) componentImpl, component, bundle);
329 });
330 return this;
331 }
332
333 @SuppressWarnings("unused")
334 private Object createCallbackInstance() {
335 Object cb = null;
336
337 cb = new Object() {
338 void add(Component c, Bundle bundle) {
339 invokeMethodRefs(Cb.ADD, c, bundle);
340 }
341
342 void change(Component c, Bundle bundle) {
343 invokeMethodRefs(Cb.CHG, c, bundle);
344 }
345
346 void remove(Component c, Bundle bundle) {
347 invokeMethodRefs(Cb.REM, c, bundle);
348 }
349 };
350
351 return cb;
352 }
353
354 private void invokeMethodRefs(Cb cbType, Component c, Bundle bundle) {
355 m_refs.computeIfPresent(cbType, (k, mrefs) -> {
356 mrefs.forEach(mref -> mref.accept(null, c, bundle));
357 return mrefs;
358 });
359 }
360
361 private void requiresNoStringCallbacks() {
362 if (m_added != null || m_changed != null || m_removed != null) {
363 throw new IllegalStateException("can't mix method references and string callbacks.");
364 }
365 }
366
367 private void requiresNoMethodRefs() {
368 if (m_refs.size() > 0) {
369 throw new IllegalStateException("can't mix method references and string callbacks.");
370 }
371 }
372}