1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package io.earcam.instrumental.archive.osgi;
20
21 import static io.earcam.instrumental.module.auto.Reader.reader;
22 import static java.util.stream.Collectors.toCollection;
23
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.function.Predicate;
31 import java.util.jar.Manifest;
32
33 import org.osgi.framework.BundleActivator;
34
35 import io.earcam.instrumental.archive.AbstractAsJarBuilder;
36 import io.earcam.instrumental.archive.ArchiveRegistrar;
37 import io.earcam.instrumental.archive.ArchiveResource;
38 import io.earcam.instrumental.archive.ArchiveResourceListener;
39 import io.earcam.instrumental.archive.osgi.auto.ClasspathBundles;
40 import io.earcam.instrumental.module.auto.Reader;
41 import io.earcam.instrumental.module.osgi.BundleInfoBuilder;
42 import io.earcam.instrumental.module.osgi.ClauseParameters;
43 import io.earcam.instrumental.reflect.Types;
44
45
46
47
48
49
50
51 class DefaultAsOsgiBundle extends AbstractAsJarBuilder<AsOsgiBundle> implements AsOsgiBundle, ArchiveResourceListener {
52
53 private final BundleInfoBuilder builder = BundleInfoBuilder.bundle();
54
55 class ExportMatcher {
56 final Predicate<String> matcher;
57 final ClauseParameters parameters;
58
59
60 ExportMatcher(Predicate<String> matcher, ClauseParameters parameters)
61 {
62 this.matcher = matcher;
63 this.parameters = parameters;
64 }
65
66
67 public boolean test(ArchiveResource resource)
68 {
69 String pkg = resource.pkg();
70 if(matcher.test(pkg)) {
71 builder.exportPackages(pkg, parameters);
72 return true;
73 }
74 return false;
75 }
76 }
77
78 class AutoImporting {
79 final Map<String, Set<String>> imports = new HashMap<>();
80 final Reader reader = reader()
81 .addImportListener(imports::put)
82 .setImportedTypeReducer(Reader::typeToPackageReducer)
83 .setImportingTypeReducer(Reader::typeToPackageReducer);
84
85
86 void process(ArchiveResource resource)
87 {
88 reader.processClass(resource.bytes());
89 }
90
91
92 Set<String> imported()
93 {
94 Predicate<? super String> ownPackages = imports.keySet()::contains;
95
96 return imports.values().stream()
97 .flatMap(Set::stream)
98 .filter(ownPackages.negate())
99 .collect(toCollection(HashSet::new));
100 }
101
102 }
103
104 private final List<ExportMatcher> exportMatchers = new ArrayList<>();
105
106 private final List<PackageBundleMapper> packageBundleMappers = new ArrayList<>();
107
108 private AutoImporting autoImporting;
109
110
111 DefaultAsOsgiBundle()
112 {}
113
114
115 @Override
116 protected AsOsgiBundle self()
117 {
118 return this;
119 }
120
121
122 @Override
123 public void attach(ArchiveRegistrar core)
124 {
125 super.attach(core);
126 core.registerResourceListener(this);
127 }
128
129
130 @Override
131 public void added(ArchiveResource resource)
132 {
133 super.added(resource);
134
135 if(autoImporting != null && resource.isClass()) {
136 autoImporting.process(resource);
137 }
138
139 for(ExportMatcher matcher : exportMatchers) {
140 if(resource.isQualifiedClass()) {
141 matcher.test(resource);
142 if(matcher.test(resource)) {
143 break;
144 }
145 }
146 }
147 }
148
149
150 @Override
151 public void process(Manifest manifest)
152 {
153 super.process(manifest);
154 resolveAutoImports();
155 builder.to(manifest);
156 }
157
158
159 private void resolveAutoImports()
160 {
161 if(autoImporting == null) {
162 return;
163 }
164 Set<String> imports = autoImporting.imported();
165
166 for(PackageBundleMapper mapper : packageBundleMappers) {
167 mapper.importsFor(imports.iterator())
168 .forEach(builder::importPackages);
169 }
170
171 if(validate() && !imports.isEmpty()) {
172 throw new IllegalStateException("' unresolved imports remain: " + imports);
173 }
174 }
175
176
177 @Override
178 public AsOsgiBundle named(String symbolicName, ClauseParameters parameters)
179 {
180 builder.symbolicName(symbolicName, parameters);
181 return this;
182 }
183
184
185 @Override
186 public AsOsgiBundle withActivator(Class<?> activator)
187 {
188 if(validate()) {
189 requireActivator(activator);
190 }
191 source.with(activator);
192 return withActivator(activator.getCanonicalName());
193 }
194
195
196 @Override
197 public AsOsgiBundle withActivator(String canonicalName)
198 {
199 builder.activator(canonicalName);
200 return this;
201 }
202
203
204 private static final void requireActivator(Class<?> activator)
205 {
206 if(!Types.implementsAll(activator, BundleActivator.class)) {
207 throw new IllegalArgumentException(activator + " does not implement " + BundleActivator.class);
208 }
209 }
210
211
212 @Override
213 public AsOsgiBundle exporting(Predicate<String> exportMatcher, ClauseParameters parameters)
214 {
215 exportMatchers.add(new ExportMatcher(exportMatcher, parameters));
216 return this;
217 }
218
219
220
221
222
223
224
225
226
227
228
229 @Override
230 public AsOsgiBundle exporting(Class<?> type, ClauseParameters parameters)
231 {
232 source.with(type);
233 return exporting(type.getPackage(), parameters);
234 }
235
236
237
238
239
240
241
242
243
244
245
246 @Override
247 public AsOsgiBundle exporting(String paquet, ClauseParameters parameters)
248 {
249 builder.exportPackages(paquet, parameters);
250 return this;
251 }
252
253
254
255
256
257
258
259
260
261
262
263 @Override
264 public AsOsgiBundle importing(String paquet, ClauseParameters parameters)
265 {
266 builder.importPackages(paquet, parameters);
267 return this;
268 }
269
270
271
272
273
274
275
276
277
278 @Override
279 public AsOsgiBundle autoImporting()
280 {
281 return autoImporting(new ClasspathBundles());
282 }
283
284
285 @Override
286 public AsOsgiBundle autoImporting(List<PackageBundleMapper> mappers)
287 {
288 this.autoImporting = new AutoImporting();
289 mappers.forEach(packageBundleMappers::add);
290 return this;
291 }
292 }