org.apache.taverna.scufl2.ucfpackage.TestUCFPackage.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.taverna.scufl2.ucfpackage.TestUCFPackage.java

Source

package org.apache.taverna.scufl2.ucfpackage;
/*
 *
 * 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.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.taverna.scufl2.ucfpackage.UCFPackage.ResourceEntry;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;
import org.junit.Before;
import org.junit.Test;

@SuppressWarnings("resource")
public class TestUCFPackage {

    private static final int MIME_OFFSET = 30;
    private static final boolean DELETE_FILES = false;
    private static final Namespace MANIFEST_NS = Namespace.getNamespace("manifest",
            "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0");
    private static final Namespace EXAMPLE_NS = Namespace.getNamespace("ex", "http://example.com/");
    private static final Namespace CONTAINER_NS = Namespace.getNamespace("c",
            "urn:oasis:names:tc:opendocument:xmlns:container");
    private File tmpFile;

    protected Document parseXml(InputStream stream) throws JDOMException, IOException {
        SAXBuilder saxBuilder = new SAXBuilder();
        return saxBuilder.build(stream);
    }

    protected void assertXpathEquals(String expected, Element element, String xpath) throws JDOMException {
        Object o = xpathSelectElement(element, xpath);
        if (o == null) {
            fail("Can't find " + xpath + " in " + element);
            return;
        }
        String text;
        if (o instanceof Attribute) {
            text = ((Attribute) o).getValue();
        } else {
            text = ((Element) o).getValue();
        }
        assertEquals(expected, text);
    }

    protected Object xpathSelectElement(Element element, String xpath) throws JDOMException {
        XPath x = XPath.newInstance(xpath);
        x.addNamespace(MANIFEST_NS);
        x.addNamespace(CONTAINER_NS);
        x.addNamespace(EXAMPLE_NS);

        return x.selectSingleNode(element);
    }

    @Test(expected = IllegalArgumentException.class)
    public void mimeTypeInvalidCharset() throws Exception {
        UCFPackage container = new UCFPackage();
        // Not even Latin-1 chars allowed
        container.setPackageMediaType("food/brd");
    }

    @Test(expected = IllegalArgumentException.class)
    public void mimeTypeEmpty() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType("");
    }

    @Test(expected = IllegalArgumentException.class)
    public void mimeTypeNoSlash() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType("nonvalid");
    }

    @Test(expected = IllegalStateException.class)
    public void noDefaultMimeType() throws Exception {
        UCFPackage container = new UCFPackage();
        container.save(tmpFile);
    }

    @Test
    public void mimeTypePosition() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_EPUB);
        assertEquals(UCFPackage.MIME_EPUB, container.getPackageMediaType());
        container.save(tmpFile);
        assertTrue(tmpFile.exists());
        ZipFile zipFile = new ZipFile(tmpFile);
        // 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", UCFPackage.MIME_EPUB,
                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" + UCFPackage.MIME_EPUB + "PK").getBytes("ASCII");
        FileInputStream in = new FileInputStream(tmpFile);
        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);
    }

    @Before
    public void createTempFile() throws IOException {
        tmpFile = File.createTempFile("scufl2-test", ".bundle");
        assertTrue(tmpFile.delete());
        if (DELETE_FILES) {
            tmpFile.deleteOnExit();
        } else {
            System.out.println(tmpFile);
        }
    }

    @Test
    public void workflowBundleMimeType() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        assertEquals(UCFPackage.MIME_WORKFLOW_BUNDLE, container.getPackageMediaType());
        container.save(tmpFile);
        ZipFile zipFile = new ZipFile(tmpFile);
        ZipEntry mimeEntry = zipFile.getEntry("mimetype");
        assertEquals("mimetype", mimeEntry.getName());
        assertEquals("Wrong mimetype", UCFPackage.MIME_WORKFLOW_BUNDLE,
                IOUtils.toString(zipFile.getInputStream(mimeEntry), "ASCII"));

    }

    @Test
    public void fileEntryFromString() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);

        container.addResource("Hello there ", "helloworld.txt", "text/plain");

        container.save(tmpFile);
        ZipFile zipFile = new ZipFile(tmpFile);
        ZipEntry manifestEntry = zipFile.getEntry("META-INF/manifest.xml");
        InputStream manifestStream = zipFile.getInputStream(manifestEntry);
        //System.out.println(IOUtils.toString(manifestStream, "UTF-8"));
        /*
        <?xml version="1.0" encoding="UTF-8"?>
        <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
        <manifest:file-entry manifest:media-type="text/plain" manifest:full-path="helloworld.txt" manifest:size="18"/>
        </manifest:manifest>
         */
        Document doc = parseXml(manifestStream);
        assertEquals(MANIFEST_NS, doc.getRootElement().getNamespace());
        assertEquals("manifest", doc.getRootElement().getNamespacePrefix());
        assertEquals("manifest:manifest", doc.getRootElement().getQualifiedName());
        assertXpathEquals("text/plain", doc.getRootElement(),
                "/manifest:manifest/manifest:file-entry/@manifest:media-type");
        assertXpathEquals("helloworld.txt", doc.getRootElement(),
                "/manifest:manifest/manifest:file-entry/@manifest:full-path");
        /*
         * Different platforms encode UTF8 in different ways
         *       assertXpathEquals("18", doc.getRootElement(), "/manifest:manifest/manifest:file-entry/@manifest:size");
         */

        InputStream io = zipFile.getInputStream(zipFile.getEntry("helloworld.txt"));
        assertEquals("Hello there ", IOUtils.toString(io, "UTF-8"));
    }

    @Test
    public void retrieveStringLoadedFromFile() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there ", "helloworld.txt", "text/plain");
        container.save(tmpFile);

        UCFPackage loaded = new UCFPackage(tmpFile);
        String s = loaded.getResourceAsString("helloworld.txt");
        assertEquals("Hello there ", s);
    }

    @Test
    public void doubleSave() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there ", "helloworld.txt", "text/plain");
        container.save(tmpFile);
        container.addResource("Hello again", "again.txt", "text/plain");
        container.save(tmpFile);
        ZipFile zipFile = new ZipFile(tmpFile);
        assertNotNull(zipFile.getEntry("helloworld.txt"));

        assertNotNull(zipFile.getEntry("again.txt"));
    }

    @Test
    public void doubleSaveOutputStream() throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there ", "helloworld.txt", "text/plain");
        container.save(outStream);
        container.addResource("Hello again", "again.txt", "text/plain");
        outStream.close();

        outStream = new ByteArrayOutputStream();
        container.save(outStream);
        outStream.close();

        FileUtils.writeByteArrayToFile(tmpFile, outStream.toByteArray());
        ZipFile zipFile = new ZipFile(tmpFile);
        assertNotNull(zipFile.getEntry("helloworld.txt"));
        assertNotNull(zipFile.getEntry("again.txt"));
    }

    @Test
    public void retrieveBytesLoadedFromFile() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        byte[] bytes = makeBytes(2048);
        container.addResource(bytes, "randomBytes", "application/octet-stream");
        container.save(tmpFile);

        UCFPackage loaded = new UCFPackage(tmpFile);
        byte[] loadedBytes = loaded.getResourceAsBytes("randomBytes");
        assertArrayEquals(bytes, loadedBytes);
    }

    @Test
    public void addResourceOutputStream() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        byte[] bytes = makeBytes(2048);

        OutputStream outStream = container.addResourceUsingOutputStream("randomBytes", "application/octet-stream");
        IOUtils.write(bytes, outStream);

        assertTrue(container.listResources().isEmpty());

        outStream.close();
        assertFalse(container.listResources().isEmpty());

        container.save(tmpFile);
        UCFPackage loaded = new UCFPackage(tmpFile);
        byte[] loadedBytes = loaded.getResourceAsBytes("randomBytes");
        assertArrayEquals(bytes, loadedBytes);
    }

    @Test
    public void retrieveInputStreamLoadedFromFile() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        byte[] bytes = makeBytes(4929);
        container.addResource(bytes, "randomBytes", "application/octet-stream");
        container.save(tmpFile);

        UCFPackage loaded = new UCFPackage(tmpFile);
        InputStream entryAsInputStream = loaded.getResourceAsInputStream("randomBytes");
        byte[] loadedBytes = IOUtils.toByteArray(entryAsInputStream);
        assertArrayEquals(bytes, loadedBytes);
    }

    @Test
    public void fileEntryFromBytes() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);

        byte[] bytes = makeBytes(1024);
        container.addResource(bytes, "binary", UCFPackage.MIME_BINARY);

        container.save(tmpFile);
        ZipFile zipFile = new ZipFile(tmpFile);
        ZipEntry manifestEntry = zipFile.getEntry("META-INF/manifest.xml");
        InputStream manifestStream = zipFile.getInputStream(manifestEntry);

        //System.out.println(IOUtils.toString(manifestStream, "UTF-8"));
        /*
        <?xml version="1.0" encoding="UTF-8"?>
        <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
        <manifest:file-entry manifest:media-type="application/octet-stream" manifest:full-path="binary" manifest:size="1024"/>
        </manifest:manifest>
         */
        Document doc = parseXml(manifestStream);
        assertEquals(MANIFEST_NS, doc.getRootElement().getNamespace());
        assertEquals("manifest", doc.getRootElement().getNamespacePrefix());
        assertEquals("manifest:manifest", doc.getRootElement().getQualifiedName());
        assertXpathEquals("application/octet-stream", doc.getRootElement(),
                "/manifest:manifest/manifest:file-entry/@manifest:media-type");
        assertXpathEquals("binary", doc.getRootElement(),
                "/manifest:manifest/manifest:file-entry/@manifest:full-path");
        assertXpathEquals("1024", doc.getRootElement(), "/manifest:manifest/manifest:file-entry/@manifest:size");

        InputStream io = zipFile.getInputStream(zipFile.getEntry("binary"));
        assertArrayEquals(bytes, IOUtils.toByteArray(io));
    }

    protected static byte[] makeBytes(int size) {
        byte[] bytes = new byte[size];
        for (int i = 0; i < size; i++) {
            bytes[i] = (byte) i;
        }
        return bytes;
    }

    @Test
    public void fileListing() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        Set<String> expectedFiles = new HashSet<String>();
        Set<String> expectedSubFiles = new HashSet<String>();
        Set<String> expectedSubSubFiles = new HashSet<String>();

        container.addResource("Hello there", "helloworld.txt", "text/plain");
        expectedFiles.add("helloworld.txt");

        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        expectedFiles.add("soup.txt");

        container.addResource("<html><body><h1>Yo</h1></body></html>", "soup.html", "text/html");
        expectedFiles.add("soup.html");

        container.addResource("Sub-folder entry 1", "sub/1.txt", "text/plain");
        container.addResource("Sub-folder entry 2", "sub/2.txt", "text/plain");
        container.addResource("Sub-folder entry 2", "sub/3/woho.txt", "text/plain");

        container.addResource("Other sub-folder entry", "sub2/3.txt", "text/plain");

        expectedFiles.add("sub/");
        expectedSubFiles.add("1.txt");
        expectedSubFiles.add("2.txt");
        expectedSubFiles.add("3/");
        expectedSubSubFiles.add("woho.txt");
        expectedFiles.add("sub2/");

        Map<String, ResourceEntry> beforeSaveRootEntries = container.listResources();
        assertEquals(expectedFiles, beforeSaveRootEntries.keySet());

        assertEquals(expectedSubFiles, container.listResources("sub").keySet());
        assertEquals(expectedSubFiles, container.listResources("sub/").keySet());
        assertEquals(expectedSubSubFiles, container.listResources("sub/3/").keySet());

        container.save(tmpFile);

        UCFPackage loaded = new UCFPackage(tmpFile);
        Map<String, ResourceEntry> loadedRootEntries = loaded.listResources();
        assertEquals(expectedFiles, loadedRootEntries.keySet());

        assertEquals(expectedSubFiles, loaded.listResources("sub").keySet());
        assertEquals(expectedSubFiles, loaded.listResources("sub/").keySet());
        assertEquals(expectedSubSubFiles, loaded.listResources("sub/3/").keySet());
    }

    @Test
    public void removeResource() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);

        container.addResource("Soup for everyone", "soup.txt", "text/plain");

        container.addResource("Sub-folder entry 1", "sub/1.txt", "text/plain");
        container.addResource("Sub-folder entry 2", "sub/2.txt", "text/plain");
        container.addResource("Sub-folder entry 2", "sub/3/woho.txt", "text/plain");

        assertTrue(container.listAllResources().keySet().contains("soup.txt"));
        container.removeResource("soup.txt");
        assertFalse(container.listAllResources().keySet().contains("soup.txt"));

        try {
            container.getResourceAsString("soup.txt");
            fail("Could still retrieve soup.txt");
        } catch (Exception ex) {
            // OK
        }

        container.save(tmpFile);
        // reload
        UCFPackage container2 = new UCFPackage(tmpFile);

        assertTrue(container2.listAllResources().keySet().contains("sub/"));
        container2.removeResource("sub"); // should not work
        assertTrue(container2.listAllResources().keySet().contains("sub/"));
        assertTrue(container2.listAllResources().keySet().contains("sub/1.txt"));

        container2.removeResource("sub/");
        assertFalse(container2.listAllResources().keySet().contains("sub/"));
        assertFalse(container2.listAllResources().keySet().contains("sub/1.txt"));

        container2.save(tmpFile);

        ZipFile zipFile = new ZipFile(tmpFile);
        assertNull("soup.txt still in zip file", zipFile.getEntry("soup.txt"));
        assertNull("sub/1.txt still in zip file", zipFile.getEntry("sub/1.txt"));
        assertNull("sub/2.txt still in zip file", zipFile.getEntry("sub/2.txt"));
        assertNull("sub/3.txt still in zip file", zipFile.getEntry("sub/3.txt"));
        assertNull("sub/ still in zip file", zipFile.getEntry("sub/"));
        zipFile.close();

        UCFPackage loaded = new UCFPackage(tmpFile);
        assertFalse(loaded.listAllResources().keySet().contains("soup.txt"));
        assertFalse(loaded.listAllResources().keySet().contains("sub/"));
        assertFalse(loaded.listAllResources().keySet().contains("sub/1.txt"));

        try {
            loaded.getResourceAsString("sub/1.txt");
            fail("Could still retrieve soup.txt");
        } catch (Exception ex) {
            // OK
        }
        loaded.save(tmpFile);
    }

    @Test
    public void fileListingRecursive() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        Set<String> expectedFiles = new HashSet<String>();

        container.addResource("Hello there", "helloworld.txt", "text/plain");
        expectedFiles.add("helloworld.txt");

        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        expectedFiles.add("soup.txt");

        container.addResource("<html><body><h1>Yo</h1></body></html>", "soup.html", "text/html");
        expectedFiles.add("soup.html");

        container.addResource("Sub-folder entry 1", "sub/1.txt", "text/plain");
        container.addResource("Sub-folder entry 2", "sub/2.txt", "text/plain");
        container.addResource("Sub-folder entry 2", "sub/3/woho.txt", "text/plain");
        expectedFiles.add("sub/");
        expectedFiles.add("sub/1.txt");
        expectedFiles.add("sub/2.txt");
        expectedFiles.add("sub/3/");
        expectedFiles.add("sub/3/woho.txt");

        Map<String, ResourceEntry> beforeSaveRootEntries = container.listAllResources();

        assertEquals(expectedFiles, beforeSaveRootEntries.keySet());

        container.save(tmpFile);

        UCFPackage loaded = new UCFPackage(tmpFile);
        Map<String, ResourceEntry> loadedRootEntries = loaded.listAllResources();
        assertEquals(expectedFiles, loadedRootEntries.keySet());
    }

    @Test
    public void resourceEntries() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("Sub-folder entry 1", "sub/1.txt", "text/plain");

        ResourceEntry helloResource = container.listResources().get("helloworld.txt");
        assertEquals("helloworld.txt", helloResource.getPath());
        assertEquals(11, helloResource.getSize());
        assertEquals("text/plain", helloResource.getMediaType());

        container.save(tmpFile);

        UCFPackage loaded = new UCFPackage(tmpFile);

        ResourceEntry loadedHelloResource = loaded.listResources().get("helloworld.txt");
        assertEquals("helloworld.txt", loadedHelloResource.getPath());
        assertEquals(11, loadedHelloResource.getSize());
        assertEquals("text/plain", loadedHelloResource.getMediaType());

    }

    @Test
    public void getResourceEntry() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("Sub-folder entry 1", "sub/1.txt", "text/plain");

        ResourceEntry helloResource = container.getResourceEntry("helloworld.txt");
        assertEquals("helloworld.txt", helloResource.getPath());
        assertEquals(11, helloResource.getSize());
        assertEquals("text/plain", helloResource.getMediaType());
        assertFalse(helloResource.isFolder());

        ResourceEntry subResource = container.getResourceEntry("sub/");
        assertEquals("sub/", subResource.getPath());
        assertEquals(-1, subResource.getSize());
        assertEquals("text/plain", subResource.getMediaType());
        assertTrue(subResource.isFolder());

        container.save(tmpFile);

        UCFPackage loaded = new UCFPackage(tmpFile);

        ResourceEntry loadedHelloResource = loaded.getResourceEntry("helloworld.txt");
        assertEquals("helloworld.txt", loadedHelloResource.getPath());
        assertEquals(11, loadedHelloResource.getSize());
        assertEquals("text/plain", loadedHelloResource.getMediaType());
        assertFalse(loadedHelloResource.isFolder());

        ResourceEntry loadedSubResource = loaded.getResourceEntry("sub/");
        assertEquals("sub/", loadedSubResource.getPath());
        assertEquals(-1, loadedSubResource.getSize());
        assertEquals("text/plain", loadedSubResource.getMediaType());
        assertTrue(loadedSubResource.isFolder());
    }

    @Test
    public void manifestMimetype() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);

        container.save(tmpFile);
        ZipFile zipFile = new ZipFile(tmpFile);
        ZipEntry manifestEntry = zipFile.getEntry("META-INF/manifest.xml");
        InputStream manifestStream = zipFile.getInputStream(manifestEntry);

        //System.out.println(IOUtils.toString(manifestStream, "UTF-8"));
        /*
        <?xml version="1.0" encoding="UTF-8"?>
        <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
        </manifest:manifest>       
        */
        Document doc = parseXml(manifestStream);
        assertEquals(MANIFEST_NS, doc.getRootElement().getNamespace());
        assertEquals("manifest", doc.getRootElement().getNamespacePrefix());
        assertEquals("manifest:manifest", doc.getRootElement().getQualifiedName());
        assertNull(xpathSelectElement(doc.getRootElement(), "/manifest:manifest/*"));

    }

    @Test
    public void getRootfiles() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        assertTrue("Root files not empty", container.getRootFiles().isEmpty());
        assertNotSame("Should return copy of rootfiles", container.getRootFiles(), container.getRootFiles());
    }

    @Test
    public void setRootfile() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");

        List<ResourceEntry> rootFiles = container.getRootFiles();

        assertEquals(1, rootFiles.size());
        ResourceEntry rootFile = rootFiles.get(0);
        assertEquals("helloworld.txt", rootFile.getPath());
        assertEquals("text/plain", rootFile.getMediaType());
    }

    @Test
    public void setRootfileSaved() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        container.save(tmpFile);

        ZipFile zipFile = new ZipFile(tmpFile);
        ZipEntry manifestEntry = zipFile.getEntry("META-INF/container.xml");
        InputStream manifestStream = zipFile.getInputStream(manifestEntry);

        //System.out.println(IOUtils.toString(manifestStream, "UTF-8"));
        /*
        <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
        <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns3="http://www.w3.org/2001/04/xmlenc#">
        <rootFiles>
          <rootFile full-path="helloworld.txt" media-type="text/plain"/>
        </rootFiles>
        </container>
         */
        Document doc = parseXml(manifestStream);
        assertEquals(CONTAINER_NS, doc.getRootElement().getNamespace());

        // Should work, but might still fail on Windows due to
        // TAVERNA-920. We'll avoid testing this to not break the build.
        // assertEquals("", doc.getRootElement().getNamespacePrefix());
        // assertEquals("container", doc.getRootElement().getQualifiedName());
        assertEquals("container", doc.getRootElement().getName());

        assertXpathEquals("helloworld.txt", doc.getRootElement(), "/c:container/c:rootFiles/c:rootFile/@full-path");
        assertXpathEquals("text/plain", doc.getRootElement(), "/c:container/c:rootFiles/c:rootFile/@media-type");

    }

    @Test
    public void addResourceContainerXml() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        assertEquals("helloworld.txt", container.getRootFiles().get(0).getPath());

        String containerXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
                + "<container xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\" xmlns:ns2=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:ns3=\"http://www.w3.org/2001/04/xmlenc#\">\n"
                + "    <ex:example xmlns:ex=\"http://example.com/\">first example</ex:example>\n"
                + "    <rootFiles>\n"
                + "        <rootFile xmlns:ex=\"http://example.com/\" media-type=\"text/plain\" full-path=\"soup.txt\" ex:extraAnnotation=\"hello\"/>\n"
                + "    </rootFiles>\n"
                + "    <ex:example xmlns:ex=\"http://example.com/\">second example</ex:example>\n"
                + "    <ex:example xmlns:ex=\"http://example.com/\">third example</ex:example>\n"
                + "</container>\n";
        // Should overwrite setRootFile()
        container.addResource(containerXml, "META-INF/container.xml", "text/xml");

        assertEquals("soup.txt", container.getRootFiles().get(0).getPath());

        container.save(tmpFile);

        ZipFile zipFile = new ZipFile(tmpFile);
        ZipEntry manifestEntry = zipFile.getEntry("META-INF/container.xml");
        InputStream manifestStream = zipFile.getInputStream(manifestEntry);

        //System.out.println(IOUtils.toString(manifestStream, "UTF-8"));
        /*
        <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
        <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns3="http://www.w3.org/2001/04/xmlenc#">
        <ex:example xmlns:ex="http://example.com/">first example</ex:example>
        <rootFiles>
          <rootFile xmlns:ex="http://example.com/" full-path="soup.txt" media-type="text/plain" ex:extraAnnotation="hello"/>
        </rootFiles>
        <ex:example xmlns:ex="http://example.com/">second example</ex:example>
        <ex:example xmlns:ex="http://example.com/">third example</ex:example>
        </container>
         */
        Document doc = parseXml(manifestStream);
        assertEquals(CONTAINER_NS, doc.getRootElement().getNamespace());

        // Should work, but we'll ignore testing these (TAVERNA-920)
        //assertEquals("", doc.getRootElement().getNamespacePrefix());
        //assertEquals("container", doc.getRootElement().getQualifiedName());
        assertEquals("container", doc.getRootElement().getName());

        assertXpathEquals("soup.txt", doc.getRootElement(), "/c:container/c:rootFiles/c:rootFile[1]/@full-path");
        assertXpathEquals("text/plain", doc.getRootElement(), "/c:container/c:rootFiles/c:rootFile[1]/@media-type");
        assertXpathEquals("hello", doc.getRootElement(),
                "/c:container/c:rootFiles/c:rootFile[1]/@ex:extraAnnotation");

        assertXpathEquals("first example", doc.getRootElement(), "/c:container/ex:example[1]");
        assertXpathEquals("second example", doc.getRootElement(), "/c:container/ex:example[2]");
        assertXpathEquals("third example", doc.getRootElement(), "/c:container/ex:example[3]");

        // Check order
        Element first = (Element) xpathSelectElement(doc.getRootElement(), "/c:container/*[1]");
        assertEquals("ex:example", first.getQualifiedName());
        Element second = (Element) xpathSelectElement(doc.getRootElement(), "/c:container/*[2]");

        // Should work, but we'll ignore testing these (TAVERNA-920)
        //assertEquals("rootFiles", second.getQualifiedName());
        assertEquals("rootFiles", second.getName());

        Element third = (Element) xpathSelectElement(doc.getRootElement(), "/c:container/*[3]");
        assertEquals("ex:example", third.getQualifiedName());
        Element fourth = (Element) xpathSelectElement(doc.getRootElement(), "/c:container/*[4]");
        assertEquals("ex:example", fourth.getQualifiedName());

    }

    @Test
    public void setRootfileExtendsContainerXml() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("<html><body><h1>Yo</h1></body></html>", "helloworld.html", "text/html");

        String containerXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
                + "<container xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\" xmlns:ex='http://example.com/' xmlns:ns2=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:ns3=\"http://www.w3.org/2001/04/xmlenc#\">\n"
                + "    <rootFiles>\n"
                + "        <rootFile ex:extraAnnotation='hello' media-type=\"text/html\" full-path=\"helloworld.html\"/>\n"
                + "    </rootFiles>\n" + "   <ex:example>more example</ex:example>\n" + "</container>\n";
        // Should overwrite setRootFile()
        container.addResource(containerXml, "META-INF/container.xml", "text/xml");
        assertEquals("helloworld.html", container.getRootFiles().get(0).getPath());

        container.setRootFile("helloworld.txt");
        assertEquals("helloworld.html", container.getRootFiles().get(0).getPath());
        assertEquals("helloworld.txt", container.getRootFiles().get(1).getPath());
        container.save(tmpFile);
        //
        //      String expectedContainerXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
        //            + "<container xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\" xmlns:ns2=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:ns3=\"http://www.w3.org/2001/04/xmlenc#\">\n"
        //            + "    <rootFiles>\n"
        //            + "        <rootFile xmlns:ex=\"http://example.com/\" media-type=\"text/html\" full-path=\"helloworld.html\" ex:extraAnnotation=\"hello\"/>\n"
        //            + "        <rootFile media-type=\"text/plain\" full-path=\"helloworld.txt\"/>\n"
        //            + "    </rootFiles>\n"
        //            + "    <ex:example xmlns:ex=\"http://example.com/\">more example</ex:example>\n"
        //            +
        //            "</container>\n";

        ZipFile zipFile = new ZipFile(tmpFile);
        ZipEntry manifestEntry = zipFile.getEntry("META-INF/container.xml");
        InputStream manifestStream = zipFile.getInputStream(manifestEntry);

        //System.out.println(IOUtils.toString(manifestStream, "UTF-8"));
        /*
        <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
        <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns3="http://www.w3.org/2001/04/xmlenc#">
        <rootFiles>
          <rootFile xmlns:ex="http://example.com/" full-path="helloworld.html" media-type="text/html" ex:extraAnnotation="hello"/>
          <rootFile full-path="helloworld.txt" media-type="text/plain"/>
        </rootFiles>
        <ex:example xmlns:ex="http://example.com/">more example</ex:example>
        </container>
         */
        Document doc = parseXml(manifestStream);
        assertEquals(CONTAINER_NS, doc.getRootElement().getNamespace());
        // Should work, but we'll avoid testing it (TAVERNA-920)
        //assertEquals("", doc.getRootElement().getNamespacePrefix());
        //assertEquals("container", doc.getRootElement().getQualifiedName());
        assertEquals("container", doc.getRootElement().getName());
        assertXpathEquals("helloworld.html", doc.getRootElement(),
                "/c:container/c:rootFiles/c:rootFile[1]/@full-path");
        assertXpathEquals("text/html", doc.getRootElement(), "/c:container/c:rootFiles/c:rootFile[1]/@media-type");
        assertXpathEquals("hello", doc.getRootElement(),
                "/c:container/c:rootFiles/c:rootFile[1]/@ex:extraAnnotation");

        assertXpathEquals("helloworld.txt", doc.getRootElement(),
                "/c:container/c:rootFiles/c:rootFile[2]/@full-path");
        assertXpathEquals("text/plain", doc.getRootElement(), "/c:container/c:rootFiles/c:rootFile[2]/@media-type");

        assertXpathEquals("more example", doc.getRootElement(), "/c:container/ex:example");

        // Check order
        Element first = (Element) xpathSelectElement(doc.getRootElement(), "/c:container/*[1]");
        //assertEquals("rootFiles", first.getQualifiedName());
        assertEquals("rootFiles", first.getName());
        Element second = (Element) xpathSelectElement(doc.getRootElement(), "/c:container/*[2]");
        assertEquals("ex:example", second.getQualifiedName());

    }

    @Test(expected = IllegalArgumentException.class)
    public void setRootfileMissing() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        try {
            container.setRootFile("unknown.txt");
        } finally {
            assertTrue("Should not have added unknown.txt", container.getRootFiles().isEmpty());
        }

    }

    @Test
    public void unmodifiableRootFiles() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        List<ResourceEntry> rootFiles = container.getRootFiles();
        assertEquals(1, rootFiles.size());
        rootFiles.remove(0);
        assertEquals(0, rootFiles.size());
        assertEquals("Should not be able to modify rootFiles list", 1, container.getRootFiles().size());
    }

    @Test
    public void rootFileVersion() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.setRootFile("helloworld.txt", "1.2.3");
        container.save(tmpFile);
        UCFPackage reloaded = new UCFPackage(tmpFile);
        ResourceEntry rootFile = reloaded.getRootFiles().get(0);
        assertEquals("helloworld.txt", rootFile.getPath());
        assertEquals("1.2.3", reloaded.getRootFileVersion("helloworld.txt"));
    }

    @Test
    public void rootFileVersionNull() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.setRootFile("helloworld.txt", "1.2.3");
        container.save(tmpFile);
        UCFPackage reloaded = new UCFPackage(tmpFile);
        reloaded.setRootFile("helloworld.txt");
        reloaded.save(tmpFile);
        @SuppressWarnings("unused")
        UCFPackage again = new UCFPackage(tmpFile);
        assertNull(reloaded.getRootFileVersion("helloworld.txt"));
    }

    @Test
    public void multipleRootfiles() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("<html><body><h1>Yo</h1></body></html>", "helloworld.html", "text/html");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        // Adding a second file of a different mime type
        container.setRootFile("helloworld.html");
        List<ResourceEntry> rootFiles = container.getRootFiles();
        assertEquals(2, rootFiles.size());

        ResourceEntry rootFile0 = rootFiles.get(0);
        assertEquals("helloworld.txt", rootFile0.getPath());
        assertEquals("text/plain", rootFile0.getMediaType());

        ResourceEntry rootFile1 = rootFiles.get(1);
        assertEquals("helloworld.html", rootFile1.getPath());
        assertEquals("text/html", rootFile1.getMediaType());
    }

    @Test
    public void multipleRootfilesReloaded() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("<html><body><h1>Yo</h1></body></html>", "helloworld.html", "text/html");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        // Adding a second file of a different mime type
        container.setRootFile("helloworld.html");

        container.save(tmpFile);
        UCFPackage reloaded = new UCFPackage(tmpFile);

        List<ResourceEntry> rootFiles = reloaded.getRootFiles();
        assertEquals(2, rootFiles.size());

        ResourceEntry rootFile0 = rootFiles.get(0);
        assertEquals("helloworld.txt", rootFile0.getPath());
        assertEquals("text/plain", rootFile0.getMediaType());

        ResourceEntry rootFile1 = rootFiles.get(1);
        assertEquals("helloworld.html", rootFile1.getPath());
        assertEquals("text/html", rootFile1.getMediaType());
    }

    @Test
    public void unsetMultipleRootFiles() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("<html><body><h1>Yo</h1></body></html>", "helloworld.html", "text/html");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        container.setRootFile("helloworld.html");
        List<ResourceEntry> rootFiles = container.getRootFiles();
        assertEquals(2, rootFiles.size());

        container.unsetRootFile("helloworld.txt");
        rootFiles = container.getRootFiles();
        assertEquals(1, rootFiles.size());

        ResourceEntry rootFile0 = rootFiles.get(0);
        assertEquals("helloworld.html", rootFile0.getPath());
        assertEquals("text/html", rootFile0.getMediaType());
    }

    @Test
    public void unsetRootFile() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        assertEquals(1, container.getRootFiles().size());
        container.unsetRootFile("helloworld.txt");
        assertEquals(0, container.getRootFiles().size());
    }

    @Test
    public void cloneUcfPackage() throws Exception {
        UCFPackage container = new UCFPackage();
        container.setPackageMediaType(UCFPackage.MIME_WORKFLOW_BUNDLE);
        container.addResource("Hello there", "helloworld.txt", "text/plain");
        container.addResource("Soup for everyone", "soup.txt", "text/plain");
        container.setRootFile("helloworld.txt");
        assertEquals(2, container.listAllResources().size());

        UCFPackage clone = container.clone();

        // Change the original to ensure independence
        container.setPackageMediaType("text/other");
        container.removeResource("soup.txt");
        container.addResource("Something else", "helloworld.txt", "test/other");
        container.addResource("extra", "extra1.txt", "text/plain");
        container.addResource("extra", "extra2.txt", "text/plain");
        container.setRootFile("extra1.txt");

        assertEquals(UCFPackage.MIME_WORKFLOW_BUNDLE, clone.getPackageMediaType());
        assertEquals("Hello there", clone.getResourceAsString("helloworld.txt"));
        ResourceEntry helloWorldEntry = clone.getResourceEntry("helloworld.txt");
        assertEquals("text/plain", helloWorldEntry.getMediaType());
        assertEquals("Soup for everyone", clone.getResourceAsString("soup.txt"));
        assertEquals(Arrays.asList(helloWorldEntry), clone.getRootFiles());
        assertEquals(2, clone.listAllResources().size());
    }

}