/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.wkf.swleditor;
import java.util.Hashtable;
import java.util.Vector;
import java.util.logging.Logger;
import org.openflexo.fge.geom.FGEPoint;
import org.openflexo.foundation.utils.FlexoProgress;
import org.openflexo.foundation.wkf.Role;
import org.openflexo.foundation.wkf.node.AbstractNode;
import org.openflexo.foundation.wkf.node.EventNode;
import org.openflexo.foundation.wkf.node.OperatorNode;
import org.openflexo.foundation.wkf.node.PetriGraphNode;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.wkf.WKFLayoutManager;
public class SWLLayoutManager extends WKFLayoutManager {
private static final Logger logger = Logger.getLogger(SWLLayoutManager.class.getPackage().getName());
private final SwimmingLaneRepresentation _representation;
private Hashtable<Role, AutoLayoutNodePath> isolatedNodePath;
public SWLLayoutManager(SwimmingLaneRepresentation representation) {
super(representation.getProcess());
_representation = representation;
}
@Override
public void recomputeProcessStructure(FlexoProgress progress) {
super.recomputeProcessStructure(progress);
if (progress != null) {
progress.setProgress(FlexoLocalization.localizedForKey("finding_isolated_nodes"));
}
isolatedNodePath = new Hashtable<Role, AutoLayoutNodePath>();
for (AutoLayoutNode n : getIsolatedNodes()) {
Role role = bestRepresentationRole(n.node);
AutoLayoutNodePath path = isolatedNodePath.get(role);
if (path == null) {
isolatedNodePath.put(role, path = new AutoLayoutNodePath());
}
path.add(n);
}
}
protected Role bestRepresentationRole(PetriGraphNode node) {
Role role = SwimmingLaneRepresentation.getRepresentationRole(node);
if (role == null || role.isDefaultRole()) {
if (node instanceof EventNode || node instanceof OperatorNode) {
role = node.getBestRole();
if (node instanceof EventNode) {
((EventNode) node).setRole(role);
} else {
((OperatorNode) node).setRole(role);
}
}
if (role == null) {
return node.getProcess().getWorkflow().getRoleList().getDefaultRole();
}
}
return role;
}
/*private Role bestRepresentationRole(AbstractNode node, Vector<AbstractNode> acc)
{
if (acc.contains(node))
return getProcess().getWorkflow().getDefaultSystemRole();
else
acc.add(node);
AutoLayoutNode aln = nodeMap.get(node);
if (aln == null) return getProcess().getWorkflow().getDefaultSystemRole();
boolean chooseSystemRole = false;
if (node instanceof AbstractActivityNode) {
if (((AbstractActivityNode)node).getRole() != null)
return ((AbstractActivityNode)node).getRole();
else
return getProcess().getWorkflow().getDefaultSystemRole();
}
if ((node instanceof FlexoNode && ((FlexoNode)node).isBeginNode())
|| (node instanceof FlexoNode && ((FlexoNode)node).isEndNode())
|| (node instanceof SelfExecutableNode)) chooseSystemRole=true;
Hashtable<Role,Integer> roles = new Hashtable<Role,Integer>();
for (AutoLayoutNode previous : aln.previousNodes.values()) {
Role r = bestRepresentationRole(previous.node,acc);
if (r != null) roles.put(r, (roles.get(r)!=null?roles.get(r):0)+1);
}
for (AutoLayoutNode next : aln.followingNodes.values()) {
Role r = bestRepresentationRole(next.node,acc);
if (r != null) roles.put(r, (roles.get(r)!=null?roles.get(r):0)+1);
}
Role returned = null;
int bestOccurence = 0;
for (Role r : roles.keySet()) {
if (!chooseSystemRole || r.getIsSystemRole()) {
if (roles.get(r) > bestOccurence) {
returned = r;
bestOccurence = roles.get(r);
}
}
}
if (returned == null) return getProcess().getWorkflow().getDefaultSystemRole();
return returned;
}*/
private Hashtable<Role, SwimmingPool> generalLayout;
@Override
public void layoutProcess(FlexoProgress progress) {
recomputeProcessStructure(progress);
if (getMainPath() == null) {
return;
}
generalLayout = new Hashtable<Role, SwimmingPool>();
if (progress != null) {
progress.setProgress(FlexoLocalization.localizedForKey("computing_layout"));
}
layoutPath(getMainPath(), progress);
for (AutoLayoutNodePath secondaryPath : getSecondaryPaths()) {
layoutPath(secondaryPath, progress);
}
for (Role r : isolatedNodePath.keySet()) {
layoutPath(isolatedNodePath.get(r), progress);
}
logger.info("Layout has been computed, apply it");
if (progress != null) {
progress.setProgress(FlexoLocalization.localizedForKey("applying_layout"));
}
if (progress != null) {
progress.resetSecondaryProgress(generalLayout.size() + nodeMap.size());
}
_representation.setSWLWidth(maxWidth + 3 * MARGIN);
for (Role r : generalLayout.keySet()) {
if (progress != null) {
progress.setSecondaryProgress(FlexoLocalization.localizedForKey("role") + " " + r.getName());
}
SwimmingPool pool = generalLayout.get(r);
for (SwimmingLane lane : pool.lanes.values()) {
for (AutoLayoutNode node : lane.nodes) {
node.proposedLocation.y = lane.index * pool.maxLaneHeight;
}
}
_representation.setHeight(r, pool.maxLaneHeight * pool.lanes.size());
}
for (AutoLayoutNode n : nodeMap.values()) {
if (progress != null) {
progress.setSecondaryProgress(FlexoLocalization.localizedForKey("node") + " " + n.node.getName());
}
n.node.setX(n.proposedLocation.x, SWLEditorConstants.SWIMMING_LANE_EDITOR);
n.node.setY(n.proposedLocation.y, SWLEditorConstants.SWIMMING_LANE_EDITOR);
}
}
private static final double MARGIN = 30;
private static final double SPACE_IN_SAME_LANE = 45;
private static final double SPACE_WHEN_CHANGING_LANE = 30;
private static final int SWL_HEIGHT = 80;
private double maxWidth = 1000 - 3 * MARGIN;
private void layoutPath(AutoLayoutNodePath path, FlexoProgress progress) {
SwimmingLane previousLane = null;
AbstractNode previousNode = null;
boolean isMainPath = path == getMainPath();
double x;
if (isMainPath) {
x = MARGIN;
} else {
if (path.startPath != null) {
AutoLayoutNode firstElement = path.firstElement();
x = firstElement.proposedLocation.x + SPACE_WHEN_CHANGING_LANE;
} else if (path.endPath != null) {
double requiredWidth = 0;
for (AutoLayoutNode n : path) {
if (n != path.lastElement()) {
requiredWidth += n.node.getWidth(SWLEditorConstants.SWIMMING_LANE_EDITOR) + SPACE_WHEN_CHANGING_LANE;
}
}
AutoLayoutNode lastElement = path.lastElement();
x = lastElement.proposedLocation.x - requiredWidth;
} else {
x = MARGIN;
}
}
if (progress != null) {
progress.resetSecondaryProgress(path.size());
}
for (AutoLayoutNode n : path) {
if (progress != null) {
progress.setSecondaryProgress(FlexoLocalization.localizedForKey("laying_out") + " " + n.node.getName());
}
if (!isMainPath) {
if (path.startPath != null && n == path.firstElement()) {
// In this case, this is a secondary path attached to an other path
// And this is the first node, which location is then already determined
// System.out.println("Forget about "+n+" for secondary path "+path);
continue;
}
if (path.endPath != null && n == path.lastElement()) {
// In this case, this is a secondary path attached to an other path
// And this is the last node, which location is then already determined
// System.out.println("Forget about "+n+" for secondary path "+path);
continue;
}
}
Role role = bestRepresentationRole(n.node);
SwimmingPool pool = generalLayout.get(role);
if (pool == null) {
pool = new SwimmingPool(role);
generalLayout.put(role, pool);
}
SwimmingLane lane = pool.lanes.get(path);
if (lane == null) {
lane = new SwimmingLane(path, pool.lanes.size());
pool.lanes.put(path, lane);
}
lane.nodes.add(n);
double height = n.node.getHeight(SWLEditorConstants.SWIMMING_LANE_EDITOR);
if (height + 2 * MARGIN > pool.maxLaneHeight) {
pool.maxLaneHeight = height + 2 * MARGIN;
}
if (previousLane != null && lane != previousLane && n != path.lastElement()) {
x += SPACE_WHEN_CHANGING_LANE;
if (lane.nodes.size() > 1) {
AutoLayoutNode previousNodeInLane = lane.nodes.get(lane.nodes.size() - 2);
x = previousNodeInLane.proposedLocation.x + previousNodeInLane.node.getWidth(SWLEditorConstants.SWIMMING_LANE_EDITOR)
+ SPACE_IN_SAME_LANE;
}
} else if (previousNode != null) {
x = x + previousNode.getWidth(SWLEditorConstants.SWIMMING_LANE_EDITOR) + SPACE_IN_SAME_LANE;
}
n.proposedLocation = new FGEPoint(x, lane.index * SWL_HEIGHT);
previousLane = lane;
previousNode = n.node;
}
if (x + previousNode.getWidth(SWLEditorConstants.SWIMMING_LANE_EDITOR) + MARGIN > maxWidth) {
maxWidth = x + previousNode.getWidth(SWLEditorConstants.SWIMMING_LANE_EDITOR) + MARGIN;
}
}
class SwimmingPool {
Role role;
Hashtable<AutoLayoutNodePath, SwimmingLane> lanes;
double maxLaneHeight = SWL_HEIGHT;
public SwimmingPool(Role aRole) {
role = aRole;
lanes = new Hashtable<AutoLayoutNodePath, SwimmingLane>();
}
}
class SwimmingLane {
int index;
AutoLayoutNodePath path;
Vector<AutoLayoutNode> nodes;
SwimmingLane(AutoLayoutNodePath aPath, int anIndex) {
index = anIndex;
path = aPath;
nodes = new Vector<AutoLayoutNode>();
}
}
}