Bug 544220 - Huge performance impact due to product EE as required
capability

Add special handling for cap.namespace == 'osgi.ee'.

Change-Id: I957d401c3f043daf40db13397829993fe3fc5dc2
Signed-off-by: Sebastian Ratz <sebastian.ratz@sap.com>
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/index/CapabilityIndex.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/index/CapabilityIndex.java
index 6fa4cc9..e742b63 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/index/CapabilityIndex.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/index/CapabilityIndex.java
@@ -20,6 +20,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 import org.eclipse.equinox.internal.p2.metadata.IRequiredCapability;
 import org.eclipse.equinox.internal.p2.metadata.InstallableUnit;
 import org.eclipse.equinox.internal.p2.metadata.ProvidedCapability;
@@ -46,14 +47,20 @@
 @SuppressWarnings("unchecked")
 public class CapabilityIndex extends Index<IInstallableUnit> {
 
+	private static final String NAMESPACE_EXECUTION_ENVIRONMENT = "osgi.ee";
 	private final Map<String, Object> capabilityMap;
+	private final Set<IInstallableUnit> eeProvidersSet;
 
 	public CapabilityIndex(Iterator<IInstallableUnit> itor) {
 		HashMap<String, Object> index = new HashMap<>(300);
+		Set<IInstallableUnit> eeProviders = new HashSet<>(10);
 		while (itor.hasNext()) {
 			IInstallableUnit iu = itor.next();
 			Collection<IProvidedCapability> pcs = iu.getProvidedCapabilities();
 			for (IProvidedCapability pc : pcs) {
+				if (NAMESPACE_EXECUTION_ENVIRONMENT.equals(pc.getNamespace())) {
+					eeProviders.add(iu);
+				}
 				String name = pc.getName();
 				Object prev = index.put(name, iu);
 				if (prev == null || prev == iu)
@@ -70,6 +77,7 @@
 			}
 		}
 		this.capabilityMap = index;
+		this.eeProvidersSet = Collections.unmodifiableSet(eeProviders);
 	}
 
 	private Object getRequirementIDs(IEvaluationContext ctx, IExpression requirement, Object queriedKeys) {
@@ -162,6 +170,15 @@
 					//
 					LambdaExpression lambda = cf.lambda;
 					queriedKeys = getQueriedIDs(ctx, lambda.getItemVariable(), ProvidedCapability.MEMBER_NAME, lambda.getOperand(), queriedKeys);
+					if (queriedKeys == null) {
+						// Special handling to support
+						//     osgi.ee; (&(osgi.ee=JavaSE)(version=1.8))
+						//     providedCapabilities.exists(cap | cap.namespace == $0 && cap.properties ~= $1)
+						// in a performant way
+						if (NAMESPACE_EXECUTION_ENVIRONMENT.equals(getQueriedIDs(ctx, lambda.getItemVariable(), ProvidedCapability.MEMBER_NAMESPACE, lambda.getOperand(), queriedKeys))) {
+							return this.eeProvidersSet.iterator();
+						}
+					}
 				} else {
 					// Might be the requirements array.
 					//
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ql/PerformanceTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ql/PerformanceTest.java
index 6956350..473d5f4 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ql/PerformanceTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ql/PerformanceTest.java
@@ -15,8 +15,10 @@
 
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.equinox.internal.p2.director.PermissiveSlicer;
 import org.eclipse.equinox.internal.p2.director.QueryableArray;
@@ -32,6 +34,7 @@
 import org.eclipse.equinox.p2.metadata.VersionRange;
 import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil;
 import org.eclipse.equinox.p2.metadata.expression.IExpressionParser;
+import org.eclipse.equinox.p2.publisher.actions.JREAction;
 import org.eclipse.equinox.p2.query.IQuery;
 import org.eclipse.equinox.p2.query.IQueryResult;
 import org.eclipse.equinox.p2.query.IQueryable;
@@ -252,6 +255,44 @@
 		System.out.println();
 	}
 
+	public void testCapabilityQueryPerformanceEE() throws Exception {
+
+		IMetadataRepository repo = getMDR("/testData/galileoM7");
+
+		Version jreVersion = Version.parseVersion("1.8");
+		Map<String, Object> capAttrs = new HashMap<>();
+		capAttrs.put(JREAction.NAMESPACE_OSGI_EE, "JavaSE");
+		capAttrs.put(JREAction.VERSION_OSGI_EE, jreVersion);
+		IProvidedCapability jreCap = MetadataFactory.createProvidedCapability(JREAction.NAMESPACE_OSGI_EE, capAttrs);
+		IInstallableUnit jre8IU = createIU("a.jre.javase", jreVersion, new IProvidedCapability[] {jreCap});
+		repo.addInstallableUnits(Collections.singletonList(jre8IU));
+
+		jreVersion = Version.parseVersion("1.9");
+		capAttrs = new HashMap<>();
+		capAttrs.put(JREAction.NAMESPACE_OSGI_EE, "JavaSE");
+		capAttrs.put(JREAction.VERSION_OSGI_EE, jreVersion);
+		jreCap = MetadataFactory.createProvidedCapability(JREAction.NAMESPACE_OSGI_EE, capAttrs);
+		IInstallableUnit jre9IU = createIU("b.jre.anotherjavase", jreVersion, new IProvidedCapability[] {jreCap});
+		repo.addInstallableUnits(Collections.singletonList(jre9IU));
+
+		IRequirement capability = MetadataFactory.createRequirement(JREAction.NAMESPACE_OSGI_EE, "(&(osgi.ee=JavaSE)(version=1.8))", null, 0, 0, false);
+		IQuery<IInstallableUnit> capabilityQuery = QueryUtil.createMatchQuery(capability.getMatches());
+		IQueryResult<IInstallableUnit> result;
+		long tradQueryMS = 0;
+
+		for (int i = 0; i < 5; ++i) {
+			long start = System.currentTimeMillis();
+			for (int idx = 0; idx < 80; ++idx) {
+				result = repo.query(capabilityQuery, new NullProgressMonitor());
+				assertEquals(1, queryResultSize(result));
+				assertEquals(jre8IU, result.toUnmodifiableSet().iterator().next());
+			}
+			tradQueryMS += (System.currentTimeMillis() - start);
+		}
+		System.out.println("CapabilityQuery took: " + tradQueryMS + " milliseconds");
+		System.out.println();
+	}
+
 	public void testIUPropertyQueryPerformance() throws Exception {
 
 		IMetadataRepository repo = getMDR("/testData/galileoM7");