Collections, meet Expression!

Introduction

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();
   } 
}

If you did not know about Apache Collections, I would recommend that you look into it. It can do much more than 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.
  • When you get used to the syntax, your code easier to read.

But there are also some side effects:

  • This syntax is very verbose. It balances our for complex predicates, but for simple cases like in our previous example, it seems an awful lot of extra work. 
  • 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

The idea of “selecting” elements like this looks very similar to a concept from a functional language. 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 yet 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:

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.

Quick links and references

Apache Collections: http://commons.apache.org/collections/

Unified Expression Language: http://docs.oracle.com/javaee/1.4/tutorial/doc/JSPIntro7.html

Lambdas & Collections: http://cr.openjdk.java.net/~briangoetz/lambda/collections-overview.html

About these ads

3 thoughts on “Collections, meet Expression!

  1. It pains me a bit to say this, but .NET’s implementation is probably the best I’ve seen, whether it be lambda functions or LINQ. For example:

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

    Becomes (way less boilerplate and easier to read):
    var apple = apples.Find(c => greenApple & c != oldApple);

    or if you want to get just GreenApples from a bushel:
    var greenApples = bushel.FindAll(c => c is GreenApple);

    etc etc.

    I haven’t read up too much on Java 8, but hopefully we get something similar.

    • I agreed; Java 8 will allow us to do simular things compared to LINQ, and will (of course) support type safety checking. But it is not realistic for Enterprise applications to already migrate to Java 8, and I do estimate that this will take at least another year before Java 8 can be safely used… Until then, this is a “workarround” and it memains easy to refactor afterwards.

  2. Very creative solution. Nicer to write than anonymous inner functions, although I find typesafety quite important. Personally since I’ve moved on from Java to Scala I rather do not want to go back since it’s exactly these kind of issues which are solved beautifully while being fully interoperable with Java. Of all features of Scala, it’s collection API is it’s killer feature.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s