package urban.shapes;
import static org.apache.commons.collections15.CollectionUtils.collect;
import static org.apache.commons.collections15.CollectionUtils.select;
import static org.apache.commons.collections15.CollectionUtils.union;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import org.apache.commons.collections15.Predicate;
import org.apache.commons.collections15.SortedBag;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.bag.TreeBag;
import urban.model.Agent;
import urban.model.Site;
import urban.transformers.CanonicalBindingsTransformer;
import urban.transformers.ShapeToRuleGraphTransformer;
/**
* A shape - a connected set of agents.
*/
public class Shape {
private static CanonicalBindingsTransformer transformer = new CanonicalBindingsTransformer();
private final Collection<Agent> agents;
private final Transformer<ShapePlusSite, RuleGraph> createGraph = new ShapeToRuleGraphTransformer();
/**
* @param agents The agents that make up the shape
*/
public Shape(Collection<Agent> agents) {
this.agents = canonicalise(agents);
}
/**
* @return the agents that make up the shape
*/
public Collection<Agent> getAgents() {
return agents;
}
/**
* Adds this shape to the rule graph in all possible ways to produce new rule graphs.
* @param rg1 the rulegraph that is to be extended
* @return a collection of rule graphs
*/
public Collection<RuleGraph> getMatchPermutations(RuleGraph rg1) {
List<RuleGraph> result = new ArrayList<RuleGraph>();
result.addAll(getMatchPermutations(rg1, true));
result.addAll(getMatchPermutations(rg1, false));
return result;
}
/**
* Adds this shape to the rule graph in all possible ways to produce new rule graphs.
* @param rg1 the rulegraph that is to be extended
* @param left true is shape is to be merge on the left
* @return a collection of rule graphs
*/
public Collection<RuleGraph> getMatchPermutations(RuleGraph rg1, boolean left) {
List<RuleGraph> result = new ArrayList<RuleGraph>();
for(RuleGraph r : getPossibleGraphs(rg1, left)){
RuleGraph tmp = new RuleMerger(rg1, r).merge();
if (tmp != null)
result.add(tmp);
}
return result;
}
@Override
public String toString(){
return new ArrayList<Agent>(agents).toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((agents == null) ? 0 : agents.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Shape other = (Shape) obj;
if (agents == null) {
if (other.agents != null)
return false;
} else if (!agents.equals(other.agents))
return false;
return true;
}
private Collection<Agent> canonicalise(Collection<Agent> agents2) {
SortedBag<Agent> bag = new TreeBag<Agent>(new Comparator<Agent>() {
@Override
public int compare(Agent o1, Agent o2) {
int tmp = o1.getName().compareTo(o2.getName());
if (tmp != 0)
return tmp;
return o1.toString().compareTo(o2.toString());
}
});
bag.addAll(transformer.transform(agents2));
return bag;
}
private Collection<RuleGraph> getPossibleGraphs(RuleGraph rg1, boolean left) {
if (left && rg1.hasSymmetricalBond()){
// there is an awkward case where a site may match both sides of a bond if
// the bond is on the same agent and site eg. A(a!1),A(a!1)
return union(
collect(select(toSites(agents), siteMatchesNode(rg1, left,true)), createGraph),
collect(select(toSites(agents), siteMatchesNode(rg1, left,false)), new ShapeToRuleGraphTransformer(rg1)));
}
else
return collect(select(toSites(agents), siteMatchesNode(rg1, left)), createGraph);
}
private Predicate<ShapePlusSite> siteMatchesNode(final RuleGraph rg1, final boolean onLeft) {
final Node root = rg1.getRoot();
if (root instanceof BondNode) {
Iterator<Entry<Link, RuleGraph>> iterator = rg1.getChildren().iterator();
final Entry<Link, RuleGraph> left = iterator.next();
final Entry<Link, RuleGraph> right = iterator.next();
return new Predicate<ShapePlusSite>() {
public boolean evaluate(ShapePlusSite p) {
Site s = p.site;
if (!onLeft && s.getBindingMark() == null)
return false;
if (onLeft && s.getBindingMark() != null)
return false;
return test(s, left) || test(s, right);
}
private boolean test(Site s, Entry<Link, RuleGraph> e) {
return (e.getKey().getDst().equals(s.getName())
&& e.getValue().getRoot().getName().equals(s.getAgent()));
}
};
} else {
final String state = ((SiteNode)root).getLeft().getState();
return new Predicate<ShapePlusSite>() {
public boolean evaluate(ShapePlusSite p) {
Site s = p.site;
return root.getName().equals(s.getAgent())
&& root.getGenerator().equals(s.getName())
&& (
(onLeft && state.equals(s.getState()))
||
(!onLeft && !state.equals(s.getState()))
);
}
};
}
}
private Predicate<ShapePlusSite> siteMatchesNode(final RuleGraph rg1, final boolean onLeft, final boolean firstAgent) {
Iterator<Entry<Link, RuleGraph>> iterator = rg1.getChildren().iterator();
final Entry<Link, RuleGraph> left = iterator.next();
final Entry<Link, RuleGraph> right = iterator.next();
return new Predicate<ShapePlusSite>() {
public boolean evaluate(ShapePlusSite p) {
Site s = p.site;
if (s.getBindingMark() != null)
return false;
return (firstAgent && test(s, left)) || (!firstAgent && test(s, right));
}
private boolean test(Site s, Entry<Link, RuleGraph> e) {
return (e.getKey().getDst().equals(s.getName())
&& e.getValue().getRoot().getName().equals(s.getAgent()));
}
};
}
private Collection<ShapePlusSite> toSites(Collection<Agent> agents){
List<ShapePlusSite> sites = new ArrayList<ShapePlusSite>();
for(Agent a : agents){
for(Site s : a.getSites())
sites.add(new ShapePlusSite(this, a,s));
}
return sites;
}
/**
* Simple class for representing a Shape plus a site within the shape.
*/
public static class ShapePlusSite {
/**
* Target shape
*/
public final Shape shape;
/**
* Agent that site can be found in
*/
public final Agent agent;
/**
* Target site
*/
public final Site site;
ShapePlusSite(Shape shape, Agent agent, Site site) {
this.shape = shape;
this.agent = agent;
this.site = site;
}
}
}