Search code examples
javacomparisonmvelisnullorempty

MVEL not working as desired for empty comparison


As per the documentation for MVEL in [http://mvel.codehaus.org/Value+Emptiness

empty

should evaluate to true if the below mentioned conditions are true.

a string has a length greater than 0 but is comprised only of whitespace

a boolean value is false

a numeric value is 0

But It does not give desired result.

  1. For String case condition evaluates to false when String length>0 but comprised only of whitespace(Code used is given below).

    String stringValue="     ";
    Map<String,Object> contextMap=new HashMap<String, Object>();
    contextMap.put("stringValue", stringValue);
    System.out.println(MVEL.eval("stringValue == empty",contextMap));
    
  2. For Boolean it evalautes to false irrespective of boolean value(Code used is given below);

    Boolean booleanValue=false;
    Map<String,Object> contextMap=new HashMap<String, Object>();
    contextMap.put("booleanValue", booleanValue);
    System.out.println(MVEL.eval("booleanValue == empty",contextMap));
    
  3. And it shows error on comparing integers. Code:

        Integer integerValue=0;
        Map<String,Object> contextMap=new HashMap<String, Object>();
        contextMap.put("integerValue", integerValue);
        System.out.println(MVEL.eval("integerValue == empty",contextMap));
    

Error:

Exception in thread "main" [Error: failed to subEval expression]
[Near : {... integerValue == empty ....}]
                             ^
[Line: 1, Column: 17]
    at org.mvel2.compiler.AbstractParser.reduce(AbstractParser.java:2653)
    at org.mvel2.compiler.AbstractParser.arithmeticFunctionReduction(AbstractParser.java:2552)
    at org.mvel2.MVELInterpretedRuntime.parseAndExecuteInterpreted(MVELInterpretedRuntime.java:152)
    at org.mvel2.MVELInterpretedRuntime.parse(MVELInterpretedRuntime.java:49)
    at org.mvel2.MVEL.eval(MVEL.java:165)
    at com.Test1.main(Test1.java:15)
Caused by: java.lang.RuntimeException: cannot convert <> to a numeric type: class org.mvel2.compiler.BlankLiteral [200]
    at org.mvel2.math.MathProcessor.getNumber(MathProcessor.java:702)
    at org.mvel2.math.MathProcessor._doOperations(MathProcessor.java:214)
    at org.mvel2.math.MathProcessor.doOperations(MathProcessor.java:79)
    at org.mvel2.math.MathProcessor.doOperations(MathProcessor.java:48)
    at org.mvel2.util.ExecutionStack.op(ExecutionStack.java:178)
    at org.mvel2.compiler.AbstractParser.reduce(AbstractParser.java:2593)
    ... 5 more

Why it is not working as per documentation?


Solution

  • All three are related to MVEL issues.

    For the Q1 and Q2,

    For empty operator, MVEL has a class BlankLiteral, and it has a method

    public boolean equals(Object obj) {
        if (obj == null || "".equals(valueOf(obj))) {
          return true;
        }
        else if (isNumeric(obj)) {
          return "0".equals(valueOf(obj));
        }
        else if (obj instanceof Collection) {
          return ((Collection) obj).size() == 0;
        }
        else if (obj.getClass().isArray()) {
          return getLength(obj) == 0;
        }
        return false;
      }
    

    which does not handle the cases as questioned by you in Q1 and Q2.

    Q3, expression integerValue == empty,

    MVEl tries to cast empty to Number, control comes to this class MathProcessor.class

    Method is

    private static Double getNumber(Object in, int type) {
        if (in == null)
          return 0d;
        switch (type) {
          case BIG_DECIMAL:
            return ((Number) in).doubleValue();
          case DataTypes.BIG_INTEGER:
            return ((Number) in).doubleValue();
          case DataTypes.INTEGER:
          case DataTypes.W_INTEGER:
            return ((Number) in).doubleValue();
          case DataTypes.LONG:
          case DataTypes.W_LONG:
            return ((Number) in).doubleValue();
          case DataTypes.STRING:
            return Double.parseDouble((String) in);
          case DataTypes.FLOAT:
          case DataTypes.W_FLOAT:
            return ((Number) in).doubleValue();
          case DataTypes.DOUBLE:
          case DataTypes.W_DOUBLE:
            return (Double) in;
          case DataTypes.SHORT:
          case DataTypes.W_SHORT:
            return ((Number) in).doubleValue();
          case DataTypes.CHAR:
          case DataTypes.W_CHAR:
            return Double.parseDouble(String.valueOf((Character) in));
          case DataTypes.BOOLEAN:
          case DataTypes.W_BOOLEAN:
            return ((Boolean) in) ? 1d : 0d;
          case DataTypes.W_BYTE:
          case DataTypes.BYTE:
            return ((Byte) in).doubleValue();
        }
    
        throw new RuntimeException("cannot convert <" + in + "> to a numeric type: " + in.getClass() + " [" + type + "]");
    
    }
    

    Observing the values in debug mode,

    Object in - BlankLiteral
    int type - 200
    

    200 is nothing but DataTypes.EMPTY which is currently not handled by MVEL. So since no case is matched it throws exception.

    So still 'empty' is not fully implemented in MVEL