package org.odata4j.producer.inmemory;
import java.math.BigDecimal;
import java.util.Set;
import org.core4j.Enumerable;
import org.odata4j.edm.EdmSimpleType;
import org.odata4j.edm.EdmType;
import org.odata4j.expression.AddExpression;
import org.odata4j.expression.AndExpression;
import org.odata4j.expression.BinaryCommonExpression;
import org.odata4j.expression.BoolCommonExpression;
import org.odata4j.expression.BoolMethodExpression;
import org.odata4j.expression.BoolParenExpression;
import org.odata4j.expression.BooleanLiteral;
import org.odata4j.expression.CastExpression;
import org.odata4j.expression.CommonExpression;
import org.odata4j.expression.ConcatMethodCallExpression;
import org.odata4j.expression.DivExpression;
import org.odata4j.expression.EndsWithMethodCallExpression;
import org.odata4j.expression.EntitySimpleProperty;
import org.odata4j.expression.EqExpression;
import org.odata4j.expression.Expression;
import org.odata4j.expression.GeExpression;
import org.odata4j.expression.GtExpression;
import org.odata4j.expression.IndexOfMethodCallExpression;
import org.odata4j.expression.LeExpression;
import org.odata4j.expression.LengthMethodCallExpression;
import org.odata4j.expression.LiteralExpression;
import org.odata4j.expression.LtExpression;
import org.odata4j.expression.ModExpression;
import org.odata4j.expression.MulExpression;
import org.odata4j.expression.NeExpression;
import org.odata4j.expression.NotExpression;
import org.odata4j.expression.OrExpression;
import org.odata4j.expression.ParenExpression;
import org.odata4j.expression.ReplaceMethodCallExpression;
import org.odata4j.expression.StartsWithMethodCallExpression;
import org.odata4j.expression.SubExpression;
import org.odata4j.expression.SubstringMethodCallExpression;
import org.odata4j.expression.SubstringOfMethodCallExpression;
import org.odata4j.expression.ToLowerMethodCallExpression;
import org.odata4j.expression.ToUpperMethodCallExpression;
import org.odata4j.expression.TrimMethodCallExpression;
import org.odata4j.internal.TypeConverter;
public class InMemoryEvaluation {
public static Object evaluate(CommonExpression expression, Object target, PropertyModel properties) {
if (expression instanceof LiteralExpression)
return Expression.literalValue((LiteralExpression) expression);
if (expression instanceof EntitySimpleProperty)
return properties.getPropertyValue(target,
((EntitySimpleProperty) expression).getPropertyName());
if (expression instanceof BoolCommonExpression)
return evaluate((BoolCommonExpression) expression, target,
properties);
if (expression instanceof AddExpression)
return binaryFunction((BinaryCommonExpression) expression, target,
properties, BinaryFunction.ADD);
if (expression instanceof SubExpression)
return binaryFunction((BinaryCommonExpression) expression, target,
properties, BinaryFunction.SUB);
if (expression instanceof MulExpression)
return binaryFunction((BinaryCommonExpression) expression, target,
properties, BinaryFunction.MUL);
if (expression instanceof DivExpression)
return binaryFunction((BinaryCommonExpression) expression, target,
properties, BinaryFunction.DIV);
if (expression instanceof ModExpression)
return binaryFunction((BinaryCommonExpression) expression, target,
properties, BinaryFunction.MOD);
if (expression instanceof ParenExpression)
return evaluate(((ParenExpression) expression).getExpression(),
target, properties);
if (expression instanceof BoolParenExpression)
return evaluate(((BoolParenExpression) expression).getExpression(),
target, properties);
if (expression instanceof CastExpression) {
CastExpression castExpression = (CastExpression) expression;
EdmSimpleType<?> t = EdmType.getSimple(castExpression.getType());
if (t == null)
throw new UnsupportedOperationException("Only simple types supported");
Class<?> javaType = t.getJavaTypes().iterator().next();
return TypeConverter.convert(evaluate(castExpression.getExpression(), target, properties), javaType);
}
if (expression instanceof ToLowerMethodCallExpression) {
ToLowerMethodCallExpression e = (ToLowerMethodCallExpression) expression;
String value = evaluateToString(e.getTarget(), target, properties);
return value == null ? null : value.toLowerCase();
}
if (expression instanceof ToUpperMethodCallExpression) {
ToUpperMethodCallExpression e = (ToUpperMethodCallExpression) expression;
String value = evaluateToString(e.getTarget(), target, properties);
return value == null ? null : value.toUpperCase();
}
if (expression instanceof SubstringMethodCallExpression) {
SubstringMethodCallExpression e = (SubstringMethodCallExpression) expression;
String value = evaluateToString(e.getTarget(), target, properties);
if (value == null || e.getStart() == null)
return value;
int start = (Integer) evaluate(e.getStart(), target, properties);
if (e.getLength() == null) {
return value.substring(start);
}
int length = (Integer) evaluate(e.getLength(), target, properties);
return length == 0 ? "" : value.substring(start, start + length);
}
if (expression instanceof IndexOfMethodCallExpression) {
IndexOfMethodCallExpression e = (IndexOfMethodCallExpression) expression;
String text = evaluateToString(e.getTarget(), target, properties);
String search = evaluateToString(e.getValue(), target, properties);
return text.indexOf(search);
}
if (expression instanceof ReplaceMethodCallExpression) {
ReplaceMethodCallExpression e = (ReplaceMethodCallExpression) expression;
String text = evaluateToString(e.getTarget(), target, properties);
String find = evaluateToString(e.getFind(), target, properties);
String replace = evaluateToString(e.getReplace(), target, properties);
return text.replace(find, replace);
}
if (expression instanceof ConcatMethodCallExpression) {
ConcatMethodCallExpression e = (ConcatMethodCallExpression) expression;
String left = evaluateToString(e.getLHS(), target, properties);
String right = evaluateToString(e.getRHS(), target, properties);
return left + right;
}
if (expression instanceof TrimMethodCallExpression) {
TrimMethodCallExpression e = (TrimMethodCallExpression) expression;
String left = evaluateToString(e.getTarget(), target, properties);
return left == null ? null : left.trim();
}
if (expression instanceof LengthMethodCallExpression) {
LengthMethodCallExpression e = (LengthMethodCallExpression) expression;
String left = evaluateToString(e.getTarget(), target, properties);
return left == null ? 0 : left.length();
}
throw new UnsupportedOperationException("unsupported expression " + expression);
}
private static String evaluateToString(CommonExpression expression, Object target, PropertyModel properties) {
Object value = evaluate(expression, target, properties);
if (value == null)
return null;
if (value instanceof String)
return ((String) value);
return String.valueOf(value);
}
public static boolean evaluate(BoolCommonExpression expression, Object target, PropertyModel properties) {
if (expression instanceof EqExpression) {
return equals((EqExpression) expression, target, properties);
}
if (expression instanceof NeExpression) {
return !equals((NeExpression) expression, target, properties);
}
if (expression instanceof AndExpression) {
AndExpression e = (AndExpression) expression;
return evaluate(e.getLHS(), target, properties)
&& evaluate(e.getRHS(), target, properties);
}
if (expression instanceof OrExpression) {
OrExpression e = (OrExpression) expression;
return evaluate(e.getLHS(), target, properties)
|| evaluate(e.getRHS(), target, properties);
}
if (expression instanceof BooleanLiteral) {
return ((BooleanLiteral) expression).getValue();
}
if (expression instanceof GtExpression) {
return compareTo((GtExpression) expression, target, properties) > 0;
}
if (expression instanceof LtExpression) {
return compareTo((LtExpression) expression, target, properties) < 0;
}
if (expression instanceof GeExpression) {
return compareTo((GeExpression) expression, target, properties) >= 0;
}
if (expression instanceof LeExpression) {
return compareTo((LeExpression) expression, target, properties) <= 0;
}
if (expression instanceof NotExpression) {
NotExpression e = (NotExpression) expression;
Boolean rt = (Boolean) evaluate(e.getExpression(), target,
properties);
return !rt;
}
if (expression instanceof BoolMethodExpression) {
return evaluate((BoolMethodExpression) expression, target, properties);
}
if (expression instanceof ParenExpression) {
@SuppressWarnings("unused")
Object obj = null;
}
if (expression instanceof BoolParenExpression) {
BoolParenExpression e = (BoolParenExpression) expression;
return evaluate((BoolCommonExpression) e.getExpression(), target,
properties);
}
//Let's try to evaluate generic expressions in hope it evaluates to bool
Object o = evaluate((CommonExpression) expression, target, properties);
if (o instanceof Boolean) {
return (Boolean) o;
}
throw new UnsupportedOperationException("unsupported expression "
+ expression);
}
private static boolean evaluate(BoolMethodExpression expression, Object target, PropertyModel properties) {
String targetValue = (String) evaluate(expression.getTarget(), target, properties);
String searchValue = (String) evaluate(expression.getValue(), target, properties);
if (targetValue == null || searchValue == null) {
return false;
}
if (expression instanceof SubstringOfMethodCallExpression) {
return targetValue.contains(searchValue);
}
if (expression instanceof StartsWithMethodCallExpression) {
return targetValue.startsWith(searchValue);
}
if (expression instanceof EndsWithMethodCallExpression) {
return targetValue.endsWith(searchValue);
}
throw new UnsupportedOperationException("unsupported expression "
+ expression);
}
private static interface BinaryFunction {
BigDecimal apply(BigDecimal lhs, BigDecimal rhs);
Double apply(Double lhs, Double rhs);
Float apply(Float lhs, Float rhs);
Integer apply(Integer lhs, Integer rhs);
Long apply(Long lhs, Long rhs);
public static final BinaryFunction ADD = new BinaryFunction() {
public BigDecimal apply(BigDecimal lhs, BigDecimal rhs) {
return lhs.add(rhs);
}
public Double apply(Double lhs, Double rhs) {
return lhs + rhs;
}
public Float apply(Float lhs, Float rhs) {
return lhs + rhs;
}
public Integer apply(Integer lhs, Integer rhs) {
return lhs + rhs;
}
public Long apply(Long lhs, Long rhs) {
return lhs + rhs;
}
};
public static final BinaryFunction SUB = new BinaryFunction() {
public BigDecimal apply(BigDecimal lhs, BigDecimal rhs) {
return lhs.subtract(rhs);
}
public Double apply(Double lhs, Double rhs) {
return lhs - rhs;
}
public Float apply(Float lhs, Float rhs) {
return lhs - rhs;
}
public Integer apply(Integer lhs, Integer rhs) {
return lhs - rhs;
}
public Long apply(Long lhs, Long rhs) {
return lhs - rhs;
}
};
public static final BinaryFunction MUL = new BinaryFunction() {
public BigDecimal apply(BigDecimal lhs, BigDecimal rhs) {
return lhs.multiply(rhs);
}
public Double apply(Double lhs, Double rhs) {
return lhs * rhs;
}
public Float apply(Float lhs, Float rhs) {
return lhs * rhs;
}
public Integer apply(Integer lhs, Integer rhs) {
return lhs * rhs;
}
public Long apply(Long lhs, Long rhs) {
return lhs * rhs;
}
};
public static final BinaryFunction DIV = new BinaryFunction() {
public BigDecimal apply(BigDecimal lhs, BigDecimal rhs) {
return lhs.divide(rhs);
}
public Double apply(Double lhs, Double rhs) {
return lhs / rhs;
}
public Float apply(Float lhs, Float rhs) {
return lhs / rhs;
}
public Integer apply(Integer lhs, Integer rhs) {
return lhs / rhs;
}
public Long apply(Long lhs, Long rhs) {
return lhs / rhs;
}
};
public static final BinaryFunction MOD = new BinaryFunction() {
public BigDecimal apply(BigDecimal lhs, BigDecimal rhs) {
return lhs.remainder(rhs);
}
public Double apply(Double lhs, Double rhs) {
return lhs % rhs;
}
public Float apply(Float lhs, Float rhs) {
return lhs % rhs;
}
public Integer apply(Integer lhs, Integer rhs) {
return lhs % rhs;
}
public Long apply(Long lhs, Long rhs) {
return lhs % rhs;
}
};
}
private static class ObjectPair {
public Object lhs;
public Object rhs;
public ObjectPair(CommonExpression lhs, CommonExpression rhs,
Object target, PropertyModel properties) {
this(evaluate(lhs, target, properties), evaluate(rhs, target,
properties));
}
public ObjectPair(Object lhs, Object rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
}
private static Object binaryFunction(BinaryCommonExpression be,
Object target, PropertyModel properties, BinaryFunction function) {
ObjectPair pair = new ObjectPair(be.getLHS(), be.getRHS(), target,
properties);
binaryNumericPromotion(pair);
// * Edm.Decimal
// * Edm.Double
// * Edm.Single
// * Edm.Int32
// * Edm.Int64
if (pair.lhs instanceof BigDecimal)
return function.apply((BigDecimal) pair.lhs, (BigDecimal) pair.rhs);
if (pair.lhs instanceof Double)
return function.apply((Double) pair.lhs, (Double) pair.rhs);
if (pair.lhs instanceof Float)
return function.apply((Float) pair.lhs, (Float) pair.rhs);
if (pair.lhs instanceof Integer)
return function.apply((Integer) pair.lhs, (Integer) pair.rhs);
if (pair.lhs instanceof Long)
return function.apply((Long) pair.lhs, (Long) pair.rhs);
throw new UnsupportedOperationException("unsupported add type "
+ pair.lhs);
}
private static boolean equals(BinaryCommonExpression be, Object target,
PropertyModel properties) {
ObjectPair pair = new ObjectPair(be.getLHS(), be.getRHS(), target,
properties);
binaryNumericPromotion(pair);
return (pair.lhs == null ? pair.rhs == null : pair.lhs.equals(pair.rhs));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static int compareTo(BinaryCommonExpression be, Object target,
PropertyModel properties) {
ObjectPair pair = new ObjectPair(be.getLHS(), be.getRHS(), target,
properties);
binaryNumericPromotion(pair);
return ((Comparable) pair.lhs).compareTo(((Comparable) pair.rhs));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static final Set<Class> SUPPORTED_CLASSES_FOR_BINARY_PROMOTION = Enumerable
.create(BigDecimal.class, Double.class, Float.class, Byte.class,
Integer.class, Short.class, Long.class).cast(Class.class)
.toSet();
@SuppressWarnings("unchecked")
private static void binaryNumericPromotion(ObjectPair pair) {
// * Edm.Decimal
// * Edm.Double
// * Edm.Single
// * Edm.Byte
// * Edm.Int16
// * Edm.Int32
// * Edm.Int64
if (pair.lhs == null || pair.rhs == null)
return;
Class<?> lhsClass = pair.lhs.getClass();
Class<?> rhsClass = pair.rhs.getClass();
if (lhsClass.equals(rhsClass))
return;
if (!SUPPORTED_CLASSES_FOR_BINARY_PROMOTION.contains(lhsClass)
|| !SUPPORTED_CLASSES_FOR_BINARY_PROMOTION.contains(rhsClass))
return;
// If supported, binary numeric promotion SHOULD consist of the
// application of the following rules in the order specified:
// * If either operand is of type Edm.Decimal, the other operand is
// converted to Edm.Decimal unless it is of type Edm.Single or
// Edm.Double.
if (lhsClass.equals(BigDecimal.class)
&& Enumerable
.create(Byte.class, Short.class, Integer.class,
Long.class).cast(Class.class)
.contains(rhsClass))
pair.rhs = BigDecimal.valueOf(((Number) pair.rhs).longValue());
else if (rhsClass.equals(BigDecimal.class)
&& Enumerable
.create(Byte.class, Short.class, Integer.class,
Long.class).cast(Class.class)
.contains(lhsClass))
pair.lhs = BigDecimal.valueOf(((Number) pair.lhs).longValue());
// * Otherwise, if either operand is Edm.Double, the other operand is
// converted to type Edm.Double.
else if (lhsClass.equals(Double.class))
pair.rhs = ((Number) pair.rhs).doubleValue();
else if (rhsClass.equals(Double.class))
pair.lhs = ((Number) pair.lhs).doubleValue();
// * Otherwise, if either operand is Edm.Single, the other operand is
// converted to type Edm.Single.
else if (lhsClass.equals(Float.class))
pair.rhs = ((Number) pair.rhs).floatValue();
else if (rhsClass.equals(Float.class))
pair.lhs = ((Number) pair.lhs).floatValue();
// * Otherwise, if either operand is Edm.Int64, the other operand is
// converted to type Edm.Int64.
else if (lhsClass.equals(Long.class))
pair.rhs = ((Number) pair.rhs).longValue();
else if (rhsClass.equals(Long.class))
pair.lhs = ((Number) pair.lhs).longValue();
// * Otherwise, if either operand is Edm.Int32, the other operand is
// converted to type Edm.Int32
else if (lhsClass.equals(Integer.class))
pair.rhs = ((Number) pair.rhs).intValue();
else if (rhsClass.equals(Integer.class))
pair.lhs = ((Number) pair.lhs).intValue();
// * Otherwise, if either operand is Edm.Int16, the other operand is
// converted to type Edm.Int16.
else if (lhsClass.equals(Short.class))
pair.rhs = ((Number) pair.rhs).shortValue();
else if (rhsClass.equals(Short.class))
pair.lhs = ((Number) pair.lhs).shortValue();
// * If binary numeric promotion is supported, a data service SHOULD use
// a castExpression to promote an operand to the target type.
}
public static Object cast(Object obj, Class<?> targetType) {
if (obj == null)
return null;
Class<?> objClass = obj.getClass();
if (targetType.isAssignableFrom(objClass))
return obj;
if ((obj instanceof Number) && targetType.equals(Double.class))
return ((Double) obj).doubleValue();
if ((obj instanceof Number) && targetType.equals(Float.class))
return ((Number) obj).floatValue();
if ((obj instanceof Number) && targetType.equals(Long.class))
return ((Number) obj).longValue();
if ((obj instanceof Number) && targetType.equals(Integer.class))
return ((Number) obj).intValue();
if ((obj instanceof Number) && targetType.equals(Short.class))
return ((Number) obj).shortValue();
if ((obj instanceof Number) && targetType.equals(Byte.class))
return ((Number) obj).byteValue();
if (objClass.equals(Integer.class) && targetType.equals(Integer.TYPE))
return obj;
throw new UnsupportedOperationException("Unable to cast a "
+ objClass.getSimpleName() + " to a "
+ targetType.getSimpleName());
}
}