You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2010/04/23 20:49:25 UTC

svn commit: r937463 [3/3] - in /felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver: ./ cs/ manifestparser/ proto3/

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/RequirementImpl.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/RequirementImpl.java?rev=937463&r1=937462&r2=937463&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/RequirementImpl.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/RequirementImpl.java Fri Apr 23 18:49:24 2010
@@ -29,6 +29,7 @@ import org.apache.felix.resolver.manifes
 
 public class RequirementImpl implements Requirement
 {
+    private final Module m_module;
     private final String m_namespace;
     private SimpleFilter m_filter;
     private boolean m_optional;
@@ -38,8 +39,9 @@ public class RequirementImpl implements 
     private final List<Attribute> m_attrsConst;
 
 // TODO: FELIX3 - Get rid of dynamic argument.
-    public RequirementImpl(String namespace)
+    public RequirementImpl(Module module, String namespace)
     {
+        m_module = module;
         m_namespace = namespace;
         m_dirs = new ArrayList();
         m_dirsConst = Collections.unmodifiableList(m_dirs);
@@ -51,8 +53,9 @@ public class RequirementImpl implements 
     }
 
     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);
@@ -72,6 +75,11 @@ public class RequirementImpl implements 
         m_optional = optional;
     }
 
+    public Module getModule()
+    {
+        return m_module;
+    }
+
     public String getNamespace()
     {
         return m_namespace;
@@ -106,7 +114,7 @@ public class RequirementImpl implements 
 
     public String toString()
     {
-        return m_namespace + "; " + getFilter().toString();
+        return "[" + m_module + "] " + m_namespace + "; " + getFilter().toString();
     }
 
     public RequirementImpl optional()

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/StatefulResolver.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/StatefulResolver.java?rev=937463&r1=937462&r2=937463&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/StatefulResolver.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/StatefulResolver.java Fri Apr 23 18:49:24 2010
@@ -38,7 +38,7 @@ public class StatefulResolver
         return m_state;
     }
 
-    public void resolve(Module module)
+    public Map<Module, List<Wire>> resolve(Module module)
     {
         System.out.println("Target: " + module + " (" + module.getRequirements().size() + ")");
         long starttime = System.currentTimeMillis();
@@ -49,6 +49,8 @@ public class StatefulResolver
 
         // Mark all modules as resolved.
         markResolvedModules(wireMap);
+
+        return wireMap;
     }
 
     private void markResolvedModules(Map<Module, List<Wire>> wireMap)

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/cs/Requirement.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/cs/Requirement.java?rev=937463&r1=937462&r2=937463&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/cs/Requirement.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/cs/Requirement.java Fri Apr 23 18:49:24 2010
@@ -19,9 +19,11 @@
 package org.apache.felix.resolver.cs;
 
 import java.util.List;
+import org.apache.felix.resolver.Module;
 
 public interface Requirement
 {
+    Module getModule();
     String getNamespace();
     SimpleFilter getFilter();
     boolean isOptional();

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Main.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Main.java?rev=937463&r1=937462&r2=937463&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Main.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Main.java Fri Apr 23 18:49:24 2010
@@ -697,7 +697,7 @@ public class Main
 
     private static RequirementImpl convertToRequirement(Module module, FelixRequirement req)
     {
-        RequirementImpl ri = new RequirementImpl(req.getNamespace());
+        RequirementImpl ri = new RequirementImpl(module, req.getNamespace());
 
         R4Directive[] dirs = req.getDirectives();
         for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.length); dirIdx++)

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java?rev=937463&r1=937462&r2=937463&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/ManifestParser.java Fri Apr 23 18:49:24 2010
@@ -669,7 +669,7 @@ public class ManifestParser
         return caps;
     }
 
-    private static FelixCapability[] parseExportHeader(String header)
+    public static FelixCapability[] parseExportHeader(String header)
     {
         Object[][][] clauses = parseStandardHeader(header);
 
@@ -765,7 +765,7 @@ public class ManifestParser
         return (FelixCapability[]) capList.toArray(new FelixCapability[capList.size()]);
     }
 
-    private static FelixRequirement[] parseImportHeader(String header)
+    public static FelixRequirement[] parseImportHeader(String header)
     {
         Object[][][] clauses = parseStandardHeader(header);
 

Modified: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java
URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java?rev=937463&r1=937462&r2=937463&view=diff
==============================================================================
--- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java (original)
+++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/proto3/Proto3Resolver.java Fri Apr 23 18:49:24 2010
@@ -35,6 +35,7 @@ import org.apache.felix.resolver.Resolve
 import org.apache.felix.resolver.Resolver;
 import org.apache.felix.resolver.Resolver.ResolverState;
 import org.apache.felix.resolver.ResolverStateImpl;
+import org.apache.felix.resolver.StatefulResolver;
 import org.apache.felix.resolver.Wire;
 import org.apache.felix.resolver.WireImpl;
 import org.apache.felix.resolver.WireModuleImpl;
@@ -54,7 +55,16 @@ public class Proto3Resolver implements R
     private static boolean m_isInvokeCount = false;
 
     // Reusable empty array.
-    private static final List<Wire> m_emptyWires = new ArrayList<Wire>(0);
+    private static final List<Wire> m_emptyWires = Collections.emptyList();
+
+    // 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 Proto3Resolver()
     {
@@ -63,9 +73,6 @@ System.out.println("+++ PROTO3 RESOLVER"
         m_isInvokeCount = (v == null) ? false : Boolean.valueOf(v);
     }
 
-    private final List<Map<Requirement, Set<Capability>>> m_candidatePermutations =
-        new ArrayList<Map<Requirement, Set<Capability>>>();
-
     public Map<Module, List<Wire>> resolve(ResolverState state, Module module)
     {
         m_invokeCounts.clear();
@@ -84,32 +91,44 @@ System.out.println("+++ PROTO3 RESOLVER"
 
         if (!module.isResolved())
         {
-            m_candidatePermutations.clear();
+// TODO: FELIX3 - Should we clear these in a finally block?
+            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();
+                m_packageSourcesCache.clear();
+
+                candidateMap = (m_usesPermutations.size() > 0)
+                    ? m_usesPermutations.remove(0)
+                    : m_importPermutations.remove(0);
 dumpCandidateMap(state, candidateMap);
 
+                calculatePackageSpaces(
+                    module, candidateMap, modulePkgMap,
+                    capDepSet, new HashMap(), 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)
                 {
@@ -117,13 +136,13 @@ dumpCandidateMap(state, candidateMap);
                     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,
@@ -148,8 +167,6 @@ dumpCandidateMap(state, candidateMap);
             m_invokeCounts.put(methodName, count);
         }
 
-        Capability candidate = null;
-
         // We can only create a dynamic import if the following
         // conditions are met:
         // 1. The specified module is resolved.
@@ -163,31 +180,39 @@ dumpCandidateMap(state, candidateMap);
             getDynamicImportCandidates(state, module, pkgName);
         if (candidateMap != null)
         {
-            m_candidatePermutations.clear();
+// TODO: FELIX3 - Should we clear these in a finally block?
+            m_usesPermutations.clear();
+            m_importPermutations.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);
+
+                calculatePackageSpaces(
+                    module, candidateMap, modulePkgMap,
+                    capDepSet, new HashMap(), new HashSet());
 
                 try
                 {
-                    findConsistentDynamicCandidate(
-                        module,
-                        new ArrayList(),
-                        candidateMap,
-                        modulePkgMap);
+                    checkPackageSpaceConsistency(
+                        module, candidateMap, modulePkgMap, capDepSet, new HashMap());
                 }
                 catch (ResolveException ex)
                 {
@@ -195,26 +220,25 @@ dumpCandidateMap(state, candidateMap);
                     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 =
-                populateDynamicWireMap(
-                    module, pkgName, modulePkgMap, wireMap, candidateMap);
+            wireMap = populateDynamicWireMap(
+                module, pkgName, modulePkgMap, wireMap, candidateMap);
 
-//System.out.println("+++ DYNAMIC SUCCESS: " + wireMap.get(module));
             return wireMap;
         }
 
-//System.out.println("+++ DYNAMIC FAILURE");
         return null;
     }
 
-    // TODO: FELIX3 - It would be nice to make this private.
+// TODO: FELIX3 - It would be nice to make this private.
     public static Map<Requirement, Set<Capability>> getDynamicImportCandidates(
         ResolverState state, Module module, String pkgName)
     {
@@ -262,7 +286,7 @@ dumpCandidateMap(state, candidateMap);
         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();
 
@@ -301,7 +325,6 @@ dumpCandidateMap(state, candidateMap);
             candidates.clear();
         }
 
-
         if (candidates.size() > 0)
         {
             Map<Requirement, Set<Capability>> candidateMap = new HashMap();
@@ -312,68 +335,6 @@ dumpCandidateMap(state, candidateMap);
         return null;
     }
 
-    private static void dumpCandidateMap(
-        ResolverState state, Map<Requirement, Set<Capability>> candidateMap)
-    {
-        System.out.println("=== BEGIN CANDIDATE MAP ===");
-        for (Module module : ((ResolverStateImpl) state).getModules())
-        {
-            System.out.println("  " + module
-                 + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
-            for (Requirement req : module.getRequirements())
-            {
-                Set<Capability> candidates = candidateMap.get(req);
-                if ((candidates != null) && (candidates.size() > 0))
-                {
-                    System.out.println("    " + req + ": " + candidates);
-                }
-            }
-            for (Requirement req : module.getDynamicRequirements())
-            {
-                Set<Capability> candidates = candidateMap.get(req);
-                if ((candidates != null) && (candidates.size() > 0))
-                {
-                    System.out.println("    " + req + ": " + candidates);
-                }
-            }
-        }
-        System.out.println("=== END CANDIDATE MAP ===");
-    }
-
-    private static void dumpModulePkgMap(Map<Module, Packages> modulePkgMap)
-    {
-        System.out.println("+++MODULE PKG MAP+++");
-        for (Entry<Module, Packages> entry : modulePkgMap.entrySet())
-        {
-            dumpModulePkgs(entry.getKey(), entry.getValue());
-        }
-    }
-
-    private static void dumpModulePkgs(Module module, Packages packages)
-    {
-        System.out.println(module + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
-        System.out.println("  EXPORTED");
-        for (Entry<String, Blame> entry : packages.m_exportedPkgs.entrySet())
-        {
-            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
-        }
-        System.out.println("  IMPORTED");
-        for (Entry<String, Blame> entry : packages.m_importedPkgs.entrySet())
-        {
-            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
-        }
-        System.out.println("  REQUIRED");
-        for (Entry<String, List<Blame>> entry : packages.m_requiredPkgs.entrySet())
-        {
-            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
-        }
-        System.out.println("  USED");
-        for (Entry<String, List<Blame>> entry : packages.m_usedPkgs.entrySet())
-        {
-            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
-        }
-    }
-
 // TODO: FELIX3 - Modify to not be recursive.
     private static void populateCandidates(
         ResolverState state, Module module,
@@ -577,22 +538,15 @@ System.out.println("RE: Candidate not re
             candidateMap.remove(dynReq);
             throw new ResolveException("Dynamic import failed.", module, dynReq);
         }
-
-        // Add existing wires as candidates.
-        for (Wire wire : module.getWires())
-        {
-            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,
+        Map<Capability, List<Module>> usesCycleMap,
+        Set<Module> cycle)
     {
         if (m_isInvokeCount)
         {
@@ -602,83 +556,50 @@ System.out.println("RE: Candidate not re
             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);
 
+        // Create a map of wires or proposed wired depending on whether
+        // the module is resolved or not.
+        List<Requirement> reqs = new ArrayList();
+        List<Capability> caps = new ArrayList();
         if (module.isResolved())
         {
-            List<Wire> wires = (List<Wire>) ((Object[]) o)[1];
-
-            while (wires.size() > 0)
+            // Use wires to get actual requirements and satisfying capabilities.
+            for (Wire wire : module.getWires())
             {
-                Wire wire = wires.remove(0);
+                reqs.add(wire.getRequirement());
+                caps.add(wire.getCapability());
+            }
 
-                // Try to resolve the candidate.
-                findConsistentCandidates(
-                    wire.getCapability().getModule(),
-                    incomingReqs,
-                    candidateMap,
-                    modulePkgMap,
-                    resultCache);
+            // Since the module is resolved, it could be dynamically importing,
+            // so check to see if there are candidates for any of its dynamic
+            // imports.
+            for (Requirement req : module.getDynamicRequirements())
+            {
+                // Get the candidates for the current requirement.
+                Set<Capability> candCaps = candidateMap.get(req);
+                // Optional requirements may not have any candidates.
+                if (candCaps == null)
+                {
+                    continue;
+                }
 
-                // If we are here, the candidate was consistent. Try to
-                // merge the candidate into the target module's packages.
-                mergeCandidatePackages(
-                    module,
-                    incomingReqs,
-                    wire.getCapability(),
-                    modulePkgMap,
-                    candidateMap);
+                Capability cap = candCaps.iterator().next();
+                reqs.add(req);
+                caps.add(cap);
+                // Can only dynamically import one at a time, so break
+                // out of the loop after the first.
+                break;
             }
         }
         else
         {
-            List<Requirement> reqs = (List<Requirement>) ((Object[]) o)[1];
-
-            while (reqs.size() > 0)
+            for (Requirement req : module.getRequirements())
             {
-                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.
@@ -687,199 +608,79 @@ System.out.println("RE: Candidate not re
                     continue;
                 }
 
-                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,
-                            resultCache);
-
-                        // 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);
-                        }
-                    }
-                }
+                Capability cap = candCaps.iterator().next();
+                reqs.add(req);
+                caps.add(cap);
             }
         }
 
-        // 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)
+        // 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.
+        for (int i = 0; i < reqs.size(); i++)
         {
-            // Record that the module was successfully populated.
-            resultCache.put(module, Boolean.TRUE);
+            Requirement req = reqs.get(i);
+            Capability cap = caps.get(i);
+            calculateExportedPackages(cap.getModule(), modulePkgMap);
+            mergeCandidatePackages(module, req, cap, modulePkgMap, candidateMap);
+            addCapabilityDependency(cap, req, capDepSet);
         }
-    }
 
-    private void findConsistentDynamicCandidate(
-        Module module, List<Requirement> incomingReqs,
-        Map<Requirement, Set<Capability>> candidateMap,
-        Map<Module, Packages> modulePkgMap)
-    {
-        if (m_isInvokeCount)
+        // Third, ask our candidates to calculate their package spaces.
+        for (int i = 0; i < caps.size(); i++)
         {
-            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);
+            calculatePackageSpaces(
+                caps.get(i).getModule(), candidateMap, modulePkgMap,
+                capDepSet, usesCycleMap, cycle);
         }
 
-//System.out.println("+++ RESOLVING " + module);
-        calculateExportedPackages(module, incomingReqs, modulePkgMap);
-
-        List<Requirement> reqs = new ArrayList(module.getRequirements());
-        reqs.addAll(module.getDynamicRequirements());
-        for (Requirement req : reqs)
-        {
-            // Get the candidates for the current requirement.
-            Set<Capability> candCaps = candidateMap.get(req);
-            // Optional requirements may not have any candidates.
-            if (candCaps == null)
-            {
-                continue;
-            }
-
-            List<Requirement> outgoingReqs = new ArrayList<Requirement>(incomingReqs);
-            outgoingReqs.add(req);
-
-            for (Iterator<Capability> it = candCaps.iterator(); it.hasNext(); )
+        // Fourth, add all of the uses constraints implied by our imported
+        // and required packages.
+        for (Entry<String, List<Blame>> entry : modulePkgs.m_importedPkgs.entrySet())
+        {
+            for (Blame blame : entry.getValue())
             {
-                Capability candCap = it.next();
-//System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
-                try
+                // Ignore modules that import from themselves.
+                if (!blame.m_cap.getModule().equals(module))
                 {
-                    // Try to resolve the candidate.
-                    findConsistentCandidates(
-                        candCap.getModule(),
-                        outgoingReqs,
-                        candidateMap,
-                        modulePkgMap,
-                        new HashMap());
+                    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(
+                    mergeUses(
                         module,
-                        outgoingReqs,
-                        candCap,
+                        modulePkgs,
+                        blame.m_cap,
+                        blameReqs,
                         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);
-                    }
+                        candidateMap,
+                        usesCycleMap);
                 }
             }
         }
-    }
-
-    private static void calculateExportedPackages(
-        Module module, List<Requirement> incomingReqs, 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 = new Packages();
-
-        List<Capability> caps = module.getCapabilities();
-
-        if (caps.size() > 0)
+        for (Entry<String, List<Blame>> entry : modulePkgs.m_requiredPkgs.entrySet())
         {
-            for (int i = 0; i < caps.size(); i++)
+            for (Blame blame : entry.getValue())
             {
-// 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)))
-                {
-                    packages.m_exportedPkgs.put(
-                        (String) caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue(),
-                        new Blame(incomingReqs, caps.get(i)));
-                }
-            }
-        }
-
-        modulePkgMap.put(module, packages);
-    }
-
-    private static boolean hasOverlappingImport(Module module, Capability cap)
-    {
-        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);
-        }
+                List<Requirement> blameReqs = new ArrayList();
+                blameReqs.add(blame.m_reqs.get(0));
 
-        List<Requirement> reqs = module.getRequirements();
-        for (int i = 0; i < reqs.size(); i++)
-        {
-            if (reqs.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
-                && CapabilitySet.matches(cap, reqs.get(i).getFilter()))
-            {
-                return true;
+                mergeUses(
+                    module,
+                    modulePkgs,
+                    blame.m_cap,
+                    blameReqs,
+                    modulePkgMap,
+                    candidateMap,
+                    usesCycleMap);
             }
         }
-        return false;
     }
 
     private void mergeCandidatePackages(
-        Module current, List<Requirement> outgoingReqs,
-        Capability candCap, Map<Module, Packages> modulePkgMap,
-        Map<Requirement, Set<Capability>> candidateMap)
+        Module current, Requirement currentReq, Capability candCap,
+        Map<Module, Packages> modulePkgMap, Map<Requirement, Set<Capability>> candidateMap)
     {
         if (m_isInvokeCount)
         {
@@ -892,10 +693,13 @@ ex.printStackTrace();
         if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
         {
             mergeCandidatePackage(
-                current, false, new Blame(outgoingReqs, candCap), modulePkgMap, candidateMap);
+                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());
@@ -907,9 +711,9 @@ ex.printStackTrace();
                 mergeCandidatePackage(
                     current,
                     true,
-                    new Blame(outgoingReqs, entry.getValue().m_cap),
-                    modulePkgMap,
-                    candidateMap);
+                    currentReq,
+                    entry.getValue().m_cap,
+                    modulePkgMap);
             }
 
             // If the candidate requires any other bundles with reexport visibility,
@@ -924,7 +728,7 @@ ex.printStackTrace();
                     {
                         mergeCandidatePackages(
                             current,
-                            outgoingReqs,
+                            currentReq,
                             candidateMap.get(req).iterator().next(),
                             modulePkgMap,
                             candidateMap);
@@ -936,8 +740,8 @@ ex.printStackTrace();
 
     private void mergeCandidatePackage(
         Module current, boolean requires,
-        Blame candBlame, Map<Module, Packages> modulePkgMap,
-        Map<Requirement, Set<Capability>> candidateMap)
+        Requirement currentReq, Capability candCap,
+        Map<Module, Packages> modulePkgMap)
     {
         if (m_isInvokeCount)
         {
@@ -949,112 +753,54 @@ ex.printStackTrace();
 
 // 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))
+        if (candCap.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();
+                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);
-            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)
             {
+                List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(pkgName);
                 if (currentRequiredBlames == null)
                 {
                     currentRequiredBlames = new ArrayList<Blame>();
-                    currentPkgsCopy.m_requiredPkgs.put(pkgName, currentRequiredBlames);
+                    currentPkgs.m_requiredPkgs.put(pkgName, currentRequiredBlames);
                 }
-// TODO: FELIX3 - This is potentially modifying the original, we need to modify a copy.
-                currentRequiredBlames.add(candBlame);
+                currentRequiredBlames.add(new Blame(candCap, blameReqs));
             }
             else
             {
-                currentPkgsCopy.m_importedPkgs.put(pkgName, candBlame);
+                List<Blame> currentImportedBlames = currentPkgs.m_importedPkgs.get(pkgName);
+                if (currentImportedBlames == null)
+                {
+                    currentImportedBlames = new ArrayList<Blame>();
+                    currentPkgs.m_importedPkgs.put(pkgName, currentImportedBlames);
+                }
+                currentImportedBlames.add(new Blame(candCap, blameReqs));
             }
 
-            // 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);
+//dumpModulePkgs(current, currentPkgs);
         }
     }
 
-    private void checkExistingUsesConstraints(
-        Module current, String pkgName, List<Blame> currentUsedBlames,
-        Blame candBlame, Map<Module, Packages> modulePkgMap,
-        Map<Requirement, Set<Capability>> candidateMap)
+    private static void addCapabilityDependency(
+        Capability cap, Requirement req, Map<Capability, Set<Requirement>> capDepSet)
     {
         if (m_isInvokeCount)
         {
@@ -1064,30 +810,22 @@ dumpModulePkgs(current, currentPkgs);
             m_invokeCounts.put(methodName, count);
         }
 
-        for (int i = 0; (currentUsedBlames != null) && (i < currentUsedBlames.size()); i++)
+        Set<Requirement> reqs = capDepSet.get(cap);
+        if (reqs == null)
         {
-//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);
-            }
+            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 void verifyAndMergeUses(
+    private void mergeUses(
         Module current, Packages currentPkgs,
-        Blame candBlame, Map<Module, Packages> modulePkgMap,
+        Capability mergeCap, List<Requirement> blameReqs, Map<Module, Packages> modulePkgMap,
         Map<Requirement, Set<Capability>> candidateMap,
-        Map<String, List<Module>> cycleMap)
+        Map<Capability, List<Module>> cycleMap)
     {
         if (m_isInvokeCount)
         {
@@ -1097,99 +835,82 @@ dumpModulePkgs(current, currentPkgs);
             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))
+        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;
         }
-        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>()))
+        // Check for cycles.
+        List<Module> list = cycleMap.get(mergeCap);
+        if ((list != null) && list.contains(current))
+        {
+            return;
+        }
+        list = (list == null) ? new ArrayList<Module>() : list;
+        list.add(current);
+        cycleMap.put(mergeCap, list);
+
+//System.out.println("+++ MERGING USES " + current + " FOR " + candBlame);
+        for (Capability candSourceCap : getPackageSources(mergeCap, modulePkgMap))
         {
             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))
+                Blame candExportedBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
+                List<Blame> candSourceBlames = null;
+                if (candExportedBlame != 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;
+                    candSourceBlames = new ArrayList(1);
+                    candSourceBlames.add(candExportedBlame);
+                }
+                else
+                {
+                    candSourceBlames = candSourcePkgs.m_importedPkgs.get(usedPkgName);
+                }
+
+                if (candSourceBlames == null)
+                {
+                    continue;
                 }
-                else if (!current.isResolved())
+
+                List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
+                if (usedCaps == null)
                 {
-                    if ((currentExportedBlame != null)
-                        && !isCompatible(currentExportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
+                    usedCaps = new ArrayList<Blame>();
+                    currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
+                }
+                for (Blame blame : candSourceBlames)
+                {
+                    if (blame.m_reqs != null)
                     {
-                        throw new ResolveException(
-                            "Constraint violation for package '" + usedPkgName
-                            + "' when resolving module " + current
-                            + " between existing constraint "
-                            + currentExportedBlame
-                            + " and candidate constraint "
-                            + candSourceBlame, null, null);
+                        List<Requirement> blameReqs2 = new ArrayList(blameReqs);
+                        blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs2));
+                            mergeUses(current, currentPkgs, blame.m_cap, blameReqs2,
+                            modulePkgMap, candidateMap, cycleMap);
                     }
-                    else if ((currentImportedBlame != null)
-                        && !isCompatible(currentImportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
+                    else
                     {
-                        permutateCandidates(
-                            current, usedPkgName, currentImportedBlame,
-                            candSourceBlame, candidateMap);
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs));
+                        mergeUses(current, currentPkgs, blame.m_cap, blameReqs,
+                            modulePkgMap, candidateMap, cycleMap);
                     }
                 }
-
-                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
+    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)
         {
@@ -1199,73 +920,235 @@ dumpModulePkgs(current, currentPkgs);
             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.
+        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();
+
+        // Check for conflicting imports from fragments.
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            if (entry.getValue().size() > 1)
+            {
+                Blame sourceBlame = null;
+                for (Blame blame : entry.getValue())
+                {
+                    if (sourceBlame == null)
+                    {
+                        sourceBlame = blame;
+                    }
+                    else if (!sourceBlame.m_cap.equals(blame.m_cap))
+                    {
+                        // Try to permutate the conflicting requirement.
+                        Requirement req = blame.m_reqs.get(0);
+                        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();
+                            m_importPermutations.add(importPerm);
+                        }
+                        // Try to permutate the source requirement.
+                        req = sourceBlame.m_reqs.get(0);
+                        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();
+                            m_importPermutations.add(importPerm);
+                        }
+                        throw new ResolveException(
+                            "Constraint violation for package '"
+                            + entry.getKey() + "' when resolving module "
+                            + module + " between an imported constraint "
+                            + sourceBlame + " and an additional imported constraint "
+                            + blame, module, blame.m_reqs.get(0));
+                    }
+                }
+            }
+        }
+
+        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(
+                            "Constraint 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, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            for (Blame importBlame : entry.getValue())
+            {
+                if (!module.equals(importBlame.m_cap.getModule()))
+                {
+                    checkModules.add(importBlame.m_cap.getModule());
+                }
+
+                String pkgName = entry.getKey();
+                if (!pkgs.m_usedPkgs.containsKey(pkgName))
+                {
+                    continue;
+                }
+                for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
+                {
+                    if (!isCompatible(importBlame.m_cap, usedBlame.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(
+                                "Constraint violation for package '"
+                                + pkgName + "' when resolving module "
+                                + module + " between existing imported constraint "
+                                + importBlame + " and uses constraint "
+                                + usedBlame, null, null);
+
+                        mutated = (mutated != null)
+                            ? mutated
+                            : new HashSet();
+
+                        for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                        {
+                            Requirement req = usedBlame.m_reqs.get(reqIdx);
 
-        // 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 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 = importBlame.m_reqs.get(0);
+                    if (!mutated.contains(req))
                     {
-                        if (isSubsetPermutation(m_candidatePermutations.get(permIdx), copy))
+                        Set<Capability> candidates = candidateMap.get(req);
+                        if (candidates.size() > 1)
                         {
-                            isSubset = true;
+                            Map<Requirement, Set<Capability>> importPerm =
+                                copyCandidateMap(candidateMap);
+                            candidates = importPerm.get(req);
+                            Iterator it = candidates.iterator();
+                            it.next();
+                            it.remove();
+                            m_importPermutations.add(importPerm);
                         }
                     }
-                    if (!isSubset)
+
+                    if (mutated.size() > 0)
                     {
-//System.out.println("+++ SETTING "
-//    + currentBlame.m_reqs.get(reqIdx)
-//    + " CANDS FROM " + candidates + " TO " + candCopy);
-                        m_candidatePermutations.add(0, copy);
+                        m_usesPermutations.add(copyConflict);
                     }
+
+                    throw rethrow;
                 }
             }
         }
 
-        throw new ResolveException(
-            "Constraint violation for package '"
-            + pkgName + "' when resolving module "
-            + current + " between existing constraint "
-            + currentBlame + " and candidate constraint "
-            + candBlame, null, null);
+        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 static boolean isSubsetPermutation(
-        Map<Requirement, Set<Capability>> orig, Map<Requirement, Set<Capability>> copy)
+    private void removeInvalidateCandidate(
+        Module invalid, Map<Capability, Set<Requirement>> capDepSet,
+        Map<Requirement, Set<Capability>> candidateMap)
     {
         if (m_isInvokeCount)
         {
@@ -1275,22 +1158,109 @@ dumpModulePkgs(current, currentPkgs);
             m_invokeCounts.put(methodName, count);
         }
 
-        for (Entry<Requirement, Set<Capability>> entry : orig.entrySet())
+        Set<Module> invalidated = new HashSet();
+
+        for (Requirement req : invalid.getRequirements())
         {
-            Set<Capability> copyCands = copy.get(entry.getKey());
-            if (copyCands == null)
+            candidateMap.remove(req);
+        }
+
+        boolean wasRequired = false;
+
+        for (Capability cap : invalid.getCapabilities())
+        {
+            Set<Requirement> reqs = capDepSet.remove(cap);
+            if (reqs == null)
             {
-                return false;
+                continue;
             }
-            if (!entry.getValue().containsAll(copyCands))
+            wasRequired = true;
+            for (Requirement req : reqs)
             {
-                return false;
+                Set<Capability> candidates = candidateMap.get(req);
+                candidates.remove(cap);
+                if (candidates.size() == 0)
+                {
+                    candidateMap.remove(req);
+                    invalidated.add(req.getModule());
+                }
             }
         }
-        return true;
+
+        if (!wasRequired)
+        {
+            throw new ResolveException(
+                "Unable to resolve module", invalid, null);
+        }
+
+        for (Module m : invalidated)
+        {
+            removeInvalidateCandidate(m, capDepSet, candidateMap);
+        }
     }
 
-    private static boolean isCompatible(
+    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();
+
+        if (caps.size() > 0)
+        {
+            for (int i = 0; i < caps.size(); i++)
+            {
+// 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)))
+                {
+                    packages.m_exportedPkgs.put(
+                        (String) caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue(),
+                        new Blame(caps.get(i), null));
+                }
+            }
+        }
+
+        modulePkgMap.put(module, packages);
+    }
+
+    private static boolean hasOverlappingImport(Module module, Capability cap)
+    {
+        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);
+        }
+
+        List<Requirement> reqs = module.getRequirements();
+        for (int i = 0; i < reqs.size(); i++)
+        {
+            if (reqs.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+                && CapabilitySet.matches(cap, reqs.get(i).getFilter()))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isCompatible(
         Capability currentCap, Capability candCap, Map<Module, Packages> modulePkgMap)
     {
         if (m_isInvokeCount)
@@ -1303,25 +1273,54 @@ dumpModulePkgs(current, currentPkgs);
 
         if ((currentCap != null) && (candCap != null))
         {
+            if (currentCap.equals(candCap))
+            {
+                return true;
+            }
+
             List<Capability> currentSources =
                 getPackageSources(
                     currentCap,
-                    modulePkgMap,
-                    new ArrayList<Capability>(),
-                    new HashSet<Capability>());
+                    modulePkgMap);
             List<Capability> candSources =
                 getPackageSources(
                     candCap,
-                    modulePkgMap,
-                    new ArrayList<Capability>(),
-                    new HashSet<Capability>());
+                    modulePkgMap);
 //System.out.println("+++ currentSources " + currentSources + " - candSources " + candSources);
             return currentSources.containsAll(candSources) || candSources.containsAll(currentSources);
         }
         return true;
     }
 
-    private static List<Capability> getPackageSources(
+    private Map<Capability, List<Capability>> m_packageSourcesCache = new HashMap();
+
+    private List<Capability> getPackageSources(
+        Capability cap, 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);
+        }
+
+        if (cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+        {
+            List<Capability> sources = m_packageSourcesCache.get(cap);
+            if (sources == null)
+            {
+                sources = getPackageSourcesInternal(
+                    cap, modulePkgMap, new ArrayList(), new HashSet());
+                m_packageSourcesCache.put(cap, sources);
+            }
+            return sources;
+        }
+
+        return Collections.emptyList();
+    }
+
+    private static List<Capability> getPackageSourcesInternal(
         Capability cap, Map<Module, Packages> modulePkgMap, List<Capability> sources,
         Set<Capability> cycleMap)
     {
@@ -1363,7 +1362,7 @@ dumpModulePkgs(current, currentPkgs);
             {
                 for (Blame blame : required)
                 {
-                    getPackageSources(blame.m_cap, modulePkgMap, sources, cycleMap);
+                    getPackageSourcesInternal(blame.m_cap, modulePkgMap, sources, cycleMap);
                 }
             }
         }
@@ -1471,30 +1470,37 @@ dumpModulePkgs(current, currentPkgs);
         List<Wire> packageWires = new ArrayList<Wire>();
 
         Packages pkgs = modulePkgMap.get(module);
-        for (Entry<String, Blame> entry : pkgs.m_importedPkgs.entrySet())
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
         {
-            if (!entry.getValue().m_cap.getModule().isResolved())
+            for (Blame blame : entry.getValue())
             {
-                populateWireMap(entry.getValue().m_cap.getModule(), modulePkgMap, wireMap,
-                    candidateMap);
-            }
+                // Ignore modules that import themselves.
+                if (!module.equals(blame.m_cap.getModule())
+                    && blame.m_cap.getAttribute(
+                        Capability.PACKAGE_ATTR).getValue().equals(pkgName))
+                {
+                    if (!blame.m_cap.getModule().isResolved())
+                    {
+                        populateWireMap(blame.m_cap.getModule(), modulePkgMap, wireMap,
+                            candidateMap);
+                    }
 
-            // Ignore modules that import themselves.
-            if (!module.equals(entry.getValue().m_cap.getModule())
-                && entry.getValue().m_cap.getAttribute(
-                    Capability.PACKAGE_ATTR).getValue().equals(pkgName))
-            {
-                List<Attribute> attrs = new ArrayList();
-                attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
-                packageWires.add(
-                    new WireImpl(
-                        module,
-                        // 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),
-                        entry.getValue().m_cap.getModule(),
-                        entry.getValue().m_cap));
+                    List<Attribute> attrs = new ArrayList();
+                    attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
+                    packageWires.add(
+                        new WireImpl(
+                            module,
+                            // We need an unique requirement here or else subsequent
+                            // dynamic imports for the same dynamic requirement will
+                            // conflict with previous ones.
+                            new RequirementImpl(
+                                module,
+                                Capability.PACKAGE_NAMESPACE,
+                                new ArrayList(0),
+                                attrs),
+                            blame.m_cap.getModule(),
+                            blame.m_cap));
+                }
             }
         }
 
@@ -1503,16 +1509,74 @@ dumpModulePkgs(current, currentPkgs);
         return wireMap;
     }
 
+    private static void dumpCandidateMap(
+        ResolverState state, Map<Requirement, Set<Capability>> candidateMap)
+    {
+        System.out.println("=== BEGIN CANDIDATE MAP ===");
+        for (Module module : ((ResolverStateImpl) state).getModules())
+        {
+            System.out.println("  " + module
+                 + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
+            for (Requirement req : module.getRequirements())
+            {
+                Set<Capability> candidates = candidateMap.get(req);
+                if ((candidates != null) && (candidates.size() > 0))
+                {
+                        System.out.println("    " + req + ": " + candidates);
+                }
+            }
+            for (Requirement req : module.getDynamicRequirements())
+            {
+                Set<Capability> candidates = candidateMap.get(req);
+                if ((candidates != null) && (candidates.size() > 0))
+                {
+                    System.out.println("    " + req + ": " + candidates);
+                }
+            }
+        }
+        System.out.println("=== END CANDIDATE MAP ===");
+    }
+
+    private static void dumpModulePkgMap(Map<Module, Packages> modulePkgMap)
+    {
+        System.out.println("+++MODULE PKG MAP+++");
+        for (Entry<Module, Packages> entry : modulePkgMap.entrySet())
+        {
+            dumpModulePkgs(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private static void dumpModulePkgs(Module module, Packages packages)
+    {
+        System.out.println(module + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
+        System.out.println("  EXPORTED");
+        for (Entry<String, Blame> entry : packages.m_exportedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  IMPORTED");
+        for (Entry<String, List<Blame>> entry : packages.m_importedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  REQUIRED");
+        for (Entry<String, List<Blame>> entry : packages.m_requiredPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  USED");
+        for (Entry<String, List<Blame>> entry : packages.m_usedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+    }
+
     private static class Packages
     {
-        public final Map<String, Blame> m_exportedPkgs
-            = new HashMap<String, Blame>();
-        public final Map<String, Blame> m_importedPkgs
-            = new HashMap<String, Blame>();
-        public final Map<String, List<Blame>> m_requiredPkgs
-            = new HashMap<String, List<Blame>>();
-        public final Map<String, List<Blame>> m_usedPkgs
-            = new HashMap<String, List<Blame>>();
+        public final Map<String, Blame> m_exportedPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_importedPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_requiredPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_usedPkgs = new HashMap();
 
         public Packages()
         {
@@ -1555,22 +1619,22 @@ dumpModulePkgs(current, currentPkgs);
 
     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)