Consider a scenario of an external API provided by vendor. The API had tree like data structure, described by diagram below.
Now the problem was that the main data structure part of external API was POJO. Though it was a tree structure it did not provide any operations for traversal or find operations. Here is the watered down version of the component.
public class NodeComponent {Let's see how google collection can be used to provide nice searcher methods. Consider following use cases:
private String name;
private List<NodeComponent> children;
private Map<String, String> attributes;
public NodeComponent(String name)
{
this.name = name;
}
// Getters and Setters omitted for brevity
}
- Find child nodes based on node name
- Find child nodes based on attribute name and value
- Find child nodes on composite criteria of node name and attribute name and value
Case 1: Find child nodes based on node name
First we will define NodeNameCriteria implementing Predicate interface
Further, define NodeSearcher class which will represent current node and will provide all these helper methods
public class NodeNameCriteria implements Predicate<NodeComponent>
{
private String nameCriteria;
public NodeNameCriteria(String nameCriteria)
{
this.nameCriteria = nameCriteria;
}
public boolean apply(NodeComponent node)
{
return node.getName().equals(nameCriteria);
}
}
Case 2: Find child nodes based on attribute name and value
public class NodeSearcher
{
private NodeComponent currentNode;
public NodeSearcher(NodeComponent currentNode)
{
this.currentNode = currentNode;
}
public Collection<NodeComponent> findChildrenByNodeName(String name)
{
return Collections2.filter(currentNode.getChildren(),new NodeNameCriteria(name));
}
}
Similar to first approach, this requires creating a Predicate implementation, let's name it AttributeNameValueCriteria
Case 3: Find child nodes on composite criteria of node name and attribute name and value
public class AttributeNameValueCriteria implements Predicate<NodeComponent>
{
private String nameCriteria;
private String valueCriteria;
public AttributeNameValueCriteria(String nameCriteria, String valueCriteria)
{
this.nameCriteria = nameCriteria;
this.valueCriteria = valueCriteria;
}
public boolean apply(NodeComponent component)
{
Set<Map.Entry<String, String>> entrySet = component.getAttributes().entrySet();
Set<Map.Entry<String, String>> matchedAttrSet = Sets.filter(entrySet, new Predicate<Map.Entry<String, String>>(){
public boolean apply(Map.Entry<String, String> entry) {
return nameCriteria.equals(entry.getKey()) &&
valueCriteria.equals(entry.getValue());
}
});
return matchedAttrSet != null && !matchedAttrSet.isEmpty();
}
}
So far so good but the real benefit of defining the Predicate comes with the third case. In this case what is needed is to define a criteria which is composition of NodeNameCriteria and AttributeNameValueCriteria. We don't need to define third criteria implementation. Instead we will use Predicates compose method. Add following implmentation of findChildrenByNodeNameAndAttributeNameValue to NodeSearcher
public Collection<NodeComponent> findChildrenByNodeNameAndAttributeNameValue(String nodeName, String attrName, String attrValue)In simpler approach, one would have defined filter criteria as condition of if loop. But with implementing them as Predicate implementation, you can reuse criteria implementation and mix and match with help of following Predicates function and and or methods.
{
Predicate<NodeComponent> compositePredicate = Predicates.and(new NodeNameCriteria(nodeName),
new AttributeNameValueCriteria(attrName, attrValue));
return Collections2.filter(currentNode.getChildren(), compositePredicate);
}
Blogged with the Flock Browser
1 comment:
Hello Kartik, thanks for putting this code online. I'm now using it, with some improvements, to monitor a Glassfish server for performance testing.
Cheers,
Ludovic
Post a Comment