/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2008, Benedikt Huber (benedikt.huber@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.wcet.uppaal.model;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
/* BROKEN */
public class LayoutCFG {
public int x_step;
public int y_step;
private Map<Integer,LinkedList<Location>> nodesByYCoord;
private Map<Location,Location> maxPred;
private Template template;
private Map<Location,Integer> orderNumber;
public LayoutCFG(int x_step, int y_step) {
this.x_step = x_step;
this.y_step = y_step;
}
/**
* Compute a (heuristic) partial order on the locations of the automaton.
* Conceptually, we reduce the graph to a DAG, removing cycles depth first.
*
* Then we assign order numbers to locations, s.t. ORD(l) = 1 + max ORD predecessors(l).
*
* NOTE: This a heuristic for layouting only, as removing cycles isn't predictable.
* Consider the complete graph K3, with initial node A:
*
* A <-> B <-> C
* ^-----------^
*
* Removing cycles either gives:
* A -> B -> C
* | |----^
* ^---------^
* or
* A -> C -> B
* | |----^
* ^---------^
*
* Now we either get: A < B < C or A < C < B, depending on the implementation.
*
*/
public Map<Location,Integer> computeLocOrdering(Template t) {
this.template = t;
this.orderNumber = new TreeMap<Location,Integer>();
computeOrdering();
return this.orderNumber;
}
/* 'struct' for DFS */
private class PathEntry {
public PathEntry(Location l, Iterator<Transition> succIterator) {
loc = l;
cont = succIterator;
}
Location loc;
Iterator<Transition> cont;
public String toString() { return "PE_"+loc.toString(); }
}
/* compute partial order on locations */
private void computeOrdering() {
Set<Transition> cycleTransitions = new TreeSet<Transition>();
/* Mark cycles using path-aware DFS */
cycleTransitions = markCycles();
/* assign order numbers */
Stack<Location> locStack = new Stack<Location>();
locStack.push(template.getInitial());
while(! locStack.empty()) {
Location loc = locStack.pop();
/* check if all predecessors have been processed, ignoring cycle transitions */
List<Location> todoPreds = new Stack<Location>();
int maxPred = -1;
for(Transition tPred : loc.getPredecessors()) {
if(cycleTransitions.contains(tPred)) continue;
if(orderNumber.containsKey(tPred.getSource())) {
maxPred = Math.max(maxPred, orderNumber.get(tPred.getSource()));
} else {
todoPreds.add(tPred.getSource());
}
}
/* If we're done, compute the order number, and push successors which do not yet
* have an order number */
if(todoPreds.isEmpty()) {
orderNumber.put(loc, maxPred + 1);
for(Transition tSucc : loc.getSuccessors()) {
if(cycleTransitions.contains(tSucc)) continue;
if(! orderNumber.containsKey(tSucc.getTarget())) {
locStack.push(tSucc.getTarget());
}
}
}
/* Otherwise, process the predecessors */
else {
locStack.addAll(todoPreds);
}
}
}
/* mark cycle transitions */
private Set<Transition> markCycles() {
Set<Transition> cycleTransitions = new HashSet<Transition>();
/* A stack of locations */
Stack<Location> locStack = new Stack<Location>();
Stack<PathEntry> locPath = new Stack<PathEntry>();
Set<Location> visitedLocs = new TreeSet<Location>();
/* Initialize */
locStack.add(template.getInitial());
while(! locStack.empty()) {
Location l = locStack.pop();
/* find an unvisited successor, remembering the iterator */
visitedLocs.add(l);
Location succLocation = null;
Iterator<Transition> succIterator = l.getSuccessors().iterator();
while(succIterator.hasNext()) {
Transition succTransition = succIterator.next();
Location tmpLoc = succTransition.getTarget();
if(containsLoc(tmpLoc,locPath)) {
/* Cycle transition */
cycleTransitions.add(succTransition);
} else if(visitedLocs.contains(tmpLoc)) {
} else {
succLocation = tmpLoc;
break;
}
}
/* Check if there is a successor */
if(succLocation != null) {
locStack.push(succLocation);
locPath.push(new PathEntry(l,succIterator));
} else {
/* move back */
while(! locPath.isEmpty() &&
! locPath.peek().cont.hasNext()) {
locPath.pop(); /* remove processed entry from locPath */
}
if(! locPath.isEmpty() &&
locPath.peek().cont.hasNext()) {
locStack.push(locPath.peek().cont.next().getTarget());
}
}
}
return cycleTransitions;
}
/* check if a locations occurs in a path */
private static boolean containsLoc(Location l, Collection<PathEntry> path) {
for(PathEntry e : path) {
if(l.equals(e.loc)) return true;
}
return false;
}
/* Comparator for order numbers */
private final Comparator<Transition> compareSourceByOrder =
new Comparator<Transition>() {
public int compare(Transition l1, Transition l2) {
Integer n1 = orderNumber.get(l1.getSource());
Integer n2 = orderNumber.get(l2.getSource());
return n1.compareTo(n2);
}
};
/**
* Simple layout for hierarchical templates with cycles.
* Writing a good layout algorithm is very hard, so we chose to take
* a simple, yet usable variant.
*
* The strategy is a three-pass layout algorithm on a graph G
* 1st pass: Y-Layout
* - Locations are partially ordered (see computeLocOrdering)
* - The y-coordinate corresponds to the order number
* 2nd pass: X-Layout
* - The node with the smallest y-coordinate is layouted first.
* - If more than one node is on some y-coordinate, we sort the nodes
* by the x-coordinate of their predecessors, and then stack them horizontally
* - TODO (low priority) : compact X-Layout
* Before the 3rd pass, convert the pseudo into screen coordinates
* 3rd pass: Transitions
* - If a transition is a forward ref or a backlink, add two nails and slightly displace
* the transition.
* @param template The automaton template to layout
*/
public void layoutCfgModel(Template template) {
this.template = template;
this.nodesByYCoord = new TreeMap<Integer, LinkedList<Location>>();
this.maxPred = new TreeMap<Location, Location>();
/* Firt pass */
this.orderNumber = new TreeMap<Location,Integer>();
this.computeOrdering();
/* y - coord ~ order number */
for(Location l : template.getLocations()) {
setYCoord(l, orderNumber.get(l));
}
/* compute lowest predecessors */
for(Location l : template.getLocations()) {
if(! l.getPredecessors().isEmpty()) {
maxPred.put(l,Collections.max(l.getPredecessors(),compareSourceByOrder).getSource());
}
}
/* 2nd pass */
displaceLocations();
/* Screen Coordinates */
for(Location l: template.getLocations()) {
l.setPos(l.getCoords().x * x_step, l.getCoords().y * y_step);
}
/* 3rd pass */
transitionLayout();
// for(Location l: template.getLocations()) {
// System.err.println("Loc: "+l);
// System.err.println("Pos: "+l.getCoords());
// }
}
/* set and record the y-coordinate of a location. */
private void setYCoord(Location loc, int y) {
loc.setPos(0, y);
if(! this.nodesByYCoord.containsKey(y)) {
LinkedList<Location> entries = new LinkedList<Location>();
entries.add(loc);
this.nodesByYCoord.put(y, entries);
} else {
this.nodesByYCoord.get(y).add(loc);
}
}
/* Compare to locations by the x-coord of their lowest predecessor */
private final Comparator<Location> compareByLowestPredsXCoord =
new Comparator<Location>() {
public int compare(Location l1, Location l2) {
Location p1 = maxPred.get(l1);
Location p2 = maxPred.get(l2);
return compareInt(p1.getCoords().x, p2.getCoords().x);
}
};
/*
* Traverse the locations by y-coordinate.
* Sort locations sharing the same y-coord in order of the x-coord of their max y-coord
* predecessors.
* Stack the locations vertically in this order.
*/
private void displaceLocations() {
for(List<Location> locs : this.nodesByYCoord.values()) {
if(locs.get(0).getCoords().y == 0) {
locs.get(0).setPos(0,locs.get(0).getCoords().y);
continue;
}
Collections.sort(locs, compareByLowestPredsXCoord);
int minX = 0;
for(Location l : locs) {
int predX = maxPred.get(l).getCoords().x;
int xCoord = Math.max(minX, predX);
l.setPos(xCoord,l.getCoords().y);
minX = xCoord+1;
}
}
}
private void transitionLayout() {
for(Transition t: template.getTransitions()) {
Location src = t.getSource();
Location target = t.getTarget();
if(target.getCoords().y - src.getCoords().y != y_step) {
int displace_y = y_step / 2;
if(src.getCoords().y > target.getCoords().y) displace_y = -displace_y;
int displace_x = x_step / 3;
int displace_x_src, displace_x_target;
if(src.getCoords().x == target.getCoords().x) {
displace_x_src = -displace_x;
displace_x_target = -displace_x;
} else if(src.getCoords().x > target.getCoords().x) {
displace_x_src = -displace_x;
displace_x_target = displace_x;
} else {
displace_x_src = displace_x;
displace_x_target = -displace_x;
}
t.addNail(src.getCoords().x+displace_x_src,src.getCoords().y+displace_y);
t.addNail(target.getCoords().x+displace_x_target,target.getCoords().y-displace_y);
}
}
}
public static int compareInt(int i1, int i2) {
return new Integer(i1).compareTo(i2);
}
}