Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java')
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java341
1 files changed, 341 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java
new file mode 100644
index 000000000..75f47dd94
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * 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.metadata.expression;
+
+import java.util.*;
+import org.eclipse.equinox.p2.metadata.expression.*;
+
+/**
+ * The base class of the expression tree.
+ */
+public abstract class Expression implements IExpression, Comparable<Expression>, IExpressionConstants {
+
+ static final Expression[] emptyArray = new Expression[0];
+
+ public static void appendOperand(StringBuffer bld, Variable rootVariable, Expression operand, int priority) {
+ if (priority < operand.getPriority()) {
+ bld.append('(');
+ operand.toString(bld, rootVariable);
+ bld.append(')');
+ } else
+ operand.toString(bld, rootVariable);
+ }
+
+ public 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;
+ }
+
+ public 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;
+ }
+
+ public static int compare(Expression[] arr1, Expression[] arr2) {
+ int max = arr1.length;
+ if (max > arr2.length)
+ max = arr2.length;
+ for (int idx = 0; idx < max; ++idx) {
+ int cmp = arr1[idx].compareTo(arr2[idx]);
+ if (cmp != 0)
+ return cmp;
+ }
+ if (max == arr2.length) {
+ if (max < arr1.length)
+ return 1;
+ return 0;
+ }
+ return -1;
+ }
+
+ public static boolean equals(Expression[] arr1, Expression[] arr2) {
+ int idx = arr1.length;
+ if (idx != arr2.length)
+ return false;
+ while (--idx >= 0)
+ if (!arr1[idx].equals(arr2[idx]))
+ return false;
+ return true;
+ }
+
+ public static int hashCode(Expression[] arr) {
+ int idx = arr.length;
+ int result = 1;
+ while (--idx >= 0)
+ result = 31 * result + arr[idx].hashCode();
+ return result;
+ }
+
+ public static void elementsToString(StringBuffer bld, Variable rootVariable, Expression[] elements) {
+ int top = elements.length;
+ if (top > 0) {
+ elements[0].toString(bld, rootVariable);
+ for (int idx = 1; idx < top; ++idx) {
+ bld.append(", "); //$NON-NLS-1$
+ appendOperand(bld, rootVariable, elements[idx], PRIORITY_MAX);
+ }
+ }
+ }
+
+ /**
+ * 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.visit(this);
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = getPriority() - e.getPriority();
+ if (cmp == 0) {
+ int e1 = getExpressionType();
+ int e2 = e.getExpressionType();
+ cmp = e1 > e2 ? 1 : (e1 == e2 ? 0 : -1);
+ }
+ return cmp;
+ }
+
+ public boolean equals(Object e) {
+ if (e == this)
+ return true;
+ if (e == null || getClass() != e.getClass())
+ return false;
+ return getExpressionType() == ((Expression) e).getExpressionType();
+ }
+
+ /**
+ * 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;
+ }
+
+ public abstract String getOperator();
+
+ public abstract int getPriority();
+
+ public boolean isRootVariable() {
+ return false;
+ }
+
+ public final String toLDAPString() {
+ StringBuffer bld = new StringBuffer();
+ toLDAPString(bld);
+ return bld.toString();
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ throw new UnsupportedOperationException();
+ }
+
+ public final String toString() {
+ StringBuffer bld = new StringBuffer();
+ toString(bld);
+ return bld.toString();
+ }
+
+ public void toString(StringBuffer bld) {
+ toString(bld, ExpressionFactory.THIS);
+ }
+
+ public abstract void toString(StringBuffer bld, Variable rootVariable);
+
+ private static class Compacter {
+ private Expression base;
+
+ private List<Expression> parts;
+
+ private int op;
+
+ Compacter(Expression base, int op) {
+ this.base = base;
+ this.op = op;
+ }
+
+ Expression getResultingFilter() {
+ if (parts == null)
+ return base;
+
+ int partsOp = op == TYPE_AND ? TYPE_OR : TYPE_AND;
+ return addFilter(base, normalize(parts, partsOp), op);
+ }
+
+ boolean merge(Expression b) {
+ Expression[] aArr;
+ Expression[] bArr;
+ if (base.getExpressionType() == op)
+ aArr = getFilterImpls(base);
+ else
+ aArr = new Expression[] {base};
+
+ if (b.getExpressionType() == op)
+ bArr = getFilterImpls(b);
+ else
+ bArr = new Expression[] {b};
+
+ List<Expression> common = null;
+ List<Expression> onlyA = null;
+
+ int atop = aArr.length;
+ int btop = bArr.length;
+ int aidx;
+ int bidx;
+ for (aidx = 0; aidx < atop; ++aidx) {
+ Expression af = aArr[aidx];
+ for (bidx = 0; bidx < btop; ++bidx) {
+ Expression bf = bArr[bidx];
+ if (af.equals(bf)) {
+ if (common == null)
+ common = new ArrayList<Expression>();
+ common.add(af);
+ break;
+ }
+ }
+ if (bidx == btop) {
+ if (onlyA == null)
+ onlyA = new ArrayList<Expression>();
+ onlyA.add(af);
+ }
+ }
+ if (common == null)
+ // Nothing in common
+ return false;
+
+ if (onlyA == null && parts == null)
+ return true;
+
+ List<Expression> onlyB = null;
+ for (bidx = 0; bidx < btop; ++bidx) {
+ Expression bf = bArr[bidx];
+ for (aidx = 0; aidx < atop; ++aidx)
+ if (bf.equals(aArr[aidx]))
+ break;
+ if (aidx == atop) {
+ if (onlyB == null)
+ onlyB = new ArrayList<Expression>();
+ onlyB.add(bf);
+ }
+ }
+
+ if (onlyB == null && parts == null) {
+ // All of B is already covered by base
+ base = b;
+ return true;
+ }
+
+ if (parts == null)
+ parts = new ArrayList<Expression>();
+
+ if (onlyA != null) {
+ base = normalize(common, op);
+ Expression af = normalize(onlyA, op);
+ if (!parts.contains(af))
+ parts.add(af);
+ }
+ Expression bf = normalize(onlyB, op);
+ if (!parts.contains(bf))
+ parts.add(bf);
+ return true;
+ }
+ }
+
+ static Expression addFilter(Expression base, Expression subFilter, int expressionType) {
+ if (base.equals(subFilter))
+ return base;
+
+ ArrayList<Expression> filters = new ArrayList<Expression>(2);
+ filters.add(base);
+ filters.add(subFilter);
+ return normalize(filters, expressionType);
+ }
+
+ static Expression normalize(List<Expression> operands, int op) {
+ int top = operands.size();
+ if (top == 1)
+ return operands.get(0);
+
+ // a | (b | c) becomes a | b | c
+ // a & (b & c) becomes a & b & c
+ //
+ for (int idx = 0; idx < top; ++idx) {
+ Expression f = operands.get(idx);
+ if (f.getExpressionType() != op)
+ continue;
+
+ Expression[] sfs = getFilterImpls(f);
+ operands.remove(idx);
+ --top;
+ for (int ndx = 0; ndx < sfs.length; ++ndx) {
+ Expression nf = sfs[ndx];
+ if (!operands.contains(nf))
+ operands.add(nf);
+ }
+ }
+ top = operands.size();
+ if (top == 1)
+ return operands.get(0);
+
+ Collections.sort(operands);
+ List<Compacter> splits = new ArrayList<Compacter>();
+ int reverseOp = op == TYPE_AND ? TYPE_OR : TYPE_AND;
+
+ for (int idx = 0; idx < top; ++idx)
+ merge(splits, operands.get(idx), reverseOp);
+
+ operands.clear();
+ top = splits.size();
+ for (int idx = 0; idx < top; ++idx) {
+ Expression filter = splits.get(idx).getResultingFilter();
+ if (!operands.contains(filter))
+ operands.add(filter);
+ }
+ top = operands.size();
+ if (top == 1)
+ return operands.get(0);
+
+ Collections.sort(operands);
+ Expression[] expArray = operands.toArray(new Expression[top]);
+ return op == TYPE_AND ? new And(expArray) : new Or(expArray);
+ }
+
+ static void merge(List<Compacter> splits, Expression base, int op) {
+ int top = splits.size();
+ for (int idx = 0; idx < top; ++idx) {
+ Compacter split = splits.get(idx);
+ if (split.merge(base))
+ return;
+ }
+ splits.add(new Compacter(base, op));
+ }
+
+ static Expression[] getFilterImpls(Expression expression) {
+ if (expression instanceof NAry)
+ return ((NAry) expression).operands;
+ throw new IllegalArgumentException();
+ }
+}

Back to the top