Bug 123648 Need a way to allow Eclipse Resolver to resolve to lowest version number
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/Resolver.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/Resolver.java
index 499186e..fd04b5f 100644
--- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/Resolver.java
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/service/resolver/Resolver.java
@@ -10,6 +10,7 @@
  *******************************************************************************/
 package org.eclipse.osgi.service.resolver;
 
+import java.util.Comparator;
 import java.util.Dictionary;
 /**
  * An implementation of a resolver which resolves the constraints of the bundles
@@ -113,4 +114,32 @@
 	 * a value of <code>null</code> is returned if none is available.
 	 */
 	public ExportPackageDescription resolveDynamicImport(BundleDescription importingBundle, String requestedPackage);
+
+	/**
+	 * Sets the selection policy for this resolver.  A selection policy is used to sort 
+	 * possible suppliers of a version constraint in descending order.  That is an order
+	 * which is from most desired to least desired.  The objects passed to the 
+	 * selection policy {@link Comparator#compare(Object, Object)} method 
+	 * will be of type {@link BaseDescription}.  The selection policy should return a 
+	 * negative number, zero, or a positive number depending on if the first object is 
+	 * more desired, equal amount of desire, or less desired than the second object respectively.
+	 * <p>
+	 * If no selection policy is set then a default policy will be used which sorts according
+	 * to the following rules: 
+	 * <ol>
+	 * <li> The resolution status of the bundle which supplies the base description.  Resolved bundles take priority over unresolved ones.
+	 * <li> The version of the base description.  Higher versions take priority over lower versions.
+	 * <li> The bundle ID which supplies the base description.  Lower IDs take priority over higher IDs. 
+	 * </ol>
+	 * @param selectionPolicy the selection policy for this resolver
+	 * @since 3.2
+	 */
+	public void setSelectionPolicy(Comparator selectionPolicy);
+
+	/**
+	 * Returns the selection policy for this resolver or null if it is not set 
+	 * @return the selection policy for this resolver or null if it is not set
+	 * @since 3.2 
+	 */
+	public Comparator getSelectionPolicy();
 }
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
index e24b614..a6133cc 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
@@ -55,6 +55,7 @@
 	// Keys are BundleDescriptions, values are ResolverBundles
 	private HashMap bundleMapping = null;
 	private GroupingChecker groupingChecker;
+	private Comparator selectionPolicy;
 
 	public ResolverImpl(BundleContext context, boolean checkPermissions) {
 		this.permissionChecker = new PermissionChecker(context, checkPermissions, this);
@@ -66,8 +67,8 @@
 
 	// Initializes the resolver
 	private void initialize() {
-		resolverExports = new VersionHashMap();
-		resolverBundles = new VersionHashMap();
+		resolverExports = new VersionHashMap(this);
+		resolverBundles = new VersionHashMap(this);
 		unresolvedBundles = new ArrayList();
 		bundleMapping = new HashMap();
 		BundleDescription[] bundles = state.getBundles();
@@ -268,8 +269,13 @@
 					BundleDescription sameNameDesc = ((ResolverBundle) sameName[j]).getBundle();
 					if (sameName[j] == bundle || !sameNameDesc.isSingleton() || rejectedSingletons.contains(sameNameDesc))
 						continue; // Ignore the bundle we are selecting, non-singletons and rejected singletons
-					// if the bundle sameNameDesc is resolved or has a greater version then reject the other bundle 
-					if (sameNameDesc.isResolved() || sameNameDesc.getVersion().compareTo(bundle.getBundle().getVersion()) > 0) {
+					boolean rejectedPolicy;
+					if (selectionPolicy == null)
+						// if the bundle sameNameDesc is resolved or has a greater version then reject the other bundle
+						rejectedPolicy = sameNameDesc.isResolved() || sameNameDesc.getVersion().compareTo(bundle.getBundle().getVersion()) > 0;
+					else
+						rejectedPolicy = selectionPolicy.compare(sameNameDesc, bundle.getBundle()) < 0;
+					if (rejectedPolicy) {
 						rejectedSingletons.add(bundle.getBundle());
 						return;
 					}
@@ -430,7 +436,8 @@
 					if (sameName[j] == bundles[i] || !sameNameDesc.isSingleton() || !sameNameDesc.isResolved() || rejectedSingletons.contains(sameNameDesc))
 						continue; // Ignore the bundle we are selecting, non-singletons, and non-resolved
 					result = true;
-					if (sameNameDesc.getVersion().compareTo(bundleDesc.getVersion()) > 0 && sameNameBundle.getRefs() >= bundles[i].getRefs()) {
+					boolean rejectedPolicy = selectionPolicy != null ? selectionPolicy.compare(sameNameDesc, bundleDesc) < 0 : sameNameDesc.getVersion().compareTo(bundleDesc.getVersion()) > 0;
+					if (rejectedPolicy && sameNameBundle.getRefs() >= bundles[i].getRefs()) {
 						// this bundle is not selected; add it to the rejected list
 						if (!rejectedSingletons.contains(bundles[i].getBundle()))
 							rejectedSingletons.add(bundles[i].getBundle());
@@ -1122,4 +1129,12 @@
 	VersionHashMap getResolverExports() {
 		return resolverExports;
 	}
+
+	public void setSelectionPolicy(Comparator selectionPolicy) {
+		this.selectionPolicy = selectionPolicy;
+	}
+
+	public Comparator getSelectionPolicy() {
+		return selectionPolicy;
+	}
 }
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/VersionHashMap.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/VersionHashMap.java
index 341e94a..5545306 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/VersionHashMap.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/VersionHashMap.java
@@ -13,6 +13,11 @@
 import java.util.*;
 
 public class VersionHashMap extends MappedList implements Comparator {
+	private ResolverImpl resolver;
+
+	public VersionHashMap(ResolverImpl resolver) {
+		this.resolver = resolver;
+	}
 
 	// sorts using the Comparator#compare method to sort
 	protected void sort(Object[] values) {
@@ -93,6 +98,9 @@
 			throw new IllegalArgumentException();
 		VersionSupplier vs1 = (VersionSupplier) o1;
 		VersionSupplier vs2 = (VersionSupplier) o2;
+		// if the selection policy is set then use that
+		if (resolver.getSelectionPolicy() != null)
+			return resolver.getSelectionPolicy().compare(vs1.getBaseDescription(), vs2.getBaseDescription());
 		if (vs1.getBundle().isResolved() != vs2.getBundle().isResolved())
 			return vs1.getBundle().isResolved() ? -1 : 1;
 		int versionCompare = -(vs1.getVersion().compareTo(vs2.getVersion()));