package urban.model.info;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.collections15.CollectionUtils;
import org.apache.commons.collections15.SortedBag;
import org.apache.commons.collections15.bag.TreeBag;
import urban.model.Agent;
import urban.model.Rule;
import urban.model.ShapeParameterStatement;
import urban.model.Site;
import urban.model.SiteArray;
import urban.model.Statement;
import urban.shapes.ShapeParameters;
/**
* This provides all sorts of useful calculated information about a collection of statements
*/
public class ModelInfo {
private HashMap<String, Site> sites;
private HashMap<String, SiteArray> arrays;
private Collection<Statement> lines;
private HashMap<String, Set<String>> bindings;
private ShapeParameters shapeParameters;
private HashMap<String, Agent> agents;
/**
* The constructor requires the collection of statements to be used.
* The collection is not changed in any way by this class.
* @param lines
*/
public ModelInfo(Collection<Statement> lines) {
this.lines = lines;
}
/**
* @return Any parameters related to shapes are returned as ShapeParameters
*/
public ShapeParameters getShapeParameters(){
if (shapeParameters == null)
shapeParameters = createShapeParameters();
return shapeParameters;
}
/**
* @return The set of sites mentioned in the model
*/
public Collection<Site> getSites() {
if (sites == null){
sites = new HashMap<String, Site>();
arrays = new HashMap<String, SiteArray>();
for(Statement s : lines)
addAllSites(s.getSites());
}
return CollectionUtils.union(sites.values(), arrays.values());
}
/**
* The sites within the agent will be a merged set of sites referred to
* @return The set of agents mentioned in the model.
*/
public Collection<Agent> getAgents() {
if (agents == null){
agents = new HashMap<String, Agent>();
for(Statement s : lines)
addAgent(s.getAgents());
}
return agents.values();
}
/**
* @param qualifiedName
* @return A collection of sites that may be bound to the site name given
*/
public Collection<String> getBindingsForSite(String qualifiedName){
getSites();
getBindings();
return getSiteSet(qualifiedName);
}
/**
* @return a map of all the bindings in the model
*/
public HashMap<String, Set<String>> getBindings() {
if (bindings == null)
bindings = new HashMap<String, Set<String>>();
for(Statement s : lines){
if (s instanceof Rule){
addBindings((Rule)s);
}
}
return bindings;
}
/**
* @param qualifiedName
* @return A list of possible states for the given site
*/
public Collection<String> getStatesForSite(String qualifiedName) {
getSites();
final Site site = sites.get(qualifiedName);
if (site == null)
return null;
final String state = site.getState();
if (state == null)
return null;
return Arrays.asList(state.split(", "));
}
/**
* @param name
* @return An agent with the name if it exists in the model or null
*/
public Agent getAgent(String name) {
if (agents == null)
getAgents();
return agents.get(name);
}
private ShapeParameters createShapeParameters() {
ShapeParameters tmp = new ShapeParameters();
for (Statement s : lines)
if (s instanceof ShapeParameterStatement){
((ShapeParameterStatement)s).addParametersTo(tmp);
}
return tmp;
}
private Comparator<? super Site> compareByName = new Comparator<Site>() {
public int compare(Site o1, Site o2) {
return o1.getName().compareTo(o2.getName());
}
};
private void addAgent(Collection<Agent> list) {
for(Agent a : list){
final Agent mergeAgent = mergeAgent(a, agents.get(a.getName()));
agents.put(a.getName(), mergeAgent);
}
}
private Agent mergeAgent(Agent a1, Agent a2) {
if (a1 == null)
return a2;
if (a2 == null)
return a1;
return new Agent(a1.getName(), mergeSites(a1.getSites(), a2.getSites()));
}
Collection<Site> mergeSites(Collection<Site> a, Collection<Site> b) {
SortedBag<Site> s1 = getSorted(a);
SortedBag<Site> s2 = getSorted(b);
SortedBag<Site> result = getSorted(Collections.<Site>emptyList());
Iterator<Site> i1= s1.iterator();
Iterator<Site> i2 = s2.iterator();
Site x=null,y = null;
while(x != null || i1.hasNext() || y != null || i2.hasNext()){
if (i1.hasNext())
x = i1.next();
if (i2.hasNext())
y = i2.next();
if (x == null){
result.add(y);
y = null;
continue;
}
if (y == null){
result.add(x);
x = null;
continue;
}
int comparison = x.getName().compareTo(y.getName());
if (comparison == 0){ // have the same name
result.add(mergeSite(x,y));
x = null;
y = null;
} else if (comparison < 0){ // x is less than y
result.add(x);
x = null;
} else if (comparison > 0){ // x is less than y
result.add(y);
y = null;
}
}
return result;
}
Site mergeSite(Site x, Site y) {
String states = null;
if (x.getState() != null && y.getState() != null){
TreeSet<String> list = new TreeSet<String>(Arrays.asList(x.getState().split(", ")));
list.addAll( Arrays.asList(y.getState().split(", ")) );
states = list.toString().replaceAll("[\\[\\]]", "");
} else if (x.getState() != null) {
states = x.getState();
} else if (y.getState() != null) {
states = y.getState();
}
return new Site(x.getAgent(), x.getName(), states, null);
}
private SortedBag<Site> getSorted(Collection<Site> a) {
TreeBag<Site> tb = new TreeBag<Site>(compareByName);
tb.addAll(a);
return tb;
}
private void addAllSites(Collection<Site> toBeAdded) {
for(Site s : toBeAdded){
if (s.getClass() == SiteArray.class)
addToArrays((SiteArray) s);
else
addToSite(s);
}
}
private void addToSite(Site s) {
String key = s.getQualifiedName();
Site tmp = sites.get(key);
if (tmp == null)
sites.put(key, s);
else {
sites.put(key, mergeSite(tmp,s));
}
}
private void addToArrays(SiteArray s) {
String key = s.getQualifiedName();
SiteArray tmp = arrays.get(key);
if (tmp == null)
arrays.put(key, s);
else {
if (tmp.getIndex() < s.getIndex()){
arrays.put(key, s);
}
}
}
private void addBindings(Rule s) {
addBindings(s.getLhs());
addBindings(s.getRhs());
}
private void addBindings(Collection<Agent> agents) {
HashMap<String, Site> b = new HashMap<String, Site>();
for (Agent a : agents){
for (Site s : a.getSites()){
if (s.getBindingMark() != null){
final Site s1 = b.get(s.getBindingMark());
if (s1 != null){
if (s.getQualifiedName().compareTo(s1.getQualifiedName()) <= 0)
addBinding(s.getQualifiedName(), s1);
else
addBinding(s1.getQualifiedName(), s);
} else {
b.put(s.getBindingMark(), s);
}
}
}
}
}
private void addBinding(String qualifiedName, Site s) {
getSiteSet(qualifiedName).add(s.getQualifiedName());
}
private Set<String> getSiteSet(String qualifiedName) {
Set<String> set = bindings.get(qualifiedName);
if (set == null){
set = new HashSet<String>();
bindings.put(qualifiedName, set);
}
return set;
}
}