blob: 59e82a0f24a769bd434f2ed74afeae102ccb79d8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 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.expression;
import java.lang.reflect.*;
import java.util.*;
import org.eclipse.equinox.p2.metadata.expression.*;
/**
* <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>
*/
public abstract class Member extends Unary {
public static final class DynamicMember extends Member {
private static final String GET_PREFIX = "get"; //$NON-NLS-1$
private static final String IS_PREFIX = "is"; //$NON-NLS-1$
private static final Class<?>[] NO_ARG_TYPES = new Class[0];
private Class<?> lastClass;
private transient Method method;
private transient String methodName;
DynamicMember(Expression operand, String name) {
super(operand, name, Expression.emptyArray);
}
public Object evaluate(IEvaluationContext context) {
return invoke(operand.evaluate(context));
}
public final Object invoke(Object self) {
if (self instanceof IMemberProvider)
return ((IMemberProvider) self).getMember(name);
if (self == null)
throw new IllegalArgumentException("Cannot access member \'" + name + "\' in null"); //$NON-NLS-1$//$NON-NLS-2$
Class<?> c = self.getClass();
synchronized (this) {
if (methodName == null) {
String n = name;
if (!(n.startsWith(GET_PREFIX) || n.startsWith(IS_PREFIX)))
n = GET_PREFIX + Character.toUpperCase(n.charAt(0)) + n.substring(1);
methodName = n;
}
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$
}
}
}
public static class LengthMember extends Member {
private static final Integer ZERO = new Integer(0);
LengthMember(Expression operand) {
super(operand, "length", Expression.emptyArray); //$NON-NLS-1$
}
public Object evaluate(IEvaluationContext context) {
int len = getLength(operand.evaluate(context));
return len == 0 ? ZERO : new Integer(len);
}
int getLength(Object val) {
if (val == null)
return 0;
if (val.getClass().isArray())
return java.lang.reflect.Array.getLength(val);
if (val instanceof Collection<?>)
return ((Collection<?>) val).size();
if (val instanceof String)
return ((String) val).length();
if (val instanceof Map<?, ?>)
return ((Map<?, ?>) val).size();
return 0;
}
}
public static class EmptyMember extends LengthMember {
EmptyMember(Expression operand) {
super(operand);
}
public Object evaluate(IEvaluationContext context) {
Object val = operand.evaluate(context);
boolean empty = (val instanceof Iterator<?>) ? !((Iterator<?>) val).hasNext() : getLength(val) == 0;
return Boolean.valueOf(empty);
}
}
static final Object[] NO_ARGS = new Object[0];
static Member createDynamicMember(Expression operand, String name) {
return new DynamicMember(operand, name);
}
protected final Expression[] argExpressions;
final String name;
protected Member(Expression operand, String name, Expression[] args) {
super(operand);
this.name = name.intern();
this.argExpressions = args;
}
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;
}
public int compareTo(Expression e) {
int cmp = super.compareTo(e);
if (cmp == 0) {
cmp = name.compareTo(((Member) e).name);
if (cmp == 0)
cmp = compare(argExpressions, ((Member) e).argExpressions);
}
return cmp;
}
public boolean equals(Object o) {
if (super.equals(o)) {
Member mo = (Member) o;
return name.equals(mo.name) && equals(argExpressions, mo.argExpressions);
}
return false;
}
public int getExpressionType() {
return TYPE_MEMBER;
}
public String getName() {
return name;
}
public String getOperator() {
return OPERATOR_MEMBER;
}
public int getPriority() {
return PRIORITY_MEMBER;
}
public int hashCode() {
int result = 31 + name.hashCode();
result = 31 * result + operand.hashCode();
return 31 * result + hashCode(argExpressions);
}
public void toString(StringBuffer bld, Variable rootVariable) {
if (operand != rootVariable) {
appendOperand(bld, rootVariable, operand, getPriority());
bld.append('.');
}
bld.append(name);
if (argExpressions.length > 0) {
bld.append('(');
elementsToString(bld, rootVariable, argExpressions);
bld.append(')');
}
}
}