Bug 530470 - Restored complex version range match expressions

- Removed the use of the match (~=) operator against a VersionRange
object and the use of a single pre-built expression.

- Restored the use of comparison operators against Version objects and
the use of multiple pre-built expressions for all version range
possibilities.

- This is a partial revert of Bug 528494

Change-Id: I353ca42c25111ca2f1ed8d7be5166e872b2e57bd
Signed-off-by: Todor Boev <rinsvind@gmail.com>
diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/PermissiveSlicer.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/PermissiveSlicer.java
index b5e7985..8f1c9ab 100644
--- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/PermissiveSlicer.java
+++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/PermissiveSlicer.java
@@ -49,7 +49,7 @@
 				return false;
 
 		if (considerOnlyStrictDependency) {
-			if (!RequiredCapability.isVersionStrict(req.getMatches()))
+			if (!RequiredCapability.isStrictVersionRequirement(req.getMatches()))
 				return false;
 		}
 
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java
index c08af57..6c42119 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java
@@ -16,9 +16,14 @@
 import static org.eclipse.equinox.internal.p2.metadata.ProvidedCapability.MEMBER_NAME;
 import static org.eclipse.equinox.internal.p2.metadata.ProvidedCapability.MEMBER_NAMESPACE;
 import static org.eclipse.equinox.internal.p2.metadata.ProvidedCapability.MEMBER_VERSION;
+import static org.eclipse.equinox.p2.metadata.Version.MAX_VERSION;
+import static org.eclipse.equinox.p2.metadata.VersionRange.emptyRange;
 
+import java.util.Arrays;
+import java.util.List;
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.Version;
 import org.eclipse.equinox.p2.metadata.VersionRange;
 import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil;
 import org.eclipse.equinox.p2.metadata.expression.IExpression;
@@ -39,15 +44,41 @@
  * @see IInstallableUnit#NAMESPACE_IU_ID
  */
 public class RequiredCapability extends Requirement implements IRequiredCapability {
-	/**
-	 * Argument $0 must evaluate to a String
-	 * Argument $1 must evaluate to a String
-	 * Argument $2 must evaluate to a {@link VersionRange}
-	 */
-	private static final IExpression VERSION_RANGE_MATCH = ExpressionUtil.parse(
-			String.format("%s.exists(cap | cap.%s == $0 && cap.%s == $1 && cap.%s ~= $2)", //$NON-NLS-1$
+	private static final IExpression ALL = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1)", //$NON-NLS-1$
+					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE));
+
+	private static final IExpression STRICT = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1 && x.%s == $2)", //$NON-NLS-1$
 					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE, MEMBER_VERSION));
 
+	private static final IExpression OPEN_I = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1 && x.%s >= $2)", //$NON-NLS-1$
+					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE, MEMBER_VERSION));
+
+	private static final IExpression OPEN_N = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1 && x.%s > $2)", //$NON-NLS-1$
+					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE, MEMBER_VERSION));
+
+	private static final IExpression CLOSED_II = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1 && x.%s >= $2 && x.%s <= $3)", //$NON-NLS-1$
+					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE, MEMBER_VERSION, MEMBER_VERSION));
+
+	private static final IExpression CLOSED_IN = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1 && x.%s >= $2 && x.%s < $3)", //$NON-NLS-1$
+					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE, MEMBER_VERSION, MEMBER_VERSION));
+
+	private static final IExpression CLOSED_NI = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1 && x.%s > $2 && x.%s <= $3)", //$NON-NLS-1$
+					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE, MEMBER_VERSION, MEMBER_VERSION));
+
+	private static final IExpression CLOSED_NN = ExpressionUtil.parse(
+			String.format("%s.exists(x | x.%s == $0 && x.%s == $1 && x.%s > $2 && x.%s < $3)", //$NON-NLS-1$
+					MEMBER_PROVIDED_CAPABILITIES, MEMBER_NAME, MEMBER_NAMESPACE, MEMBER_VERSION, MEMBER_VERSION));
+
+	private static final List<IExpression> PREDEFINED = Arrays.asList(
+			ALL, STRICT, OPEN_I, OPEN_N, CLOSED_II, CLOSED_IN, CLOSED_NI, CLOSED_NN);
+
 	/**
 	 * TODO Remove. This is a private impl class. Users must call the analogous MetadataFactory.createRequirement()
 	 * @deprecated To be removed once CBI is fixed.
@@ -103,42 +134,83 @@
 	public static IMatchExpression<IInstallableUnit> createMatchExpressionFromRange(String namespace, String name, VersionRange range) {
 		Assert.isNotNull(namespace);
 		Assert.isNotNull(name);
-		Object resolvedRange = (range != null) ? range : VersionRange.emptyRange;
-		IExpressionFactory factory = ExpressionUtil.getFactory();
-		return factory.matchExpression(VERSION_RANGE_MATCH, name, namespace, resolvedRange);
-	}
 
-	public static String extractNamespace(IMatchExpression<IInstallableUnit> matchExpression) {
-		assertValid(matchExpression);
-		return (String) matchExpression.getParameters()[1];
+		IExpressionFactory factory = ExpressionUtil.getFactory();
+
+		// All versions
+		if (range == null || range.equals(emptyRange)) {
+			return factory.matchExpression(ALL, name, namespace);
+		}
+
+		// Exact version
+		if (range.getMinimum().equals(range.getMaximum())) {
+			return factory.matchExpression(STRICT, name, namespace, range.getMinimum());
+		}
+
+		// Open range
+		if (range.getMaximum().equals(MAX_VERSION)) {
+			// Open inclusive or non-inclusive
+			IExpression expr = range.getIncludeMinimum() ? OPEN_I : OPEN_N;
+			return factory.matchExpression(expr, name, namespace, range.getMinimum());
+		}
+
+		// Closed range
+		IExpression expr = range.getIncludeMinimum()
+				// Left inclusive. Right inclusive or non-inclusive
+				? (range.getIncludeMaximum() ? CLOSED_II : CLOSED_IN)
+				// Left non-inclusive. Right inclusive or non-inclusive
+				: (range.getIncludeMaximum() ? CLOSED_NI : CLOSED_NN);
+		return factory.matchExpression(expr, name, namespace, range.getMinimum(), range.getMaximum());
 	}
 
 	public static String extractName(IMatchExpression<IInstallableUnit> matchExpression) {
-		assertValid(matchExpression);
+		assertVersionRangeRequirement(matchExpression);
 		return (String) matchExpression.getParameters()[0];
 	}
 
-	public static VersionRange extractRange(IMatchExpression<IInstallableUnit> matchExpression) {
-		assertValid(matchExpression);
-		Object[] params = matchExpression.getParameters();
-		return (VersionRange) params[2];
+	public static String extractNamespace(IMatchExpression<IInstallableUnit> matchExpression) {
+		assertVersionRangeRequirement(matchExpression);
+		return (String) matchExpression.getParameters()[1];
 	}
 
-	public static boolean isVersionStrict(IMatchExpression<IInstallableUnit> matchExpression) {
-		if (!isVersionRangeRequirement(matchExpression)) {
-			return false;
-		}
+	public static VersionRange extractRange(IMatchExpression<IInstallableUnit> matchExpression) {
+		assertVersionRangeRequirement(matchExpression);
 
+		IExpression expr = ExpressionUtil.getOperand(matchExpression);
 		Object[] params = matchExpression.getParameters();
-		VersionRange range = (VersionRange) params[2];
-		return range.getMinimum().equals(range.getMaximum());
+
+		switch (params.length) {
+			// No version parameter
+			case 2 :
+				return emptyRange;
+			// One version parameter: strict or one of the open ranges
+			case 3 :
+				Version v = (Version) params[2];
+				if (expr.equals(STRICT)) {
+					return new VersionRange(v, true, v, true);
+				}
+				return new VersionRange(v, expr.equals(OPEN_I), MAX_VERSION, true);
+			// Two version parameters: one of the closed ranges
+			default :
+				Version left = (Version) params[2];
+				boolean leftInclusive = expr.equals(CLOSED_II) || expr.equals(CLOSED_IN);
+
+				Version right = (Version) params[3];
+				boolean rightInclusive = expr.equals(CLOSED_II) || expr.equals(CLOSED_NI);
+
+				return new VersionRange(left, leftInclusive, right, rightInclusive);
+		}
+	}
+
+	public static boolean isStrictVersionRequirement(IMatchExpression<IInstallableUnit> matchExpression) {
+		return STRICT == ExpressionUtil.getOperand(matchExpression);
 	}
 
 	public static boolean isVersionRangeRequirement(IMatchExpression<IInstallableUnit> matchExpression) {
-		return VERSION_RANGE_MATCH.equals(ExpressionUtil.getOperand(matchExpression));
+		return PREDEFINED.contains(ExpressionUtil.getOperand(matchExpression));
 	}
 
-	private static void assertValid(IMatchExpression<IInstallableUnit> matchExpression) {
+	private static void assertVersionRangeRequirement(IMatchExpression<IInstallableUnit> matchExpression) {
 		if (!isVersionRangeRequirement(matchExpression)) {
 			throw new IllegalArgumentException();
 		}