You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/02/23 11:05:50 UTC

[65/79] incubator-taverna-language git commit: taverna-scufl2-rdfxml -> taverna-scufl2-wfbundle

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLSerializer.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLSerializer.java b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLSerializer.java
new file mode 100644
index 0000000..3ab7501
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLSerializer.java
@@ -0,0 +1,860 @@
+package org.apache.taverna.scufl2.rdfxml;
+/*
+ *
+ * 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.
+ *
+*/
+
+
+import static java.lang.Boolean.TRUE;
+import static java.util.logging.Level.FINE;
+import static java.util.logging.Level.WARNING;
+import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
+import static javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.taverna.scufl2.api.activity.Activity;
+import org.apache.taverna.scufl2.api.annotation.Annotation;
+import org.apache.taverna.scufl2.api.common.Typed;
+import org.apache.taverna.scufl2.api.common.URITools;
+import org.apache.taverna.scufl2.api.common.Visitor;
+import org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.api.configurations.Configuration;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.BlockingControlLink;
+import org.apache.taverna.scufl2.api.core.DataLink;
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.io.WriterException;
+import org.apache.taverna.scufl2.api.iterationstrategy.CrossProduct;
+import org.apache.taverna.scufl2.api.iterationstrategy.DotProduct;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyStack;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode;
+import org.apache.taverna.scufl2.api.iterationstrategy.PortNode;
+import org.apache.taverna.scufl2.api.port.InputActivityPort;
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputActivityPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.profiles.ProcessorBinding;
+import org.apache.taverna.scufl2.api.profiles.ProcessorInputPortBinding;
+import org.apache.taverna.scufl2.api.profiles.ProcessorOutputPortBinding;
+import org.apache.taverna.scufl2.api.profiles.Profile;
+import org.apache.taverna.scufl2.rdfxml.impl.NamespacePrefixMapperJAXB_RI;
+import org.apache.taverna.scufl2.ucfpackage.UCFPackage;
+import org.w3._1999._02._22_rdf_syntax_ns_.RDF;
+import org.w3._1999._02._22_rdf_syntax_ns_.Resource;
+import org.w3._1999._02._22_rdf_syntax_ns_.Type;
+import org.w3._2000._01.rdf_schema_.SeeAlso;
+import org.xml.sax.SAXException;
+
+import org.apache.taverna.scufl2.rdfxml.jaxb.Blocking;
+import org.apache.taverna.scufl2.rdfxml.jaxb.Control;
+import org.apache.taverna.scufl2.rdfxml.jaxb.DataLink.MergePosition;
+import org.apache.taverna.scufl2.rdfxml.jaxb.DataLinkEntry;
+import org.apache.taverna.scufl2.rdfxml.jaxb.GranularPortDepth;
+import org.apache.taverna.scufl2.rdfxml.jaxb.IterationStrategyStack.IterationStrategies;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ObjectFactory;
+import org.apache.taverna.scufl2.rdfxml.jaxb.PortDepth;
+import org.apache.taverna.scufl2.rdfxml.jaxb.PortNode.DesiredDepth;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ProcessorBinding.ActivityPosition;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ProcessorBinding.InputPortBinding;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ProcessorBinding.OutputPortBinding;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ProductOf;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ProfileDocument;
+import org.apache.taverna.scufl2.rdfxml.jaxb.SeeAlsoType;
+import org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundleDocument;
+import org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowDocument;
+
+public class RDFXMLSerializer {
+	private static final String DOT_RDF = ".rdf";
+	protected static final URI OA = URI.create("http://www.w3.org/ns/oa#");
+	protected static final URI PAV = URI.create("http://purl.org/pav/");
+	private static boolean warnedOnce = false;
+	
+	public class ProfileSerialisationVisitor implements Visitor {
+		private org.apache.taverna.scufl2.rdfxml.jaxb.Activity activity;
+		private final ProfileDocument doc;
+		private org.apache.taverna.scufl2.rdfxml.jaxb.Profile profileElem;
+		private Profile profile;
+		private org.apache.taverna.scufl2.rdfxml.jaxb.ProcessorBinding processorBindingElem;
+
+		public ProfileSerialisationVisitor(ProfileDocument doc) {
+			this.doc = doc;
+		}
+
+		private void activity(Activity node) {
+			activity = objectFactory.createActivity();
+			activity.setAbout(uri(node));
+			activity.setType(type(node));
+			activity.setName(node.getName());
+			doc.getAny().add(activity);
+		}
+
+		private void configuration(Configuration node) {
+			org.apache.taverna.scufl2.rdfxml.jaxb.Configuration configuration = objectFactory
+					.createConfiguration();
+			configuration.setConfigure(resource(uri(node.getConfigures())));
+			configuration.setName(node.getName());
+			configuration.setType(type(node));
+			
+			URI configUri = uriTools.relativeUriForBean(node, profile);
+            String jsonPath = configUri.toString().replaceFirst("/$", ".json");
+
+			URI profilePath = uriTools.relativeUriForBean(profile, profile.getParent());
+			
+			String bundlePath = profilePath + jsonPath;
+			
+			UCFPackage bundle = profile.getParent().getResources();
+			try {
+                bundle.addResource(node.getJsonAsString(), bundlePath, "application/json");
+            } catch (IOException e) {
+                logger.log(WARNING, "Can't save JSON to " + bundlePath, e);
+            }
+			configuration.setAbout(configUri.toString());
+			
+			SeeAlso seeAlso = rdfsObjectFactory.createSeeAlso();
+			seeAlso.setResource(jsonPath);
+			configuration.setSeeAlso(seeAlso);
+            
+			// TODO: No way in API to mark non-activated configurations
+			profileElem.getActivateConfiguration().add(resource(uri(node)));
+			doc.getAny().add(configuration);
+		}
+
+		private GranularPortDepth granularPortDepth(Integer integer) {
+			if (integer == null)
+				return null;
+			GranularPortDepth p = objectFactory.createGranularPortDepth();
+			p.setValue(integer);
+			p.setDatatype(p.getDatatype());
+			return p;
+		}
+
+		private void inputActivityPort(InputActivityPort node) {
+			org.apache.taverna.scufl2.rdfxml.jaxb.InputActivityPort inputActivityPort = objectFactory
+					.createInputActivityPort();
+			inputActivityPort.setAbout(uri(node));
+			inputActivityPort.setName(node.getName());
+			inputActivityPort.setPortDepth(portDepth(node.getDepth()));
+
+			org.apache.taverna.scufl2.rdfxml.jaxb.Activity.InputActivityPort wrapper = objectFactory
+					.createActivityInputActivityPort();
+			wrapper.setInputActivityPort(inputActivityPort);
+			activity.getInputActivityPort().add(wrapper);
+		}
+
+		private void outputActivityPort(OutputActivityPort node) {
+			org.apache.taverna.scufl2.rdfxml.jaxb.OutputActivityPort outputActivityPort = objectFactory
+					.createOutputActivityPort();
+			outputActivityPort.setAbout(uri(node));
+			outputActivityPort.setName(node.getName());
+			outputActivityPort.setPortDepth(portDepth(node.getDepth()));
+			outputActivityPort.setGranularPortDepth(granularPortDepth(node
+					.getGranularDepth()));
+
+			org.apache.taverna.scufl2.rdfxml.jaxb.Activity.OutputActivityPort wrapper = objectFactory
+					.createActivityOutputActivityPort();
+			wrapper.setOutputActivityPort(outputActivityPort);
+			activity.getOutputActivityPort().add(wrapper);
+		}
+
+		private PortDepth portDepth(Integer integer) {
+			if (integer == null)
+				return null;
+			PortDepth p = objectFactory.createPortDepth();
+			p.setValue(integer);
+			p.setDatatype(p.getDatatype());
+			return p;
+		}
+
+		private void processorBinding(ProcessorBinding node) {
+			processorBindingElem = objectFactory.createProcessorBinding();
+			processorBindingElem.setAbout(uri(node));
+			processorBindingElem.setName(node.getName());
+			processorBindingElem.setBindActivity(resource(uri(node
+					.getBoundActivity())));
+			processorBindingElem.setBindProcessor(resource(uri(node
+					.getBoundProcessor())));
+			if (node.getActivityPosition() != null) {
+				ActivityPosition value = new ActivityPosition();
+				value.setDatatype(value.getDatatype());
+				value.setValue(node.getActivityPosition());
+				processorBindingElem.setActivityPosition(value);
+			}
+
+			profileElem.getProcessorBinding().add(resource(uri(node)));
+			doc.getAny().add(processorBindingElem);
+		}
+
+		private void processorInputPortBinding(ProcessorInputPortBinding node) {
+			org.apache.taverna.scufl2.rdfxml.jaxb.InputPortBinding inputBinding = objectFactory
+					.createInputPortBinding();
+			inputBinding.setAbout(uri(node));
+			inputBinding.setBindInputActivityPort(resource(uri(node
+					.getBoundActivityPort())));
+			inputBinding.setBindInputProcessorPort(resource(uri(node
+					.getBoundProcessorPort())));
+			InputPortBinding b = objectFactory
+					.createProcessorBindingInputPortBinding();
+			b.setInputPortBinding(inputBinding);
+			processorBindingElem.getInputPortBinding().add(b);
+		}
+
+		private void processorOutputPortBinding(ProcessorOutputPortBinding node) {
+			org.apache.taverna.scufl2.rdfxml.jaxb.OutputPortBinding outputBinding = objectFactory
+					.createOutputPortBinding();
+			outputBinding.setAbout(uri(node));
+			outputBinding.setBindOutputActivityPort(resource(uri(node
+					.getBoundActivityPort())));
+			outputBinding.setBindOutputProcessorPort(resource(uri(node
+					.getBoundProcessorPort())));
+			OutputPortBinding b = objectFactory
+					.createProcessorBindingOutputPortBinding();
+			b.setOutputPortBinding(outputBinding);
+			processorBindingElem.getOutputPortBinding().add(b);
+		}
+
+		private void profile(Profile node) {
+			profile = node;
+			profileElem = objectFactory.createProfile();
+			profileElem.setAbout(uri(node));
+			profileElem.setName(node.getName());
+			doc.getAny().add(profileElem);
+		}
+
+		private String uri(WorkflowBean node) {
+			return uriTools.relativeUriForBean(node, profile).toASCIIString();
+		}
+
+		@Override
+		public boolean visit(WorkflowBean node) {
+			if (node instanceof Profile)
+				profile((Profile) node);
+			else if (node instanceof Activity)
+				activity((Activity) node);
+			else if (node instanceof InputActivityPort)
+				inputActivityPort((InputActivityPort) node);
+			else if (node instanceof OutputActivityPort)
+				outputActivityPort((OutputActivityPort) node);
+			else if (node instanceof ProcessorBinding)
+				processorBinding((ProcessorBinding) node);
+			else if (node instanceof ProcessorInputPortBinding)
+				processorInputPortBinding((ProcessorInputPortBinding) node);
+			else if (node instanceof ProcessorOutputPortBinding)
+				processorOutputPortBinding((ProcessorOutputPortBinding) node);
+			else if (node instanceof Configuration)
+				configuration((Configuration) node);
+			else
+				throw new IllegalStateException("Unexpected node " + node);
+			return true;
+		}
+
+		@Override
+		public boolean visitEnter(WorkflowBean node) {
+			return visit(node);
+		}
+
+		@Override
+		public boolean visitLeave(WorkflowBean node) {
+			return true;
+		}
+	}
+
+	public class WorkflowSerialisationVisitor implements Visitor {
+		private final org.apache.taverna.scufl2.rdfxml.jaxb.Workflow workflow;
+		private org.apache.taverna.scufl2.rdfxml.jaxb.Processor proc;
+		private Workflow wf;
+		@SuppressWarnings("unused")
+		private org.apache.taverna.scufl2.rdfxml.jaxb.DispatchStack dispatchStack;
+		private org.apache.taverna.scufl2.rdfxml.jaxb.IterationStrategyStack iterationStrategyStack;
+		private IterationStrategies iterationStrategies;
+		private Stack<List<Object>> productStack;
+
+		public WorkflowSerialisationVisitor(
+				org.apache.taverna.scufl2.rdfxml.jaxb.Workflow workflow) {
+			this.workflow = workflow;
+		}
+
+		private GranularPortDepth makeGranularPortDepth(Integer granularDepth) {
+			if (granularDepth == null)
+				return null;
+			GranularPortDepth portDepth = objectFactory
+					.createGranularPortDepth();
+			portDepth.setValue(granularDepth);
+			portDepth.setDatatype(portDepth.getDatatype());
+			return portDepth;
+		}
+
+		private PortDepth makePortDepth(Integer depth) {
+			if (depth == null)
+				return null;
+			PortDepth portDepth = objectFactory.createPortDepth();
+			portDepth.setValue(depth);
+			portDepth.setDatatype(portDepth.getDatatype());
+			return portDepth;
+		}
+
+		private Resource makeResource(URI uri) {
+			Resource resource = rdfObjectFactory.createResource();
+			resource.setResource(uri.toASCIIString());
+			return resource;
+		}
+
+		@Override
+		public boolean visit(WorkflowBean node) {
+			if (node instanceof Workflow) {
+				wf = (Workflow) node;
+				workflow.setAbout("");
+				workflow.setName(wf.getName());
+
+				if (wf.getIdentifier() != null) {
+					Resource wfId = rdfObjectFactory.createResource();
+					wfId.setResource(wf.getIdentifier().toASCIIString());
+					workflow.setWorkflowIdentifier(wfId);
+				}
+			}
+						
+			URI uri = uriTools.relativeUriForBean(node, wf);
+
+			if (node instanceof InputWorkflowPort) {
+				InputWorkflowPort ip = (InputWorkflowPort) node;
+				org.apache.taverna.scufl2.rdfxml.jaxb.Workflow.InputWorkflowPort inP = objectFactory
+						.createWorkflowInputWorkflowPort();
+				org.apache.taverna.scufl2.rdfxml.jaxb.InputWorkflowPort inPort = objectFactory
+						.createInputWorkflowPort();
+				inP.setInputWorkflowPort(inPort);
+				inPort.setName(ip.getName());
+
+				URI portURI = uriTools.relativeUriForBean(ip, ip.getParent());
+				inPort.setAbout(portURI.toASCIIString());
+
+				PortDepth portDepth = makePortDepth(ip.getDepth());
+				inPort.setPortDepth(portDepth);
+				workflow.getInputWorkflowPort().add(inP);
+			}
+			if (node instanceof OutputWorkflowPort) {
+				OutputWorkflowPort op = (OutputWorkflowPort) node;
+				org.apache.taverna.scufl2.rdfxml.jaxb.Workflow.OutputWorkflowPort inP = objectFactory
+						.createWorkflowOutputWorkflowPort();
+				org.apache.taverna.scufl2.rdfxml.jaxb.OutputWorkflowPort outPort = objectFactory
+						.createOutputWorkflowPort();
+				inP.setOutputWorkflowPort(outPort);
+				outPort.setName(op.getName());
+
+				URI portURI = uriTools.relativeUriForBean(op, op.getParent());
+				outPort.setAbout(portURI.toASCIIString());
+				workflow.getOutputWorkflowPort().add(inP);
+			}
+			if (node instanceof Processor) {
+				Processor processor = (Processor) node;
+				org.apache.taverna.scufl2.rdfxml.jaxb.Workflow.Processor wfProc = objectFactory
+						.createWorkflowProcessor();
+				proc = objectFactory.createProcessor();
+				wfProc.setProcessor(proc);
+				proc.setName(processor.getName());
+				URI procUri = uriTools.relativeUriForBean(processor, wf);
+				proc.setAbout(procUri.toASCIIString());
+				wfProc.setProcessor(proc);
+				workflow.getProcessor().add(wfProc);
+			}
+			if (node instanceof InputProcessorPort) {
+				InputProcessorPort inPort = (InputProcessorPort) node;
+				org.apache.taverna.scufl2.rdfxml.jaxb.InputProcessorPort port = objectFactory
+						.createInputProcessorPort();
+				port.setAbout(uri.toASCIIString());
+				port.setName(inPort.getName());
+				port.setPortDepth(makePortDepth(inPort.getDepth()));
+				org.apache.taverna.scufl2.rdfxml.jaxb.Processor.InputProcessorPort inputProcessorPort = objectFactory
+						.createProcessorInputProcessorPort();
+				inputProcessorPort.setInputProcessorPort(port);
+				proc.getInputProcessorPort().add(inputProcessorPort);
+			}
+			if (node instanceof OutputProcessorPort) {
+				org.apache.taverna.scufl2.rdfxml.jaxb.OutputProcessorPort port;
+				OutputProcessorPort outPort = (OutputProcessorPort) node;
+				port = objectFactory.createOutputProcessorPort();
+				port.setAbout(uri.toASCIIString());
+				port.setName(outPort.getName());
+				port.setPortDepth(makePortDepth(outPort.getDepth()));
+				port.setGranularPortDepth(makeGranularPortDepth(outPort
+						.getGranularDepth()));
+
+				org.apache.taverna.scufl2.rdfxml.jaxb.Processor.OutputProcessorPort outputProcessorPort = objectFactory
+						.createProcessorOutputProcessorPort();
+				outputProcessorPort.setOutputProcessorPort(port);
+				proc.getOutputProcessorPort().add(outputProcessorPort);
+			}
+			if (node instanceof IterationStrategyStack) {
+				iterationStrategyStack = objectFactory
+						.createIterationStrategyStack();
+				iterationStrategyStack.setAbout(uri.toASCIIString());
+				org.apache.taverna.scufl2.rdfxml.jaxb.Processor.IterationStrategyStack processorIterationStrategyStack = objectFactory
+						.createProcessorIterationStrategyStack();
+				processorIterationStrategyStack
+						.setIterationStrategyStack(iterationStrategyStack);
+				proc.setIterationStrategyStack(processorIterationStrategyStack);
+				productStack = new Stack<List<Object>>();
+			}
+			if (node instanceof IterationStrategyTopNode
+					&& productStack.isEmpty()) {
+				iterationStrategies = objectFactory
+						.createIterationStrategyStackIterationStrategies();
+				iterationStrategyStack
+						.setIterationStrategies(iterationStrategies);
+				iterationStrategies.setParseType(iterationStrategies
+						.getParseType());
+				List<Object> dotProductOrCrossProduct = iterationStrategies
+						.getDotProductOrCrossProduct();
+				productStack.add(dotProductOrCrossProduct);
+			}
+			if (node instanceof CrossProduct) {
+				org.apache.taverna.scufl2.rdfxml.jaxb.CrossProduct crossProduct = objectFactory
+						.createCrossProduct();
+				crossProduct.setAbout(uri.toASCIIString());
+				productStack.peek().add(crossProduct);
+				ProductOf productOf = objectFactory.createProductOf();
+				productOf.setParseType(productOf.getParseType());
+				crossProduct.setProductOf(productOf);
+				productStack.add(crossProduct.getProductOf().getCrossProductOrDotProductOrPortNode());
+			}
+			if (node instanceof DotProduct) {
+				org.apache.taverna.scufl2.rdfxml.jaxb.DotProduct dotProduct = objectFactory
+						.createDotProduct();
+				dotProduct.setAbout(uri.toASCIIString());
+				productStack.peek().add(dotProduct);
+				ProductOf productOf = objectFactory.createProductOf();
+				productOf.setParseType(productOf.getParseType());
+				dotProduct.setProductOf(productOf);
+				productStack.add(dotProduct.getProductOf()
+						.getCrossProductOrDotProductOrPortNode());
+			}
+			if (node instanceof PortNode) {
+				PortNode portNode = (PortNode) node;
+				InputProcessorPort inPort = portNode.getInputProcessorPort();
+				URI portUri = uriTools.relativeUriForBean(inPort, wf);
+				org.apache.taverna.scufl2.rdfxml.jaxb.PortNode port = objectFactory.createPortNode();
+				port.setAbout(uri.toASCIIString());
+				if (portNode.getDesiredDepth() != null) {
+					DesiredDepth value = objectFactory.createPortNodeDesiredDepth();
+					value.setDatatype(value.getDatatype());
+					value.setValue(portNode.getDesiredDepth());
+					port.setDesiredDepth(value);
+				}
+				port.setIterateOverInputPort(makeResource(portUri));
+				productStack.peek().add(port);
+			}
+			if (node instanceof DataLink) {
+				DataLink dataLink = (DataLink) node;
+				org.apache.taverna.scufl2.rdfxml.jaxb.DataLink link = objectFactory
+						.createDataLink();
+				link.setAbout(uri.toASCIIString());
+				URI fromUri = uriTools.relativeUriForBean(
+						dataLink.getReceivesFrom(), wf);
+				URI toUri = uriTools.relativeUriForBean(dataLink.getSendsTo(),
+						wf);
+				link.setReceiveFrom(makeResource(fromUri));
+				link.setSendTo(makeResource(toUri));
+
+				if (dataLink.getMergePosition() != null) {
+					MergePosition value = objectFactory.createDataLinkMergePosition();
+					value.setValue(dataLink
+							.getMergePosition());
+					value.setDatatype(value.getDatatype());
+					link.setMergePosition(value);
+				}
+
+				DataLinkEntry linkEntry = objectFactory.createDataLinkEntry();
+				linkEntry.setDataLink(link);
+				workflow.getDatalink().add(linkEntry);
+			}
+			if (node instanceof BlockingControlLink) {
+				BlockingControlLink controlLink = (BlockingControlLink) node;
+				URI blockUri = uriTools.relativeUriForBean(
+						controlLink.getBlock(), wf);
+				URI untilUri = uriTools.relativeUriForBean(
+						controlLink.getUntilFinished(), wf);
+
+				Blocking blocking = objectFactory.createBlocking();
+				blocking.setAbout(uri.toASCIIString());
+				blocking.setBlock(makeResource(blockUri));
+				blocking.setUntilFinished(makeResource(untilUri));
+
+				Control control = objectFactory.createControl();
+				control.setBlocking(blocking);
+				workflow.getControl().add(control);
+			}
+
+			// TODO: Datalinks
+
+			return true;
+		}
+
+		@Override
+		public boolean visitEnter(WorkflowBean node) {
+			return visit(node);
+		}
+
+		@Override
+		public boolean visitLeave(WorkflowBean node) {
+			if (node instanceof IterationStrategyTopNode)
+				// Actually for any Cross/Dot product
+				productStack.pop();
+			return true;
+		}
+	}
+
+	protected synchronized static JAXBContext getJAxbContextStatic()
+			throws JAXBException {
+		if (jaxbContextStatic == null) {
+			Class<?>[] packages = { ObjectFactory.class,
+					org.w3._1999._02._22_rdf_syntax_ns_.ObjectFactory.class,
+					org.w3._2000._01.rdf_schema_.ObjectFactory.class };
+			jaxbContextStatic = JAXBContext.newInstance(packages);
+		}
+		return jaxbContextStatic;
+	}
+
+	public void annotation(final Annotation ann) {
+		URI wfBundleURI = uriTools.uriForBean(wfBundle);
+		URI annUri = uriTools.uriForBean(ann);
+		URI bodyURI = ann.getBody();
+		if (bodyURI == null || bodyURI.isAbsolute())
+			// Workaround with separate file for the annotation alone
+			bodyURI = annUri.resolve(uriTools.validFilename(ann.getName()) + DOT_RDF);
+		URI pathUri = uriTools.relativePath(wfBundleURI, bodyURI);			
+		if (ann.getBody() == null || ann.getBody().equals(wfBundleURI.resolve(pathUri)))
+			// Set the relative path
+			ann.setBody(pathUri);
+
+		// TODO: Add annotation to RO manifest
+		
+//		// Miniature OA description for now
+//		// See http://openannotation.org/spec/core/20130205/
+//		final PropertyResource annProv = new PropertyResource();
+//		annProv.setResourceURI(annUri);
+//		annProv.setTypeURI(OA.resolve("#Annotation"));
+//		
+//		if (ann.getAnnotatedAt() != null) {
+//			annProv.addProperty(OA.resolve("#annotedAt"),
+//					new PropertyLiteral(ann.getAnnotatedAt()));
+//		}
+//		if (ann.getSerializedAt() != null) {
+//			annProv.addProperty(OA.resolve("#serializedAt"),
+//					new PropertyLiteral(ann.getSerializedAt()));
+//		}
+//		
+//		if (ann.getAnnotatedBy() != null) {
+//			annProv.addPropertyReference(OA.resolve("#annotatedBy"),
+//					ann.getAnnotatedBy());
+//		}
+//		if (ann.getSerializedBy() != null) {
+//			annProv.addPropertyReference(OA.resolve("#serializedBy"),
+//					ann.getSerializedBy());
+//		}
+//		
+//		if (ann.getBody() != null) {
+//			annProv.addPropertyReference(OA.resolve("#hasBody"), ann.getBody());
+//		} else if (! ann.getBodyStatements().isEmpty()){						
+//			// FIXME: Hack - Our body is also the annotation!
+//			annProv.addPropertyReference(OA.resolve("#hasBody"), pathUri);
+//		}
+//		
+//		// CHECK: should this be a relative reference instead?
+//		annProv.addPropertyReference(OA.resolve("#hasTarget"), 
+//				uriTools.uriForBean(ann.getTarget()));					
+//		// Serialize the metadata
+//
+//	
+//		try {
+//			/*
+//			 * TODO: Serialize manually with nicer indentation/namespaces etc.,
+//			 * as done for our other RDF/XML documents
+//			 */
+//			wfBundle.getResources()
+//					.addResource(visitor.getDoc(), pathUri.toASCIIString(), APPLICATION_RDF_XML);
+//		} catch (IOException e) {
+//			logger.log(Level.WARNING, "Can't write annotation to " + pathUri, e);
+//		}
+		
+	}
+
+	private ObjectFactory objectFactory = new ObjectFactory();
+
+	private org.w3._2000._01.rdf_schema_.ObjectFactory rdfsObjectFactory = new org.w3._2000._01.rdf_schema_.ObjectFactory();
+	private org.w3._1999._02._22_rdf_syntax_ns_.ObjectFactory rdfObjectFactory = new org.w3._1999._02._22_rdf_syntax_ns_.ObjectFactory();
+	private URITools uriTools = new URITools();
+	private boolean usingSchema = false;
+
+	private WorkflowBundle wfBundle;
+
+	private JAXBContext jaxbContext;
+
+	private Map<WorkflowBean, URI> seeAlsoUris = new HashMap<>();
+	private static JAXBContext jaxbContextStatic;
+	private static Logger logger = Logger.getLogger(RDFXMLSerializer.class
+			.getCanonicalName());
+	public RDFXMLSerializer() {
+	}
+
+	public RDFXMLSerializer(WorkflowBundle wfBundle) {
+		setWfBundle(wfBundle);
+	}
+
+	public JAXBContext getJaxbContext() throws JAXBException {
+		if (jaxbContext == null)
+			return getJAxbContextStatic();
+		return jaxbContext;
+	}
+
+	public Marshaller getMarshaller() {
+		String schemaPath = "xsd/scufl2.xsd";
+		Marshaller marshaller;
+		try {
+			marshaller = getJaxbContext().createMarshaller();
+
+			if (isUsingSchema()) {
+				SchemaFactory schemaFactory = SchemaFactory
+						.newInstance(W3C_XML_SCHEMA_NS_URI);
+				Schema schema = schemaFactory.newSchema(getClass().getResource(
+						schemaPath));
+				// FIXME: re-enable schema
+				marshaller.setSchema(schema);
+			}
+			marshaller.setProperty(JAXB_FORMATTED_OUTPUT, TRUE);
+			marshaller
+					.setProperty(
+							"jaxb.schemaLocation",
+							"http://ns.taverna.org.uk/2010/scufl2# http://ns.taverna.org.uk/2010/scufl2/scufl2.xsd "
+									+ "http://www.w3.org/1999/02/22-rdf-syntax-ns# http://ns.taverna.org.uk/2010/scufl2/rdf.xsd");
+		} catch (JAXBException e) {
+			throw new IllegalStateException(e);
+		} catch (SAXException e) {
+			throw new IllegalStateException("Could not load schema "
+					+ schemaPath, e);
+		}
+		setPrefixMapper(marshaller);
+		return marshaller;
+	}
+
+	public WorkflowBundle getWfBundle() {
+		return wfBundle;
+	}
+
+	public boolean isUsingSchema() {
+		return usingSchema;
+	}
+
+	protected ProfileDocument makeProfile(Profile pf, URI path) {
+		ProfileDocument doc = objectFactory.createProfileDocument();
+
+		objectFactory.createProfile();
+		pf.accept(new ProfileSerialisationVisitor(doc) {
+		});
+		return doc;
+	}
+
+	protected org.apache.taverna.scufl2.rdfxml.jaxb.Workflow makeWorkflow(
+			Workflow wf, URI documentPath) {
+		org.apache.taverna.scufl2.rdfxml.jaxb.Workflow workflow = objectFactory
+				.createWorkflow();
+		wf.accept(new WorkflowSerialisationVisitor(workflow) {
+		});
+		return workflow;
+	}
+
+	protected org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle makeWorkflowBundleElem() {
+		org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle bundle = objectFactory
+				.createWorkflowBundle();
+		// FIXME: Support other URIs
+		bundle.setAbout("");
+		bundle.setName(wfBundle.getName());
+
+		if (wfBundle.getGlobalBaseURI() != null) {
+			Resource globalBaseURI = rdfObjectFactory.createResource();
+			globalBaseURI.setResource(wfBundle.getGlobalBaseURI().toASCIIString());
+			bundle.setGlobalBaseURI(globalBaseURI);
+		}
+
+		for (Workflow wf : wfBundle.getWorkflows()) {
+			org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle.Workflow wfElem = objectFactory
+					.createWorkflowBundleWorkflow();
+			SeeAlsoType seeAlsoElem = objectFactory.createSeeAlsoType();
+			seeAlsoElem.setAbout(uriTools.relativeUriForBean(wf, wfBundle)
+					.toASCIIString());
+
+			if (seeAlsoUris.containsKey(wf)) {
+				SeeAlso seeAlso = rdfsObjectFactory.createSeeAlso();
+				seeAlso.setResource(seeAlsoUris.get(wf).toASCIIString());
+				seeAlsoElem.setSeeAlso(seeAlso);
+			} else
+				logger.warning("Can't find bundle URI for workflow document "
+						+ wf.getName());
+
+			wfElem.setWorkflow(seeAlsoElem);
+			bundle.getWorkflow().add(wfElem);
+
+			if (wfBundle.getMainWorkflow() == wf) {
+				Resource mainWorkflow = rdfObjectFactory.createResource();
+				mainWorkflow.setResource(seeAlsoElem.getAbout());
+				bundle.setMainWorkflow(mainWorkflow);
+			}
+		}
+
+		for (Profile pf : wfBundle.getProfiles()) {
+			org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle.Profile wfElem = objectFactory
+					.createWorkflowBundleProfile();
+			SeeAlsoType seeAlsoElem = objectFactory.createSeeAlsoType();
+			seeAlsoElem.setAbout(uriTools.relativeUriForBean(pf, wfBundle)
+					.toASCIIString());
+
+			if (seeAlsoUris.containsKey(pf)) {
+				SeeAlso seeAlso = rdfsObjectFactory.createSeeAlso();
+				seeAlso.setResource(seeAlsoUris.get(pf).toASCIIString());
+				seeAlsoElem.setSeeAlso(seeAlso);
+			} else
+				logger.warning("Can't find bundle URI for profile document "
+						+ pf.getName());
+
+			wfElem.setProfile(seeAlsoElem);
+			bundle.getProfile().add(wfElem);
+
+			if (wfBundle.getMainProfile() == pf) {
+				Resource mainProfile = rdfObjectFactory.createResource();
+				mainProfile.setResource(seeAlsoElem.getAbout());
+				bundle.setMainProfile(mainProfile);
+			}
+		}
+
+		for (Annotation ann : wfBundle.getAnnotations())
+			annotation(ann);
+		
+		return bundle;
+	}
+
+	public void profileDoc(OutputStream outputStream, Profile pf, URI path)
+			throws JAXBException, WriterException {
+		ProfileDocument doc = makeProfile(pf, path);
+
+		URI wfUri = uriTools.relativeUriForBean(pf, wfBundle);
+		doc.setBase(uriTools.relativePath(path, wfUri).toASCIIString());
+
+		JAXBElement<RDF> element = rdfObjectFactory.createRDF(doc);
+		getMarshaller().marshal(element, outputStream);
+		seeAlsoUris.put(pf, path);
+	}
+
+	private Resource resource(String uri) {
+		Resource r = rdfObjectFactory.createResource();
+		r.setResource(uri);
+		return r;
+	}
+
+	public void setJaxbContext(JAXBContext jaxbContext) {
+		this.jaxbContext = jaxbContext;
+	}
+
+	protected void setPrefixMapper(Marshaller marshaller) {
+		boolean setPrefixMapper = false;
+
+		try {
+			/*
+			 * This only works with JAXB RI, in which case we can set the
+			 * namespace prefix mapper
+			 */
+			Class.forName("com.sun.xml.bind.marshaller.NamespacePrefixMapper");
+			marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper",
+					new NamespacePrefixMapperJAXB_RI());
+			/*
+			 * Note: A similar mapper for the built-in java
+			 * (com.sun.xml.bind.internal.namespacePrefixMapper) is no longer
+			 * included here, as it will not (easily) compile with Maven.
+			 */
+			setPrefixMapper = true;
+		} catch (Exception e) {
+			logger.log(FINE, "Can't find NamespacePrefixMapper", e);
+		}
+
+		if (!setPrefixMapper && ! warnedOnce) {
+			logger.info("Could not set prefix mapper (missing or incompatible JAXB) "
+					+ "- will use prefixes ns0, ns1, ..");
+			warnedOnce = true;
+		}
+	}
+
+	public void setUsingSchema(boolean usingSchema) {
+		this.usingSchema = usingSchema;
+	}
+
+	public void setWfBundle(WorkflowBundle wfBundle) {
+		this.wfBundle = wfBundle;
+	}
+
+	private Type type(Typed typed) {
+		if (typed.getType() == null)
+			return null;
+		Type t = rdfObjectFactory.createType();
+		t.setResource(typed.getType().toASCIIString());
+		return t;
+	}
+
+	public void workflowBundleDoc(OutputStream outputStream, URI path)
+			throws JAXBException, WriterException {
+		org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle bundle = makeWorkflowBundleElem();
+		WorkflowBundleDocument doc = objectFactory
+				.createWorkflowBundleDocument();
+		doc.getAny().add(bundle);
+
+		doc.setBase(path.relativize(URI.create("./")).toASCIIString());
+		JAXBElement<RDF> element = rdfObjectFactory.createRDF(doc);
+
+		getMarshaller().marshal(element, outputStream);
+		seeAlsoUris.put(wfBundle, path);
+	}
+
+	public void workflowDoc(OutputStream outputStream, Workflow wf, URI path)
+			throws JAXBException, WriterException {
+		org.apache.taverna.scufl2.rdfxml.jaxb.Workflow wfElem = makeWorkflow(wf,
+				path);
+		WorkflowDocument doc = objectFactory.createWorkflowDocument();
+		doc.getAny().add(wfElem);
+
+		URI wfUri = uriTools.relativeUriForBean(wf, wfBundle);
+		doc.setBase(uriTools.relativePath(path, wfUri).toASCIIString());
+
+		JAXBElement<RDF> element = rdfObjectFactory.createRDF(doc);
+		getMarshaller().marshal(element, outputStream);
+		seeAlsoUris.put(wf, path);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLWriter.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLWriter.java b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLWriter.java
new file mode 100644
index 0000000..3670898
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RDFXMLWriter.java
@@ -0,0 +1,164 @@
+package org.apache.taverna.scufl2.rdfxml;
+/*
+ *
+ * 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.
+ *
+*/
+
+
+import static org.apache.taverna.scufl2.rdfxml.RDFXMLReader.APPLICATION_RDF_XML;
+import static org.apache.taverna.scufl2.rdfxml.RDFXMLReader.APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.xml.bind.JAXBException;
+
+import org.apache.taverna.scufl2.api.annotation.Revision;
+import org.apache.taverna.scufl2.api.annotation.Revisioned;
+import org.apache.taverna.scufl2.api.common.URITools;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.io.WorkflowBundleWriter;
+import org.apache.taverna.scufl2.api.io.WriterException;
+import org.apache.taverna.scufl2.api.profiles.Profile;
+import org.apache.taverna.scufl2.ucfpackage.UCFPackage;
+
+
+public class RDFXMLWriter implements WorkflowBundleWriter {
+	private static final String WF = "wf-";
+	private static final String REVISIONS = "-revisions";
+	protected static final String RDF = ".rdf";
+	protected static final String WORKFLOW = "workflow/";
+	protected static final String HISTORY = "history/";
+	protected static final String PROFILE = "profile/";
+	protected static final String WORKFLOW_BUNDLE_RDF = "workflowBundle.rdf";
+
+	private static URITools uriTools = new URITools();
+	
+	public static final URITools getUriTools() {
+		return uriTools;
+	}
+
+	public static final void setUriTools(URITools uriTools) {
+		RDFXMLWriter.uriTools = uriTools;
+	}
+
+	/**
+	 * Version of Workflow Bundle format
+	 */
+    public final String WORKFLOW_BUNDLE_VERSION = "0.4.0";
+
+	@Override
+	public Set<String> getMediaTypes() {
+		return Collections
+				.singleton(APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE);
+	}
+
+	@Override
+	public void writeBundle(WorkflowBundle wfBundle, File destination,
+			String mediaType) throws WriterException, IOException {
+		UCFPackage ucfPackage = makeUCFPackage(wfBundle);
+		ucfPackage.save(destination);
+	}
+
+	protected UCFPackage makeUCFPackage(WorkflowBundle wfBundle)
+			throws IOException, WriterException {
+		//UCFPackage ucfPackage = new UCFPackage();
+		UCFPackage ucfPackage = wfBundle.getResources();		
+		if (ucfPackage.getPackageMediaType() == null)
+			ucfPackage
+				.setPackageMediaType(APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE);
+
+		RDFXMLSerializer serializer = new RDFXMLSerializer(wfBundle);
+		
+		for (Workflow wf : wfBundle.getWorkflows()) {
+			String path = WORKFLOW + uriTools.validFilename(wf.getName()) + RDF;
+
+			try (OutputStream outputStream = ucfPackage
+					.addResourceUsingOutputStream(path, APPLICATION_RDF_XML)) {
+				serializer.workflowDoc(outputStream, wf, URI.create(path));
+			} catch (JAXBException e) {
+				throw new WriterException("Can't generate " + path, e);
+			}
+			
+			path = HISTORY + WF +  
+					uriTools.validFilename(wf.getName()) + REVISIONS + RDF;
+			addRevisions(wf, path, wfBundle);
+		}
+
+		for (Profile pf : wfBundle.getProfiles()) {
+			String path = PROFILE + uriTools.validFilename(pf.getName()) + RDF;
+			try (OutputStream outputStream = ucfPackage
+					.addResourceUsingOutputStream(path, APPLICATION_RDF_XML)) {
+				serializer.profileDoc(outputStream, pf, URI.create(path));
+			} catch (JAXBException e) {
+				throw new WriterException("Can't generate " + path, e);
+			}
+			path = HISTORY + "pf-" +  
+					uriTools.validFilename(pf.getName()) + REVISIONS + RDF;
+			addRevisions(pf, path, wfBundle);
+		}
+
+		try (OutputStream outputStream = ucfPackage
+				.addResourceUsingOutputStream(WORKFLOW_BUNDLE_RDF,
+						APPLICATION_RDF_XML)) {
+			serializer.workflowBundleDoc(outputStream,
+					URI.create(WORKFLOW_BUNDLE_RDF));
+		} catch (JAXBException e) {
+			throw new WriterException("Can't generate " + WORKFLOW_BUNDLE_RDF,
+					e);
+		}
+		
+		if (ucfPackage.getPackageMediaType().equals(
+				APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE))
+			ucfPackage
+					.setRootFile(WORKFLOW_BUNDLE_RDF, WORKFLOW_BUNDLE_VERSION);
+
+		String path = HISTORY + "wfbundle" + REVISIONS + RDF;
+		addRevisions(wfBundle, path, wfBundle);
+		
+		return ucfPackage;
+	}
+
+
+	protected void addRevisions(Revisioned revisioned, String path, WorkflowBundle wfBundle) throws WriterException {
+		@SuppressWarnings("unused")
+		URI uriBase = uriTools.uriForBean(wfBundle).resolve(path);		
+		Revision currentRevision = revisioned.getCurrentRevision();
+		if (currentRevision == null)
+			return;
+//		try {
+//			wfBundle.getResources()
+//					.addResource(visitor.getDoc(), path, APPLICATION_RDF_XML);
+//		} catch (IOException e) {
+//			throw new WriterException("Can't write revisions to " + path, e);
+//		}
+	}
+
+	@Override
+	public void writeBundle(WorkflowBundle wfBundle, OutputStream output,
+			String mediaType) throws WriterException, IOException {
+		UCFPackage ucfPackage = makeUCFPackage(wfBundle);
+		ucfPackage.save(output);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RevisionParser.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RevisionParser.java b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RevisionParser.java
new file mode 100644
index 0000000..366069b
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/RevisionParser.java
@@ -0,0 +1,173 @@
+package org.apache.taverna.scufl2.rdfxml;
+/*
+ *
+ * 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.
+ *
+*/
+
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import org.apache.taverna.scufl2.api.annotation.Revision;
+import org.apache.taverna.scufl2.api.io.ReaderException;
+import org.purl.wf4ever.roevo.jaxb.Change;
+import org.purl.wf4ever.roevo.jaxb.ChangeSpecification;
+import org.purl.wf4ever.roevo.jaxb.ChangeSpecification.HasChange;
+import org.purl.wf4ever.roevo.jaxb.RoEvoDocument;
+import org.purl.wf4ever.roevo.jaxb.VersionableResource;
+import org.w3._1999._02._22_rdf_syntax_ns_.Resource;
+
+
+public class RevisionParser {
+	private JAXBContext jaxbContext;
+
+	protected JAXBContext getJaxbContext() throws JAXBException {
+		if (jaxbContext == null) {
+			Class<?>[] packages = {
+					org.purl.wf4ever.roevo.jaxb.ObjectFactory.class,
+					org.w3.prov.jaxb.ObjectFactory.class,
+					org.w3._1999._02._22_rdf_syntax_ns_.ObjectFactory.class,
+					org.w3._2000._01.rdf_schema_.ObjectFactory.class };
+			jaxbContext = JAXBContext.newInstance(packages);
+		}
+		return jaxbContext;
+	}
+
+	@SuppressWarnings({ "unchecked" })
+	public Map<URI, Revision> readRevisionChain(
+			InputStream revisionDocumentStream, URI base)
+			throws ReaderException {
+		JAXBElement<RoEvoDocument> roEvoDoc;
+		try {
+			Unmarshaller unmarshaller = getJaxbContext().createUnmarshaller();
+			roEvoDoc = (JAXBElement<RoEvoDocument>) unmarshaller
+					.unmarshal(revisionDocumentStream);
+		} catch (JAXBException e) {
+			throw new ReaderException(e);
+		}
+
+		RoEvoDocument document = roEvoDoc.getValue();
+		if (document.getBase() != null)
+			base = base.resolve(document.getBase());
+		Map<URI, Revision> revisions = new LinkedHashMap<>();
+		// NOTE: Silly hack to iterate/cast in one go.. will it work?
+		for (VersionableResource verResource : document.getAny().toArray(
+				new VersionableResource[0]))
+			parse(base, verResource, revisions);
+		return revisions;
+	}
+
+	private Revision parse(URI base, VersionableResource verResource,
+			Map<URI, Revision> revisions) throws ReaderException {
+		URI uri = base.resolve(verResource.getAbout());
+		Revision revision = addOrExisting(uri, revisions);
+
+		if (verResource.getGeneratedAtTime() != null) {
+			XMLGregorianCalendar xmlCal = verResource.getGeneratedAtTime()
+					.getValue();
+			revision.setGeneratedAtTime(xmlCal.toGregorianCalendar());
+		}
+
+		Resource wasRevisionOf = verResource.getWasRevisionOf();
+		if (wasRevisionOf != null) {
+			// TODO Put these in a map
+			Revision r = addOrExisting(
+					base.resolve(wasRevisionOf.getResource()), revisions);
+			revision.setPreviousRevision(r);
+		}
+
+		if (verResource.getWasChangedBy() != null) {
+			ChangeSpecification changeSpec = verResource.getWasChangedBy()
+					.getChangeSpecification();
+			if (changeSpec.getFromVersion() != null) {
+				Revision r = addOrExisting(
+						base.resolve(changeSpec.getFromVersion().getResource()),
+						revisions);
+				if (revision.getPreviousRevision() != null
+						&& revision.getPreviousRevision() != r)
+					throw new ReaderException(
+							"Inconsistent previous revision: "
+									+ revision.getPreviousRevision()
+											.getIdentifier() + " or "
+									+ r.getIdentifier());
+				revision.setPreviousRevision(r);
+			}
+
+			if (changeSpec.getType() != null)
+				revision.setChangeSpecificationType(base.resolve(changeSpec
+						.getType().getResource()));
+
+			for (HasChange hasChange : changeSpec.getHasChange()) {
+				if (hasChange.getAddition() != null) {
+					Set<URI> additions = parse(hasChange.getAddition(), base);
+					// Note: Use addAll in case a buggy XML has multiple
+					// <hasChange><Addition>
+					revision.getAdditionOf().addAll(additions);
+				}
+				if (hasChange.getModification() != null) {
+					Set<URI> modifications = parse(hasChange.getModification(),
+							base);
+					revision.getModificationsOf().addAll(modifications);
+				}
+				if (hasChange.getRemoval() != null) {
+					Set<URI> removals = parse(hasChange.getRemoval(), base);
+					revision.getRemovalOf().addAll(removals);
+				}
+			}
+		}
+
+		for (Resource assoc : verResource.getWasAttributedTo())
+			revision.getWasAttributedTo()
+					.add(base.resolve(assoc.getResource()));
+
+		for (Resource assoc : verResource.getHadOriginalSource()) {
+			Revision r = addOrExisting(base.resolve(assoc.getResource()),
+					revisions);
+			revision.getHadOriginalSources().add(r);
+		}
+
+		return revision;
+	}
+
+	private Revision addOrExisting(URI uri, Map<URI, Revision> revisions) {
+		Revision rev = revisions.get(uri);
+		if (rev != null)
+			return rev;
+		rev = new Revision(uri, null);
+		revisions.put(uri, rev);
+		return rev;
+	}
+
+	private Set<URI> parse(Change addition, URI base) {
+		Set<URI> uris = new LinkedHashSet<>();
+		for (Resource r : addition.getRelatedResource())
+			uris.add(base.resolve(r.getResource()));
+		return uris;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowBundleParser.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowBundleParser.java b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowBundleParser.java
new file mode 100644
index 0000000..4e56a1f
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowBundleParser.java
@@ -0,0 +1,190 @@
+package org.apache.taverna.scufl2.rdfxml;
+/*
+ *
+ * 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.
+ *
+*/
+
+
+import static org.apache.taverna.scufl2.rdfxml.RDFXMLReader.APPLICATION_RDF_XML;
+import static org.apache.taverna.scufl2.rdfxml.RDFXMLReader.APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE;
+import static org.apache.taverna.scufl2.rdfxml.RDFXMLWriter.WORKFLOW_BUNDLE_RDF;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.io.ReaderException;
+import org.apache.taverna.scufl2.ucfpackage.UCFPackage;
+import org.apache.taverna.scufl2.ucfpackage.UCFPackage.ResourceEntry;
+
+import org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundleDocument;
+
+public class WorkflowBundleParser extends AbstractParser {
+
+	private WorkflowParser workflowParser;
+	private ProfileParser profileParser;
+
+	public WorkflowBundleParser() {
+		super();
+		workflowParser = new WorkflowParser(parserState);
+		profileParser = new ProfileParser(parserState);
+	}
+
+	protected String findWorkflowBundlePath() {
+		if (APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE
+				.equals(getParserState().getUcfPackage().getPackageMediaType()))
+			for (ResourceEntry potentialRoot : getParserState().getUcfPackage()
+					.getRootFiles())
+				if (APPLICATION_RDF_XML.equals(potentialRoot.getMediaType()))
+					return potentialRoot.getPath();
+		return WORKFLOW_BUNDLE_RDF;
+	}
+
+	protected WorkflowBundle parseWorkflowBundle(
+			org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle wb, URI base)
+			throws ReaderException, IOException {
+		WorkflowBundle workflowBundle = new WorkflowBundle();
+		getParserState().push(workflowBundle);
+		try {
+			workflowBundle.setResources(getParserState().getUcfPackage());
+			if (wb.getName() != null)
+				workflowBundle.setName(wb.getName());
+			if (wb.getGlobalBaseURI() != null
+					&& wb.getGlobalBaseURI().getResource() != null)
+				workflowBundle.setGlobalBaseURI(base.resolve(wb
+						.getGlobalBaseURI().getResource()));
+			mapBean(base.resolve(wb.getAbout()), workflowBundle);
+			for (org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle.Workflow wfEntry : wb
+					.getWorkflow()) {
+				URI wfUri = base.resolve(wfEntry.getWorkflow().getAbout());
+				String resource = wfEntry.getWorkflow().getSeeAlso()
+						.getResource();
+				URI source = uriTools.relativePath(getParserState()
+						.getLocation(), base.resolve(resource));
+				workflowParser.readWorkflow(wfUri, source);
+			}
+			for (org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle.Profile pfEntry : wb
+					.getProfile()) {
+				URI wfUri = base.resolve(pfEntry.getProfile().getAbout());
+				String resource = pfEntry.getProfile().getSeeAlso()
+						.getResource();
+				URI source = uriTools.relativePath(getParserState()
+						.getLocation(), base.resolve(resource));
+				profileParser.readProfile(wfUri, source);
+			}
+
+			if (wb.getMainWorkflow() != null
+					&& wb.getMainWorkflow().getResource() != null) {
+				URI mainWfUri = base
+						.resolve(wb.getMainWorkflow().getResource());
+				Workflow mainWorkflow = (Workflow) resolveBeanUri(mainWfUri);
+				if (mainWorkflow == null)
+					throw new ReaderException("Unknown main workflow "
+							+ mainWfUri + ", got"
+							+ getParserState().getUriToBean().keySet());
+				workflowBundle.setMainWorkflow(mainWorkflow);
+			}
+			if (wb.getMainProfile() != null
+					&& wb.getMainProfile().getResource() != null) {
+				URI profileUri = base
+						.resolve(wb.getMainProfile().getResource());
+				org.apache.taverna.scufl2.api.profiles.Profile mainWorkflow = (org.apache.taverna.scufl2.api.profiles.Profile) resolveBeanUri(profileUri);
+				workflowBundle.setMainProfile(mainWorkflow);
+			}
+		} finally {
+			getParserState().pop();
+		}
+		return workflowBundle;
+	}
+
+	@SuppressWarnings("unchecked")
+	public WorkflowBundle readWorkflowBundle(UCFPackage ucfPackage,
+			URI suggestedLocation) throws IOException, ReaderException {
+		try {
+			getParserState().setUcfPackage(ucfPackage);
+			getParserState().setLocation(suggestedLocation);
+			if (getParserState().getLocation() == null) {
+				getParserState().setLocation(URI.create(""));
+			} else if (!getParserState().getLocation().getRawPath()
+					.endsWith("/")) {
+				if (getParserState().getLocation().getQuery() != null
+						|| getParserState().getLocation().getFragment() != null)
+					/*
+					 * Ouch.. Perhaps some silly website with ?bundleId=15 ?
+					 * We'll better conserve that somehow. Let's do the jar:
+					 * trick and hope it works. Have to escape evil chars.
+					 */
+					getParserState().setLocation(
+							URI.create("jar:"
+									+ getParserState().getLocation()
+									.toASCIIString()
+									.replace("?", "%63")
+									.replace("#", "#35") + "!/"));
+				else
+					/*
+					 * Simple, pretend we're one level down inside the ZIP file
+					 * as a directory
+					 */
+					getParserState().setLocation(
+							getParserState().getLocation().resolve(
+									getParserState().getLocation().getRawPath()
+											+ "/"));
+			}
+			String workflowBundlePath = findWorkflowBundlePath();
+
+			InputStream bundleStream = getParserState().getUcfPackage()
+					.getResourceAsInputStream(workflowBundlePath);
+
+			JAXBElement<WorkflowBundleDocument> elem;
+			try {
+				elem = (JAXBElement<WorkflowBundleDocument>) unmarshaller
+						.unmarshal(bundleStream);
+			} catch (JAXBException e) {
+				throw new ReaderException(
+						"Can't parse workflow bundle document "
+								+ workflowBundlePath, e);
+			}
+			WorkflowBundleDocument workflowBundleDocument = elem.getValue();
+
+			URI base = getParserState().getLocation().resolve(
+					workflowBundlePath);
+			if (workflowBundleDocument.getBase() != null)
+				base = getParserState().getLocation().resolve(
+						workflowBundleDocument.getBase());
+
+			if (workflowBundleDocument.getAny().size() != 1)
+				throw new ReaderException(
+						"Invalid WorkflowBundleDocument, expected only one <WorkflowBundle>");
+
+			org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle wb = (org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle) workflowBundleDocument
+					.getAny().get(0);
+			WorkflowBundle workflowBundle = parseWorkflowBundle(wb, base);
+
+			scufl2Tools.setParents(workflowBundle);
+			return workflowBundle;
+		} finally {
+			clearParserState();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowParser.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowParser.java b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowParser.java
new file mode 100644
index 0000000..6d84ef7
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/WorkflowParser.java
@@ -0,0 +1,381 @@
+package org.apache.taverna.scufl2.rdfxml;
+/*
+ *
+ * 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.
+ *
+*/
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+
+import org.apache.taverna.scufl2.api.common.WorkflowBean;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.BlockingControlLink;
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.io.ReaderException;
+import org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyParent;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+import org.apache.taverna.scufl2.api.port.SenderPort;
+
+import org.apache.taverna.scufl2.rdfxml.jaxb.Blocking;
+import org.apache.taverna.scufl2.rdfxml.jaxb.CrossProduct;
+import org.apache.taverna.scufl2.rdfxml.jaxb.DataLink;
+import org.apache.taverna.scufl2.rdfxml.jaxb.DispatchStack;
+import org.apache.taverna.scufl2.rdfxml.jaxb.DotProduct;
+import org.apache.taverna.scufl2.rdfxml.jaxb.IterationStrategyStack;
+import org.apache.taverna.scufl2.rdfxml.jaxb.PortNode;
+import org.apache.taverna.scufl2.rdfxml.jaxb.Processor.InputProcessorPort;
+import org.apache.taverna.scufl2.rdfxml.jaxb.Processor.OutputProcessorPort;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ProductOf;
+import org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowDocument;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+
+public class WorkflowParser extends AbstractParser {
+	private static Logger logger = Logger.getLogger(WorkflowParser.class
+			.getCanonicalName());
+	@SuppressWarnings("unused")
+	private static final JsonNodeFactory JSON_NODE_FACTORY = JsonNodeFactory.instance;
+
+	public WorkflowParser() {
+	}
+
+	public WorkflowParser(ThreadLocal<ParserState> parserState) {
+		super(parserState);
+	}
+
+	protected void parseControlLink(Blocking original) {
+		URI blockUri = getParserState().getCurrentBase().resolve(
+				original.getBlock().getResource());
+		URI untilFinishedUri = getParserState().getCurrentBase().resolve(
+				original.getUntilFinished().getResource());
+		WorkflowBean block = resolveBeanUri(blockUri);
+		WorkflowBean untilFinished = resolveBeanUri(untilFinishedUri);
+
+		BlockingControlLink blocking = new BlockingControlLink();
+		blocking.setBlock((org.apache.taverna.scufl2.api.core.Processor) block);
+		blocking.setUntilFinished((org.apache.taverna.scufl2.api.core.Processor) untilFinished);
+
+		blocking.setParent(getParserState().getCurrent(Workflow.class));
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				blocking);
+	}
+
+	protected void parseCrossDotOrPortNodeList(List<Object> nodeList)
+			throws ReaderException {
+		for (Object node : nodeList)
+			if (node instanceof DotProduct)
+				parseDotProduct((DotProduct) node);
+			else if (node instanceof CrossProduct)
+				parseCrossProduct((CrossProduct) node);
+			else if (node instanceof PortNode)
+				parsePortNode((PortNode) node);
+			else
+				throw new ReaderException("Unexpected node " + node);
+	}
+
+	protected void parseCrossProduct(CrossProduct original)
+			throws ReaderException {
+		org.apache.taverna.scufl2.api.iterationstrategy.CrossProduct cross = new org.apache.taverna.scufl2.api.iterationstrategy.CrossProduct();
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				cross);
+		cross.setParent(getParserState().getCurrent(
+				IterationStrategyParent.class));
+		getParserState().push(cross);
+		try {
+			parseProductOf(original.getProductOf());
+		} finally {
+			getParserState().pop();
+		}
+	}
+
+	protected void parseDataLink(DataLink original) {
+		URI fromUri = getParserState().getCurrentBase().resolve(
+				original.getReceiveFrom().getResource());
+		URI toUri = getParserState().getCurrentBase().resolve(
+				original.getSendTo().getResource());
+		WorkflowBean from = resolveBeanUri(fromUri);
+		WorkflowBean to = resolveBeanUri(toUri);
+
+		org.apache.taverna.scufl2.api.core.DataLink link = new org.apache.taverna.scufl2.api.core.DataLink();
+		link.setReceivesFrom((SenderPort) from);
+		link.setSendsTo((ReceiverPort) to);
+		if (original.getMergePosition() != null)
+			link.setMergePosition(original.getMergePosition().getValue());
+		link.setParent(getParserState().getCurrent(Workflow.class));
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				link);
+	}
+
+	protected void parseDispatchStack(DispatchStack original) {
+        logger.fine("Ignoring Dispatch stack: not supported (SCUFL2-130)");
+        return;
+
+//        // FIXME: Legacy code - support parsing old dispatch stack configurations
+//		Processor processor = getParserState().getCurrent(
+//				org.apache.taverna.scufl2.api.core.Processor.class);
+//        ObjectNode config = JSON_NODE_FACTORY.objectNode();
+//        getParserState().getDispatchConfigs().put(processor, config);        
+//		if (original.getDispatchStackLayers() != null) {
+//			for (DispatchStackLayer dispatchStackLayer : original
+//					.getDispatchStackLayers().getDispatchStackLayer()) {
+//				parseDispatchStackLayer(dispatchStackLayer);
+//			}
+//		}
+	}
+
+//	protected void parseDispatchStackLayer(DispatchStackLayer original) {
+//	    Processor processor = getParserState().getCurrent(Processor.class);
+//	    URI type = getParserState().getCurrentBase().resolve(
+//				original.getType().getResource());
+//	    URI config = getParserState().getCurrentBase().resolve(original.getAbout());
+//	    // TODO: SCUFL2-130
+//	    // Add Legacy code for wfbundle 0.3.0 to
+//	    // support parsing old dispatch stack configurations
+//	    // 
+//	    // The difficult bit is that the layers themselves has moved to 
+//	    // to be a Configuration on a Processor - but we are here within
+//	    // parsing of the Workflow. In 0.3.0 each layer is then configured
+//	    // separately. So we need to pass over somehow the current stack 
+//	    // to the ParserState so that it can be picked up in ProfileParser
+//	    // and added to each of the profiles -- or at least where the
+//	    // stack layers have been configured.
+//	    // 
+//	    // Here's an idea on how it can work. Here we should push each layer to a
+//	    // List<Pair<URI,URI>> that we can keep in the ParserState. 
+//	    // Then, within ProfileParser, we can pick them up and
+//      // recreate what the Processor config would look like for
+//      // the default configs - and then delete those dangling configs
+//	}
+
+	protected void parseDotProduct(DotProduct original) throws ReaderException {
+		org.apache.taverna.scufl2.api.iterationstrategy.DotProduct dot = new org.apache.taverna.scufl2.api.iterationstrategy.DotProduct();
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				dot);
+		dot.setParent(getParserState()
+				.getCurrent(IterationStrategyParent.class));
+
+		getParserState().push(dot);
+		try {
+			parseProductOf(original.getProductOf());
+		} finally {
+			getParserState().pop();
+		}
+	}
+
+	protected void parseInputWorkflowPort(
+			org.apache.taverna.scufl2.rdfxml.jaxb.InputWorkflowPort original) {
+		org.apache.taverna.scufl2.api.port.InputWorkflowPort port = new org.apache.taverna.scufl2.api.port.InputWorkflowPort();
+		port.setName(original.getName());
+		if (original.getPortDepth() != null)
+			port.setDepth(original.getPortDepth().getValue());
+		port.setParent(getParserState().getCurrent(Workflow.class));
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				port);
+	}
+
+	protected void parseIterationStrategyStack(IterationStrategyStack original)
+			throws ReaderException {
+		org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyStack iterationStrategyStack = new org.apache.taverna.scufl2.api.iterationstrategy.IterationStrategyStack();
+		iterationStrategyStack.setParent(getParserState().getCurrent(
+				Processor.class));
+
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				iterationStrategyStack);
+		if (original.getIterationStrategies() != null) {
+			getParserState().push(iterationStrategyStack);
+			try {
+				parseCrossDotOrPortNodeList(original.getIterationStrategies()
+						.getDotProductOrCrossProduct());
+			} finally {
+				getParserState().pop();
+			}
+		}
+	}
+
+	protected void parseOutputWorkflowPort(
+			org.apache.taverna.scufl2.rdfxml.jaxb.OutputWorkflowPort original) {
+		org.apache.taverna.scufl2.api.port.OutputWorkflowPort port = new org.apache.taverna.scufl2.api.port.OutputWorkflowPort();
+		port.setName(original.getName());
+		port.setParent(getParserState().getCurrent(Workflow.class));
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				port);
+	}
+
+	protected void parsePortNode(PortNode original) {
+		org.apache.taverna.scufl2.api.iterationstrategy.PortNode node = new org.apache.taverna.scufl2.api.iterationstrategy.PortNode();
+		node.setParent(getParserState().getCurrent(
+				IterationStrategyParent.class));
+		if (original.getDesiredDepth() != null)
+			node.setDesiredDepth(original.getDesiredDepth().getValue());
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				node);
+		URI inputPortUri = getParserState().getCurrentBase().resolve(
+				original.getIterateOverInputPort().getResource());
+		org.apache.taverna.scufl2.api.port.InputProcessorPort inputPort = (org.apache.taverna.scufl2.api.port.InputProcessorPort) resolveBeanUri(inputPortUri);
+		node.setInputProcessorPort(inputPort);
+	}
+
+	protected void parseProcessor(
+			org.apache.taverna.scufl2.rdfxml.jaxb.Processor processor)
+			throws ReaderException {
+		org.apache.taverna.scufl2.api.core.Processor p = new org.apache.taverna.scufl2.api.core.Processor();
+		getParserState().push(p);
+		try {
+			p.setParent(getParserState().getCurrent(Workflow.class));
+			mapBean(getParserState().getCurrentBase().resolve(
+					processor.getAbout()), p);
+			if (processor.getName() != null)
+				p.setName(processor.getName());
+			for (InputProcessorPort inputProcessorPort : processor
+					.getInputProcessorPort())
+				processorInputProcessorPort(inputProcessorPort
+						.getInputProcessorPort());
+			for (OutputProcessorPort outputProcessorPort : processor
+					.getOutputProcessorPort())
+				processorOutputProcessorPort(outputProcessorPort
+						.getOutputProcessorPort());
+			if (processor.getDispatchStack() != null)
+			    // Legacy wfbundle
+				parseDispatchStack(processor.getDispatchStack()
+						.getDispatchStack());
+			if (processor.getIterationStrategyStack() != null)
+				parseIterationStrategyStack(processor
+						.getIterationStrategyStack()
+						.getIterationStrategyStack());
+		} finally {
+			getParserState().pop();
+		}
+	}
+
+	protected void parseProductOf(ProductOf productOf) throws ReaderException {
+		if (productOf == null)
+			return;
+		parseCrossDotOrPortNodeList(productOf
+				.getCrossProductOrDotProductOrPortNode());
+	}
+
+	protected void parseWorkflow(
+			org.apache.taverna.scufl2.rdfxml.jaxb.Workflow workflow, URI wfUri)
+			throws ReaderException {
+		Workflow wf = new Workflow();
+		wf.setParent(getParserState().getCurrent(WorkflowBundle.class));
+
+		if (workflow.getAbout() != null)
+			mapBean(getParserState().getCurrentBase().resolve(
+					workflow.getAbout()), wf);
+			// TODO: Compare resolved URI with desired wfUri
+		else
+			mapBean(wfUri, wf);
+
+		getParserState().push(wf);
+		try {
+			if (workflow.getName() != null)
+				wf.setName(workflow.getName());
+			if (workflow.getWorkflowIdentifier() != null
+					&& workflow.getWorkflowIdentifier().getResource() != null)
+				wf.setIdentifier(getParserState().getCurrentBase().resolve(
+						workflow.getWorkflowIdentifier().getResource()));
+
+			for (org.apache.taverna.scufl2.rdfxml.jaxb.Workflow.InputWorkflowPort inputWorkflowPort : workflow
+					.getInputWorkflowPort())
+				parseInputWorkflowPort(inputWorkflowPort.getInputWorkflowPort());
+			for (org.apache.taverna.scufl2.rdfxml.jaxb.Workflow.OutputWorkflowPort outputWorkflowPort : workflow
+					.getOutputWorkflowPort())
+				parseOutputWorkflowPort(outputWorkflowPort
+						.getOutputWorkflowPort());
+			for (org.apache.taverna.scufl2.rdfxml.jaxb.Workflow.Processor processor : workflow
+					.getProcessor())
+				parseProcessor(processor.getProcessor());
+			for (org.apache.taverna.scufl2.rdfxml.jaxb.DataLinkEntry dataLinkEntry : workflow
+					.getDatalink())
+				parseDataLink(dataLinkEntry.getDataLink());
+			for (org.apache.taverna.scufl2.rdfxml.jaxb.Control c : workflow
+					.getControl())
+				parseControlLink(c.getBlocking());
+		} finally {
+			getParserState().pop();
+		}
+	}
+
+	protected void processorInputProcessorPort(
+			org.apache.taverna.scufl2.rdfxml.jaxb.InputProcessorPort original) {
+		org.apache.taverna.scufl2.api.port.InputProcessorPort port = new org.apache.taverna.scufl2.api.port.InputProcessorPort();
+		port.setName(original.getName());
+		if (original.getPortDepth() != null)
+			port.setDepth(original.getPortDepth().getValue());
+		port.setParent(getParserState().getCurrent(Processor.class));
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				port);
+	}
+
+	protected void processorOutputProcessorPort(
+			org.apache.taverna.scufl2.rdfxml.jaxb.OutputProcessorPort original) {
+		org.apache.taverna.scufl2.api.port.OutputProcessorPort port = new org.apache.taverna.scufl2.api.port.OutputProcessorPort();
+		port.setName(original.getName());
+		if (original.getPortDepth() != null)
+			port.setDepth(original.getPortDepth().getValue());
+		if (original.getGranularPortDepth() != null)
+			port.setGranularDepth(original.getGranularPortDepth().getValue());
+		port.setParent(getParserState().getCurrent(
+				org.apache.taverna.scufl2.api.core.Processor.class));
+		mapBean(getParserState().getCurrentBase().resolve(original.getAbout()),
+				port);
+	}
+
+	@SuppressWarnings("unchecked")
+	protected void readWorkflow(URI wfUri, URI source) throws ReaderException,
+			IOException {
+		if (source.isAbsolute())
+			throw new ReaderException("Can't read external workflow source "
+					+ source);
+
+		InputStream bundleStream = getParserState().getUcfPackage()
+				.getResourceAsInputStream(source.getRawPath());
+
+		JAXBElement<WorkflowDocument> elem;
+		try {
+			elem = (JAXBElement<WorkflowDocument>) unmarshaller
+					.unmarshal(bundleStream);
+		} catch (JAXBException e) {
+			throw new ReaderException(
+					"Can't parse workflow document " + source, e);
+		}
+
+		URI base = getParserState().getLocation().resolve(source);
+		if (elem.getValue().getBase() != null)
+			base = base.resolve(elem.getValue().getBase());
+
+		if (elem.getValue().getAny().size() != 1)
+			throw new ReaderException("Expects only a <Workflow> element in "
+					+ source);
+		org.apache.taverna.scufl2.rdfxml.jaxb.Workflow workflow = (org.apache.taverna.scufl2.rdfxml.jaxb.Workflow) elem
+				.getValue().getAny().get(0);
+
+		getParserState().setCurrentBase(base);
+		parseWorkflow(workflow, wfUri);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/impl/NamespacePrefixMapperJAXB_RI.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/impl/NamespacePrefixMapperJAXB_RI.java b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/impl/NamespacePrefixMapperJAXB_RI.java
new file mode 100644
index 0000000..822419a
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/java/org/apache/taverna/scufl2/rdfxml/impl/NamespacePrefixMapperJAXB_RI.java
@@ -0,0 +1,54 @@
+package org.apache.taverna.scufl2.rdfxml.impl;
+/*
+ *
+ * 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.
+ *
+*/
+
+
+import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
+
+public class NamespacePrefixMapperJAXB_RI extends NamespacePrefixMapper {
+	@Override
+	public String getPreferredPrefix(String namespaceUri, String suggestion,
+			boolean requirePrefix) {
+		switch (namespaceUri) {
+		case "http://www.w3.org/2001/XMLSchema-instance":
+			return "xsi";
+		case "http://ns.taverna.org.uk/2010/scufl2#":
+			return ""; // default
+		case "http://www.w3.org/1999/02/22-rdf-syntax-ns#":
+			return "rdf";
+		case "http://www.w3.org/2000/01/rdf-schema#":
+			return "rdfs";
+		case "http://purl.org/dc/elements/1.1/":
+			return "dc";
+		case "http://purl.org/dc/terms/":
+			return "dcterms";
+		case "http://www.w3.org/2002/07/owl#":
+			return "owl";
+		default:
+			return suggestion;
+		}
+	}
+
+	@Override
+	public String[] getPreDeclaredNamespaceUris() {
+		return new String[] {};
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleReader
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleReader b/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleReader
new file mode 100644
index 0000000..42ee9f0
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleReader
@@ -0,0 +1 @@
+org.apache.taverna.scufl2.rdfxml.RDFXMLReader

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleWriter
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleWriter b/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleWriter
new file mode 100644
index 0000000..d3370c3
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/resources/META-INF/services/org.apache.taverna.scufl2.api.io.WorkflowBundleWriter
@@ -0,0 +1 @@
+org.apache.taverna.scufl2.rdfxml.RDFXMLWriter

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context-osgi.xml
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context-osgi.xml b/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context-osgi.xml
new file mode 100644
index 0000000..e05618f
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context-osgi.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+
+<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:beans="http://www.springframework.org/schema/beans"
+	xsi:schemaLocation="http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
+		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<service ref="rdfXMLReader" interface="org.apache.taverna.scufl2.api.io.WorkflowBundleReader" />
+
+	<service ref="rdfXMLWriter" interface="org.apache.taverna.scufl2.api.io.WorkflowBundleWriter" />
+
+</beans:beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context.xml
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context.xml b/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context.xml
new file mode 100644
index 0000000..2560aac
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/main/resources/META-INF/spring/scufl2-rdfxml-context.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans 
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+	<bean id="rdfXMLReader" class="org.apache.taverna.scufl2.rdfxml.RDFXMLReader" />
+
+	<bean id="rdfXMLWriter" class="org.apache.taverna.scufl2.rdfxml.RDFXMLWriter" />
+
+</beans>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/e27d1dbe/taverna-scufl2-wfbundle/src/test/java/org/apache/taverna/scufl2/rdfxml/DummyParserTest.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-wfbundle/src/test/java/org/apache/taverna/scufl2/rdfxml/DummyParserTest.java b/taverna-scufl2-wfbundle/src/test/java/org/apache/taverna/scufl2/rdfxml/DummyParserTest.java
new file mode 100644
index 0000000..8f5d548
--- /dev/null
+++ b/taverna-scufl2-wfbundle/src/test/java/org/apache/taverna/scufl2/rdfxml/DummyParserTest.java
@@ -0,0 +1,100 @@
+package org.apache.taverna.scufl2.rdfxml;
+/*
+ *
+ * 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.
+ *
+*/
+
+
+import java.net.URI;
+import java.net.URL;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.taverna.scufl2.rdfxml.jaxb.ObjectFactory;
+import org.apache.taverna.scufl2.rdfxml.jaxb.Profile;
+import org.apache.taverna.scufl2.rdfxml.jaxb.ProfileDocument;
+import org.apache.taverna.scufl2.rdfxml.jaxb.Workflow;
+import org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundle;
+import org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowBundleDocument;
+import org.apache.taverna.scufl2.rdfxml.jaxb.WorkflowDocument;
+
+public class DummyParserTest {
+
+	private JAXBContext jaxbContext;
+	private Unmarshaller unmarshaller;
+
+	@SuppressWarnings("unchecked")
+	@Test
+	public void parse() throws Exception {
+		URL resource = getClass().getResource("example/workflowBundle.rdf");
+		URI baseUri = resource.toURI();
+
+		@SuppressWarnings("rawtypes")
+		JAXBElement<WorkflowBundleDocument> workflowBundle = (JAXBElement<WorkflowBundleDocument>) unmarshaller
+				.unmarshal(resource);
+		WorkflowBundleDocument bundleDoc = workflowBundle.getValue();
+		WorkflowBundle wfBundle = (WorkflowBundle) bundleDoc.getAny().get(0);
+
+		//System.out.println(wfBundle.getName());
+		//System.out.println(wfBundle.getMainWorkflow());
+		//System.out.println(wfBundle.getSameBaseAs().getResource());
+		for (WorkflowBundle.Workflow wfLink : wfBundle.getWorkflow()) {
+			String about = wfLink.getWorkflow().getAbout();
+			String seeAlso = wfLink.getWorkflow().getSeeAlso().getResource();
+
+			URI wfResource = baseUri.resolve(seeAlso);
+			JAXBElement<WorkflowDocument> unmarshalled = (JAXBElement<WorkflowDocument>) unmarshaller
+					.unmarshal(wfResource.toURL());
+			WorkflowDocument wfDoc = unmarshalled.getValue();
+			Workflow wf = (Workflow) wfDoc.getAny().get(0);
+			//System.out.println(about + " " + wf.getName());
+		}
+
+		for (WorkflowBundle.Profile profileLink : wfBundle.getProfile()) {
+			String about = profileLink.getProfile().getAbout();
+			String seeAlso = profileLink.getProfile().getSeeAlso()
+					.getResource();
+
+			URI profileResource = baseUri.resolve(seeAlso);
+			JAXBElement unmarshalled = (JAXBElement) unmarshaller
+					.unmarshal(profileResource.toURL());
+			ProfileDocument profileDoc = (ProfileDocument) unmarshalled
+					.getValue();
+			Profile profile = (Profile) profileDoc.getAny().get(0);
+			//System.out.println(about + " " + profile.getName());
+		}
+	}
+
+	@Before
+	public void makeUnmarshaller() throws JAXBException {
+		
+		Class<?>[] packages = { ObjectFactory.class,
+				org.w3._1999._02._22_rdf_syntax_ns_.ObjectFactory.class,
+				org.w3._2000._01.rdf_schema_.ObjectFactory.class };
+		jaxbContext = JAXBContext.newInstance(packages);	
+		unmarshaller = jaxbContext.createUnmarshaller();
+	}
+
+}