blob: bb135121d1cfb7c69deaa2c41b4f68e6392776bc [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.HashMap;
5import java.util.List;
6import java.util.Map;
7import java.util.function.Consumer;
8import java.util.stream.Stream;
9
10import org.apache.felix.dm.Component;
11import org.apache.felix.dm.DependencyManager;
12import org.apache.felix.dm.lambda.BundleAdapterBuilder;
13import org.apache.felix.dm.lambda.ComponentBuilder;
14import org.apache.felix.dm.lambda.callbacks.CbBundle;
Pierre De Rop11527502016-02-18 21:07:16 +000015import org.apache.felix.dm.lambda.callbacks.CbBundleComponent;
16import org.apache.felix.dm.lambda.callbacks.InstanceCbBundle;
17import org.apache.felix.dm.lambda.callbacks.InstanceCbBundleComponent;
Pierre De Ropfaca2892016-01-31 23:27:05 +000018import org.osgi.framework.Bundle;
19
20public class BundleAdapterBuilderImpl implements AdapterBase<BundleAdapterBuilder>, BundleAdapterBuilder {
21 private Consumer<ComponentBuilder<?>> m_compBuilder = (compBuilder -> {});
22 protected final Map<Cb, List<MethodRef<Object>>> m_refs = new HashMap<>();
23 private DependencyManager m_dm;
24 private boolean m_autoAdd;
25 private String m_added;
26 private String m_changed;
27 private String m_removed;
28 private String m_filter;
29 private int m_stateMask = -1;
30 private boolean m_propagate;
31 private Object m_callbackInstance;
32 private String m_add;
33 private String m_change;
34 private String m_remove;
35
36 enum Cb {
37 ADD,
38 CHG,
39 REM
40 };
41
42 @FunctionalInterface
43 interface MethodRef<I> {
44 public void accept(I instance, Component c, Bundle b);
45 }
46
47 public BundleAdapterBuilderImpl(DependencyManager dm) {
48 m_dm = dm;
49 }
50
51 public void andThenBuild(Consumer<ComponentBuilder<?>> builder) {
52 m_compBuilder = m_compBuilder.andThen(builder);
53 }
54
55 @Override
56 public BundleAdapterBuilderImpl autoAdd(boolean autoAdd) {
57 m_autoAdd = autoAdd;
58 return this;
59 }
60
61 public boolean isAutoAdd() {
62 return m_autoAdd;
63 }
64
65 public BundleAdapterBuilder mask(int mask) {
66 m_stateMask = mask;
67 return this;
68 }
69
70 public BundleAdapterBuilder filter(String filter) {
71 m_filter = filter;
72 return this;
73 }
74
75 public BundleAdapterBuilder propagate(boolean propagate) {
76 m_propagate = propagate;
77 return this;
78 }
79
80 public BundleAdapterBuilder propagate() {
81 m_propagate = true;
82 return this;
83 }
84
Pierre De Rop11527502016-02-18 21:07:16 +000085 public BundleAdapterBuilder add(String callback) {
86 return callbacks(callback, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +000087 }
88
Pierre De Rop11527502016-02-18 21:07:16 +000089 public BundleAdapterBuilder change(String callback) {
90 return callbacks(null, callback, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +000091 }
92
Pierre De Rop11527502016-02-18 21:07:16 +000093 public BundleAdapterBuilder remove(String callback) {
94 return callbacks(null, null, callback);
95 }
96
97 public BundleAdapterBuilder callbackInstance(Object callbackInstance) {
Pierre De Ropfaca2892016-01-31 23:27:05 +000098 m_callbackInstance = callbackInstance;
Pierre De Ropfaca2892016-01-31 23:27:05 +000099 return this;
100 }
101
Pierre De Rop11527502016-02-18 21:07:16 +0000102 private BundleAdapterBuilder callbacks(String add, String change, String remove) {
103 checkHasNoMethodRefs();
104 m_add = add != null ? add : m_add;
105 m_change = change != null ? change : m_change;
106 m_remove = remove != null ? remove : m_remove;
107 return this;
108 }
109
110 public <T> BundleAdapterBuilder add(CbBundle<T> add) {
111 return callbacks(add, (CbBundle<T>) null, (CbBundle<T>) null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000112 }
113
Pierre De Rop11527502016-02-18 21:07:16 +0000114 public <T> BundleAdapterBuilder change(CbBundle<T> change) {
115 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000116 }
117
Pierre De Rop11527502016-02-18 21:07:16 +0000118 public <T> BundleAdapterBuilder remove(CbBundle<T> remove) {
119 return callbacks(null, null, remove);
120 }
121
122 private <T> BundleAdapterBuilder callbacks(CbBundle<T> add, CbBundle<T> change, CbBundle<T> remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000123 if (add != null) {
124 Class<T> type = Helpers.getLambdaArgType(add, 0);
125 setComponentCallbackRef(Cb.ADD, type, (instance, component, bundle) -> { add.accept((T) instance, bundle); });
126 }
127 if (change != null) {
128 Class<T> type = Helpers.getLambdaArgType(change, 0);
129 setComponentCallbackRef(Cb.CHG, type, (instance, component, bundle) -> { change.accept((T) instance, bundle); });
130 }
131 if (remove != null) {
132 Class<T> type = Helpers.getLambdaArgType(remove, 0);
133 setComponentCallbackRef(Cb.REM, type, (instance, component, bundle) -> { remove.accept((T) instance, bundle); });
134 }
135 return this;
136 }
137
Pierre De Rop11527502016-02-18 21:07:16 +0000138 public BundleAdapterBuilder add(InstanceCbBundle add) {
139 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000140 }
141
Pierre De Rop11527502016-02-18 21:07:16 +0000142 public BundleAdapterBuilder change(InstanceCbBundle change) {
143 return callbacks(null, change, null);
144 }
145
146 public BundleAdapterBuilder remove(InstanceCbBundle remove) {
147 return callbacks(null, null, remove);
148 }
149
150 public BundleAdapterBuilder callbacks(InstanceCbBundle add, InstanceCbBundle change, InstanceCbBundle remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000151 if (add != null) setInstanceCallbackRef(Cb.ADD, (instance, component, bundle) -> { add.accept(bundle); });
152 if (change != null) setInstanceCallbackRef(Cb.CHG, (instance, component, bundle) -> { change.accept(bundle); });
153 if (remove != null) setInstanceCallbackRef(Cb.REM, (instance, component, bundle) -> { remove.accept(bundle); });
154 return this;
155 }
156
Pierre De Rop11527502016-02-18 21:07:16 +0000157 public <T> BundleAdapterBuilder add(CbBundleComponent<T> add) {
158 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000159 }
160
Pierre De Rop11527502016-02-18 21:07:16 +0000161 public <T> BundleAdapterBuilder change(CbBundleComponent<T> change) {
162 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000163 }
164
Pierre De Rop11527502016-02-18 21:07:16 +0000165 public <T> BundleAdapterBuilder remove(CbBundleComponent<T> remove) {
166 return callbacks(null, null, remove);
167 }
168
169 public <T> BundleAdapterBuilder callbacks(CbBundleComponent<T> add, CbBundleComponent<T> change, CbBundleComponent<T> remove) {
Pierre De Ropfaca2892016-01-31 23:27:05 +0000170 if (add != null) {
171 Class<T> type = Helpers.getLambdaArgType(add, 0);
Pierre De Rop11527502016-02-18 21:07:16 +0000172 return setComponentCallbackRef(Cb.ADD, type, (instance, component, bundle) -> { add.accept((T) instance, bundle, component); });
Pierre De Ropfaca2892016-01-31 23:27:05 +0000173 }
174 if (change != null) {
175 Class<T> type = Helpers.getLambdaArgType(change, 0);
Pierre De Rop11527502016-02-18 21:07:16 +0000176 return setComponentCallbackRef(Cb.CHG, type, (instance, component, bundle) -> { change.accept((T) instance, bundle, component); });
Pierre De Ropfaca2892016-01-31 23:27:05 +0000177 }
178 if (remove != null) {
179 Class<T> type = Helpers.getLambdaArgType(remove, 0);
Pierre De Rop11527502016-02-18 21:07:16 +0000180 return setComponentCallbackRef(Cb.ADD, type, (instance, component, bundle) -> { remove.accept((T) instance, bundle, component); });
Pierre De Ropfaca2892016-01-31 23:27:05 +0000181 }
182 return this;
183 }
184
Pierre De Rop11527502016-02-18 21:07:16 +0000185 public BundleAdapterBuilder add(InstanceCbBundleComponent add) {
186 return callbacks(add, null, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000187 }
188
Pierre De Rop11527502016-02-18 21:07:16 +0000189 public BundleAdapterBuilder change(InstanceCbBundleComponent change) {
190 return callbacks(null, change, null);
Pierre De Ropfaca2892016-01-31 23:27:05 +0000191 }
Pierre De Rop11527502016-02-18 21:07:16 +0000192
193 public BundleAdapterBuilder remove(InstanceCbBundleComponent remove) {
194 return callbacks(null, null, remove);
195 }
196
197 public BundleAdapterBuilder callbacks(InstanceCbBundleComponent add, InstanceCbBundleComponent change, InstanceCbBundleComponent remove) {
198 if (add != null) setInstanceCallbackRef(Cb.ADD, (instance, component, bundle) -> { add.accept(bundle, component); });
199 if (change != null) setInstanceCallbackRef(Cb.CHG, (instance, component, bundle) -> { change.accept(bundle, component); });
200 if (remove != null) setInstanceCallbackRef(Cb.REM, (instance, component, bundle) -> { remove.accept(bundle, component); });
Pierre De Ropfaca2892016-01-31 23:27:05 +0000201 return this;
202 }
203
204 @Override
205 public Component build() {
206 Component c = null;
207
208 if (m_refs.size() > 0) {
209 @SuppressWarnings("unused")
210 Object wrapCallback = new Object() {
211 public void add(Component comp, Bundle bundle) {
212 invokeMethodRefs(Cb.ADD, comp, bundle);
213 }
214
215 public void change(Component comp, Bundle bundle) {
216 invokeMethodRefs(Cb.CHG, comp, bundle);
217 }
218
219 public void remove(Component comp, Bundle bundle) {
220 invokeMethodRefs(Cb.REM, comp, bundle);
221 }
222 };
223 c = m_dm.createBundleAdapterService(m_stateMask, m_filter, m_propagate, wrapCallback, "add", "change", "remove");
224 } else {
225 c = m_dm.createBundleAdapterService(m_stateMask, m_filter, m_propagate, m_callbackInstance, m_add, m_change, m_remove);
226 }
227 ComponentBuilderImpl cb = new ComponentBuilderImpl(c, false);
228 m_compBuilder.accept (cb);
229 return cb.build();
230 }
231
232 private <U> BundleAdapterBuilder setInstanceCallbackRef(Cb cbType, MethodRef<U> ref) {
233 checkHasNoReflectionCallbacks();
234 List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>());
235 list.add((instance, component, bundle) -> ref.accept(null, component, bundle));
236 return this;
237 }
238
239 @SuppressWarnings("unchecked")
240 private <U> BundleAdapterBuilder setComponentCallbackRef(Cb cbType, Class<U> type, MethodRef<U> ref) {
241 checkHasNoReflectionCallbacks();
242 List<MethodRef<Object>> list = m_refs.computeIfAbsent(cbType, l -> new ArrayList<>());
243 list.add((instance, component, bundle) -> {
244 Object componentImpl = Stream.of(component.getInstances())
245 .filter(impl -> Helpers.getClass(impl).equals(type))
246 .findFirst()
247 .orElseThrow(() -> new IllegalStateException("The method reference " + ref + " does not match any available component impl classes."));
248 ref.accept((U) componentImpl, component, bundle);
249 });
250 return this;
251 }
252
253 private void invokeMethodRefs(Cb cbType, Component comp, Bundle bundle) {
254 m_refs.computeIfPresent(cbType, (k, mrefs) -> {
255 mrefs.forEach(mref -> mref.accept(null, comp, bundle));
256 return mrefs;
257 });
258 }
259
260 private void checkHasNoMethodRefs() {
261 if (m_refs.size() > 0) {
262 throw new IllegalStateException("Can't mix method references with reflection based callbacks");
263 }
264 }
265
266 private void checkHasNoReflectionCallbacks() {
267 if (m_added != null || m_changed != null || m_removed != null) {
268 throw new IllegalStateException("Can't mix method references with reflection based callbacks");
269 }
270 }
271}