package urban.transformers;
import static urban.util.Pair.pairOf;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.TreeMap;
import org.apache.commons.collections15.Transformer;
import urban.model.Agent;
import urban.model.Rule;
import urban.model.Site;
import urban.shapes.BondNode;
import urban.shapes.Link;
import urban.shapes.Node;
import urban.shapes.RuleGraph;
import urban.shapes.SiteNode;
import urban.util.Pair;
/**
* Converts a Rule to a RuleGraph
*
* An error will be thrown if the rule is not a simple on the changes only a single
* bond or state.
*
* Converting from a Rule to RuleGraph and then back to Rule may change
* the ordering of the agents in the Rule and is used to create a canonical form of a
* rule.
*/
public class RuleToRuleGraphTransformer implements Transformer<Rule, RuleGraph> {
private static class Data {
public Map<String, Agent> first;
public Map<String, Agent> second;
public Queue<Pair<Agent,RuleGraph>> q;
public LinkedList<Agent> lhs;
public LinkedList<Agent> rhs;
public RuleGraph[] rgArrays;
public Data(Collection<Agent> lhs, Collection<Agent> rhs) {
this.first = new TreeMap<String, Agent>();
this.second = new TreeMap<String, Agent>();
this.q = new LinkedList<Pair<Agent, RuleGraph>>();
this.lhs = new LinkedList<Agent>(lhs);
this.rhs = new LinkedList<Agent>(rhs);
this.rgArrays = new RuleGraph[this.lhs.size()];
}
}
@Override
public RuleGraph transform(Rule r) {
if (r == null)
return null;
Data data = new Data(r.getLhs(),r.getRhs());
RuleGraph result = createRoot(r, data);
fillDAG(data);
return result;
}
private void fillDAG(Data data) {
while(!data.q.isEmpty()){
Pair<Agent,RuleGraph> p = data.q.poll();
Agent aL = p.fst;
RuleGraph current = p.snd;
for(Site s : aL.getSites()){
String m = s.getBindingMark();
if (m != null && !"?".equals(m) && !"_".equals(m)){
Agent a = getLinked(aL, m, data.first, data.second);
if (a != null){
int indexOf = data.lhs.indexOf(a);
if (data.rgArrays[indexOf] == null){
data.rgArrays[indexOf] = new RuleGraph(new Node(a));
data.q.add(pairOf(a,data.rgArrays[indexOf]));
}
RuleGraph value = data.rgArrays[indexOf];
current.addChild(new Link(s.getName(),getSiteName(a,m)), value);
}
}
if ("_".equals(m)){
current.addChild(new Link(s.getName(),"_"), null);
}
}
}
}
private RuleGraph createRoot(Rule r, Data data) {
RuleGraph result = null;
{
Agent aL = null;
Agent bondA = null;
Agent bondB = null;
Site siteA = null;
Site siteB = null;
Iterator<Agent> rhsIt = data.rhs.iterator();
Iterator<Agent> lhsIt = data.lhs.iterator();
boolean reverse=false;
while(lhsIt.hasNext() && rhsIt.hasNext()) {
Agent a = lhsIt.next();
Agent b = rhsIt.next();
Iterator<Site> asIt = a.getSites().iterator();
Iterator<Site> bsIt = b.getSites().iterator();
while(asIt.hasNext() && bsIt.hasNext()){
Site s = asIt.next();
Site bs = bsIt.next();
String m = s.getBindingMark();
String n = bs.getBindingMark();
if (m != null && !"?".equals(m) && !"_".equals(m)){
if (data.first.containsKey(m))
data.second.put(m, a);
else
data.first.put(m, a);
}
if ((m == null && n != null) || (n == null && m != null)){
if (bondA != null){
if(bondB != null)
throw new IllegalArgumentException("Rule not supported: More than one bond formation dissallowed.");
bondB = a;
siteB = bs;
if (m != null){
data.first.remove(m);
data.second.remove(m);
reverse = true;
}
} else {
bondA = a;
siteA = bs;
}
}
if (s.getState() != null && !s.getState().equals(bs.getState())){
if (aL != null)
throw new IllegalArgumentException("Rule not supported: More than one flip dissallowed.");
aL = a;
}
}
}
if (aL == null && bondA == null)
throw new IllegalArgumentException("At least one site should differ");
if (aL != null && bondA != null)
throw new IllegalArgumentException("Rule not supported: Either flip or bond, not both.");
if (bondA == null){
int indexOf = data.lhs.indexOf(aL);
Agent aR = data.rhs.get(indexOf);
result = data.rgArrays[indexOf] = new RuleGraph(new SiteNode(aL, aR));
data.q.add(pairOf(aL, data.rgArrays[indexOf]));
} else {
result = new RuleGraph(new BondNode());
int i=0;
i = data.lhs.indexOf(bondA);
RuleGraph r1 = data.rgArrays[i] = new RuleGraph(new Node(reverse ? data.rhs.get(i) : bondA));
result.addChild(new Link("1",siteA.getName()), r1);
i = data.lhs.indexOf(bondB);
RuleGraph r2 = data.rgArrays[i] = new RuleGraph(new Node(reverse ? data.rhs.get(i) : bondB));
result.addChild(new Link("2",siteB.getName()), r2);
data.q.add(pairOf(bondA, r1));
data.q.add(pairOf(bondB, r2));
}
}
return result;
}
private static String getSiteName(Agent a, String m) {
for(Site s : a.getSites())
if (m.equals(s.getBindingMark()))
return s.getName();
return null;
}
private static Agent getLinked(Agent aL, String m, Map<String, Agent> first, Map<String, Agent> second) {
Agent agent = first.get(m);
if (agent == null || agent == aL)
agent = second.get(m);
first.remove(m);
second.remove(m);
return agent;
}
}