diff options
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Member.java')
-rw-r--r-- | bundles/org.eclipse.equinox.p2.ql/src/org/eclipse/equinox/internal/p2/ql/expression/Member.java | 194 |
1 files changed, 194 insertions, 0 deletions
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 "value" 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$ + } + } +} |