Support nested workflows by parsing processes recursively

Merge commit 'refs/pull/39/head' of github.com:apache/incubator-taverna-language into cwlparser
Merges #39
Contributed by @haouech, ICLA on file.

Signed-off-by: Stian Soiland-Reyes <stain@apache.org>
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 382208d..63e70d1 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
@@ -35,11 +35,10 @@
 public class CWLParser {
 
     private JsonNode cwlFile;
-    private YAMLHelper yamlHelper;
+    private YAMLHelper yamlHelper = new YAMLHelper();
 
     public CWLParser(JsonNode cwlFile) {
         this.cwlFile = cwlFile;
-        this.yamlHelper = new YAMLHelper();
     }
 
     public Set<Step> parseSteps() {
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/CWLReader.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/CWLReader.java
new file mode 100644
index 0000000..de9d799
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/CWLReader.java
@@ -0,0 +1,62 @@
+/*
+ * 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 static java.util.Collections.singleton;
+
+import java.io.*;
+import java.util.Set;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.io.WorkflowBundleReader;
+import org.apache.taverna.scufl2.api.io.ReaderException;
+
+
+public class CWLReader implements WorkflowBundleReader {
+
+    public static final String CWL_TYPE = "text/cwl";
+
+    @Override
+    public Set<String> getMediaTypes() {
+        return singleton(CWL_TYPE);
+    }
+
+    @Override
+    public WorkflowBundle readBundle(File bundleFile, String mediaType) throws ReaderException, IOException {
+        try (BufferedInputStream is = new BufferedInputStream(
+                new FileInputStream(bundleFile))) {
+            Converter converter = new Converter();
+            return converter.buildWorkflowBundle(new WorkflowProcess(is));
+        }
+    }
+
+    @Override
+    public WorkflowBundle readBundle(InputStream inputStream, String mediaType) throws ReaderException, IOException {
+        Converter converter = new Converter();
+        return converter.buildWorkflowBundle(new WorkflowProcess(inputStream));
+    }
+
+    @Override
+    public String guessMediaTypeForSignature(byte[] firstBytes) {
+        if (new String(firstBytes)
+                .contains("cwlVersion"))
+            return CWL_TYPE;
+        return null;
+    }
+}
\ No newline at end of file
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 eff7ef6..5cfd9ce 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
@@ -25,6 +25,7 @@
 
 import org.apache.taverna.scufl2.api.core.Workflow;
 import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
 
 import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
 import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
@@ -62,17 +63,17 @@
         Processor processor = new Processor(null, step.getId());
         // Convert input ports
         Set<InputProcessorPort> processorInputs = new HashSet<>();
-        Set<StepInput> inputs = step.getInputs();
-        for(StepInput input: inputs) {
-            InputProcessorPort port = new InputProcessorPort(processor, input.getId());
+        Set<InputPort> inputs = step.getInputs();
+        for(InputPort input: inputs) {
+            InputProcessorPort port = new InputProcessorPort(processor, input.getName());
             processorInputs.add(port);
         }
         processor.setInputPorts(processorInputs);
         // Convert output ports
         Set<OutputProcessorPort> processorOutputs = new HashSet<>();
-        Set<StepOutput> outputs = step.getOutputs();
-        for(StepOutput output: outputs) {
-            OutputProcessorPort port = new OutputProcessorPort(processor, output.getId());
+        Set<OutputPort> outputs = step.getOutputs();
+        for(OutputPort output: outputs) {
+            OutputProcessorPort port = new OutputProcessorPort(processor, output.getName());
             processorOutputs.add(port);
         }
         processor.setOutputPorts(processorOutputs);
@@ -81,10 +82,6 @@
     }
 
     public JsonNode convertWorkflowProcessToJsonNode(WorkflowProcess workflow) {
-        Map<String, Processor> workflowProcessors = workflow.getWorkflowProcessors();
-        Map<String, InputProcessorPort> processorInputs = workflow.getProcessorInputs();
-        Map<String, OutputProcessorPort> processorOutputs = workflow.getProcessorOutputs();
-
         ObjectNode result = jsonNodeFactory.objectNode();
         ObjectNode inputs = convertInputWorkflows(workflow.getWorkflowInputs());
         ObjectNode outputs = convertOutputWorkflows(workflow.getWorkflowOutputs());
@@ -162,4 +159,77 @@
 
         return port;
     }
+
+    public WorkflowBundle buildWorkflowBundle(Process process) {
+        WorkflowBundle bundle = new WorkflowBundle();
+        if(!(process instanceof WorkflowProcess)) {
+            throw new UnsupportedOperationException("WorkflowBundle is not created without an initial workflow yet");
+        }
+        Workflow workflow = convertWorkflowProcess((WorkflowProcess) process, bundle);
+        workflow.setParent(bundle);
+
+        return bundle;
+    }
+
+    public Workflow convertWorkflowProcess(WorkflowProcess workflowProcess, WorkflowBundle bundle) {
+        Workflow workflow = new Workflow();
+        Set<InputWorkflowPort> inputs = new HashSet<>(workflowProcess.getWorkflowInputs().values());
+        Set<OutputWorkflowPort> outputs = new HashSet<>(workflowProcess.getWorkflowOutputs().values());
+        workflow.setInputPorts(inputs);
+        workflow.setOutputPorts(outputs);
+
+        for(Process process: workflowProcess.getProcesses()) {
+            if(process instanceof WorkflowProcess) {
+                Workflow childWorkflow = convertWorkflowProcess((WorkflowProcess) process, bundle); // TODO: Add nested relationship
+                bundle.getWorkflows().add(childWorkflow);
+            } else if(process instanceof CommandLineTool) {
+                Processor processor = convertCommandLineTool((CommandLineTool) process);
+                workflow.getProcessors().add(processor);
+            } else {
+                assert(process instanceof Reference);
+                Processor processor = convertReference((Reference) process);
+                workflow.getProcessors().add(processor);
+            }
+        }
+
+        return workflow;
+    }
+
+    public Processor convertCommandLineTool(CommandLineTool command) {
+        Processor processor = new Processor(null, command.getBaseCommand());
+
+        processor.setInputPorts(convertInputProcessPorts(command.getInputPorts()));
+        processor.setOutputPorts(convertOutputProcessPorts(command.getOutputPorts()));
+
+        return processor;
+    }
+
+    public Processor convertReference(Reference reference) {
+        Processor processor = new Processor(null, reference.getSource());
+
+        processor.setInputPorts(convertInputProcessPorts(reference.getInputPorts()));
+        processor.setOutputPorts(convertOutputProcessPorts(reference.getOutputPorts()));
+
+        return processor;
+    }
+
+    public Set<InputProcessorPort> convertInputProcessPorts(Set<InputPort> inputPorts) {
+        Set<InputProcessorPort> processorInputPorts = new HashSet<>();
+
+        for(InputPort port: inputPorts) {
+            processorInputPorts.add(new InputProcessorPort(null, port.getName()));
+        }
+
+        return processorInputPorts;
+    }
+
+    public Set<OutputProcessorPort> convertOutputProcessPorts(Set<OutputPort> outputPorts) {
+        Set<OutputProcessorPort> processorOutputPorts = new HashSet<>();
+
+        for(OutputPort port: outputPorts) {
+            processorOutputPorts.add(new OutputProcessorPort(null, port.getName()));
+        }
+
+        return processorOutputPorts;
+    }
 }
\ No newline at end of file
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
deleted file mode 100644
index e5f7159..0000000
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/Main.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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;
-import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-
-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);
-        printAsYaml(node);
-    }
-
-    private static void printAsYaml(JsonNode node) {
-        try {
-            String yaml = new YAMLMapper().writeValueAsString(node);
-            System.out.println("YAML DATA");
-            System.out.println(yaml);
-        } catch (JsonProcessingException e) {
-            System.err.println("Error writing JsonNode to YAML");
-        }
-    }
-}
\ 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 b18dc54..2680bf4 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
@@ -53,38 +53,24 @@
 
     private static final String FILE_NAME = "/hello_world.cwl";
     private CWLParser cwlParser;
-    private Converter converter;
+    private Converter converter = new Converter();
 
-    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 Map<String, InputWorkflowPort> workflowInputs = new HashMap<>();
+    private Map<String, OutputWorkflowPort> workflowOutputs = new HashMap<>();
+    private Map<String, Processor> workflowProcessors = new HashMap<>();
+    private Map<String, InputProcessorPort> processorInputs = new HashMap<>();
+    private Map<String, OutputProcessorPort> processorOutputs = new HashMap<>();
+    private Set<DataLink> dataLinks = new HashSet<DataLink>();
 
     public WorkflowParser() {
         Yaml reader = new Yaml();
         ObjectMapper mapper = new ObjectMapper();
         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) {
         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 Workflow buildWorkflow() {
@@ -92,7 +78,7 @@
         parseOutputs();
         Set<Step> cwlSteps = cwlParser.parseSteps();
         parseProcessors(cwlSteps);
-        parseDataLinks(cwlSteps);
+//        parseDataLinks(cwlSteps);
 
         Workflow workflow = new Workflow();
         Set<InputWorkflowPort> inputs = new HashSet<>(workflowInputs.values());
@@ -151,20 +137,20 @@
             Processor processor = converter.convertStepToProcessor(step);
             workflowProcessors.put(step.getId(), processor);
 
-            for(StepInput stepInput: step.getInputs()) {
-                InputProcessorPort processorPort = new InputProcessorPort(processor, stepInput.getId());
-                processorInputs.put(stepInput.getId(), processorPort);
+            for(InputPort stepInput: step.getInputs()) {
+                InputProcessorPort processorPort = new InputProcessorPort(processor, stepInput.getName());
+                processorInputs.put(stepInput.getName(), processorPort);
             }
-            for(StepOutput stepOutput: step.getOutputs()) {
-                OutputProcessorPort processorPort = new OutputProcessorPort(processor, stepOutput.getId());
-                processorOutputs.put(stepOutput.getId(), processorPort);
+            for(OutputPort stepOutput: step.getOutputs()) {
+                OutputProcessorPort processorPort = new OutputProcessorPort(processor, stepOutput.getName());
+                processorOutputs.put(stepOutput.getName(), processorPort);
             }
         }
     }
 
     public void parseDataLinks(Set<Step> cwlSteps) {
         for(Step step: cwlSteps) {
-            for(StepInput stepInput: step.getInputs()) {
+            for(InputPort stepInput: step.getInputs()) {
                 String[] sourcePath = stepInput.getSource().split("/");
                 String source = sourcePath[sourcePath.length-1];
                 source = source.replace("#", "");
@@ -177,7 +163,7 @@
                 if(sender == null) {
                     throw new NullPointerException("Cannot find sender port with name: " + source);
                 }
-                String receiverId = stepInput.getId();
+                String receiverId = stepInput.getName();
                 ReceiverPort receiver = workflowOutputs.get(receiverId);
                 if(receiver == null) {
                     receiver = processorInputs.get(receiverId);
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 c9b10dc..1b016f8 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
@@ -121,7 +121,7 @@
                 JsonNode runNode = stepNode.get(RUN);
                 Process run = ProcessFactory.createProcess(runNode);
                 run.parse();  // Recursively parse nested process
-                Set<StepInput> inputs = processStepInput(stepNode.get(INPUTS));
+                Set<InputPort> inputs = processStepInput(stepNode.get(INPUTS));
                 step.setId(id);
                 step.setRun(run);
                 step.setInputs(inputs);
@@ -141,7 +141,7 @@
                     run.parse();
                     step.setRun(run);
                 }
-                Set<StepInput> inputs = processStepInput(value.get(INPUTS));
+                Set<InputPort> inputs = processStepInput(value.get(INPUTS));
                 step.setId(id);
                 step.setInputs(inputs);
 
@@ -152,9 +152,9 @@
         return result;
     }
 
-    private Set<StepInput> processStepInput(JsonNode inputs) {
+    private Set<InputPort> processStepInput(JsonNode inputs) {
 
-        Set<StepInput> result = new HashSet<>();
+        Set<InputPort> result = new HashSet<>();
         if(inputs == null) {
             return result;
         }
@@ -164,7 +164,7 @@
                 String id = input.get(ID).asText();
                 String source = input.get(SOURCE).asText();
 
-                result.add(new StepInput(id, source));
+                result.add(new InputPort(id, source));
             }
         } else if (inputs.isObject()) {
             Iterator<Entry<String, JsonNode>> iterator = inputs.fields();
@@ -174,7 +174,7 @@
                 String id = entry.getKey();
                 String source = entry.getValue().get(SOURCE).asText();
 
-                result.add(new StepInput(id, source));
+                result.add(new InputPort(id, source));
             }
         }
         return result;
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
index 450304a..dc5e16d 100644
--- 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
@@ -28,7 +28,7 @@
 import org.apache.taverna.scufl2.api.port.InputProcessorPort;
 import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
 
-public class CommandLineTool implements Process {
+public class CommandLineTool extends Process {
 
     private final static String BASE_COMMAND = "baseCommand";
     private final static String ID = "id";
@@ -39,14 +39,12 @@
     private JsonNode node;
 
     private String baseCommand = null;
-    private Map<String, InputProcessorPort> processorInputs;
-    private Map<String, OutputProcessorPort> processorOutputs;
+    private Map<String, InputProcessorPort> processorInputs = new HashMap<>();
+    private Map<String, OutputProcessorPort> processorOutputs = new HashMap<>();
 
     public CommandLineTool(JsonNode node) {
         this.node = node;
         this.cwlParser = new CWLParser(node);
-        this.processorInputs = new HashMap<>();
-        this.processorOutputs = new HashMap<>();
         this.parse();
     }
 
@@ -57,6 +55,7 @@
     }
 
     public void parseInputs() {
+        // TODO: Set the processor port depth from the CWL type
         Set<PortDetail> cwlInputs = cwlParser.parseInputs();
         for(PortDetail detail: cwlInputs) {
             String portId = detail.getId();
@@ -75,4 +74,8 @@
             processorOutputs.put(portId, port);
         }
     }
+
+    public String getBaseCommand() {
+        return baseCommand;
+    }
 }
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/StepInput.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/InputPort.java
similarity index 76%
rename from taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/StepInput.java
rename to taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/InputPort.java
index 0432272..1d1522d 100644
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/StepInput.java
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/InputPort.java
@@ -19,36 +19,35 @@
 
 package org.apache.taverna.scufl2.cwl;
 
-import java.util.*;
+public class InputPort {
 
-public class StepInput {
+    private String name;
 
-    private String id;
     private String source;
 
-    public StepInput() {
-        this.id = null;
-        this.source = null;
+    public InputPort() {
+        this.name = "";
+        this.source = "";
     }
 
-    public StepInput(String id, String source) {
-        this.id = id;
+    public InputPort(String name, String source) {
+        this.name = name;
         this.source = source;
     }
 
-    public void setId(String id) {
-        this.id = id;
+    public String getName() {
+        return this.name;
     }
 
-    public String getId() {
-        return id;
-    }
-
-    public void setSource(String source) {
-        this.source = source;
+    public void setName(String name) {
+        this.name = name;
     }
 
     public String getSource() {
         return source;
     }
+
+    public void setSource(String source) {
+        this.source = source;
+    }
 }
\ 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
deleted file mode 100644
index e69de29..0000000
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/Main.java
+++ /dev/null
diff --git a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/StepOutput.java b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/OutputPort.java
similarity index 75%
rename from taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/StepOutput.java
rename to taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/OutputPort.java
index 145fa2d..c865e9c 100644
--- a/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/StepOutput.java
+++ b/taverna-scufl2-cwl/src/main/java/org/apache/taverna/scufl2/cwl/components/OutputPort.java
@@ -19,23 +19,23 @@
 
 package org.apache.taverna.scufl2.cwl;
 
-public class StepOutput {
+public class OutputPort {
 
-    private String id;
+    private String name;
 
-    public StepOutput() {
-        this.id = null;
+    public OutputPort() {
+        this.name = "";
     }
 
-    public StepOutput(String id) {
-        this.id = id;
+    public OutputPort(String name) {
+        this.name = name;
     }
 
-    public void setId(String id) {
-        this.id = id;
+    public String getName() {
+        return this.name;
     }
 
-    public String getId() {
-        return id;
+    public void setName(String name) {
+        this.name = name;
     }
 }
\ No newline at end of file
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
index 70e3669..991df0a 100644
--- 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
@@ -19,15 +19,31 @@
 
 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;
+import java.util.Set;
 
-public interface Process {
+public abstract class Process {
 
-    public void parse();
+    private Set<InputPort> inputPorts = new HashSet<>();
+    private Set<OutputPort> outputPorts = new HashSet<>();
+
+
+    public abstract void parse();
+
+    public Set<InputPort> getInputPorts() {
+        return this.inputPorts;
+    }
+
+    public void setInputPorts(Set<InputPort> inputs) {
+        this.inputPorts = inputs;
+    }
+
+    public Set<OutputPort> getOutputPorts() {
+        return this.outputPorts;
+    }
+
+    public void setOutputPorts(Set<OutputPort> outputs) {
+        this.outputPorts = outputs;
+    }
 
 }
\ 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
index 7a25744..9d5d0c0 100644
--- 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
@@ -21,7 +21,7 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 
-public class Reference implements Process {
+public class Reference extends Process {
 
     private String source;
 
@@ -40,4 +40,8 @@
     public String toString() {
         return source;
     }
+
+    public String getSource() {
+        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 cdcd46e..80ccf03 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
@@ -30,8 +30,8 @@
     private String id;
     private Process run;
 
-    private Set<StepInput> inputs;
-    private Set<StepOutput> outputs;
+    private Set<InputPort> inputs;
+    private Set<OutputPort> outputs;
 
     public Step() {
         inputs = new HashSet<>();
@@ -54,27 +54,27 @@
         this.run = run;
     }
 
-    public void addInput(String id, String source) {
-        inputs.add(new StepInput(id, source));
+    public void addInput(String name, String source) {
+        inputs.add(new InputPort(name, source));
     }
 
-    public void setInputs(Set<StepInput> inputs) {
+    public void setInputs(Set<InputPort> inputs) {
         this.inputs = inputs;
     }
 
-    public Set<StepInput> getInputs() {
+    public Set<InputPort> getInputs() {
         return inputs;
     }
 
-    public void addOutput(String id) {
-        outputs.add(new StepOutput(id));
+    public void addOutput(String name) {
+        outputs.add(new OutputPort(name));
     }
 
-    public void setOutputs(Set<StepOutput> outputs) {
+    public void setOutputs(Set<OutputPort> outputs) {
         this.outputs = outputs;
     }
 
-    public Set<StepOutput> getOutputs() {
+    public Set<OutputPort> getOutputs() {
         return outputs;
     }
 
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
index b41d054..a8e6607 100644
--- 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
@@ -19,20 +19,19 @@
 
 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 java.io.InputStream;
 
+import org.yaml.snakeyaml.Yaml;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 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;
@@ -40,33 +39,35 @@
 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 {
+public class WorkflowProcess extends 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 Map<String, InputWorkflowPort> workflowInputs = new HashMap<>();
+    private Map<String, OutputWorkflowPort> workflowOutputs = new HashMap<>();
+    private Map<String, Processor> workflowProcessors = new HashMap<>();
+    private Map<String, InputProcessorPort> processorInputs = new HashMap<>();
+    private Map<String, OutputProcessorPort> processorOutputs = new HashMap<>();
+    private Set<DataLink> dataLinks = new HashSet<>();
 
-    private Converter converter;
+    private Set<Process> processes = new HashSet<>();
+
+    private Converter converter = new Converter();
+
+    public WorkflowProcess(InputStream stream) {
+
+        Yaml reader = new Yaml();
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode node = mapper.valueToTree(reader.load(stream));
+
+        cwlParser = new CWLParser(node);
+        this.parse();
+    }
 
     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();
     }
 
@@ -74,44 +75,7 @@
         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");
-        }
+        this.processes = parseProcessors(cwlSteps);
     }
 
     public void parseInputs() {
@@ -132,27 +96,40 @@
         }
     }
 
-    public void parseProcessors(Set<Step> cwlSteps) {
+    public Set<Process> parseProcessors(Set<Step> cwlSteps) {
+        Set<Process> result = new HashSet<>();
+
         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);
-            }
+            Process process = step.getRun();
+            process.setInputPorts(step.getInputs());
+            process.setOutputPorts(step.getOutputs());
+            result.add(process);
         }
+
+        return result;
     }
 
+//    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()) {
+            for(InputPort stepInput: step.getInputs()) {
                 String[] sourcePath = stepInput.getSource().split("/");
                 String source = sourcePath[sourcePath.length-1];
                 source = source.replace("#", "");
@@ -165,7 +142,7 @@
                 if(sender == null) {
                     throw new NullPointerException("Cannot find sender port with name: " + source);
                 }
-                String receiverId = stepInput.getId();
+                String receiverId = stepInput.getName();
                 ReceiverPort receiver = workflowOutputs.get(receiverId);
                 if(receiver == null) {
                     receiver = processorInputs.get(receiverId);
@@ -227,4 +204,12 @@
     public void setDataLinks(Set<DataLink> dataLinks) {
         this.dataLinks = dataLinks;
     }
+
+    public Set<Process> getProcesses() {
+        return processes;
+    }
+
+    public void setProcesses(Set<Process> processes) {
+        this.processes = processes;
+    }
 }
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleReader b/taverna-scufl2-cwl/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleReader
new file mode 100644
index 0000000..607d8b7
--- /dev/null
+++ b/taverna-scufl2-cwl/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleReader
@@ -0,0 +1 @@
+org.apache.taverna.scufl2.cwl.CWLReader
\ No newline at end of file
diff --git a/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestConverting.java b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestConverting.java
new file mode 100644
index 0000000..2b34a3c
--- /dev/null
+++ b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestConverting.java
@@ -0,0 +1,90 @@
+/*
+ * 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.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import org.yaml.snakeyaml.Yaml;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.core.Processor;
+
+public class TestConverting {
+    private static final String HELLO_WORLD_CWL = "/hello_world.cwl";
+    private static final String WORKFLOW_WITH_COMMAND = "/workflow_with_command.cwl";
+    private static final String WORKFLOW_WITH_WORKFLOW = "/workflow_with_workflow.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 testWorkflowBundleWithOneWorkflow() {
+        this.cwlFile = loadYamlFile(HELLO_WORLD_CWL);
+
+        WorkflowProcess workflow = (WorkflowProcess) ProcessFactory.createProcess(cwlFile);
+        Converter converter = new Converter();
+        WorkflowBundle workflowBundle = converter.buildWorkflowBundle(workflow);
+
+        assertEquals(workflowBundle.getWorkflows().size(), 1);
+    }
+
+    @Test
+    public void testWorkflowBundleWithMultipleWorkflows() {
+        this.cwlFile = loadYamlFile(WORKFLOW_WITH_WORKFLOW);
+
+        WorkflowProcess workflow = (WorkflowProcess) ProcessFactory.createProcess(cwlFile);
+        Converter converter = new Converter();
+        WorkflowBundle workflowBundle = converter.buildWorkflowBundle(workflow);
+
+        assertEquals(workflowBundle.getWorkflows().size(), 2);
+    }
+
+    @Test
+    public void testConvertWorkflowProcessToWorkflow() {
+        this.cwlFile = loadYamlFile(HELLO_WORLD_CWL);
+
+        WorkflowProcess workflowProcess = (WorkflowProcess) ProcessFactory.createProcess(cwlFile);
+        WorkflowBundle bundle = new WorkflowBundle();
+        Converter converter = new Converter();
+        Workflow workflow = converter.convertWorkflowProcess(workflowProcess, bundle);
+        assertEquals(1, workflow.getProcessors().size());
+        assertEquals(1, workflow.getInputPorts().size());
+        assertEquals(1, workflow.getOutputPorts().size());
+
+        Processor processor = workflow.getProcessors().iterator().next();
+        assertEquals(1, processor.getInputPorts().size());
+        assertEquals(0, processor.getOutputPorts().size());
+    }
+}
diff --git a/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestWorkflowNesting.java b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestWorkflowNesting.java
new file mode 100644
index 0000000..d0f3e88
--- /dev/null
+++ b/taverna-scufl2-cwl/src/test/java/org/apache/taverna/scufl2/cwl/TestWorkflowNesting.java
@@ -0,0 +1,95 @@
+/*
+ * 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.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import org.yaml.snakeyaml.Yaml;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import org.apache.taverna.scufl2.api.core.Workflow;
+
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+
+import org.apache.taverna.scufl2.api.io.WorkflowBundleIO;
+import org.apache.taverna.scufl2.api.io.WriterException;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+
+public class TestWorkflowNesting {
+    private static final String HELLO_WORLD_CWL = "/hello_world.cwl";
+    private static final String WORKFLOW_WITH_COMMAND = "/workflow_with_command.cwl";
+    private static final String WORKFLOW_WITH_WORKFLOW = "/workflow_with_workflow.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 testWorkflowWithProcessor() {
+        JsonNode cwlFile = loadYamlFile(WORKFLOW_WITH_COMMAND);
+
+        WorkflowProcess workflow = (WorkflowProcess) ProcessFactory.createProcess(cwlFile);
+        Set<Process> processes = workflow.getProcesses();
+        assertEquals(processes.size(), 1);
+        Process child = processes.iterator().next();
+        assert(child instanceof CommandLineTool);
+        assertEquals(((CommandLineTool) child).getBaseCommand(), "echo");
+    }
+
+    @Test
+    public void testWorkflowWithWorkflow() {
+        JsonNode cwlFile = loadYamlFile(WORKFLOW_WITH_WORKFLOW);
+
+        WorkflowProcess workflow = (WorkflowProcess) ProcessFactory.createProcess(cwlFile);
+        Set<Process> processes = workflow.getProcesses();
+        assert(processes.size() == 1);
+        Process child = processes.iterator().next();
+        assert(child instanceof WorkflowProcess);
+        WorkflowProcess childWorkflow = (WorkflowProcess) child;
+        assertEquals(childWorkflow.getProcesses().size(), 1);
+
+        Process reference = childWorkflow.getProcesses().iterator().next();
+
+        assert(reference instanceof Reference);
+        assertEquals(((Reference) reference).toString(), "example.cwl");
+    }
+}
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
index 015ded4..4b3bba7 100644
--- 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
@@ -19,32 +19,33 @@
 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.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 com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
 
 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;
 
+import org.apache.taverna.scufl2.api.io.WorkflowBundleIO;
+import org.apache.taverna.scufl2.api.io.WriterException;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
 
 public class TestWorkflowProcess {
     private static final String HELLO_WORLD_CWL = "/hello_world.cwl";
@@ -90,10 +91,49 @@
     }
 
     @Test
-    public void testWorkflowSteps() {
+    public void testProcessType() {
         cwlFile = loadYamlFile(WORKFLOW_WITH_COMMAND);
-        WorkflowProcess workflow = new WorkflowProcess(cwlFile);
+        Process workflow = ProcessFactory.createProcess(cwlFile);
 
+        assert(workflow instanceof WorkflowProcess);
+    }
+
+    @Test
+    public void testConvertWorkflowProcess() {
+        cwlFile = loadYamlFile(WORKFLOW_WITH_COMMAND);
+
+        Process workflow = ProcessFactory.createProcess(cwlFile);
+        workflow.parse();
+        Converter converter = new Converter();
+        JsonNode node = converter.convertWorkflowProcessToJsonNode((WorkflowProcess) workflow);
+        printAsYaml(node);
+    }
+
+    private static void printAsYaml(JsonNode node) {
+        try {
+            String yaml = new YAMLMapper().writeValueAsString(node);
+            System.out.println("YAML DATA");
+            System.out.println(yaml);
+        } catch (JsonProcessingException e) {
+            System.err.println("Error writing JsonNode to YAML");
+        }
+    }
+
+    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");
+        }
     }
 
 }
diff --git a/taverna-scufl2-cwl/src/main/resources/workflow_with_command.cwl b/taverna-scufl2-cwl/src/test/resources/workflow_with_command.cwl
similarity index 100%
rename from taverna-scufl2-cwl/src/main/resources/workflow_with_command.cwl
rename to taverna-scufl2-cwl/src/test/resources/workflow_with_command.cwl
diff --git a/taverna-scufl2-cwl/src/test/resources/worklflow2.cwl b/taverna-scufl2-cwl/src/test/resources/workflow_with_workflow.cwl
similarity index 81%
rename from taverna-scufl2-cwl/src/test/resources/worklflow2.cwl
rename to taverna-scufl2-cwl/src/test/resources/workflow_with_workflow.cwl
index fc5e846..58323c5 100644
--- a/taverna-scufl2-cwl/src/test/resources/worklflow2.cwl
+++ b/taverna-scufl2-cwl/src/test/resources/workflow_with_workflow.cwl
@@ -30,14 +30,18 @@
 steps:
   step1:
     run:
-      class: CommandLineTool
-      baseCommand: echo
+      class: Workflow
       inputs:
-        text:
-          type: string
-          inputBinding:
-            position: 1
-      outputs: []
+        name: string
+      outputs:
+        output1: string
+      steps:
+        step2:
+          run: example.cwl
+          inputs:
+            - id: text
+              source: "#x/name"
+          outputs: []
     in:
       text: message