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:22 UTC

[37/79] [partial] incubator-taverna-language git commit: Revert "temporarily empty repository"

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/main/resources/ontologies/ro.owl
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/main/resources/ontologies/ro.owl b/taverna-robundle/src/main/resources/ontologies/ro.owl
new file mode 100644
index 0000000..c64f5ae
--- /dev/null
+++ b/taverna-robundle/src/main/resources/ontologies/ro.owl
@@ -0,0 +1,218 @@
+<?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.
+-->
+<!-- Processed by Id: cwm.py,v 1.197 2007/12/13 15:38:39 syosi Exp -->
+<!--     using base file:///home/stain/stuff/src/wf4ever/ro/ro.owl-->
+
+
+<rdf:RDF xmlns="http://www.w3.org/2002/07/owl#"
+    xmlns:owl="http://www.w3.org/2002/07/owl#"
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
+
+    <Ontology rdf:about="http://purl.org/wf4ever/ro">
+        <rdfs:comment xml:lang="en">This ontology shows how AO and ORE ontologies can be used together to define a ResearchObject. This ontology is further customized by the wf4ever ontology.</rdfs:comment>
+        <rdfs:seeAlso rdf:resource="http://www.wf4ever-project.org/wiki/display/docs/Research+Object+Vocabulary+Specification"/>
+        <imports rdf:resource="http://purl.org/NET/dc_owl2dl/terms_od"/>
+        <imports rdf:resource="http://purl.org/ao/core/"/>
+        <imports rdf:resource="http://purl.org/wf4ever/ore-owl"/>
+        <imports rdf:resource="http://xmlns.com/foaf/0.1/"/>
+        <versionIRI rdf:resource="https://raw.github.com/wf4ever/ro/0.1/ro.owl"/>
+    </Ontology>
+
+    <Class rdf:about="http://purl.org/wf4ever/ro#AggregatedAnnotation">
+        <rdfs:comment xml:lang="en">An annotation aggregated within an ro:ResearchObject.  
+
+Instances of this class are used to annotated resources aggregated within the aggregating research object, proxies of these resources, or the research object itself. In other words, if :ro is the ro:ResearchObject this annotation has been ore:isAggregatedBy, then the annotation should have at least one ao:annotatesResource which is an ore:AggregatedResource which is ore:isAggregatedBy :ro, or the annotated resource is an ore:Proxy which ore:proxyIn :ro, or the annotated resource is :ro.
+
+It is possible for the annotation to also annotate non-aggregated resources, but as above, at least one of them needs to be part of the RO or the RO itself.
+
+As a subclass of ro:SemanticAnnotation the ao:body must point to an rdfg:Graph which contains the actual annotation. </rdfs:comment>
+        <rdfs:subClassOf rdf:resource="http://purl.org/wf4ever/ro#SemanticAnnotation"/>
+        <rdfs:subClassOf rdf:resource="http://www.openarchives.org/ore/terms/AggregatedResource"/>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://www.openarchives.org/ore/terms/isAggregatedBy"/>
+            <someValuesFrom rdf:resource="http://purl.org/wf4ever/ro#ResearchObject"/>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://purl.org/ao/annotatesResource"/>
+            <someValuesFrom rdf:parseType="Resource">
+                <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
+                <unionOf rdf:parseType="Resource">
+                    <rdf:first rdf:resource="http://purl.org/wf4ever/ro#ResearchObject"/>
+                    <rdf:rest rdf:parseType="Resource">
+                        <rdf:first rdf:resource="http://www.openarchives.org/ore/terms/AggregatedResource"/>
+                        <rdf:rest rdf:parseType="Resource">
+                            <rdf:first rdf:resource="http://www.openarchives.org/ore/terms/Proxy"/>
+                            <rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
+                        </rdf:rest>
+                    </rdf:rest>
+                </unionOf>
+            </someValuesFrom>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://purl.org/dc/terms/created"/>
+            <someValuesFrom rdf:resource="http://www.w3.org/2001/XMLSchema#dateTime"/>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://purl.org/dc/terms/creator"/>
+            <someValuesFrom rdf:resource="http://xmlns.com/foaf/0.1/Agent"/>
+        </rdfs:subClassOf>
+    </Class>
+
+    <Class rdf:about="http://purl.org/wf4ever/ro#Folder">
+        <rdfs:comment xml:lang="en">An ro:Folder is a special kind of ore:Aggregation where every ro:AggregatedResource must have a ro:FolderEntry proxy with a unique ro:entryName within that folder.
+
+Note that all resources which are aggregated within an (potentially nested) ro:Folder SHOULD also be aggregated by the same ro:ResearchObject this ro:Folder is aggregated within.
+
+Such folders can be nested and (optionally) used to organize the resources of the research object into a file-like structure. All such resources should also be aggregated by the ro:ResearchObject
+          </rdfs:comment>
+        <rdfs:subClassOf rdf:resource="http://purl.org/wf4ever/ro#Resource"/>
+        <rdfs:subClassOf rdf:resource="http://www.openarchives.org/ore/terms/Aggregation"/>
+    </Class>
+
+    <Class rdf:about="http://purl.org/wf4ever/ro#FolderEntry">
+        <rdfs:comment xml:lang="en">An ro:FolderEntry is any ore:Proxy instance that associates a resources aggregated within an ro:Folder with a ro:entryName. This name is (case-sensitive) unique within a given folder.</rdfs:comment>
+        <rdfs:subClassOf rdf:resource="http://www.openarchives.org/ore/terms/Proxy"/>
+        <equivalentClass rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://purl.org/wf4ever/ro#entryName"/>
+            <someValuesFrom rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
+        </equivalentClass>
+        <equivalentClass rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://www.openarchives.org/ore/terms/proxyIn"/>
+            <someValuesFrom rdf:resource="http://purl.org/wf4ever/ro#Folder"/>
+        </equivalentClass>
+        <hasKey rdf:parseType="Resource">
+            <rdf:first rdf:resource="http://www.openarchives.org/ore/terms/proxyIn"/>
+            <rdf:rest rdf:parseType="Resource">
+                <rdf:first rdf:resource="http://purl.org/wf4ever/ro#entryName"/>
+                <rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
+            </rdf:rest>
+        </hasKey>
+    </Class>
+
+    <Class rdf:about="http://purl.org/wf4ever/ro#Manifest">
+        <rdfs:comment xml:lang="en">The ro:Manifest is used to describe an ro:ResearchObject. This identifies the resource for the manifest which lists all the aggregations of the research object, typically called ".ro/manifest.rdf" relative to the research object this manifest ore:describes.</rdfs:comment>
+        <rdfs:subClassOf rdf:resource="http://www.openarchives.org/ore/terms/ResourceMap"/>
+        <equivalentClass rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://www.openarchives.org/ore/terms/describes"/>
+            <someValuesFrom rdf:resource="http://purl.org/wf4ever/ro#ResearchObject"/>
+        </equivalentClass>
+    </Class>
+
+    <Class rdf:about="http://purl.org/wf4ever/ro#ResearchObject">
+        <rdfs:comment xml:lang="en">A research object aggregates a number of resources. A resource can be a workflow, web service, document, data item, data set, workflow run, software or a research object.</rdfs:comment>
+        <rdfs:subClassOf rdf:resource="http://www.openarchives.org/ore/terms/Aggregation"/>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://purl.org/dc/terms/created"/>
+            <someValuesFrom rdf:resource="http://www.w3.org/2001/XMLSchema#dateTime"/>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://purl.org/dc/terms/creator"/>
+            <someValuesFrom rdf:resource="http://xmlns.com/foaf/0.1/Agent"/>
+        </rdfs:subClassOf>
+        <equivalentClass rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://www.openarchives.org/ore/terms/isDescribedBy"/>
+            <someValuesFrom rdf:resource="http://purl.org/wf4ever/ro#Manifest"/>
+        </equivalentClass>
+    </Class>
+
+    <Class rdf:about="http://purl.org/wf4ever/ro#Resource">
+        <rdfs:comment xml:lang="en">An ro:Resource is an ore:AggregatedResource which ore:isAggregatedBy an ro:ResearchObject. 
+
+This specialisation requires that there exists an ore:Proxy which is ore:proxyFor this resource, and which is ore:proxyIn the same ro:ResearchObject the resource ore:isAggregatedBy. Any annotations on such a proxy will descrive the ro:Resource within that particular ro:ResearchObject, in particular dct:creator and dct:created on the proxy will specify who added the resource to the aggregation at what time.
+
+Note that annotations (ro:AggregatedAnnotation) can be added to both the ro:Resource and the ore:Proxy - depending on if the annotation is seen to be globally true (such as the provenance of how the resource was created) or locally true within the Research Object (such as the the resource playing the role of a wf4ever:Dataset).
+
+Not all resources aggregated by an ro:ResearchObject are ro:Resource instances, in particular ro:AggregatedAnnotations will also be aggregated, but will not be "true" RO resources (and thus don't need their own ore:Proxy).  
+
+Aggregated resources MAY also be organised in (potentially nested) ro:Folders to reflect a file-system like structure. Note that any such resources SHOULD also be aggregated in the "mother" ro:ResearchObject.
+</rdfs:comment>
+        <rdfs:subClassOf rdf:resource="http://www.openarchives.org/ore/terms/AggregatedResource"/>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:resource="http://www.openarchives.org/ore/terms/isAggregatedBy"/>
+            <someValuesFrom rdf:resource="http://purl.org/wf4ever/ro#ResearchObject"/>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <onProperty rdf:parseType="Resource">
+                <inverseOf rdf:resource="http://www.openarchives.org/ore/terms/proxyFor"/>
+            </onProperty>
+            <someValuesFrom rdf:parseType="Resource">
+                <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+                <onProperty rdf:resource="http://www.openarchives.org/ore/terms/proxyIn"/>
+                <someValuesFrom rdf:resource="http://purl.org/wf4ever/ro#ResearchObject"/>
+            </someValuesFrom>
+        </rdfs:subClassOf>
+    </Class>
+
+    <Class rdf:about="http://purl.org/wf4ever/ro#SemanticAnnotation">
+        <rdfs:comment xml:lang="en">An ro:SemanticAnnotation is a specialisation of ao:Annotation which requires that ao:body points to an RDF Graph.
+
+This might be a Named Graph or a resource which can be resolved separately from the URI given by ao:body.
+
+This graph SHOULD mention the resources identified by ao:annotatesResource from this annotation, preferably by using their URIs as subject or object of statements.
+
+Note that this use of ao:body is distinct from ao:hasTopic, which also allows the association of a an RDF Graph with an ao:Annotation, but which also implies that this graph is the "topic" (subproperty of bookmark:hasTopic) of the annotated resource. This class does not require this interpretation, it is merely enough that the annotation body mentions the annotated resource, for instance to give it a dc:title or to relate two annotated resources.  Also note that the next version of the AO ontology (v2) might change this definition of ao:hasTopic, removing the need for this class.</rdfs:comment>
+        <rdfs:seeAlso rdf:resource="http://code.google.com/p/annotation-ontology/wiki/GraphsAnnotations"/>
+        <rdfs:seeAlso rdf:resource="http://purl.org/ao/body"/>
+        <rdfs:seeAlso rdf:resource="http://purl.org/ao/hasTopic"/>
+        <rdfs:seeAlso rdf:resource="http://www.w3.org/2001/Annotea/User/BookmarkSchema"/>
+        <rdfs:seeAlso rdf:resource="http://www.w3.org/2002/01/bookmark#hasTopic"/>
+        <rdfs:subClassOf rdf:resource="http://purl.org/ao/Annotation"/>
+        <rdfs:subClassOf rdf:parseType="Resource">
+            <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Restriction"/>
+            <allValuesFrom rdf:resource="http://www.w3.org/2004/03/trix/rdfg-1/Graph"/>
+            <onProperty rdf:resource="http://purl.org/ao/body"/>
+        </rdfs:subClassOf>
+    </Class>
+
+    <ObjectProperty rdf:about="http://purl.org/wf4ever/ro#annotatesAggregatedResource">
+        <rdfs:comment xml:lang="en">ro:annotatesAggregatedResource specifies that an ao:Annotation annotates an aggregated ro:Resource. 
+                               
+When used on an ro:AggregatedAnnotation, both the domain and range of this property must ore:isAggregatedBy the same ro:ResearchObject.  
+
+TODO: Should also ro:ResearchObject and ore:Proxy be in the range of this property, or is this subproperty even needed?
+</rdfs:comment>
+        <rdfs:domain rdf:resource="http://purl.org/ao/Annotation"/>
+        <rdfs:range rdf:resource="http://purl.org/wf4ever/ro#Resource"/>
+        <rdfs:subPropertyOf rdf:resource="http://purl.org/ao/annotatesResource"/>
+    </ObjectProperty>
+
+    <DatatypeProperty rdf:about="http://purl.org/wf4ever/ro#entryName">
+        <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
+        <rdfs:comment xml:lang="en">This functional property specifies the name of a ro:FolderEntry within an ro:Folder. 
+
+This name must be case-sensitively unique within the ro:Folder, similar to a filename in a directory.
+
+TODO: Need a functional property to specify the top level folder structure of an {{ro:ResearchObject}}?
+</rdfs:comment>
+        <rdfs:domain rdf:resource="http://purl.org/wf4ever/ro#FolderEntry"/>
+        <rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
+    </DatatypeProperty>
+</rdf:RDF>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.rng
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.rng b/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.rng
new file mode 100644
index 0000000..af13a26
--- /dev/null
+++ b/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.rng
@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+        Open Document Format for Office Applications (OpenDocument) Version 1.2
+        OASIS Standard, 29 September 2011
+	Manifest Relax-NG Schema
+        Source: http://docs.oasis-open.org/office/v1.2/os/
+        Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
+
+	All capitalized terms in the following text have the meanings assigned to them
+   	in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
+	full Policy may be found at the OASIS website.
+
+	This document and translations of it may be copied and furnished to others, and
+	derivative works that comment on or otherwise explain it or assist in its
+	implementation may be prepared, copied, published, and distributed, in whole or
+	in part, without restriction of any kind, provided that the above copyright
+	notice and this section are included on all such copies and derivative works.
+	However, this document itself may not be modified in any way, including by
+	removing the copyright notice or references to OASIS, except as needed for the
+	purpose of developing any document or deliverable produced by an OASIS
+	Technical Committee (in which case the rules applicable to copyrights, as set
+	forth in the OASIS IPR Policy, must be followed) or as required to translate it
+	into languages other than English.
+
+	The limited permissions granted above are perpetual and will not be revoked by
+	OASIS or its successors or assigns.
+
+	This document and the information contained herein is provided on an "AS IS"
+	basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+	LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
+	INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+	FITNESS FOR A PARTICULAR PURPOSE. 
+-->
+<grammar 
+	xmlns="http://relaxng.org/ns/structure/1.0"
+
+	datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+
+	xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
+>
+<start>
+	<choice>
+		<ref name="manifest"/>
+	</choice>
+</start>
+<define name="manifest">
+	<element name="manifest:manifest">
+		<ref name="manifest-attlist"/>
+		<oneOrMore>
+			<ref name="file-entry"/>
+		</oneOrMore>
+	</element>
+</define>
+<define name="manifest-attlist">
+	<attribute name="manifest:version">
+		<value>1.2</value>
+	</attribute>
+</define>
+<define name="file-entry">
+	<element name="manifest:file-entry">
+		<ref name="file-entry-attlist"/>
+		<optional>
+			<ref name="encryption-data"/>
+		</optional>
+	</element>
+</define>
+<define name="file-entry-attlist">
+  <interleave>
+	<attribute name="manifest:full-path">
+		<ref name="string"/>
+	</attribute>
+	<optional>
+		<attribute name="manifest:size">
+			<ref name="nonNegativeInteger"/>
+		</attribute>
+	</optional>
+	<attribute name="manifest:media-type">
+		<ref name="string"/>
+	</attribute>
+	<optional>
+		<attribute name="manifest:preferred-view-mode">
+			<choice>
+				<value>edit</value>
+				<value>presentation-slide-show</value>
+				<value>read-only</value>
+				<ref name="namespacedToken"/> 
+			</choice> 
+		</attribute> 
+	</optional> 
+	<optional>
+		<attribute name="manifest:version">
+			<ref name="string"/>
+		</attribute>
+	</optional>
+  </interleave>
+</define>
+
+<define name="encryption-data">
+	<element name="manifest:encryption-data">
+		<ref name="encryption-data-attlist"/>
+		<ref name="algorithm"/>
+		<optional>
+			<ref name="start-key-generation"/>
+		</optional>
+		<ref name="key-derivation"/>
+	</element>
+</define>
+<define name="encryption-data-attlist">
+  <interleave>
+	<attribute name="manifest:checksum-type">
+		<choice>
+			<value>SHA1/1K</value>
+			<ref name="anyURI"/>
+		</choice>
+	</attribute>
+	<attribute name="manifest:checksum">
+		<ref name="base64Binary"/>
+	</attribute>
+  </interleave>
+</define>
+<define name="algorithm">
+	<element name="manifest:algorithm">
+		<ref name="algorithm-attlist"/>
+		<ref name="anyElements"/>
+	</element>
+</define>
+<define name="algorithm-attlist">
+  <interleave>
+	<attribute name="manifest:algorithm-name">
+		<choice>
+			<value>Blowfish CFB</value>
+			<ref name="anyURI"/>
+		</choice>
+	</attribute>
+	<attribute name="manifest:initialisation-vector">
+		<ref name="base64Binary"/>
+	</attribute>
+  </interleave>
+</define>
+<define name="anyAttListOrElements">
+	<zeroOrMore>
+		<attribute>
+			<anyName/>
+			<text/>
+		</attribute>
+	</zeroOrMore>
+	<ref name="anyElements"/>
+</define>
+<define name="anyElements">
+	<zeroOrMore>
+		<element>
+			<anyName/>
+			<mixed>
+				<ref name="anyAttListOrElements"/>
+			</mixed>
+		</element>
+	</zeroOrMore>
+</define>
+<define name="key-derivation">
+	<element name="manifest:key-derivation">
+		<ref name="key-derivation-attlist"/>
+		<empty/>
+	</element>
+</define>
+<define name="key-derivation-attlist">
+  <interleave>
+	<attribute name="manifest:key-derivation-name">
+		<choice>
+			<value>PBKDF2</value>
+			<ref name="anyURI"/>
+		</choice>
+	</attribute>
+	<attribute name="manifest:salt">
+		<ref name="base64Binary"/>
+	</attribute>
+	<attribute name="manifest:iteration-count">
+			<ref name="nonNegativeInteger"/>
+		</attribute>
+	<optional>
+		<attribute name="manifest:key-size">
+			<ref name="nonNegativeInteger"/>
+		</attribute>
+	</optional>
+  </interleave>
+</define>
+<define name="start-key-generation">
+	<element name="manifest:start-key-generation">
+		<ref name="start-key-generation-attlist"/>
+		<empty/>
+	</element>
+</define>
+<define name="start-key-generation-attlist">
+  <interleave>
+	<attribute name="manifest:start-key-generation-name">
+		<choice>
+			<value>SHA1</value>
+			<ref name="anyURI"/>
+		</choice>
+	</attribute>
+	<optional>
+		<attribute name="manifest:key-size">
+			<ref name="nonNegativeInteger"/>
+		</attribute>
+	</optional>
+  </interleave>
+</define>
+<define name="base64Binary">
+	<data type="base64Binary"/>
+</define>
+<define name="namespacedToken">
+	<data type="QName">
+		<param name="pattern">[^:]+:[^:]+</param>
+	</data>
+</define>
+<define name="nonNegativeInteger">
+	<data type="nonNegativeInteger"/>
+</define>
+<define name="string">
+	<data type="string"/>
+</define>
+<define name="anyURI">
+	<data type="anyURI"/>
+</define>
+</grammar>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.xsd
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.xsd b/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.xsd
new file mode 100644
index 0000000..9e5e863
--- /dev/null
+++ b/taverna-robundle/src/main/xsd/OpenDocument-v1.2-os-manifest-schema.xsd
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Open Document Format for Office Applications (OpenDocument) Version 1.2
+  OASIS Standard, 29 September 2011
+  Manifest Relax-NG Schema
+  Source: http://docs.oasis-open.org/office/v1.2/os/
+  Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
+  
+  All capitalized terms in the following text have the meanings assigned to them
+  in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
+  full Policy may be found at the OASIS website.
+  
+  This document and translations of it may be copied and furnished to others, and
+  derivative works that comment on or otherwise explain it or assist in its
+  implementation may be prepared, copied, published, and distributed, in whole or
+  in part, without restriction of any kind, provided that the above copyright
+  notice and this section are included on all such copies and derivative works.
+  However, this document itself may not be modified in any way, including by
+  removing the copyright notice or references to OASIS, except as needed for the
+  purpose of developing any document or deliverable produced by an OASIS
+  Technical Committee (in which case the rules applicable to copyrights, as set
+  forth in the OASIS IPR Policy, must be followed) or as required to translate it
+  into languages other than English.
+  
+  The limited permissions granted above are perpetual and will not be revoked by
+  OASIS or its successors or assigns.
+  
+  This document and the information contained herein is provided on an "AS IS"
+  basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+  LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
+  INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
+  FITNESS FOR A PARTICULAR PURPOSE. 
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
+  <xs:element name="manifest">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element maxOccurs="unbounded" ref="manifest:file-entry"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="manifest:manifest-attlist"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:attributeGroup name="manifest-attlist">
+    <xs:attribute name="version" use="required" form="qualified">
+      <xs:simpleType>
+        <xs:restriction base="xs:token">
+          <xs:enumeration value="1.2"/>
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:attribute>
+  </xs:attributeGroup>
+  <xs:element name="file-entry">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element minOccurs="0" ref="manifest:encryption-data"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="manifest:file-entry-attlist"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:attributeGroup name="file-entry-attlist">
+    <xs:attribute name="full-path" use="required" form="qualified" type="manifest:string"/>
+    <xs:attribute name="size" form="qualified" type="manifest:nonNegativeInteger"/>
+    <xs:attribute name="media-type" use="required" form="qualified" type="manifest:string"/>
+    <xs:attribute name="preferred-view-mode" form="qualified">
+      <xs:simpleType>
+        <xs:union memberTypes="manifest:namespacedToken">
+          <xs:simpleType>
+            <xs:restriction base="xs:token">
+              <xs:enumeration value="edit"/>
+            </xs:restriction>
+          </xs:simpleType>
+          <xs:simpleType>
+            <xs:restriction base="xs:token">
+              <xs:enumeration value="presentation-slide-show"/>
+            </xs:restriction>
+          </xs:simpleType>
+          <xs:simpleType>
+            <xs:restriction base="xs:token">
+              <xs:enumeration value="read-only"/>
+            </xs:restriction>
+          </xs:simpleType>
+        </xs:union>
+      </xs:simpleType>
+    </xs:attribute>
+    <xs:attribute name="version" form="qualified" type="manifest:string"/>
+  </xs:attributeGroup>
+  <xs:element name="encryption-data">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="manifest:algorithm"/>
+        <xs:element minOccurs="0" ref="manifest:start-key-generation"/>
+        <xs:element ref="manifest:key-derivation"/>
+      </xs:sequence>
+      <xs:attributeGroup ref="manifest:encryption-data-attlist"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:attributeGroup name="encryption-data-attlist">
+    <xs:attribute name="checksum-type" use="required" form="qualified">
+      <xs:simpleType>
+        <xs:union memberTypes="manifest:anyURI">
+          <xs:simpleType>
+            <xs:restriction base="xs:token">
+              <xs:enumeration value="SHA1/1K"/>
+            </xs:restriction>
+          </xs:simpleType>
+        </xs:union>
+      </xs:simpleType>
+    </xs:attribute>
+    <xs:attribute name="checksum" use="required" form="qualified" type="manifest:base64Binary"/>
+  </xs:attributeGroup>
+  <xs:element name="algorithm">
+    <xs:complexType>
+      <xs:group ref="manifest:anyElements"/>
+      <xs:attributeGroup ref="manifest:algorithm-attlist"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:attributeGroup name="algorithm-attlist">
+    <xs:attribute name="algorithm-name" use="required" form="qualified">
+      <xs:simpleType>
+        <xs:union memberTypes="manifest:anyURI">
+          <xs:simpleType>
+            <xs:restriction base="xs:token">
+              <xs:enumeration value="Blowfish CFB"/>
+            </xs:restriction>
+          </xs:simpleType>
+        </xs:union>
+      </xs:simpleType>
+    </xs:attribute>
+    <xs:attribute name="initialisation-vector" use="required" form="qualified" type="manifest:base64Binary"/>
+  </xs:attributeGroup>
+  <xs:group name="anyAttListOrElements">
+    <xs:sequence>
+      <xs:group ref="manifest:anyElements"/>
+    </xs:sequence>
+  </xs:group>
+  <xs:attributeGroup name="anyAttListOrElements">
+    <xs:anyAttribute processContents="skip"/>
+  </xs:attributeGroup>
+  <xs:group name="anyElements">
+    <xs:sequence>
+      <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
+    </xs:sequence>
+  </xs:group>
+  <xs:element name="key-derivation">
+    <xs:complexType>
+      <xs:attributeGroup ref="manifest:key-derivation-attlist"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:attributeGroup name="key-derivation-attlist">
+    <xs:attribute name="key-derivation-name" use="required" form="qualified">
+      <xs:simpleType>
+        <xs:union memberTypes="manifest:anyURI">
+          <xs:simpleType>
+            <xs:restriction base="xs:token">
+              <xs:enumeration value="PBKDF2"/>
+            </xs:restriction>
+          </xs:simpleType>
+        </xs:union>
+      </xs:simpleType>
+    </xs:attribute>
+    <xs:attribute name="salt" use="required" form="qualified" type="manifest:base64Binary"/>
+    <xs:attribute name="iteration-count" use="required" form="qualified" type="manifest:nonNegativeInteger"/>
+    <xs:attribute name="key-size" form="qualified" type="manifest:nonNegativeInteger"/>
+  </xs:attributeGroup>
+  <xs:element name="start-key-generation">
+    <xs:complexType>
+      <xs:attributeGroup ref="manifest:start-key-generation-attlist"/>
+    </xs:complexType>
+  </xs:element>
+  <xs:attributeGroup name="start-key-generation-attlist">
+    <xs:attribute name="start-key-generation-name" use="required" form="qualified">
+      <xs:simpleType>
+        <xs:union memberTypes="manifest:anyURI">
+          <xs:simpleType>
+            <xs:restriction base="xs:token">
+              <xs:enumeration value="SHA1"/>
+            </xs:restriction>
+          </xs:simpleType>
+        </xs:union>
+      </xs:simpleType>
+    </xs:attribute>
+    <xs:attribute name="key-size" form="qualified" type="manifest:nonNegativeInteger"/>
+  </xs:attributeGroup>
+  <xs:simpleType name="base64Binary">
+    <xs:restriction base="xs:base64Binary"/>
+  </xs:simpleType>
+  <xs:simpleType name="namespacedToken">
+    <xs:restriction base="xs:QName">
+      <xs:pattern value="[^:]+:[^:]+"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="nonNegativeInteger">
+    <xs:restriction base="xs:nonNegativeInteger"/>
+  </xs:simpleType>
+  <xs:simpleType name="string">
+    <xs:restriction base="xs:string"/>
+  </xs:simpleType>
+  <xs:simpleType name="anyURI">
+    <xs:restriction base="xs:anyURI"/>
+  </xs:simpleType>
+</xs:schema>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/main/xsd/combine.xsd
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/main/xsd/combine.xsd b/taverna-robundle/src/main/xsd/combine.xsd
new file mode 100644
index 0000000..a8d356b
--- /dev/null
+++ b/taverna-robundle/src/main/xsd/combine.xsd
@@ -0,0 +1,102 @@
+<?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.
+-->
+<xsd:schema
+	targetNamespace="http://identifiers.org/combine.specifications/omex-manifest"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://identifiers.org/combine.specifications/omex-manifest"
+	elementFormDefault="qualified" attributeFormDefault="unqualified">
+    <xsd:annotation>
+    	<xsd:documentation># COMBINE Archive specification
+
+Based on Version 1, Draft, 2014-04-02
+http://co.mbine.org/specifications/combine_archive-Draft1.pdf
+
+Authors: 
+  * [Stian Soiland-Reyes](http://orcid.org/0000-0001-9842-9718)
+
+Copyright 2014, University of Manchester
+
+MIT license (See LICENSE.md or http://opensource.org/licenses/MIT)
+</xsd:documentation></xsd:annotation>
+    <xsd:element name="omexManifest">
+    	<xsd:annotation>
+    		<xsd:documentation>At the root of the COMBINE archive stands one file, with the prescribed name `manifest.xml`. 
+This file contains an instantiation of the *OmexManifest* class.</xsd:documentation>
+    	</xsd:annotation>
+    
+	    <xsd:complexType>
+	        <xsd:annotation>
+	        	<xsd:documentation>The root of the COMBINE archive.
+	
+	It contains a number of `content` entries, one of which represents the manifest itself.</xsd:documentation>
+	        </xsd:annotation>
+	        <xsd:sequence>
+	    		<xsd:element name="content" type="Content" minOccurs="1" maxOccurs="unbounded">
+	    			<xsd:annotation>
+	    				<xsd:documentation>An entry in the *OmexManifest*.
+	
+	Note that a valid manifest needs to have at least one entry, that of the manifest itself, but may contain as many
+	entries as needed.</xsd:documentation>
+	    			</xsd:annotation></xsd:element>
+	    	</xsd:sequence>
+	    </xsd:complexType>
+   	</xsd:element>
+
+            
+    <xsd:complexType name="Content">
+        <xsd:annotation>
+        	<xsd:documentation>The *Content* class represents an entry in the *OmexManifest* and by extension a file in the _COMBINE archive_.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:attribute name="location" type="xsd:string">
+    		<xsd:annotation>
+    			<xsd:documentation>The `location` attribute is a required attribute of type `string`. It represents a 
+relative location to an entry within the archive. The root of the archive 
+is represented by a dot `.`.</xsd:documentation>
+    		</xsd:annotation></xsd:attribute>
+    	<xsd:attribute name="format" type="xsd:string">
+    		<xsd:annotation>
+    			<xsd:documentation>The format is a required attribute of type `string`. It indicates the file type of the 
+*Content* element. The values of the `format` attribute fall in two categories. Either the
+`format` denotes one of the COMBINE standards, in which case the format will 
+begin with its `identifiers.org` url. Otherwise the format will represent a MIME type.
+Using identifiers.org allows to unambiguously define the COMBINE standard, and even its level and version.
+For example, the identifier:
+
+    http://identifiers.org/combine.specifications/sbml 
+
+would denote the *Content* element as being encoded in the SBML format. That is 
+usually sufficient, as tools supporting one level of SBML usually support others as
+well. However, if the software exporting the COMBINE archive wanted to be more
 precise, it could specify that it is an SBML Level 2 document with
+
+    http://identifiers.org/combine.specifications/sbml.level-2
+
+or even declare its Version with
+
     http://identifiers.org/combine.specifications/sbml.level-2.version-3
+</xsd:documentation>
+    		</xsd:annotation></xsd:attribute>
+    	<xsd:attribute name="master" type="xsd:boolean" use="optional" default="false">
+    		<xsd:annotation>
+    			<xsd:documentation>The `master` is an optional attribute of type `boolean`. It represents a hint, 
+that a certain file is to be used first when processing the content of an archive. 
+Are top model description in a composed model, calling the various submodels; 
+simulation description, calling the different model descriptions and 
+data sources used in the experiment.
+
+At most one content element per archive may have its master attribute set to `true`.</xsd:documentation>
+    		</xsd:annotation></xsd:attribute>
+    </xsd:complexType>
+</xsd:schema>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/main/xsd/container.xsd
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/main/xsd/container.xsd b/taverna-robundle/src/main/xsd/container.xsd
new file mode 100644
index 0000000..c34a24d
--- /dev/null
+++ b/taverna-robundle/src/main/xsd/container.xsd
@@ -0,0 +1,192 @@
+<?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.
+-->
+<xsd:schema targetNamespace="urn:oasis:names:tc:opendocument:xmlns:container"
+	elementFormDefault="qualified" attributeFormDefault="unqualified"
+	version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	xmlns:container="urn:oasis:names:tc:opendocument:xmlns:container"
+	xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
+	jaxb:version="2.0"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
+	<xsd:annotation>
+		<xsd:documentation>
+   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.
+
+			NOTE: urn:oasis:names:tc:opendocument:xmlns:container is *not* an
+			official namespace by OASIS, but the Adobe UCF specification defines
+			that a conforming META-INF/container.xml *must* use the *default*
+			namespace, eg:
+
+			&lt;container
+			xmlns="urn:oasis:names:tc:opendocument:xmlns:container"&gt;
+			...
+			&lt;/container&gt; </xsd:documentation>
+		<xsd:appinfo>
+			<!-- See http://docs.rakeshv.org/java/jaxb/users-guide/jaxb-custom.html -->
+			<jaxb:globalBindings />
+			<jaxb:schemaBindings>
+				<jaxb:package name="org.oasis_open.names.tc.opendocument.xmlns.container" />
+			</jaxb:schemaBindings>
+		</xsd:appinfo>
+	</xsd:annotation>
+
+	<!-- xmldsig-core-schema.xsd is also included by xenc-schema.xsd -->
+	<xsd:import namespace='http://www.w3.org/2000/09/xmldsig#'
+		schemaLocation='./xmldsig-core-schema.xsd' />
+		<!--schemaLocation="http://www.w3.org/TR/2002/REC-xmlenc-core-20021210/xenc-schema.xsd" -->
+	<xsd:import namespace="http://www.w3.org/2001/04/xmlenc#"
+		schemaLocation="xenc-schema.xsd" />
+		<!--schemaLocation='http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd' -->
+
+	<xsd:element name="container" type="container:Container"></xsd:element>
+
+	<xsd:complexType name="Container">
+
+		<xsd:sequence>
+			<xsd:choice minOccurs="0" maxOccurs="2">
+				<xsd:element name="rootFiles">
+					<xsd:complexType>
+						<xsd:choice maxOccurs="unbounded">
+							<xsd:any namespace="##other" processContents="lax" />
+							<xsd:element name="rootFile" type="container:RootFile" />
+						</xsd:choice>
+						<xsd:anyAttribute namespace="##other"
+							processContents="lax" />
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:any namespace="##other" 
+					processContents="lax" maxOccurs="unbounded" />
+			</xsd:choice>
+
+			<xsd:sequence minOccurs="0">
+				<xsd:element name="relationships">
+					<xsd:complexType>
+						<xsd:choice maxOccurs="unbounded">
+							<xsd:any namespace="##other" processContents="lax" />
+							<xsd:element name="relationship" type="container:Relationship" />
+						</xsd:choice>
+						<xsd:anyAttribute namespace="##other"
+							processContents="lax" />
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"
+					processContents="lax">
+					<xsd:annotation>
+						<xsd:appinfo>
+							<jaxb:property name="any5" />
+						</xsd:appinfo>
+					</xsd:annotation>
+
+				</xsd:any>
+			</xsd:sequence>
+
+
+			<xsd:sequence minOccurs="0">
+				<xsd:element name="signatures">
+					<xsd:complexType>
+						<xsd:choice maxOccurs="unbounded">
+							<!-- Can't use ##other as dsig: is in other <xsd:any namespace="##other" 
+								processContents="lax"> <xsd:annotation> <xsd:appinfo> <jaxb:property name="any6" 
+								/> </xsd:appinfo> </xsd:annotation> </xsd:any> -->
+							<xsd:element ref="dsig:Signature" />
+						</xsd:choice>
+						<xsd:anyAttribute namespace="##other"
+							processContents="lax" />
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"
+					processContents="lax">
+					<xsd:annotation>
+						<xsd:appinfo>
+							<jaxb:property name="any7" />
+						</xsd:appinfo>
+					</xsd:annotation>
+				</xsd:any>
+			</xsd:sequence>
+
+			<xsd:sequence minOccurs="0">
+				<xsd:element name="encryption">
+					<xsd:complexType>
+						<xsd:choice maxOccurs="unbounded">
+							<xsd:element ref="enc:EncryptedData" />
+							<xsd:element ref="enc:EncryptedKey" />
+							<!-- Can't use ##other as enc: is in other <xsd:any namespace="##other" 
+								processContents="lax"> <xsd:annotation> <xsd:appinfo> <jaxb:property name="any8" 
+								/> </xsd:appinfo> </xsd:annotation> </xsd:any> -->
+						</xsd:choice>
+						<xsd:anyAttribute namespace="##other"
+							processContents="lax" />
+					</xsd:complexType>
+				</xsd:element>
+
+				<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"
+					processContents="lax">
+					<xsd:annotation>
+						<xsd:appinfo>
+							<jaxb:property name="any9" />
+						</xsd:appinfo>
+					</xsd:annotation>
+				</xsd:any>
+			</xsd:sequence>
+
+		</xsd:sequence>
+
+		<xsd:attribute name="version" type="xsd:string" fixed="1.0"></xsd:attribute>
+		<xsd:anyAttribute namespace="##other"
+			processContents="lax" />
+
+	</xsd:complexType>
+
+	<xsd:complexType name="RootFile">
+		<xsd:sequence>
+			<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"
+				processContents="lax" />
+		</xsd:sequence>
+		<xsd:attribute name="full-path" type="xsd:string" use="required">
+		</xsd:attribute>
+		<xsd:attribute name="media-type" type="xsd:string" use="required">
+		</xsd:attribute>
+		<xsd:anyAttribute namespace="##other"
+			processContents="lax" />
+
+	</xsd:complexType>
+
+	<xsd:complexType name="Relationship">
+		<xsd:sequence>
+			<xsd:any namespace="##other" minOccurs="0" maxOccurs="unbounded"
+				processContents="lax" />
+		</xsd:sequence>
+		<xsd:attribute name="type" type="xsd:string"></xsd:attribute>
+		<xsd:attribute name="target" type="xsd:string"></xsd:attribute>
+		<xsd:anyAttribute namespace="##other"
+			processContents="lax" />
+	</xsd:complexType>
+</xsd:schema>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/main/xsd/xenc-schema.xsd
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/main/xsd/xenc-schema.xsd b/taverna-robundle/src/main/xsd/xenc-schema.xsd
new file mode 100644
index 0000000..01a5c71
--- /dev/null
+++ b/taverna-robundle/src/main/xsd/xenc-schema.xsd
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright © 2002 World Wide Web Consortium, (Massachusetts
+Institute of Technology, Institut National de Recherche en Informatique et en
+Automatique, Keio University). All Rights Reserved.
+http://www.w3.org/Consortium/Legal/
+
+XML Encryption Syntax and Processing
+W3C Recommendation 10 December 2002
+http://www.w3.org/TR/xmlenc-core/xenc-schema.xsd
+http://www.w3.org/TR/xmlenc-core/
+     -->
+<schema xmlns='http://www.w3.org/2001/XMLSchema' version='1.0'
+        xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'
+        xmlns:ds='http://www.w3.org/2000/09/xmldsig#'
+        targetNamespace='http://www.w3.org/2001/04/xmlenc#'
+        elementFormDefault='qualified'>
+
+  <import namespace="http://www.w3.org/2000/09/xmldsig#"
+          schemaLocation="xmldsig-core-schema.xsd"/>
+
+  <complexType name='EncryptedType' abstract='true'>
+    <sequence>
+      <element name='EncryptionMethod' type='xenc:EncryptionMethodType'
+       minOccurs='0'/>
+      <element ref='ds:KeyInfo' minOccurs='0'/>
+      <element ref='xenc:CipherData'/>
+      <element ref='xenc:EncryptionProperties' minOccurs='0'/>
+    </sequence>
+    <attribute name='Id' type='ID' use='optional'/>
+    <attribute name='Type' type='anyURI' use='optional'/>
+    <attribute name='MimeType' type='string' use='optional'/>
+    <attribute name='Encoding' type='anyURI' use='optional'/>
+  </complexType>
+  
+  <complexType name='EncryptionMethodType' mixed='true'>
+    <sequence>
+      <element name='KeySize' minOccurs='0' type='xenc:KeySizeType'/>
+      <element name='OAEPparams' minOccurs='0' type='base64Binary'/>
+      <any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
+    </sequence>
+    <attribute name='Algorithm' type='anyURI' use='required'/>
+  </complexType>
+
+    <simpleType name='KeySizeType'>
+      <restriction base="integer"/>
+    </simpleType>
+
+  <element name='CipherData' type='xenc:CipherDataType'/>
+  <complexType name='CipherDataType'>
+     <choice>
+       <element name='CipherValue' type='base64Binary'/>
+       <element ref='xenc:CipherReference'/>
+     </choice>
+    </complexType>
+
+   <element name='CipherReference' type='xenc:CipherReferenceType'/>
+   <complexType name='CipherReferenceType'>
+       <choice>
+         <element name='Transforms' type='xenc:TransformsType' minOccurs='0'/>
+       </choice>
+       <attribute name='URI' type='anyURI' use='required'/>
+   </complexType>
+
+     <complexType name='TransformsType'>
+       <sequence>
+         <element ref='ds:Transform' maxOccurs='unbounded'/>
+       </sequence>
+     </complexType>
+
+
+  <element name='EncryptedData' type='xenc:EncryptedDataType'/>
+  <complexType name='EncryptedDataType'>
+    <complexContent>
+      <extension base='xenc:EncryptedType'>
+       </extension>
+    </complexContent>
+  </complexType>
+
+  <!-- Children of ds:KeyInfo -->
+
+  <element name='EncryptedKey' type='xenc:EncryptedKeyType'/>
+  <complexType name='EncryptedKeyType'>
+    <complexContent>
+      <extension base='xenc:EncryptedType'>
+        <sequence>
+          <element ref='xenc:ReferenceList' minOccurs='0'/>
+          <element name='CarriedKeyName' type='string' minOccurs='0'/>
+        </sequence>
+        <attribute name='Recipient' type='string'
+         use='optional'/>
+      </extension>
+    </complexContent>
+  </complexType>
+
+    <element name="AgreementMethod" type="xenc:AgreementMethodType"/>
+    <complexType name="AgreementMethodType" mixed="true">
+      <sequence>
+        <element name="KA-Nonce" minOccurs="0" type="base64Binary"/>
+        <!-- <element ref="ds:DigestMethod" minOccurs="0"/> -->
+        <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
+        <element name="OriginatorKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
+        <element name="RecipientKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
+      </sequence>
+      <attribute name="Algorithm" type="anyURI" use="required"/>
+    </complexType>
+
+  <!-- End Children of ds:KeyInfo -->
+
+  <element name='ReferenceList'>
+    <complexType>
+      <choice minOccurs='1' maxOccurs='unbounded'>
+        <element name='DataReference' type='xenc:ReferenceType'/>
+        <element name='KeyReference' type='xenc:ReferenceType'/>
+      </choice>
+    </complexType>
+  </element>
+
+  <complexType name='ReferenceType'>
+    <sequence>
+      <any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
+    </sequence>
+    <attribute name='URI' type='anyURI' use='required'/>
+  </complexType>
+
+
+  <element name='EncryptionProperties' type='xenc:EncryptionPropertiesType'/>
+  <complexType name='EncryptionPropertiesType'>
+    <sequence>
+      <element ref='xenc:EncryptionProperty' maxOccurs='unbounded'/>
+    </sequence>
+    <attribute name='Id' type='ID' use='optional'/>
+  </complexType>
+
+    <element name='EncryptionProperty' type='xenc:EncryptionPropertyType'/>
+    <complexType name='EncryptionPropertyType' mixed='true'>
+      <choice maxOccurs='unbounded'>
+        <any namespace='##other' processContents='lax'/>
+      </choice>
+      <attribute name='Target' type='anyURI' use='optional'/>
+      <attribute name='Id' type='ID' use='optional'/>
+      <anyAttribute namespace="http://www.w3.org/XML/1998/namespace"/>
+    </complexType>
+
+</schema>
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/main/xsd/xmldsig-core-schema.xsd
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/main/xsd/xmldsig-core-schema.xsd b/taverna-robundle/src/main/xsd/xmldsig-core-schema.xsd
new file mode 100644
index 0000000..8422fdf
--- /dev/null
+++ b/taverna-robundle/src/main/xsd/xmldsig-core-schema.xsd
@@ -0,0 +1,308 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Schema for XML Signatures
+    http://www.w3.org/2000/09/xmldsig#
+    $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
+
+    Copyright 2001 The Internet Society and W3C (Massachusetts Institute
+    of Technology, Institut National de Recherche en Informatique et en
+    Automatique, Keio University). All Rights Reserved.
+    http://www.w3.org/Consortium/Legal/
+
+    This document is governed by the W3C Software License [1] as described
+    in the FAQ [2].
+
+    [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
+    [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
+-->
+
+
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+        xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+        targetNamespace="http://www.w3.org/2000/09/xmldsig#"
+        version="0.1" elementFormDefault="qualified"> 
+
+<!-- Basic Types Defined for Signatures -->
+
+<simpleType name="CryptoBinary">
+  <restriction base="base64Binary">
+  </restriction>
+</simpleType>
+
+<!-- Start Signature -->
+
+<element name="Signature" type="ds:SignatureType"/>
+<complexType name="SignatureType">
+  <sequence> 
+    <element ref="ds:SignedInfo"/> 
+    <element ref="ds:SignatureValue"/> 
+    <element ref="ds:KeyInfo" minOccurs="0"/> 
+    <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/> 
+  </sequence>  
+  <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+  <element name="SignatureValue" type="ds:SignatureValueType"/> 
+  <complexType name="SignatureValueType">
+    <simpleContent>
+      <extension base="base64Binary">
+        <attribute name="Id" type="ID" use="optional"/>
+      </extension>
+    </simpleContent>
+  </complexType>
+
+<!-- Start SignedInfo -->
+
+<element name="SignedInfo" type="ds:SignedInfoType"/>
+<complexType name="SignedInfoType">
+  <sequence> 
+    <element ref="ds:CanonicalizationMethod"/> 
+    <element ref="ds:SignatureMethod"/> 
+    <element ref="ds:Reference" maxOccurs="unbounded"/> 
+  </sequence>  
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+  <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/> 
+  <complexType name="CanonicalizationMethodType" mixed="true">
+    <sequence>
+      <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
+      <!-- (0,unbounded) elements from (1,1) namespace -->
+    </sequence>
+    <attribute name="Algorithm" type="anyURI" use="required"/> 
+  </complexType>
+
+  <element name="SignatureMethod" type="ds:SignatureMethodType"/>
+  <complexType name="SignatureMethodType" mixed="true">
+    <sequence>
+      <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
+      <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
+      <!-- (0,unbounded) elements from (1,1) external namespace -->
+    </sequence>
+    <attribute name="Algorithm" type="anyURI" use="required"/> 
+  </complexType>
+
+<!-- Start Reference -->
+
+<element name="Reference" type="ds:ReferenceType"/>
+<complexType name="ReferenceType">
+  <sequence> 
+    <element ref="ds:Transforms" minOccurs="0"/> 
+    <element ref="ds:DigestMethod"/> 
+    <element ref="ds:DigestValue"/> 
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+  <attribute name="URI" type="anyURI" use="optional"/> 
+  <attribute name="Type" type="anyURI" use="optional"/> 
+</complexType>
+
+  <element name="Transforms" type="ds:TransformsType"/>
+  <complexType name="TransformsType">
+    <sequence>
+      <element ref="ds:Transform" maxOccurs="unbounded"/>  
+    </sequence>
+  </complexType>
+
+  <element name="Transform" type="ds:TransformType"/>
+  <complexType name="TransformType" mixed="true">
+    <choice minOccurs="0" maxOccurs="unbounded"> 
+      <any namespace="##other" processContents="lax"/>
+      <!-- (1,1) elements from (0,unbounded) namespaces -->
+      <element name="XPath" type="string"/> 
+    </choice>
+    <attribute name="Algorithm" type="anyURI" use="required"/> 
+  </complexType>
+
+<!-- End Reference -->
+
+<element name="DigestMethod" type="ds:DigestMethodType"/>
+<complexType name="DigestMethodType" mixed="true"> 
+  <sequence>
+    <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+  </sequence>    
+  <attribute name="Algorithm" type="anyURI" use="required"/> 
+</complexType>
+
+<element name="DigestValue" type="ds:DigestValueType"/>
+<simpleType name="DigestValueType">
+  <restriction base="base64Binary"/>
+</simpleType>
+
+<!-- End SignedInfo -->
+
+<!-- Start KeyInfo -->
+
+<element name="KeyInfo" type="ds:KeyInfoType"/> 
+<complexType name="KeyInfoType" mixed="true">
+  <choice maxOccurs="unbounded">     
+    <element ref="ds:KeyName"/> 
+    <element ref="ds:KeyValue"/> 
+    <element ref="ds:RetrievalMethod"/> 
+    <element ref="ds:X509Data"/> 
+    <element ref="ds:PGPData"/> 
+    <element ref="ds:SPKIData"/>
+    <element ref="ds:MgmtData"/>
+    <any processContents="lax" namespace="##other"/>
+    <!-- (1,1) elements from (0,unbounded) namespaces -->
+  </choice>
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+  <element name="KeyName" type="string"/>
+  <element name="MgmtData" type="string"/>
+
+  <element name="KeyValue" type="ds:KeyValueType"/> 
+  <complexType name="KeyValueType" mixed="true">
+   <choice>
+     <element ref="ds:DSAKeyValue"/>
+     <element ref="ds:RSAKeyValue"/>
+     <any namespace="##other" processContents="lax"/>
+   </choice>
+  </complexType>
+
+  <element name="RetrievalMethod" type="ds:RetrievalMethodType"/> 
+  <complexType name="RetrievalMethodType">
+    <sequence>
+      <element ref="ds:Transforms" minOccurs="0"/> 
+    </sequence>  
+    <attribute name="URI" type="anyURI"/>
+    <attribute name="Type" type="anyURI" use="optional"/>
+  </complexType>
+
+<!-- Start X509Data -->
+
+<element name="X509Data" type="ds:X509DataType"/> 
+<complexType name="X509DataType">
+  <sequence maxOccurs="unbounded">
+    <choice>
+      <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
+      <element name="X509SKI" type="base64Binary"/>
+      <element name="X509SubjectName" type="string"/>
+      <element name="X509Certificate" type="base64Binary"/>
+      <element name="X509CRL" type="base64Binary"/>
+      <any namespace="##other" processContents="lax"/>
+    </choice>
+  </sequence>
+</complexType>
+
+<complexType name="X509IssuerSerialType"> 
+  <sequence> 
+    <element name="X509IssuerName" type="string"/> 
+    <element name="X509SerialNumber" type="integer"/> 
+  </sequence>
+</complexType>
+
+<!-- End X509Data -->
+
+<!-- Begin PGPData -->
+
+<element name="PGPData" type="ds:PGPDataType"/> 
+<complexType name="PGPDataType"> 
+  <choice>
+    <sequence>
+      <element name="PGPKeyID" type="base64Binary"/> 
+      <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/> 
+      <any namespace="##other" processContents="lax" minOccurs="0"
+       maxOccurs="unbounded"/>
+    </sequence>
+    <sequence>
+      <element name="PGPKeyPacket" type="base64Binary"/> 
+      <any namespace="##other" processContents="lax" minOccurs="0"
+       maxOccurs="unbounded"/>
+    </sequence>
+  </choice>
+</complexType>
+
+<!-- End PGPData -->
+
+<!-- Begin SPKIData -->
+
+<element name="SPKIData" type="ds:SPKIDataType"/> 
+<complexType name="SPKIDataType">
+  <sequence maxOccurs="unbounded">
+    <element name="SPKISexp" type="base64Binary"/>
+    <any namespace="##other" processContents="lax" minOccurs="0"/>
+  </sequence>
+</complexType> 
+
+<!-- End SPKIData -->
+
+<!-- End KeyInfo -->
+
+<!-- Start Object (Manifest, SignatureProperty) -->
+
+<element name="Object" type="ds:ObjectType"/> 
+<complexType name="ObjectType" mixed="true">
+  <sequence minOccurs="0" maxOccurs="unbounded">
+    <any namespace="##any" processContents="lax"/>
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+  <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
+  <attribute name="Encoding" type="anyURI" use="optional"/> 
+</complexType>
+
+<element name="Manifest" type="ds:ManifestType"/> 
+<complexType name="ManifestType">
+  <sequence>
+    <element ref="ds:Reference" maxOccurs="unbounded"/> 
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+<element name="SignatureProperties" type="ds:SignaturePropertiesType"/> 
+<complexType name="SignaturePropertiesType">
+  <sequence>
+    <element ref="ds:SignatureProperty" maxOccurs="unbounded"/> 
+  </sequence>
+  <attribute name="Id" type="ID" use="optional"/> 
+</complexType>
+
+   <element name="SignatureProperty" type="ds:SignaturePropertyType"/> 
+   <complexType name="SignaturePropertyType" mixed="true">
+     <choice maxOccurs="unbounded">
+       <any namespace="##other" processContents="lax"/>
+       <!-- (1,1) elements from (1,unbounded) namespaces -->
+     </choice>
+     <attribute name="Target" type="anyURI" use="required"/> 
+     <attribute name="Id" type="ID" use="optional"/> 
+   </complexType>
+
+<!-- End Object (Manifest, SignatureProperty) -->
+
+<!-- Start Algorithm Parameters -->
+
+<simpleType name="HMACOutputLengthType">
+  <restriction base="integer"/>
+</simpleType>
+
+<!-- Start KeyValue Element-types -->
+
+<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
+<complexType name="DSAKeyValueType">
+  <sequence>
+    <sequence minOccurs="0">
+      <element name="P" type="ds:CryptoBinary"/>
+      <element name="Q" type="ds:CryptoBinary"/>
+    </sequence>
+    <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
+    <element name="Y" type="ds:CryptoBinary"/>
+    <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
+    <sequence minOccurs="0">
+      <element name="Seed" type="ds:CryptoBinary"/>
+      <element name="PgenCounter" type="ds:CryptoBinary"/>
+    </sequence>
+  </sequence>
+</complexType>
+
+<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
+<complexType name="RSAKeyValueType">
+  <sequence>
+    <element name="Modulus" type="ds:CryptoBinary"/> 
+    <element name="Exponent" type="ds:CryptoBinary"/> 
+  </sequence>
+</complexType> 
+
+<!-- End KeyValue Element-types -->
+
+<!-- End Signature -->
+
+</schema>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/test/java/org/apache/taverna/robundle/MotifAnalysisIT.java
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/test/java/org/apache/taverna/robundle/MotifAnalysisIT.java b/taverna-robundle/src/test/java/org/apache/taverna/robundle/MotifAnalysisIT.java
new file mode 100644
index 0000000..54958b8
--- /dev/null
+++ b/taverna-robundle/src/test/java/org/apache/taverna/robundle/MotifAnalysisIT.java
@@ -0,0 +1,126 @@
+package org.apache.taverna.robundle;
+
+/*
+ * 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.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.taverna.robundle.Bundle;
+import org.apache.taverna.robundle.Bundles;
+import org.apache.taverna.robundle.manifest.Agent;
+import org.apache.taverna.robundle.manifest.Manifest;
+import org.apache.taverna.robundle.manifest.PathAnnotation;
+import org.junit.Test;
+
+public class MotifAnalysisIT {
+	@Test
+	public void motifAnalysis() throws Exception {
+
+		// The new RO bundle
+		Path ro = Files.createTempFile("motifAnalysis", ".robundle.zip");
+		try (Bundle bundle = Bundles.createBundle(ro)) {
+
+			Path orig = Paths.get(getClass().getResource("/motifAnalysis.zip")
+					.toURI());
+
+			// Copy the motifAnalysis/ folder
+			try (FileSystem origfs = FileSystems.newFileSystem(orig, null)) {
+				Path origFolder = origfs.getPath("motifAnalysis/");
+				Bundles.copyRecursively(origFolder, bundle.getRoot(),
+						StandardCopyOption.REPLACE_EXISTING,
+						StandardCopyOption.COPY_ATTRIBUTES);
+			}
+
+			// TODO: Generating manifest should be automatic!
+
+			// Generate manifest
+			Manifest manifest = bundle.getManifest();
+
+			// attributions
+
+			// Stian made the RO bundle
+			Agent stian = new Agent();
+			stian.setUri(URI.create("http://soiland-reyes.com/stian/#me"));
+			stian.setOrcid(URI.create("http://orcid.org/0000-0001-9842-9718"));
+			stian.setName("Stian Soiland-Reyes");
+			manifest.setCreatedBy(stian);
+			// RO bundle was created "now"
+			manifest.setCreatedOn(Files.getLastModifiedTime(ro));
+
+			// but it was *authored* by Daniel et al
+
+			Agent daniel = new Agent();
+			daniel.setUri(URI
+					.create("http://delicias.dia.fi.upm.es/members/DGarijo/#me"));
+			daniel.setOrcid(URI.create("http://orcid.org/0000-0003-0454-7145"));
+			daniel.setName("Daniel Garijo");
+
+			List<Agent> authors = new ArrayList<>();
+			authors.add(daniel);
+			authors.add(new Agent("Pinar Alper"));
+			authors.add(new Agent("Khalid Belhajjame"));
+			authors.add(new Agent("Oscar Corcho"));
+			authors.add(new Agent("Yolanda Gil"));
+			authors.add(new Agent("Carole Goble"));
+			manifest.setAuthoredBy(authors);
+
+			// when was the RO authored? We'll say when the README was made..
+			Path readme = bundle.getRoot().resolve("README.txt");
+			manifest.setAuthoredOn(Files.getLastModifiedTime(readme));
+
+			// And who made the README file?
+			manifest.getAggregation(readme).setCreatedBy(daniel);
+			manifest.getAggregation(readme).setMediatype("text/plain");
+
+			// Annotations
+
+			PathAnnotation readmeAnnotation = new PathAnnotation();
+			readmeAnnotation.setAbout(URI.create("/"));
+			readmeAnnotation.setContent(URI.create("/README.txt"));
+			readmeAnnotation.generateAnnotationId();
+			manifest.getAnnotations().add(readmeAnnotation);
+
+			PathAnnotation website = new PathAnnotation();
+			website.setAbout(URI.create("/"));
+			website.setContent(URI
+					.create("http://www.oeg-upm.net/files/dgarijo/motifAnalysisSite/"));
+			website.generateAnnotationId();
+			manifest.getAnnotations().add(website);
+
+			// Write out manifest
+			// This is now done automatically on close()
+			// manifest.writeAsJsonLD();
+		}
+
+		System.out.println("Generated " + ro);
+		// if (Desktop.isDesktopSupported()) {
+		// Desktop.getDesktop().open(ro.toFile());
+		// }
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-robundle/src/test/java/org/apache/taverna/robundle/TestBundles.java
----------------------------------------------------------------------
diff --git a/taverna-robundle/src/test/java/org/apache/taverna/robundle/TestBundles.java b/taverna-robundle/src/test/java/org/apache/taverna/robundle/TestBundles.java
new file mode 100644
index 0000000..05ab53e
--- /dev/null
+++ b/taverna-robundle/src/test/java/org/apache/taverna/robundle/TestBundles.java
@@ -0,0 +1,643 @@
+package org.apache.taverna.robundle;
+
+/*
+ * 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.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.nio.charset.Charset;
+import java.nio.file.DirectoryNotEmptyException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.taverna.robundle.Bundle;
+import org.apache.taverna.robundle.Bundles;
+import org.apache.taverna.robundle.fs.BundleFileSystem;
+import org.apache.taverna.robundle.fs.BundleFileSystemProvider;
+import org.apache.taverna.robundle.manifest.Manifest;
+import org.apache.taverna.robundle.manifest.PathMetadata;
+import org.apache.taverna.robundle.utils.TemporaryFiles;
+import org.junit.Test;
+import org.junit.Ignore;
+
+public class TestBundles {
+
+	private static final int MIME_OFFSET = 30;
+
+	protected void checkSignature(Path zip) throws IOException {
+		String MEDIATYPE = "application/vnd.wf4ever.robundle+zip";
+		// Check position 30++ according to RO Bundle specification
+		// http://purl.org/wf4ever/ro-bundle#ucf
+		byte[] expected = ("mimetype" + MEDIATYPE + "PK").getBytes("ASCII");
+
+		try (InputStream in = Files.newInputStream(zip)) {
+			byte[] signature = new byte[expected.length];
+			int MIME_OFFSET = 30;
+			assertEquals(MIME_OFFSET, in.skip(MIME_OFFSET));
+			assertEquals(expected.length, in.read(signature));
+			assertArrayEquals(expected, signature);
+		}
+	}
+
+	@Test
+	public void closeDeleteTemp() throws Exception {
+		Bundle bundle = Bundles.createBundle();
+		assertTrue(Files.exists(bundle.getSource()));
+		assertTrue(bundle.getFileSystem().isOpen());
+		assertTrue(bundle.isDeleteOnClose());
+		bundle.close();
+		assertFalse(Files.exists(bundle.getSource()));
+		assertFalse(bundle.getFileSystem().isOpen());
+	}
+
+	@Test
+	public void closeNotDelete() throws Exception {
+		Path path = Files.createTempFile("bundle", ".zip");
+		Bundle bundle = Bundles.createBundle(path);
+		assertFalse(bundle.isDeleteOnClose());
+		assertTrue(Files.exists(bundle.getSource()));
+		assertTrue(bundle.getFileSystem().isOpen());
+
+		bundle.close();
+		assertTrue(Files.exists(bundle.getSource()));
+		assertFalse(bundle.getFileSystem().isOpen());
+	}
+
+	@Test
+	public void closeAndOpenBundle() throws Exception {
+		Bundle bundle = Bundles.createBundle();
+		Path zip = Bundles.closeBundle(bundle);
+		Bundles.openBundle(zip).close();
+	}
+
+	@Test
+	public void closeAndOpenBundleWithStringValue() throws Exception {
+		Bundle bundle = Bundles.createBundle();
+		Path hello = bundle.getRoot().resolve("hello.txt");
+		Bundles.setStringValue(hello, "Hello");
+		Path zip = Bundles.closeBundle(bundle);
+
+		try (Bundle newBundle = Bundles.openBundle(zip)) {
+			Path newHello = newBundle.getRoot().resolve("hello.txt");
+			assertEquals("Hello", Bundles.getStringValue(newHello));
+		}
+	}
+
+	@Test
+	public void closeAndSaveBundleDelete() throws Exception {
+		Bundle bundle = Bundles.createBundle();
+		Path destination = Files.createTempFile("test", ".zip");
+		destination.toFile().deleteOnExit();
+		Files.delete(destination);
+		assertFalse(Files.exists(destination));
+		Bundles.closeAndSaveBundle(bundle, destination);
+		assertTrue(Files.exists(destination));
+		assertFalse(Files.exists(bundle.getSource()));
+	}
+
+	@Test
+	public void closeAndSaveBundleNotDelete() throws Exception {
+		Path path = Files.createTempFile("bundle", ".zip");
+		Bundle bundle = Bundles.createBundle(path);
+		Path destination = Files.createTempFile("test", ".zip");
+		destination.toFile().deleteOnExit();
+		Files.delete(destination);
+		assertFalse(Files.exists(destination));
+		Bundles.closeAndSaveBundle(bundle, destination);
+		assertTrue(Files.exists(destination));
+		assertTrue(Files.exists(bundle.getSource()));
+	}
+
+	@Test
+	public void closeBundle() throws Exception {
+		Bundle bundle = Bundles.createBundle();
+		Path zip = Bundles.closeBundle(bundle);
+		assertTrue(Files.isReadable(zip));
+		assertEquals(zip, bundle.getSource());
+		checkSignature(zip);
+	}
+
+  @Ignore("Broken in OpenJDK8 zipfs")
+	@Test
+	public void mimeTypePosition() throws Exception {
+		Bundle bundle = Bundles.createBundle();
+		String mimetype = "application/x-test";
+		Bundles.setMimeType(bundle, mimetype);
+		assertEquals(mimetype, Bundles.getMimeType(bundle));
+		Path zip = Bundles.closeBundle(bundle);
+
+		assertTrue(Files.exists(zip));
+		try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+			// Must be first entry
+			ZipEntry mimeEntry = zipFile.entries().nextElement();
+			assertEquals("First zip entry is not 'mimetype'", "mimetype",
+					mimeEntry.getName());
+			assertEquals(
+					"mimetype should be uncompressed, but compressed size mismatch",
+					mimeEntry.getCompressedSize(), mimeEntry.getSize());
+			assertEquals("mimetype should have STORED method", ZipEntry.STORED,
+					mimeEntry.getMethod());
+			assertEquals("Wrong mimetype", mimetype, IOUtils.toString(
+					zipFile.getInputStream(mimeEntry), "ASCII"));
+		}
+
+		// Check position 30++ according to
+		// http://livedocs.adobe.com/navigator/9/Navigator_SDK9_HTMLHelp/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Navigator_SDK9_HTMLHelp&file=Appx_Packaging.6.1.html#1522568
+		byte[] expected = ("mimetype" + mimetype + "PK").getBytes("ASCII");
+		FileInputStream in = new FileInputStream(zip.toFile());
+		byte[] actual = new byte[expected.length];
+
+		try {
+      
+			assertEquals(MIME_OFFSET, in.skip(MIME_OFFSET));
+			assertEquals(expected.length, in.read(actual));
+		} finally {
+			in.close();
+		}
+		assertArrayEquals(expected, actual);
+	}
+
+	@Test
+	public void createBundle() throws Exception {
+		Path source = null;
+		try (Bundle bundle = Bundles.createBundle()) {
+			assertTrue(Files.isDirectory(bundle.getRoot()));
+			source = bundle.getSource();
+			assertTrue(Files.exists(source));
+		}
+		// As it was temporary file it should be deleted on close
+		assertFalse(Files.exists(source));
+	}
+
+	@Test
+	public void createBundlePath() throws Exception {
+		Path source = Files.createTempFile("test", ".zip");
+		source.toFile().deleteOnExit();
+		Files.delete(source);
+		try (Bundle bundle = Bundles.createBundle(source)) {
+			assertTrue(Files.isDirectory(bundle.getRoot()));
+			assertEquals(source, bundle.getSource());
+			assertTrue(Files.exists(source));
+		}
+		// As it was a specific path, it should NOT be deleted on close
+		assertTrue(Files.exists(source));
+	}
+
+	@Test
+	public void createBundlePathExists() throws Exception {
+		Path source = Files.createTempFile("test", ".zip");
+		source.toFile().deleteOnExit();
+
+		assertTrue(Files.exists(source)); // will be overwritten
+		try (Bundle bundle = Bundles.createBundle(source)) {
+		}
+		// As it was a specific path, it should NOT be deleted on close
+		assertTrue(Files.exists(source));
+	}
+
+	@Test(expected = IOException.class)
+	public void createBundleExistsAsDirFails() throws Exception {
+		Path source = Files.createTempDirectory("test");
+		source.toFile().deleteOnExit();
+		try (Bundle bundle = Bundles.createBundle(source)) {
+		}
+	}
+
+	@Test
+	public void getMimeType() throws Exception {
+		Path bundlePath = TemporaryFiles.temporaryBundle();
+		try (BundleFileSystem bundleFs = BundleFileSystemProvider
+				.newFileSystemFromNew(bundlePath, "application/x-test")) {
+			Bundle bundle = new Bundle(bundleFs.getPath("/"), false);
+			assertEquals("application/x-test", Bundles.getMimeType(bundle));
+		}
+	}
+
+	@Test
+	public void setMimeType() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path mimetypePath = bundle.getRoot().resolve("mimetype");
+			assertEquals("application/vnd.wf4ever.robundle+zip",
+					Bundles.getStringValue(mimetypePath));
+
+			Bundles.setMimeType(bundle, "application/x-test");
+			assertEquals("application/x-test",
+					Bundles.getStringValue(mimetypePath));
+		}
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void setMimeTypeNoNewlines() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Bundles.setMimeType(bundle,
+					"application/x-test\nNo newlines allowed");
+		}
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void setMimeTypeNoSlash() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Bundles.setMimeType(bundle, "test");
+		}
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void setMimeTypeEmpty() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Bundles.setMimeType(bundle, "");
+		}
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void setMimeTypeNonAscii() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Bundles.setMimeType(bundle, "application/x-test-\u00E9");
+			// Include the e-acute-accent from latin1
+		}
+	}
+
+	@Test
+	public void getMimeTypeMissing() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path mimetypePath = bundle.getRoot().resolve("mimetype");
+			Files.delete(mimetypePath);
+			// Fall back according to our spec
+			assertEquals("application/vnd.wf4ever.robundle+zip",
+					Bundles.getMimeType(bundle));
+		}
+	}
+
+	@Test(expected = IOException.class)
+	public void setMimeTypeMissing() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path mimetypePath = bundle.getRoot().resolve("mimetype");
+			Files.delete(mimetypePath);
+			// sadly now we can't set it (the mimetype file must be uncompressed
+			// and at beginning of file,
+			// which we don't have the possibility to do now that file system is
+			// open)
+			Bundles.setMimeType(bundle, "application/x-test");
+		}
+	}
+
+	@Test
+	public void getReference() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path hello = bundle.getRoot().resolve("hello");
+			Bundles.setReference(hello, URI.create("http://example.org/test"));
+			URI uri = Bundles.getReference(hello);
+			assertEquals("http://example.org/test", uri.toASCIIString());
+		}
+	}
+
+	@Test
+	public void getReferenceFromWin8() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path win8 = bundle.getRoot().resolve("win8");
+			Path win8Url = bundle.getRoot().resolve("win8.url");
+			Files.copy(getClass().getResourceAsStream("/win8.url"), win8Url);
+
+			URI uri = Bundles.getReference(win8);
+			assertEquals("http://example.com/made-in-windows-8",
+					uri.toASCIIString());
+		}
+	}
+
+	@Test
+	public void getStringValue() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path hello = bundle.getRoot().resolve("hello");
+			String string = "A string";
+			Bundles.setStringValue(hello, string);
+			assertEquals(string, Bundles.getStringValue(hello));
+			assertEquals(null, Bundles.getStringValue(null));
+		}
+	}
+
+	protected boolean isEmpty(Path path) throws IOException {
+		try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
+			return !ds.iterator().hasNext();
+		}
+	}
+
+	@Test
+	public void isMissing() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path missing = bundle.getRoot().resolve("missing");
+			assertFalse(Bundles.isValue(missing));
+			assertTrue(Bundles.isMissing(missing));
+			assertFalse(Bundles.isReference(missing));
+		}
+	}
+
+	@Test
+	public void isReference() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path ref = bundle.getRoot().resolve("ref");
+			Bundles.setReference(ref, URI.create("http://example.org/test"));
+			assertTrue(Bundles.isReference(ref));
+			assertFalse(Bundles.isMissing(ref));
+			assertFalse(Bundles.isValue(ref));
+		}
+	}
+
+	@Test
+	public void isValue() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+
+			Path hello = bundle.getRoot().resolve("hello");
+			Bundles.setStringValue(hello, "Hello");
+			assertTrue(Bundles.isValue(hello));
+			assertFalse(Bundles.isReference(hello));
+		}
+	}
+
+	protected List<String> ls(Path path) throws IOException {
+		List<String> paths = new ArrayList<>();
+		try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
+			for (Path p : ds) {
+				paths.add(p.getFileName() + "");
+			}
+		}
+		Collections.sort(paths);
+		return paths;
+	}
+
+	@Test
+	public void safeMove() throws Exception {
+		Path tmp = Files.createTempDirectory("test");
+		tmp.toFile().deleteOnExit();
+		Path f1 = tmp.resolve("f1");
+		f1.toFile().deleteOnExit();
+		Files.createFile(f1);
+		assertFalse(isEmpty(tmp));
+
+		try (Bundle db = Bundles.createBundle()) {
+			Path f2 = db.getRoot().resolve("f2");
+			Bundles.safeMove(f1, f2);
+			assertFalse(Files.exists(f1));
+			assertTrue(isEmpty(tmp));
+			assertEquals(Arrays.asList("f2", "mimetype"), ls(db.getRoot()));
+		}
+
+	}
+
+	private void checkWorkflowrunBundle(Bundle b) throws IOException {
+		Path path = b.getRoot().resolve("/workflowrun.prov.ttl");
+		assertTrue(Files.exists(path));
+		// Ensure manifest was read
+		Manifest manifest = b.getManifest();
+		PathMetadata aggregation = manifest.getAggregation(path);
+		assertNotNull(aggregation);
+		@SuppressWarnings("deprecation")
+		URI proxy = aggregation.getProxy();
+		if (proxy != null)// FIXME proxy is normally null here? WHY?
+			assertEquals(
+					URI.create("urn:uuid:ac1c89cc-3ba2-462d-bd82-ab5b8297f98e"),
+					proxy);
+	}
+
+	@Test
+	public void openBundleInputStream() throws Exception {
+		try (InputStream stream = getClass().getResourceAsStream(
+				"/workflowrun.bundle.zip")) {
+			assertNotNull(stream);
+			try (Bundle b = Bundles.openBundle(stream)) {
+				checkWorkflowrunBundle(b);
+			}
+		}
+	}
+
+	@Test
+	public void openBundleURL() throws Exception {
+		URL url = getClass().getResource("/workflowrun.bundle.zip");
+		assertNotNull(url);
+		try (Bundle b = Bundles.openBundle(url)) {
+			checkWorkflowrunBundle(b);
+		}
+	}
+
+	@Test
+	public void openBundleURLNonFile() throws Exception {
+		final URL url = getClass().getResource("/workflowrun.bundle.zip");
+		assertNotNull(url);
+		URLStreamHandler handler = new URLStreamHandler() {
+			@Override
+			protected URLConnection openConnection(URL u) throws IOException {
+				return url.openConnection();
+			}
+		};
+		URL testUrl = new URL("test", "test", 0, "test", handler);
+		try (Bundle b = Bundles.openBundle(testUrl)) {
+			checkWorkflowrunBundle(b);
+		}
+	}
+
+	@Test
+	public void openBundleReadOnly() throws Exception {
+		Path untouched = Files
+				.createTempFile("test-openBundleReadOnly", ".zip");
+		try (Bundle bundle = Bundles.createBundle(untouched)) {
+			Bundles.setStringValue(bundle.getRoot().resolve("file.txt"),
+					"Untouched");
+		}
+		try (Bundle readOnly = Bundles.openBundleReadOnly(untouched)) {
+			Path file = readOnly.getRoot().resolve("file.txt");
+			// You can change the open file system
+			Bundles.setStringValue(file, "Modified");
+			assertEquals("Modified", Bundles.getStringValue(file));
+			// and even make new resources
+			Path newFile = readOnly.getRoot().resolve("newfile.txt");
+			Files.createFile(newFile);
+			assertTrue(Files.exists(newFile));
+
+		}
+		try (Bundle readOnly = Bundles.openBundleReadOnly(untouched)) {
+			// But that is not persisted in the zip
+			Path file = readOnly.getRoot().resolve("file.txt");
+			assertEquals("Untouched", Bundles.getStringValue(file));
+			Path newfile = readOnly.getRoot().resolve("newfile.txt");
+			assertFalse(Files.exists(newfile));
+		}
+	}
+
+	@Test
+	public void safeCopy() throws Exception {
+		Path tmp = Files.createTempDirectory("test");
+		tmp.toFile().deleteOnExit();
+		Path f1 = tmp.resolve("f1");
+		f1.toFile().deleteOnExit();
+		Files.createFile(f1);
+		assertFalse(isEmpty(tmp));
+
+		try (Bundle db = Bundles.createBundle()) {
+			Path f2 = db.getRoot().resolve("f2");
+			Bundles.safeCopy(f1, f2);
+			assertTrue(Files.exists(f1));
+			assertTrue(Files.exists(f2));
+			assertEquals(Arrays.asList("f2", "mimetype"), ls(db.getRoot()));
+		}
+
+	}
+
+	@Test(expected = DirectoryNotEmptyException.class)
+	public void safeCopyFails() throws Exception {
+		Path tmp = Files.createTempDirectory("test");
+		tmp.toFile().deleteOnExit();
+		Path f1 = tmp.resolve("f1");
+		f1.toFile().deleteOnExit();
+		Path d1 = tmp.resolve("d1");
+		d1.toFile().deleteOnExit();
+		Files.createFile(f1);
+
+		// Make d1 difficult to overwrite
+		Files.createDirectory(d1);
+		Files.createFile(d1.resolve("child"));
+
+		try {
+			// Files.copy(f1, d1, StandardCopyOption.REPLACE_EXISTING);
+			Bundles.safeCopy(f1, d1);
+		} finally {
+			assertEquals(Arrays.asList("d1", "f1"), ls(tmp));
+			assertTrue(Files.exists(f1));
+			assertTrue(Files.isDirectory(d1));
+		}
+	}
+
+	@Test(expected = IOException.class)
+	public void safeMoveFails() throws Exception {
+		Path tmp = Files.createTempDirectory("test");
+		tmp.toFile().deleteOnExit();
+		Path f1 = tmp.resolve("f1");
+		f1.toFile().deleteOnExit();
+		Path d1 = tmp.resolve("d1");
+		d1.toFile().deleteOnExit();
+		Files.createFile(f1);
+
+		// Make d1 difficult to overwrite
+		Files.createDirectory(d1);
+		Files.createFile(d1.resolve("child"));
+
+		try {
+			Bundles.safeMove(f1, d1);
+		} finally {
+			assertTrue(Files.exists(f1));
+			assertEquals(Arrays.asList("d1", "f1"), ls(tmp));
+		}
+	}
+
+	@Test
+	public void setReference() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+
+			Path ref = bundle.getRoot().resolve("ref");
+			Bundles.setReference(ref, URI.create("http://example.org/test"));
+
+			URI uri = URI.create("http://example.org/test");
+			Path f = Bundles.setReference(ref, uri);
+			assertEquals("ref.url", f.getFileName().toString());
+			assertEquals(bundle.getRoot(), f.getParent());
+			assertFalse(Files.exists(ref));
+
+			List<String> uriLines = Files.readAllLines(f,
+					Charset.forName("ASCII"));
+			assertEquals(3, uriLines.size());
+			assertEquals("[InternetShortcut]", uriLines.get(0));
+			assertEquals("URL=http://example.org/test", uriLines.get(1));
+			assertEquals("", uriLines.get(2));
+		}
+	}
+
+	@Test
+	public void setReferenceIri() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path ref = bundle.getRoot().resolve("ref");
+			URI uri = new URI("http", "xn--bcher-kva.example.com",
+					"/s\u00F8iland/\u2603snowman", "\u2605star");
+			Path f = Bundles.setReference(ref, uri);
+			List<String> uriLines = Files.readAllLines(f,
+					Charset.forName("ASCII"));
+			// TODO: Double-check that this is actually correct escaping :)
+			assertEquals(
+					"URL=http://xn--bcher-kva.example.com/s%C3%B8iland/%E2%98%83snowman#%E2%98%85star",
+					uriLines.get(1));
+		}
+	}
+
+	@Test
+	public void setStringValue() throws Exception {
+		try (Bundle bundle = Bundles.createBundle()) {
+			Path file = bundle.getRoot().resolve("file");
+			String string = "A string";
+			Bundles.setStringValue(file, string);
+			assertEquals(string,
+					Files.readAllLines(file, Charset.forName("UTF-8")).get(0));
+		}
+	}
+
+	@Test
+	public void withExtension() throws Exception {
+		Path testDir = Files.createTempDirectory("test");
+		testDir.toFile().deleteOnExit();
+		Path fileTxt = testDir.resolve("file.txt");
+		fileTxt.toFile().deleteOnExit();
+		assertEquals("file.txt", fileTxt.getFileName().toString()); // better
+																	// be!
+
+		Path fileHtml = Bundles.withExtension(fileTxt, ".html");
+		assertEquals(fileTxt.getParent(), fileHtml.getParent());
+		assertEquals("file.html", fileHtml.getFileName().toString());
+
+		Path fileDot = Bundles.withExtension(fileTxt, ".");
+		assertEquals("file.", fileDot.getFileName().toString());
+
+		Path fileEmpty = Bundles.withExtension(fileTxt, "");
+		assertEquals("file", fileEmpty.getFileName().toString());
+
+		Path fileDoc = Bundles.withExtension(fileEmpty, ".doc");
+		assertEquals("file.doc", fileDoc.getFileName().toString());
+
+		Path fileManyPdf = Bundles.withExtension(fileTxt, ".test.many.pdf");
+		assertEquals("file.test.many.pdf", fileManyPdf.getFileName().toString());
+
+		Path fileManyTxt = Bundles.withExtension(fileManyPdf, ".txt");
+		assertEquals("file.test.many.txt", fileManyTxt.getFileName().toString());
+	}
+
+}