You need to perform complex
conditional logic using multiple
Predicate
objects, and you need to combine and
expose multiple criteria as one Predicate
.
To combine several Predicate
instances, create a
Predicate
to capture each portion of a compound
condition, and combine each condition with
AndPredicate
, OrPredicate
,
AllPredicate
, OnePredicate
,
AnyPredicate
, or NonePredicate
.
All of these predicate implementations are used to combine the
results of multiple predicates—creating a compound predicate.
The following code demonstrates the use of the
AndPredicate
, OrPredicate
,
AllPredicate
, and OnePredicate
:
import org.apache.commons.collections.Predicate; import org.apache.commons.collections.functors.*; // Create Base Predicates Predicate isTim = new EqualsPredicate("Tim"); Predicate isDouble = new InstanceOfPredicate( Double.class ); Predicate isNotNull = NotNullPredicate.INSTANCE; Predicate[] predicates = new Predicate[] { isTim, isDouble, isNotNull };// Create 2 argument logical predicate composites
Predicate andPredicate = new AndPredicate( isTim, isNotNull );
Predicate orPredicate = new OrPredicate( isTim, isNotNull );
// Create n-argument logical predicate composites
Predicate allPredicate = new AllPredicate( predicates );
Predicate onePredicate = new OnePredicate( predicates );
System.out.println( "'Tim' and not null?: " + andPredicate. evalute( "Tim" ) ); System.out.println( "'Tim' or not null?: " + andPredicate. evalute(new Long(3))); System.out.println( "'Tim', not null, and Double?: " + allPredicate.evaluate( "Impossible" ) ); System.out.println( "XOR ('Tim', not null, or Double?): " + allPredicate.evaluate( "Impossible" ) );
This example creates the following output:
'Tim' and not null?: true 'Tim' or not null?: true 'Tim', not null, and Double?: false XOR('Tim', not null, or Double?): true
An AndPredicate
returns true
if
both predicates supplied to its constructor return
true
, and an OrPredicate
returns true
if at least one of the two predicates
passed to its constructor returns true
. An
AllPredicate
takes an array of predicates, only
returning true
if every predicate evaluates to
true
. The OnePredicate
also
takes an array of predicates, only returning true
if exactly one predicate evaluates to true
.
In the code sample, the use of the second to last predicate,
AllPredicate
, is impossible to satisfy; an object
can never be a String
and a
Double
at the same time. This example fails to
demonstrate AnyPredicate
and
NonePredicate
—both take an array of
predicates. AnyPredicate
returns
true
if any of the predicates evaluate to
true
, and NonePredicate
returns
true
only if none of the predicates evaluate to
true
. The behavior of these objects is easily
inferred from the names: And, Or, All, One, Any, or None.
Any logical expression can be modeled by connecting
Predicate
objects together— similar to the
way that simple logic gates are connected to create complex digital
logic. Logical inputs (1 and 0) are routed to logic gates (AND, OR,
NOR, NAND, XOR, etc.), and the outputs of a logic circuit are a
result of stages that perform the same function as the
Predicate
objects introduced in this recipe. In
the next example, a logic circuit will be used to demonstrate a
complex hierarchy of Predicate
objects; a circuit
diagram is drawn, and a series of predicates are developed to model
this circuit. Figure 4-1 contains a logical
expression that is implemented with digital logic and
Predicate
objects.
Assuming that every letter corresponds to a
boolean
variable, this expression corresponds to
the circuit diagram in Figure 4-2. Each gate can be
modeled as a composite Predicate
, and from Figure 4-2 it is clear that this example will include two
AndPredicates
, an OrPredicate
,
and a NotPredicate
. The
“AND” gate is modeled with an
AndPredicate
, and an
“OR” gate with an
OrPredicate
. The
“NAND” gate is transformed into a
three-input “AND” gate followed by
an inverter that is modeled with an AllPredicate
wrapped in a NotPredicate
.
The system has five inputs, which will be stored in a
Map
with five keys: A, B, C, D, and E. A simple
InputPredicate
is developed to handle the inputs
to the system—a map of Boolean
input objects
is passed to the top-level Predicate
. An
InputPredicate
is configured to evaluate the input
Map
and return the boolean
value of one of the inputs; in other words, an
InputPredicate
selects a boolean
value from a
Map
, always returning the value of that input from
the Map
it evaluates. (See Example 4-7.)
Example 4-7. InputPredicate: a predicate that selects an input from a Map
package com.discursive.jccook.collections.predicate; import org.apache.commons.collections.Predicate; public class InputPredicate implements Predicate { private String inputKey; public BooleanPredicate(String inputKey) { this.inputKey = inputKey; } public boolean evaluate(Object object) { boolean satisfies = false; Map inputMap = (Map) object; Boolean input = (Boolean) inputMap.get( inputKey ); if( input != null ) { satisfies = input.booleanValue( ); } return satisfies; } }
The entire circuit is modeled by one top-level
Predicate
and a Map
of
Boolean
input signals is passed down a hierarchy
of predicates as needed. Unlike a real circuit, where inputs would
cause gates to fire sequentially, the predicate hierarchy is
evaluated from the final stage backward—the example evaluates
the Predicate
variable circuit
.
The input map is passed to the top-most Predicate
,
which, in turn, passes this same map to the
Predicate
that precedes it in
the circuit. Example 4-8 ties everything together, and the logic to
create our circuit-modeling predicate has been confined to the
createPredicate()
method.
Example 4-8. Implementing a multilevel composite Predicate
package com.discursive.jccook.collections.predicate; import org.apache.commons.collections.Predicate; import org.apache.commons.collections.functors.*; public class CompoundPredicateExample { public static void main(String[] args) { CompoundPredicateExample example = new CompoundPredicateExample( ); example.start( ); } public void start( ) {Predicate circuit = createPredicate( );
Object[] inputsArray =
new Object[][] { {"A", Boolean.TRUE},
{"B", Boolean.FALSE},
{"C", Boolean.TRUE},
{"D", Boolean.FALSE},
{"E", Boolean.FALSE} };
Map inputs = ArrayUtils.toMap( inputsArray );
boolean result = circuit.evaluate( inputs );
System.out.println( "The circuit fired?: " + result );
}
public Predicate createPredicate( ) { Predicate aPredicate = new InputPredicate("A"); Predicate bPredicate = new InputPredicate("B"); Predicate cPredicate = new InputPredicate("C"); Predicate dPredicate = new InputPredicate("D"); Predicate ePredicate = new InputPredicate("E"); Predicate expression1 = new AndPredicate( aPredicate, bPredicate ); Predicate expression2 = new OrPredicate( cPredicate, dPredicate ); Predicate[] secondLevel = new Predicate( ) { expression1, expression2, ePredicate }; Predicate topLevel = new NotPredicate( secondLevel ); return topLevel; } }
This code prints The
circuit
fired?
: true
. This complex
example has demonstrated the process of modeling composite,
multistage logic with a hierarchy of predicates. A
Predicate
is the most basic functor and when
combined with other Predicate
instances, there is
no limit to the level of complexity that can be achieved. Logic
circuits were used in this example because a logic gate is
a great analogy
for a Predicate
. Think of a
Predicate
as a component—a gate in a logic
circuit.
Get Jakarta Commons Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.