Commits

Anonymous committed 2aedf56

Initial import from SourceForge

Comments (0)

Files changed (19)

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

+/**
+ * Created on Feb 12, 2003
+ * Copyright (C) 2002  Aditisoft Inc
+ */
+package com.opensymphony.workflow.designer;
+
+import com.jgraph.graph.DefaultGraphCell;
+import com.opensymphony.workflow.loader.ResultDescriptor;
+
+public class ResultCell extends WorkflowCell implements Keyable
+{
+  private DefaultGraphCell fromCell;
+  private ResultDescriptor descriptor;
+
+  //   private String mKey;
+  public ResultCell(DefaultGraphCell fromCell, ResultDescriptor resultDescriptor)
+  {
+    super(new Integer(resultDescriptor.getId()));
+    this.fromCell = fromCell;
+    descriptor = resultDescriptor;
+    id = descriptor.getId();
+  }
+
+  public String getKey()
+  {
+    return null;
+  }
+
+  public DefaultGraphCell getFromCell()
+  {
+    return fromCell;
+  }
+
+  public int getStep()
+  {
+    return descriptor.getStep();
+  }
+
+  public ResultDescriptor getDescriptor()
+  {
+    return descriptor;
+  }
+
+  public int getSplit()
+  {
+    return descriptor.getSplit();
+  }
+
+  public int getJoin()
+  {
+    return descriptor.getJoin();
+  }
+}

src/designer/com/opensymphony/workflow/designer/editor/ResultEditor.java

+package com.opensymphony.workflow.designer.editor;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import javax.swing.*;
+
+import com.jgoodies.forms.layout.FormLayout;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.builder.PanelBuilder;
+import com.opensymphony.workflow.designer.model.FunctionsTableModel;
+import com.opensymphony.workflow.designer.ResultEdge;
+import com.opensymphony.workflow.designer.UIFactory;
+import com.opensymphony.workflow.loader.ResultDescriptor;
+
+public class ResultEditor extends DetailPanel implements ActionListener
+{
+  private JTextField id = new JTextField(12);
+  private JTextField owner = new JTextField(12);
+  private JTextField status = new JTextField(12);
+  private JTextField oldStatus = new JTextField(12);
+  private FunctionsTableModel preFunctionsModel = new FunctionsTableModel();
+  private FunctionsTableModel postFunctionsModel = new FunctionsTableModel();
+  private JTable preFunctionsTable;
+  private JTable postFunctionsTable;
+
+  public ResultEditor()
+  {
+  }
+
+  protected void initComponents()
+  {
+    FormLayout layout = new FormLayout("2dlu, max(30dlu;pref), 2dlu, pref:grow, 4dlu", "pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 4dlu, " + //next is 11 - actions separator
+                                                                                       "pref, 2dlu, 50dlu, pref, 2dlu, pref, 2dlu, 50dlu, pref, 2dlu");
+    PanelBuilder builder = new PanelBuilder(this, layout);
+    CellConstraints cc = new CellConstraints();
+    builder.addSeparator("Info", cc.xywh(2, 1, 3, 1));
+    builder.addLabel("ID", cc.xy(2, 3));
+    builder.add(id, cc.xy(4, 3));
+    builder.addLabel("Owner", cc.xy(2, 5));
+    builder.add(owner, cc.xy(4, 5));
+    builder.addLabel("Status", cc.xy(2, 7));
+    builder.add(status, cc.xy(4, 7));
+    builder.addLabel("Old Status", cc.xy(2, 9));
+    builder.add(oldStatus, cc.xy(4, 9));
+
+    builder.addSeparator("Pre-Functions", cc.xywh(2, 11, 3, 1));
+    preFunctionsTable = new JTable(preFunctionsModel);
+    builder.add(new JScrollPane(preFunctionsTable), cc.xywh(2, 13, 3, 1));
+    builder.add(UIFactory.getTableButtonBar(this, "pre"), cc.xywh(2, 14, 3, 1));
+
+    builder.addSeparator("Post-Functions", cc.xywh(2, 16, 3, 1));
+    postFunctionsTable = new JTable(postFunctionsModel);
+    builder.add(new JScrollPane(postFunctionsTable), cc.xywh(2, 18, 3, 1));
+    builder.add(UIFactory.getTableButtonBar(this, "post"), cc.xywh(2, 19, 3, 1));
+  }
+
+  public String getTitle()
+  {
+    return "Result" + (id.getText() != null && id.getText().length()>0 ? (" #" + id.getText()) : "");
+  }
+
+  protected void updateView()
+  {
+    ResultEdge result = (ResultEdge)getEdge();
+    ResultDescriptor descriptor = result.getDescriptor();
+    preFunctionsModel.setList(descriptor.getPreFunctions());
+    postFunctionsModel.setList(descriptor.getPostFunctions());
+    id.setText(descriptor.hasId() ? Integer.toString(descriptor.getId()) : "");
+    owner.setText(descriptor.getOwner()!=null ? descriptor.getOwner() : "");
+    status.setText(descriptor.getStatus()!=null ? descriptor.getStatus() : "");
+    oldStatus.setText(descriptor.getOldStatus()!=null ? descriptor.getOldStatus() : "");
+  }
+
+  public void actionPerformed(ActionEvent e)
+  {
+    String command = e.getActionCommand();
+    if("preadd".equals(command))
+    {
+
+    }
+    else if("preremove".equals(command))
+    {
+      int[] selected = preFunctionsTable.getSelectedRows();
+      for(int i=0;i<selected.length;i++)
+      {
+        preFunctionsModel.remove(selected[i]);
+      }
+    }
+    else if("preproperties".equals(command))
+    {
+
+    }
+    else if("postadd".equals(command))
+    {
+
+    }
+    else if("postremove".equals(command))
+    {
+      int[] selected = postFunctionsTable.getSelectedRows();
+      for(int i=0;i<selected.length;i++)
+      {
+        postFunctionsModel.remove(selected[i]);
+      }
+    }
+    else if("postproperties".equals(command))
+    {
+
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/model/ConditionsTableModel.java

+package com.opensymphony.workflow.designer.model;
+
+import com.opensymphony.workflow.loader.ConditionDescriptor;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 11:04:16 AM
+ */
+public class ConditionsTableModel extends ListTableModel
+{
+  private String[] header = new String[]{"id", "type", "negate"};
+
+  public boolean isCellEditable(int rowIndex, int columnIndex)
+  {
+    return true;
+  }
+
+  public void setValueAt(Object aValue, int rowIndex, int columnIndex)
+  {
+    ConditionDescriptor condition = (ConditionDescriptor)list.get(rowIndex);
+    switch(columnIndex)
+    {
+      case 0:
+        if(aValue!=null)
+          condition.setId(((Integer)aValue).intValue());
+        break;
+      case 1:
+        if(aValue!=null)
+          condition.setType(aValue.toString());
+        break;
+      case 2:
+        condition.setNegate(((Boolean)aValue).booleanValue());
+        break;
+    }
+  }
+
+  public int getColumnCount()
+  {
+    return header.length;
+  }
+
+  public String getColumnName(int column)
+  {
+    return header[column];
+  }
+
+  public Class getColumnClass(int columnIndex)
+  {
+    switch(columnIndex)
+    {
+      case 0:
+        return Integer.class;
+      case 1:
+        return String.class;
+      case 2:
+        return Boolean.class;
+      default:
+        return String.class;
+    }
+  }
+
+  public Object getValueAt(int rowIndex, int columnIndex)
+  {
+    ConditionDescriptor condition = (ConditionDescriptor)list.get(rowIndex);
+    switch(columnIndex)
+    {
+      case 0:
+        return condition.hasId() ? new Integer(condition.getId()) : null;
+      case 1:
+        return condition.getType();
+      default:
+        return condition.isNegate() ? Boolean.TRUE : Boolean.FALSE;
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/model/FunctionsTableModel.java

+package com.opensymphony.workflow.designer.model;
+
+import com.opensymphony.workflow.loader.FunctionDescriptor;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 11:04:16 AM
+ */
+public class FunctionsTableModel extends ListTableModel
+{
+  private String[] header = new String[]{"id", "type"};
+
+  public boolean isCellEditable(int rowIndex, int columnIndex)
+  {
+    return true;
+  }
+
+  public void setValueAt(Object aValue, int rowIndex, int columnIndex)
+  {
+    FunctionDescriptor function = (FunctionDescriptor)list.get(rowIndex);
+    switch(columnIndex)
+    {
+      case 0:
+        if(aValue!=null)
+          function.setId(((Integer)aValue).intValue());
+        break;
+      case 1:
+        if(aValue!=null)
+          function.setType(aValue.toString());
+        break;
+    }
+  }
+
+  public int getColumnCount()
+  {
+    return header.length;
+  }
+
+  public String getColumnName(int column)
+  {
+    return header[column];
+  }
+
+  public Class getColumnClass(int columnIndex)
+  {
+    switch(columnIndex)
+    {
+      case 0:
+        return Integer.class;
+      case 1:
+        return String.class;
+      default:
+        return String.class;
+    }
+  }
+
+  public Object getValueAt(int rowIndex, int columnIndex)
+  {
+    FunctionDescriptor function = (FunctionDescriptor)list.get(rowIndex);
+    switch(columnIndex)
+    {
+      case 0:
+        return function.hasId() ? new Integer(function.getId()) : null;
+      case 1:
+        return function.getType();
+      default:
+        return "";
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/model/PermissionsTableModel.java

+package com.opensymphony.workflow.designer.model;
+
+import com.opensymphony.workflow.loader.PermissionDescriptor;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 11:04:16 AM
+ */
+public class PermissionsTableModel extends ListTableModel
+{
+  private String[] header = new String[]{"id", "name"};
+
+  public PermissionsTableModel()
+  {
+  }
+
+  public boolean isCellEditable(int rowIndex, int columnIndex)
+  {
+    return true;
+  }
+
+  public void setValueAt(Object aValue, int rowIndex, int columnIndex)
+  {
+    PermissionDescriptor permission = (PermissionDescriptor)list.get(rowIndex);
+    switch(columnIndex)
+    {
+      case 0:
+        if(aValue!=null)
+          permission.setId(((Integer)aValue).intValue());
+        break;
+      case 1:
+        permission.setName(aValue!=null ? aValue.toString() : null);
+    }
+  }
+
+  public int getColumnCount()
+  {
+    return header.length;
+  }
+
+  public String getColumnName(int column)
+  {
+    return header[column];
+  }
+
+  public Class getColumnClass(int columnIndex)
+  {
+    switch(columnIndex)
+    {
+      case 0:
+        return Integer.class;
+      case 1:
+        return String.class;
+      default:
+        return String.class;
+    }
+  }
+
+  public Object getValueAt(int rowIndex, int columnIndex)
+  {
+    PermissionDescriptor permission = (PermissionDescriptor)list.get(rowIndex);
+    switch(columnIndex)
+    {
+      case 0:
+        return new Integer(permission.getId());
+      case 1:
+        return permission.getName();
+      default:
+        return "";
+    }
+  }
+}

src/designer/com/opensymphony/workflow/designer/model/ResultsTableModel.java

+package com.opensymphony.workflow.designer.model;
+
+import com.opensymphony.workflow.loader.ResultDescriptor;
+
+/**
+ * @author Hani Suleiman (hani@formicary.net)
+ * Date: May 20, 2003
+ * Time: 11:37:35 AM
+ */
+public class ResultsTableModel extends ListTableModel
+{
+  private String[] header = new String[]{"id", "owner", "status", "old status","step"};
+
+  public int getColumnCount()
+  {
+    return header.length;
+  }
+
+  public String getColumnName(int column)
+  {
+    return header[column];
+  }
+
+  public Class getColumnClass(int columnIndex)
+  {
+    switch(columnIndex)
+    {
+      case 0:
+      case 4:
+        return Integer.class;
+      case 1:
+      case 2:
+      case 3:
+        return String.class;
+      default:
+        return String.class;
+    }
+  }
+
+  public Object getValueAt(int rowIndex, int columnIndex)
+  {
+    ResultDescriptor result = (ResultDescriptor)list.get(rowIndex);
+    switch(columnIndex)
+    {
+      case 0:
+        return result.hasId() ? new Integer(result.getId()) : null;
+      case 1:
+        return result.getOwner()!=null ? result.getOwner() : "";
+      case 2:
+        return result.getStatus()!=null ? result.getStatus() : "";
+      case 3:
+        return result.getOldStatus()!=null ? result.getOldStatus() : "";
+      case 4:
+        return new Integer(result.getStep());
+      default:
+        return "";
+    }
+  }
+}

src/java/com/opensymphony/workflow/loader/ActionDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
+import com.opensymphony.workflow.util.Validatable;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ActionDescriptor extends AbstractDescriptor implements Validatable {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected List conditionalResults = new ArrayList();
+    protected List postFunctions = new ArrayList();
+    protected List preFunctions = new ArrayList();
+    protected List validators = new ArrayList();
+    protected RestrictionDescriptor restriction;
+    protected ResultDescriptor unconditionalResult;
+    protected String name;
+    protected String view;
+    protected boolean autoExecute = false;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public ActionDescriptor() {
+    }
+
+    public ActionDescriptor(Element action) {
+        init(action);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setAutoExecute(boolean autoExecute) {
+        this.autoExecute = autoExecute;
+    }
+
+    public boolean getAutoExecute() {
+        return autoExecute;
+    }
+
+    public List getConditionalResults() {
+        return conditionalResults;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List getPostFunctions() {
+        return postFunctions;
+    }
+
+    public List getPreFunctions() {
+        return preFunctions;
+    }
+
+    public void setRestriction(RestrictionDescriptor restriction) {
+        this.restriction = restriction;
+    }
+
+    public RestrictionDescriptor getRestriction() {
+        return restriction;
+    }
+
+    public void setUnconditionalResult(ResultDescriptor unconditionalResult) {
+        this.unconditionalResult = unconditionalResult;
+    }
+
+    public ResultDescriptor getUnconditionalResult() {
+        return unconditionalResult;
+    }
+
+    public List getValidators() {
+        return validators;
+    }
+
+    public void setView(String view) {
+        this.view = view;
+    }
+
+    public String getView() {
+        return view;
+    }
+
+    public void validate() throws InvalidWorkflowDescriptorException {
+        ValidationHelper.validate(preFunctions);
+        ValidationHelper.validate(postFunctions);
+        ValidationHelper.validate(validators);
+        ValidationHelper.validate(conditionalResults);
+
+        if (restriction != null) {
+            restriction.validate();
+        }
+
+        if (unconditionalResult != null) {
+            unconditionalResult.validate();
+        }
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+
+        StringBuffer buf = new StringBuffer("<action id=\"");
+        buf.append(getId());
+        buf.append("\"");
+
+        if ((name != null) && (name.length() > 0)) {
+            buf.append(" name=\"");
+            buf.append(name);
+            buf.append("\"");
+        }
+
+        if ((view != null) && (view.length() > 0)) {
+            buf.append(" view=\"");
+            buf.append(view);
+            buf.append("\"");
+        }
+
+        if (autoExecute) {
+            buf.append(" auto=\"true\"");
+        }
+
+        buf.append(">");
+        out.println(buf.toString());
+
+        if (restriction != null) {
+            restriction.writeXML(out, indent);
+        }
+
+        if (validators.size() > 0) {
+            XMLUtil.printIndent(out, indent++);
+            out.println("<validators>");
+
+            for (int i = 0; i < validators.size(); i++) {
+                ValidatorDescriptor validator = (ValidatorDescriptor) validators.get(i);
+                validator.writeXML(out, indent);
+            }
+
+            XMLUtil.printIndent(out, --indent);
+            out.println("</validators>");
+        }
+
+        if (preFunctions.size() > 0) {
+            XMLUtil.printIndent(out, indent++);
+            out.println("<pre-functions>");
+
+            for (int i = 0; i < preFunctions.size(); i++) {
+                FunctionDescriptor function = (FunctionDescriptor) preFunctions.get(i);
+                function.writeXML(out, indent);
+            }
+
+            XMLUtil.printIndent(out, --indent);
+            out.println("</pre-functions>");
+        }
+
+        XMLUtil.printIndent(out, indent++);
+        out.println("<results>");
+
+        for (int i = 0; i < conditionalResults.size(); i++) {
+            ConditionalResultDescriptor result = (ConditionalResultDescriptor) conditionalResults.get(i);
+            result.writeXML(out, indent);
+        }
+
+        if (unconditionalResult != null) {
+            unconditionalResult.writeXML(out, indent);
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</results>");
+
+        if (postFunctions.size() > 0) {
+            XMLUtil.printIndent(out, indent++);
+            out.println("<post-functions>");
+
+            for (int i = 0; i < postFunctions.size(); i++) {
+                FunctionDescriptor function = (FunctionDescriptor) postFunctions.get(i);
+                function.writeXML(out, indent);
+            }
+
+            XMLUtil.printIndent(out, --indent);
+            out.println("</post-functions>");
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</action>");
+    }
+
+    protected void init(Element action) {
+        try {
+            setId(Integer.parseInt(action.getAttribute("id")));
+        } catch (Exception ex) {
+            throw new IllegalArgumentException("Invalid action id value " + action.getAttribute("id"));
+        }
+
+        this.name = action.getAttribute("name");
+        this.view = action.getAttribute("view");
+        this.autoExecute = "true".equals(action.getAttribute("auto"));
+
+        // set up validators - OPTIONAL
+        Element v = XMLUtil.getChildElement(action, "validators");
+
+        if (v != null) {
+            NodeList validators = v.getElementsByTagName("validator");
+
+            for (int k = 0; k < validators.getLength(); k++) {
+                Element validator = (Element) validators.item(k);
+                ValidatorDescriptor validatorDescriptor = new ValidatorDescriptor(validator);
+                validatorDescriptor.setParent(this);
+                this.validators.add(validatorDescriptor);
+            }
+        }
+
+        // set up pre-functions - OPTIONAL
+        Element pre = XMLUtil.getChildElement(action, "pre-functions");
+
+        if (pre != null) {
+            NodeList preFunctions = pre.getElementsByTagName("function");
+
+            for (int k = 0; k < preFunctions.getLength(); k++) {
+                Element preFunction = (Element) preFunctions.item(k);
+                FunctionDescriptor functionDescriptor = new FunctionDescriptor(preFunction);
+                functionDescriptor.setParent(this);
+                this.preFunctions.add(functionDescriptor);
+            }
+        }
+
+        // set up results - REQUIRED
+        Element resultsElememt = XMLUtil.getChildElement(action, "results");
+        NodeList results = resultsElememt.getElementsByTagName("result");
+
+        for (int k = 0; k < results.getLength(); k++) {
+            Element result = (Element) results.item(k);
+            ConditionalResultDescriptor conditionalResultDescriptor = new ConditionalResultDescriptor(result);
+            conditionalResultDescriptor.setParent(this);
+            this.conditionalResults.add(conditionalResultDescriptor);
+        }
+
+        Element unconditionalResult = (Element) resultsElememt.getElementsByTagName("unconditional-result").item(0);
+        this.unconditionalResult = new ResultDescriptor(unconditionalResult);
+        this.unconditionalResult.setParent(this);
+
+        // set up post-functions - OPTIONAL
+        Element post = XMLUtil.getChildElement(action, "post-functions");
+
+        if (post != null) {
+            NodeList postFunctions = post.getElementsByTagName("function");
+
+            for (int k = 0; k < postFunctions.getLength(); k++) {
+                Element postFunction = (Element) postFunctions.item(k);
+                FunctionDescriptor functionDescriptor = new FunctionDescriptor(postFunction);
+                functionDescriptor.setParent(this);
+                this.postFunctions.add(functionDescriptor);
+            }
+        }
+
+        // set up restrict-to - OPTIONAL
+        Element restrictElement = XMLUtil.getChildElement(action, "restrict-to");
+
+        if (restrictElement != null) {
+            restriction = new RestrictionDescriptor(restrictElement);
+            restriction.setParent(this);
+        }
+    }
+}

src/java/com/opensymphony/workflow/loader/ConditionDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author $author$
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ConditionDescriptor extends AbstractDescriptor {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected Map args = new HashMap();
+    protected String type;
+    protected boolean negate = false;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public ConditionDescriptor() {
+    }
+
+    public ConditionDescriptor(Element function) {
+        init(function);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public Map getArgs() {
+        return args;
+    }
+
+    public void setNegate(boolean negate) {
+        this.negate = negate;
+    }
+
+    public boolean isNegate() {
+        return negate;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+        out.println("<condition " + (hasId() ? ("id=\"" + getId() + "\" ") : "") + (negate ? ("negate=\"true\" ") : "") + "type=\"" + type + "\">");
+
+        Iterator iter = args.entrySet().iterator();
+
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            XMLUtil.printIndent(out, indent);
+            out.print("<arg name=\"");
+            out.print(entry.getKey());
+            out.print("\">");
+
+            if ("beanshell".equals(type) || "bsf".equals(type)) {
+                out.print("<![CDATA[");
+                out.print(entry.getValue());
+                out.print("]]>");
+            } else {
+                out.print(XMLUtil.encode(entry.getValue()));
+            }
+
+            out.println("</arg>");
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</condition>");
+    }
+
+    protected void init(Element function) {
+        type = function.getAttribute("type");
+
+        try {
+            setId(Integer.parseInt(function.getAttribute("id")));
+        } catch (NumberFormatException e) {
+        }
+
+        String n = function.getAttribute("negate");
+
+        if ("true".equalsIgnoreCase(n) || "yes".equalsIgnoreCase(n)) {
+            negate = true;
+        } else {
+            negate = false;
+        }
+
+        NodeList args = function.getElementsByTagName("arg");
+
+        for (int l = 0; l < args.getLength(); l++) {
+            Element arg = (Element) args.item(l);
+            this.args.put(arg.getAttribute("name"), arg.getChildNodes().item(0).getNodeValue());
+        }
+    }
+}

src/java/com/opensymphony/workflow/loader/ConditionalResultDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ConditionalResultDescriptor extends ResultDescriptor {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected List conditions = new ArrayList();
+    protected String conditionType;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public ConditionalResultDescriptor() {
+    }
+
+    public ConditionalResultDescriptor(Element conditionalResult) {
+        init(conditionalResult);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setConditionType(String conditionType) {
+        this.conditionType = conditionType;
+    }
+
+    public String getConditionType() {
+        return conditionType;
+    }
+
+    public List getConditions() {
+        return conditions;
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+
+        StringBuffer buf = new StringBuffer();
+        buf.append("<result");
+
+        if (hasId()) {
+            buf.append(" id=\"").append(getId()).append("\"");
+        }
+
+        buf.append(" old-status=\"").append(oldStatus).append("\"");
+
+        if (join != 0) {
+            buf.append(" join=\"").append(join).append("\"");
+        } else if (split != 0) {
+            buf.append(" split=\"").append(split).append("\"");
+        } else {
+            buf.append(" status=\"").append(status).append("\"");
+            buf.append(" step=\"").append(step).append("\"");
+
+            if ((owner != null) && (owner.length() > 0)) {
+                buf.append(" owner=\"").append(owner).append("\"");
+            }
+        }
+
+        buf.append(">");
+        out.println(buf);
+        XMLUtil.printIndent(out, indent++);
+        out.println("<conditions type=\"" + conditionType + "\">");
+
+        for (int i = 0; i < conditions.size(); i++) {
+            ConditionDescriptor condition = (ConditionDescriptor) conditions.get(i);
+            condition.writeXML(out, indent);
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</conditions>");
+
+        if (validators.size() > 0) {
+            XMLUtil.printIndent(out, indent++);
+            out.println("<validators>");
+
+            for (int i = 0; i < validators.size(); i++) {
+                ValidatorDescriptor validator = (ValidatorDescriptor) validators.get(i);
+                validator.writeXML(out, indent);
+            }
+
+            XMLUtil.printIndent(out, --indent);
+            out.println("</validators>");
+        }
+
+        printPreFunctions(out, indent);
+        printPostFunctions(out, indent);
+        XMLUtil.printIndent(out, --indent);
+        out.println("</result>");
+    }
+
+    protected void init(Element conditionalResult) {
+        super.init(conditionalResult);
+
+        Element conditions = XMLUtil.getChildElement(conditionalResult, "conditions");
+        conditionType = conditions.getAttribute("type");
+
+        NodeList conditionNodes = conditions.getElementsByTagName("condition");
+        int length = conditionNodes.getLength();
+
+        for (int i = 0; i < length; i++) {
+            Element condition = (Element) conditionNodes.item(i);
+            ConditionDescriptor conditionDescriptor = new ConditionDescriptor(condition);
+            conditionDescriptor.setParent(this);
+            this.conditions.add(conditionDescriptor);
+        }
+    }
+}

src/java/com/opensymphony/workflow/loader/FunctionDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class FunctionDescriptor extends AbstractDescriptor {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected Map args = new HashMap();
+    protected String type;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public FunctionDescriptor() {
+    }
+
+    public FunctionDescriptor(Element function) {
+        init(function);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public Map getArgs() {
+        return args;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+        out.println("<function " + (hasId() ? ("id=\"" + getId() + "\" ") : "") + "type=\"" + type + "\">");
+
+        Iterator iter = args.entrySet().iterator();
+
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            XMLUtil.printIndent(out, indent);
+            out.print("<arg name=\"");
+            out.print(entry.getKey());
+            out.print("\">");
+
+            if ("beanshell".equals(type) || "bsf".equals(type)) {
+                out.print("<![CDATA[");
+                out.print(entry.getValue());
+                out.print("]]>");
+            } else {
+                out.print(XMLUtil.encode(entry.getValue()));
+            }
+
+            out.println("</arg>");
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</function>");
+    }
+
+    protected void init(Element function) {
+        type = function.getAttribute("type");
+
+        try {
+            setId(Integer.parseInt(function.getAttribute("id")));
+        } catch (NumberFormatException e) {
+        }
+
+        NodeList args = function.getElementsByTagName("arg");
+
+        for (int l = 0; l < args.getLength(); l++) {
+            Element arg = (Element) args.item(l);
+            this.args.put(arg.getAttribute("name"), arg.getChildNodes().item(0).getNodeValue());
+        }
+    }
+}

src/java/com/opensymphony/workflow/loader/JoinDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
+import com.opensymphony.workflow.util.Validatable;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author $author$
+ * @version $Revision: 1.1.1.1 $
+ */
+public class JoinDescriptor extends AbstractDescriptor implements Validatable {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected List conditions = new ArrayList();
+    protected ResultDescriptor result;
+    protected String conditionType;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public JoinDescriptor() {
+    }
+
+    public JoinDescriptor(Element join) {
+        init(join);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setConditionType(String conditionType) {
+        this.conditionType = conditionType;
+    }
+
+    public String getConditionType() {
+        return conditionType;
+    }
+
+    public List getConditions() {
+        return conditions;
+    }
+
+    public void setResult(ResultDescriptor result) {
+        this.result = result;
+    }
+
+    public ResultDescriptor getResult() {
+        return result;
+    }
+
+    public void validate() throws InvalidWorkflowDescriptorException {
+        ValidationHelper.validate(conditions);
+        result.validate();
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+        out.println("<join id=\"" + getId() + "\">");
+        XMLUtil.printIndent(out, indent++);
+        out.println("<conditions type=\"" + conditionType + "\">");
+
+        for (int i = 0; i < conditions.size(); i++) {
+            ConditionDescriptor result = (ConditionDescriptor) conditions.get(i);
+            result.writeXML(out, indent);
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</conditions>");
+        result.writeXML(out, indent);
+        XMLUtil.printIndent(out, --indent);
+        out.println("</join>");
+    }
+
+    protected void init(Element join) {
+        try {
+            setId(Integer.parseInt(join.getAttribute("id")));
+        } catch (Exception ex) {
+            throw new IllegalArgumentException("Invalid join id value " + join.getAttribute("id"));
+        }
+
+        // get conditions
+        Element conditions = XMLUtil.getChildElement(join, "conditions");
+        conditionType = conditions.getAttribute("type");
+
+        NodeList conditionNodes = conditions.getElementsByTagName("condition");
+        int length = conditionNodes.getLength();
+
+        for (int i = 0; i < length; i++) {
+            Element condition = (Element) conditionNodes.item(i);
+            ConditionDescriptor conditionDescriptor = new ConditionDescriptor(condition);
+            conditionDescriptor.setParent(this);
+            this.conditions.add(conditionDescriptor);
+        }
+
+        //<unconditional-result status="Underway" owner="test" step="2"/>
+        Element resultElement = (Element) join.getElementsByTagName("unconditional-result").item(0);
+        result = new ResultDescriptor(resultElement);
+        result.setParent(this);
+    }
+}

src/java/com/opensymphony/workflow/loader/PermissionDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import org.w3c.dom.Element;
+
+import java.io.PrintWriter;
+
+
+/**
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class PermissionDescriptor extends AbstractDescriptor {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected RestrictionDescriptor restriction;
+    protected String name;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public PermissionDescriptor() {
+    }
+
+    public PermissionDescriptor(Element permission) {
+        init(permission);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setRestriction(RestrictionDescriptor restriction) {
+        this.restriction = restriction;
+    }
+
+    public RestrictionDescriptor getRestriction() {
+        return restriction;
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+        out.print("<permission ");
+
+        if (hasId()) {
+            out.print("id=\"" + getId() + "\" ");
+        }
+
+        out.println("name=\"" + name + "\">");
+        restriction.writeXML(out, indent);
+        XMLUtil.printIndent(out, --indent);
+        out.println("</permission>");
+    }
+
+    protected void init(Element permission) {
+        name = permission.getAttribute("name");
+
+        try {
+            setId(Integer.parseInt(permission.getAttribute("id")));
+        } catch (NumberFormatException e) {
+        }
+
+        restriction = new RestrictionDescriptor(XMLUtil.getChildElement(permission, "restrict-to"));
+    }
+}

src/java/com/opensymphony/workflow/loader/RegisterDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class RegisterDescriptor extends AbstractDescriptor {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected Map args = new HashMap();
+    protected String type;
+    protected String variableName;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public RegisterDescriptor() {
+    }
+
+    public RegisterDescriptor(Element register) {
+        init(register);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public Map getArgs() {
+        return args;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setVariableName(String variableName) {
+        this.variableName = variableName;
+    }
+
+    public String getVariableName() {
+        return variableName;
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+        out.println("<register " + (hasId() ? ("id=\"" + getId() + "\" ") : "") + "type=\"" + type + "\" variable-name=\"" + variableName + "\">");
+
+        Iterator iter = args.entrySet().iterator();
+
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            XMLUtil.printIndent(out, indent);
+            out.print("<arg name=\"");
+            out.print(entry.getKey());
+            out.print("\">");
+
+            if ("beanshell".equals(type) || "bsf".equals(type)) {
+                out.print("<![CDATA[");
+                out.print(entry.getValue());
+                out.print("]]>");
+            } else {
+                out.print(XMLUtil.encode(entry.getValue()));
+            }
+
+            out.println("</arg>");
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</register>");
+    }
+
+    protected void init(Element register) {
+        this.type = register.getAttribute("type");
+        this.variableName = register.getAttribute("variable-name");
+
+        try {
+            setId(Integer.parseInt(register.getAttribute("id")));
+        } catch (NumberFormatException e) {
+        }
+
+        NodeList args = register.getElementsByTagName("arg");
+
+        for (int l = 0; l < args.getLength(); l++) {
+            Element arg = (Element) args.item(l);
+            this.args.put(arg.getAttribute("name"), arg.getChildNodes().item(0).getNodeValue());
+        }
+    }
+}

src/java/com/opensymphony/workflow/loader/RestrictionDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
+import com.opensymphony.workflow.util.Validatable;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class RestrictionDescriptor extends AbstractDescriptor implements Validatable {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected List conditions = new ArrayList();
+    protected String conditionType;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public RestrictionDescriptor() {
+    }
+
+    public RestrictionDescriptor(Element restriction) {
+        init(restriction);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public void setConditionType(String conditionType) {
+        this.conditionType = conditionType;
+    }
+
+    public String getConditionType() {
+        return conditionType;
+    }
+
+    public List getConditions() {
+        return conditions;
+    }
+
+    public void validate() throws InvalidWorkflowDescriptorException {
+        ValidationHelper.validate(conditions);
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+        out.println("<restrict-to>");
+
+        if (conditions.size() > 0) {
+            XMLUtil.printIndent(out, indent++);
+            out.println("<conditions type=\"" + conditionType + "\">");
+
+            for (int i = 0; i < conditions.size(); i++) {
+                ConditionDescriptor condition = (ConditionDescriptor) conditions.get(i);
+                condition.writeXML(out, indent);
+            }
+
+            XMLUtil.printIndent(out, --indent);
+            out.println("</conditions>");
+        }
+
+        XMLUtil.printIndent(out, --indent);
+        out.println("</restrict-to>");
+    }
+
+    protected void init(Element restriction) {
+        // set up condition - OPTIONAL
+        Element conditions = XMLUtil.getChildElement(restriction, "conditions");
+
+        if (conditions != null) {
+            conditionType = conditions.getAttribute("type");
+
+            NodeList conditionNodes = conditions.getElementsByTagName("condition");
+            int length = conditionNodes.getLength();
+
+            for (int i = 0; i < length; i++) {
+                Element condition = (Element) conditionNodes.item(i);
+                ConditionDescriptor conditionDescriptor = new ConditionDescriptor(condition);
+                conditionDescriptor.setParent(this);
+                this.conditions.add(conditionDescriptor);
+            }
+        }
+    }
+}

src/java/com/opensymphony/workflow/loader/ResultDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
+import com.opensymphony.workflow.util.Validatable;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ResultDescriptor extends AbstractDescriptor implements Validatable {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    protected List postFunctions = new ArrayList();
+    protected List preFunctions = new ArrayList();
+    protected List validators = new ArrayList();
+    protected String dueDate;
+    protected String oldStatus;
+    protected String owner;
+    protected String status;
+    protected boolean hasStep = false;
+    protected int join;
+    protected int split;
+    protected int step = 0;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public ResultDescriptor() {
+    }
+
+    public ResultDescriptor(Element result) {
+        init(result);
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public String getDueDate() {
+        return dueDate;
+    }
+
+    public void setJoin(int join) {
+        this.join = join;
+    }
+
+    public int getJoin() {
+        return join;
+    }
+
+    public void setOldStatus(String oldStatus) {
+        this.oldStatus = oldStatus;
+    }
+
+    public String getOldStatus() {
+        return oldStatus;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public List getPostFunctions() {
+        return postFunctions;
+    }
+
+    public List getPreFunctions() {
+        return preFunctions;
+    }
+
+    public void setSplit(int split) {
+        this.split = split;
+    }
+
+    public int getSplit() {
+        return split;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStep(int step) {
+        this.step = step;
+        hasStep = true;
+    }
+
+    public int getStep() {
+        return step;
+    }
+
+    public List getValidators() {
+        return validators;
+    }
+
+    public void validate() throws InvalidWorkflowDescriptorException {
+        ValidationHelper.validate(preFunctions);
+        ValidationHelper.validate(postFunctions);
+        ValidationHelper.validate(validators);
+
+        //if it's not a split or a join, then we require a next step
+        if ((split == 0) && (join == 0)) {
+            if (!hasStep) {
+                throw new InvalidWorkflowDescriptorException("Result is not a split or join, and has no next step");
+            }
+
+            if (status.length() == 0) {
+                throw new InvalidWorkflowDescriptorException("Result is not a split or join, and has no status");
+            }
+        }
+
+        //taken out for now
+        //if ((split != 0) && ((join != 0) || (oldStatus.length() > 0) || (step != 0) || (status.length() > 0) || (owner.length() != 0))) {
+        //    throw new InvalidWorkflowDescriptorException("Result " + id + " has a split attribute, so should not any other attributes");
+        //} else if ((join != 0) && ((split != 0) || (oldStatus.length() > 0) || (step != 0) || (status.length() > 0) || (owner.length() != 0))) {
+        //    throw new InvalidWorkflowDescriptorException("Result has a join attribute, should thus not any other attributes");
+        //} else if ((oldStatus.length() == 0) || (step == 0) || (status.length() == 0)) {
+        //    throw new InvalidWorkflowDescriptorException("old-status, step, status and owner attributes are required if no split or join");
+        //}
+    }
+
+    public void writeXML(PrintWriter out, int indent) {
+        XMLUtil.printIndent(out, indent++);
+
+        StringBuffer buf = new StringBuffer();
+        buf.append("<unconditional-result");
+
+        if (hasId()) {
+            buf.append(" id=\"").append(getId()).append("\"");
+        }
+
+        buf.append(" old-status=\"").append(oldStatus).append("\"");
+
+        if (join != 0) {
+            buf.append(" join=\"").append(join).append("\"");
+        } else if (split != 0) {
+            buf.append(" split=\"").append(split).append("\"");
+        } else {
+            buf.append(" status=\"").append(status).append("\"");
+            buf.append(" step=\"").append(step).append("\"");
+
+            if ((owner != null) && (owner.length() > 0)) {
+                buf.append(" owner=\"").append(owner).append("\"");
+            }
+        }
+
+        if ((preFunctions.size() == 0) && (postFunctions.size() == 0)) {
+            buf.append("/>");
+            out.println(buf.toString());
+        } else {
+            buf.append(">");
+            out.println(buf.toString());
+            printPreFunctions(out, indent);
+            printPostFunctions(out, indent);
+            XMLUtil.printIndent(out, --indent);
+            out.println("</unconditional-result>");
+        }
+    }
+
+    protected void init(Element result) {
+        oldStatus = result.getAttribute("old-status");
+        status = result.getAttribute("status");
+
+        try {
+            setId(Integer.parseInt(result.getAttribute("id")));
+        } catch (NumberFormatException e) {
+        }
+
+        dueDate = result.getAttribute("due-date");
+
+        try {
+            split = Integer.parseInt(result.getAttribute("split"));
+        } catch (Exception ex) {
+        }
+
+        try {
+            join = Integer.parseInt(result.getAttribute("join"));
+        } catch (Exception ex) {
+        }
+
+        try {
+            step = Integer.parseInt(result.getAttribute("step"));
+            hasStep = true;
+        } catch (Exception ex) {
+        }
+
+        owner = result.getAttribute("owner");
+
+        // set up validators -- OPTIONAL
+        Element v = XMLUtil.getChildElement(result, "validators");
+
+        if (v != null) {
+            NodeList validators = v.getElementsByTagName("validator");
+
+            for (int k = 0; k < validators.getLength(); k++) {
+                Element validator = (Element) validators.item(k);
+                ValidatorDescriptor validatorDescriptor = new ValidatorDescriptor(validator);
+                validatorDescriptor.setParent(this);
+                this.validators.add(validatorDescriptor);
+            }
+        }
+
+        // set up pre-functions -- OPTIONAL
+        Element pre = XMLUtil.getChildElement(result, "pre-functions");
+
+        if (pre != null) {
+            NodeList preFunctions = pre.getElementsByTagName("function");
+
+            for (int k = 0; k < preFunctions.getLength(); k++) {
+                Element preFunction = (Element) preFunctions.item(k);
+                FunctionDescriptor functionDescriptor = new FunctionDescriptor(preFunction);
+                functionDescriptor.setParent(this);
+                this.preFunctions.add(functionDescriptor);
+            }
+        }
+
+        // set up post-functions - OPTIONAL
+        Element post = XMLUtil.getChildElement(result, "post-functions");
+
+        if (post != null) {
+            NodeList postFunctions = post.getElementsByTagName("function");
+
+            for (int k = 0; k < postFunctions.getLength(); k++) {
+                Element postFunction = (Element) postFunctions.item(k);
+                FunctionDescriptor functionDescriptor = new FunctionDescriptor(postFunction);
+                functionDescriptor.setParent(this);
+                this.postFunctions.add(functionDescriptor);
+            }
+        }
+    }
+
+    protected void printPostFunctions(PrintWriter out, int indent) {
+        if (postFunctions.size() > 0) {
+            XMLUtil.printIndent(out, indent++);
+            out.println("<post-functions>");
+
+            Iterator iter = postFunctions.iterator();
+
+            while (iter.hasNext()) {
+                FunctionDescriptor function = (FunctionDescriptor) iter.next();
+                function.writeXML(out, indent);
+            }
+
+            XMLUtil.printIndent(out, --indent);
+            out.println("</post-functions>");
+        }
+    }
+
+    protected void printPreFunctions(PrintWriter out, int indent) {
+        if (preFunctions.size() > 0) {
+            XMLUtil.printIndent(out, indent++);
+            out.println("<pre-functions>");
+
+            Iterator iter = preFunctions.iterator();
+
+            while (iter.hasNext()) {
+                FunctionDescriptor function = (FunctionDescriptor) iter.next();
+                function.writeXML(out, indent);
+            }
+
+            XMLUtil.printIndent(out, --indent);
+            out.println("</pre-functions>");
+        }
+    }
+}

src/java/com/opensymphony/workflow/loader/SplitDescriptor.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import com.opensymphony.workflow.InvalidWorkflowDescriptorException;
+import com.opensymphony.workflow.util.Validatable;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author $author$