Coercion of the Unified EL

So yes, specifications do not always specify what the user would expect, and the Unified EL is no exception. What do I refer to?

The unified EL has a very interesting behaviour defined with regards to coercion: it tries to coerce a null-value to the expected type.

An example: you bind the collapsed property of a container to a backing-bean property, and return null, to indicate that this container should not be collapsible at all - the specified EL conversion will create "false" out of this value.

An example for what the spec says:

Coerce A to Boolean

  • If A is null or "", return false
  • Otherwise, if A is a Boolean, return A
  • Otherwise, if A is a String, and Boolean.valueOf(A) does not throw an exception, return it

This is really not what you would expect, if you haven't read the spec. Facelets, on the other hand, always creates value-expressions with an expected type - these two behaviours together make bindings deliver null values to components almost impossible.

To solve this, I have implemented the following code for component-classes in a component library I am working on, called in the setValueExpression()-method of these components. What a nasty hack.

Can we repair this for the next version of the Unified EL or Facelets? And yes, if you ask me, I believe this should be repaired in the EL (cause I still want coercion from e.g. a String to a Boolean value, I just don't want to loose the null semantics). And - I am open for better suggestions to work around this problem, if there are such.

 public static void repairExpectedType(ValueExpression binding) {

    if(binding.getExpectedType()!=null &&
       binding.getExpectedType()!=Object.class) {
      try {
        Class bindingClass = binding.getClass();

        if(bindingClass.getName().equals(
            "com.sun.facelets.el.TagValueExpression")) {

          java.lang.reflect.Field origin = getFieldForName(
            bindingClass, "orig");
          origin.setAccessible(true);

          Object sunValueExpression = origin.get(binding);

          if(sunValueExpression.getClass().getName().equals(
             "com.sun.el.ValueExpressionImpl")) {
            java.lang.reflect.Field expectedType =
             getFieldForName(sunValueExpression.getClass(), "expectedType");
            expectedType.setAccessible(true);

            expectedType.set(sunValueExpression, Object.class);
          }

        }
      }
      catch(Throwable th) {
        throw new IllegalStateException(th);
      }
    }
  } 

Popular posts