/******************************************************************************* * Copyright (c) 2010 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.index; import java.util.ArrayList; import java.util.Iterator; import org.eclipse.equinox.internal.p2.metadata.expression.*; import org.eclipse.equinox.p2.metadata.expression.*; import org.eclipse.equinox.p2.metadata.index.IIndex; public abstract class Index<T> implements IIndex<T> { protected static boolean isIndexedMember(IExpression expr, IExpression variable, String memberName) { if (expr instanceof Member) { Member member = (Member) expr; return member.getOperand() == variable && member.getName().equals(memberName); } return false; } protected static Object concatenateUnique(Object previous, Object toAdd) { if (previous == null || toAdd == null || toAdd == Boolean.FALSE) return toAdd; if (previous instanceof ArrayList<?>) { @SuppressWarnings("unchecked") ArrayList<Object> prevArr = (ArrayList<Object>) previous; if (!prevArr.contains(toAdd)) prevArr.add(toAdd); return previous; } if (previous.equals(toAdd)) return previous; ArrayList<Object> arr = new ArrayList<Object>(); arr.add(previous); arr.add(toAdd); return arr; } protected Object getQueriedIDs(IEvaluationContext ctx, IExpression variable, String memberName, IExpression booleanExpr, Object queriedKeys) { IExpression targetExpr = booleanExpr; if (booleanExpr instanceof IMatchExpression<?>) { targetExpr = ((Unary) targetExpr).operand; ctx = ((IMatchExpression<?>) booleanExpr).createContext(); } int type = targetExpr.getExpressionType(); switch (type) { case IExpression.TYPE_EQUALS : Binary eqExpr = (Binary) targetExpr; IExpression lhs = eqExpr.lhs; IExpression rhs = eqExpr.rhs; if (isIndexedMember(lhs, variable, memberName)) { Object val = safeEvaluate(ctx, rhs); if (val == null) return null; return concatenateUnique(queriedKeys, val); } if (isIndexedMember(rhs, variable, memberName)) { Object val = safeEvaluate(ctx, lhs); if (val == null) return null; return concatenateUnique(queriedKeys, val); } // Not applicable for indexing return null; case IExpression.TYPE_AND : // AND is OK if at least one of the branches require the queried key for (IExpression expr : ExpressionUtil.getOperands(targetExpr)) { Object test = getQueriedIDs(ctx, variable, memberName, expr, queriedKeys); if (test != null) { if (test == Boolean.FALSE) // Failing exists so the AND will fail altogether return test; // It's safe to break here since an and'ing several queries // for different keys and the same input will yield false anyway. return test; } } return null; case IExpression.TYPE_OR : // OR is OK if all the branches require the queried key for (IExpression expr : ExpressionUtil.getOperands(targetExpr)) { Object test = getQueriedIDs(ctx, variable, memberName, expr, queriedKeys); if (test == null) // This branch did not require the key so index cannot be used return null; if (test == Boolean.FALSE) // Branch will always fail regardless of input, so just ignore continue; queriedKeys = test; } return queriedKeys; case IExpression.TYPE_EXISTS : case IExpression.TYPE_ALL : // We must evaluate the lhs to find the referenced keys // CollectionFilter cf = (CollectionFilter) targetExpr; Iterator<?> values; try { values = cf.getOperand().evaluateAsIterator(ctx); } catch (IllegalArgumentException e) { return null; } if (!values.hasNext()) // No keys are requested but we know that an exists must // fail at this point. An all will however succeed regardless // of what is used as input. return type == IExpression.TYPE_ALL ? null : Boolean.FALSE; LambdaExpression lambda = cf.lambda; IEvaluationContext lambdaCtx = lambda.prolog(ctx); Variable lambdaVar = lambda.getItemVariable(); IExpression filterExpr = lambda.getOperand(); do { lambdaVar.setValue(lambdaCtx, values.next()); queriedKeys = getQueriedIDs(lambdaCtx, variable, memberName, filterExpr, queriedKeys); if (queriedKeys == null) // No use continuing. The expression does not require the key return null; } while (values.hasNext()); return queriedKeys; } return null; } private static Object safeEvaluate(IEvaluationContext ctx, IExpression expr) { try { return expr.evaluate(ctx); } catch (IllegalArgumentException e) { return null; } } }