Modifed the resolver to try to be a little smarter in how it permutates
among potential candidates. (FELIX-961)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@748595 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/CandidateSet.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/CandidateSet.java
new file mode 100644
index 0000000..3a5bb7d
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/CandidateSet.java
@@ -0,0 +1,38 @@
+package org.apache.felix.framework.searchpolicy;
+
+import org.apache.felix.moduleloader.IModule;
+import org.apache.felix.moduleloader.IRequirement;
+
+class CandidateSet
+{
+    public static final int NORMAL = 0;
+    public static final int FRAGMENT = 1;
+    public static final int HOST = 2;
+    public final int m_type;
+    public final IModule m_module;
+    public final IRequirement m_requirement;
+    public final PackageSource[] m_candidates;
+    public final IModule[] m_modules;
+    public int m_idx = 0;
+    public int m_rotated = 0;
+
+    public CandidateSet(int type, IModule module, IRequirement requirement, PackageSource[] candidates)
+    {
+        super();
+        m_type = type;
+        m_module = module;
+        m_requirement = requirement;
+        m_candidates = candidates;
+        m_modules = null;
+    }
+
+    public CandidateSet(int type, IModule module, IRequirement requirement, IModule[] fragments)
+    {
+        super();
+        m_type = type;
+        m_module = module;
+        m_requirement = requirement;
+        m_candidates = null;
+        m_modules = fragments;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
index 3421198..17d5033 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
@@ -30,13 +30,15 @@
  */
 class ResolvedPackage
 {
-    public String m_name = null;
-    public List m_sourceList = new ArrayList();
+    public final String m_name;
+    public final CandidateSet m_cs;
+    public final List m_sourceList = new ArrayList();
 
-    public ResolvedPackage(String name)
+    public ResolvedPackage(String name, CandidateSet cs)
     {
         super();
         m_name = name;
+        m_cs = cs;
     }
 
     public boolean isSubset(ResolvedPackage rp)
@@ -55,7 +57,7 @@
 
     public Object clone()
     {
-        ResolvedPackage rp = new ResolvedPackage(m_name);
+        ResolvedPackage rp = new ResolvedPackage(m_name, m_cs);
         rp.m_sourceList.addAll(m_sourceList);
         return rp;
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
index 4d3224a..ff59a11 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
@@ -19,6 +19,8 @@
 package org.apache.felix.framework.searchpolicy;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -538,6 +540,12 @@
         }
     }
 
+    // This flag indicates whether candidates have been rotated due to a
+    // "uses" constraint conflict. If so, then it is not necessary to perform
+    // a permutation, since rotating the candidates selected a new permutation.
+    // This part of an attempt to perform smarter permutations.
+    private boolean m_candidatesRotated = false;
+
     private void findConsistentClassSpace(
         ResolverState state, Map candidatesMap, IModule rootModule)
         throws ResolveException
@@ -569,12 +577,59 @@
                 for (Iterator iter = candidatesMap.entrySet().iterator();
                     iter.hasNext(); )
                 {
-                    candidatesList.add(((Map.Entry) iter.next()).getValue());
+                    Map.Entry entry = (Map.Entry) iter.next();
+                    candidatesList.add(entry.getValue());
                 }
+
+                // Sort the bundles candidate sets according to a weighting
+                // based on how many multi-candidate requirements each has.
+                // The idea is to push bundles with more potential candidate
+                // permutations to the front so we can permutate over them
+                // more quickly, since they are likely to have more issues.
+                Collections.sort(candidatesList, new Comparator() {
+                    public int compare(Object o1, Object o2)
+                    {
+                        int w1 = calculateWeight((List) o1);
+                        int w2 = calculateWeight((List) o2);
+                        if (w1 < w2)
+                        {
+                            return -1;
+                        }
+                        else if (w1 > w2)
+                        {
+                            return 1;
+                        }
+                        return 0;
+                    }
+
+                    private int calculateWeight(List candSetList)
+                    {
+                        int weight = 0;
+                        for (int csIdx = 0; csIdx < candSetList.size(); csIdx++)
+                        {
+                            CandidateSet cs = (CandidateSet) candSetList.get(csIdx);
+                            if ((cs.m_candidates != null) && (cs.m_candidates.length > 1))
+                            {
+                                weight += cs.m_candidates.length;
+                            }
+                        }
+                        return -weight;
+                    }
+                });
             }
 
-            // Increment the candidate configuration so we can test again.
-            incrementCandidateConfiguration(candidatesList);
+            // Increment the candidate configuration to a new permutation so
+            // we can test again, unless some candidates have been rotated.
+            // In that case, we re-test the current permutation, since rotating
+            // the candidates effectively selects a new permutation.
+            if (!m_candidatesRotated)
+            {
+                incrementCandidateConfiguration(candidatesList);
+            }
+            else
+            {
+                m_candidatesRotated = false;
+            }
 
             // Clear the module map.
             moduleMap.clear();
@@ -840,6 +895,29 @@
                             "Constraint violation for " + targetModule
                             + " detected; module can see "
                             + rp + " and " + rpUses);
+
+                        // If the resolved package has a candidate set, then
+                        // attempt to directly rotate the candidates to fix the
+                        // "uses" constraint conflict. The idea is rather than
+                        // blinding incrementing to the next permutation, we will
+                        // try to target the permutation to the bundle with a
+                        // conflict, which in some cases will be smarter. Only
+                        // rotate the candidates if we have more than one and we
+                        // haven't already rotated them completely.
+                        if ((rp.m_cs != null) && (rp.m_cs.m_candidates.length > 1)
+                            && (rp.m_cs.m_rotated < rp.m_cs.m_candidates.length))
+                        {
+                            // Rotate candidates.
+                            PackageSource first = rp.m_cs.m_candidates[0];
+                            for (int i = 1; i < rp.m_cs.m_candidates.length; i++)
+                            {
+                                rp.m_cs.m_candidates[i - 1] = rp.m_cs.m_candidates[i];
+                            }
+                            rp.m_cs.m_candidates[rp.m_cs.m_candidates.length - 1] = first;
+                            rp.m_cs.m_rotated++;
+                            m_candidatesRotated = true;
+                        }
+
                         return false;
                     }
                 }
@@ -1056,7 +1134,7 @@
                 String pkgName = (String)
                     ps.m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
 
-                ResolvedPackage rp = new ResolvedPackage(pkgName);
+                ResolvedPackage rp = new ResolvedPackage(pkgName, cs);
                 rp.m_sourceList.add(ps);
                 pkgMap.put(rp.m_name, rp);
             }
@@ -1082,7 +1160,7 @@
                 String pkgName = (String)
                     wires[wireIdx].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY);
                 ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
-                rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+                rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
                 rp.m_sourceList.add(new PackageSource(wires[wireIdx].getExporter(), wires[wireIdx].getCapability()));
                 pkgMap.put(rp.m_name, rp);
             }
@@ -1106,7 +1184,7 @@
                 String pkgName = (String)
                     caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
                 ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
-                rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+                rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
                 rp.m_sourceList.add(new PackageSource(targetModule, caps[capIdx]));
                 pkgMap.put(rp.m_name, rp);
             }
@@ -1361,7 +1439,7 @@
                 String pkgName = (String)
                     candCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
                 ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
-                rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+                rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
                 rp.m_sourceList.add(new PackageSource(psTarget.m_module, candCaps[capIdx]));
                 pkgMap.put(rp.m_name, rp);
             }
@@ -1477,7 +1555,7 @@
                 String pkgName = (String)
                     caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
                 ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
-                rp = (rp == null) ? new ResolvedPackage(pkgName) : rp;
+                rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
                 rp.m_sourceList.add(new PackageSource(targetModule, caps[i]));
                 pkgMap.put(rp.m_name, rp);
             }
@@ -1719,36 +1797,4 @@
         PackageSource[] getResolvedCandidates(IRequirement req);
         PackageSource[] getUnresolvedCandidates(IRequirement req);
     }
-
-    private static class CandidateSet
-    {
-        public static final int NORMAL = 0;
-        public static final int FRAGMENT = 1;
-        public static final int HOST = 2;
-
-        public final int m_type;
-        public final IModule m_module;
-        public final IRequirement m_requirement;
-        public final PackageSource[] m_candidates;
-        public final IModule[] m_modules;
-        public int m_idx = 0;
-
-        public CandidateSet(int type, IModule module, IRequirement requirement, PackageSource[] candidates)
-        {
-            m_type = type;
-            m_module = module;
-            m_requirement = requirement;
-            m_candidates = candidates;
-            m_modules = null;
-        }
-
-        public CandidateSet(int type, IModule module, IRequirement requirement, IModule[] fragments)
-        {
-            m_type = type;
-            m_module = module;
-            m_requirement = requirement;
-            m_candidates = null;
-            m_modules = fragments;
-        }
-    }
 }
\ No newline at end of file