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
3638import org .osgi .framework .Constants ;
3739import org .osgi .framework .Version ;
3840import org .osgi .framework .namespace .HostNamespace ;
41+ import org .osgi .framework .wiring .BundleCapability ;
3942import org .osgi .framework .wiring .BundleRequirement ;
4043import org .osgi .framework .wiring .BundleRevision ;
4144import org .osgi .framework .wiring .BundleWire ;
4245import org .osgi .framework .wiring .BundleWiring ;
46+ import org .osgi .resource .Capability ;
47+ import org .osgi .resource .Namespace ;
4348import org .osgi .resource .Resource ;
4449
4550/**
@@ -173,12 +178,12 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
173178
174179 Set <BundleDescription > closure = new HashSet <>(bundles .size () * 4 / 3 + 1 );
175180 Queue <BundleDescription > pending = new ArrayDeque <>(bundles .size ());
181+ Map <String , List <BundleCapability >> provided = new HashMap <>();
176182
177183 // initialize with given bundles
178184 for (BundleDescription bundle : bundles ) {
179- addNewRequiredBundle (bundle , closure , pending );
185+ addNewRequiredBundle (bundle , closure , pending , provided );
180186 }
181-
182187 // perform exhaustive iterative bfs for required wires
183188 while (!pending .isEmpty ()) {
184189 BundleDescription bundle = pending .remove ();
@@ -192,7 +197,7 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
192197 // A fragment's host is already required by a wire
193198 for (BundleDescription fragment : bundle .getFragments ()) {
194199 if (includeAllFragments || !isTestWorkspaceProject (fragment )) {
195- addNewRequiredBundle (fragment , closure , pending );
200+ addNewRequiredBundle (fragment , closure , pending , provided );
196201 }
197202 }
198203 }
@@ -212,7 +217,11 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
212217
213218 List <BundleWire > requiredWires = wiring .getRequiredWires (null );
214219 for (BundleWire wire : requiredWires ) {
215- BundleRevision declaringBundle = wire .getRequirement ().getRevision ();
220+ BundleRequirement requirement = wire .getRequirement ();
221+ if (isSingle (requirement ) && isAlreadyProvided (requirement , provided )) {
222+ continue ;
223+ }
224+ BundleRevision declaringBundle = requirement .getRevision ();
216225 if (declaringBundle != bundle && !closure .contains (declaringBundle )) {
217226 // Requirement is declared by an attached fragment, which is
218227 // not included into the closure.
@@ -221,18 +230,42 @@ public static Set<BundleDescription> findRequirementsClosure(Collection<BundleDe
221230 BundleRevision provider = wire .getCapability ().getRevision ();
222231 // Use revision of required capability to support the case if
223232 // fragments contribute new packages to their host's API.
224- if (provider instanceof BundleDescription requiredBundle && (includeOptional || !isOptional (wire . getRequirement () ))) {
225- addNewRequiredBundle (requiredBundle , closure , pending );
233+ if (provider instanceof BundleDescription requiredBundle && (includeOptional || !isOptional (requirement ))) {
234+ addNewRequiredBundle (requiredBundle , closure , pending , provided );
226235 }
227236 }
228237 }
229238 return closure ;
230239 }
231240
241+ private static boolean isSingle (BundleRequirement requirement ) {
242+ return Namespace .CARDINALITY_SINGLE .equals (requirement .getDirectives ()
243+ .getOrDefault (Namespace .REQUIREMENT_CARDINALITY_DIRECTIVE , Namespace .CARDINALITY_SINGLE ));
244+ }
245+
246+ protected static boolean isAlreadyProvided (BundleRequirement requirement ,
247+ Map <String , List <BundleCapability >> provided ) {
248+ List <BundleCapability > list = provided .get (requirement .getNamespace ());
249+ if (list != null && !list .isEmpty ()) {
250+ for (BundleCapability bundleCapability : list ) {
251+ if (requirement .matches (bundleCapability )) {
252+ return true ;
253+ }
254+ }
255+ }
256+ return false ;
257+ }
258+
232259 private static void addNewRequiredBundle (BundleDescription bundle , Set <BundleDescription > requiredBundles ,
233- Queue <BundleDescription > pending ) {
260+ Queue <BundleDescription > pending , Map < String , List < BundleCapability >> provided ) {
234261 if (bundle != null && bundle .isResolved () && !bundle .isRemovalPending () && requiredBundles .add (bundle )) {
235262 pending .add (bundle );
263+ List <Capability > capabilities = bundle .getCapabilities (null );
264+ for (Capability capability : capabilities ) {
265+ if (capability instanceof BundleCapability bc ) {
266+ provided .computeIfAbsent (capability .getNamespace (), nil -> new ArrayList <>()).add (bc );
267+ }
268+ }
236269 }
237270 }
238271
0 commit comments