Fix bug where the resolver was not correctly calculating exported packages
for resolved bundles when they were substitutable exports. (FELIX-2725)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1045205 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
index f671d89..0dddc9c 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -34,7 +34,6 @@
 import org.apache.felix.framework.capabilityset.CapabilitySet;
 import org.apache.felix.framework.capabilityset.Directive;
 import org.apache.felix.framework.capabilityset.Requirement;
-import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.RequirementImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
@@ -592,7 +591,7 @@
         }
 
         // First, add all exported packages to our package space.
-        calculateExportedPackages(module, modulePkgMap);
+        calculateExportedPackages(module, candidateMap, modulePkgMap);
         Packages modulePkgs = modulePkgMap.get(module);
 
         // Second, add all imported packages to our candidate space.
@@ -600,7 +599,7 @@
         {
             Requirement req = reqs.get(i);
             Capability cap = caps.get(i);
-            calculateExportedPackages(cap.getModule(), modulePkgMap);
+            calculateExportedPackages(cap.getModule(), candidateMap, modulePkgMap);
             mergeCandidatePackages(module, req, cap, modulePkgMap, candidateMap);
         }
 
@@ -666,7 +665,8 @@
         else if (candCap.getNamespace().equals(Capability.MODULE_NAMESPACE))
         {
 // TODO: FELIX3 - THIS NEXT LINE IS A HACK. IMPROVE HOW/WHEN WE CALCULATE EXPORTS.
-            calculateExportedPackages(candCap.getModule(), modulePkgMap);
+            calculateExportedPackages(
+                candCap.getModule(), candidateMap, modulePkgMap);
 
             // Get the candidate's package space to determine which packages
             // will be visible to the current module.
@@ -674,8 +674,6 @@
 
             // We have to merge all exported packages from the candidate,
             // since the current module requires it.
-// TODO: FELIX3 - If a module imports its exports, then imported exports should
-//       be reexported to requiring bundles.
             for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
             {
                 mergeCandidatePackage(
@@ -1152,7 +1150,9 @@
     }
 
     private static void calculateExportedPackages(
-        Module module, Map<Module, Packages> modulePkgMap)
+        Module module,
+        Map<Requirement, Set<Capability>> candidateMap,
+        Map<Module, Packages> modulePkgMap)
     {
         Packages packages = modulePkgMap.get(module);
         if (packages != null)
@@ -1161,40 +1161,58 @@
         }
         packages = new Packages(module);
 
-        List<Capability> caps = module.getCapabilities();
-
-        if (caps.size() > 0)
+        // Get all exported packages.
+        Map<String, Capability> exports =
+            new HashMap<String, Capability>(module.getCapabilities().size());
+        for (Capability cap : module.getCapabilities())
         {
-            // Grab all exported packages that are not also imported.
-            for (int i = 0; i < caps.size(); i++)
+            if (cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
             {
-// TODO: FELIX3 - Assume if a module imports the same package it
-//       exports that the import will overlap the export.
-                if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
-                    && !hasOverlappingImport(module, caps.get(i)))
+                exports.put(
+                    (String) cap.getAttribute(Capability.PACKAGE_ATTR).getValue(),
+                    cap);
+            }
+        }
+        // Remove substitutable exports that were imported.
+        // For resolved modules look at the wires, for resolving
+        // modules look in the candidate map to determine which
+        // exports are substitutable.
+        if (module.isResolved())
+        {
+            for (Wire wire : module.getWires())
+            {
+                if (wire.getRequirement().getNamespace().equals(Capability.PACKAGE_NAMESPACE))
                 {
-                    packages.m_exportedPkgs.put(
-                        (String) caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue(),
-                        new Blame(caps.get(i), null));
+                    String pkgName = (String) wire.getCapability()
+                        .getAttribute(Capability.PACKAGE_ATTR).getValue();
+                    exports.remove(pkgName);
                 }
             }
         }
-
-        modulePkgMap.put(module, packages);
-    }
-
-    private static boolean hasOverlappingImport(Module module, Capability cap)
-    {
-        List<Requirement> reqs = module.getRequirements();
-        for (int i = 0; i < reqs.size(); i++)
+        else
         {
-            if (reqs.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
-                && CapabilitySet.matches(cap, reqs.get(i).getFilter()))
+            for (Requirement req : module.getRequirements())
             {
-                return true;
+                if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+                {
+                    Set<Capability> cands = candidateMap.get(req);
+                    if ((cands != null) && !cands.isEmpty())
+                    {
+                        String pkgName = (String) cands.iterator().next()
+                            .getAttribute(Capability.PACKAGE_ATTR).getValue();
+                        exports.remove(pkgName);
+                    }
+                }
             }
         }
-        return false;
+        // Add all non-substituted exports to the module's package space.
+        for (Entry<String, Capability> entry : exports.entrySet())
+        {
+            packages.m_exportedPkgs.put(
+                entry.getKey(), new Blame(entry.getValue(), null));
+        }
+
+        modulePkgMap.put(module, packages);
     }
 
     private boolean isCompatible(