2020import java .util .Arrays ;
2121import java .util .Collection ;
2222import java .util .Collections ;
23+ import java .util .HashMap ;
2324import java .util .HashSet ;
2425import java .util .List ;
26+ import java .util .Map ;
2527import java .util .Queue ;
2628import java .util .Set ;
2729
3537import org .eclipse .pde .core .target .NameVersionDescriptor ;
3638import org .osgi .framework .Constants ;
3739import org .osgi .framework .Version ;
40+ import org .osgi .framework .wiring .BundleCapability ;
3841import org .osgi .framework .wiring .BundleRequirement ;
3942import org .osgi .framework .wiring .BundleRevision ;
4043import org .osgi .framework .wiring .BundleWire ;
4144import org .osgi .framework .wiring .BundleWiring ;
45+ import org .osgi .resource .Capability ;
46+ import org .osgi .resource .Namespace ;
4247import org .osgi .resource .Resource ;
4348
4449/**
@@ -172,12 +177,12 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
172177
173178 Set <BundleDescription > closure = new HashSet <>(bundles .size () * 4 / 3 + 1 );
174179 Queue <BundleDescription > pending = new ArrayDeque <>(bundles .size ());
180+ Map <String , List <BundleCapability >> provided = new HashMap <>();
175181
176182 // initialize with given bundles
177183 for (BundleDescription bundle : bundles ) {
178- addNewRequiredBundle (bundle , closure , pending );
184+ addNewRequiredBundle (bundle , closure , pending , provided );
179185 }
180-
181186 // perform exhaustive iterative bfs for required wires
182187 while (!pending .isEmpty ()) {
183188 BundleDescription bundle = pending .remove ();
@@ -191,14 +196,18 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
191196 // A fragment's host is already required by a wire
192197 for (BundleDescription fragment : bundle .getFragments ()) {
193198 if (includeAllFragments || !isTestWorkspaceProject (fragment )) {
194- addNewRequiredBundle (fragment , closure , pending );
199+ addNewRequiredBundle (fragment , closure , pending , provided );
195200 }
196201 }
197202 }
198203
199204 List <BundleWire > requiredWires = wiring .getRequiredWires (null );
200205 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 ();
202211 if (declaringBundle != bundle && !closure .contains (declaringBundle )) {
203212 // Requirement is declared by an attached fragment, which is
204213 // not included into the closure.
@@ -207,18 +216,42 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
207216 BundleRevision provider = wire .getCapability ().getRevision ();
208217 // Use revision of required capability to support the case if
209218 // 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 );
212221 }
213222 }
214223 }
215224 return closure ;
216225 }
217226
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+
218245 private static void addNewRequiredBundle (BundleDescription bundle , Set <BundleDescription > requiredBundles ,
219- Queue <BundleDescription > pending ) {
246+ Queue <BundleDescription > pending , Map < String , List < BundleCapability >> provided ) {
220247 if (bundle != null && bundle .isResolved () && !bundle .isRemovalPending () && requiredBundles .add (bundle )) {
221248 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+ }
222255 }
223256 }
224257
0 commit comments