5.14. Constraining Map Values

Problem

You need to ensure that all values added to a Map satisfy a set of arbitrary conditions.

Solution

Decorate an existing Map with PredicatedMap from Commons Collections. Predicates add an inbound validation to a Map, validating keys or values any time an entry is added to a PredicatedMap. If a Predicate assigned to a key or value returns false, put() throws an IllegalArgumentException. The following example decorates a HashMap with PredicatedMap—two Predicates are created to validate the keys and the values:

import java.util.*;
import org.apache.commons.collections.map.PredicatedMap;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.functors.EqualPredicate;
import org.apache.commons.collections.functors.InstanceofPredicate;
import org.apache.commons.collections.functors.OrPredicate;

// Create a Predicate that only accepts Strings
               Predicate onlyStrings = new InstanceofPredicate( String.class );

               // Create a Predicate that only allows "green" or "red"
Predicate onlyGreen = new EqualPredicate( "green" );
Predicate onlyRed = new EqualPredicate( "red" );
Predicate greenOrRed = new OrPredicate( onlyGreen, onlyRed );

               // Created a Decorated Map - accepting String keys and "green" or "red" values
               Map map = PredicatedMap.decorate( new HashMap( ), onlyStrings, greenOrRed );

// All of these puts should work
map.put( "tony" , "green" );
map.put( "alice" , "red" );
map.put( "mike" , "red" );
map.put( "bobby" , "green" );

// All of these puts should throw an IllegalArgumentException
map.put( new Double(4.0) , "green" );
map.put( "alice" , "purple" );
map.put( new Long(32) , new Long(43) );

In the previous example, keys are validated by a simple InstanceofPredicate, which ensures that each key is a String. The values are validated with an OrPredicate, which combines two EqualPredicates; values are accepted if they are equal to the strings “green” or “red.”

Discussion

A PredicatedMap can work with any Predicate no matter how simple or complex. Passing in a null value for either the key or value Predicate instructs the PredicatedMap not to validate keys or values. In the following example, a PredicatedMap decorates a HashMap to ensure that a Map contains valid Team values:

import org.apache.commons.collections.functors.AndPredicate;
import org.apache.commons.collections.map.PredicatedMap;

// Create the Predicates
ValidTeamPredicate validTeam = new ValidTeamPredicate( );
ValidCoachPredicate validCoach = new ValidCoachPredicate( );

// Tie two Predicates together into an AndPredicate
AndPredicate valuePredicate = new AndPredicate( validTeam, validCoach );
        
// Decorate a HashMap with a predicate on the value
Map teamMap = PredicatedMap.decorate( new HashMap( ), null, valuePredicate);
        
// Manufacture some teams
Team redSox = new Team( "Red Sox", new Coach( "Patrick", "Moloney") );
Team yankees= new Team( "Yankees", new Coach( "David", "McGarry") );
Team dodgers = new Team( "Dodgers", new Coach( "Nick", "Taylor") );
Team twins = new Team( null, new Coach( "Patrick", "Moloney") );
Team braves = new Team( "Braves", null );
        
// The following put calls should work fine
teamMap.put( "RedSox", redSox );
teamMap.put( "Yankees", yankees );
teamMap.put( "Dodgers", dodgers );
        
// This put should fail because the team name is null
try {
    teamMap.put( "Twins", twins);
} catch( IllegalArgumentException iae ) {
    System.out.println( "Twins put failed, as expected" );
}

// This put should fail because the coach is null
try {
    teamMap.put( "Braves", braves);
} catch( IllegalArgumentException iae ) {
    System.out.println( "Braves put failed, as expected" );
}

An application can assume that every team in teamMap has met the following requirements:

  • Every Team object must have a non-null name property.

  • Every Team object must have a non-null coach property, and this Coach object must have a first and last name.

The previous example uses two Predicate objects to validate both the team and the coach. These two custom predicates (ValidTeamPredicate and ValidCoachPredicate) are defined in Examples Example 5-15 and Example 5-16. These Predicate objects are passed to the PredicatedMap.decorate( ) method to decorate a Map with these conditions. The first Predicate, ValidTeamPredicate, validates a Team object to see if it has a non-null name property.

Example 5-15. A Predicate to validate a Team’s name property

import org.apache.commons.collections.Predicate;

public class ValidTeamPredicate implements Predicate {

    public boolean evaluate(Object object) {
        Team team = (Team) object;
        return team.getName( ) != null;        
    }

}

Example 5-16. Predicate to validate a Team’s coach property

import org.apache.commons.collections.Predicate;

public class ValidCoachPredicate implements Predicate {

    public boolean evaluate(Object object) {
        boolean validCoach = false;
        Team team = (Team) object;
        if( team.getCoach( ) != null && 
            team.getCoach( ).getFirstName( ) != null &&
            team.getCoach( ).getLastName( ) != null ) {
            validCoach = true;
        }
        return validCoach;
    }

}

The second Predicate, ValidCoachPredicate, validates a Team object, checking to see if the coach property is non-null, and that the coach has a first and last name.

In Example 5-16, a Predicate is created by combining the ValidTeamPredicate and ValidCoachPredicate in an AndPredicate. A HashMap is then decorated with PredicatedMap.decorate( ), passing the AndPredicate in as the predicate to validate Map values. A few Team objects are created, all of which are valid with the exception of the Twins and the Braves, and, as expected, the Twins and Braves cause put( ) to throw an IllegalArgumentException.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.83.176