Commits

Anonymous committed 20e8464

Initial import from SourceForge

Comments (0)

Files changed (41)

lib/designer/forms.jar

Binary file added.

lib/designer/jgraph.jar

Binary file added.

lib/designer/looks-all.jar

Binary file added.

src/designer/com/opensymphony/workflow/config/WorkspaceManager.java

+package com.opensymphony.workflow.config;
+
+import java.io.*;
+import java.util.*;
+
+import com.opensymphony.workflow.config.ConfigLoader;
+import com.opensymphony.workflow.FactoryException;
+import com.opensymphony.workflow.designer.event.WorkspaceListener;
+import com.opensymphony.workflow.designer.event.WorkspaceEvent;
+import com.opensymphony.workflow.loader.Workspace;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 15, 2003
+ * Time: 7:57:28 PM
+ */
+public class WorkspaceManager
+{
+  private static final Log log = LogFactory.getLog(WorkspaceManager.class);
+
+  private Workspace currentWorkspace = null;
+  private Collection listeners;
+
+  public void loadWorkspace(File file) throws FactoryException, FileNotFoundException
+  {
+    fireWorkspaceClosed();
+    saveWorkspace();
+    log.debug("loading " + file);
+    ConfigLoader.load(new FileInputStream(file));
+    currentWorkspace  = (Workspace)ConfigLoader.getFactory();
+    fireWorkspaceOpened();
+    log.debug("loaded workspace " + currentWorkspace);
+  }
+
+  public void addWorkspaceListener(WorkspaceListener listener)
+  {
+    if(listeners==null)
+    {
+      listeners = new HashSet();
+    }
+    listeners.add(listener);
+  }
+
+  public void removeWorkspaceListener(WorkspaceListener listener)
+  {
+    if(listeners==null) return;
+    listeners.remove(listener);
+  }
+
+  protected void fireWorkspaceOpened()
+  {
+    if(listeners==null) return;
+    WorkspaceEvent event = new WorkspaceEvent(this, currentWorkspace, WorkspaceEvent.WORKSPACE_OPENED);
+    notifyListeners(event);
+  }
+
+  protected void fireWorkspaceClosed()
+  {
+    if(listeners==null) return;
+    WorkspaceEvent event = new WorkspaceEvent(this, currentWorkspace, WorkspaceEvent.WORKSPACE_CLOSED);
+    notifyListeners(event);
+  }
+
+  private void notifyListeners(WorkspaceEvent event)
+  {
+    Iterator iter = listeners.iterator();
+    while(iter.hasNext())
+    {
+      WorkspaceListener listener = (WorkspaceListener)iter.next();
+      listener.workspaceChanged(event);
+      if(event.isConsumed()) return;
+    }
+  }
+
+  public void setCurrentWorkspace(Workspace current)
+  {
+    saveWorkspace();
+    if(this.currentWorkspace!=null)
+      fireWorkspaceClosed();
+    currentWorkspace = current;
+    if(currentWorkspace!=null)
+      fireWorkspaceOpened();
+  }
+
+  public void saveWorkspace()
+  {
+    if(currentWorkspace!=null)
+    {
+      currentWorkspace.save();
+    }
+  }
+
+  public Workspace getCurrentWorkspace()
+  {
+    return currentWorkspace;
+  }
+}

src/designer/com/opensymphony/workflow/designer/Activity.java

+package com.opensymphony.workflow.designer;
+
+import java.io.PrintWriter;
+import java.awt.*;
+
+import org.w3c.dom.Element;
+
+import com.opensymphony.workflow.loader.XMLUtil;
+import com.opensymphony.workflow.util.XMLizable;
+
+public class Activity implements XMLizable
+{
+  String id = "";
+  private Rectangle bounds;
+
+  public Activity()
+  {
+
+  }
+
+  public Activity(WorkflowCell cell)
+  {
+    id = cell.toString();
+    bounds = (Rectangle)cell.getAttributes().get("bounds");
+  }
+
+  public Activity(Element activity)
+  {
+    id = activity.getAttribute("id");
+    bounds = new Rectangle();
+    bounds.height = Integer.parseInt(activity.getAttribute("height"));
+    bounds.width = Integer.parseInt(activity.getAttribute("width"));
+    bounds.x = Integer.parseInt(activity.getAttribute("x"));
+    bounds.y = Integer.parseInt(activity.getAttribute("y"));
+  }
+
+  /**
+   * Returns the id.
+   * @return String
+   */
+  public String getId()
+  {
+    return id;
+  }
+
+  /**
+   * Sets the id.
+   * @param id The id to set
+   */
+  public void setId(String id)
+  {
+    this.id = id;
+  }
+
+  public void writeXML(PrintWriter out, int indent)
+  {
+    XMLUtil.printIndent(out, indent++);
+    StringBuffer buf = new StringBuffer();
+    buf.append("<activity ");
+    buf.append("id=\"").append(id).append("\"");
+    buf.append(" height=\"").append(bounds.height).append("\"");
+    buf.append(" width=\"").append(bounds.width).append("\"");
+    buf.append(" x=\"").append(bounds.x).append("\"");
+    buf.append(" y=\"").append(bounds.y).append("\"");
+
+    buf.append("/>");
+    out.println(buf.toString());
+  }
+
+  public Rectangle getBounds()
+  {
+    return bounds;
+  }
+}

src/designer/com/opensymphony/workflow/designer/InitialActionCell.java

+package com.opensymphony.workflow.designer;
+
+import java.awt.*;
+
+import com.jgraph.graph.GraphConstants;
+
+public class InitialActionCell extends WorkflowCell
+{
+  public InitialActionCell(String userObject)
+  {
+    super(userObject);
+    GraphConstants.setBackground(attributes, Color.red.darker());
+   // GraphConstants.setBorder(attributes, BorderFactory.createEmptyBorder());
+  }
+
+  public String getKey()
+  {
+    String myClassName = InitialActionCell.class.toString();
+    return (myClassName);
+  }
+}

src/designer/com/opensymphony/workflow/designer/JoinCell.java

+/**
+ * Created on Feb 13, 2003
+ * Copyright (C) 2002  Aditisoft Inc
+ */
+package com.opensymphony.workflow.designer;
+
+import java.awt.*;
+
+import com.opensymphony.workflow.loader.JoinDescriptor;
+import com.jgraph.graph.GraphConstants;
+
+public class JoinCell extends WorkflowCell implements Keyable
+{
+  private JoinDescriptor descriptor;
+
+  // Construct Cell for Userobject
+  public JoinCell(JoinDescriptor userObject)
+  {
+    super("Join id " + userObject.getId());
+    descriptor = userObject;
+    id = descriptor.getId();
+    GraphConstants.setBackground(attributes, Color.gray);
+    // Set black border
+  }
+
+  public JoinCell(int id)
+  {
+    super("Join id " + id);
+    this.id = id;
+  }
+
+  public JoinDescriptor getJoinDescriptor()
+  {
+    return descriptor;
+  }
+
+  public String getKey()
+  {
+    String myClassName = JoinCell.class.toString();
+    return (myClassName + id);
+  }
+}

src/designer/com/opensymphony/workflow/designer/Keyable.java

+/**
+ * Created on Feb 12, 2003
+ * Copyright (C) 2002  Aditisoft Inc
+ */
+package com.opensymphony.workflow.designer;
+
+
+public interface Keyable {
+	public String getKey();
+}

src/designer/com/opensymphony/workflow/designer/Layout.java

+package com.opensymphony.workflow.designer;
+
+import java.awt.Rectangle;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.opensymphony.workflow.loader.XMLUtil;
+
+public class Layout
+{
+  private String url;
+
+  Map map = new HashMap();
+  Hashtable activities = new Hashtable();
+
+  public Layout(Map map)
+  {
+    this.map = map;
+  }
+
+  public void setActivity(Map map)
+  {
+    this.map = map;
+  }
+
+  /* public void addActivity(Map map) {
+
+  } */
+
+  public Layout()
+  {
+
+  }
+
+  public String getUrl()
+  {
+    return url;
+  }
+
+  public void setUrl(String url)
+  {
+    this.url = url;
+  }
+
+  public Layout(InputStream in)
+  {
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    dbf.setNamespaceAware(true);
+    DocumentBuilder db = null;
+    try
+    {
+      db = dbf.newDocumentBuilder();
+      Document doc;
+      doc = db.parse(in);
+
+      NodeList mActivitycell = doc.getElementsByTagName("activity");
+      for(int k = 0; k < mActivitycell.getLength(); k++)
+      {
+        Element cellAttr = (Element)mActivitycell.item(k);
+        Activity activityCell = new Activity(cellAttr);
+        Rectangle bound = activityCell.getBounds();
+        activities.put(activityCell.getId(), bound);
+      }
+    }
+    catch(Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  public void writeXML(PrintWriter out, int indent)
+  {
+    out.println("<?xml version=\"1.0\"?>");
+    XMLUtil.printIndent(out, indent++);
+    out.println("<layout>");
+
+    XMLUtil.printIndent(out, indent++);
+    Set set = map.keySet();
+    Iterator it = set.iterator();
+    while(it.hasNext())
+    {
+      String key = (String)it.next();
+      WorkflowCell cell = (WorkflowCell)map.get(key);
+      Activity activityCell = new Activity(cell);
+      activityCell.writeXML(out, indent);
+    }
+    XMLUtil.printIndent(out, --indent);
+    out.println("</layout>");
+    out.flush();
+    out.close();
+  }
+
+  public Rectangle getBounds(String key)
+  {
+    return (Rectangle)activities.get(key);
+  }
+}

src/designer/com/opensymphony/workflow/designer/Navigator.java

+package com.opensymphony.workflow.designer;
+
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.tree.*;
+import javax.swing.*;
+
+import com.opensymphony.workflow.loader.Workspace;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 15, 2003
+ * Time: 8:56:07 PM
+ */
+public class Navigator extends JTree implements TreeSelectionListener
+{
+  private WorkflowDesigner designer;
+  private DefaultMutableTreeNode rootNode;
+
+  public Navigator(WorkflowDesigner designer)
+  {
+    super(new DefaultTreeModel(new DefaultMutableTreeNode("<no workspace>")));
+    rootNode = (DefaultMutableTreeNode)getModel().getRoot();
+    this.designer = designer;
+    addTreeSelectionListener(this);
+    //tree.setEditable(true);
+    getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+    setShowsRootHandles(true);
+  }
+
+  public void selectWorkflow(String name)
+  {
+    Object root = getModel().getRoot();
+    int count = getModel().getChildCount(root);
+    for(int i=0;i<count;i++)
+    {
+      DefaultMutableTreeNode node = (DefaultMutableTreeNode)getModel().getChild(root, i);
+      if(node.getUserObject().equals(name))
+      {
+        TreePath path = new TreePath(new Object[]{root, node});
+        getSelectionModel().setSelectionPath(path);
+        expandPath(path);
+        designer.selectWorkflow(name);
+        return;
+      }
+    }
+  }
+
+  public void valueChanged(TreeSelectionEvent e)
+  {
+    DefaultMutableTreeNode node = (DefaultMutableTreeNode)getLastSelectedPathComponent();
+    if(node == null || node.equals(getModel().getRoot()))
+      return;
+
+    if(node.isLeaf())
+    {
+      String  workflowName = node.getUserObject().toString();
+      designer.selectWorkflow(workflowName);
+    }
+  }
+
+  public void setWorkspace(Workspace workspace)
+  {
+    rootNode.removeAllChildren();
+    ((DefaultTreeModel)getModel()).reload();
+    DefaultMutableTreeNode root = (DefaultMutableTreeNode)getModel().getRoot();
+    if(workspace != null)
+    {
+      String[] workflows = workspace.getWorkflowNames();
+      for(int i = 0; i < workflows.length; i++)
+      {
+        addWorkflow(workflows[i]);
+      }
+      root.setUserObject(workspace.getName());
+      expandRow(0);
+    }
+    else
+    {
+      root.setUserObject("<no workspace>");
+    }
+  }
+
+  public void addWorkflow(String name)
+  {
+    DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(name);
+    DefaultTreeModel model = (DefaultTreeModel)getModel();
+    DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot();
+    model.insertNodeInto(childNode, root, root.getChildCount());
+  }
+}

src/designer/com/opensymphony/workflow/designer/Prefs.java

+package com.opensymphony.workflow.designer;
+
+import java.util.prefs.Preferences;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 21, 2003
+ * Time: 12:15:05 AM
+ */
+public final class Prefs
+{
+  public static Preferences INSTANCE = Preferences.userNodeForPackage(WorkflowDesigner.class);
+  public static final String DESIGNER_BOUNDS = "designer.bounds";
+  public static final String CURRENT_DIR = "designer.dir";
+  public static final String MAIN_DIVIDER_LOCATION = "designer.maindivider.location";
+  public static final String DETAIL_DIVIDER_LOCATION = "designer.detaildivider.location";
+  public static final String LAST_WORKSPACE = "designer.last.workspace";
+  public static final String WORKFLOW_CURRENT = "designer.current.workflow";
+  public static final String OPEN_WORKFLOWS = "designer.open.workflows";
+}

src/designer/com/opensymphony/workflow/designer/ResultCellCollection.java

+/**
+ * Created on Feb 12, 2003
+ * Copyright (C) 2002  Aditisoft Inc
+ */
+package com.opensymphony.workflow.designer;
+
+import java.util.*;
+
+public class ResultCellCollection extends HashMap
+{
+
+  public String getNextKey()
+  {
+    return Integer.toString(size());
+  }
+
+  public List getStepEndPointResults(int stepId)
+  {
+    Iterator results = values().iterator();
+    Vector returnValue = new Vector();
+    while(results.hasNext())
+    {
+      ResultCell result = (ResultCell)results.next();
+      if(stepId == result.getStep())
+      {
+        returnValue.add(result);
+      }
+    }
+    return returnValue;
+  }
+
+  public List getSplitEndPointResults(int splitId)
+  {
+    Iterator results = values().iterator();
+    Vector returnValue = new Vector();
+    while(results.hasNext())
+    {
+      ResultCell result = (ResultCell)results.next();
+      if(splitId == result.getSplit())
+      {
+        returnValue.add(result);
+      }
+    }
+    return returnValue;
+  }
+
+  public List getJoinEndPointResults(int joinId)
+  {
+    Iterator results = values().iterator();
+    Vector returnValue = new Vector();
+    while(results.hasNext())
+    {
+      ResultCell result = (ResultCell)results.next();
+      if(joinId == result.getJoin())
+      {
+        returnValue.add(result);
+      }
+    }
+    return returnValue;
+  }
+
+}

src/designer/com/opensymphony/workflow/designer/ResultEdge.java

+package com.opensymphony.workflow.designer;
+
+import com.opensymphony.workflow.loader.ResultDescriptor;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 3:27:52 PM
+ */
+public class ResultEdge extends WorkflowEdge
+{
+  private ResultDescriptor descriptor;
+
+  public ResultDescriptor getDescriptor()
+  {
+    return descriptor;
+  }
+
+  public void setDescriptor(ResultDescriptor descriptor)
+  {
+    this.descriptor = descriptor;
+  }
+}

src/designer/com/opensymphony/workflow/designer/SplitCell.java

+package com.opensymphony.workflow.designer;
+
+import java.awt.*;
+
+import com.opensymphony.workflow.loader.SplitDescriptor;
+import com.jgraph.graph.GraphConstants;
+
+public class SplitCell extends WorkflowCell implements Keyable
+{
+  private SplitDescriptor descriptor;
+
+  // Construct Cell for Userobject
+  public SplitCell(SplitDescriptor userObject)
+  {
+    super("Split id " + userObject.getId());
+    descriptor = userObject;
+    id = descriptor.getId();
+    GraphConstants.setBackground(attributes, Color.gray);
+  }
+
+  public SplitCell(int id)
+  {
+    super("Split id " + id);
+    this.id = id;
+  }
+
+  public String getKey()
+  {
+    String myClassName = SplitCell.class.toString();
+    return (myClassName + id);
+  }
+
+  public SplitDescriptor getSplitDescriptor()
+  {
+    return descriptor;
+  }
+
+}
+
+

src/designer/com/opensymphony/workflow/designer/StepCell.java

+/**
+ * Created on Feb 3, 2003
+ * Copyright (C) 2002  Aditisoft Inc
+ */
+package com.opensymphony.workflow.designer;
+
+import com.opensymphony.workflow.loader.StepDescriptor;
+
+/**
+ * @author apatel
+ */
+public class StepCell extends WorkflowCell implements Keyable
+{
+  private StepDescriptor descriptor;
+
+  // Construct Cell for Userobject
+  public StepCell(StepDescriptor userObject)
+  {
+    super(userObject.getName());
+    descriptor = userObject;
+    id = descriptor.getId();
+    name = descriptor.getName();
+  }
+
+  public StepCell(int id, String name)
+  {
+    super(name);
+    this.id = id;
+    this.name = name;
+  }
+
+  public String getKey()
+  {
+    String myClassName = StepCell.class.toString();
+    return (myClassName + id);
+  }
+
+  public String toString()
+  {
+    return descriptor.getName() + " " + descriptor.getId();
+
+  }
+
+  public String getName()
+  {
+    return name;
+  }
+
+  public StepDescriptor getDescriptor()
+  {
+    return descriptor;
+  }
+
+  /**
+   * If Key values are equal the objects are equal
+   */
+  public boolean equals(Object obj)
+  {
+    boolean returnVal = false;
+    if(obj instanceof StepCell)
+    {
+      if(obj != null)
+      {
+        StepCell recObj = (StepCell)obj;
+        if(getKey().equals(recObj.getKey()))
+        {
+          returnVal = true;
+        }
+      }
+    }
+    return returnVal;
+  }
+
+}

src/designer/com/opensymphony/workflow/designer/WorkflowCell.java

+package com.opensymphony.workflow.designer;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+import com.jgraph.graph.DefaultGraphCell;
+import com.jgraph.graph.GraphConstants;
+
+/**
+ * @author Harsh Vijaywargiya Mar 9, 2003 1:04:57 PM
+ * Description
+ */
+public class WorkflowCell extends DefaultGraphCell
+{
+  public static final Rectangle defaultBounds = new Rectangle(10, 10, 100, 30);
+  protected int id;
+  protected String name;
+
+  /**
+   * Constructor for OSWfCell.
+   * @param arg0
+   */
+  public WorkflowCell(Object arg0)
+  {
+    super(arg0);
+    GraphConstants.setBounds(attributes, defaultBounds);
+    GraphConstants.setOpaque(attributes, true);
+    GraphConstants.setBackground(attributes, Color.blue.darker());
+    GraphConstants.setForeground(attributes, Color.white);
+    GraphConstants.setBorder(attributes, BorderFactory.createRaisedBevelBorder());
+    GraphConstants.setEditable(attributes, false);
+    //GraphConstants.setFont(attributes, GraphConstants.defaultFont.deriveFont(Font.BOLD, 12));
+  }
+
+  /**
+   * Returns the mId.
+   * @return int
+   */
+  public int getId()
+  {
+    return id;
+  }
+
+  /**
+   * Returns the mName.
+   * @return String
+   */
+  public String getName()
+  {
+    return name;
+  }
+}

src/designer/com/opensymphony/workflow/designer/WorkflowEdge.java

+package com.opensymphony.workflow.designer;
+
+import com.jgraph.graph.DefaultEdge;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 3:28:20 PM
+ */
+public class WorkflowEdge extends DefaultEdge
+{
+}

src/designer/com/opensymphony/workflow/designer/WorkflowGraph.java

+package com.opensymphony.workflow.designer;
+
+import java.util.*;
+import java.awt.Rectangle;
+
+import javax.swing.*;
+
+import com.jgraph.JGraph;
+import com.jgraph.graph.*;
+import com.opensymphony.workflow.loader.*;
+import com.opensymphony.workflow.designer.layout.SugiyamaLayoutAlgorithm;
+import com.opensymphony.workflow.designer.layout.LayoutAlgorithm;
+import com.opensymphony.workflow.designer.views.*;
+
+public class WorkflowGraph extends JGraph
+{
+  private Layout layout = new Layout();
+
+  private WorkflowDescriptor descriptor;
+
+  public WorkflowGraph(GraphModel model, WorkflowDescriptor descriptor, Layout layout, boolean doAutoLayout)
+  {
+    super(model);
+    ToolTipManager.sharedInstance().registerComponent(this);
+    this.layout = layout;
+    if(descriptor != null)
+    {
+      this.descriptor = descriptor;
+      addInitailAction();
+      addSteps();
+      addSplits();
+      addJoins();
+      getWorkflowGraphModel().insertResultConnections();
+    }
+    if(doAutoLayout)
+    {
+      autoLayout();
+    }
+  }
+
+  public void autoLayout()
+  {
+    if(descriptor.getSteps().size() > 0)
+    {
+      LayoutAlgorithm algo = new SugiyamaLayoutAlgorithm();
+      Properties p = new Properties();
+      p.put(SugiyamaLayoutAlgorithm.KEY_HORIZONTAL_SPACING, "110");
+      p.put(SugiyamaLayoutAlgorithm.KEY_VERTICAL_SPACING, "70");
+      algo.perform(this, true, p);
+    }
+  }
+
+  public void addInitailAction()
+  {
+    List initialActionList = descriptor.getInitialActions();
+    addInitialActionView(initialActionList);
+  }
+
+  private void addInitialActionView(List initialActionList)
+  {
+    InitialActionCell initialActionCell = new InitialActionCell("Start");
+    // Create Vertex Attributes
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(initialActionCell.toString());
+      if(bounds != null)
+      {
+        GraphConstants.setBounds(initialActionCell.getAttributes(), bounds);
+      }
+    }
+    getWorkflowGraphModel().insertInitialActions(initialActionList, initialActionCell, null, null, null);
+  }
+
+  public void addSteps()
+  {
+    List stepsList = descriptor.getSteps();
+    for(int i = 0; i < stepsList.size(); i++)
+    {
+      StepDescriptor step = (StepDescriptor)stepsList.get(i);
+      addStepView(step);
+    }
+  }
+
+  public void addSplits()
+  {
+    List splitsList = descriptor.getSplits();
+    for(int i = 0; i < splitsList.size(); i++)
+    {
+      SplitDescriptor split = (SplitDescriptor)splitsList.get(i);
+      addSplitView(split);
+
+    }
+  }
+
+  public void addJoins()
+  {
+    List joinsList = descriptor.getJoins();
+    for(int i = 0; i < joinsList.size(); i++)
+    {
+      JoinDescriptor join = (JoinDescriptor)joinsList.get(i);
+      addJoinView(join);
+
+    }
+  }
+
+  private void addJoinView(JoinDescriptor descriptor)
+  {
+    JoinCell join = new JoinCell(descriptor);
+    // Create Vertex Attributes
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(join.toString());
+      if(bounds != null)
+        join.getAttributes().put(GraphConstants.BOUNDS, bounds);
+    }
+    getWorkflowGraphModel().insertJoinCell(join, null, null, null);
+  }
+
+  private void addSplitView(SplitDescriptor descriptor)
+  {
+    SplitCell split = new SplitCell(descriptor);
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(split.toString());
+      if(bounds != null)
+        split.getAttributes().put(GraphConstants.BOUNDS, bounds);
+    }
+    getWorkflowGraphModel().insertSplitCell(split, null, null, null);
+  }
+
+  private void addStepView(StepDescriptor descriptor)
+  {
+    StepCell step = new StepCell(descriptor);
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(step.toString());
+      if(bounds != null)
+        step.getAttributes().put(GraphConstants.BOUNDS, bounds);
+    }
+    // Insert into Model
+    getWorkflowGraphModel().insertStepCell(step, null, null, null);
+  }
+
+  private WorkflowGraphModel getWorkflowGraphModel()
+  {
+    return (WorkflowGraphModel)getModel();
+  }
+
+  /**
+   * Overriding method as required by JGraph API,
+   * In order to return right View object corresponding to Cell.
+   */
+  protected VertexView createVertexView(Object v, CellMapper cm)
+  {
+    if(v instanceof StepCell)
+      return new StepView(v, this, cm);
+    if(v instanceof SplitCell)
+      return new SplitView(v, this, cm);
+    if(v instanceof JoinCell)
+      return new JoinView(v, this, cm);
+    if(v instanceof InitialActionCell)
+      return new InitialActionView(v, this, cm);
+    // Else Call Superclass
+    return super.createVertexView(v, cm);
+  }
+
+  public void addStepCell(StepCell step)
+  {
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(step.toString());
+      if(bounds != null)
+        step.getAttributes().put(GraphConstants.BOUNDS, bounds);
+    }
+    getWorkflowGraphModel().insertStep(step, null, null, null);
+  }
+
+  public void addSplitCell(SplitCell split)
+  {
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(split.toString());
+      if(bounds != null)
+        split.getAttributes().put(GraphConstants.BOUNDS, bounds);
+    }
+    getWorkflowGraphModel().insertSplit(split, null, null, null);
+  }
+
+  public void addJoinCell(JoinCell join)
+  {
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(join.toString());
+      if(bounds != null)
+        join.getAttributes().put(GraphConstants.BOUNDS, bounds);
+    }
+    getWorkflowGraphModel().insertJoin(join, null, null, null);
+  }
+
+  private void addInitialActionsCell(InitialActionCell initialActionCell)
+  {
+    if(layout != null)
+    {
+      Rectangle bounds = layout.getBounds(initialActionCell.toString());
+      if(bounds != null)
+      {
+        initialActionCell.getAttributes().put(GraphConstants.BOUNDS, bounds);
+      }
+    }
+    getWorkflowGraphModel().insertInitialActions(null, initialActionCell, null, null, null);
+  }
+
+}
+

src/designer/com/opensymphony/workflow/designer/actions/AutoLayout.java

+package com.opensymphony.workflow.designer.actions;
+
+import java.awt.event.ActionEvent;
+import javax.swing.*;
+
+import com.opensymphony.workflow.designer.WorkflowGraph;
+import com.opensymphony.workflow.designer.WorkflowDesigner;
+import com.opensymphony.workflow.designer.event.WorkspaceListener;
+import com.opensymphony.workflow.designer.event.WorkspaceEvent;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 8:25:28 PM
+ */
+public class AutoLayout extends AbstractAction implements WorkspaceListener
+{
+  private WorkflowGraph graph;
+
+  /**
+   * Create an auto layout action.
+   * This action calls an auto layout algorithm on the specified graph.
+   * @param graph the graph to autolayout. If the graph is null, then the currently
+   * showing graph has auto layout applied to it.
+   */
+  public AutoLayout(WorkflowGraph graph)
+  {
+    this.graph = graph;
+    putValue(LONG_DESCRIPTION, "Automatically layout the graph");
+    putValue(SHORT_DESCRIPTION, "Layout graph");
+    putValue(NAME, "Layout graph");
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    if(graph==null)
+    {
+      WorkflowGraph selected = WorkflowDesigner.INSTANCE.getCurrentGraph();
+      if(selected!=null)
+        selected.autoLayout();
+    }
+    else
+    {
+      graph.autoLayout();
+    }
+  }
+
+  public void workspaceChanged(WorkspaceEvent event)
+  {
+    if(event.getId()==WorkspaceEvent.WORKSPACE_OPENED)
+    {
+      setEnabled(true);
+    }
+    else
+    {
+      setEnabled(false);
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/actions/CloseWorkspace.java

+package com.opensymphony.workflow.designer.actions;
+
+import java.awt.event.ActionEvent;
+import javax.swing.*;
+
+import com.opensymphony.workflow.designer.WorkflowDesigner;
+import com.opensymphony.workflow.designer.event.WorkspaceListener;
+import com.opensymphony.workflow.designer.event.WorkspaceEvent;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 21, 2003
+ * Time: 12:21:03 AM
+ */
+public class CloseWorkspace extends AbstractAction implements WorkspaceListener
+{
+  public CloseWorkspace()
+  {
+    setEnabled(false);
+    putValue(SHORT_DESCRIPTION, "Close current workspace");
+    putValue(NAME, "Close workspace");
+    putValue(LONG_DESCRIPTION, "Close the currently open workspace");
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    WorkflowDesigner.INSTANCE.closeWorkspace();
+  }
+
+  public void workspaceChanged(WorkspaceEvent event)
+  {
+    if(event.getId()==WorkspaceEvent.WORKSPACE_OPENED)
+    {
+      setEnabled(true);
+    }
+    else
+    {
+      setEnabled(false);
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/actions/NewWorkflow.java

+package com.opensymphony.workflow.designer.actions;
+
+import java.awt.event.ActionEvent;
+import java.awt.*;
+import javax.swing.*;
+
+import com.opensymphony.workflow.designer.event.WorkspaceListener;
+import com.opensymphony.workflow.designer.event.WorkspaceEvent;
+import com.opensymphony.workflow.designer.WorkflowDesigner;
+import com.opensymphony.workflow.loader.Workspace;
+import com.opensymphony.workflow.FactoryException;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.Log;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 21, 2003
+ * Time: 1:14:41 AM
+ */
+public class NewWorkflow extends AbstractAction implements WorkspaceListener
+{
+  private static final Log log = LogFactory.getLog(NewWorkflow.class);
+
+  private Workspace currentWorkspace;
+
+  public NewWorkflow()
+  {
+    setEnabled(false);
+    putValue(SHORT_DESCRIPTION, "Create new workflow");
+    putValue(NAME, "New workflow");
+    putValue(LONG_DESCRIPTION, "Create a new workflow in the current workspace");
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    String name = JOptionPane.showInputDialog(this, "Please specify a new workflow name");
+    try
+    {
+      if(currentWorkspace.getWorkflow(name)!=null)
+      {
+        JOptionPane.showMessageDialog((Component)e.getSource(), "A workflow with the name " + name + " already exists.", "Error adding workflow", JOptionPane.ERROR_MESSAGE);
+        return;
+      }
+    }
+    catch(FactoryException ex)
+    {
+      log.error("Error creating definition:" + ex.toString());
+      return;
+    }
+    currentWorkspace.createWorkflow(name);
+    WorkflowDesigner.INSTANCE.newWorkflowCreated(name);
+  }
+
+  public void workspaceChanged(WorkspaceEvent event)
+  {
+    if(event.getId()==WorkspaceEvent.WORKSPACE_OPENED)
+    {
+      setEnabled(true);
+      currentWorkspace = event.getWorkspace();
+    }
+    else
+    {
+      setEnabled(false);
+      currentWorkspace = null;
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/actions/NewWorkspace.java

+package com.opensymphony.workflow.designer.actions;
+
+import java.awt.event.ActionEvent;
+import javax.swing.*;
+
+import com.opensymphony.workflow.designer.WorkflowDesigner;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 21, 2003
+ * Time: 1:14:32 AM
+ */
+public class NewWorkspace extends AbstractAction
+{
+  public NewWorkspace()
+  {
+    putValue(SHORT_DESCRIPTION, "Create new workspace");
+    putValue(NAME, "New workspace");
+    putValue(LONG_DESCRIPTION, "Create a new workspace");
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    WorkflowDesigner.INSTANCE.newWorkspace();
+  }
+}

src/designer/com/opensymphony/workflow/designer/actions/OpenWorkspace.java

+package com.opensymphony.workflow.designer.actions;
+
+import java.awt.event.ActionEvent;
+import java.awt.*;
+import java.io.File;
+import javax.swing.*;
+
+import com.opensymphony.workflow.designer.Utils;
+import com.opensymphony.workflow.designer.WorkflowDesigner;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 21, 2003
+ * Time: 12:09:41 AM
+ */
+public class OpenWorkspace extends AbstractAction
+{
+  public OpenWorkspace()
+  {
+    putValue(SHORT_DESCRIPTION, "Open a workspace");
+    putValue(NAME, "Open workspace");
+    putValue(LONG_DESCRIPTION, "Load a workflow workspace");
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    File file = Utils.promptUserForFile((Component)e.getSource(), JFileChooser.FILES_AND_DIRECTORIES, false, WorkflowDesigner.WORKSPACE_SUFFIX, "Workspace Files");
+    WorkflowDesigner.INSTANCE.openWorkspace(file);
+  }
+}

src/designer/com/opensymphony/workflow/designer/actions/Quit.java

+package com.opensymphony.workflow.designer.actions;
+
+import java.awt.event.ActionEvent;
+import javax.swing.*;
+
+import com.opensymphony.workflow.designer.WorkflowDesigner;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 21, 2003
+ * Time: 1:11:43 AM
+ */
+public class Quit extends AbstractAction
+{
+  public Quit()
+  {
+    putValue(SHORT_DESCRIPTION, "Exit designer");
+    putValue(NAME, "Exit");
+    putValue(LONG_DESCRIPTION, "Exit the designer application");
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    WorkflowDesigner.INSTANCE.quit();
+  }
+}

src/designer/com/opensymphony/workflow/designer/actions/SaveWorkspace.java

+package com.opensymphony.workflow.designer.actions;
+
+import java.awt.event.ActionEvent;
+import java.awt.*;
+import java.io.File;
+import javax.swing.*;
+
+import com.opensymphony.workflow.designer.event.WorkspaceListener;
+import com.opensymphony.workflow.designer.event.WorkspaceEvent;
+import com.opensymphony.workflow.designer.Utils;
+import com.opensymphony.workflow.designer.Prefs;
+import com.opensymphony.workflow.designer.WorkflowDesigner;
+import com.opensymphony.workflow.loader.Workspace;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 21, 2003
+ * Time: 1:02:27 AM
+ */
+public class SaveWorkspace extends AbstractAction implements WorkspaceListener
+{
+  private Workspace currentWorkspace;
+
+  public SaveWorkspace()
+  {
+    setEnabled(false);
+    putValue(SHORT_DESCRIPTION, "Save current workspace");
+    putValue(NAME, "Save workspace");
+    putValue(LONG_DESCRIPTION, "Save the currently loaded workspace");    
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    if(currentWorkspace.getLocation()==null)
+    {
+      File toSave = Utils.promptUserForFile((Component)e.getSource(), JFileChooser.FILES_AND_DIRECTORIES, true, WorkflowDesigner.WORKSPACE_SUFFIX, "Workspace Files");
+      if(toSave!=null)
+      {
+        currentWorkspace.setLocation(toSave);
+        Prefs.INSTANCE.put(Prefs.LAST_WORKSPACE, toSave.toString());
+        WorkflowDesigner.INSTANCE.navigator().setWorkspace(currentWorkspace);
+      }
+    }
+    WorkflowDesigner.INSTANCE.saveWorkspace();
+    WorkflowDesigner.INSTANCE.saveOpenGraphs();
+  }
+
+  public void workspaceChanged(WorkspaceEvent event)
+  {
+    if(event.getId()==WorkspaceEvent.WORKSPACE_OPENED)
+    {
+      setEnabled(true);
+      currentWorkspace = event.getWorkspace();
+    }
+    else
+    {
+      setEnabled(false);
+      currentWorkspace = null;
+    }
+
+  }
+}

src/designer/com/opensymphony/workflow/designer/event/WorkspaceEvent.java

+package com.opensymphony.workflow.designer.event;
+
+import java.util.EventObject;
+
+import com.opensymphony.workflow.loader.Workspace;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 11:52:02 PM
+ */
+public class WorkspaceEvent extends EventObject
+{
+  public static final int WORKSPACE_OPENED = 1;  public static final int WORKSPACE_CLOSED = 2;
+  private int id;
+  private Workspace workspace;
+  private boolean consumed;
+
+  public WorkspaceEvent(Object source)
+  {
+    super(source);
+  }
+
+  public WorkspaceEvent(Object source, Workspace workspace, int id)
+  {
+    super(source);
+    this.id = id;
+    this.workspace = workspace;
+  }
+
+  public int getId()
+  {
+    return id;
+  }
+
+  public Workspace getWorkspace()
+  {
+    return workspace;
+  }
+
+  public boolean isConsumed()
+  {
+    return consumed;
+  }
+
+  public void consume()
+  {
+    consumed = true;
+  }
+
+  public String toString()
+  {
+    StringBuffer sb = new StringBuffer(getClass().getName());
+    sb.append("[");
+    sb.append("id=");
+    sb.append(id==WORKSPACE_OPENED ? "WORKSPACE_OPENED " : "WORKSPACE_CLOSED ");
+    sb.append("source=").append(source);
+    sb.append("]");
+    return sb.toString();
+  }
+}

src/designer/com/opensymphony/workflow/designer/event/WorkspaceListener.java

+package com.opensymphony.workflow.designer.event;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 11:57:44 PM
+ */
+public interface WorkspaceListener
+{
+  public void workspaceChanged(WorkspaceEvent event);
+}

src/designer/com/opensymphony/workflow/designer/layout/LayoutAlgorithm.java

+/*
+ * @(#)LayoutAlgorithm.java	1.0 01/20/03
+ *
+ * Copyright (C) 2003 Sven Luzar
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.opensymphony.workflow.designer.layout;
+
+import com.jgraph.JGraph;
+
+import java.util.Properties;
+
+/**
+ * Represents an Algorithm that is applied to a graph.<br>
+ * It is supposed to arrange the nodes in some usefull way.<br>
+ *<br>
+ *<br>
+ * @author <a href="mailto:Sven.Luzar@web.de">Sven Luzar</a>
+ * @since 1.2.2
+ * @version 1.0 init
+ */
+public interface LayoutAlgorithm
+{
+
+  /**
+   * Called when the Algorithm shall start its work.
+   */
+  public abstract void perform(JGraph jgraph, boolean applyToAll, Properties configuration);
+
+}

src/designer/com/opensymphony/workflow/designer/layout/SugiyamaLayoutAlgorithm.java

+// This file is part of the Echidna project
+// (C) 2002 Forschungszentrum Informatik (FZI) Karlsruhe
+// Please visit our website at http://echidna.sf.net
+package com.opensymphony.workflow.designer.layout;
+
+import javax.swing.*;
+
+import com.jgraph.JGraph;
+import com.jgraph.graph.*;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.text.NumberFormat;
+import java.util.*;
+
+/**
+ * Arranges the nodes with the Sugiyama Layout Algorithm.<br>
+ *
+ * <a href="http://plg.uwaterloo.ca/~itbowman/CS746G/Notes/Sugiyama1981_MVU/">
+ *  Link to the algorithm</a>
+ *
+ *<br>
+ *<br>
+ * @author Sven Luzar<br>
+ * @version 1.0 init
+ */
+public class SugiyamaLayoutAlgorithm implements LayoutAlgorithm
+{
+
+  /** Field for debug output
+   */
+  protected final boolean verbose = false;
+
+  /** Const to add Attributes at the Nodes
+   *
+   */
+  public static final String SUGIYAMA_VISITED = "SugiyamaVisited"/*#Frozen*/;
+
+  /** Const to add the Cell Wrapper to the Nodes
+   */
+  public static final String SUGIYAMA_CELL_WRAPPER = "SugiyamaCellWrapper"/*#Frozen*/;
+
+  /** represents the size of the grid in horizontal grid elements
+   *
+   */
+  protected int gridAreaSize = Integer.MIN_VALUE;
+
+  /** A List with Integer Objects. The List contains the
+   *  history of movements per loop
+   *  It was needed for the progress dialog
+   */
+  List movements = null;
+  /** Represents the movements in the current loop.
+   *  It was needed for the progress dialog
+   */
+  int movementsCurrentLoop = -1;
+  /** Represents the maximum of movements in the current loop.
+   *  It was needed for the progress dialog
+   */
+  int movementsMax = Integer.MIN_VALUE;
+  /** Represents the current loop number
+   *  It was needed for the progress dialog
+   */
+  int iteration = 0;
+  public static final String KEY_HORIZONTAL_SPACING = "HorizontalSpacing";
+  public static final String KEY_VERTICAL_SPACING = "VerticalSpacing";
+
+  /**
+   * Implementation.
+   *
+   * First of all the Algorithm searches the roots from the
+   * Graph. Starting from this roots the Algorithm creates
+   * levels and stores them in the member <code>levels</code>.
+   * The Member levels contains List Objects and the List per level
+   * contains Cell Wrapper Objects. After that the Algorithm
+   * tries to solve the edge crosses from level to level and
+   * goes top down and bottom up. After minimization of the
+   * edge crosses the algorithm moves each node to its
+   * bary center. Last but not Least the method draws the Graph.
+   *
+   * @see LayoutAlgorithm
+   *
+   */
+  public void perform(JGraph jgraph, boolean applyToAll, Properties configuration)
+  {
+
+    Object[] selectedCells = (applyToAll ? jgraph.getRoots() : jgraph.getSelectionCells());
+    CellView[] selectedCellViews = jgraph.getGraphLayoutCache().getMapping(selectedCells);
+
+    Point spacing = new Point();
+    /*  The Algorithm distributes the nodes on a grid.
+     *  For this grid you can configure the horizontal spacing.
+     *  This field specifies the configured value
+     *
+     */
+    spacing.x = Integer.parseInt(configuration.getProperty(KEY_HORIZONTAL_SPACING));
+
+    /*  The Algorithm distributes the nodes on a grid.
+     *  For this grid you can configure the vertical spacing.
+     *  This field specifies the configured value
+     *
+     */
+
+    spacing.y = Integer.parseInt(configuration.getProperty(KEY_VERTICAL_SPACING));
+
+    // search all roots
+    List roots = searchRoots(jgraph, selectedCellViews);
+
+    // return if no root found
+    if(roots.size() == 0)
+      return;
+
+    // create levels
+    List levels = fillLevels(jgraph, selectedCellViews, roots);
+
+    // solves the edge crosses
+    solveEdgeCrosses(jgraph, levels);
+
+    // move all nodes into the barycenter
+    moveToBarycenter(jgraph, selectedCellViews, levels);
+
+    Point min = findMinimumAndSpacing(selectedCellViews, spacing);
+
+    // draw the graph in the window
+    drawGraph(jgraph, levels, min, spacing);
+
+    // clean temp values from the nodes / cells
+    // the clean up was made in drawGraph
+    //cleanUp(selectedCellViews);
+
+  }
+
+  /** Debugdisplay for the edge crosses indicators on the System out
+   */
+  protected void displayEdgeCrossesValues(List levels)
+  {
+    System.out.println("----------------Edge Crosses Indicator Values"/*#Frozen*/);
+
+    for(int i = 0; i < levels.size() - 1; i++)
+    {
+      // Get the current level
+      List currentLevel = (List)levels.get(i);
+      System.out.print("Level (" + i + "):"/*#Frozen*/);
+      for(int j = 0; j < currentLevel.size(); j++)
+      {
+        CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(j);
+
+        System.out.print(NumberFormat.getNumberInstance().format(sourceWrapper.getEdgeCrossesIndicator()) + " - "/*#Frozen*/);
+      }
+      System.out.println();
+    }
+  }
+
+  /** Debugdisplay for the grid positions on the System out
+   */
+  protected void displayGridPositions(List levels)
+  {
+
+    System.out.println("----------------GridPositions"/*#Frozen*/);
+
+    for(int i = 0; i < levels.size() - 1; i++)
+    {
+      // Get the current level
+      List currentLevel = (List)levels.get(i);
+      System.out.print("Level (" + i + "):"/*#Frozen*/);
+      for(int j = 0; j < currentLevel.size(); j++)
+      {
+        CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(j);
+        System.out.print(NumberFormat.getNumberInstance().format(sourceWrapper.getGridPosition()) + " - "/*#Frozen*/);
+      }
+      System.out.println();
+    }
+  }
+
+  /** Debugdisplay for the priorities on the System out
+   */
+  protected void displayPriorities(List levels)
+  {
+
+    System.out.println("----------------down Priorities"/*#Frozen*/);
+
+    for(int i = 0; i < levels.size() - 1; i++)
+    {
+      // Get the current level
+      List currentLevel = (List)levels.get(i);
+      System.out.print("Level (" + i + "):"/*#Frozen*/);
+      for(int j = 0; j < currentLevel.size(); j++)
+      {
+        CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(j);
+        System.out.print(sourceWrapper.getPriority() + /*" (" +
+                           sourceWrapper.nearestDownNeighborLevel + ") " +*/
+                         " - "/*#Frozen*/);
+      }
+      System.out.println();
+    }
+  }
+
+  /** Searches all Roots for the current Graph
+   *  First the method marks any Node as not visited.
+   *  Than calls searchRoots(MyGraphCell) for each
+   *  not visited Cell.
+   *  The Roots are stored in the List named roots
+   *
+   * 	@return returns a List with the roots
+   *  @see #searchRoots(JGraph, CellView[])
+   */
+  protected List searchRoots(JGraph jgraph, CellView[] selectedCellViews)
+  {
+
+    // get all cells and relations
+    List vertexViews = new ArrayList(selectedCellViews.length);
+    List roots = new ArrayList();
+
+    // first: mark all as not visited
+    // O(allCells&Edges)
+    for(int i = 0; i < selectedCellViews.length; i++)
+    {
+      if(selectedCellViews[i] instanceof VertexView)
+      {
+        VertexView vertexView = (VertexView)selectedCellViews[i];
+        vertexView.getAttributes().remove(SUGIYAMA_VISITED);
+        vertexViews.add(selectedCellViews[i]);
+      }
+    }
+
+    // O(graphCells)
+    for(int i = 0; i < vertexViews.size(); i++)
+    {
+      VertexView vertexView = (VertexView)vertexViews.get(i);
+      if(vertexView.getAttributes().get(SUGIYAMA_VISITED) == null)
+      {
+        searchRoots(jgraph, vertexView, roots);
+      }
+    }
+
+    // Error Msg if the graph has no roots
+    if(roots.size() == 0)
+    {
+      JOptionPane.showMessageDialog(null, "The Graph is not a DAG. Can't use Sugiyama Algorithm!"/*#Finished:Original="The Graph is not a DAG. Can't use Sugiyama Algorithm!"*/, null, JOptionPane.ERROR_MESSAGE);
+    }
+    return roots;
+  }
+
+  /** Searches Roots for the current Cell.
+   *
+   *  Therefore he looks at all Ports from the Cell.
+   *  At the Ports he looks for Edges.
+   *  At the Edges he looks for the Target.
+   *  If the Ports of the current Cell contains the target ReViewNodePort
+   *  he follows the edge to the source and looks at the
+   *  Cell for this source.
+   *
+   */
+  protected void searchRoots(JGraph jgraph, VertexView vertexViewToInspect, List roots)
+  {
+    // the node already visited
+    if(vertexViewToInspect.getAttributes().get(SUGIYAMA_VISITED) != null)
+    {
+      return;
+    }
+
+    // mark as visited for cycle tests
+    vertexViewToInspect.getAttributes().put(SUGIYAMA_VISITED, new Boolean(true));
+
+    GraphModel model = jgraph.getModel();
+
+    // get all Ports and search the relations at the ports
+    //List vertexPortViewList = new ArrayList() ;
+
+    Object vertex = vertexViewToInspect.getCell();
+
+    int portCount = model.getChildCount(vertex);
+    for(int j = 0; j < portCount; j++)
+    {
+      Object port = model.getChild(vertex, j);
+
+      // Test all relations for where
+      // the current node is a target node
+      // for roots
+
+      boolean isRoot = true;
+      Iterator itrEdges = model.edges(port);
+      while(itrEdges.hasNext())
+      {