Showing posts with label TransformerUtils. Show all posts
Showing posts with label TransformerUtils. Show all posts

Saturday, February 7, 2009

Examples of Functors, Transformers, Predicates, and Closures in Java

One day, I found myself re-designing a procurement portal, and I kept re-writing the same for-loop over and over again (no pun intended). I had an epiphany; I could do better and I started using the Apache Commons Collection Utilities (Transformers, Predicates, and Closures). Now don’t think just because I started to use the Apache Commons Collection Utilities, the project was better. However, the result was a highly extensible framework…. Later it was dismantled by another team… but that is a different story (Grin).

Functors

I laugh every time I think of the word Functors, but that is because I’m immature, case in point, I still laugh at fart jokes. Anyway, Functors, or Function Objects, in the Apache or Jakarta Commons Collection Utilities are a set of interfaces designed specifically to be used against collections of objects. This framework embodies a balance between code reuse and behavioral specialization through composition as opposed to strict Object Oriented design. Composition is well suited for Creational patterns such as Factories, Structural patterns like Decorators, or Behavioral patterns like Strategies. The Apache Commons Collections framework defines three types of interfaces:
  • Closures are functions that can alter the object and get a reference to each object in the collection.

  • Transformers are responsible for transforming data from one format to another or from one object to another.

  • Predicates simply execute a conditional test against each item in a collection and return true or false for each item.
NOTE:
My examples sometimes use Anonymous Inner Classes and Inner Classes. Some developers have strong feelings about defining classes in this manner. There are times when doing this is appropriate and times when it is inappropriate. As with any programming solutions, this technique may or may not suit your needs or environment. So, let’s get over it and move on.

Closure

Here is a typical problem statement which maybe resolved with the use of Closures. I want to execute a specific method or change the state on every object in a collection. For example, I might want to execute the toString method. Please note that these examples do not pull out a value and transform into another collection of objects. They simply iterate over the collection and do something to it or with it. For the purpose of this first example, I will send the results of the toString to system.out. In the second example, I will alter the state of each bean and change the name of every object. Both examples use the utility method CollectionUtils.forAllDo.

Lets take some baby steps, and look at this example.

The Code:
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
import java.util.*;
import org.apache.commons.collections.*;
import org.apache.commons.lang.*;
import com.blogspot.apachecommonstipsandtricks.*;
public class SimpleClosure
{
public static void main(String[] args)
{
System.out.println("\nTest Number One Results :");
List<String> collectionOfWords = Arrays.asList("Java", "Example",
"Help", "Tips", "And",
"Tricks", "Apache",
"Commons", "Collections");
// Lets call toString on every object and print it out.
CollectionUtils.forAllDo(collectionOfWords, new Closure()
{
public void execute(Object o)
{
assert o != null;
System.out.print(o.toString() + " ");
}
});
System.out.println("\n\nTest Number Two Results :");
int i = 1;
List<DTO> collectionOfDTOs = Arrays.asList(new DTO(i++, "Java Tips and Tricks", Gender.Male, State.WI),
new DTO(i++, "Apache Commons" , Gender.Male, State.WI),
new DTO(i++, "Jakarta Commons" , Gender.Male, State.WI),
new DTO(i++, "Collections" , Gender.Male, State.WI),
new DTO(i++, "Closures" , Gender.Male, State.WI) );
CollectionUtils.forAllDo(collectionOfDTOs, new Closure()
{
public void execute(Object o)
{
DTO dto = (DTO) o;
assert dto != null;
String s = StringUtils.defaultIfEmpty(dto.getName(), "null");
dto.setName("Yoda says, " + s + " Rocks!");
}
});
CollectionUtils.forAllDo(collectionOfDTOs,PrintIt.getInstance());
}
}
The Results:
Test Number One Results :
Java Example Help Tips And Tricks Apache Commons Collections

Test Number Two Results :
com.blogspot.apachecommonstipsandtricks.DTO{id=1, name='Yoda says, Java Tips and Tricks Rocks!', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=2, name='Yoda says, Apache Commons Rocks!', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Yoda says, Jakarta Commons Rocks!', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=4, name='Yoda says, Collections Rocks!', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=5, name='Yoda says, Closures Rocks!', gender=Male, state=WI}

You might have noticed I created a class called PrintIt and it has a static method called getInstance. This class is an implementation of the singleton design pattern. In a large system where Closures (Predicates and Transformers) are being created by the hundreds, it makes sense to keep the memory foot print to a minimum. This is accomplished by using a singleton pattern. Word of caution, there is only one instance of this class per Java Virtual Machine (jvm), so using static properties could cause big problems, if you don’t understand how they work.

The Code:
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
import org.apache.commons.collections.*;
public class PrintIt implements Closure
{
// This class implements a Singleton Pattern
private static PrintIt ourInstance = new PrintIt();
/**
* Get a singleton instance of PrintIt
*/
public static PrintIt getInstance()
{
return ourInstance;
}
private PrintIt() // This is a singleton, dont change this!
{
}
public void execute(Object o)
{
System.out.println( o.toString() );
}
}
Once again, in the first example, all we did was iterate through a collection and call toString on every element in the collection. In the second example, we are actually modifying the state of the bean by rewriting the name of the DTO.. I love Yoda! Lets talk about transformers now.

Transformer

I’m a little older than the Transformer cartoons, but I cant help but think of the movie that was released not long ago. So, here is a problem statement that would best be resolved by transformers. You just have a whole bunch of string values from a Http Request object and you need to convert them to Integers.

The Code:
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
import java.util.*;
import org.apache.commons.collections.*;
public class SimpleTransformer
{
public static void main(String[] args)
{
Collection<String> stringOfNumbers = Arrays.asList("1", "2", "3", "4");
Collection<Integer> intNums = CollectionUtils.collect(stringOfNumbers, new Transformer() {
public Object transform(Object o) {
return Integer.valueOf((String) o);
}
});
CollectionUtils.forAllDo(intNums, PrintIt.getInstance() );
}
}
The Results:
1
2
3
4
Again, I used the PrintIt class to print the results, but you get the idea with this example. I converted a collection of Strings to Integers. Of course there are many ways to skin a cat, this is just an example. You might have noticed by now, that the interface is public Object transform(Object o)… It is not a Java 1.5 Generics implementation. I’m not entirely sure why the Apache team hasn’t released a Java 1.5 Generics version of these utilities, but if you are in a pinch and you have to have it, someone posted a link to an adaptation of the library that has the Generics. http://larvalabs.com/collections

Here is a more practical problem statement. You have a collection of plain old java beans and in each bean a method that returns a String, part of the String represents the id into another system, environment or sub identity. For example, the String might be prefixed with three letters, for example "PAS". You might find some legacy ERP systems that do this to the PO number. Maybe the billing department puts the initials of the sales person or team at the front of the number. In this example we will transform an Array of Strings that have ids in them into an array of ids in the form of numbers only.

The Code:
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
import java.util.*;
import org.apache.commons.collections.*;
import org.apache.commons.lang.*;
public class SimpleTransformer
{
public static void main(String[] args)
{
Collection<String> stringOfNumbers = Arrays.asList("ABC0001", "BCD0002", "CDF0003", "BFA0004");
Collection<Integer> intNums = CollectionUtils.collect(stringOfNumbers, new Transformer()
{
public Object transform(Object o)
{
String s = ((String) o);
return Integer.valueOf(s.substring(3, s.length()));
}
});
CollectionUtils.forAllDo(intNums, PrintIt.getInstance());
}
}
The Results:
1
2
3
4
Now let’s look at something a little more practical. As a problem statement, let’s say we have a billing object from the old system called OldBill, and we need to identify it in the new system, called NewBill. The ids in the old system started with “A” and a number. In the new system they will start with “Z” and the number from the old system plus 500. In the next example we will break apart the concerns of the transformers into two different transformers and glue them together with the utility class TransformerUtils.chainedTransformer. Creating a transformer in this manner allows us to plug in a different behavior or add and subtract behaviors, even on the fly.

The Code:
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
import java.util.*;
import org.apache.commons.collections.*;
public class ChainedTransformer
{
public static void main(String[] args)
{
List<OldBill> aList = Arrays.asList(new OldBill("A1"), new OldBill("A2"),
new OldBill("A3"), new OldBill("A4"));
Transformer[] chainedTransformer = new Transformer[]{
new Transformer() {
public Object transform(Object o) {
return ((OldBill )o).getId().replace('A', 'Z');
}
},
new Transformer() {
public Object transform(Object o) {
char[] c = ((String) o).toCharArray();
int x = Integer.parseInt(String.valueOf(c[1])) + 500;
return new NewBill( String.valueOf(c[0]) + x );
}
}
};
System.out.println("The aList");
CollectionUtils.forAllDo(aList, PrintIt.getInstance());
List<NewBill> bList = (List<NewBill>) CollectionUtils.collect(aList, TransformerUtils.chainedTransformer(chainedTransformer));
System.out.println("\nThe bList");
CollectionUtils.forAllDo(bList, PrintIt.getInstance());
}
}
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
public class OldBill
{
private String id;
public OldBill(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
@Override public String toString()
{
return "OldBill{id='" + id + "\'}";
}
}
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
public class NewBill
{
private String id;
public NewBill(String id)
{
this.id = id;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
@Override public String toString()
{
return "NewBill{id='" + id + "\'}";
}
}
The Results:
The aList
OldBill{id='A1'}
OldBill{id='A2'}
OldBill{id='A3'}
OldBill{id='A4'}

The bList
NewBill{id='Z501'}
NewBill{id='Z502'}
NewBill{id='Z503'}
NewBill{id='Z504'}
Predicate

Predicates do one thing and one thing only, they return either true or false. As a problem statement, let’s say we have a collection of Strings and we want keep out values that can not be converted to numbers.

The Code:
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
import java.util.*;
import org.apache.commons.collections.*;
public class SimplePredicate
{
public static void main(String[] args)
{
List<String> mixedup = Arrays.asList("A", "0", "B", "C", "1", "D", "F", "3");
Collection numbersOnlyList = CollectionUtils.predicatedCollection(new ArrayList(),
new Predicate() {
public boolean evaluate(Object o) {
try {
Integer.valueOf((String) o);
return true;
} catch (NumberFormatException e) {
return false;
}
}
});
for (String s : mixedup) {
try {
numbersOnlyList.add(s);
} catch (IllegalArgumentException e) {
System.out.println("I love CollectionUtils!");
}
}
System.out.println("\nResults of the predicatedCollection List:");
CollectionUtils.forAllDo(numbersOnlyList, PrintIt.getInstance() );
}
}
The Results:
I love CollectionUtils!
I love CollectionUtils!
I love CollectionUtils!
I love CollectionUtils!
I love CollectionUtils!

Results of the predicatedCollection List:
0
1
3
Ok here is something more useful. Have you ever had a need to do something like SQL but in Java? For example, you wanted to select beans from a collection based on some conditional blocks? Here are some great examples of SQL commands that can be used in Java and they behave just like SQL complex where clauses, distinct, like, and group by. In this example, we will be using the folloing utlitly classes and maps
The Code:
package com.blogspot.apachecommonstipsandtricks.transformersexamples;
import java.util.*;
import org.apache.commons.collections.*;
import org.apache.commons.collections.map.*;
import com.blogspot.apachecommonstipsandtricks.*;
public class PredicatesSQLSample
{
public static void main(String[] args)
{
List<DTO> list = Arrays.asList(new DTO(1,"Bob", Gender.Male, State.WI), new DTO(2,"Larry",Gender.Male, State.WI),
new DTO(3,"Bill", Gender.Male, State.WI), new DTO(4,"Sue", Gender.Female, State.AZ),
new DTO(3,"Bill", Gender.Male, State.WI), new DTO(4,"Sue", Gender.Female, State.AZ),
new DTO(5,"Joe", Gender.Male, State.AZ), new DTO(6,"Zoe", Gender.Female, State.MI));
Predicate sqlOrQueryPredicate = PredicateUtils.anyPredicate(new Predicate[]{
new Predicate()
{
public boolean evaluate(Object o)
{
return State.WI.equals(((DTO) o).getState());
}
}, new Predicate()
{
public boolean evaluate(Object o)
{
return Gender.Female.equals(((DTO) o).getGender());
}
}
});
Predicate sqlAndQueryPredicate = PredicateUtils.allPredicate(new Predicate[]{
new Predicate()
{
public boolean evaluate(Object o)
{
return State.AZ.equals(((DTO) o).getState());
}
}, new Predicate()
{
public boolean evaluate(Object o)
{
return Gender.Male.equals(((DTO) o).getGender());
}
}
});
Predicate likeNameStartsWithB = new Predicate(){
public boolean evaluate(Object o)
{
return ((DTO) o).getName().startsWith("B");
}
};

Collection aList = CollectionUtils.select(list, sqlOrQueryPredicate);
Collection bList = CollectionUtils.select(list, PredicateUtils.notPredicate( sqlOrQueryPredicate ));
Collection cList = CollectionUtils.select(list, sqlAndQueryPredicate);
Collection dList = CollectionUtils.select(list, PredicateUtils.allPredicate(new Predicate[]{PredicateUtils.uniquePredicate(), sqlOrQueryPredicate} ));
Collection eList = CollectionUtils.select(list, PredicateUtils.allPredicate(new Predicate[]{PredicateUtils.uniquePredicate() ,likeNameStartsWithB} ));
Collection fList = CollectionUtils.select(list, PredicateUtils.uniquePredicate() );

Map aGroupByStateMap = TransformedMap.decorate(new MultiValueMap(),new Transformer(){
public Object transform(Object o)
{
return ((DTO) o).getState();
}
}, TransformerUtils.nopTransformer() );
for (Object o : fList)
{
aGroupByStateMap.put( o, o );
}

System.out.println("\nAll the people :\nselect * from list");
CollectionUtils.forAllDo(list,PrintIt.getInstance());
System.out.println("\nAll the people in Wisconsin OR Female :\nselect * from list where ( state = WI or gender = female );");
CollectionUtils.forAllDo(aList, PrintIt.getInstance());
System.out.println("\nAll the people NOT ( Wisconsin OR Female ) :\nselect * from list where ! ( state = WI or gender = female );");
CollectionUtils.forAllDo(bList, PrintIt.getInstance());
System.out.println("\nAll the people in Arizona AND Male :\nselect * from list where ( state = AZ and gender = male );");
CollectionUtils.forAllDo(cList, PrintIt.getInstance());
System.out.println("\nAll the distinct people in Arizona AND Male :\nselect distinct * from list where ( state = WI or gender = female );");
CollectionUtils.forAllDo(dList, PrintIt.getInstance());
System.out.println("\nAll the distinc people with the name that starts with B :\nselect distinct * from list where name like \"B%\";");
CollectionUtils.forAllDo(eList, PrintIt.getInstance());
System.out.println("\nAll the distinct people grouped by state :\nselect distinct * from list group by state;");
Set states = aGroupByStateMap.keySet();
for (Object state : states)
{
System.out.println(((State)state).getFullyQualifiedName());
CollectionUtils.forAllDo((Collection) aGroupByStateMap.get(state), PrintIt.getInstance());
}
}
}
The Results:
All the people :
select * from list
com.blogspot.apachecommonstipsandtricks.DTO{id=1, name='Bob', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=2, name='Larry', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Bill', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=4, name='Sue', gender=Female, state=AZ}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Bill', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=4, name='Sue', gender=Female, state=AZ}
com.blogspot.apachecommonstipsandtricks.DTO{id=5, name='Joe', gender=Male, state=AZ}
com.blogspot.apachecommonstipsandtricks.DTO{id=6, name='Zoe', gender=Female, state=MI}

All the people in Wisconsin OR Female :
select * from list where ( state = WI or gender = female );
com.blogspot.apachecommonstipsandtricks.DTO{id=1, name='Bob', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=2, name='Larry', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Bill', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=4, name='Sue', gender=Female, state=AZ}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Bill', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=4, name='Sue', gender=Female, state=AZ}
com.blogspot.apachecommonstipsandtricks.DTO{id=6, name='Zoe', gender=Female, state=MI}

All the people NOT ( Wisconsin OR Female ) :
select * from list where ! ( state = WI or gender = female );
com.blogspot.apachecommonstipsandtricks.DTO{id=5, name='Joe', gender=Male, state=AZ}

All the people in Arizona AND Male :
select * from list where ( state = AZ and gender = male );
com.blogspot.apachecommonstipsandtricks.DTO{id=5, name='Joe', gender=Male, state=AZ}

All the distinct people in Arizona AND Male :
select distinct * from list where ( state = WI or gender = female );
com.blogspot.apachecommonstipsandtricks.DTO{id=1, name='Bob', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=2, name='Larry', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Bill', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=4, name='Sue', gender=Female, state=AZ}
com.blogspot.apachecommonstipsandtricks.DTO{id=6, name='Zoe', gender=Female, state=MI}

All the distinc people with the name that starts with B :
select distinct * from list where name like "B%";
com.blogspot.apachecommonstipsandtricks.DTO{id=1, name='Bob', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Bill', gender=Male, state=WI}

All the distinct people grouped by state :
select distinct * from list group by state;
ARIZONA
com.blogspot.apachecommonstipsandtricks.DTO{id=4, name='Sue', gender=Female, state=AZ}
com.blogspot.apachecommonstipsandtricks.DTO{id=5, name='Joe', gender=Male, state=AZ}
MICHIGAN
com.blogspot.apachecommonstipsandtricks.DTO{id=6, name='Zoe', gender=Female, state=MI}
WISCONSIN
com.blogspot.apachecommonstipsandtricks.DTO{id=1, name='Bob', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=2, name='Larry', gender=Male, state=WI}
com.blogspot.apachecommonstipsandtricks.DTO{id=3, name='Bill', gender=Male, state=WI}

Well, that wraps it up. Next week I plan on showing you some more examples of this fantastic api called Apache Commons.

Author: Philip A Senger

Sunday, January 25, 2009

TransformedMap and Transformers: Examples of Strategy, Decorator, and Factory

I mentioned some software design patterns in my last two examples one was a Factory, Decorator, and the other a Strategy. Before we get into the next example, I want to make sure you understand these patterns because, clearly the developers at Apache where thinking of these patterns when they developed the Commons Collections API. Let’s talk about a Factory first.

Factories:
Factories are a subset of what is called creational design patterns (See the wikipedia Abstract factory pattern). There are a couple of variations on the factory pattern but basically, you ask the factory for a named object, it assembles it and returns it. Just like a factory, hence the name. This is the building block of a lot of my web applications.

Decorator:
A decorator pattern is simply a way of receiving one value and decorating it into something suitable for a different view. For example, if you have a bean and it has a value of 12.599999999 you might want to decorator it to say something like $12.54. The apache team decided that the TransformedMap.decorate method was actually decorating the key not really a strategy.

Strategy:
A strategy pattern (See the wikipedia Strategy pattern) is a subset of behavioral design patterns. This pattern is analogous to a socket wrench (the socket part not the wrench part). So, as you might know, the socket can be replaced with different sizes at anytime. It simply plugs into the wrench and can be turned right or left. The concept here is we create an interface (which is a contract or in the wrench analogy the part that connects the socket to the wrench) whereby algorithms can be selected and dynamically installed at runtime.

Personally, I feel the TransformedMap.decorate is a borderline case between a strategy and a decorator…. On one hand it decorates the values going into the key, but on the other hand it changes the behavior at run time...Also, Im using a MultiValueMap for the backing map which totally changes the behavior. So, I will refer to it as a Strategy. I know some people will take issue with this, oh well. Lets declare some simple objects, like the Gender and State enum.


package com.blogspot.apachecommonstipsandtricks;
public enum Gender
{
Male, Female
}



package com.blogspot.apachecommonstipsandtricks;
/**
* Official USPS Abbreviations
*/
public enum State
{
AL("ALABAMA"), AK("ALASKA"), AS("AMERICAN SAMOA"),
AZ("ARIZONA "), AR("ARKANSAS"), CA("CALIFORNIA "),
CO("COLORADO "), CT("CONNECTICUT"), DE("DELAWARE"),
DC("DISTRICT OF COLUMBIA"), FM("FEDERATED STATES OF MICRONESIA"), FL("FLORIDA"),
GA("GEORGIA"), GU("GUAM "), HI("HAWAII"),
ID("IDAHO"), IL("ILLINOIS"), IN("INDIANA"),
IA("IOWA"), KS("KANSAS"), KY("KENTUCKY"), LA("LOUISIANA"),
ME("MAINE"), MH("MARSHALL ISLANDS"), MD("MARYLAND"),
MA("MASSACHUSETTS"), MI("MICHIGAN"), MN("MINNESOTA"),
MS("MISSISSIPPI"), MO("MISSOURI"), MT("MONTANA"),
NE("NEBRASKA"), NV("NEVADA"), NH("NEW HAMPSHIRE"),
NJ("NEW JERSEY"), NM("NEW MEXICO"), NY("NEW YORK"),
NC("NORTH CAROLINA"), ND("NORTH DAKOTA"), MP("NORTHERN MARIANA ISLANDS"),
OH("OHIO"), OK("OKLAHOMA"), OR("OREGON"),
PW("PALAU"), PA("PENNSYLVANIA"), PR("PUERTO RICO"),
RI("RHODE ISLAND"), SC("SOUTH CAROLINA"), SD("SOUTH DAKOTA"),
TN("TENNESSEE"), TX("TEXAS"), UT("UTAH"),
VT("VERMONT"), VI("VIRGIN ISLANDS"), VA("VIRGINIA "),
WA("WASHINGTON"), WV("WEST VIRGINIA"), WI("WISCONSIN"), WY("WYOMING");
private String fullyQualifiedName;
State(String fullyQualifiedName)
{
this.fullyQualifiedName = fullyQualifiedName;
}
public String getFullyQualifiedName()
{
return fullyQualifiedName;
}
}


Ok, now define our Data Transfer Object (DTO), just like the last example.


package com.blogspot.apachecommonstipsandtricks;
public class DTO
{
private int id;
private String name;
private Gender gender;
private State state;

public DTO(int id, String name, Gender gender, State state)
{
this.id = id;
this.name = name;
this.gender = gender;
this.state = state;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Gender getGender()
{
return gender;
}
public void setGender(Gender gender)
{
this.gender = gender;
}
public State getState()
{
return state;
}
public void setState(State state)
{
this.state = state;
}
@Override
public String toString()
{
return "com.blogspot.apachecommonstipsandtricks.DTO{id=" + id + ", name='" + name + '\'' + ", gender=" + gender + ", state=" + state + '}';
}
}


And now, ( drum roll please ) the factory.


package com.blogspot.apachecommonstipsandtricks;
import java.util.*;
import org.apache.commons.collections.map.*;
import org.apache.commons.collections.*;
public class MapFactory
{
public static Map getMap(MapFactoryEnum whichFactory)
{
Map returnMap = null;
if (null != whichFactory)
{
Transformer transformer;
switch (whichFactory)
{
case NAME:
transformer = TransformerUtils.invokerTransformer("getName");
break;
case STATE:
transformer = TransformerUtils.invokerTransformer("getState");
break;
case GENDER:
transformer = TransformerUtils.invokerTransformer("getGender");
break;
default:
throw new IllegalArgumentException("Unknown Map");
}
returnMap = TransformedMap.decorate( new MultiValueMap(), transformer, TransformerUtils.nopTransformer());
}
return returnMap ;
}
public enum MapFactoryEnum
{
NAME, STATE, GENDER
}
}

So, when you call getMap, you request a map by a name. The factory builds a new MultiValueMap and decorates it, as in a Strategy with the transformer.

The code:

package com.blogspot.apachecommonstipsandtricks;
import java.util.*;
import org.apache.commons.collections.*;
import org.apache.commons.lang.*;
public class TestMapFactory
{
public static void main(String[] args)
{
int i = 0;
List<dto> list = Arrays.asList(new DTO(i++,"Bob", Gender.Male, State.WI), new DTO(i++,"Larry",Gender.Male, State.WI),
new DTO(i++,"Bill", Gender.Male, State.WI), new DTO(i++,"Sue", Gender.Female, State.AZ),
new DTO(i++,"Joe", Gender.Male, State.AZ), new DTO(i++,"Zoe", Gender.Female, State.WI));

List<dto> dtosFromTheMap;
Collection names;
Map map;

System.out.println("------------------------- By State -------------------------");
map = MapFactory.getMap(MapFactory.MapFactoryEnum.STATE);

for (DTO dto : list)
{
map.put( dto, dto );
}

dtosFromTheMap = (List<dto>) map.get(State.WI);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are in " + State.WI.getFullyQualifiedName() );

dtosFromTheMap = (List<dto>) map.get(State.AZ);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are in " + State.AZ.getFullyQualifiedName() );

System.out.println("------------------------- By Gender -------------------------");
map = MapFactory.getMap(MapFactory.MapFactoryEnum.GENDER);

for (DTO dto : list)
{
map.put( dto, dto );
}

dtosFromTheMap = (List<dto>) map.get(Gender.Male);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are " + Gender.Male );

dtosFromTheMap = (List<dto>) map.get(Gender.Female);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are " + Gender.Female );
}
}


This code could be very useful if you where caching items or grouping selections together inside a controller. Just a side note, maps are always non-thread safe. I will explain in my next example what that means.

The results:

------------------------- By State -------------------------
Bob,Larry,Bill,Zoe are in WISCONSIN
Sue,Joe are in ARIZONA
------------------------- By Gender -------------------------
Bob,Larry,Bill,Joe are Male
Sue,Zoe are Female

Author: Philip A Senger

Tuesday, January 20, 2009

TransformedMap and Transformers: Decorated "put" strategy

This is a long example regarding decorated maps and transformers. It’s important to understand the concepts I will cover, because I will use them in the next example, and continue to build on them.

The object TransformedMap has a static method called decorate (http://commons.apache.org/collections/api-release/org/apache/commons/collections/map/TransformedMap.html), this method allows you to load a backing map and two transformers (http://commons.apache.org/collections/api-release/org/apache/commons/collections/Transformer.html), one for the key and the other for the value. The returning object is a decorated map. So, when you put an item into this new decorated map, the key and values are transformed. Unfortunately, the transformer interface doesn’t allow you to gain access to the backing object through the interface. You could be cleaver and jam it into a constructor ( more on this later ). Be forewarned, modifying a map while in the midst of another modification method will result in a big ugly run time exception ( just in case you thought you could us this method to build your own MultiValueMap ).

Enough chatter… lets look at some code

The code:

package com;
import java.util.*;
import org.apache.commons.collections.map.*;
import org.apache.commons.collections.*;
public class MapDecorator
{
public static void main(String[] args)
{
int i = 0;
List<DTO> list = Arrays.asList(new DTO(i++,"Bob",Gender.Male,State.WI), new DTO(i++,"Larry",Gender.Male,State.WI),
new DTO(i++,"Bill", Gender.Male, State.WI), new DTO(i++,"Sue", Gender.Female, State.AZ),
new DTO(i++,"Joe", Gender.Male, State.AZ), new DTO(i++,"Zoe", Gender.Female, State.WI));
// Decorate a map where the key is the id, and the value is the object.
Map exampleOne = TransformedMap.decorate( new HashMap(),
TransformerUtils.invokerTransformer("getId"),
TransformerUtils.nopTransformer());
// Decorate a map where the key is the name and the value is the id
Map exampleTwo = TransformedMap.decorate( new HashMap(),
new Transformer() {
public Object transform(Object o)
{
return ((DTO)o).getName();
}
},
new Transformer() {
public Object transform(Object o)
{
return ((DTO)o).getId();
}
} );
// load up the maps.
for (DTO dto : list)
{
exampleOne.put(dto, dto);
exampleTwo.put(dto, dto);
}
printTheMap("exampleOne",exampleOne);
printTheMap("exampleTwo",exampleTwo);
}

private static void printTheMap(String mapName, Map exampleOne)
{
System.out.println("Map Name = " + mapName );
System.out.println("------------ Keys ------------");
for (Object key : exampleOne.keySet())
{
System.out.println("key = " + key);
}
System.out.println("------------ Values ------------");
for (Object value : exampleOne.values())
{
System.out.println("value = " + value);
}
System.out.println("------------------------------");
}
public static class DTO
{
private int id;
private String name;
private Gender gender;
private State state;
public DTO(int id, String name, Gender gender, State state)
{
this.id = id;
this.name = name;
this.gender = gender;
this.state = state;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Gender getGender()
{
return gender;
}
public void setGender(Gender gender)
{
this.gender = gender;
}
public State getState()
{
return state;
}
public void setState(State state)
{
this.state = state;
}
@Override public String toString()
{
return "DTO{id=" + id + ", name='" + name + '\'' + ", gender=" + gender + ", state=" + state + '}';
}
}
public static enum Gender
{
Male, Female
}
/**
* Official USPS Abbreviations
*/
public static enum State
{
AL("ALABAMA"), AK("ALASKA"), AS("AMERICAN SAMOA"),
AZ("ARIZONA "), AR("ARKANSAS"), CA("CALIFORNIA "),
CO("COLORADO "), CT("CONNECTICUT"), DE("DELAWARE"),
DC("DISTRICT OF COLUMBIA"), FM("FEDERATED STATES OF MICRONESIA"), FL("FLORIDA"),
GA("GEORGIA"), GU("GUAM "), HI("HAWAII"),
ID("IDAHO"), IL("ILLINOIS"), IN("INDIANA"),
IA("IOWA"), KS("KANSAS"), KY("KENTUCKY"), LA("LOUISIANA"),
ME("MAINE"), MH("MARSHALL ISLANDS"), MD("MARYLAND"),
MA("MASSACHUSETTS"), MI("MICHIGAN"), MN("MINNESOTA"),
MS("MISSISSIPPI"), MO("MISSOURI"), MT("MONTANA"),
NE("NEBRASKA"), NV("NEVADA"), NH("NEW HAMPSHIRE"),
NJ("NEW JERSEY"), NM("NEW MEXICO"), NY("NEW YORK"),
NC("NORTH CAROLINA"), ND("NORTH DAKOTA"), MP("NORTHERN MARIANA ISLANDS"),
OH("OHIO"), OK("OKLAHOMA"), OR("OREGON"),
PW("PALAU"), PA("PENNSYLVANIA"), PR("PUERTO RICO"),
RI("RHODE ISLAND"), SC("SOUTH CAROLINA"), SD("SOUTH DAKOTA"),
TN("TENNESSEE"), TX("TEXAS"), UT("UTAH"),
VT("VERMONT"), VI("VIRGIN ISLANDS"), VA("VIRGINIA "),
WA("WASHINGTON"), WV("WEST VIRGINIA"), WI("WISCONSIN"), WY("WYOMING");
private String fullyQualifiedName;
State(String fullyQualifiedName)
{
this.fullyQualifiedName = fullyQualifiedName;
}
public String getFullyQualifiedName()
{
return fullyQualifiedName;
}
}
}



The results:

Map Name = exampleOne
------------ Keys ------------
key = 2
key = 4
key = 1
key = 3
key = 5
key = 0
------------ Values ------------
value = DTO{id=2, name='Bill', gender=Male, state=WI}
value = DTO{id=4, name='Joe', gender=Male, state=AZ}
value = DTO{id=1, name='Larry', gender=Male, state=WI}
value = DTO{id=3, name='Sue', gender=Female, state=AZ}
value = DTO{id=5, name='Zoe', gender=Female, state=WI}
value = DTO{id=0, name='Bob', gender=Male, state=WI}
------------------------------
Map Name = exampleTwo
------------ Keys ------------
key = Bob
key = Larry
key = Zoe
key = Joe
key = Sue
key = Bill
------------ Values ------------
value = 0
value = 1
value = 5
value = 4
value = 3
value = 2
------------------------------


The decorated Map, exampleOne, is backed by a HashMap and when put is called on the map, it calls the TransformerUtils.invokerTransformer("getId") (http://commons.apache.org/collections/api-release/org/apache/commons/collections/TransformerUtils.html#invokerTransformer(java.lang.String)) on the Object for the key. This results in the invoker using reflections to pull the id off the bean. In the same stroke, the value is transformed by TransformerUtils.nopTransformer() (http://commons.apache.org/collections/api-release/org/apache/commons/collections/TransformerUtils.html#nopTransformer()). This literally does nothing to the object used as the value; it is a kind of pass through.

// Decorate a map where the key is the id, and the value is the object.
Map exampleOne = TransformedMap.decorate( new HashMap(),
TransformerUtils.invokerTransformer("getId"),
TransformerUtils.nopTransformer());


For example

DTO dto = new DTO(100,"Bob",Gender.Male,State.WI);
exampleOne.put(dto, dto);

Results in an entry that has a key of 100 and a value of DTO(100,"Bob",Gender.Male,State.WI).

You might want to get a couple of values out and build a multi-value key or something else. The Map, exampleTwo, shows two anonymous inner implementations of the transformer interface.

Map exampleTwo = TransformedMap.decorate( new HashMap(),
new Transformer() {
public Object transform(Object o)
{
return ((DTO)o).getName();
}
},
new Transformer() {
public Object transform(Object o)
{
return ((DTO)o).getId();
}
} );


For example if you did this.

DTO dto = new DTO(100,"Bob",Gender.Male,State.WI);
exampleTwo.put(dto, dto);

The results would be a key of Bob pointing to a value of 100… are you beginning to see how awesome this api is? Are your gears grinding yet? Wait to you see what we do next time.
Author: Philip A Senger

Sunday, January 18, 2009

Transformer Invoker: For a collection of beans, collect a property

How many times have you had to collect a property from a list of beans, statically or dynamically? I use this little method a lot. For-loops are nice, but because this uses reflection to get the value, you can cook up a factory to get the data.

The code:

package com;
import java.util.*;
import org.apache.commons.collections.*;
public class TransformerExample
{
public static void main(String[] args)
{
List list = Arrays.asList(new DTO("Bob"), new DTO("Larry"), new DTO("Mo"), new DTO("Joe"));
Collection<String> names = CollectionUtils.collect(list, TransformerUtils.invokerTransformer("getName"));
for (String name : names)
{
System.out.println("name = " + name);
}
}
public static class DTO
{
private String name;
public DTO(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
}



The results:

name = Bob
name = Larry
name = Mo
name = Joe
Author: Philip A Senger