Parse workflows and command line tools recursively
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/CWLParser.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/CWLParser.java
index 5a4d6cf..382208d 100644
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/CWLParser.java
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/CWLParser.java
@@ -51,7 +51,7 @@
         Map<String, Integer> inputDepths = yamlHelper.processInputDepths(cwlFile);
 
         if(inputs == null || inputDepths == null) {
-            return null;
+            return new HashSet<PortDetail>();
         }
         Set<PortDetail> result = new HashSet<PortDetail>();
         for(String id: inputs.keySet()) {
@@ -66,14 +66,15 @@
     }
 
     public Set<PortDetail> parseOutputs() {
-        Map<String, PortDetail> inputs = yamlHelper.processOutputDetails(cwlFile);
+        Map<String, PortDetail> outputs = yamlHelper.processOutputDetails(cwlFile);
 
-        if(inputs == null) {
-            return null;
+        if(outputs == null) {
+            return new HashSet<PortDetail>();
         }
         Set<PortDetail> result = new HashSet<PortDetail>();
-        for(String id: inputs.keySet()) {
-            PortDetail port = inputs.get(id);
+        for(String id: outputs.keySet()) {
+            PortDetail port = outputs.get(id);
+            port.setId(id);
             result.add(port);
         }
 
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Converter.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Converter.java
index 7ddf9de..b607190 100644
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Converter.java
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Converter.java
@@ -20,6 +20,8 @@
 
 import java.util.Set;
 import java.util.HashSet;
+import java.util.Map;
+import java.util.HashMap;
 
 import org.apache.taverna.scufl2.api.core.Workflow;
 import org.apache.taverna.scufl2.api.core.Processor;
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Main.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Main.java
new file mode 100644
index 0000000..d4fa6b8
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Main.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.taverna.scufl2.cwl;
+
+import org.yaml.snakeyaml.Yaml;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class Main {
+
+    private static final String HELLO_WORLD_CWL = "/hello_world.cwl";
+    private static final String WORKFLOW_WITH_COMMAND = "/workflow_with_command.cwl";
+    private static JsonNode cwlFile;
+
+    public static void main(String[] args) {
+
+        Yaml reader = new Yaml();
+        ObjectMapper mapper = new ObjectMapper();
+        cwlFile = mapper.valueToTree(reader.load(Main.class.getResourceAsStream(WORKFLOW_WITH_COMMAND)));
+        System.out.println(cwlFile);
+
+        Process workflow = ProcessFactory.createProcess(cwlFile);
+        workflow.parse();
+        Converter converter = new Converter();
+        JsonNode node = converter.convertWorkflowProcessToJsonNode((WorkflowProcess) workflow);
+        System.out.println("NODE");
+        System.out.println(node);
+    }
+}
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/WorkflowParser.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/WorkflowParser.java
index 0b6c095..b18dc54 100644
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/WorkflowParser.java
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/WorkflowParser.java
@@ -43,6 +43,7 @@
 import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
 import org.apache.taverna.scufl2.api.port.SenderPort;
 import org.apache.taverna.scufl2.api.port.ReceiverPort;
+
 import org.apache.taverna.scufl2.api.container.WorkflowBundle;
 
 import org.apache.taverna.scufl2.api.io.WorkflowBundleIO;
@@ -67,6 +68,12 @@
         JsonNode cwlFile = mapper.valueToTree(reader.load(WorkflowParser.class.getResourceAsStream(FILE_NAME)));
         this.cwlParser = new CWLParser(cwlFile);
         this.converter = new Converter();
+        workflowInputs = new HashMap<>();
+        workflowOutputs = new HashMap<>();
+        workflowProcessors = new HashMap<>();
+        processorInputs = new HashMap<>();
+        processorOutputs = new HashMap<>();
+        dataLinks = new HashSet<DataLink>();
     }
 
     public WorkflowParser(JsonNode cwlFile) {
@@ -144,7 +151,6 @@
             Processor processor = converter.convertStepToProcessor(step);
             workflowProcessors.put(step.getId(), processor);
 
-            DataLink datalink = new DataLink();
             for(StepInput stepInput: step.getInputs()) {
                 InputProcessorPort processorPort = new InputProcessorPort(processor, stepInput.getId());
                 processorInputs.put(stepInput.getId(), processorPort);
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/YAMLHelper.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/YAMLHelper.java
index dacdc4f..c9b10dc 100644
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/YAMLHelper.java
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/YAMLHelper.java
@@ -108,42 +108,44 @@
     public Set<Step> processSteps(JsonNode file) {
         Set<Step> result = new HashSet<>();
 
-        if(file == null) {
+        if(file == null || !file.has(STEPS)) {
             return result;
         }
 
-        if(file.has(STEPS)) {
-            JsonNode steps = file.get(STEPS);
-            if(steps.isArray()) {
-                for (JsonNode stepNode : steps) {
-                    Step step = new Step();
-                    String id = stepNode.get(ID).asText();
+        JsonNode steps = file.get(STEPS);
+        if(steps.isArray()) {
+            for (JsonNode stepNode : steps) {
+                Step step = new Step();
+                String id = stepNode.get(ID).asText();
 
-                    String run = stepNode.get(RUN).asText();
-                    Set<StepInput> inputs = processStepInput(stepNode.get(INPUTS));
-                    step.setId(id);
+                JsonNode runNode = stepNode.get(RUN);
+                Process run = ProcessFactory.createProcess(runNode);
+                run.parse();  // Recursively parse nested process
+                Set<StepInput> inputs = processStepInput(stepNode.get(INPUTS));
+                step.setId(id);
+                step.setRun(run);
+                step.setInputs(inputs);
+                result.add(step);
+            }
+        } else if(steps.isObject()) {
+            Iterator<Entry<String, JsonNode>> iterator = steps.fields();
+            while(iterator.hasNext()) {
+                Entry<String, JsonNode> entry = iterator.next();
+                Step step = new Step();
+
+                String id = entry.getKey();
+                JsonNode value = entry.getValue();
+                if(value.has(RUN)) {
+                    JsonNode runNode = value.get(RUN);
+                    Process run = ProcessFactory.createProcess(runNode);
+                    run.parse();
                     step.setRun(run);
-                    step.setInputs(inputs);
-                    result.add(step);
                 }
-            } else if(steps.isObject()) {
-                Iterator<Entry<String, JsonNode>> iterator = steps.fields();
-                while(iterator.hasNext()) {
-                    Entry<String, JsonNode> entry = iterator.next();
-                    Step step = new Step();
+                Set<StepInput> inputs = processStepInput(value.get(INPUTS));
+                step.setId(id);
+                step.setInputs(inputs);
 
-                    String id = entry.getKey();
-                    JsonNode value = entry.getValue();
-                    if(value.has(RUN)) {
-                        String run = entry.getValue().get(RUN).asText();
-                        step.setRun(run);
-                    }
-                    Set<StepInput> inputs = processStepInput(value.get(INPUTS));
-                    step.setId(id);
-                    step.setInputs(inputs);
-
-                    result.add(step);
-                }
+                result.add(step);
             }
         }
 
@@ -156,7 +158,7 @@
         if(inputs == null) {
             return result;
         }
-        if (inputs.getClass() == ArrayNode.class) {
+        if (inputs.isArray()) {
 
             for (JsonNode input : inputs) {
                 String id = input.get(ID).asText();
@@ -164,7 +166,7 @@
 
                 result.add(new StepInput(id, source));
             }
-        } else if (inputs.getClass() == ObjectNode.class) {
+        } else if (inputs.isObject()) {
             Iterator<Entry<String, JsonNode>> iterator = inputs.fields();
             while (iterator.hasNext()) {
                 Entry<String, JsonNode> entry = iterator.next();
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/CommandLineTool.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/CommandLineTool.java
new file mode 100644
index 0000000..d05d38a
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/CommandLineTool.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.taverna.scufl2.cwl;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+
+public class CommandLineTool implements Process {
+
+    private final static String BASE_COMMAND = "baseCommand";
+    private final static String ID = "id";
+    private final static String INPUT_BINDINDGS = "inputBinding";
+
+    private CWLParser cwlParser;
+
+    private JsonNode node;
+
+    private String baseCommand = null;
+    private Map<String, InputProcessorPort> processorInputs;
+    private Map<String, OutputProcessorPort> processorOutputs;
+
+    public CommandLineTool(JsonNode node) {
+        this.node = node;
+        this.cwlParser = new CWLParser(node);
+        this.processorInputs = new HashMap<>();
+        this.processorOutputs = new HashMap<>();
+        this.parse();
+        this.receiverPorts = new HashSet(processorInputs.values());
+        this.senderPorts = new HashSet(processorOutputs.values());
+    }
+
+    public void parse() {
+        baseCommand = node.get(BASE_COMMAND).asText();
+        parseInputs();
+        parseOutputs();
+    }
+
+    public void parseInputs() {
+        Set<PortDetail> cwlInputs = cwlParser.parseInputs();
+        for(PortDetail detail: cwlInputs) {
+            String portId = detail.getId();
+            InputProcessorPort port = new InputProcessorPort();
+            port.setName(portId);
+            processorInputs.put(portId, port);
+        }
+    }
+
+    public void parseOutputs() {
+        Set<PortDetail> cwlOutputs = cwlParser.parseOutputs();
+        for(PortDetail detail: cwlOutputs) {
+            String portId = detail.getId();
+            OutputProcessorPort port = new OutputProcessorPort();
+            port.setName(portId);
+            processorOutputs.put(portId, port);
+        }
+    }
+}
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Main.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Main.java
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Main.java
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Process.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Process.java
new file mode 100644
index 0000000..342864a
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Process.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.taverna.scufl2.cwl;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import org.apache.taverna.scufl2.api.port.SenderPort;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+
+public interface Process {
+
+    Set<ReceiverPort> receiverPorts;
+    Set<SenderPort> senderPorts;
+
+    public void parse();
+
+}
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/ProcessFactory.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/ProcessFactory.java
new file mode 100644
index 0000000..301d417
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/ProcessFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.taverna.scufl2.cwl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class ProcessFactory {
+
+    private final static String RUN = "run";
+    private final static String CLASS = "class";
+
+    public static Process createProcess(JsonNode node) {
+
+        Process process = null;
+
+        if(node.isValueNode()) {
+            return new Reference(node.asText());
+        }
+
+        JsonNode className = node.get(CLASS);
+        if(className != null) {
+            if(className.asText().equals("Workflow")) {
+                process = new WorkflowProcess(node);
+            } else if(className.asText().equals("CommandLineTool")) {
+                process = new CommandLineTool(node);
+            }
+        } else {
+            JsonNode runNode = node.get(RUN);
+            if(runNode.isValueNode()) {
+                process = new Reference(runNode.asText());
+            } else {
+                String runClass = runNode.get(CLASS).asText();
+                switch(runClass) {
+                    case "CommandLineTool":
+                        process = new CommandLineTool(runNode);
+                        break;
+                    case "Workflow":
+                        process = new WorkflowProcess(runNode);
+                        break;
+                }
+            }
+        }
+
+
+        return process;
+    }
+
+}
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Reference.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Reference.java
new file mode 100644
index 0000000..7a25744
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Reference.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.taverna.scufl2.cwl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class Reference implements Process {
+
+    private String source;
+
+    public Reference() {
+        source = "";
+    }
+
+    public Reference(String src) {
+        this.source = src;
+    }
+
+    public void parse() {
+        // TODO: read source file and parse nested workflow
+    }
+
+    public String toString() {
+        return source;
+    }
+}
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Step.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Step.java
index 3f3d653..cdcd46e 100644
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Step.java
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Step.java
@@ -28,7 +28,7 @@
 
 
     private String id;
-    private String run;
+    private Process run;
 
     private Set<StepInput> inputs;
     private Set<StepOutput> outputs;
@@ -46,11 +46,11 @@
         this.id = id;
     }
 
-    public String getRun() {
+    public Process getRun() {
         return run;
     }
 
-    public void setRun(String run) {
+    public void setRun(Process run) {
         this.run = run;
     }
 
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/WorkflowProcess.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/WorkflowProcess.java
new file mode 100644
index 0000000..c56cbb5
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/WorkflowProcess.java
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.taverna.scufl2.cwl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.HashMap;
+
+
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.DataLink;
+import org.apache.taverna.scufl2.api.core.Workflow;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+import org.apache.taverna.scufl2.api.port.SenderPort;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+
+import org.apache.taverna.scufl2.api.io.WorkflowBundleIO;
+import org.apache.taverna.scufl2.api.io.WriterException;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class WorkflowProcess implements Process {
+
+    private CWLParser cwlParser;
+
+    private Map<String, InputWorkflowPort> workflowInputs;
+    private Map<String, OutputWorkflowPort> workflowOutputs;
+    private Map<String, Processor> workflowProcessors;
+    private Map<String, InputProcessorPort> processorInputs;
+    private Map<String, OutputProcessorPort> processorOutputs;
+    private Set<DataLink> dataLinks;
+
+    private Converter converter;
+
+    public WorkflowProcess(JsonNode node) {
+        cwlParser = new CWLParser(node);
+        converter = new Converter();
+        workflowInputs = new HashMap<>();
+        workflowOutputs = new HashMap<>();
+        workflowProcessors = new HashMap<>();
+        processorInputs = new HashMap<>();
+        processorOutputs = new HashMap<>();
+        dataLinks = new HashSet<>();
+        this.parse();
+        this.receiverPorts = new HashSet(workflowInputs.values());
+        this.senderPorts = new HashSet(workflowOutputs.values());
+    }
+
+    public void parse() {
+        parseInputs();
+        parseOutputs();
+        Set<Step> cwlSteps = cwlParser.parseSteps();
+        parseProcessors(cwlSteps);
+        parseDataLinks(cwlSteps);
+
+        Workflow workflow = new Workflow();
+        Set<InputWorkflowPort> inputs = new HashSet<>(workflowInputs.values());
+        Set<OutputWorkflowPort> outputs = new HashSet<>(workflowOutputs.values());
+        Set<Processor> processors = new HashSet<>(workflowProcessors.values());
+
+        workflow.setInputPorts(inputs);
+        workflow.setOutputPorts(outputs);
+        workflow.setProcessors(processors);
+        workflow.setDataLinks(dataLinks);
+
+//        System.out.println(workflow);
+//        writeWorkflowToFile(workflow);
+//
+//        System.out.println("DEBUG WORKFLOW");
+//        System.out.println(workflow.getInputPorts());
+//        System.out.println(workflow.getOutputPorts());
+//        System.out.println(workflow.getProcessors());
+
+    }
+
+    public void writeWorkflowToFile(Workflow workflow) {
+        try {
+            WorkflowBundleIO io = new WorkflowBundleIO();
+            File scufl2File = new File("workflow.wfbundle");
+            WorkflowBundle bundle = io.createBundle();
+            Set<Workflow> workflowSet = new HashSet<>();
+            workflowSet.add(workflow);
+            bundle.setWorkflows(workflowSet);
+            bundle.setMainWorkflow(workflow);
+            io.writeBundle(bundle, scufl2File, "text/vnd.taverna.scufl2.structure");
+        } catch(WriterException e) {
+            System.out.println("Exception writing the workflow bundle");
+        } catch(IOException e) {
+            System.out.println("IOException");
+        }
+    }
+
+    public void parseInputs() {
+        Set<PortDetail> cwlInputs = cwlParser.parseInputs();
+        for (PortDetail port: cwlInputs) {
+            String portId = port.getId();
+            InputWorkflowPort workflowPort = converter.convertInputWorkflowPort(port);
+            workflowInputs.put(portId, workflowPort);
+        }
+    }
+
+    public void parseOutputs() {
+        Set<PortDetail> cwlOutputs = cwlParser.parseOutputs();
+        for(PortDetail port: cwlOutputs) {
+            String portId = port.getId();
+            OutputWorkflowPort workflowPort = converter.convertOutputWorkflowPort(port);
+            workflowOutputs.put(portId, workflowPort);
+        }
+    }
+
+    public void parseProcessors(Set<Step> cwlSteps) {
+        for(Step step: cwlSteps) {
+
+            Processor processor = converter.convertStepToProcessor(step);
+            workflowProcessors.put(step.getId(), processor);
+
+            // TODO: Add only receiver and sender ports from the Process interface
+            for(StepInput stepInput: step.getInputs()) {
+                InputProcessorPort processorPort = new InputProcessorPort(processor, stepInput.getId());
+                processorInputs.put(stepInput.getId(), processorPort);
+            }
+            for(StepOutput stepOutput: step.getOutputs()) {
+                OutputProcessorPort processorPort = new OutputProcessorPort(processor, stepOutput.getId());
+                processorOutputs.put(stepOutput.getId(), processorPort);
+            }
+        }
+    }
+
+    public void parseDataLinks(Set<Step> cwlSteps) {
+        for(Step step: cwlSteps) {
+            for(StepInput stepInput: step.getInputs()) {
+                String[] sourcePath = stepInput.getSource().split("/");
+                String source = sourcePath[sourcePath.length-1];
+                source = source.replace("#", "");
+
+                DataLink dataLink = new DataLink();
+                SenderPort sender = workflowInputs.get(source);
+                if(sender == null) {
+                    sender = processorOutputs.get(source);
+                }
+                if(sender == null) {
+                    throw new NullPointerException("Cannot find sender port with name: " + source);
+                }
+                String receiverId = stepInput.getId();
+                ReceiverPort receiver = workflowOutputs.get(receiverId);
+                if(receiver == null) {
+                    receiver = processorInputs.get(receiverId);
+                }
+                if(receiver == null) {
+                    throw new NullPointerException("Cannot find receiver port with name: " + receiverId);
+                }
+                dataLink.setSendsTo(receiver);
+                dataLink.setReceivesFrom(sender);
+                dataLinks.add(dataLink);
+            }
+        }
+    }
+
+    public Map<String, InputWorkflowPort> getWorkflowInputs() {
+        return workflowInputs;
+    }
+
+    public void setWorkflowInputs(Map<String, InputWorkflowPort> workflowInputs) {
+        this.workflowInputs = workflowInputs;
+    }
+
+    public Map<String, OutputWorkflowPort> getWorkflowOutputs() {
+        return workflowOutputs;
+    }
+
+    public void setWorkflowOutputs(Map<String, OutputWorkflowPort> workflowOutputs) {
+        this.workflowOutputs = workflowOutputs;
+    }
+
+    public Map<String, Processor> getWorkflowProcessors() {
+        return workflowProcessors;
+    }
+
+    public void setWorkflowProcessors(Map<String, Processor> workflowProcessors) {
+        this.workflowProcessors = workflowProcessors;
+    }
+
+    public Map<String, InputProcessorPort> getProcessorInputs() {
+        return processorInputs;
+    }
+
+    public void setProcessorInputs(Map<String, InputProcessorPort> processorInputs) {
+        this.processorInputs = processorInputs;
+    }
+
+    public Map<String, OutputProcessorPort> getProcessorOutputs() {
+        return processorOutputs;
+    }
+
+    public void setProcessorOutputs(Map<String, OutputProcessorPort> processorOutputs) {
+        this.processorOutputs = processorOutputs;
+    }
+
+    public Set<DataLink> getDataLinks() {
+        return dataLinks;
+    }
+
+    public void setDataLinks(Set<DataLink> dataLinks) {
+        this.dataLinks = dataLinks;
+    }
+}
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/resources/hello_world.cwl b/taverna-scufl2-cwl/src/main/resources/hello_world.cwl
new file mode 100644
index 0000000..4207afa
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/resources/hello_world.cwl
@@ -0,0 +1,37 @@
+#!/usr/bin/env cwl-runner
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements. See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership. The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License. You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied. See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+cwlVersion: v1.0
+class: Workflow
+
+inputs:
+  name: string
+
+outputs:
+  output1: string
+
+steps:
+  step1:
+    run: example.cwl
+
+    inputs:
+      - id: text
+        source: "#x/name"
+
+    outputs: []
diff --git a/taverna-scufl2-cwl/src/main/resources/workflow_with_command.cwl b/taverna-scufl2-cwl/src/main/resources/workflow_with_command.cwl
new file mode 100644
index 0000000..e640ecc
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/resources/workflow_with_command.cwl
@@ -0,0 +1,45 @@
+#!/usr/bin/env cwl-runner
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements. See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership. The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License. You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied. See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+cwlVersion: v1.0
+class: Workflow
+
+inputs:
+  name: string
+
+outputs: []
+
+steps:
+  step1:
+    run:
+      class: CommandLineTool
+      baseCommand: echo
+      inputs:
+       message:
+         type: string
+
+         inputBinding:
+           position: 1
+      outputs: []
+
+    inputs:
+      - id: text
+        source: "#x/name"
+
+    outputs: []
diff --git a/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestParser.java b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestParser.java
index 4e3e69e..20c494c 100644
--- a/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestParser.java
+++ b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestParser.java
@@ -77,15 +77,6 @@
     }
 
     @Test
-    public void testParseOutputs() throws Exception {
-
-        NamedSet<OutputWorkflowPort> workflowOutputs = workflow.getOutputPorts();
-        NamedSet<OutputWorkflowPort> expectedOutputs = new NamedSet<>();
-
-        assertEquals(expectedOutputs, workflowOutputs);
-    }
-
-    @Test
     public void testParseProcessors() throws Exception {
 
         workflow.setParent(null);
diff --git a/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestWorkflowProcess.java b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestWorkflowProcess.java
new file mode 100644
index 0000000..015ded4
--- /dev/null
+++ b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestWorkflowProcess.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.taverna.scufl2.cwl;
+
+
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import org.yaml.snakeyaml.Yaml;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.DataLink;
+
+import org.apache.taverna.scufl2.api.common.NamedSet;
+
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+
+
+public class TestWorkflowProcess {
+    private static final String HELLO_WORLD_CWL = "/hello_world.cwl";
+    private static final String WORKFLOW_WITH_COMMAND = "/workflow_with_command.cwl";
+
+    private static JsonNode cwlFile;
+
+    public JsonNode loadYamlFile(String filename) {
+
+        Yaml reader = new Yaml();
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode node = mapper.valueToTree(reader.load(TestWorkflowProcess.class.getResourceAsStream(filename)));
+
+        return node;
+    }
+
+    @Test
+    public void testWorkflowInputs() {
+        cwlFile = loadYamlFile(HELLO_WORLD_CWL);
+        WorkflowProcess workflow = new WorkflowProcess(cwlFile);
+        workflow.parseInputs();
+
+        Map<String, InputWorkflowPort> expected = new HashMap<>();
+        InputWorkflowPort expectedInput = new InputWorkflowPort();
+        expectedInput.setName("name");
+        expected.put("name", expectedInput);
+
+        assertEquals(expected, workflow.getWorkflowInputs());
+    }
+
+    @Test
+    public void testWorkflowOutputs() {
+        cwlFile = loadYamlFile(HELLO_WORLD_CWL);
+        WorkflowProcess workflow = new WorkflowProcess(cwlFile);
+        workflow.parseOutputs();
+
+        Map<String, OutputWorkflowPort> expected = new HashMap<>();
+        OutputWorkflowPort expectedOutput = new OutputWorkflowPort();
+        expectedOutput.setName("output1");
+        expected.put("output1", expectedOutput);
+
+        assertEquals(expected, workflow.getWorkflowOutputs());
+    }
+
+    @Test
+    public void testWorkflowSteps() {
+        cwlFile = loadYamlFile(WORKFLOW_WITH_COMMAND);
+        WorkflowProcess workflow = new WorkflowProcess(cwlFile);
+
+    }
+
+}
diff --git a/taverna-scufl2-cwl/src/test/resources/hello_world.cwl b/taverna-scufl2-cwl/src/test/resources/hello_world.cwl
index 2172578..4207afa 100644
--- a/taverna-scufl2-cwl/src/test/resources/hello_world.cwl
+++ b/taverna-scufl2-cwl/src/test/resources/hello_world.cwl
@@ -23,7 +23,8 @@
 inputs:
   name: string
 
-outputs: []
+outputs:
+  output1: string
 
 steps:
   step1: