Package urban.shapes

Source Code of urban.shapes.RuleMerger

package urban.shapes;

import static urban.util.Pair.pairOf;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import urban.model.Rule;
import urban.transformers.RuleGraphToRuleTransformer;
import urban.transformers.RuleToRuleGraphTransformer;
import urban.util.Pair;

/**
* Merges compatible Rules or RuleGraphs together.
*
* If parameters cannot be merged then null will be returned.
*/
public class RuleMerger {
  /**
   * Converts rules to rulegraphs, merges them and converts the result back to a rule.
   * Used by test code.
   *
   * @param r first rule
   * @param r2 second rule
   * @return merged rule
   */
  public static Rule merge(Rule r, Rule r2) {
    try {
      RuleGraph rg1 = new RuleToRuleGraphTransformer().transform(r);
      RuleGraph rg2 = new RuleToRuleGraphTransformer().transform(r2);
     
      return new RuleGraphToRuleTransformer().transform(new RuleMerger(rg1,rg2).merge());
    } catch (IllegalArgumentException ex){
      return null;
    }
  }

  private final RuleGraph rg1;
  private final RuleGraph rg2;
  private Map<RuleGraph, RuleGraph> reverse = new HashMap<RuleGraph, RuleGraph>();
  private Map<RuleGraph, List<Pair<RuleGraph,Link>>> reverseLinks = new HashMap<RuleGraph, List<Pair<RuleGraph,Link>>>();
 
  /**
   * Constructor
   * @param rg1 first RuleGraph
   * @param rg2 second RuleGraph
   */
  public RuleMerger(RuleGraph rg1, RuleGraph rg2) {
    this.rg1 = rg1;
    this.rg2 = rg2;
  }

  /**
   * Merges the two RuleGraphs provided in the constructor.
   *
   * @return a merged rulegraph or null if they cannot be merged.
   */
  public RuleGraph merge() {   
    try {
      final RuleGraph merge = merge(rg1, rg2);
      testForInvalidPinch();
      return merge;
    } catch (IllegalArgumentException ex){
      return null;
    }
  }

  private RuleGraph merge(RuleGraph r1, RuleGraph r2) {
    if (r1 == null && r2 == null)
      return null;
    if (r2 == null){
      return r1;
    }
    if (r1 == null){
      return r2;
    }
    if (r1 == r2){
      return r1;
    }

    RuleGraph graph = new RuleGraph(merge(r1.getRoot(), r2.getRoot()));
   
    if (r1.getRoot() instanceof BondNode && (r1.getChildren().size() == 1 || r2.getChildren().size() == 1))
    {
      try {
        doMerge(r1, r2, graph);       
      } catch(IllegalArgumentException e){
        if (r1.hasSymmetricalBond() || r2.hasSymmetricalBond())
          throw e;
       
        graph = new RuleGraph(merge(r1.getRoot(), r2.getRoot()));
        final TreeMap<Link, RuleGraph> list = new TreeMap<Link, RuleGraph>(r2.getChildrenMap());
        for(Entry<Link, RuleGraph> entry : list.entrySet()){
          r2.removeChild(entry.getKey());
        }
        for(Entry<Link, RuleGraph> entry : list.entrySet()){
          r2.addChild(swap(entry.getKey()), entry.getValue());
        }
        doMerge(r1,r2,graph);
      }
    } else {
      doMerge(r1, r2, graph);
    }
    return graph;
  }


  private Link swap(Link key) {
    return new Link(key.getSrc().equals("1") ? "2" : "1", key.getDst());
  }

  private void doMerge(RuleGraph r1, RuleGraph r2, RuleGraph graph) {
    List<String> used = new LinkedList<String>();
    mergeShared(r1, r2, graph, used);
    mergeOnlyInFirst(r1, r2, graph, used);
    mergeOnlyInFirst(r2, r1, graph, used);
  }

  /*
   * Simple merges for graphs in r2 that r1 does not contain.
   */
  private void mergeOnlyInFirst(RuleGraph r2, RuleGraph r1, RuleGraph graph, List<String> used) {
    for(Entry<Link, RuleGraph> e : r2.getChildren()){
      if(r1.getChild(e.getKey()) == null){
        String site = e.getKey().getSrc();
        if (used.contains(site))
          throw new IllegalArgumentException("Conflicting sites - rules do not merge");
        if (graph.getRoot().contains(site))
          throw new IllegalArgumentException("Conflicting sites - rules do not merge");
        used.add(site);
        RuleGraph rg2 = e.getValue();
        RuleGraph pinch2 = reverse.get(rg2);
        RuleGraph value = pinch2 == null ? rg2 : pinch2;
       
        maintainPinchRecords(null, pinch2, value);
        maintainPinchRecords(r1, graph, e, rg2, value);
        recordInfoForPinches(value);// value is not merged with anything so need to add the contents to the pinch data
        graph.addChild(e.getKey(), value);
      }
    }
  }
  private void recordInfoForPinches(RuleGraph r2) {
    if (r2 != null && r2.getChildren() != null){
      for(Entry<Link, RuleGraph> e : r2.getChildren()){
        RuleGraph rg2 = e.getValue();
        RuleGraph pinch2 = reverse.get(rg2);
        RuleGraph value = pinch2 == null ? rg2 : pinch2;
        maintainPinchRecords(null, pinch2, value);
        maintainPinchRecords(r2, r2, e, rg2, value);
        recordInfoForPinches(value)
      }
    }
  }

  private void testForInvalidPinch() {
    ArrayList<RuleGraph> list = new ArrayList<RuleGraph>(reverse.values());
    while(!list.isEmpty()){
      int c = 1;
      RuleGraph tmp = list.get(0);
      list.remove(tmp);
      while(list.remove(tmp))
        c++;
      if (c > 2)
        throw new IllegalArgumentException("Pinch shape conflicts with non-pinched shape");       
    }
  }

  private void mergeShared(RuleGraph r1, RuleGraph r2, RuleGraph graph, List<String> used) {
    for(Entry<Link, RuleGraph> e : r1.getChildren()){
      RuleGraph rg2 = r2.getChild(e.getKey());
      if (rg2 == null)
        continue;
      String site = e.getKey().getSrc();
      used.add(site);
      if (graph.getRoot().contains(site))
        throw new IllegalArgumentException("Conflicting sites - rules do not merge");
      RuleGraph rg1 = e.getValue();
     
      RuleGraph pinch1 = reverse.get(rg1);
      RuleGraph pinch2 = reverse.get(rg2);
     
      RuleGraph value = merge(pinch1 == null ? rg1 : pinch1, pinch2 == null ? rg2 : pinch2);

      maintainPinchRecords(pinch1, pinch2, value);
      maintainPinchRecords(r2, graph, e, rg1, value);
      maintainPinchRecords(r1, graph, e, rg2, value);

      graph.addChild(e.getKey(), value);
    }
  }

  private void maintainPinchRecords(RuleGraph pinch1, RuleGraph pinch2, RuleGraph value) {
    if (pinch1 == null && pinch2 == null)
      return;
    for(Entry<RuleGraph, RuleGraph> e : new ArrayList<Entry<RuleGraph, RuleGraph>>(reverse.entrySet())){
      if (e.getValue() == pinch1 || e.getValue() == pinch2)
        reverse.put(e.getKey(), value);
    }
  }

  private void maintainPinchRecords(RuleGraph oldParent, RuleGraph newParent, Entry<Link, RuleGraph> e, RuleGraph oldChild, RuleGraph newChild) {
    List<Pair<RuleGraph, Link>> list = reverseLinks.get(oldChild);
    if (list != null){
      for(Pair<RuleGraph,Link> l : list){
        l.fst.addChild(l.snd, newChild);
      }
    } else {
      list = new ArrayList<Pair<RuleGraph,Link>>();
      reverseLinks.put(oldChild, list);
    }
    reverse.put(oldChild, newChild);
    list.add(pairOf(newParent,e.getKey()));
  }

  private static Node merge(Node root, Node root2) {
    if (!root.getName().equals(root2.getName()))
      throw new IllegalArgumentException();
   
    if (root instanceof BondNode)
      return new BondNode();
   
    if (root instanceof SiteNode)
      return new SiteNode((SiteNode)root, (SiteNode)root2);
   
    return new Node(root, root2);
  }
}
TOP

Related Classes of urban.shapes.RuleMerger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.