Rewrite of the new prototype resolver's core data structure and algorithm.
The code still needs a bit of cleaning, but this captures the nearly fully
working algorithm for safety before tinkering with it. (FELIX-2035,
FELIX-2036, FELIX-2037)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@935394 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 982ce59..590cc56 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -3043,7 +3043,7 @@
         List<Directive> dirs = new ArrayList<Directive>(0);
         List<Attribute> attrs = new ArrayList<Attribute>(1);
         attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
-        Requirement req = new RequirementImpl(Capability.PACKAGE_NAMESPACE, dirs, attrs);
+        Requirement req = new RequirementImpl(null, Capability.PACKAGE_NAMESPACE, dirs, attrs);
         Set<Capability> exports = m_resolverState.getCandidates(null, req, false);
 
         // We only want resolved capabilities.
@@ -3184,7 +3184,7 @@
                         List<Attribute> attrs = new ArrayList<Attribute>(1);
                         attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
                         Requirement req =
-                            new RequirementImpl(Capability.PACKAGE_NAMESPACE, dirs, attrs);
+                            new RequirementImpl(null, Capability.PACKAGE_NAMESPACE, dirs, attrs);
                         Set<Capability> exports = m_resolverState.getCandidates(null, req, false);
                         // We only want resolved capabilities.
                         for (Iterator<Capability> it = exports.iterator(); it.hasNext(); )
@@ -3356,7 +3356,7 @@
                 throw new BundleException(
                     "Unresolved constraint in bundle " + b + ": "
                     + ((ex.getRequirement() == null)
-                        ? ex.getMessage() : ex.getRequirement().toString()));
+                        ? ex.getMessage() : ex.getMessage() + " - " + ex.getRequirement().toString()));
             }
             else
             {
diff --git a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
index e20a1ce..06c446c 100644
--- a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
@@ -2085,7 +2085,7 @@
             List<Attribute> attrs = new ArrayList(1);
             attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
             Requirement req = new RequirementImpl(
-            Capability.PACKAGE_NAMESPACE, dirs, attrs);
+                module, Capability.PACKAGE_NAMESPACE, dirs, attrs);
             Set<Capability> exporters = resolver.getCandidates(module, req, false);
 
             Wire wire = null;
@@ -2124,7 +2124,7 @@
         List<Attribute> attrs = new ArrayList(1);
         attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
         Requirement req = new RequirementImpl(
-            Capability.PACKAGE_NAMESPACE, dirs, attrs);
+            module, Capability.PACKAGE_NAMESPACE, dirs, attrs);
         Set<Capability> exports = resolver.getCandidates(module, req, false);
         if (exports.size() > 0)
         {
@@ -2244,6 +2244,11 @@
             return m_fragment;
         }
 
+        public Module getModule()
+        {
+            return m_req.getModule();
+        }
+
         public String getNamespace()
         {
             return m_req.getNamespace();
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java
index e6e789d..7934280 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/Requirement.java
@@ -19,9 +19,11 @@
 package org.apache.felix.framework.capabilityset;
 
 import java.util.List;
+import org.apache.felix.framework.resolver.Module;
 
 public interface Requirement
 {
+    Module getModule();
     String getNamespace();
     SimpleFilter getFilter();
     boolean isOptional();
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 751a96b..43546c5 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
@@ -38,9 +38,6 @@
 import org.apache.felix.framework.util.manifestparser.RequirementImpl;
 import org.osgi.framework.Constants;
 
-// 1. Treat hard pkg constraints separately from implied package constraints
-// 2. Map pkg constraints to a set of capabilities, not a single capability.
-// 3. Uses constraints cannot conflict with other uses constraints, only with hard constraints.
 public class ResolverImpl implements Resolver
 {
     private final Logger m_logger;
@@ -60,7 +57,13 @@
         m_isInvokeCount = (v == null) ? false : Boolean.valueOf(v);
     }
 
-    private final List<Map<Requirement, Set<Capability>>> m_candidatePermutations =
+    // Holds candidate permutations based on permutating "uses" chains.
+    // These permutations are given higher priority.
+    private final List<Map<Requirement, Set<Capability>>> m_usesPermutations =
+        new ArrayList<Map<Requirement, Set<Capability>>>();
+    // Holds candidate permutations based on permutating requirement candidates.
+    // These permutations represent backtracking on previous decisions.
+    private final List<Map<Requirement, Set<Capability>>> m_importPermutations =
         new ArrayList<Map<Requirement, Set<Capability>>>();
 
     public Map<Module, List<Wire>> resolve(ResolverState state, Module module)
@@ -81,32 +84,42 @@
 
         if (!module.isResolved())
         {
-            m_candidatePermutations.clear();
+            m_usesPermutations.clear();
+            m_importPermutations.clear();
 
 //System.out.println("+++ RESOLVING " + module);
             Map<Requirement, Set<Capability>> candidateMap =
                 new HashMap<Requirement, Set<Capability>>();
 
             populateCandidates(state, module, candidateMap, new HashMap<Module, Object>());
-            m_candidatePermutations.add(candidateMap);
+            m_usesPermutations.add(candidateMap);
 
             ResolveException rethrow = null;
 
+            Map<Capability, Set<Requirement>> capDepSet = new HashMap();
+
             do
             {
                 rethrow = null;
 
-                candidateMap = m_candidatePermutations.remove(0);
+                modulePkgMap.clear();
+                capDepSet.clear();
+
+                candidateMap = (m_usesPermutations.size() > 0)
+                    ? m_usesPermutations.remove(0)
+                    : m_importPermutations.remove(0);
 //dumpCandidateMap(state, candidateMap);
 
+                calculatePackageSpaces(
+                    module, candidateMap, modulePkgMap, capDepSet, new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpModulePkgMap(modulePkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
                 try
                 {
-                    findConsistentCandidates(
-                        module,
-                        new ArrayList(),
-                        candidateMap,
-                        modulePkgMap,
-                        new HashMap<Module, Object>());
+                    checkPackageSpaceConsistency(
+                        module, candidateMap, modulePkgMap, capDepSet, new HashMap());
                 }
                 catch (ResolveException ex)
                 {
@@ -114,13 +127,13 @@
                     System.out.println("RE: " + ex);
                 }
             }
-            while ((rethrow != null) && (m_candidatePermutations.size() > 0));
+            while ((rethrow != null)
+                && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
 
             if (rethrow != null)
             {
                 throw rethrow;
             }
-//dumpModulePkgMap(modulePkgMap);
 
             wireMap =
                 populateWireMap(module, modulePkgMap, wireMap,
@@ -160,31 +173,37 @@
             getDynamicImportCandidates(state, module, pkgName);
         if (candidateMap != null)
         {
-            m_candidatePermutations.clear();
+            m_usesPermutations.clear();
 
             Map<Module, List<Wire>> wireMap = new HashMap();
             Map<Module, Packages> modulePkgMap = new HashMap();
 
 //System.out.println("+++ DYNAMICALLY RESOLVING " + module + " - " + pkgName);
             populateDynamicCandidates(state, module, candidateMap);
-            m_candidatePermutations.add(candidateMap);
+            m_usesPermutations.add(candidateMap);
 
             ResolveException rethrow = null;
 
+            Map<Capability, Set<Requirement>> capDepSet = new HashMap();
+
             do
             {
                 rethrow = null;
 
-                candidateMap = m_candidatePermutations.remove(0);
-//dumpCandidateMap(state, candidateMap);
+                modulePkgMap.clear();
+                capDepSet.clear();
+
+                candidateMap = (m_usesPermutations.size() > 0)
+                    ? m_usesPermutations.remove(0)
+                    : m_importPermutations.remove(0);
+
+                calculateDynamicPackageSpaces(
+                    module, candidateMap, modulePkgMap, capDepSet);
 
                 try
                 {
-                    findConsistentDynamicCandidate(
-                        module,
-                        new ArrayList(),
-                        candidateMap,
-                        modulePkgMap);
+                    checkPackageSpaceConsistency(
+                        module, candidateMap, modulePkgMap, capDepSet, new HashMap());
                 }
                 catch (ResolveException ex)
                 {
@@ -192,7 +211,8 @@
                     System.out.println("RE: " + ex);
                 }
             }
-            while ((rethrow != null) && (m_candidatePermutations.size() > 0));
+            while ((rethrow != null)
+                && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
 
             if (rethrow != null)
             {
@@ -259,7 +279,7 @@
         List<Attribute> attrs = new ArrayList(1);
         attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
         Requirement req = new RequirementImpl(
-            Capability.PACKAGE_NAMESPACE, dirs, attrs);
+            module, Capability.PACKAGE_NAMESPACE, dirs, attrs);
         Set<Capability> candidates = state.getCandidates(module, req, false);
         List<Requirement> dynamics = module.getDynamicRequirements();
 
@@ -298,7 +318,6 @@
             candidates.clear();
         }
 
-
         if (candidates.size() > 0)
         {
             Map<Requirement, Set<Capability>> candidateMap = new HashMap();
@@ -322,7 +341,7 @@
                 Set<Capability> candidates = candidateMap.get(req);
                 if ((candidates != null) && (candidates.size() > 0))
                 {
-                    System.out.println("    " + req + ": " + candidates);
+                        System.out.println("    " + req + ": " + candidates);
                 }
             }
             for (Requirement req : module.getDynamicRequirements())
@@ -578,18 +597,19 @@
         // Add existing wires as candidates.
         for (Wire wire : module.getWires())
         {
+// TODO: FELIX3 - HOW ARE CAPABILITIES BEING SORTED NOW?
             Set<Capability> cs = new TreeSet();
             cs.add(wire.getCapability());
             candidateMap.put(wire.getRequirement(), cs);
         }
     }
 
-// TODO: FELIX3 - Modify to not be recursive.
-    private void findConsistentCandidates(
-        Module module, List<Requirement> incomingReqs,
+    private void calculatePackageSpaces(
+        Module module,
         Map<Requirement, Set<Capability>> candidateMap,
         Map<Module, Packages> modulePkgMap,
-        Map<Module, Object> resultCache)
+        Map<Capability, Set<Requirement>> capDepSet,
+        Set<Module> cycle)
     {
         if (m_isInvokeCount)
         {
@@ -598,83 +618,88 @@
             count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
             m_invokeCounts.put(methodName, count);
         }
-
-        // Determine if we've already checked candidate consistency for this
-        // module. The result cache will have one of two values:
-        //   1. Boolean.TRUE if candidate consistency has been checked.
-        //   2. An array containing the cycle count and a list of wires for an
-        //      already resolved (and consistent) module or a list of remaining
-        //      requirments whose candidates still need to be checked for an
-        //      unresolved module.
-        // For case 1, return immediately. For case 2, this means we are in a
-        // cycle so we should just continue checking consistency where we left
-        // off.
-
-        Integer cycleCount = null;
-
-        Object o = resultCache.get(module);
-
-        if (o instanceof Boolean)
+        if (cycle.contains(module))
         {
             return;
         }
-        else if (o == null)
-        {
-            List list;
-            if (module.isResolved())
-            {
-                list = new ArrayList(module.getWires());
-            }
-            else
-            {
-                list = new ArrayList(module.getRequirements());
-            }
-            resultCache.put(module, o = new Object[] { cycleCount = new Integer(0), list });
-            calculateExportedPackages(module, incomingReqs, modulePkgMap);
-        }
-        else
-        {
-            // Increment and get the cycle count.
-            cycleCount = (Integer)
-                (((Object[]) o)[0]
-                    = new Integer(((Integer) ((Object[]) o)[0]).intValue() + 1));
-        }
-
-//System.out.println("+++ RESOLVING " + module);
+        cycle.add(module);
 
         if (module.isResolved())
         {
-            List<Wire> wires = (List<Wire>) ((Object[]) o)[1];
+            // First, add all exported packages to our package space.
+            calculateExportedPackages(module, modulePkgMap);
+            Packages modulePkgs = modulePkgMap.get(module);
 
+            // Second, add all imported packages to our candidate space.
+            List<Wire> wires = new ArrayList(module.getWires());
+            List<Capability> selected = new ArrayList();
             while (wires.size() > 0)
             {
                 Wire wire = wires.remove(0);
-
-                // Try to resolve the candidate.
-                findConsistentCandidates(
-                    wire.getCapability().getModule(),
-                    incomingReqs,
-                    candidateMap,
-                    modulePkgMap,
-                    resultCache);
-
-                // If we are here, the candidate was consistent. Try to
-                // merge the candidate into the target module's packages.
-                mergeCandidatePackages(
+                selected.add(wire.getCapability());
+                calculateExportedPackages(wire.getCapability().getModule(), modulePkgMap);
+                mergeCandidatePackagesNoUses(
                     module,
-                    incomingReqs,
+                    wire.getRequirement(),
                     wire.getCapability(),
                     modulePkgMap,
                     candidateMap);
+                addCapabilityDependency(wire.getCapability(), wire.getRequirement(), capDepSet);
+            }
+
+            // Third, ask our candidates to calculate their package space.
+            while (selected.size() > 0)
+            {
+                Capability candidate = selected.remove(0);
+                calculatePackageSpaces(candidate.getModule(), candidateMap, modulePkgMap, capDepSet, cycle);
+            }
+
+            // Fourth, add all of the uses constraints implied by our imported
+            // and required packages.
+            for (Entry<String, Blame> entry : modulePkgs.m_importedPkgs.entrySet())
+            {
+                List<Requirement> blameReqs = new ArrayList();
+                blameReqs.add(entry.getValue().m_reqs.get(0));
+
+                mergeUses(
+                    module,
+                    modulePkgs,
+                    entry.getValue().m_cap,
+                    blameReqs,
+                    modulePkgMap,
+                    candidateMap,
+                    new HashMap<String, List<Module>>());
+            }
+            for (Entry<String, List<Blame>> entry : modulePkgs.m_requiredPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
+                {
+                    List<Requirement> blameReqs = new ArrayList();
+                    blameReqs.add(blame.m_reqs.get(0));
+
+                    mergeUses(
+                        module,
+                        modulePkgs,
+                        blame.m_cap,
+                        blameReqs,
+                        modulePkgMap,
+                        candidateMap,
+                        new HashMap<String, List<Module>>());
+                }
             }
         }
         else
         {
-            List<Requirement> reqs = (List<Requirement>) ((Object[]) o)[1];
+            // First, add all exported packages to our package space.
+            calculateExportedPackages(module, modulePkgMap);
+            Packages modulePkgs = modulePkgMap.get(module);
 
-            while (reqs.size() > 0)
+            // Second, add all imported packages to our candidate space.
+            List<Requirement> list = new ArrayList(module.getRequirements());
+            List<Capability> selected = new ArrayList();
+            while (list.size() > 0)
             {
-                Requirement req = reqs.remove(0);
+                Requirement req = list.remove(0);
 
                 // Get the candidates for the current requirement.
                 Set<Capability> candCaps = candidateMap.get(req);
@@ -684,68 +709,208 @@
                     continue;
                 }
 
-                List<Requirement> outgoingReqs = new ArrayList<Requirement>(incomingReqs);
-                outgoingReqs.add(req);
+                calculateExportedPackages(module, modulePkgMap);
+                Capability candidate = candCaps.iterator().next();
+                selected.add(candidate);
+                calculateExportedPackages(candidate.getModule(), modulePkgMap);
+                mergeCandidatePackagesNoUses(module, req, candidate, modulePkgMap, candidateMap);
+                addCapabilityDependency(candidate, req, capDepSet);
+            }
 
-                for (Iterator<Capability> it = candCaps.iterator(); it.hasNext(); )
+            // Third, ask our candidates to calculate their package space.
+            while (selected.size() > 0)
+            {
+                Capability candidate = selected.remove(0);
+                calculatePackageSpaces(candidate.getModule(), candidateMap, modulePkgMap, capDepSet, cycle);
+            }
+
+            // Fourth, add all of the uses constraints implied by our imported
+            // and required packages.
+// TODO: FELIX3 - DUPLICATES CODE ABOVE FOR RESOLVED MODULES.
+            for (Entry<String, Blame> entry : modulePkgs.m_importedPkgs.entrySet())
+            {
+                List<Requirement> blameReqs = new ArrayList();
+                blameReqs.add(entry.getValue().m_reqs.get(0));
+
+                mergeUses(
+                    module,
+                    modulePkgs,
+                    entry.getValue().m_cap,
+                    blameReqs,
+                    modulePkgMap,
+                    candidateMap,
+                    new HashMap<String, List<Module>>());
+            }
+            for (Entry<String, List<Blame>> entry : modulePkgs.m_requiredPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
                 {
-                    Capability candCap = it.next();
-//System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
-                    try
-                    {
-                        // Try to resolve the candidate.
-                        findConsistentCandidates(
-                            candCap.getModule(),
-                            outgoingReqs,
-                            candidateMap,
-                            modulePkgMap,
-                            resultCache);
+                    List<Requirement> blameReqs = new ArrayList();
+                    blameReqs.add(blame.m_reqs.get(0));
 
-                        // If we are here, the candidate was consistent. Try to
-                        // merge the candidate into the target module's packages.
-                        mergeCandidatePackages(
-                            module,
-                            outgoingReqs,
-                            candCap,
+                    mergeUses(
+                        module,
+                        modulePkgs,
+                        blame.m_cap,
+                        blameReqs,
+                        modulePkgMap,
+                        candidateMap,
+                        new HashMap<String, List<Module>>());
+                }
+            }
+        }
+    }
+
+// TODO: FELIX3 - This code duplicates a lot of calculatePackageSpaces()
+    private void calculateDynamicPackageSpaces(
+        Module module,
+        Map<Requirement, Set<Capability>> candidateMap,
+        Map<Module, Packages> modulePkgMap,
+        Map<Capability, Set<Requirement>> capDepSet)
+    {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
+        // First, add all exported packages to our package space.
+        calculateExportedPackages(module, modulePkgMap);
+        Packages modulePkgs = modulePkgMap.get(module);
+
+        // Second, add all imported packages to our candidate space.
+        List<Requirement> reqs = new ArrayList(module.getRequirements());
+        reqs.addAll(module.getDynamicRequirements());
+        List<Capability> selected = new ArrayList();
+        while (reqs.size() > 0)
+        {
+            Requirement req = reqs.remove(0);
+
+            // Get the candidates for the current requirement.
+            Set<Capability> candCaps = candidateMap.get(req);
+            // Optional requirements may not have any candidates.
+            if (candCaps == null)
+            {
+                continue;
+            }
+
+            calculateExportedPackages(module, modulePkgMap);
+            Capability candidate = candCaps.iterator().next();
+            selected.add(candidate);
+            calculateExportedPackages(candidate.getModule(), modulePkgMap);
+            mergeCandidatePackagesNoUses(module, req, candidate, modulePkgMap, candidateMap);
+            addCapabilityDependency(candidate, req, capDepSet);
+        }
+
+        // Third, ask our candidates to calculate their package space.
+        while (selected.size() > 0)
+        {
+            Capability candidate = selected.remove(0);
+            calculatePackageSpaces(
+                candidate.getModule(), candidateMap,
+                modulePkgMap, capDepSet, new HashSet());
+        }
+
+        // Fourth, add all of the uses constraints implied by our imported
+        // and required packages.
+        for (Entry<String, Blame> entry : modulePkgs.m_importedPkgs.entrySet())
+        {
+            List<Requirement> blameReqs = new ArrayList();
+            blameReqs.add(entry.getValue().m_reqs.get(0));
+
+            mergeUses(
+                module,
+                modulePkgs,
+                entry.getValue().m_cap,
+                blameReqs,
+                modulePkgMap,
+                candidateMap,
+                new HashMap<String, List<Module>>());
+        }
+        for (Entry<String, List<Blame>> entry : modulePkgs.m_requiredPkgs.entrySet())
+        {
+            for (Blame blame : entry.getValue())
+            {
+                List<Requirement> blameReqs = new ArrayList();
+                blameReqs.add(blame.m_reqs.get(0));
+
+                mergeUses(
+                    module,
+                    modulePkgs,
+                    blame.m_cap,
+                    blameReqs,
+                    modulePkgMap,
+                    candidateMap,
+                    new HashMap<String, List<Module>>());
+            }
+        }
+    }
+
+    private void mergeCandidatePackagesNoUses(
+        Module current, Requirement currentReq, Capability candCap,
+        Map<Module, Packages> modulePkgMap, Map<Requirement, Set<Capability>> candidateMap)
+    {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
+        if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+        {
+            mergeCandidatePackageNoUses(
+                current, false, currentReq, candCap, modulePkgMap);
+        }
+        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);
+
+            // Get the candidate's package space to determine which packages
+            // will be visible to the current module.
+            Packages candPkgs = modulePkgMap.get(candCap.getModule());
+
+            // We have to merge all exported packages from the candidate,
+            // since the current module requires it.
+            for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
+            {
+                mergeCandidatePackageNoUses(
+                    current,
+                    true,
+                    currentReq,
+                    entry.getValue().m_cap,
+                    modulePkgMap);
+            }
+
+            // If the candidate requires any other bundles with reexport visibility,
+            // then we also need to merge their packages too.
+            for (Requirement req : candCap.getModule().getRequirements())
+            {
+                if (req.getNamespace().equals(Capability.MODULE_NAMESPACE))
+                {
+                    Directive dir = req.getDirective(Constants.VISIBILITY_DIRECTIVE);
+                    if ((dir != null) && dir.getValue().equals(Constants.VISIBILITY_REEXPORT)
+                        && (candidateMap.get(req) != null))
+                    {
+                        mergeCandidatePackagesNoUses(
+                            current,
+                            currentReq,
+                            candidateMap.get(req).iterator().next(),
                             modulePkgMap,
                             candidateMap);
-
-                        // If we are here, we merged the candidate successfully,
-                        // so we can continue with the next requirement
-                        break;
-                    }
-                    catch (ResolveException ex)
-                    {
-//System.out.println("RE: " + ex);
-//ex.printStackTrace();
-                        // Remove the current candidate since it failed to resolve.
-                        it.remove();
-                        if (!it.hasNext() && !req.isOptional())
-                        {
-                            throw new ResolveException("Unresolved constraint "
-                                + req + " in " + module, module, req);
-                        }
                     }
                 }
             }
         }
-
-        // If we are exiting from a cycle then decrement
-        // cycle counter, otherwise record the result.
-        if (cycleCount.intValue() > 0)
-        {
-            ((Object[]) o)[0] = new Integer(cycleCount.intValue() - 1);
-        }
-        else if (cycleCount.intValue() == 0)
-        {
-            // Record that the module was successfully populated.
-            resultCache.put(module, Boolean.TRUE);
-        }
     }
 
-    private void findConsistentDynamicCandidate(
-        Module module, List<Requirement> incomingReqs,
-        Map<Requirement, Set<Capability>> candidateMap,
+    private void mergeCandidatePackageNoUses(
+        Module current, boolean requires,
+        Requirement currentReq, Capability candCap,
         Map<Module, Packages> modulePkgMap)
     {
         if (m_isInvokeCount)
@@ -756,69 +921,68 @@
             m_invokeCounts.put(methodName, count);
         }
 
-//System.out.println("+++ RESOLVING " + module);
-        calculateExportedPackages(module, incomingReqs, modulePkgMap);
-
-        List<Requirement> reqs = new ArrayList(module.getRequirements());
-        reqs.addAll(module.getDynamicRequirements());
-        for (Requirement req : reqs)
+// TODO: FELIX3 - Check for merging where module imports from itself,
+//       then it should be listed as an export for requiring bundles.
+        if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
         {
-            // Get the candidates for the current requirement.
-            Set<Capability> candCaps = candidateMap.get(req);
-            // Optional requirements may not have any candidates.
-            if (candCaps == null)
+//System.out.println("+++ MERGING " + candBlame.m_cap + " INTO " + current);
+            String pkgName = (String)
+                candCap.getAttribute(Capability.PACKAGE_ATTR).getValue();
+
+            // Since this capability represents a package, it will become
+            // a hard constraint on the module's package space, so we need
+            // to make sure it doesn't conflict with any other hard constraints
+            // or any other uses constraints.
+
+            List blameReqs = new ArrayList();
+            blameReqs.add(currentReq);
+
+            //
+            // First, check to see if the capability conflicts with
+            // any existing hard constraints.
+            //
+
+            Packages currentPkgs = modulePkgMap.get(current);
+            List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(pkgName);
+
+            if (requires)
             {
-                continue;
+                if (currentRequiredBlames == null)
+                {
+                    currentRequiredBlames = new ArrayList<Blame>();
+                    currentPkgs.m_requiredPkgs.put(pkgName, currentRequiredBlames);
+                }
+                currentRequiredBlames.add(new Blame(candCap, blameReqs));
+            }
+            else
+            {
+// TODO: FELIX3 - We might need to make this a list, since fragments can add duplicates.
+                currentPkgs.m_importedPkgs.put(pkgName, new Blame(candCap, blameReqs));
             }
 
-            List<Requirement> outgoingReqs = new ArrayList<Requirement>(incomingReqs);
-            outgoingReqs.add(req);
-
-            for (Iterator<Capability> it = candCaps.iterator(); it.hasNext(); )
-            {
-                Capability candCap = it.next();
-//System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
-                try
-                {
-                    // Try to resolve the candidate.
-                    findConsistentCandidates(
-                        candCap.getModule(),
-                        outgoingReqs,
-                        candidateMap,
-                        modulePkgMap,
-                        new HashMap());
-
-                    // If we are here, the candidate was consistent. Try to
-                    // merge the candidate into the target module's packages.
-                    mergeCandidatePackages(
-                        module,
-                        outgoingReqs,
-                        candCap,
-                        modulePkgMap,
-                        candidateMap);
-
-                    // If we are here, we merged the candidate successfully,
-                    // so we can continue with the next requirement
-                    break;
-                }
-                catch (ResolveException ex)
-                {
-System.out.println("RE: " + ex);
-ex.printStackTrace();
-                    // Remove the current candidate since it failed to resolve.
-                    it.remove();
-                    if (!it.hasNext() && !req.isOptional())
-                    {
-                        throw new ResolveException("Unresolved constraint "
-                            + req + " in " + module, module, req);
-                    }
-                }
-            }
+//dumpModulePkgs(current, currentPkgs);
         }
     }
 
-    private static void calculateExportedPackages(
-        Module module, List<Requirement> incomingReqs, Map<Module, Packages> modulePkgMap)
+    private static void addCapabilityDependency(
+        Capability cap, Requirement req, Map<Capability, Set<Requirement>> capDepSet)
+    {
+        Set<Requirement> reqs = capDepSet.get(cap);
+        if (reqs == null)
+        {
+            reqs = new HashSet();
+            capDepSet.put(cap, reqs);
+        }
+        reqs.add(req);
+    }
+
+// TODO: FELIX3 - We end up with duplicates in uses constraints,
+//       see scenario 2 for an example.
+    private static void mergeUses(
+        Module current, Packages currentPkgs,
+        Capability mergeCap, List<Requirement> blameReqs, Map<Module, Packages> modulePkgMap,
+        Map<Requirement, Set<Capability>> candidateMap,
+        Map<String, List<Module>> cycleMap)
     {
         if (m_isInvokeCount)
         {
@@ -828,7 +992,328 @@
             m_invokeCounts.put(methodName, count);
         }
 
-        Packages packages = new Packages();
+        if (!mergeCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+        {
+            return;
+        }
+        // If the candidate module is the same as the current module,
+        // then we don't need to verify and merge the uses constraints
+        // since this will happen as we build up the package space.
+        else if (current == mergeCap.getModule())
+        {
+            return;
+        }
+
+        // Check for cycles.
+        String pkgName = (String)
+            mergeCap.getAttribute(Capability.PACKAGE_ATTR).getValue();
+        List<Module> list = cycleMap.get(pkgName);
+        if ((list != null) && list.contains(current))
+        {
+            return;
+        }
+        list = (list == null) ? new ArrayList<Module>() : list;
+        list.add(current);
+        cycleMap.put(pkgName, list);
+
+//System.out.println("+++ MERGING USES " + current + " FOR " + candBlame);
+        for (Capability candSourceCap : getPackageSources(
+            mergeCap, modulePkgMap, new ArrayList<Capability>(), new HashSet<Capability>()))
+        {
+            for (String usedPkgName : candSourceCap.getUses())
+            {
+                Packages candSourcePkgs = modulePkgMap.get(candSourceCap.getModule());
+                Blame candSourceBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
+                candSourceBlame = (candSourceBlame != null)
+                    ? candSourceBlame
+                    : candSourcePkgs.m_importedPkgs.get(usedPkgName);
+
+                if (candSourceBlame == null)
+                {
+                    continue;
+                }
+
+                List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
+                if (usedCaps == null)
+                {
+                    usedCaps = new ArrayList<Blame>();
+                    currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
+                }
+                if (candSourceBlame.m_reqs != null)
+                {
+                    List<Requirement> blameReqs2 = new ArrayList(blameReqs);
+                    blameReqs2.add(candSourceBlame.m_reqs.get(candSourceBlame.m_reqs.size() - 1));
+                    usedCaps.add(new Blame(candSourceBlame.m_cap, blameReqs2));
+                    mergeUses(current, currentPkgs, candSourceBlame.m_cap, blameReqs2,
+                        modulePkgMap, candidateMap, cycleMap);
+                }
+                else
+                {
+                    usedCaps.add(new Blame(candSourceBlame.m_cap, blameReqs));
+                    mergeUses(current, currentPkgs, candSourceBlame.m_cap, blameReqs,
+                        modulePkgMap, candidateMap, cycleMap);
+                }
+            }
+        }
+    }
+
+    private void checkPackageSpaceConsistency(
+        Module module, Map<Requirement, Set<Capability>> candidateMap,
+        Map<Module, Packages> modulePkgMap, Map<Capability, Set<Requirement>> capDepSet,
+        Map<Module, Object> resultCache)
+    {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
+        if (module.isResolved())
+        {
+            return;
+        }
+        else if (resultCache.containsKey(module))
+        {
+            return;
+        }
+
+//System.out.println("+++ checkPackageSpaceConsistency(" + module + ")");
+        Packages pkgs = modulePkgMap.get(module);
+
+        ResolveException rethrow = null;
+        Map<Requirement, Set<Capability>> copyConflict = null;
+        Set<Requirement> mutated = null;
+
+        Set<Module> checkModules = new HashSet();
+
+        for (Entry<String, Blame> entry : pkgs.m_exportedPkgs.entrySet())
+        {
+            String pkgName = entry.getKey();
+            if (!pkgs.m_usedPkgs.containsKey(pkgName))
+            {
+                continue;
+            }
+            for (Blame blame : pkgs.m_usedPkgs.get(pkgName))
+            {
+                if (!isCompatible(entry.getValue().m_cap, blame.m_cap, modulePkgMap))
+                {
+                    copyConflict = (copyConflict != null)
+                        ? copyConflict
+                        : copyCandidateMap(candidateMap);
+                    rethrow = (rethrow != null)
+                        ? rethrow
+                        : new ResolveException(
+                            "3Constraint violation for package '"
+                            + pkgName + "' when resolving module "
+                            + module + " between existing exported constraint "
+                            + entry.getValue() + " and uses constraint "
+                            + blame, null, null);
+                    mutated = (mutated != null)
+                        ? mutated
+                        : new HashSet();
+// TODO: FELIX3 - I think we need to walk up this chain too.
+// TODO: FELIX3 - What about uses and import permutations?
+                    Requirement req = blame.m_reqs.get(blame.m_reqs.size() - 1);
+                    if (!mutated.contains(req))
+                    {
+                        mutated.add(req);
+                        Set<Capability> caps = copyConflict.get(req);
+                        Iterator it = caps.iterator();
+                        it.next();
+                        it.remove();
+                        if (caps.size() == 0)
+                        {
+                            removeInvalidateCandidate(req.getModule(), capDepSet, copyConflict);
+                        }
+                    }
+                }
+            }
+            if (rethrow != null)
+            {
+                m_usesPermutations.add(copyConflict);
+                throw rethrow;
+            }
+        }
+
+        // Check if there are any conflicts with imported packages.
+        for (Entry<String, Blame> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            if (!module.equals(entry.getValue().m_cap.getModule()))
+            {
+                checkModules.add(entry.getValue().m_cap.getModule());
+            }
+
+            String pkgName = entry.getKey();
+            if (!pkgs.m_usedPkgs.containsKey(pkgName))
+            {
+                continue;
+            }
+            for (Blame blame : pkgs.m_usedPkgs.get(pkgName))
+            {
+                if (!isCompatible(entry.getValue().m_cap, blame.m_cap, modulePkgMap))
+                {
+                    // Create a candidate permutation that eliminates any candidates
+                    // that conflict with existing selected candidates.
+                    copyConflict = (copyConflict != null)
+                        ? copyConflict
+                        : copyCandidateMap(candidateMap);
+                    rethrow = (rethrow != null)
+                        ? rethrow
+                        : new ResolveException(
+                            "4Constraint violation for package '"
+                            + pkgName + "' when resolving module "
+                            + module + " between existing imported constraint "
+                            + entry.getValue() + " and uses constraint "
+                            + blame, null, null);
+
+                    mutated = (mutated != null)
+                        ? mutated
+                        : new HashSet();
+
+                    for (int reqIdx = blame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                    {
+                        Requirement req = blame.m_reqs.get(reqIdx);
+
+                        // If we've already permutated this requirement in another
+                        // uses constraint, don't permutate it again just continue
+                        // with the next uses constraint.
+                        if (mutated.contains(req))
+                        {
+                            break;
+                        }
+
+                        // See if we can permutate the candidates for blamed
+                        // requirement; there may be no candidates if the module
+                        // associated with the requirement is already resolved.
+                        Set<Capability> candidates = copyConflict.get(req);
+                        if ((candidates != null) && (candidates.size() > 1))
+                        {
+                            mutated.add(req);
+                            Iterator it = candidates.iterator();
+                            it.next();
+                            it.remove();
+                            // Continue with the next uses constraint.
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (rethrow != null)
+            {
+                // If we couldn't permutate the uses constraints,
+                // then try to permutate the import.
+// TODO: FELIX3 - Maybe we will push too many permutations this way, since
+//       we will push one on each level as we move down.
+                Requirement req = entry.getValue().m_reqs.get(0);
+                if (!mutated.contains(req))
+                {
+                    Set<Capability> candidates = candidateMap.get(req);
+                    if (candidates.size() > 1)
+                    {
+                        Map<Requirement, Set<Capability>> importPerm =
+                            copyCandidateMap(candidateMap);
+                        candidates = importPerm.get(req);
+                        Iterator it = candidates.iterator();
+                        it.next();
+                        it.remove();
+System.out.println("+++ ADDING IMPORT PERM " + req);
+                        m_importPermutations.add(importPerm);
+                    }
+                }
+
+                if (mutated.size() > 0)
+                {
+System.out.println("+++ ADDING CONFLICT PERM " + mutated);
+                    m_usesPermutations.add(copyConflict);
+                }
+
+                throw rethrow;
+            }
+        }
+
+        resultCache.put(module, Boolean.TRUE);
+
+        // Now check the consistency of all modules on which the
+        // current module depends.
+        for (Module m : checkModules)
+        {
+            checkPackageSpaceConsistency(m, candidateMap, modulePkgMap, capDepSet, resultCache);
+        }
+    }
+
+    private void removeInvalidateCandidate(
+        Module invalid, Map<Capability, Set<Requirement>> capDepSet,
+        Map<Requirement, Set<Capability>> candidateMap)
+    {
+System.out.println("+++ REMOVING INVALID CANDIDATE: " + invalid + ":" + invalid.getSymbolicName());
+        Set<Module> invalidated = new HashSet();
+
+        for (Requirement req : invalid.getRequirements())
+        {
+            candidateMap.remove(req);
+        }
+
+        boolean wasRequired = false;
+
+        for (Capability cap : invalid.getCapabilities())
+        {
+            Set<Requirement> reqs = capDepSet.remove(cap);
+            if (reqs == null)
+            {
+                continue;
+            }
+            wasRequired = true;
+            for (Requirement req : reqs)
+            {
+                Set<Capability> candidates = candidateMap.get(req);
+                if (candidates != null)
+                {
+                    candidates.remove(cap);
+                    if (candidates.size() == 0)
+                    {
+                        candidateMap.remove(req);
+                        invalidated.add(req.getModule());
+                    }
+                }
+                else
+                {
+                    System.out.println("+++ INVALIDATED REQ WITH NULL CAPS: " + req);
+                }
+            }
+        }
+
+        if (!wasRequired)
+        {
+            throw new ResolveException(
+                "Unable to resolve module", invalid, null);
+        }
+
+        for (Module m : invalidated)
+        {
+            removeInvalidateCandidate(m, capDepSet, candidateMap);
+        }
+    }
+
+    private static void calculateExportedPackages(
+        Module module, Map<Module, Packages> modulePkgMap)
+    {
+        if (m_isInvokeCount)
+        {
+            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+            Long count = m_invokeCounts.get(methodName);
+            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+            m_invokeCounts.put(methodName, count);
+        }
+
+        Packages packages = modulePkgMap.get(module);
+        if (packages != null)
+        {
+            return;
+        }
+        packages = new Packages();
 
         List<Capability> caps = module.getCapabilities();
 
@@ -843,7 +1328,7 @@
                 {
                     packages.m_exportedPkgs.put(
                         (String) caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue(),
-                        new Blame(incomingReqs, caps.get(i)));
+                        new Blame(caps.get(i), null));
                 }
             }
         }
@@ -873,420 +1358,6 @@
         return false;
     }
 
-    private void mergeCandidatePackages(
-        Module current, List<Requirement> outgoingReqs,
-        Capability candCap, Map<Module, Packages> modulePkgMap,
-        Map<Requirement, Set<Capability>> candidateMap)
-    {
-        if (m_isInvokeCount)
-        {
-            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
-            Long count = m_invokeCounts.get(methodName);
-            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
-            m_invokeCounts.put(methodName, count);
-        }
-
-        if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
-        {
-            mergeCandidatePackage(
-                current, false, new Blame(outgoingReqs, candCap), modulePkgMap, candidateMap);
-        }
-        else if (candCap.getNamespace().equals(Capability.MODULE_NAMESPACE))
-        {
-            // Get the candidate's package space to determine which packages
-            // will be visible to the current module.
-            Packages candPkgs = modulePkgMap.get(candCap.getModule());
-
-            // We have to merge all exported packages from the candidate,
-            // since the current module requires it.
-            for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
-            {
-                mergeCandidatePackage(
-                    current,
-                    true,
-                    new Blame(outgoingReqs, entry.getValue().m_cap),
-                    modulePkgMap,
-                    candidateMap);
-            }
-
-            // If the candidate requires any other bundles with reexport visibility,
-            // then we also need to merge their packages too.
-            for (Requirement req : candCap.getModule().getRequirements())
-            {
-                if (req.getNamespace().equals(Capability.MODULE_NAMESPACE))
-                {
-                    Directive dir = req.getDirective(Constants.VISIBILITY_DIRECTIVE);
-                    if ((dir != null) && dir.getValue().equals(Constants.VISIBILITY_REEXPORT)
-                        && (candidateMap.get(req) != null))
-                    {
-                        mergeCandidatePackages(
-                            current,
-                            outgoingReqs,
-                            candidateMap.get(req).iterator().next(),
-                            modulePkgMap,
-                            candidateMap);
-                    }
-                }
-            }
-        }
-    }
-
-    private void mergeCandidatePackage(
-        Module current, boolean requires,
-        Blame candBlame, Map<Module, Packages> modulePkgMap,
-        Map<Requirement, Set<Capability>> candidateMap)
-    {
-        if (m_isInvokeCount)
-        {
-            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
-            Long count = m_invokeCounts.get(methodName);
-            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
-            m_invokeCounts.put(methodName, count);
-        }
-
-// TODO: FELIX3 - Check for merging where module imports from itself,
-//       then it should be listed as an export for requiring bundles.
-        if (candBlame.m_cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
-        {
-//System.out.println("+++ MERGING " + candBlame.m_cap + " INTO " + current);
-            String pkgName = (String)
-                candBlame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue();
-
-            // Since this capability represents a package, it will become
-            // a hard constraint on the module's package space, so we need
-            // to make sure it doesn't conflict with any other hard constraints
-            // or any other uses constraints.
-
-            //
-            // First, check to see if the capability conflicts with
-            // any existing hard constraints.
-            //
-
-            Packages currentPkgs = modulePkgMap.get(current);
-            Blame currentExportedBlame = currentPkgs.m_exportedPkgs.get(pkgName);
-            Blame currentImportedBlame = currentPkgs.m_importedPkgs.get(pkgName);
-            List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(pkgName);
-
-            // We don't need to worry about an import conflicting with a required
-            // bundle's export, since imported package wires are terminal the
-            // bundle will never see the exported package from the required bundle.
-// TODO: FELIX3 - See scenario 21, this seems odd.
-            if (!requires &&
-                (currentImportedBlame != null) && !currentImportedBlame.m_cap.equals(candBlame.m_cap))
-//            if (!requires &&
-//                (((currentExportedBlame != null) && !currentExportedBlame.m_cap.equals(candBlame.m_cap))
-//                || ((currentImportedBlame != null) && !currentImportedBlame.m_cap.equals(candBlame.m_cap))))
-//                || ((currentRequiredBlames != null) && !currentRequiredBlames.contains(candBlame))))
-            {
-                // Permutate the candidate map and throw a resolve exception.
-                // NOTE: This method ALWAYS throws an exception.
-                permutateCandidates(
-                    current,
-                    pkgName,
-                    currentImportedBlame,
-                    candBlame,
-                    candidateMap);
-            }
-
-            //
-            // Second, check to see if the capability conflicts with
-            // any existing uses constraints
-            //
-
-            Packages currentPkgsCopy = currentPkgs;
-
-            if (!current.isResolved())
-            {
-                List<Blame> currentUsedBlames = currentPkgs.m_usedPkgs.get(pkgName);
-                checkExistingUsesConstraints(
-                    current, pkgName, currentUsedBlames, candBlame, modulePkgMap, candidateMap);
-
-                //
-                // Last, check to see if any uses constraints implied by the
-                // candidate conflict with any of the existing hard constraints.
-                //
-
-                // For now, create a copy of the module's package space and
-                // add the current candidate to the imported packages.
-                currentPkgsCopy = new Packages(currentPkgs);
-            }
-
-            if (requires)
-            {
-                if (currentRequiredBlames == null)
-                {
-                    currentRequiredBlames = new ArrayList<Blame>();
-                    currentPkgsCopy.m_requiredPkgs.put(pkgName, currentRequiredBlames);
-                }
-// TODO: FELIX3 - This is potentially modifying the original, we need to modify a copy.
-                currentRequiredBlames.add(candBlame);
-            }
-            else
-            {
-                currentPkgsCopy.m_importedPkgs.put(pkgName, candBlame);
-            }
-
-            // Verify and merge the candidate's transitive uses constraints.
-            verifyAndMergeUses(
-                current,
-                currentPkgsCopy,
-                candBlame,
-                modulePkgMap,
-                candidateMap,
-                new HashMap<String, List<Module>>());
-
-            // If we are here, then there were no conflict, so we should update
-            // the module's package space.
-            if (!current.isResolved())
-            {
-                currentPkgs.m_exportedPkgs.putAll(currentPkgsCopy.m_exportedPkgs);
-                currentPkgs.m_importedPkgs.putAll(currentPkgsCopy.m_importedPkgs);
-                currentPkgs.m_requiredPkgs.putAll(currentPkgsCopy.m_requiredPkgs);
-                currentPkgs.m_usedPkgs.putAll(currentPkgsCopy.m_usedPkgs);
-            }
-//dumpModulePkgs(current, currentPkgs);
-        }
-    }
-
-    private void checkExistingUsesConstraints(
-        Module current, String pkgName, List<Blame> currentUsedBlames,
-        Blame candBlame, Map<Module, Packages> modulePkgMap,
-        Map<Requirement, Set<Capability>> candidateMap)
-    {
-        if (m_isInvokeCount)
-        {
-            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
-            Long count = m_invokeCounts.get(methodName);
-            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
-            m_invokeCounts.put(methodName, count);
-        }
-
-        for (int i = 0; (currentUsedBlames != null) && (i < currentUsedBlames.size()); i++)
-        {
-//System.out.println("+++ CHECK " + candBlame + " IN EXISTING " + currentUsedBlames.get(i));
-            if (!isCompatible(currentUsedBlames.get(i).m_cap, candBlame.m_cap, modulePkgMap))
-            {
-                // Permutate the candidate map and throw a resolve exception.
-                // NOTE: This method ALWAYS throws an exception.
-                permutateCandidates(
-                    current,
-                    pkgName,
-                    currentUsedBlames.get(i),
-                    candBlame,
-                    candidateMap);
-            }
-        }
-    }
-
-// TODO: FELIX3 - We end up with duplicates in uses constraints,
-//       see scenario 2 for an example.
-    private void verifyAndMergeUses(
-        Module current, Packages currentPkgs,
-        Blame candBlame, Map<Module, Packages> modulePkgMap,
-        Map<Requirement, Set<Capability>> candidateMap,
-        Map<String, List<Module>> cycleMap)
-    {
-        if (m_isInvokeCount)
-        {
-            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
-            Long count = m_invokeCounts.get(methodName);
-            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
-            m_invokeCounts.put(methodName, count);
-        }
-
-        // Check for cycles.
-        String pkgName = (String)
-            candBlame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue();
-        List<Module> list = cycleMap.get(pkgName);
-        if ((list != null) && list.contains(current))
-        {
-            return;
-        }
-        list = (list == null) ? new ArrayList<Module>() : list;
-        list.add(current);
-        cycleMap.put(pkgName, list);
-
-//System.out.println("+++ VERIFYING USES " + current + " FOR " + candBlame);
-        for (Capability candSourceCap : getPackageSources(
-            candBlame.m_cap, modulePkgMap, new ArrayList<Capability>(), new HashSet<Capability>()))
-        {
-            for (String usedPkgName : candSourceCap.getUses())
-            {
-                Blame currentExportedBlame = currentPkgs.m_exportedPkgs.get(usedPkgName);
-                Blame currentImportedBlame = currentPkgs.m_importedPkgs.get(usedPkgName);
-// TODO: FELIX3 - What do we do with required packages?
-                List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(usedPkgName);
-
-                Packages candSourcePkgs = modulePkgMap.get(candSourceCap.getModule());
-//System.out.println("+++ candSourceCap " + candSourceCap);
-//System.out.println("+++ candSourceCap.getModule() " + candSourceCap.getModule() + " (" + candSourceCap.getModule().isResolved() + ")");
-//System.out.println("+++ candSourcePkgs " + candSourcePkgs);
-//System.out.println("+++ candSourcePkgs.m_exportedPkgs " + candSourcePkgs.m_exportedPkgs);
-                Blame candSourceBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
-                candSourceBlame = (candSourceBlame != null)
-                    ? candSourceBlame
-                    : candSourcePkgs.m_importedPkgs.get(usedPkgName);
-//                sourceCap = (sourceCap != null)
-//                    ? sourceCap
-//                    : sourcePkgs.m_requiredPkgs.get(usedPkgName);
-
-                // If the candidate doesn't actually have a constraint for
-                // the used package, then just ignore it since this is likely
-                // an error in its metadata.
-                if (candSourceBlame == null)
-                {
-                    return;
-                }
-
-                // If there is no current mapping for this package, then
-                // we can just return.
-                if ((currentExportedBlame == null)
-                    && (currentImportedBlame == null)
-                    && (currentRequiredBlames == null))
-                {
-                    List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
-                    if (usedCaps == null)
-                    {
-                        usedCaps = new ArrayList<Blame>();
-                        currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
-                    }
-//System.out.println("+++ MERGING CB " + candBlame + " SB " + candSourceBlame);
-//                    usedCaps.add(new Blame(candBlame.m_reqs, sourceBlame.m_cap));
-                    usedCaps.add(candSourceBlame);
-//                    return;
-                }
-                else if (!current.isResolved())
-                {
-                    if ((currentExportedBlame != null)
-                        && !isCompatible(currentExportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
-                    {
-                        throw new ResolveException(
-                            "Constraint violation for package '" + usedPkgName
-                            + "' when resolving module " + current
-                            + " between existing constraint "
-                            + currentExportedBlame
-                            + " and candidate constraint "
-                            + candSourceBlame, null, null);
-                    }
-                    else if ((currentImportedBlame != null)
-                        && !isCompatible(currentImportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
-                    {
-                        permutateCandidates(
-                            current, usedPkgName, currentImportedBlame,
-                            candSourceBlame, candidateMap);
-                    }
-                }
-
-                verifyAndMergeUses(current, currentPkgs, candSourceBlame,
-                    modulePkgMap, candidateMap, cycleMap);
-            }
-        }
-    }
-
-    private void permutateCandidates(
-        Module current, String pkgName, Blame currentBlame, Blame candBlame,
-        Map<Requirement, Set<Capability>> candidateMap)
-        throws ResolveException
-    {
-        if (m_isInvokeCount)
-        {
-            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
-            Long count = m_invokeCounts.get(methodName);
-            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
-            m_invokeCounts.put(methodName, count);
-        }
-
-// TODO: FELIX3 - I think permutation is not as efficient as it could be, since
-//       the check for subsets is costly.
-
-        // When we detect a conflict, we need to permutate the candidate map
-        // we when we try again, we'll select different candidates. To achieve
-        // this, we create a different permutation for each requirement in
-        // the set of blames requirements by eliminating the first candidate
-        // (i.e., the selected candidate) for each. We need to create an separate
-        // permutation for each blamed requirement to ensure we try all possible
-        // combination. This is a form of back tracking, since we eliminate each
-        // requirement's selected candidate, which will force them to choose
-        // another in the new permutation.
-        //
-        // The blamed requirement may be null if the bundle itself is exports
-        // the package imposing the uses constraint.
-        if ((currentBlame.m_reqs != null) && (currentBlame.m_reqs.size() != 0))
-        {
-            // Permutate the candidate map for each blamed requirement.
-            for (int reqIdx = 0; reqIdx < currentBlame.m_reqs.size(); reqIdx++)
-            {
-                // Verify whether we have more than one candidate to create
-                // a permutation.
-                Set<Capability> candidates = candidateMap.get(currentBlame.m_reqs.get(reqIdx));
-                if (candidates.size() > 1)
-                {
-                    // Create a copy of the current candidate map and then remove
-                    // the first (i.e., selected) candidate for the current
-                    // blamed requirement.
-                    Map<Requirement, Set<Capability>> copy = copyCandidateMap(candidateMap);
-                    Set<Capability> candCopy = copy.get(currentBlame.m_reqs.get(reqIdx));
-                    Iterator it = candCopy.iterator();
-                    it.next();
-                    it.remove();
-
-                    // Check if the created permutation is a subset of a previously
-                    // created permutation. If so, then we don't need to record it
-                    // since it will be generated again when the superset permutation
-                    // is processed.
-                    boolean isSubset = false;
-                    for (int permIdx = 0; !isSubset && (permIdx < m_candidatePermutations.size()); permIdx++)
-                    {
-                        if (isSubsetPermutation(m_candidatePermutations.get(permIdx), copy))
-                        {
-                            isSubset = true;
-                        }
-                    }
-                    if (!isSubset)
-                    {
-//System.out.println("+++ SETTING "
-//    + currentBlame.m_reqs.get(reqIdx)
-//    + " CANDS FROM " + candidates + " TO " + candCopy);
-                        m_candidatePermutations.add(0, copy);
-                    }
-                }
-            }
-        }
-
-        throw new ResolveException(
-            "Constraint violation for package '"
-            + pkgName + "' when resolving module "
-            + current + " between existing constraint "
-            + currentBlame + " and candidate constraint "
-            + candBlame, null, null);
-    }
-
-    private static boolean isSubsetPermutation(
-        Map<Requirement, Set<Capability>> orig, Map<Requirement, Set<Capability>> copy)
-    {
-        if (m_isInvokeCount)
-        {
-            String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
-            Long count = m_invokeCounts.get(methodName);
-            count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
-            m_invokeCounts.put(methodName, count);
-        }
-
-        for (Entry<Requirement, Set<Capability>> entry : orig.entrySet())
-        {
-            Set<Capability> copyCands = copy.get(entry.getKey());
-            if (copyCands == null)
-            {
-                return false;
-            }
-            if (!entry.getValue().containsAll(copyCands))
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
     private static boolean isCompatible(
         Capability currentCap, Capability candCap, Map<Module, Packages> modulePkgMap)
     {
@@ -1300,6 +1371,11 @@
 
         if ((currentCap != null) && (candCap != null))
         {
+            if (currentCap.equals(candCap))
+            {
+                return true;
+            }
+
             List<Capability> currentSources =
                 getPackageSources(
                     currentCap,
@@ -1489,7 +1565,7 @@
                         // We need an unique requirement here or else subsequent
                         // dynamic imports for the same dynamic requirement will
                         // conflict with previous ones.
-                        new RequirementImpl(Capability.PACKAGE_NAMESPACE, new ArrayList(0), attrs),
+                        new RequirementImpl(module, Capability.PACKAGE_NAMESPACE, new ArrayList(0), attrs),
                         entry.getValue().m_cap.getModule(),
                         entry.getValue().m_cap));
             }
@@ -1552,22 +1628,22 @@
 
     private static class Blame
     {
-        public final List<Requirement> m_reqs;
         public final Capability m_cap;
+        public final List<Requirement> m_reqs;
 
-        public Blame(List<Requirement> reqs, Capability cap)
+        public Blame(Capability cap, List<Requirement> reqs)
         {
-            m_reqs = reqs;
             m_cap = cap;
+            m_reqs = reqs;
         }
 
         public String toString()
         {
             return m_cap.getModule()
                 + "." + m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue()
-                + ((m_reqs.size() == 0)
+                + (((m_reqs == null) || (m_reqs.size() == 0))
                     ? " NO BLAME"
-                    : " BLAMED ON " + m_reqs.get(m_reqs.size() - 1));
+                    : " BLAMED ON " + m_reqs);
         }
 
         public boolean equals(Object o)
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index dcffea3..8fc4a0c 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -125,7 +125,7 @@
         // Parse Fragment-Host.
         //
 
-        List<Requirement> hostReqs = parseFragmentHost(m_logger, m_headerMap);
+        List<Requirement> hostReqs = parseFragmentHost(m_logger, owner, m_headerMap);
 
         //
         // Parse Require-Bundle
@@ -134,7 +134,7 @@
         List<ParsedHeaderClause> requireClauses =
             parseStandardHeader((String) headerMap.get(Constants.REQUIRE_BUNDLE));
         requireClauses = normalizeRequireClauses(m_logger, requireClauses, getManifestVersion());
-        List<Requirement> requireReqs = convertRequires(requireClauses);
+        List<Requirement> requireReqs = convertRequires(requireClauses, owner);
 
         //
         // Parse Import-Package.
@@ -143,7 +143,7 @@
         List<ParsedHeaderClause> importClauses =
             parseStandardHeader((String) headerMap.get(Constants.IMPORT_PACKAGE));
         importClauses = normalizeImportClauses(m_logger, importClauses, getManifestVersion());
-        List<Requirement> importReqs = convertImports(importClauses);
+        List<Requirement> importReqs = convertImports(importClauses, owner);
 
         //
         // Parse DynamicImport-Package.
@@ -152,7 +152,7 @@
         List<ParsedHeaderClause> dynamicClauses =
             parseStandardHeader((String) headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
         dynamicClauses = normalizeDynamicImportClauses(m_logger, dynamicClauses, getManifestVersion());
-        m_dynamicRequirements = convertImports(dynamicClauses);
+        m_dynamicRequirements = convertImports(dynamicClauses, owner);
 
         //
         // Parse Export-Package.
@@ -173,7 +173,7 @@
         {
             List<ParsedHeaderClause> implicitClauses =
                 calculateImplicitImports(exportCaps, importClauses);
-            importReqs.addAll(convertImports(implicitClauses));
+            importReqs.addAll(convertImports(implicitClauses, owner));
 
             List<ParsedHeaderClause> allImportClauses =
                 new ArrayList<ParsedHeaderClause>(implicitClauses.size() + importClauses.size());
@@ -376,7 +376,8 @@
         return clauses;
     }
 
-    private static List<Requirement> convertImports(List<ParsedHeaderClause> clauses)
+    private static List<Requirement> convertImports(
+        List<ParsedHeaderClause> clauses, Module owner)
     {
         // Now convert generic header clauses into requirements.
         List reqList = new ArrayList();
@@ -397,6 +398,7 @@
                 // Create package requirement and add to requirement list.
                 reqList.add(
                     new RequirementImpl(
+                        owner,
                         Capability.PACKAGE_NAMESPACE,
                         clauses.get(clauseIdx).m_dirs,
                         newAttrs));
@@ -1113,7 +1115,8 @@
         return null;
     }
 
-    private static List<Requirement> parseFragmentHost(Logger logger, Map headerMap)
+    private static List<Requirement> parseFragmentHost(
+        Logger logger, Module owner, Map headerMap)
         throws BundleException
     {
         List<Requirement> reqs = new ArrayList();
@@ -1164,7 +1167,8 @@
                     clauses.get(0).m_paths.get(0), false));
                 newAttrs.addAll(attrs);
 
-                reqs.add(new RequirementImpl(Capability.HOST_NAMESPACE,
+                reqs.add(new RequirementImpl(
+                    owner, Capability.HOST_NAMESPACE,
                     clauses.get(0).m_dirs,
                     newAttrs));
             }
@@ -1262,7 +1266,8 @@
         return clauses;
     }
 
-    private static List<Requirement> convertRequires(List<ParsedHeaderClause> clauses)
+    private static List<Requirement> convertRequires(
+        List<ParsedHeaderClause> clauses, Module owner)
     {
         List<Requirement> reqList = new ArrayList();
         for (int clauseIdx = 0; clauseIdx < clauses.size(); clauseIdx++)
@@ -1283,6 +1288,7 @@
                 // Create package requirement and add to requirement list.
                 reqList.add(
                     new RequirementImpl(
+                        owner,
                         Capability.MODULE_NAMESPACE,
                         clauses.get(clauseIdx).m_dirs,
                         newAttrs));
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java
index e091803..9cbc942 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/RequirementImpl.java
@@ -25,11 +25,13 @@
 import org.apache.felix.framework.capabilityset.Directive;
 import org.apache.felix.framework.capabilityset.Requirement;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.framework.resolver.Module;
 import org.apache.felix.framework.util.VersionRange;
 import org.osgi.framework.Constants;
 
 public class RequirementImpl implements Requirement
 {
+    private final Module m_module;
     private final String m_namespace;
     private final SimpleFilter m_filter;
     private final boolean m_optional;
@@ -37,8 +39,10 @@
     private final List<Directive> m_dirsConst;
 
     public RequirementImpl(
-        String namespace, List<Directive> dirs, List<Attribute> attrs)
+        Module module, String namespace,
+        List<Directive> dirs, List<Attribute> attrs)
     {
+        m_module = module;
         m_namespace = namespace;
         m_dirs = dirs;
         m_dirsConst = Collections.unmodifiableList(m_dirs);
@@ -56,6 +60,11 @@
         m_optional = optional;
     }
 
+    public Module getModule()
+    {
+        return m_module;
+    }
+
     public String getNamespace()
     {
         return m_namespace;
@@ -90,7 +99,7 @@
 
     public String toString()
     {
-        return m_namespace + "; " + getFilter().toString();
+        return "[" + m_module + "] " + m_namespace + "; " + getFilter().toString();
     }
 
     private static SimpleFilter convertToFilter(List<Attribute> attrs)