20
20
import java .util .Arrays ;
21
21
import java .util .Collection ;
22
22
import java .util .Collections ;
23
+ import java .util .HashMap ;
23
24
import java .util .HashSet ;
24
25
import java .util .List ;
26
+ import java .util .Map ;
25
27
import java .util .Queue ;
26
28
import java .util .Set ;
27
29
35
37
import org .eclipse .pde .core .target .NameVersionDescriptor ;
36
38
import org .osgi .framework .Constants ;
37
39
import org .osgi .framework .Version ;
40
+ import org .osgi .framework .wiring .BundleCapability ;
38
41
import org .osgi .framework .wiring .BundleRequirement ;
39
42
import org .osgi .framework .wiring .BundleRevision ;
40
43
import org .osgi .framework .wiring .BundleWire ;
41
44
import org .osgi .framework .wiring .BundleWiring ;
45
+ import org .osgi .resource .Capability ;
46
+ import org .osgi .resource .Namespace ;
42
47
import org .osgi .resource .Resource ;
43
48
44
49
/**
@@ -172,12 +177,12 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
172
177
173
178
Set <BundleDescription > closure = new HashSet <>(bundles .size () * 4 / 3 + 1 );
174
179
Queue <BundleDescription > pending = new ArrayDeque <>(bundles .size ());
180
+ Map <String , List <BundleCapability >> provided = new HashMap <>();
175
181
176
182
// initialize with given bundles
177
183
for (BundleDescription bundle : bundles ) {
178
- addNewRequiredBundle (bundle , closure , pending );
184
+ addNewRequiredBundle (bundle , closure , pending , provided );
179
185
}
180
-
181
186
// perform exhaustive iterative bfs for required wires
182
187
while (!pending .isEmpty ()) {
183
188
BundleDescription bundle = pending .remove ();
@@ -191,14 +196,18 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
191
196
// A fragment's host is already required by a wire
192
197
for (BundleDescription fragment : bundle .getFragments ()) {
193
198
if (includeAllFragments || !isTestWorkspaceProject (fragment )) {
194
- addNewRequiredBundle (fragment , closure , pending );
199
+ addNewRequiredBundle (fragment , closure , pending , provided );
195
200
}
196
201
}
197
202
}
198
203
199
204
List <BundleWire > requiredWires = wiring .getRequiredWires (null );
200
205
for (BundleWire wire : requiredWires ) {
201
- BundleRevision declaringBundle = wire .getRequirement ().getRevision ();
206
+ BundleRequirement requirement = wire .getRequirement ();
207
+ if (isSingle (requirement ) && isAlreadyProvided (requirement , provided )) {
208
+ continue ;
209
+ }
210
+ BundleRevision declaringBundle = requirement .getRevision ();
202
211
if (declaringBundle != bundle && !closure .contains (declaringBundle )) {
203
212
// Requirement is declared by an attached fragment, which is
204
213
// not included into the closure.
@@ -207,18 +216,42 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
207
216
BundleRevision provider = wire .getCapability ().getRevision ();
208
217
// Use revision of required capability to support the case if
209
218
// fragments contribute new packages to their host's API.
210
- if (provider instanceof BundleDescription requiredBundle && (includeOptional || !isOptional (wire . getRequirement () ))) {
211
- addNewRequiredBundle (requiredBundle , closure , pending );
219
+ if (provider instanceof BundleDescription requiredBundle && (includeOptional || !isOptional (requirement ))) {
220
+ addNewRequiredBundle (requiredBundle , closure , pending , provided );
212
221
}
213
222
}
214
223
}
215
224
return closure ;
216
225
}
217
226
227
+ private static boolean isSingle (BundleRequirement requirement ) {
228
+ return Namespace .CARDINALITY_SINGLE .equals (requirement .getDirectives ()
229
+ .getOrDefault (Namespace .REQUIREMENT_CARDINALITY_DIRECTIVE , Namespace .CARDINALITY_SINGLE ));
230
+ }
231
+
232
+ protected static boolean isAlreadyProvided (BundleRequirement requirement ,
233
+ Map <String , List <BundleCapability >> provided ) {
234
+ List <BundleCapability > list = provided .get (requirement .getNamespace ());
235
+ if (list != null && !list .isEmpty ()) {
236
+ for (BundleCapability bundleCapability : list ) {
237
+ if (requirement .matches (bundleCapability )) {
238
+ return true ;
239
+ }
240
+ }
241
+ }
242
+ return false ;
243
+ }
244
+
218
245
private static void addNewRequiredBundle (BundleDescription bundle , Set <BundleDescription > requiredBundles ,
219
- Queue <BundleDescription > pending ) {
246
+ Queue <BundleDescription > pending , Map < String , List < BundleCapability >> provided ) {
220
247
if (bundle != null && bundle .isResolved () && !bundle .isRemovalPending () && requiredBundles .add (bundle )) {
221
248
pending .add (bundle );
249
+ List <Capability > capabilities = bundle .getCapabilities (null );
250
+ for (Capability capability : capabilities ) {
251
+ if (capability instanceof BundleCapability bc ) {
252
+ provided .computeIfAbsent (capability .getNamespace (), nil -> new ArrayList <>()).add (bc );
253
+ }
254
+ }
222
255
}
223
256
}
224
257
0 commit comments