Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Hallgren2009-12-12 00:19:20 +0000
committerThomas Hallgren2009-12-12 00:19:20 +0000
commita37c758862531b18770cf25284a6dead2354a6a9 (patch)
treed882ec382771c87b2bdbdeb62dfd4ce27da698e7 /bundles/org.eclipse.equinox.p2.ql
parente5dd6d76fd742b19444a89fd132eb587835836ce (diff)
downloadrt.equinox.p2-a37c758862531b18770cf25284a6dead2354a6a9.tar.gz
rt.equinox.p2-a37c758862531b18770cf25284a6dead2354a6a9.tar.xz
rt.equinox.p2-a37c758862531b18770cf25284a6dead2354a6a9.zip
Bug 294691 - Create a QueryLanguage for p2 (attachment 153914)
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.ql')
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/All.java46
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/And.java73
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Array.java100
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Assignment.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/At.java85
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Binary.java47
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/BooleanFunction.java38
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CapabilityIndexFunction.java35
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ClassFunction.java39
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Collect.java70
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CollectionFilter.java73
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Compare.java71
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Condition.java58
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Constant.java93
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ContextExpression.java68
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Equals.java60
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Exists.java46
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Expression.java174
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ExpressionFactory.java228
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/FilterFunction.java43
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/First.java51
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Flatten.java37
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Function.java77
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/LambdaExpression.java121
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Latest.java93
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Limit.java96
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/MatchExpression.java64
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Matches.java130
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Member.java194
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/NAry.java87
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Not.java42
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Or.java70
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Parameter.java64
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/RangeFunction.java37
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Select.java50
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/SetFunction.java36
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Traverse.java64
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unary.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/UnaryCollectionFilter.java46
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unique.java84
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Variable.java87
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/VersionFunction.java37
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/WrappedIQuery.java74
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/ExpressionParser.java849
-rw-r--r--bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/IParserConstants.java75
45 files changed, 4108 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/All.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/All.java
new file mode 100644
index 000000000..73784486e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/All.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * A collection filter that yields true if the <code>filter</code> yields true for
+ * all of the elements of the <code>collection</code>
+ */
+final class All extends CollectionFilter {
+ All(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ public int getExpressionType() {
+ return TYPE_ALL;
+ }
+
+ Object evaluate(IEvaluationContext context, Iterator itor) {
+ Variable variable = lambda.getItemVariable();
+ while (itor.hasNext()) {
+ variable.setValue(context, itor.next());
+ if (lambda.evaluate(context) != Boolean.TRUE)
+ return Boolean.FALSE;
+ }
+ return Boolean.TRUE;
+ }
+
+ String getOperator() {
+ return KEYWORD_ALL;
+ }
+
+ boolean isBoolean() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/And.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/And.java
new file mode 100644
index 000000000..4ecf87efe
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/And.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * n-ary AND operator. This operator evaluates its first operand and then checks
+ * the class of the result. If the class is {@link Boolean} then it is assumed
+ * that all other operands also evaluates to a boolean and the full evaluation is
+ * <code>true</code> if all its operands evaluate to <code>true</code>. If the first
+ * result was not of class {@link Boolean}, then it is assumed that it can be accessed
+ * as an {@link Iterator} and that all other operands also evaluates to something that
+ * can be accessed as an {@link Iterator}. The AND operator will then function as a
+ * INTERSECT operator and the result is the set of elements that were found in all operands.
+ */
+final class And extends NAry {
+ And(Expression[] operands) {
+ super(assertLength(operands, 2, OPERATOR_AND));
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object firstValue = operands[0].evaluate(context);
+
+ // Determine operation mode
+ if (firstValue instanceof Boolean) {
+ // The first value was boolean. Assume that the rest are too
+ if (!((Boolean) firstValue).booleanValue())
+ return Boolean.FALSE;
+
+ for (int idx = 1; idx < operands.length; ++idx) {
+ if (operands[idx].evaluate(context) != Boolean.TRUE)
+ return Boolean.FALSE;
+ }
+ return Boolean.TRUE;
+ }
+
+ // Not a boolean. Assume that we can use an iterator on all values
+ Set resultSet = asSet(firstValue, false); // Safe since it will not be modified
+ for (int idx = 1; idx < operands.length && !resultSet.isEmpty(); ++idx) {
+ Iterator itor = operands[idx].evaluateAsIterator(context);
+ Set retained = new HashSet();
+ while (itor.hasNext()) {
+ Object value = itor.next();
+ if (resultSet.contains(value))
+ retained.add(value);
+ }
+ resultSet = retained;
+ }
+ return resultSet;
+ }
+
+ public int getExpressionType() {
+ return TYPE_AND;
+ }
+
+ String getOperator() {
+ return OPERATOR_AND;
+ }
+
+ int getPriority() {
+ return PRIORITY_AND;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Array.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Array.java
new file mode 100644
index 000000000..0ec90059b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Array.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.eclipse.equinox.internal.p2.ql.parser.IParserConstants;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An array of expressions
+ */
+final class Array extends NAry {
+ final class ArrayIterator implements Iterator {
+ private final IEvaluationContext context;
+
+ private int pos = -1;
+
+ public ArrayIterator(IEvaluationContext context) {
+ this.context = context;
+ }
+
+ public boolean hasNext() {
+ return pos + 1 < operands.length;
+ }
+
+ public Object next() {
+ if (++pos >= operands.length) {
+ --pos;
+ throw new NoSuchElementException();
+ }
+ return operands[pos].evaluate(context);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static void elementsToString(StringBuffer bld, Expression[] elements) {
+ int top = elements.length;
+ if (top > 0) {
+ elements[0].toString(bld);
+ for (int idx = 1; idx < top; ++idx) {
+ bld.append(", "); //$NON-NLS-1$
+ appendOperand(bld, elements[idx], PRIORITY_COMMA);
+ }
+ }
+ }
+
+ Array(Expression[] operands) {
+ super(assertLength(operands, 0, IParserConstants.OPERATOR_ARRAY));
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return evaluateAsIterator(context);
+ }
+
+ public Iterator evaluateAsIterator(IEvaluationContext context) {
+ return new ArrayIterator(context);
+ }
+
+ public int getExpressionType() {
+ return TYPE_ARRAY;
+ }
+
+ public void toString(StringBuffer bld) {
+ bld.append('[');
+ elementsToString(bld, operands);
+ bld.append(']');
+ }
+
+ String getOperator() {
+ return IParserConstants.OPERATOR_ARRAY;
+ }
+
+ int getPriority() {
+ return PRIORITY_CONSTRUCTOR;
+ }
+
+ boolean isBoolean() {
+ return false;
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+
+ boolean isElementBoolean() {
+ return super.isBoolean();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Assignment.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Assignment.java
new file mode 100644
index 000000000..23b69e8a7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Assignment.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression representing a variable stack in the current thread.
+ */
+class Assignment extends Binary {
+ Assignment(Variable variable, Expression expression) {
+ super(variable, expression);
+ }
+
+ public final Object evaluate(IEvaluationContext context) {
+ Object value = rhs.evaluate(context);
+ context.setValue(lhs, value);
+ return value;
+ }
+
+ public int getExpressionType() {
+ return TYPE_ASSIGNMENT;
+ }
+
+ int getPriority() {
+ return PRIORITY_ASSIGNMENT;
+ }
+
+ String getOperator() {
+ return OPERATOR_ASSIGN;
+ }
+
+ boolean isBoolean() {
+ return rhs.isBoolean();
+ }
+
+ boolean isCollection() {
+ return rhs.isCollection();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/At.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/At.java
new file mode 100644
index 000000000..f4ae7004c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/At.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.ql.expression.Member.DynamicMember;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.ITranslationSupport;
+
+/**
+ * This class represents indexed or keyed access to an indexed collection
+ * or a map.
+ */
+final class At extends Binary {
+ At(Expression lhs, Expression rhs) {
+ super(lhs, rhs);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object lval;
+ if (lhs instanceof DynamicMember) {
+ DynamicMember lm = (DynamicMember) lhs;
+ Object instance = lm.operand.evaluate(context);
+ if (instance instanceof IInstallableUnit) {
+ if ("properties".equals(lm.name)) //$NON-NLS-1$
+ // Avoid full copy of the properties map just to get one member
+ return ((IInstallableUnit) instance).getProperty((String) rhs.evaluate(context));
+
+ if (VARIABLE_TRANSLATIONS.equals(lm.name)) {
+ ITranslationSupport ts = (ITranslationSupport) Variable.TRANSLATIONS.evaluate(context);
+ return ts.getIUProperty((IInstallableUnit) instance, (String) rhs.evaluate(context));
+ }
+ }
+ lval = lm.invoke(instance);
+ } else
+ lval = lhs.evaluate(context);
+
+ Object rval = rhs.evaluate(context);
+ if (lval == null)
+ throw new IllegalArgumentException("Unable to use [] on null"); //$NON-NLS-1$
+
+ if (lval instanceof Map)
+ return ((Map) lval).get(rval);
+
+ if (rval instanceof Number) {
+ if (lval instanceof List)
+ return ((List) lval).get(((Number) rval).intValue());
+ if (lval != null && lval.getClass().isArray())
+ return ((Object[]) lval)[((Number) rval).intValue()];
+ }
+
+ if (lval instanceof Dictionary)
+ return ((Dictionary) lval).get(rval);
+
+ throw new IllegalArgumentException("Unable to use [] on a " + lval.getClass().getName()); //$NON-NLS-1$
+ }
+
+ public int getExpressionType() {
+ return TYPE_AT;
+ }
+
+ public void toString(StringBuffer bld) {
+ appendOperand(bld, lhs, getPriority());
+ bld.append('[');
+ appendOperand(bld, rhs, PRIORITY_COMMA);
+ bld.append(']');
+ }
+
+ String getOperator() {
+ return OPERATOR_AT;
+ }
+
+ int getPriority() {
+ return PRIORITY_MEMBER;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Binary.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Binary.java
new file mode 100644
index 000000000..ac17e0d49
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Binary.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IExpressionVisitor;
+
+/**
+ * The abstract base class for all binary operations
+ */
+abstract class Binary extends Expression {
+ final Expression lhs;
+
+ final Expression rhs;
+
+ Binary(Expression lhs, Expression rhs) {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return super.accept(visitor) && lhs.accept(visitor) && rhs.accept(visitor);
+ }
+
+ public void toString(StringBuffer bld) {
+ appendOperand(bld, lhs, getPriority());
+ bld.append(' ');
+ bld.append(getOperator());
+ bld.append(' ');
+ appendOperand(bld, rhs, getPriority());
+ }
+
+ int countReferenceToEverything() {
+ return lhs.countReferenceToEverything() + rhs.countReferenceToEverything();
+ }
+
+ int getPriority() {
+ return PRIORITY_BINARY; // Default priority
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/BooleanFunction.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/BooleanFunction.java
new file mode 100644
index 000000000..10268eb36
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/BooleanFunction.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+/**
+ * A function that obtains a class based on a String
+ */
+final class BooleanFunction extends Function {
+
+ public BooleanFunction(Expression[] operands) {
+ super(assertLength(operands, 1, 1, KEYWORD_BOOLEAN));
+ assertNotCollection(operands[0], "parameter"); //$NON-NLS-1$
+ }
+
+ boolean assertSingleArgumentClass(Object v) {
+ return v instanceof String || v instanceof Boolean;
+ }
+
+ Object createInstance(Object arg) {
+ if (arg instanceof String)
+ return Boolean.valueOf("true".equalsIgnoreCase((String) arg)); //$NON-NLS-1$
+ if (arg instanceof Boolean)
+ return arg;
+ return Boolean.FALSE;
+ }
+
+ String getOperator() {
+ return KEYWORD_BOOLEAN;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CapabilityIndexFunction.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CapabilityIndexFunction.java
new file mode 100644
index 000000000..bd01ae714
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CapabilityIndexFunction.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.internal.p2.ql.CapabilityIndex;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * A function that creates a {@link CapabilityIndex} based on a collection of
+ * {@link IInstallableUnit} instances.
+ */
+final class CapabilityIndexFunction extends Function {
+
+ public CapabilityIndexFunction(Expression[] operands) {
+ super(assertLength(operands, 1, 1, KEYWORD_CAPABILITY_INDEX));
+ assertNotBoolean(operands[0], "parameter"); //$NON-NLS-1$
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return new CapabilityIndex(operands[0].evaluateAsIterator(context));
+ }
+
+ String getOperator() {
+ return KEYWORD_CAPABILITY_INDEX;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ClassFunction.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ClassFunction.java
new file mode 100644
index 000000000..5fe40c0ac
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ClassFunction.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+/**
+ * A function that obtains a class based on a String
+ */
+final class ClassFunction extends Function {
+
+ public ClassFunction(Expression[] operands) {
+ super(assertLength(operands, 1, 1, KEYWORD_CLASS));
+ assertNotBoolean(operands[0], "parameter"); //$NON-NLS-1$
+ assertNotCollection(operands[0], "parameter"); //$NON-NLS-1$
+ }
+
+ boolean assertSingleArgumentClass(Object v) {
+ return v instanceof String;
+ }
+
+ Object createInstance(Object arg) {
+ try {
+ return Class.forName((String) arg);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ String getOperator() {
+ return KEYWORD_CLASS;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Collect.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Collect.java
new file mode 100644
index 000000000..1e858bcaf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Collect.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.IExpression;
+
+/**
+ */
+final class Collect extends CollectionFilter {
+ final class CollectIterator implements Iterator {
+ private final IEvaluationContext context;
+
+ private final IExpression variable;
+
+ private final Iterator innerIterator;
+
+ public CollectIterator(IEvaluationContext context, Iterator iterator) {
+ this.context = context;
+ this.variable = lambda.getItemVariable();
+ this.innerIterator = iterator;
+ }
+
+ public boolean hasNext() {
+ return innerIterator.hasNext();
+ }
+
+ public Object next() {
+ context.setValue(variable, innerIterator.next());
+ return lambda.evaluate(context);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ Collect(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ Object evaluate(IEvaluationContext context, Iterator itor) {
+ return evaluateAsIterator(context, itor);
+ }
+
+ Iterator evaluateAsIterator(IEvaluationContext context, Iterator itor) {
+ return new CollectIterator(context, itor);
+ }
+
+ public int getExpressionType() {
+ return TYPE_COLLECT;
+ }
+
+ String getOperator() {
+ return KEYWORD_COLLECT;
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CollectionFilter.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CollectionFilter.java
new file mode 100644
index 000000000..325f43f74
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/CollectionFilter.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.IExpressionVisitor;
+
+/**
+ * Some kind of operation that is performed for each element of a collection. I.e.
+ * <code>x.&lt;operation&gt;(y | &lt;expression&rt;)</code>
+ */
+abstract class CollectionFilter extends Unary {
+ static void appendProlog(StringBuffer bld, Expression lhs, String operator) {
+ if (lhs != Variable.EVERYTHING && lhs != Variable.ITEM) {
+ appendOperand(bld, lhs, PRIORITY_COLLECTION);
+ bld.append('.');
+ }
+ bld.append(operator);
+ bld.append('(');
+ }
+
+ final LambdaExpression lambda;
+
+ CollectionFilter(Expression collection, LambdaExpression lambda) {
+ super(collection);
+ this.lambda = lambda;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return super.accept(visitor) && lambda.accept(visitor);
+ }
+
+ public final Object evaluate(IEvaluationContext context) {
+ Iterator lval = operand.evaluateAsIterator(context);
+ context = lambda.prolog(context);
+ return evaluate(context, lval);
+ }
+
+ public final Iterator evaluateAsIterator(IEvaluationContext context) {
+ Iterator lval = operand.evaluateAsIterator(context);
+ context = lambda.prolog(context);
+ return evaluateAsIterator(context, lval);
+ }
+
+ public void toString(StringBuffer bld) {
+ appendProlog(bld, operand, getOperator());
+ appendOperand(bld, lambda, PRIORITY_LAMBDA);
+ bld.append(')');
+ }
+
+ int countReferenceToEverything() {
+ return super.countReferenceToEverything() + lambda.countReferenceToEverything();
+ }
+
+ abstract Object evaluate(final IEvaluationContext context, Iterator iterator);
+
+ Iterator evaluateAsIterator(IEvaluationContext context, Iterator iterator) {
+ throw new UnsupportedOperationException();
+ }
+
+ int getPriority() {
+ return PRIORITY_COLLECTION;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Compare.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Compare.java
new file mode 100644
index 000000000..f82f37a57
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Compare.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.internal.provisional.p2.metadata.Version;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * Comparisons for magnitude.
+ */
+final class Compare extends Binary {
+ final boolean compareLess;
+ final boolean equalOK;
+
+ Compare(Expression lhs, Expression rhs, boolean compareLess, boolean equalOK) {
+ super(lhs, rhs);
+ this.compareLess = compareLess;
+ this.equalOK = equalOK;
+ assertNotCollection(lhs, "lhs"); //$NON-NLS-1$
+ assertNotCollection(rhs, "rhs"); //$NON-NLS-1$
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object lval = lhs.evaluate(context);
+ Object rval = rhs.evaluate(context);
+ if (lval == null || rval == null)
+ throw new IllegalArgumentException("Cannot compare null to anything"); //$NON-NLS-1$
+
+ try {
+
+ if (lval.getClass() != rval.getClass()) {
+ if (lval instanceof Version && rval instanceof String)
+ rval = Version.create((String) rval);
+ else if (rval instanceof Version && lval instanceof String)
+ lval = Version.create((String) lval);
+ else if (lval instanceof String)
+ rval = rval.toString();
+ else if (rval instanceof String)
+ lval = lval.toString();
+ }
+
+ if (lval instanceof Comparable) {
+ int cmpResult = ((Comparable) lval).compareTo(rval);
+ return Boolean.valueOf(cmpResult == 0 ? equalOK : (cmpResult < 0 ? compareLess : !compareLess));
+ }
+ } catch (Exception e) {
+ //
+ }
+ throw new IllegalArgumentException("Cannot compare a " + lval.getClass().getName() + " to a " + rval.getClass().getName()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ public int getExpressionType() {
+ return compareLess ? (equalOK ? TYPE_LESS_EQUAL : TYPE_LESS) : (equalOK ? TYPE_GREATER_EQUAL : TYPE_GREATER);
+ }
+
+ String getOperator() {
+ return compareLess ? (equalOK ? OPERATOR_LT_EQUAL : OPERATOR_LT) : (equalOK ? OPERATOR_GT_EQUAL : OPERATOR_GT);
+ }
+
+ boolean isBoolean() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Condition.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Condition.java
new file mode 100644
index 000000000..543294be4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Condition.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * Comparisons for magnitude.
+ */
+final class Condition extends Binary {
+ final Expression ifFalse;
+
+ Condition(Expression test, Expression ifTrue, Expression ifFalse) {
+ super(test, ifTrue);
+ this.ifFalse = ifFalse;
+ assertNotCollection(test, "test"); //$NON-NLS-1$
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return lhs.evaluate(context) == Boolean.TRUE ? rhs.evaluate(context) : ifFalse.evaluate(context);
+ }
+
+ public int getExpressionType() {
+ return TYPE_CONDITION;
+ }
+
+ public void toString(StringBuffer bld) {
+ super.toString(bld);
+ bld.append(' ');
+ bld.append(OPERATOR_ELSE);
+ bld.append(' ');
+ appendOperand(bld, ifFalse, getPriority());
+ }
+
+ String getOperator() {
+ return OPERATOR_IF;
+ }
+
+ int getPriority() {
+ return PRIORITY_CONDITION;
+ }
+
+ boolean isBoolean() {
+ return rhs.isBoolean() && ifFalse.isBoolean();
+ }
+
+ boolean isCollection() {
+ return rhs.isCollection() && ifFalse.isCollection();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Constant.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Constant.java
new file mode 100644
index 000000000..fdb66aaa3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Constant.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.SimplePattern;
+
+/**
+ * An expression that represents a constant value.
+ */
+final class Constant extends Expression {
+ private final Object value;
+
+ static final Constant NULL_CONSTANT = new Constant(null);
+
+ static final Constant TRUE_CONSTANT = new Constant(Boolean.TRUE);
+
+ static final Constant FALSE_CONSTANT = new Constant(Boolean.FALSE);
+
+ static Constant create(Object value) {
+ if (value == null)
+ return NULL_CONSTANT;
+ if (value == Boolean.TRUE)
+ return TRUE_CONSTANT;
+ if (value == Boolean.FALSE)
+ return FALSE_CONSTANT;
+ return new Constant(value);
+ }
+
+ private Constant(Object value) {
+ this.value = value;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return value;
+ }
+
+ public int getExpressionType() {
+ return TYPE_LITERAL;
+ }
+
+ public void toString(StringBuffer bld) {
+ if (value == null)
+ bld.append("null"); //$NON-NLS-1$
+ else if (value instanceof String) {
+ String str = (String) value;
+ char sep = str.indexOf('\'') >= 0 ? '"' : '\'';
+ bld.append(sep);
+ bld.append(str);
+ bld.append(sep);
+ } else if (value instanceof SimplePattern) {
+ appendEscaped(bld, '/', value.toString());
+ } else
+ bld.append(value);
+ }
+
+ int getPriority() {
+ return PRIORITY_LITERAL;
+ }
+
+ private void appendEscaped(StringBuffer bld, char delimiter, String str) {
+ bld.append(delimiter);
+ int top = str.length();
+ for (int idx = 0; idx < top; ++idx) {
+ char c = str.charAt(idx);
+ if (c == delimiter)
+ bld.append('\\');
+ bld.append(c);
+ }
+ bld.append(delimiter);
+ }
+
+ boolean isBoolean() {
+ return value instanceof Boolean;
+ }
+
+ boolean isCollection() {
+ return value instanceof Collection || value instanceof Map || value instanceof Iterator || value != null && value.getClass().isArray();
+ }
+
+ String getOperator() {
+ return "<literal>"; //$NON-NLS-1$
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ContextExpression.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ContextExpression.java
new file mode 100644
index 000000000..f50b4043d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ContextExpression.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.internal.p2.ql.*;
+import org.eclipse.equinox.p2.ql.*;
+
+/**
+ * The context expression is the top expression in context queries. It introduces the
+ * variable 'everything' and initialized it with the iterator that represents all
+ * available items.
+ */
+final class ContextExpression extends Unary implements IContextExpression {
+
+ ContextExpression(Expression expression) {
+ super(expression);
+ }
+
+ public void toString(StringBuffer bld) {
+ operand.toString(bld);
+ }
+
+ public IEvaluationContext createContext(Class elementClass, Iterator iterator, Object[] params) {
+ IEvaluationContext context = new SingleVariableContext(new ParameterContext(params), Variable.EVERYTHING);
+ context.setValue(Variable.EVERYTHING, new Everything(elementClass, iterator, operand.needsRepeatedIterations()));
+ return context;
+ }
+
+ public IEvaluationContext createContext(Class elementClass, Iterator iterator, Object[] params, ITranslationSupport ts) {
+ IEvaluationContext context = new MultiVariableContext(new ParameterContext(params), new IExpression[] {Variable.EVERYTHING, Variable.TRANSLATIONS});
+ context.setValue(Variable.EVERYTHING, new Everything(elementClass, iterator, operand.needsRepeatedIterations()));
+ context.setValue(Variable.TRANSLATIONS, ts);
+ return context;
+ }
+
+ public int getExpressionType() {
+ return operand.getExpressionType();
+ }
+
+ String getOperator() {
+ throw new UnsupportedOperationException();
+ }
+
+ int getPriority() {
+ return operand.getPriority();
+ }
+
+ Expression pipeFrom(Expression expr) {
+ return new ContextExpression(operand.pipeFrom(expr));
+ }
+
+ boolean isBoolean() {
+ return operand.isBoolean();
+ }
+
+ boolean isCollection() {
+ return operand.isCollection();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Equals.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Equals.java
new file mode 100644
index 000000000..773fb75b4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Equals.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that performs the == and != comparisons
+ */
+final class Equals extends Binary {
+ final boolean negate;
+
+ Equals(Expression lhs, Expression rhs, boolean negate) {
+ super(lhs, rhs);
+ this.negate = negate;
+ assertNotCollection(lhs, "lhs"); //$NON-NLS-1$
+ assertNotCollection(rhs, "rhs"); //$NON-NLS-1$
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object lval = lhs.evaluate(context);
+ Object rval = rhs.evaluate(context);
+ boolean result;
+ if (lval == null || rval == null)
+ result = lval == rval;
+ else {
+ if (lval.getClass() != rval.getClass()) {
+ if (lval instanceof String)
+ rval = rval.toString();
+ else if (rval instanceof String)
+ lval = lval.toString();
+ }
+ result = lval.equals(rval);
+ }
+ if (negate)
+ result = !result;
+ return Boolean.valueOf(result);
+ }
+
+ public int getExpressionType() {
+ return negate ? TYPE_NOT_EQUALS : TYPE_EQUALS;
+ }
+
+ String getOperator() {
+ return negate ? OPERATOR_NOT_EQUALS : OPERATOR_EQUALS;
+ }
+
+ boolean isBoolean() {
+ return true;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Exists.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Exists.java
new file mode 100644
index 000000000..64a23d7f6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Exists.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * A collection filter that yields true if the <code>filter</code> yields true for
+ * any of the elements of the <code>collection</code>
+ */
+final class Exists extends CollectionFilter {
+ Exists(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ public int getExpressionType() {
+ return TYPE_EXISTS;
+ }
+
+ Object evaluate(IEvaluationContext context, Iterator itor) {
+ Variable variable = lambda.getItemVariable();
+ while (itor.hasNext()) {
+ variable.setValue(context, itor.next());
+ if (lambda.evaluate(context) == Boolean.TRUE)
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
+ String getOperator() {
+ return KEYWORD_EXISTS;
+ }
+
+ boolean isBoolean() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Expression.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Expression.java
new file mode 100644
index 000000000..7992dda5b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Expression.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.ql.IRepeatableIterator;
+import org.eclipse.equinox.internal.p2.ql.RepeatableIterator;
+import org.eclipse.equinox.internal.p2.ql.parser.IParserConstants;
+import org.eclipse.equinox.internal.provisional.p2.metadata.query.Collector;
+import org.eclipse.equinox.p2.ql.*;
+
+/**
+ * The base class of the expression tree.
+ */
+abstract class Expression implements IExpression, IParserConstants {
+
+ static final Expression[] emptyArray = new Expression[0];
+
+ static Set asSet(Object val, boolean forcePrivateCopy) {
+ if (val == null)
+ throw new IllegalArgumentException("Cannot convert null into an set"); //$NON-NLS-1$
+
+ if (val instanceof IRepeatableIterator) {
+ Object provider = ((IRepeatableIterator) val).getIteratorProvider();
+ if (!forcePrivateCopy) {
+ if (provider instanceof Set)
+ return (Set) provider;
+ if (provider instanceof Collector)
+ return (Set) ((Collector) provider).toCollection();
+ }
+
+ if (provider instanceof Collection)
+ val = provider;
+ } else {
+ if (!forcePrivateCopy) {
+ if (val instanceof Set)
+ return (Set) val;
+ if (val instanceof Collector)
+ return (Set) ((Collector) val).toCollection();
+ }
+ }
+
+ HashSet result;
+ if (val instanceof Collection)
+ result = new HashSet((Collection) val);
+ else {
+ result = new HashSet();
+ Iterator iterator = RepeatableIterator.create(val);
+ while (iterator.hasNext())
+ result.add(iterator.next());
+ }
+ return result;
+ }
+
+ /**
+ * Let the visitor visit this instance and all expressions that this
+ * instance contains.
+ * @param visitor The visiting visitor.
+ * @return <code>true</code> if the visitor should continue visiting, <code>false</code> otherwise.
+ */
+ public boolean accept(IExpressionVisitor visitor) {
+ return visitor.accept(this);
+ }
+
+ /**
+ * Evaluate this expression with given context and variables.
+ * @param context The evaluation context
+ * @return The result of the evaluation.
+ */
+ public abstract Object evaluate(IEvaluationContext context);
+
+ public Iterator evaluateAsIterator(IEvaluationContext context) {
+ Object value = evaluate(context);
+ if (!(value instanceof Iterator))
+ value = RepeatableIterator.create(value);
+ return (Iterator) value;
+ }
+
+ /**
+ * Checks if the expression will make repeated requests for the 'everything' iterator.
+ * @return <code>true</code> if repeated requests will be made, <code>false</code> if not.
+ */
+ public boolean needsRepeatedIterations() {
+ return countReferenceToEverything() > 1;
+ }
+
+ public final boolean needsTranslations() {
+ final boolean[] translationSupportNeeded = new boolean[] {false};
+ accept(new IExpressionVisitor() {
+ public boolean accept(IExpression expr) {
+ if (((Expression) expr).isReferencingTranslations()) {
+ translationSupportNeeded[0] = true;
+ return false;
+ }
+ return true;
+ }
+ });
+ return translationSupportNeeded[0];
+ }
+
+ public String toString() {
+ StringBuffer bld = new StringBuffer();
+ toString(bld);
+ return bld.toString();
+ }
+
+ public abstract void toString(StringBuffer bld);
+
+ static void appendOperand(StringBuffer bld, Expression operand, int priority) {
+ if (priority < operand.getPriority()) {
+ bld.append('(');
+ operand.toString(bld);
+ bld.append(')');
+ } else
+ operand.toString(bld);
+ }
+
+ void assertNotCollection(Expression expr, String usage) {
+ if (expr.isCollection())
+ throw new IllegalArgumentException("A collection cannot be used as " + usage + " in a " + getOperator()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ void assertNotBoolean(Expression expr, String usage) {
+ if (expr.isBoolean())
+ throw new IllegalArgumentException("A boolean cannot be used as " + usage + " in a " + getOperator()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ int countReferenceToEverything() {
+ return 0;
+ }
+
+ abstract String getOperator();
+
+ abstract int getPriority();
+
+ boolean isBoolean() {
+ return false;
+ }
+
+ boolean isCollection() {
+ return false;
+ }
+
+ boolean isElementBoolean() {
+ return false;
+ }
+
+ boolean isReferencingTranslations() {
+ return false;
+ }
+
+ boolean isPipeable() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ Expression pipeFrom(Expression nxt) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ boolean isElementCollection() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ExpressionFactory.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ExpressionFactory.java
new file mode 100644
index 000000000..e6dac860d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/ExpressionFactory.java
@@ -0,0 +1,228 @@
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import org.eclipse.equinox.internal.p2.ql.parser.IParserConstants;
+import org.eclipse.equinox.p2.ql.*;
+
+public class ExpressionFactory implements IExpressionFactory, IParserConstants {
+ public static final IExpressionFactory INSTANCE = new ExpressionFactory();
+
+ private static final Map functionMap;
+
+ static {
+ Class[] args = new Class[] {Expression[].class};
+ Map f = new HashMap();
+ try {
+ f.put(KEYWORD_BOOLEAN, BooleanFunction.class.getConstructor(args));
+ f.put(KEYWORD_FILTER, FilterFunction.class.getConstructor(args));
+ f.put(KEYWORD_VERSION, VersionFunction.class.getConstructor(args));
+ f.put(KEYWORD_RANGE, RangeFunction.class.getConstructor(args));
+ f.put(KEYWORD_SET, SetFunction.class.getConstructor(args));
+ f.put(KEYWORD_CLASS, ClassFunction.class.getConstructor(args));
+ f.put(KEYWORD_IQUERY, WrappedIQuery.class.getConstructor(args));
+ f.put(KEYWORD_CAPABILITY_INDEX, CapabilityIndexFunction.class.getConstructor(args));
+ functionMap = Collections.unmodifiableMap(f);
+ } catch (Exception e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ private static Expression[] convertArray(IExpression[] operands) {
+ Expression[] ops = new Expression[operands.length];
+ System.arraycopy(operands, 0, ops, 0, operands.length);
+ return ops;
+ }
+
+ private ExpressionFactory() {
+ // Maintain singleton
+ }
+
+ public IExpression all(IExpression collection, IExpression lambda) {
+ return new All((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression and(IExpression[] operands) {
+ return new And(convertArray(operands));
+ }
+
+ public IExpression array(IExpression[] operands) {
+ return new Array(convertArray(operands));
+ }
+
+ public IExpression assignment(IExpression variable, IExpression expression) {
+ return new Assignment((Variable) variable, (Expression) expression);
+ }
+
+ public IExpression at(IExpression target, IExpression key) {
+ return new At((Expression) target, (Expression) key);
+ }
+
+ public IExpression collect(IExpression collection, IExpression lambda) {
+ return new Collect((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression condition(IExpression test, IExpression ifTrue, IExpression ifFalse) {
+ return new Condition((Expression) test, (Expression) ifTrue, (Expression) ifFalse);
+ }
+
+ public IExpression constant(Object value) {
+ return Constant.create(value);
+ }
+
+ public IContextExpression contextExpression(IExpression expr) {
+ return new ContextExpression((Expression) expr);
+ }
+
+ public IExpression dynamicMember(IExpression target, String name) {
+ return new Member.DynamicMember((Expression) target, name);
+ }
+
+ public IExpression equals(IExpression lhs, IExpression rhs) {
+ return new Equals((Expression) lhs, (Expression) rhs, false);
+ }
+
+ public IExpression exists(IExpression collection, IExpression lambda) {
+ return new Exists((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression first(IExpression collection, IExpression lambda) {
+ return new First((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression flatten(IExpression collection) {
+ return new Flatten((Expression) collection);
+ }
+
+ public IExpression function(Object function, IExpression[] args) {
+ try {
+ return (IExpression) ((Constructor) function).newInstance(new Object[] {convertArray(args)});
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof RuntimeException)
+ throw (RuntimeException) t;
+ throw new RuntimeException(t);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Map getFunctionMap() {
+ return functionMap;
+ }
+
+ public IExpression greater(IExpression lhs, IExpression rhs) {
+ return new Compare((Expression) lhs, (Expression) rhs, false, false);
+ }
+
+ public IExpression indexedParameter(int index) {
+ return new Parameter.Indexed(index);
+ }
+
+ public IExpression keyedParameter(String key) {
+ return new Parameter.Keyed(key);
+ }
+
+ public IExpression lambda(IExpression variable, IExpression body) {
+ return new LambdaExpression((Variable) variable, (Expression) body);
+ }
+
+ public IExpression lambda(IExpression variable, IExpression body, IExpression[] assignments) {
+ Assignment[] asgns = new Assignment[assignments.length];
+ System.arraycopy(assignments, 0, asgns, 0, assignments.length);
+ return new LambdaExpression((Variable) variable, (Expression) body, asgns);
+ }
+
+ public IExpression latest(IExpression collection) {
+ return new Latest((Expression) collection);
+ }
+
+ public IExpression less(IExpression lhs, IExpression rhs) {
+ return new Compare((Expression) lhs, (Expression) rhs, true, false);
+ }
+
+ public IExpression limit(IExpression collection, IExpression limit) {
+ return new Limit((Expression) collection, (Expression) limit);
+ }
+
+ public IExpression matches(IExpression lhs, IExpression rhs) {
+ return new Matches((Expression) lhs, (Expression) rhs);
+ }
+
+ public IMatchExpression matchExpression(IExpression expr) {
+ return new MatchExpression((Expression) expr);
+ }
+
+ public IExpression member(IExpression target, String name) {
+ return new Member.DynamicMember((Expression) target, name);
+ }
+
+ public IExpression memberCall(IExpression target, String name, IExpression[] args) {
+ if (args.length == 0)
+ return member(target, name);
+
+ Expression[] eargs = convertArray(args);
+ if (KEYWORD_SATISFIES_ANY.equals(name))
+ return new Member.CapabilityIndex_satisfiesAny((Expression) target, eargs);
+ if (KEYWORD_SATISFIES_ALL.equals(name))
+ return new Member.CapabilityIndex_satisfiesAll((Expression) target, eargs);
+
+ StringBuffer bld = new StringBuffer();
+ bld.append("Don't know how to do a member call with "); //$NON-NLS-1$
+ bld.append(name);
+ bld.append('(');
+ Array.elementsToString(bld, eargs);
+ bld.append(')');
+ throw new IllegalArgumentException(bld.toString());
+ }
+
+ public IExpression not(IExpression operand) {
+ if (operand instanceof Equals) {
+ Equals eq = (Equals) operand;
+ return new Equals(eq.lhs, eq.rhs, !eq.negate);
+ }
+ if (operand instanceof Compare) {
+ Compare cmp = (Compare) operand;
+ return new Compare(cmp.lhs, cmp.rhs, !cmp.compareLess, !cmp.equalOK);
+ }
+ if (operand instanceof Not)
+ return ((Not) operand).operand;
+
+ return new Not((Expression) operand);
+ }
+
+ public IExpression or(IExpression[] operands) {
+ return new Or(convertArray(operands));
+ }
+
+ public IExpression pipe(IExpression[] operands) {
+ if (operands.length == 0)
+ return null;
+
+ Expression pipe = (Expression) operands[0];
+ for (int idx = 1; idx < operands.length; ++idx)
+ pipe = ((Expression) operands[idx]).pipeFrom(pipe);
+ return pipe;
+ }
+
+ public IExpression select(IExpression collection, IExpression lambda) {
+ return new Select((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression traverse(IExpression collection, IExpression lambda) {
+ return new Traverse((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression unique(IExpression collection, IExpression cache) {
+ return new Unique((Expression) collection, (Expression) cache);
+ }
+
+ public IExpression variable(String name) {
+ return Variable.create(name);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/FilterFunction.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/FilterFunction.java
new file mode 100644
index 000000000..5f0130c36
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/FilterFunction.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.internal.p2.metadata.LDAPQuery;
+import org.eclipse.equinox.internal.p2.ql.QLActivator;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * A function that creates an OSGi filter based on a String
+ */
+final class FilterFunction extends Function {
+ public FilterFunction(Expression[] operands) {
+ super(assertLength(operands, 1, 1, KEYWORD_FILTER));
+ assertNotBoolean(operands[0], "parameter"); //$NON-NLS-1$
+ assertNotCollection(operands[0], "parameter"); //$NON-NLS-1$
+ }
+
+ boolean assertSingleArgumentClass(Object v) {
+ return v instanceof LDAPQuery || v instanceof String;
+ }
+
+ Object createInstance(Object arg) {
+ String str = (arg instanceof LDAPQuery) ? ((LDAPQuery) arg).getFilter() : (String) arg;
+ try {
+ return QLActivator.context.createFilter(str);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ String getOperator() {
+ return KEYWORD_FILTER;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/First.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/First.java
new file mode 100644
index 000000000..ee20a1466
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/First.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * A collection filter that yields the first element of the <code>collection</code> for which
+ * the <code>filter</code> yields <code>true</code>
+ */
+final class First extends CollectionFilter {
+ First(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ public int getExpressionType() {
+ return TYPE_FIRST;
+ }
+
+ Object evaluate(IEvaluationContext context, Iterator itor) {
+ Variable variable = lambda.getItemVariable();
+ while (itor.hasNext()) {
+ Object each = itor.next();
+ variable.setValue(context, each);
+ if (lambda.evaluate(context) == Boolean.TRUE)
+ return each;
+ }
+ return null;
+ }
+
+ String getOperator() {
+ return KEYWORD_FIRST;
+ }
+
+ boolean isBoolean() {
+ return operand.isElementBoolean();
+ }
+
+ boolean isCollection() {
+ return operand.isElementCollection();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Flatten.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Flatten.java
new file mode 100644
index 000000000..2aaf288ed
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Flatten.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.internal.p2.ql.FlattenIterator;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that yields a new collection consisting of all elements of the
+ * <code>collection</code> for which the <code>filter</code> yields <code>true</code>.
+ */
+final class Flatten extends UnaryCollectionFilter {
+ Flatten(Expression collection) {
+ super(collection);
+ }
+
+ public Iterator evaluateAsIterator(IEvaluationContext context) {
+ return new FlattenIterator(operand.evaluateAsIterator(context));
+ }
+
+ public int getExpressionType() {
+ return TYPE_FLATTEN;
+ }
+
+ String getOperator() {
+ return KEYWORD_FLATTEN;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Function.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Function.java
new file mode 100644
index 000000000..5497dca2b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Function.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that represents a function such as filter(&lt;expr&gt;) or version(&lt;expr&gt;)
+ */
+abstract class Function extends NAry {
+
+ private Object instance;
+
+ Function(Expression[] operands) {
+ super(operands);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ if (instance != null)
+ return instance;
+
+ Expression operand = operands[0];
+ Object arg = operand.evaluate(context);
+ if (assertSingleArgumentClass(arg)) {
+ Object result = createInstance(arg);
+ if (operand instanceof Constant || operand instanceof Parameter)
+ // operand won't change over time so we can cache this instance.
+ instance = result;
+ return result;
+ }
+ String what = arg == null ? "null" : ("a " + arg.getClass().getName()); //$NON-NLS-1$ //$NON-NLS-2$
+ throw new IllegalArgumentException("Cannot create a " + getOperator() + " from " + what); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ boolean assertSingleArgumentClass(Object v) {
+ return true;
+ }
+
+ public int getExpressionType() {
+ return TYPE_FUNCTION;
+ }
+
+ public void toString(StringBuffer bld) {
+ bld.append(getOperator());
+ bld.append('(');
+ Array.elementsToString(bld, operands);
+ bld.append(')');
+ }
+
+ final Object createInstance(String arg) {
+ throw new UnsupportedOperationException();
+ }
+
+ Object createInstance(Object arg) {
+ throw new UnsupportedOperationException();
+ }
+
+ int getPriority() {
+ return PRIORITY_CONSTRUCTOR;
+ }
+
+ boolean isBoolean() {
+ return false;
+ }
+
+ boolean isCollection() {
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/LambdaExpression.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/LambdaExpression.java
new file mode 100644
index 000000000..cdc1d7216
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/LambdaExpression.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.internal.p2.ql.MultiVariableContext;
+import org.eclipse.equinox.internal.p2.ql.SingleVariableContext;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.IExpressionVisitor;
+
+/**
+ * A function that executes some code
+ */
+final class LambdaExpression extends Unary {
+ private static final Assignment[] emptyAssignmentArray = new Assignment[0];
+ private final Assignment[] assignments;
+ private final Variable each;
+
+ LambdaExpression(Variable each, Expression body, Assignment[] assignments) {
+ super(body);
+ this.each = each;
+ if (assignments == null)
+ assignments = emptyAssignmentArray;
+ this.assignments = assignments;
+ }
+
+ LambdaExpression(Variable variable, Expression body) {
+ this(variable, body, null);
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ if (super.accept(visitor) && each.accept(visitor)) {
+ for (int idx = 0; idx < assignments.length; ++idx)
+ if (!assignments[idx].accept(visitor))
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ public int getExpressionType() {
+ return TYPE_LAMBDA;
+ }
+
+ public void toString(StringBuffer bld) {
+ int top = assignments.length;
+ if (top > 0) {
+ for (int idx = 0; idx < top; ++idx) {
+ appendOperand(bld, assignments[idx].rhs, PRIORITY_COMMA);
+ bld.append(", "); //$NON-NLS-1$
+ }
+ bld.append(OPERATOR_EACH);
+ bld.append(", {"); //$NON-NLS-1$
+ for (int idx = 0; idx < top; ++idx) {
+ appendOperand(bld, assignments[idx].lhs, PRIORITY_COMMA);
+ bld.append(", "); //$NON-NLS-1$
+ }
+ }
+ each.toString(bld);
+ bld.append(" | "); //$NON-NLS-1$
+ appendOperand(bld, operand, PRIORITY_COMMA);
+ if (top > 0)
+ bld.append('}');
+ }
+
+ int countReferenceToEverything() {
+ if (super.countReferenceToEverything() > 0)
+ return 2;
+ for (int idx = 0; idx < assignments.length; ++idx)
+ if (assignments[idx].countReferenceToEverything() > 0)
+ return 2;
+ return 0;
+ }
+
+ Variable getItemVariable() {
+ return each;
+ }
+
+ String getOperator() {
+ return "|"; //$NON-NLS-1$
+ }
+
+ int getPriority() {
+ return PRIORITY_LAMBDA;
+ }
+
+ boolean isBoolean() {
+ return operand.isBoolean();
+ }
+
+ boolean isCollection() {
+ return operand.isCollection();
+ }
+
+ IEvaluationContext prolog(IEvaluationContext context) {
+ IEvaluationContext lambdaContext = new SingleVariableContext(context, each);
+ int top = assignments.length;
+ if (top > 0) {
+ if (top == 1) {
+ Assignment v = assignments[0];
+ lambdaContext = new SingleVariableContext(lambdaContext, v.lhs);
+ lambdaContext.setValue(v.lhs, v.rhs.evaluate(context));
+ } else {
+ Variable[] vars = new Variable[top];
+ for (int idx = 0; idx < top; ++idx)
+ vars[idx] = (Variable) assignments[idx].lhs;
+ lambdaContext = new MultiVariableContext(lambdaContext, vars);
+ for (int idx = 0; idx < top; ++idx)
+ lambdaContext.setValue(vars[idx], assignments[idx].rhs.evaluate(context));
+ }
+ }
+ return lambdaContext;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Latest.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Latest.java
new file mode 100644
index 000000000..d4ddc0032
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Latest.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.p2.metadata.IVersionedId;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that is especially targeted towards {@link IVersionedId} instances. It will
+ * reject any objects that is not an <code>IVersionedId</code> and it will ensure that the
+ * resulting iterator only iterates over the latest version of any found id.
+ */
+final class Latest extends UnaryCollectionFilter {
+
+ Latest(Expression collection) {
+ super(collection);
+ }
+
+ public Iterator evaluateAsIterator(IEvaluationContext context) {
+ HashMap greatestIUVersion;
+ if (operand instanceof Select) {
+ // Inline element evaluation here so that we don't build a map that is
+ // larger then it has to be
+ Select select = (Select) operand;
+ Iterator iterator = select.operand.evaluateAsIterator(context);
+ if (!iterator.hasNext())
+ return Collections.EMPTY_SET.iterator();
+
+ greatestIUVersion = new HashMap();
+ LambdaExpression lambda = select.lambda;
+ context = lambda.prolog(context);
+ Variable variable = lambda.getItemVariable();
+ while (iterator.hasNext()) {
+ Object next = iterator.next();
+ if (!(next instanceof IVersionedId))
+ continue;
+
+ variable.setValue(context, next);
+ if (lambda.evaluate(context) != Boolean.TRUE)
+ continue;
+
+ IVersionedId versionedID = (IVersionedId) next;
+ String id = versionedID.getId();
+ IVersionedId prev = (IVersionedId) greatestIUVersion.put(id, versionedID);
+ if (prev == null)
+ continue;
+ if (prev.getVersion().compareTo(versionedID.getVersion()) > 0)
+ greatestIUVersion.put(id, prev);
+ }
+ } else {
+ Iterator iterator = operand.evaluateAsIterator(context);
+ if (iterator == null)
+ return null;
+ if (!iterator.hasNext())
+ return Collections.EMPTY_SET.iterator();
+
+ greatestIUVersion = new HashMap();
+ while (iterator.hasNext()) {
+ Object next = iterator.next();
+ if (!(next instanceof IVersionedId))
+ continue;
+
+ IVersionedId versionedID = (IVersionedId) next;
+ String id = versionedID.getId();
+
+ IVersionedId prev = (IVersionedId) greatestIUVersion.put(id, versionedID);
+ if (prev == null)
+ continue;
+
+ if (prev.getVersion().compareTo(versionedID.getVersion()) > 0)
+ greatestIUVersion.put(id, prev);
+ }
+ }
+ return greatestIUVersion.values().iterator();
+ }
+
+ public int getExpressionType() {
+ return TYPE_LATEST;
+ }
+
+ String getOperator() {
+ return KEYWORD_LATEST;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Limit.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Limit.java
new file mode 100644
index 000000000..5c0ae42a3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Limit.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * A collection filter that limits the number of entries in the collection
+ */
+final class Limit extends Binary {
+
+ /**
+ * An iterator that stops iterating after a given number of iterations.
+ */
+ static final class CountingIterator implements Iterator {
+ private final Iterator innerIterator;
+ private int counter;
+
+ public CountingIterator(Iterator iterator, int count) {
+ this.innerIterator = iterator;
+ this.counter = count;
+ }
+
+ public boolean hasNext() {
+ return counter > 0 && innerIterator.hasNext();
+ }
+
+ public Object next() {
+ if (counter > 0) {
+ --counter;
+ return innerIterator.next();
+ }
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ innerIterator.remove();
+ }
+ }
+
+ Limit(Expression operand, Expression param) {
+ super(operand, param);
+ assertNotBoolean(operand, "operand"); //$NON-NLS-1$
+ assertNotCollection(param, "parameter"); //$NON-NLS-1$
+ }
+
+ Limit(Expression operand, int limit) {
+ this(operand, Constant.create(new Integer(limit)));
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object rval = rhs.evaluate(context);
+ int limit = -1;
+ if (rval instanceof Integer)
+ limit = ((Integer) rval).intValue();
+ if (limit <= 0)
+ throw new IllegalArgumentException("limit expression did not evalutate to a positive integer"); //$NON-NLS-1$
+ return new CountingIterator(lhs.evaluateAsIterator(context), limit);
+ }
+
+ public int getExpressionType() {
+ return TYPE_LIMIT;
+ }
+
+ public void toString(StringBuffer bld) {
+ CollectionFilter.appendProlog(bld, lhs, getOperator());
+ appendOperand(bld, rhs, PRIORITY_COMMA);
+ bld.append(')');
+ }
+
+ String getOperator() {
+ return KEYWORD_LIMIT;
+ }
+
+ int getPriority() {
+ return PRIORITY_COLLECTION;
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+
+ boolean isElementBoolean() {
+ return lhs.isElementBoolean();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/MatchExpression.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/MatchExpression.java
new file mode 100644
index 000000000..dadd153bb
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/MatchExpression.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.internal.p2.ql.*;
+import org.eclipse.equinox.p2.ql.*;
+
+/**
+ * The match expression is the top expression in item queries. It introduces the
+ * variable 'item' and initializes it with the item to match.
+ */
+final class MatchExpression extends Unary implements IMatchExpression {
+
+ MatchExpression(Expression expression) {
+ super(expression);
+ }
+
+ public boolean isMatch(IEvaluationContext context, Object value) {
+ Variable.ITEM.setValue(context, value);
+ return operand.evaluate(context) == Boolean.TRUE;
+ }
+
+ public IEvaluationContext createContext(Object[] params) {
+ return new SingleVariableContext(new ParameterContext(params), Variable.ITEM);
+ }
+
+ public IEvaluationContext createContext(Object[] params, ITranslationSupport ts) {
+ IEvaluationContext context = new MultiVariableContext(new ParameterContext(params), new IExpression[] {Variable.ITEM, Variable.TRANSLATIONS});
+ context.setValue(Variable.TRANSLATIONS, ts);
+ return context;
+ }
+
+ public int getExpressionType() {
+ return operand.getExpressionType();
+ }
+
+ public void toString(StringBuffer bld) {
+ operand.toString(bld);
+ }
+
+ protected int getPriority() {
+ return operand.getPriority();
+ }
+
+ String getOperator() {
+ throw new UnsupportedOperationException();
+ }
+
+ Expression pipeFrom(Expression expr) {
+ return new MatchExpression(operand.pipeFrom(expr));
+ }
+
+ boolean isBoolean() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Matches.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Matches.java
new file mode 100644
index 000000000..b36aa1666
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Matches.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.metadata.IRequiredCapability;
+import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.SimplePattern;
+import org.osgi.framework.Filter;
+
+/**
+ * <p>A class that performs &quot;matching&quot; The actual algorithm used for
+ * performing the match varies depending on the types of the items to match.</p>
+ * <p>The following things can be matched:</p>
+ * <table border="1" cellpadding="3">
+ * <tr><th>LHS</th><th>RHS</th><th>Implemented as</th></tr>
+ * <tr><td>IProvidedCapability</td><td>IRequiredCapability</td><td>lhs.satisfies(rhs)</td></tr>
+ * <tr><td>IInstallableUnit</td><td>IRequiredCapability</td><td>lhs.satisfies(rhs)</td></tr>
+ * <tr><td>Version</td><td>VersionRange</td><td>rhs.isIncluded(lhs)</td></tr>
+ * <tr><td>IInstallableUnit</td><td>Filter</td><td>rhs.matches(lhs.properties)</td></tr>
+ * <tr><td>Map</td><td>Filter</td><td>rhs.match(lhs)</td></tr>
+ * <tr><td>String</td><td>Pattern</td><td>rhs.matcher(lhs).matches()</td></tr>
+ * <tr><td>&lt;any&gt;</td><td>Class</td><td>rhs.isInstance(lhs)</td></tr>
+ * <tr><td>Class</td><td>Class</td><td>rhs.isAssignableFrom(lhs)</td></tr>
+ * </table>
+ */
+final class Matches extends Binary {
+ Matches(Expression lhs, Expression rhs) {
+ super(lhs, rhs);
+ assertNotBoolean(lhs, "lhs"); //$NON-NLS-1$
+ assertNotBoolean(rhs, "rhs"); //$NON-NLS-1$
+ assertNotCollection(rhs, "rhs"); //$NON-NLS-1$
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object lval = lhs.evaluate(context);
+ Object rval = rhs.evaluate(context);
+
+ if (rval instanceof IRequiredCapability) {
+ IRequiredCapability cap = (IRequiredCapability) rval;
+ if (lval instanceof IInstallableUnit)
+ return Boolean.valueOf(((IInstallableUnit) lval).satisfies(cap));
+ if (lval instanceof IProvidedCapability)
+ return Boolean.valueOf(cap.satisfiedBy((IProvidedCapability) lval));
+
+ } else if (rval instanceof VersionRange) {
+ if (lval instanceof Version)
+ return Boolean.valueOf(((VersionRange) rval).isIncluded((Version) lval));
+
+ } else if (rval instanceof SimplePattern) {
+ if (lval instanceof CharSequence)
+ return Boolean.valueOf(((SimplePattern) rval).isMatch((CharSequence) lval));
+
+ } else if (rval instanceof IUpdateDescriptor) {
+ if (lval instanceof IInstallableUnit)
+ return Boolean.valueOf(((IUpdateDescriptor) rval).isUpdateOf((IInstallableUnit) lval));
+
+ } else if (rval instanceof Filter) {
+ if (lval instanceof IInstallableUnit)
+ return Boolean.valueOf(((Filter) rval).match(new Hashtable(((IInstallableUnit) lval).getProperties())));
+ if (lval instanceof Dictionary)
+ return Boolean.valueOf(((Filter) rval).match((Dictionary) lval));
+ if (lval instanceof Map)
+ return Boolean.valueOf(((Filter) rval).match(new Hashtable((Map) lval)));
+
+ } else if (rval instanceof Locale) {
+ if (lval instanceof String)
+ return Boolean.valueOf(matchLocaleVariants((Locale) rval, (String) lval));
+
+ } else if (rval instanceof Class) {
+ Class rclass = (Class) rval;
+ return Boolean.valueOf(lval instanceof Class ? rclass.isAssignableFrom((Class) lval) : rclass.isInstance(lval));
+ }
+
+ if (lval == null || rval == null)
+ return Boolean.FALSE;
+
+ throw new IllegalArgumentException("Cannot match a " + lval.getClass().getName() + " with a " + rval.getClass().getName()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ public int getExpressionType() {
+ return TYPE_MATCHES;
+ }
+
+ private static boolean equals(String a, String b, int startPos, int endPos) {
+ if (endPos - startPos != b.length())
+ return false;
+
+ int bidx = 0;
+ while (startPos < endPos)
+ if (a.charAt(startPos++) != b.charAt(bidx++))
+ return false;
+ return true;
+ }
+
+ private static boolean matchLocaleVariants(Locale rval, String lval) {
+ int uscore = lval.indexOf('_');
+ if (uscore < 0)
+ // No country and no variant. Just match language
+ return lval.equals(rval.getLanguage());
+
+ if (!equals(lval, rval.getLanguage(), 0, uscore))
+ // Language part doesn't match. Give up.
+ return false;
+
+ // Check country and variant
+ int countryStart = uscore + 1;
+ uscore = lval.indexOf('_', countryStart);
+ return uscore < 0 ? equals(lval, rval.getCountry(), countryStart, lval.length()) //
+ : equals(lval, rval.getCountry(), countryStart, uscore) && equals(lval, rval.getVariant(), uscore + 1, lval.length());
+ }
+
+ String getOperator() {
+ return OPERATOR_MATCHES;
+ }
+
+ boolean isBoolean() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Member.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Member.java
new file mode 100644
index 000000000..ff8efbc88
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Member.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.lang.reflect.*;
+import java.util.Iterator;
+import org.eclipse.equinox.p2.ql.*;
+
+/**
+ * <p>An expression that performs member calls to obtain some value
+ * from some object instance. It uses standard bean semantics so
+ * that an attempt to obtain &quot;value&quot; will cause an
+ * attempt to call <code>getValue()</code> and if no such method
+ * exists, <code>isValue()</code> and if that doesn't work either,
+ * <code>value()</code>.</p>
+ */
+abstract class Member extends Unary {
+
+ static Member createDynamicMember(Expression operand, String name) {
+ return new DynamicMember(operand, name);
+ }
+
+ static final Object[] NO_ARGS = new Object[0];
+
+ final String name;
+ final Expression[] argExpressions;
+
+ static abstract class CapabilityIndexMethod extends Member {
+ public CapabilityIndexMethod(Expression operand, String name, Expression[] args) {
+ super(operand, name, args);
+ }
+
+ final ICapabilityIndex getSelf(IEvaluationContext context) {
+ Object self = operand.evaluate(context);
+ if (self instanceof ICapabilityIndex)
+ return (ICapabilityIndex) self;
+ throw new IllegalArgumentException("lhs of member expected to be an ICapabilityIndex implementation"); //$NON-NLS-1$
+ }
+
+ public final Object evaluate(IEvaluationContext context) {
+ return evaluateAsIterator(context);
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+ }
+
+ static final class CapabilityIndex_satisfiesAny extends CapabilityIndexMethod {
+
+ public CapabilityIndex_satisfiesAny(Expression operand, Expression[] argExpressions) {
+ super(operand, KEYWORD_SATISFIES_ANY, NAry.assertLength(argExpressions, 1, 1, KEYWORD_SATISFIES_ANY));
+ }
+
+ public Iterator evaluateAsIterator(IEvaluationContext context) {
+ return getSelf(context).satisfiesAny(argExpressions[0].evaluateAsIterator(context));
+ }
+ }
+
+ static final class CapabilityIndex_satisfiesAll extends CapabilityIndexMethod {
+
+ public CapabilityIndex_satisfiesAll(Expression operand, Expression[] argExpressions) {
+ super(operand, KEYWORD_SATISFIES_ALL, NAry.assertLength(argExpressions, 1, 1, KEYWORD_SATISFIES_ALL));
+ }
+
+ public Iterator evaluateAsIterator(IEvaluationContext context) {
+ return getSelf(context).satisfiesAll(argExpressions[0].evaluateAsIterator(context));
+ }
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ if (super.accept(visitor))
+ for (int idx = 0; idx < argExpressions.length; ++idx)
+ if (!argExpressions[idx].accept(visitor))
+ return false;
+ return true;
+ }
+
+ Member(Expression operand, String name, Expression[] args) {
+ super(operand);
+ this.name = name;
+ this.argExpressions = args;
+ }
+
+ public int getExpressionType() {
+ return TYPE_MEMBER;
+ }
+
+ public void toString(StringBuffer bld) {
+ if (operand == Variable.ITEM || operand == Variable.EVERYTHING)
+ bld.append(name);
+ else {
+ appendOperand(bld, operand, getPriority());
+ bld.append('.');
+ bld.append(name);
+ }
+ if (argExpressions.length > 0) {
+ bld.append('(');
+ Array.elementsToString(bld, argExpressions);
+ bld.append(')');
+ }
+ }
+
+ String getOperator() {
+ return OPERATOR_MEMBER;
+ }
+
+ int getPriority() {
+ return PRIORITY_MEMBER;
+ }
+
+ static final class DynamicMember extends Member {
+ private static final Class[] NO_ARG_TYPES = new Class[0];
+ private static final String GET_PREFIX = "get"; //$NON-NLS-1$
+ private static final String IS_PREFIX = "is"; //$NON-NLS-1$
+
+ DynamicMember(Expression operand, String name) {
+ super(operand, name, Expression.emptyArray);
+ if (!(name.startsWith(GET_PREFIX) || name.startsWith(IS_PREFIX)))
+ name = GET_PREFIX + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ this.methodName = name;
+ }
+
+ private Class lastClass;
+ private Method method;
+ private String methodName;
+
+ public Object evaluate(IEvaluationContext context) {
+ return invoke(operand.evaluate(context));
+ }
+
+ boolean isReferencingTranslations() {
+ return VARIABLE_TRANSLATIONS.equals(name);
+ }
+
+ Object invoke(Object self) {
+ if (self == null)
+ throw new IllegalArgumentException("Cannot access member " + name + " in null"); //$NON-NLS-1$//$NON-NLS-2$
+
+ Class c = self.getClass();
+ if (lastClass == null || !lastClass.isAssignableFrom(c)) {
+ Method m;
+ for (;;) {
+ try {
+ m = c.getMethod(methodName, NO_ARG_TYPES);
+ if (!Modifier.isPublic(m.getModifiers()))
+ throw new NoSuchMethodException();
+ break;
+ } catch (NoSuchMethodException e) {
+ if (methodName.startsWith(GET_PREFIX))
+ // Switch from using getXxx() to isXxx()
+ methodName = IS_PREFIX + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ else if (methodName.startsWith(IS_PREFIX))
+ // Switch from using isXxx() to xxx()
+ methodName = name;
+ else
+ throw new IllegalArgumentException("Cannot find a public member " + name + " in a " + self.getClass().getName()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
+
+ // Since we already checked that it's public. This will speed
+ // up the calls a bit.
+ m.setAccessible(true);
+ lastClass = c;
+ method = m;
+ }
+
+ Exception checked;
+ try {
+ return method.invoke(self, NO_ARGS);
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (IllegalAccessException e) {
+ checked = e;
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getTargetException();
+ if (cause instanceof RuntimeException)
+ throw (RuntimeException) cause;
+ if (cause instanceof Error)
+ throw (Error) cause;
+ checked = (Exception) cause;
+ }
+ throw new RuntimeException("Problem invoking " + methodName + " on a " + self.getClass().getName(), checked); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/NAry.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/NAry.java
new file mode 100644
index 000000000..22595deb7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/NAry.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IExpressionVisitor;
+
+/**
+ * The abstract baseclass for all N-ary expressions
+ *
+ */
+abstract class NAry extends Expression {
+ static Expression[] assertLength(Expression[] operands, int length, String operand) {
+ if (operands == null)
+ operands = emptyArray;
+ if (operands.length < length)
+ throw new IllegalArgumentException("Not enough operands for " + operand); //$NON-NLS-1$
+ return operands;
+ }
+
+ static Expression[] assertLength(Expression[] operands, int minLength, int maxLength, String operand) {
+ if (operands == null)
+ operands = emptyArray;
+ if (operands.length < minLength)
+ throw new IllegalArgumentException("Not enough operands for " + operand); //$NON-NLS-1$
+ if (operands.length > maxLength)
+ throw new IllegalArgumentException("Too many operands for " + operand); //$NON-NLS-1$
+ return operands;
+ }
+
+ final Expression[] operands;
+
+ NAry(Expression[] operands) {
+ this.operands = operands;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ if (super.accept(visitor))
+ for (int idx = 0; idx < operands.length; ++idx)
+ if (!operands[idx].accept(visitor))
+ return false;
+ return true;
+ }
+
+ public void toString(StringBuffer bld) {
+ appendOperand(bld, operands[0], getPriority());
+ for (int idx = 1; idx < operands.length; ++idx) {
+ bld.append(' ');
+ bld.append(getOperator());
+ bld.append(' ');
+ appendOperand(bld, operands[idx], getPriority());
+ }
+ }
+
+ int countReferenceToEverything() {
+ int count = 0;
+ for (int idx = 0; count < 2 && idx < operands.length; ++idx)
+ count += operands[idx].countReferenceToEverything();
+ return count;
+ }
+
+ abstract String getOperator();
+
+ boolean isBoolean() {
+ int idx = operands.length;
+ while (--idx >= 0)
+ if (!operands[idx].isBoolean())
+ return false;
+ return true;
+ }
+
+ boolean isCollection() {
+ int idx = operands.length;
+ while (--idx >= 0)
+ if (!operands[idx].isCollection())
+ return false;
+ return true;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Not.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Not.java
new file mode 100644
index 000000000..ab7b31f53
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Not.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that yields <code>true</code> when its operand does not.
+ */
+final class Not extends Unary {
+ Not(Expression operand) {
+ super(operand);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return Boolean.valueOf(operand.evaluate(context) != Boolean.TRUE);
+ }
+
+ public int getExpressionType() {
+ return TYPE_NOT;
+ }
+
+ String getOperator() {
+ return OPERATOR_NOT;
+ }
+
+ int getPriority() {
+ return PRIORITY_NOT;
+ }
+
+ boolean isBoolean() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Or.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Or.java
new file mode 100644
index 000000000..c4fad533a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Or.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import java.util.Set;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * n-ary OR operator. This operator evaluates its first operand and then checks
+ * the class of the result. If the class is {@link Boolean} then it is assumed
+ * that all other operands also evaluates to a boolean and the full evaluation is
+ * <code>false</code> if none of its operands evaluate to <code>true</code>. If the
+ * first result was not of class {@link Boolean}, then it is assumed that it can
+ * be accessed as an {@link Iterator} and that all other operands also evaluates to
+ * something that can be accessed an {@link Iterator}. The OR operator will then
+ * function as a UNION operator and the result is the unique sum of all elements that
+ * were found in all operands.
+ */
+final class Or extends NAry {
+ public Or(Expression[] operands) {
+ super(assertLength(operands, 2, OPERATOR_OR));
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object firstValue = operands[0].evaluate(context);
+
+ // Determine operation mode
+ if (firstValue instanceof Boolean) {
+ // The first value was boolean. Assume that the rest are too
+ if (((Boolean) firstValue).booleanValue())
+ return Boolean.TRUE;
+
+ for (int idx = 1; idx < operands.length; ++idx) {
+ if (operands[idx].evaluate(context) == Boolean.TRUE)
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
+ // Not a boolean. Assume that we can use an iterator on all values
+ Set resultSet = asSet(firstValue, true);
+ for (int idx = 1; idx < operands.length; ++idx) {
+ Iterator itor = operands[idx].evaluateAsIterator(context);
+ while (itor.hasNext())
+ resultSet.add(itor.next());
+ }
+ return resultSet;
+ }
+
+ public int getExpressionType() {
+ return TYPE_OR;
+ }
+
+ String getOperator() {
+ return OPERATOR_OR;
+ }
+
+ int getPriority() {
+ return PRIORITY_OR;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Parameter.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Parameter.java
new file mode 100644
index 000000000..c6374ea2e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Parameter.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * The abstract base class for the indexed and keyed parameters
+ */
+abstract class Parameter extends Expression {
+ public int getExpressionType() {
+ return TYPE_PARAMETER;
+ }
+
+ int getPriority() {
+ return PRIORITY_PARAMETER;
+ }
+
+ String getOperator() {
+ return OPERATOR_PARAMETER;
+ }
+
+ static final class Indexed extends Parameter {
+ final int position;
+
+ Indexed(int position) {
+ this.position = position;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return context.getParameter(position);
+ }
+
+ public void toString(StringBuffer bld) {
+ bld.append('$');
+ bld.append(position);
+ }
+ }
+
+ static final class Keyed extends Parameter {
+ final String key;
+
+ public Keyed(String key) {
+ this.key = key;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return context.getParameter(key);
+ }
+
+ public void toString(StringBuffer bld) {
+ bld.append('$');
+ bld.append(key);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/RangeFunction.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/RangeFunction.java
new file mode 100644
index 000000000..a749ac8cf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/RangeFunction.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.internal.provisional.p2.metadata.VersionRange;
+
+/**
+ * A function that creates a {@link VersionRange} from a String
+ */
+final class RangeFunction extends Function {
+
+ public RangeFunction(Expression[] operands) {
+ super(assertLength(operands, 1, 1, KEYWORD_RANGE));
+ assertNotBoolean(operands[0], "parameter"); //$NON-NLS-1$
+ assertNotCollection(operands[0], "parameter"); //$NON-NLS-1$
+ }
+
+ boolean assertSingleArgumentClass(Object v) {
+ return v instanceof String;
+ }
+
+ Object createInstance(Object arg) {
+ return new VersionRange((String) arg);
+ }
+
+ String getOperator() {
+ return KEYWORD_RANGE;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Select.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Select.java
new file mode 100644
index 000000000..76ee0cacd
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Select.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.internal.p2.ql.MatchIteratorFilter;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that yields a new collection consisting of all elements of the
+ * <code>collection</code> for which the <code>filter</code> yields <code>true</code>.
+ */
+final class Select extends CollectionFilter {
+ Select(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ public int getExpressionType() {
+ return TYPE_SELECT;
+ }
+
+ Object evaluate(IEvaluationContext context, Iterator itor) {
+ return evaluateAsIterator(context, itor);
+ }
+
+ Iterator evaluateAsIterator(final IEvaluationContext context, Iterator itor) {
+ return new MatchIteratorFilter(itor) {
+ protected boolean isMatch(Object val) {
+ lambda.getItemVariable().setValue(context, val);
+ return lambda.evaluate(context) == Boolean.TRUE;
+ }
+ };
+ }
+
+ String getOperator() {
+ return KEYWORD_SELECT;
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/SetFunction.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/SetFunction.java
new file mode 100644
index 000000000..b8c83f10c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/SetFunction.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.HashSet;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+final class SetFunction extends Function {
+
+ public SetFunction(Expression[] operands) {
+ super(operands);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ HashSet result = new HashSet();
+ for (int idx = 0; idx < operands.length; ++idx)
+ result.add(operands[idx].evaluate(context));
+ return result;
+ }
+
+ String getOperator() {
+ return KEYWORD_SET;
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Traverse.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Traverse.java
new file mode 100644
index 000000000..27fad3ebc
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Traverse.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.ql.SingleVariableContext;
+import org.eclipse.equinox.internal.p2.ql.parser.IParserConstants;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that will collect items recursively based on a <code>rule</code>.
+ * The <code>rule</code> is applied for each item in the <code>collection</code> and
+ * is supposed to create a new collection. The <code>rule</code> is then applied for each item
+ * in the new collection. All items are collected into a set and items that are already
+ * in that set will not be perused again. The set becomes the result of the traversal.
+ */
+final class Traverse extends CollectionFilter {
+
+ Traverse(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ public int getExpressionType() {
+ return TYPE_TRAVERSE;
+ }
+
+ Object evaluate(IEvaluationContext context, Iterator itor) {
+ return evaluateAsIterator(context, itor);
+ }
+
+ Iterator evaluateAsIterator(IEvaluationContext context, Iterator iterator) {
+ HashSet collector = new HashSet();
+ while (iterator.hasNext())
+ traverse(collector, iterator.next(), context);
+ return collector.iterator();
+ }
+
+ String getOperator() {
+ return IParserConstants.KEYWORD_TRAVERSE;
+ }
+
+ void traverse(Set collector, Object parent, IEvaluationContext context) {
+ if (collector.add(parent)) {
+ Variable variable = lambda.getItemVariable();
+ context = new SingleVariableContext(context, variable);
+ variable.setValue(context, parent);
+ Iterator subIterator = lambda.evaluateAsIterator(context);
+ while (subIterator.hasNext())
+ traverse(collector, subIterator.next(), context);
+ }
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unary.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unary.java
new file mode 100644
index 000000000..ff4e017c6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unary.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.IExpressionVisitor;
+
+/**
+ * The abstract base class for all unary expressions
+ */
+abstract class Unary extends Expression {
+ public final Expression operand;
+
+ Unary(Expression operand) {
+ this.operand = operand;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return super.accept(visitor) && operand.accept(visitor);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return operand.evaluate(context);
+ }
+
+ public void toString(StringBuffer bld) {
+ bld.append(getOperator());
+ appendOperand(bld, operand, getPriority());
+ }
+
+ public Expression getOperand() {
+ return operand;
+ }
+
+ int countReferenceToEverything() {
+ return operand.countReferenceToEverything();
+ }
+
+ abstract String getOperator();
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/UnaryCollectionFilter.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/UnaryCollectionFilter.java
new file mode 100644
index 000000000..85d2a5527
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/UnaryCollectionFilter.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+abstract class UnaryCollectionFilter extends Unary {
+
+ UnaryCollectionFilter(Expression collection) {
+ super(collection);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return evaluateAsIterator(context);
+ }
+
+ public void toString(StringBuffer bld) {
+ if (operand instanceof Select) {
+ Select select = (Select) operand;
+ CollectionFilter.appendProlog(bld, select.operand, getOperator());
+ appendOperand(bld, select.lambda, getPriority());
+ } else
+ CollectionFilter.appendProlog(bld, operand, getOperator());
+ bld.append(')');
+ }
+
+ int getPriority() {
+ return PRIORITY_COLLECTION;
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+
+ boolean isElementBoolean() {
+ return operand.isElementBoolean();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unique.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unique.java
new file mode 100644
index 000000000..1d592792a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Unique.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.ql.MatchIteratorFilter;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+
+/**
+ * An expression that ensures that the elements of its collection is only returned
+ * once throughout the whole query.
+ */
+final class Unique extends Binary {
+ static class UniqueIterator extends MatchIteratorFilter {
+ private final Set uniqueSet;
+
+ public UniqueIterator(Iterator iterator, Set uniqueSet) {
+ super(iterator);
+ this.uniqueSet = uniqueSet;
+ }
+
+ protected boolean isMatch(Object val) {
+ synchronized (uniqueSet) {
+ return uniqueSet.add(val);
+ }
+ }
+ }
+
+ Unique(Expression collection, Expression explicitCache) {
+ super(collection, explicitCache);
+ assertNotBoolean(collection, "collection"); //$NON-NLS-1$
+ assertNotBoolean(explicitCache, "cache"); //$NON-NLS-1$
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object explicitCache = rhs.evaluate(context);
+ Set uniqueSet;
+ if (explicitCache == null)
+ // No cache, we just ensure that the iteration is unique
+ uniqueSet = new HashSet();
+ else {
+ if (!(explicitCache instanceof Set))
+ throw new IllegalArgumentException("Unique cache must be a java.util.Set"); //$NON-NLS-1$
+ uniqueSet = (Set) explicitCache;
+ }
+ return new UniqueIterator(lhs.evaluateAsIterator(context), uniqueSet);
+ }
+
+ public int getExpressionType() {
+ return TYPE_UNIQUE;
+ }
+
+ public void toString(StringBuffer bld) {
+ CollectionFilter.appendProlog(bld, lhs, getOperator());
+ if (rhs != Constant.NULL_CONSTANT)
+ appendOperand(bld, rhs, PRIORITY_COMMA);
+ bld.append(')');
+ }
+
+ String getOperator() {
+ return KEYWORD_UNIQUE;
+ }
+
+ int getPriority() {
+ return PRIORITY_COLLECTION;
+ }
+
+ boolean isCollection() {
+ return true;
+ }
+
+ boolean isElementBoolean() {
+ return lhs.isElementBoolean();
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Variable.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Variable.java
new file mode 100644
index 000000000..fcc51ff09
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Variable.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.internal.p2.ql.IRepeatableIterator;
+import org.eclipse.equinox.internal.p2.ql.RepeatableIterator;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.IExpression;
+
+/**
+ * An expression representing a variable stack in the current thread.
+ */
+class Variable extends Expression {
+
+ static final Variable EVERYTHING = new Variable(VARIABLE_EVERYTHING);
+
+ static final Variable TRANSLATIONS = new Variable(VARIABLE_TRANSLATIONS);
+
+ static final Variable ITEM = new Variable(VARIABLE_ITEM);
+
+ private final String name;
+
+ public static Variable create(String name) {
+ if (VARIABLE_ITEM.equals(name))
+ return ITEM;
+ if (VARIABLE_EVERYTHING.equals(name))
+ return EVERYTHING;
+ if (VARIABLE_TRANSLATIONS.equals(name))
+ return TRANSLATIONS;
+ return new Variable(name);
+ }
+
+ Variable(String name) {
+ this.name = name;
+ }
+
+ public final Object evaluate(IEvaluationContext context) {
+ return context.getValue(this);
+ }
+
+ public Iterator evaluateAsIterator(IEvaluationContext context) {
+ Object value = context.getValue(this);
+ if (value instanceof IRepeatableIterator)
+ return ((IRepeatableIterator) value).getCopy();
+
+ Iterator itor = RepeatableIterator.create(value);
+ setValue(context, itor);
+ return itor;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public final void setValue(IEvaluationContext context, Object value) {
+ context.setValue(this, value);
+ }
+
+ public void toString(StringBuffer bld) {
+ bld.append(name);
+ }
+
+ public int getExpressionType() {
+ return TYPE_VARIABLE;
+ }
+
+ int countReferenceToEverything() {
+ return IExpression.VARIABLE_EVERYTHING.equals(name) ? 1 : 0;
+ }
+
+ int getPriority() {
+ return PRIORITY_VARIABLE;
+ }
+
+ String getOperator() {
+ return "<variable>"; //$NON-NLS-1$
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/VersionFunction.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/VersionFunction.java
new file mode 100644
index 000000000..7d2694e9e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/VersionFunction.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import org.eclipse.equinox.internal.provisional.p2.metadata.Version;
+
+/**
+ * A function that creates a {@link Version} from a string
+ */
+final class VersionFunction extends Function {
+
+ public VersionFunction(Expression[] operands) {
+ super(assertLength(operands, 1, 1, KEYWORD_VERSION));
+ assertNotBoolean(operands[0], "parameter"); //$NON-NLS-1$
+ assertNotCollection(operands[0], "parameter"); //$NON-NLS-1$
+ }
+
+ boolean assertSingleArgumentClass(Object v) {
+ return v instanceof String;
+ }
+
+ Object createInstance(Object arg) {
+ return Version.create((String) arg);
+ }
+
+ String getOperator() {
+ return KEYWORD_VERSION;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/WrappedIQuery.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/WrappedIQuery.java
new file mode 100644
index 000000000..88fbbdddb
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/WrappedIQuery.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.internal.provisional.p2.metadata.query.Collector;
+import org.eclipse.equinox.internal.provisional.p2.metadata.query.IMatchQuery;
+import org.eclipse.equinox.p2.metadata.query.IQuery;
+import org.eclipse.equinox.p2.ql.IEvaluationContext;
+import org.eclipse.equinox.p2.ql.QLContextQuery;
+
+final class WrappedIQuery extends Function {
+
+ public WrappedIQuery(Expression[] operands) {
+ super(assertLength(operands, 1, 3, KEYWORD_IQUERY));
+ assertNotBoolean(operands[0], "parameter"); //$NON-NLS-1$
+ assertNotCollection(operands[0], "parameter"); //$NON-NLS-1$
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ Object query = operands[0].evaluate(context);
+
+ if (query instanceof IMatchQuery) {
+ if (operands.length > 2)
+ throw new IllegalArgumentException("iquery third argument cannot be combined with a match query"); //$NON-NLS-1$
+
+ Object value = null;
+ if (operands.length > 1)
+ value = operands[1].evaluate(context);
+ else
+ value = Variable.ITEM.evaluate(context);
+ return Boolean.valueOf(((IMatchQuery) query).isMatch(value));
+ }
+
+ if (!(query instanceof IQuery))
+ throw new IllegalArgumentException("iquery first argument must be an IQuery instance"); //$NON-NLS-1$
+
+ Collector collector = null;
+ if (operands.length == 3) {
+ Object cobj = operands[2].evaluate(context);
+ if (cobj instanceof Collector)
+ collector = (Collector) cobj;
+ else if (cobj == null)
+ collector = new Collector();
+ else
+ throw new IllegalArgumentException("iquery third argument must be a collector"); //$NON-NLS-1$
+ }
+
+ Iterator iterator = null;
+ if (operands.length > 1)
+ iterator = operands[1].evaluateAsIterator(context);
+ else
+ iterator = Variable.EVERYTHING.evaluateAsIterator(context);
+
+ if (collector == null) {
+ if (query instanceof QLContextQuery)
+ return ((QLContextQuery) query).evaluate(iterator);
+ collector = new Collector();
+ }
+ return ((IQuery) query).perform(iterator, collector);
+ }
+
+ String getOperator() {
+ return KEYWORD_IQUERY;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/ExpressionParser.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/ExpressionParser.java
new file mode 100644
index 000000000..630b71983
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/ExpressionParser.java
@@ -0,0 +1,849 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.parser;
+
+import org.eclipse.equinox.p2.ql.IExpression;
+
+import java.util.*;
+import org.eclipse.equinox.p2.ql.*;
+
+public class ExpressionParser extends Stack implements IParserConstants, IExpressionParser {
+ private static final long serialVersionUID = 882034383978853143L;
+
+ private static final int TOKEN_OR = 1;
+ private static final int TOKEN_AND = 2;
+
+ private static final int TOKEN_EQUAL = 10;
+ private static final int TOKEN_NOT_EQUAL = 11;
+ private static final int TOKEN_LESS = 12;
+ private static final int TOKEN_LESS_EQUAL = 13;
+ private static final int TOKEN_GREATER = 14;
+ private static final int TOKEN_GREATER_EQUAL = 15;
+ private static final int TOKEN_MATCHES = 16;
+
+ private static final int TOKEN_NOT = 20;
+ private static final int TOKEN_DOT = 21;
+ private static final int TOKEN_COMMA = 22;
+ private static final int TOKEN_PIPE = 23;
+ private static final int TOKEN_DOLLAR = 24;
+ private static final int TOKEN_IF = 25;
+ private static final int TOKEN_ELSE = 26;
+
+ private static final int TOKEN_LP = 30;
+ private static final int TOKEN_RP = 31;
+ private static final int TOKEN_LB = 32;
+ private static final int TOKEN_RB = 33;
+ private static final int TOKEN_LC = 34;
+ private static final int TOKEN_RC = 35;
+
+ private static final int TOKEN_IDENTIFIER = 40;
+ private static final int TOKEN_LITERAL = 41;
+ private static final int TOKEN_ANY = 42;
+
+ private static final int TOKEN_NULL = 50;
+ private static final int TOKEN_TRUE = 51;
+ private static final int TOKEN_FALSE = 52;
+
+ private static final int TOKEN_LATEST = 60;
+ private static final int TOKEN_LIMIT = 61;
+ private static final int TOKEN_FIRST = 62;
+ private static final int TOKEN_FLATTEN = 63;
+ private static final int TOKEN_UNIQUE = 64;
+ private static final int TOKEN_SELECT = 65;
+ private static final int TOKEN_COLLECT = 66;
+ private static final int TOKEN_TRAVERSE = 67;
+ private static final int TOKEN_EXISTS = 68;
+ private static final int TOKEN_ALL = 69;
+
+ private static final int TOKEN_END = 0;
+ private static final int TOKEN_ERROR = -1;
+
+ private static final Map keywords;
+ static {
+ keywords = new HashMap();
+ keywords.put(KEYWORD_ALL, new Integer(TOKEN_ALL));
+ keywords.put(KEYWORD_COLLECT, new Integer(TOKEN_COLLECT));
+ keywords.put(KEYWORD_EXISTS, new Integer(TOKEN_EXISTS));
+ keywords.put(KEYWORD_FALSE, new Integer(TOKEN_FALSE));
+ keywords.put(KEYWORD_FIRST, new Integer(TOKEN_FIRST));
+ keywords.put(KEYWORD_FLATTEN, new Integer(TOKEN_FLATTEN));
+ keywords.put(KEYWORD_LATEST, new Integer(TOKEN_LATEST));
+ keywords.put(KEYWORD_LIMIT, new Integer(TOKEN_LIMIT));
+ keywords.put(KEYWORD_NULL, new Integer(TOKEN_NULL));
+ keywords.put(KEYWORD_SELECT, new Integer(TOKEN_SELECT));
+ keywords.put(KEYWORD_TRAVERSE, new Integer(TOKEN_TRAVERSE));
+ keywords.put(KEYWORD_TRUE, new Integer(TOKEN_TRUE));
+ keywords.put(KEYWORD_UNIQUE, new Integer(TOKEN_UNIQUE));
+ keywords.put(OPERATOR_EACH, new Integer(TOKEN_ANY));
+ }
+
+ private final IExpressionFactory factory;
+
+ private String expression;
+ private int tokenPos;
+ private int currentToken;
+ private int lastTokenPos;
+ private Object tokenValue;
+ private String rootVariable;
+
+ public ExpressionParser(IExpressionFactory factory) {
+ this.factory = factory;
+ }
+
+ public synchronized IMatchExpression parsePredicate(String exprString) {
+ expression = exprString;
+ tokenPos = 0;
+ currentToken = 0;
+ tokenValue = null;
+ rootVariable = IExpression.VARIABLE_ITEM;
+ IExpression itemVariable = factory.variable(IExpression.VARIABLE_ITEM);
+ push(itemVariable);
+ try {
+ nextToken();
+ IExpression expr = currentToken == TOKEN_END ? factory.constant(Boolean.TRUE) : parseCondition();
+ assertToken(TOKEN_END);
+ return factory.matchExpression(expr);
+ } finally {
+ popVariable(); // pop item
+ }
+ }
+
+ public synchronized IContextExpression parseQuery(String exprString) {
+ expression = exprString;
+ tokenPos = 0;
+ currentToken = 0;
+ tokenValue = null;
+ rootVariable = IExpression.VARIABLE_EVERYTHING;
+ IExpression everythingVariable = factory.variable(IExpression.VARIABLE_EVERYTHING);
+ push(everythingVariable);
+ try {
+ nextToken();
+ IExpression expr = parseCondition();
+ assertToken(TOKEN_END);
+ return factory.contextExpression(expr);
+ } finally {
+ popVariable(); // pop context
+ }
+ }
+
+ private IExpression parseCondition() {
+ IExpression expr = parseOr();
+ if (currentToken == TOKEN_IF) {
+ nextToken();
+ IExpression ifTrue = parseOr();
+ assertToken(TOKEN_ELSE);
+ nextToken();
+ expr = factory.condition(expr, ifTrue, parseOr());
+ }
+ return expr;
+ }
+
+ private IExpression parseOr() {
+ IExpression expr = parseAnd();
+ if (currentToken != TOKEN_OR)
+ return expr;
+
+ ArrayList exprs = new ArrayList();
+ exprs.add(expr);
+ do {
+ nextToken();
+ exprs.add(parseAnd());
+ } while (currentToken == TOKEN_OR);
+ return factory.or((IExpression[]) exprs.toArray(new IExpression[exprs.size()]));
+ }
+
+ private IExpression parseAnd() {
+ IExpression expr = parseBinary();
+ if (currentToken != TOKEN_AND)
+ return expr;
+
+ ArrayList exprs = new ArrayList();
+ exprs.add(expr);
+ do {
+ nextToken();
+ exprs.add(parseBinary());
+ } while (currentToken == TOKEN_AND);
+ return factory.and((IExpression[]) exprs.toArray(new IExpression[exprs.size()]));
+ }
+
+ private IExpression parseBinary() {
+ IExpression expr = parseNot();
+ switch (currentToken) {
+ case TOKEN_OR :
+ case TOKEN_AND :
+ case TOKEN_RP :
+ case TOKEN_RB :
+ case TOKEN_RC :
+ case TOKEN_COMMA :
+ case TOKEN_IF :
+ case TOKEN_ELSE :
+ case TOKEN_END :
+ break;
+ case TOKEN_EQUAL :
+ case TOKEN_NOT_EQUAL :
+ case TOKEN_GREATER :
+ case TOKEN_GREATER_EQUAL :
+ case TOKEN_LESS :
+ case TOKEN_LESS_EQUAL :
+ case TOKEN_MATCHES :
+ int realToken = currentToken;
+ nextToken();
+ IExpression rhs = parseNot();
+ switch (realToken) {
+ case TOKEN_EQUAL :
+ expr = factory.equals(expr, rhs);
+ break;
+ case TOKEN_NOT_EQUAL :
+ expr = factory.not(factory.equals(expr, rhs));
+ break;
+ case TOKEN_GREATER :
+ expr = factory.greater(expr, rhs);
+ break;
+ case TOKEN_GREATER_EQUAL :
+ expr = factory.not(factory.less(expr, rhs));
+ break;
+ case TOKEN_LESS :
+ expr = factory.less(expr, rhs);
+ break;
+ case TOKEN_LESS_EQUAL :
+ expr = factory.not(factory.greater(expr, rhs));
+ break;
+ default :
+ expr = factory.matches(expr, rhs);
+ }
+ break;
+ default :
+ throw syntaxError();
+ }
+ return expr;
+ }
+
+ private IExpression parseNot() {
+ if (currentToken == TOKEN_NOT) {
+ nextToken();
+ IExpression expr = parseNot();
+ return factory.not(expr);
+ }
+ return parseCollectionExpression();
+ }
+
+ private IExpression parseCollectionExpression() {
+ IExpression expr;
+ switch (currentToken) {
+ case TOKEN_SELECT :
+ case TOKEN_COLLECT :
+ case TOKEN_EXISTS :
+ case TOKEN_FIRST :
+ case TOKEN_FLATTEN :
+ case TOKEN_ALL :
+ case TOKEN_TRAVERSE :
+ case TOKEN_LATEST :
+ case TOKEN_LIMIT :
+ case TOKEN_UNIQUE :
+ expr = getVariableOrRootMember(rootVariable);
+ break;
+ default :
+ expr = parseMember();
+ if (currentToken != TOKEN_DOT)
+ return expr;
+ nextToken();
+ }
+
+ for (;;) {
+ int filterToken = currentToken;
+ nextToken();
+ assertToken(TOKEN_LP);
+ nextToken();
+ switch (filterToken) {
+ case TOKEN_SELECT :
+ expr = factory.select(expr, parseLambdaDefinition());
+ break;
+ case TOKEN_COLLECT :
+ expr = factory.collect(expr, parseLambdaDefinition());
+ break;
+ case TOKEN_EXISTS :
+ expr = factory.exists(expr, parseLambdaDefinition());
+ break;
+ case TOKEN_FIRST :
+ expr = factory.first(expr, parseLambdaDefinition());
+ break;
+ case TOKEN_ALL :
+ expr = factory.all(expr, parseLambdaDefinition());
+ break;
+ case TOKEN_TRAVERSE :
+ expr = factory.traverse(expr, parseLambdaDefinition());
+ break;
+ case TOKEN_LATEST :
+ if (currentToken == TOKEN_RP) {
+ expr = factory.latest(expr);
+ assertToken(TOKEN_RP);
+ nextToken();
+ } else
+ expr = factory.latest(factory.select(expr, parseLambdaDefinition()));
+ break;
+ case TOKEN_FLATTEN :
+ if (currentToken == TOKEN_RP) {
+ expr = factory.flatten(expr);
+ assertToken(TOKEN_RP);
+ nextToken();
+ } else
+ expr = factory.flatten(factory.select(expr, parseLambdaDefinition()));
+ break;
+ case TOKEN_LIMIT :
+ expr = factory.limit(expr, parseCondition());
+ assertToken(TOKEN_RP);
+ nextToken();
+ break;
+ case TOKEN_UNIQUE :
+ if (currentToken == TOKEN_RP)
+ expr = factory.unique(expr, factory.constant(null));
+ else {
+ expr = factory.unique(expr, parseMember());
+ assertToken(TOKEN_RP);
+ nextToken();
+ }
+ break;
+ default :
+ throw syntaxError();
+ }
+ if (currentToken != TOKEN_DOT)
+ break;
+ nextToken();
+ }
+ return expr;
+ }
+
+ private IExpression parseMember() {
+ IExpression expr = parseConstructor();
+ String name;
+ while (currentToken == TOKEN_DOT || currentToken == TOKEN_LB) {
+ int savePos = tokenPos;
+ int saveToken = currentToken;
+ Object saveTokenValue = tokenValue;
+ nextToken();
+ if (saveToken == TOKEN_DOT) {
+ switch (currentToken) {
+ case TOKEN_SELECT :
+ case TOKEN_COLLECT :
+ case TOKEN_EXISTS :
+ case TOKEN_FIRST :
+ case TOKEN_FLATTEN :
+ case TOKEN_ALL :
+ case TOKEN_TRAVERSE :
+ case TOKEN_LATEST :
+ case TOKEN_LIMIT :
+ case TOKEN_UNIQUE :
+ tokenPos = savePos;
+ currentToken = saveToken;
+ tokenValue = saveTokenValue;
+ return expr;
+
+ case TOKEN_IDENTIFIER :
+ name = (String) tokenValue;
+ nextToken();
+ if (currentToken == TOKEN_LP) {
+ nextToken();
+ IExpression[] callArgs = parseArray();
+ assertToken(TOKEN_RP);
+ nextToken();
+ expr = factory.memberCall(expr, name, callArgs);
+ } else
+ expr = factory.memberCall(expr, name, IExpressionFactory.NO_ARGS);
+ break;
+
+ default :
+ throw syntaxError();
+ }
+ } else {
+ IExpression atExpr = parseMember();
+ assertToken(TOKEN_RB);
+ nextToken();
+ expr = factory.at(expr, atExpr);
+ }
+ }
+ return expr;
+ }
+
+ private IExpression parseLambdaDefinition() {
+ boolean endingRC = false;
+ int anyIndex = -1;
+ IExpression[] initializers = IExpressionFactory.NO_ARGS;
+ IExpression[] variables;
+ if (currentToken == TOKEN_LC) {
+ // Lambda starts without currying.
+ endingRC = true;
+ nextToken();
+ anyIndex = 0;
+ variables = parseVariables();
+ if (variables == null)
+ // empty means no pipe at the end.
+ throw syntaxError();
+ } else {
+ anyIndex = 0;
+ variables = parseVariables();
+ if (variables == null) {
+ anyIndex = -1;
+ initializers = parseArray();
+ assertToken(TOKEN_LC);
+ nextToken();
+ endingRC = true;
+ for (int idx = 0; idx < initializers.length; ++idx) {
+ IExpression initializer = initializers[idx];
+ if (initializer.getExpressionType() == IExpression.TYPE_VARIABLE && OPERATOR_EACH.equals(initializer.toString())) {
+ if (anyIndex == -1)
+ anyIndex = idx;
+ else
+ anyIndex = -1; // Second Each. This is illegal
+ break;
+ }
+ }
+ if (anyIndex == -1)
+ throw new IllegalArgumentException("Exaclty one _ must be present among the currying expressions"); //$NON-NLS-1$
+
+ variables = parseVariables();
+ if (variables == null)
+ // empty means no pipe at the end.
+ throw syntaxError();
+ }
+
+ }
+ nextToken();
+ IExpression body = parseCondition();
+ if (endingRC) {
+ assertToken(TOKEN_RC);
+ nextToken();
+ }
+
+ assertToken(TOKEN_RP);
+ nextToken();
+ IExpression each;
+ IExpression[] assignments;
+ if (initializers.length == 0) {
+ if (variables.length != 1)
+ throw new IllegalArgumentException("Must have exactly one variable unless currying is used"); //$NON-NLS-1$
+ each = variables[0];
+ assignments = IExpressionFactory.NO_ARGS;
+ } else {
+ if (initializers.length != variables.length)
+ throw new IllegalArgumentException("Number of currying expressions and variables differ"); //$NON-NLS-1$
+
+ if (initializers.length == 1) {
+ // This is just a map from _ to some variable
+ each = variables[0];
+ assignments = IExpressionFactory.NO_ARGS;
+ } else {
+ int idx;
+ each = variables[anyIndex];
+ assignments = new IExpression[initializers.length - 1];
+ for (idx = 0; idx < anyIndex; ++idx)
+ assignments[idx] = factory.assignment(variables[idx], initializers[idx]);
+ for (++idx; idx < initializers.length; ++idx)
+ assignments[idx] = factory.assignment(variables[idx], initializers[idx]);
+ }
+ }
+ return factory.lambda(each, body, assignments);
+ }
+
+ private IExpression[] parseVariables() {
+ int savePos = tokenPos;
+ int saveToken = currentToken;
+ Object saveTokenValue = tokenValue;
+ List ids = null;
+ while (currentToken == TOKEN_IDENTIFIER) {
+ if (ids == null)
+ ids = new ArrayList();
+ ids.add(tokenValue);
+ nextToken();
+ if (currentToken == TOKEN_COMMA) {
+ nextToken();
+ continue;
+ }
+ break;
+ }
+
+ if (currentToken != TOKEN_PIPE) {
+ // This was not a variable list
+ tokenPos = savePos;
+ currentToken = saveToken;
+ tokenValue = saveTokenValue;
+ return null;
+ }
+
+ if (ids == null)
+ // Empty list but otherwise OK
+ return IExpressionFactory.NO_ARGS;
+
+ int top = ids.size();
+ IExpression[] result = new IExpression[top];
+ for (int idx = 0; idx < top; ++idx) {
+ String name = (String) ids.get(idx);
+ IExpression var = factory.variable(name);
+ push(var);
+ result[idx] = var;
+ }
+ return result;
+ }
+
+ private IExpression parseConstructor() {
+ if (currentToken == TOKEN_IDENTIFIER) {
+ int savePos = tokenPos;
+ int saveToken = currentToken;
+ Object saveTokenValue = tokenValue;
+
+ Object function = factory.getFunctionMap().get(tokenValue);
+ if (function != null) {
+ nextToken();
+ if (currentToken == TOKEN_LP) {
+ nextToken();
+ IExpression[] args = currentToken == TOKEN_RP ? IExpressionFactory.NO_ARGS : parseArray();
+ assertToken(TOKEN_RP);
+ nextToken();
+ return factory.function(function, args);
+ }
+ tokenPos = savePos;
+ currentToken = saveToken;
+ tokenValue = saveTokenValue;
+ }
+ }
+ return parseUnary();
+ }
+
+ private IExpression parseUnary() {
+ IExpression expr;
+ switch (currentToken) {
+ case TOKEN_LP :
+ nextToken();
+ expr = parseCondition();
+ assertToken(TOKEN_RP);
+ nextToken();
+ break;
+ case TOKEN_LB :
+ nextToken();
+ expr = factory.array(parseArray());
+ assertToken(TOKEN_RB);
+ nextToken();
+ break;
+ case TOKEN_LITERAL :
+ expr = factory.constant(tokenValue);
+ nextToken();
+ break;
+ case TOKEN_DOLLAR :
+ expr = parseParameter();
+ break;
+ case TOKEN_IDENTIFIER :
+ expr = getVariableOrRootMember((String) tokenValue);
+ nextToken();
+ break;
+ case TOKEN_ANY :
+ expr = factory.variable(OPERATOR_EACH);
+ nextToken();
+ break;
+ case TOKEN_NULL :
+ expr = factory.constant(null);
+ nextToken();
+ break;
+ case TOKEN_TRUE :
+ expr = factory.constant(Boolean.TRUE);
+ nextToken();
+ break;
+ case TOKEN_FALSE :
+ expr = factory.constant(Boolean.FALSE);
+ nextToken();
+ break;
+ default :
+ throw syntaxError();
+ }
+ return expr;
+ }
+
+ private IExpression parseParameter() {
+ if (currentToken == TOKEN_DOLLAR) {
+ nextToken();
+
+ IExpression param = null;
+ if (currentToken == TOKEN_LITERAL && tokenValue instanceof Integer)
+ param = factory.indexedParameter(((Integer) tokenValue).intValue());
+ else if (currentToken == TOKEN_IDENTIFIER)
+ param = factory.keyedParameter((String) tokenValue);
+
+ if (param != null) {
+ nextToken();
+ return param;
+ }
+ }
+ throw syntaxError();
+ }
+
+ private IExpression[] parseArray() {
+ IExpression expr = parseCondition();
+ if (currentToken != TOKEN_COMMA)
+ return new IExpression[] {expr};
+
+ ArrayList operands = new ArrayList();
+ operands.add(expr);
+ do {
+ nextToken();
+ if (currentToken == TOKEN_LC)
+ // We don't allow lambdas in the array
+ break;
+ operands.add(parseCondition());
+ } while (currentToken == TOKEN_COMMA);
+ return (IExpression[]) operands.toArray(new IExpression[operands.size()]);
+ }
+
+ private void assertToken(int token) {
+ if (currentToken != token)
+ throw syntaxError();
+ }
+
+ private IExpression getVariableOrRootMember(String id) {
+ int idx = size();
+ while (--idx >= 0) {
+ IExpression v = (IExpression) get(idx);
+ if (id.equals(v.toString()))
+ return v;
+ }
+
+ if (rootVariable.equals(id))
+ throw syntaxError("No such variable: " + id); //$NON-NLS-1$
+
+ return factory.memberCall(getVariableOrRootMember(rootVariable), id, IExpressionFactory.NO_ARGS);
+ }
+
+ private void nextToken() {
+ tokenValue = null;
+ int top = expression.length();
+ char c = 0;
+ while (tokenPos < top) {
+ c = expression.charAt(tokenPos);
+ if (!Character.isWhitespace(c))
+ break;
+ ++tokenPos;
+ }
+ if (tokenPos >= top) {
+ lastTokenPos = top;
+ currentToken = TOKEN_END;
+ return;
+ }
+
+ lastTokenPos = tokenPos;
+ switch (c) {
+ case '|' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '|') {
+ tokenValue = OPERATOR_OR;
+ currentToken = TOKEN_OR;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_PIPE;
+ ++tokenPos;
+ }
+ break;
+
+ case '&' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '&') {
+ tokenValue = OPERATOR_ARRAY;
+ currentToken = TOKEN_AND;
+ tokenPos += 2;
+ } else
+ currentToken = TOKEN_ERROR;
+ break;
+
+ case '=' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_EQUALS;
+ currentToken = TOKEN_EQUAL;
+ tokenPos += 2;
+ } else
+ currentToken = TOKEN_ERROR;
+ break;
+
+ case '!' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_NOT_EQUALS;
+ currentToken = TOKEN_NOT_EQUAL;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_NOT;
+ ++tokenPos;
+ }
+ break;
+
+ case '~' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = IParserConstants.OPERATOR_MATCHES;
+ currentToken = TOKEN_MATCHES;
+ tokenPos += 2;
+ } else
+ currentToken = TOKEN_ERROR;
+ break;
+
+ case '>' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_GT_EQUAL;
+ currentToken = TOKEN_GREATER_EQUAL;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_GREATER;
+ ++tokenPos;
+ }
+ break;
+
+ case '<' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_LT_EQUAL;
+ currentToken = TOKEN_LESS_EQUAL;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_LESS;
+ ++tokenPos;
+ }
+ break;
+
+ case '?' :
+ currentToken = TOKEN_IF;
+ ++tokenPos;
+ break;
+
+ case ':' :
+ currentToken = TOKEN_ELSE;
+ ++tokenPos;
+ break;
+
+ case '.' :
+ currentToken = TOKEN_DOT;
+ ++tokenPos;
+ break;
+
+ case '$' :
+ currentToken = TOKEN_DOLLAR;
+ ++tokenPos;
+ break;
+
+ case '{' :
+ currentToken = TOKEN_LC;
+ ++tokenPos;
+ break;
+
+ case '}' :
+ currentToken = TOKEN_RC;
+ ++tokenPos;
+ break;
+
+ case '(' :
+ currentToken = TOKEN_LP;
+ ++tokenPos;
+ break;
+
+ case ')' :
+ currentToken = TOKEN_RP;
+ ++tokenPos;
+ break;
+
+ case '[' :
+ currentToken = TOKEN_LB;
+ ++tokenPos;
+ break;
+
+ case ']' :
+ currentToken = TOKEN_RB;
+ ++tokenPos;
+ break;
+
+ case ',' :
+ currentToken = TOKEN_COMMA;
+ ++tokenPos;
+ break;
+
+ case '"' :
+ case '\'' : {
+ int start = ++tokenPos;
+ while (tokenPos < top && expression.charAt(tokenPos) != c)
+ ++tokenPos;
+ if (tokenPos == top) {
+ tokenPos = start - 1;
+ currentToken = TOKEN_ERROR;
+ } else {
+ tokenValue = expression.substring(start, tokenPos++);
+ currentToken = TOKEN_LITERAL;
+ }
+ break;
+ }
+
+ case '/' : {
+ int start = ++tokenPos;
+ StringBuffer buf = new StringBuffer();
+ while (tokenPos < top) {
+ c = expression.charAt(tokenPos);
+ if (c == '\\' && tokenPos + 1 < top) {
+ c = expression.charAt(++tokenPos);
+ if (c != '/')
+ buf.append('\\');
+ } else if (c == '/')
+ break;
+ buf.append(c);
+ ++tokenPos;
+ }
+ if (tokenPos == top) {
+ tokenPos = start - 1;
+ currentToken = TOKEN_ERROR;
+ } else {
+ tokenValue = SimplePattern.compile(expression.substring(start, tokenPos++));
+ currentToken = TOKEN_LITERAL;
+ }
+ break;
+ }
+
+ default :
+ if (Character.isDigit(c)) {
+ int start = tokenPos++;
+ while (tokenPos < top && Character.isDigit(expression.charAt(tokenPos)))
+ ++tokenPos;
+ tokenValue = Integer.valueOf(expression.substring(start, tokenPos));
+ currentToken = TOKEN_LITERAL;
+ break;
+ }
+ if (Character.isJavaIdentifierStart(c)) {
+ int start = tokenPos++;
+ while (tokenPos < top && Character.isJavaIdentifierPart(expression.charAt(tokenPos)))
+ ++tokenPos;
+ String word = expression.substring(start, tokenPos);
+ Integer token = (Integer) keywords.get(word);
+ if (token == null)
+ currentToken = TOKEN_IDENTIFIER;
+ else
+ currentToken = token.intValue();
+ tokenValue = word;
+ break;
+ }
+ throw syntaxError();
+ }
+ }
+
+ private void popVariable() {
+ if (isEmpty())
+ throw syntaxError();
+ pop();
+ }
+
+ private QLParseException syntaxError() {
+ Object tv = tokenValue;
+ if (tv == null) {
+ if (lastTokenPos >= expression.length())
+ return syntaxError("Unexpeced end of expression"); //$NON-NLS-1$
+ tv = expression.substring(lastTokenPos, lastTokenPos + 1);
+ }
+ return syntaxError("Unexpected token \"" + tv + '"'); //$NON-NLS-1$
+ }
+
+ private QLParseException syntaxError(String message) {
+ return new QLParseException(expression, message, tokenPos);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/IParserConstants.java b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/IParserConstants.java
new file mode 100644
index 000000000..381ad712b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/parser/IParserConstants.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.ql.parser;
+
+public interface IParserConstants {
+
+ String KEYWORD_ALL = "all"; //$NON-NLS-1$
+ String KEYWORD_BOOLEAN = "boolean"; //$NON-NLS-1$
+ String KEYWORD_CAPABILITY_INDEX = "capabilityIndex"; //$NON-NLS-1$
+ String KEYWORD_CLASS = "class"; //$NON-NLS-1$
+ String KEYWORD_COLLECT = "collect"; //$NON-NLS-1$
+ String KEYWORD_EXISTS = "exists"; //$NON-NLS-1$
+ String KEYWORD_FALSE = "false"; //$NON-NLS-1$
+ String KEYWORD_FILTER = "filter"; //$NON-NLS-1$
+ String KEYWORD_FIRST = "first"; //$NON-NLS-1$
+ String KEYWORD_FLATTEN = "flatten"; //$NON-NLS-1$
+ String KEYWORD_IQUERY = "iquery"; //$NON-NLS-1$
+ String KEYWORD_LATEST = "latest"; //$NON-NLS-1$
+ String KEYWORD_LIMIT = "limit"; //$NON-NLS-1$
+ String KEYWORD_LOCALIZED_KEYS = "localizedKeys"; //$NON-NLS-1$
+ String KEYWORD_LOCALIZED_MAP = "localizedMap"; //$NON-NLS-1$
+ String KEYWORD_LOCALIZED_PROPERTY = "localizedProperty"; //$NON-NLS-1$
+ String KEYWORD_NULL = "null"; //$NON-NLS-1$
+ String KEYWORD_RANGE = "range"; //$NON-NLS-1$
+ String KEYWORD_SATISFIES_ALL = "satisfiesAll"; //$NON-NLS-1$
+ String KEYWORD_SATISFIES_ANY = "satisfiesAny"; //$NON-NLS-1$
+ String KEYWORD_SELECT = "select"; //$NON-NLS-1$
+ String KEYWORD_SET = "set"; //$NON-NLS-1$
+ String KEYWORD_TRAVERSE = "traverse"; //$NON-NLS-1$
+ String KEYWORD_TRUE = "true"; //$NON-NLS-1$
+ String KEYWORD_UNIQUE = "unique"; //$NON-NLS-1$
+ String KEYWORD_VERSION = "version"; //$NON-NLS-1$
+
+ String OPERATOR_AND = "&&"; //$NON-NLS-1$
+ String OPERATOR_ARRAY = "[]"; //$NON-NLS-1$
+ String OPERATOR_ASSIGN = "="; //$NON-NLS-1$
+ String OPERATOR_AT = "[]"; //$NON-NLS-1$
+ String OPERATOR_EACH = "_"; //$NON-NLS-1$
+ String OPERATOR_ELSE = ":"; //$NON-NLS-1$
+ String OPERATOR_EQUALS = "=="; //$NON-NLS-1$
+ String OPERATOR_GT = ">"; //$NON-NLS-1$
+ String OPERATOR_GT_EQUAL = ">="; //$NON-NLS-1$
+ String OPERATOR_IF = "?"; //$NON-NLS-1$
+ String OPERATOR_LT = "<"; //$NON-NLS-1$
+ String OPERATOR_LT_EQUAL = "<="; //$NON-NLS-1$
+ String OPERATOR_MATCHES = "~="; //$NON-NLS-1$
+ String OPERATOR_MEMBER = "."; //$NON-NLS-1$
+ String OPERATOR_NOT = "!"; //$NON-NLS-1$
+ String OPERATOR_NOT_EQUALS = "!="; //$NON-NLS-1$
+ String OPERATOR_OR = "||"; //$NON-NLS-1$
+ String OPERATOR_PARAMETER = "$"; //$NON-NLS-1$
+
+ int PRIORITY_PARAMETER = 1;
+ int PRIORITY_VARIABLE = 1;
+ int PRIORITY_LITERAL = 1;
+ int PRIORITY_CONSTRUCTOR = 2;
+ int PRIORITY_MEMBER = 3;
+ int PRIORITY_COLLECTION = 4;
+ int PRIORITY_NOT = 5;
+ int PRIORITY_BINARY = 6;
+ int PRIORITY_AND = 7;
+ int PRIORITY_OR = 8;
+ int PRIORITY_CONDITION = 9;
+ int PRIORITY_ASSIGNMENT = 10;
+ int PRIORITY_LAMBDA = 11;
+ int PRIORITY_COMMA = 12;
+}

Back to the top