DevOps Zone is brought to you in partnership with:

I’m a passionate Java developer / architect, who enjoys coaching and sharing knowledge about all topics IT-related. Kevin has posted 2 posts at DZone. You can read more from them at their website. View Full User Profile

Collections, meet Expression!

02.23.2013
| 6462 views |
  • submit to reddit

On my current project, we use a lot of data collections. And like in most software projects, the most common (although far from exclusive) operations are finding an element from a collection and doing something with it.

As a stereotyped Java developer, I am also used to writing a solution that is clean and simple. Something that is readable and easily understood by everyone. I am also a very lazy person when it comes to writing code, so I basically don’t want to repeat myself. That is why I often use the Apache Collections API to make my life a bit more easy. For those who do not know this library, it contains a set of very useful types and functions, that help free you from writing this boiler plate code. Allow me to explain:

Imagine that you have a collection called “apples” that contains a bunch of Apple objects. If we want to find the first green apple from that bunch, we could write something like this:

CollectionUtils.find ( apples, isGreenApple );

The “greenApples” variable is defined as an implementation of the Predicate interface. This interface is very simple: it contains only one method (evaluate) which returns a boolean. Its sole purpose is to identify if the object provided matches the predicate or not. We could define the “isGreenApple” predicate from our previous example as such:

Predicate isGreenApple = new Predicate () {
   public boolean evaluate(Apple apple) {
      return apple.isGreen();
   } 
}

Apache Collections contains much more features than just finding objects in a collection, but we will constrict ourself to that concept for now.

The benefits from working with collection like this should become clear over time:

  • We separate the concern of the operation to perform on a collection from where.
  • It will allow us to re-use our predicates often, and independent of what we want to do with them.

But there are also some side effects:

  • This syntax is very verbose.
  • In reality, on a big software project, the re-use of these kind of predicates is almost non-existent. It difficult to find and reuse predicates.

I think we can do better than this…

Expression predicates

Basically, we want to describe how to identify and object and then what to do with it. And, of course, this should be easy to read and work with. We already figured out the “what to do with it” part, but not how to identify it.

At the moment of writing, Lambda expressions are not wildly available to us. At least, in the Java industry, it is not feasible yet to migrate to Java 8 in order for us to gain access at using Lambda expressions. So that is not yet an option.

But, there is a specification in the Java world we could actually. It is easy to read, easy to use and very powerful. We have been using it a lot in frameworks such as JSF, WebFlow, etc. It’s called the Unified Expression Language.

The condition from our previous example could easily be re-written as an EL expression:

“apple.isGreenApple()” or “apple.greenApple”

So why not use that?

We can build our own Predicate implementation and use an EL library to resolve the expression for us. Our ExpressionPredicate might look something like this:

public class ExpressionPredicate<T> implements Predicate<T> {

    private ContextMap contextMap;
    private String objectName;
    private ELContext context;

    private ValueExpression expression;
    
    public ExpressionPredicate(String objectName, String expression) {
        this(objectName, expression, Maps.<String, Object>newHashMap());
    }
    
    public ExpressionPredicate(String objectName, String expression, Object expectedResult, Map<? extends String, ? extends Object> variableMap) {
        this.contextMap = new ContextMap(variableMap);
        this.objectName = objectName;
        
        CompositeELResolver compositeELResolver = createELResolver(contextMap);
        this.context = createContext(compositeELResolver);
        
        ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
        this.expression = expressionFactory.createValueExpression(
                this.context, expression, expectedResult.getClass());
    }

    @Override
    public boolean evaluate(T object) {
        contextMap.assignBaseVariable(objectName, object);
        return Boolean.valueOf( evaluateExpression(context) );
    }

    Object evaluateExpression(ELContext context) {
        return this.expression.getValue(context);
    }
    
    private CompositeELResolver createELResolver(ContextMap contextMap) {
        CompositeELResolver compositeELResolver = new CompositeELResolver();
        compositeELResolver.add(contextMap);
        compositeELResolver.add(new ArrayELResolver());
        compositeELResolver.add(new ListELResolver());
        compositeELResolver.add(new BeanELResolver());
        compositeELResolver.add(new MapELResolver());
        return compositeELResolver;
    }

    ELContext createContext(final ELResolver elResolver) {
        return new ELContext() {
            @Override
            public VariableMapper getVariableMapper() {
                return new VariableMapperImpl();
            }
            @Override
            public FunctionMapper getFunctionMapper() {
                return new FunctionMapperImpl();
            }
            @Override
            public ELResolver getELResolver() {
                return elResolver;
            }
        };
    }

    static class ContextMap extends MapELResolver {
        Map<String, Object> context;
    
        public ContextMap(Map<? extends String, ? extends Object> context) {
            this.context = Maps.<String, Object>newHashMap(context);
        }

        @Override
        public Object getValue(ELContext elContext, Object base, Object property) {
            if (base == null) {
                base = context;
            }
            return super.getValue(elContext, base, property);
        }
        
        public void assignBaseVariable(String objectName, Object objectValue) {
            context.put(objectName, objectValue);
        }
    }

}

It may not be pretty and even look a bit overwhelming at first… but these are the basic things that happen here:

  • We set-up an Expression Language resolver, so that we can evaluate an expression
  • We bind a “context map” to the resolver, which is going to contain all our variable when we evaluate it
  • Before each evaluation runs, we  put the current object into the “context map”. The objectName will be used to identify the current object during our iteration.
  • In this example, we assume the expression evaluates to either true or false. We parse that result and return it after the evaluation. Note that if the evaluation does not return true, it will always revert to false. So if you want to support more return types for your expression, you should edit the example above and add support to compare an “expected result” as opposed to assuming that the returned result is a boolean.

In the example above, I’ve used the JBoss EL implementation, but there are many more providers out there for the javax.el API. I also make use of Apache Collections (of course) and Google Guava.

With this predicate, we are already half way of where we want to be. By now, we can already do this:

CollectionUtils.find (apples, new ExpressionPredicate("apple", "${apple.greenApple}") );

Also notice that it is possible to provide a basic Map to the ExpressionPredicate. It will allow us to pass-on variables and other objects into our expression. Imagine the following example:

Map<string, apple=""> arguments = Maps.newHashMap();
arguments.put("color", "green");
CollectionUtils.find (apples, new ExpressionPredicate("apple", "${apple.colorName eq color}", arguments) );

In the code above, we bind the “green” string to the “color” variable. The expression is a standard EL expression, performing an equals on the “colorName” property to the “color” variable we provided.

But we can do a little bit better…

Syntactic sugar

What is missing here is some syntactic sugar. We don’t always want to name the object we are going to iterate over it; we can just call it plain “var”. I would also like to have an easy method I can call in order to construct a variable map. And finally, it may be a good idea to foresee a builder pattern. Our expressions can be very powerful already, but we may want to de-couple our expression into multiple parts. Or, we may even want to construct our ExpressionPredicate more dynamically later on.

public class ExpressionUtils {
    
    public static final String OBJECT_NAME = "var";
    
    public static <T> ExpressionBuilder<T> newExpression() {
        return new ExpressionBuilder<T>();
    }
    
    public static class ExpressionBuilder<T> {
        
        private List<ExpressionPredicate<T>> expressions;
        
        public ExpressionBuilder() {
            expressions = Lists.newArrayList();
        }
        
        public ExpressionBuilder<T> withProperty(String propertyName) {
            expressions.add(new ExpressionPredicate<T>(OBJECT_NAME, "${" + OBJECT_NAME + "." + propertyName + "}"));
            return this;
        }
        
        public ExpressionBuilder<T> with(String expression) {
            return with(expression, Maps.<String, Object>newHashMap());
        }
        
        public ExpressionBuilder<T> with(String expression, Map<? extends String, ? extends Object> arguments) {
            expressions.add(new ExpressionPredicate<T>(OBJECT_NAME, "${" + expression + "}", arguments));
            return this;
        }

        public Predicate<T> build() {
            return AllPredicate.getInstance( expressions );
        }

    }

    public static Map<? extends String, ? extends Object> newMap(String arg, Object value) {
        return ImmutableMap.<String, Object>of(arg, value);
    }

    public static Map<? extends String, ? extends Object> newMap(String arg0, Object value0, String arg1, Object value1) {
        return ImmutableMap.<String, Object>of(arg0, value0, arg1, value1);
    }
    
    public static Map<? extends String, ? extends Object> newMap(String arg0, Object value0, String arg1, Object value1, String arg2, Object value2) {
        return ImmutableMap.<String, Object>of(arg0, value0, arg1, value1, arg2, value2);
    }

    public static Builder<String, Object> newMap() {
        return ImmutableMap.<String, Object>builder();
    }

    public static <T> Predicate<T> newExpression(String expression, Map<? extends String, ? extends Object> newMap) {
        return new ExpressionBuilder<T>().with(expression, newMap).build();
    }

    public static <T> Predicate<T> newExpression(String expression) {
        return new ExpressionBuilder<T>().with(expression).build();
    }
}

With this utility / builder class, we have a very powerful tool where we can use expressions to select elements from collections.

We can construct complex evaluations:

CollectionUtils.find ( apples, ExpressionUtils.newExpression ( "var.greenApple and not var.oldApple" ) );

We can add variables to our expressions:

CollectionUtils.find ( apples, ExpressionUtils.newExpression ( 
    "var.greenApple and var.brandName eq brand", ExpressionUtils.newMap( "brand", "PinkLady" ) ) );

And we can even construct them dynamically if we need to:

ExpressionBuilder expression= ExpressionUtils.newExpression()
     .with ("var.greenApple);
if (excludeOldApples) {
    expression.with("var.oldApple");
}
CollectionUtils.find ( apples, expression.build() );

There is no silver bullet

There is, however, an important side note I want to mention: for every method we write that uses an expression like this, we should write a good unit test (if we don’t already do this anyway).

The reason is that our compiler will not be able to code check this expression for us. That is a big disadvantage. If we write a mistake in the expression, our compiler will ignore it until we evaluate the expression at runtime. It is even more important if we want to refactor or re-write our code: we won’t immediately know if we broke an expression when we, for example, change the name of a method or property of our object.

Still, we should write unit tests for these kind of operations by default. The gain in readability and usability will eventually outweigh the loss in pre-compilation checking.

Conclusion

The Apache Collections library is a great library for working with collections. However, the need to write verbose predicates that are sometimes difficult to read reduces the ease of use for that library. By using the Unified Expression Language, and building a Predicate implementation around that, we can greatly increase the readability, the flexibility and the ease of use for the Collections library. We can imagine evaluating complex expressions, expressions with variable binding and even construct these expressions dynamically at run time. Because of this, we greatly decrease the amount of code we develop and increase our own productivity.

Still, this is not a perfect solution: by using an expression language, we lose the ability of our compiler to validate our code at compile time, because these expressions are ignored.

With Java 8 currently being finalized, we will gain access to Lambda expressions. Eventually, when Lambda becomes an accepted principle in the Java industry, we may consider again “refactoring” our ExpressionPredicate to use those language constructs, which may again allow our compiler to validate at compile time.

Published at DZone with permission of its author, Kevin Chabot.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)