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:04:47 UTC

[02/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-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackage.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackage.java b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackage.java
new file mode 100644
index 0000000..c99cdbe
--- /dev/null
+++ b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackage.java
@@ -0,0 +1,1880 @@
+/************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
+ *
+ * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2009 IBM. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * Licensed 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. You can also
+ * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ************************************************************************/
+package org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg;
+
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.SEVERE;
+import static java.util.logging.Level.WARNING;
+import static java.util.zip.ZipEntry.DEFLATED;
+import static java.util.zip.ZipEntry.STORED;
+import static org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg.StreamHelper.PAGE_SIZE;
+import static org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg.TempDir.newTempOdfDirectory;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.JarURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg.manifest.Algorithm;
+import org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg.manifest.EncryptionData;
+import org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg.manifest.KeyDerivation;
+import org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg.manifest.OdfFileEntry;
+import org.w3c.dom.Document;
+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSSerializer;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+
+/**
+ * OdfPackage represents the package view to an OpenDocument document. The
+ * OdfPackage will be created from an ODF document and represents a copy of the
+ * loaded document, where files can be inserted and deleted. The changes take
+ * effect, when the OdfPackage is being made persistend by save().
+ */
+public class OdfPackage implements AutoCloseable {
+	/**
+	 * This class solely exists to clean up after a package object has been
+	 * removed by garbage collector. Finalizable classes are said to have slow
+	 * garbage collection, so we don't make the whole OdfPackage finalizable.
+	 */
+	static private class OdfFinalizablePackage {
+		File mTempDirForDeletion;
+
+		OdfFinalizablePackage(File tempDir) {
+			mTempDirForDeletion = tempDir;
+		}
+
+		@Override
+		protected void finalize() {
+			if (mTempDirForDeletion != null)
+				TempDir.deleteTempOdfDirectory(mTempDirForDeletion);
+		}
+	}
+	private final Logger mLog = Logger.getLogger(OdfPackage.class.getName());
+
+	public enum OdfFile {
+		IMAGE_DIRECTORY("Pictures"), MANIFEST("META-INF/manifest.xml"), MEDIA_TYPE(
+				"mimetype");
+		private final String packagePath;
+
+		OdfFile(String packagePath) {
+			this.packagePath = packagePath;
+		}
+
+		public String getPath() {
+			return packagePath;
+		}
+	}
+
+	private static HashSet<String> mCompressedFileTypes = new HashSet<>();
+	{
+		String[] typelist = new String[] { "jpg", "gif", "png", "zip", "rar",
+				"jpeg", "mpe", "mpg", "mpeg", "mpeg4", "mp4", "7z", "ari",
+				"arj", "jar", "gz", "tar", "war", "mov", "avi" };
+		for (int i = 0; i < typelist.length; i++)
+			mCompressedFileTypes.add(typelist[i]);
+	}
+
+	// Static parts of file references
+	private static final String TWO_DOTS = "..";
+	private static final String SLASH = "/";
+	private static final String COLON = ":";
+	private static final String EMPTY_STRING = "";
+	private static final String XML_MEDIA_TYPE = "text/xml";
+	// temp Dir for this ODFpackage (2DO: temp dir handling will be removed most
+	// likely)
+	private File mTempDirParent;
+	private File mTempDir;
+	// only used indirectly for its finalizer (garbage collection)
+	@SuppressWarnings("unused")
+	private OdfFinalizablePackage mFinalize;
+	// some well known streams inside ODF packages
+	private String mMediaType;
+	private List<String> mPackageEntries;
+	private ZipHelper mZipFile;
+	private HashMap<String, ZipEntry> mZipEntries;
+	private HashMap<String, Document> mContentDoms;
+	private HashMap<String, byte[]> mContentStreams;
+	private HashMap<String, File> mTempFiles;
+	private boolean mUseTempFile;
+	private List<String> mManifestList;
+	private HashMap<String, OdfFileEntry> mManifestEntries;
+	private String mBaseURI;
+	private Resolver mResolver;
+
+	/**
+	 * Creates the ODFPackage as an empty Package. For setting a specific temp
+	 * directory, set the System variable org.odftoolkit.odfdom.tmpdir:<br>
+	 * <code>System.setProperty("org.odftoolkit.odfdom.tmpdir");</code>
+	 */
+	private OdfPackage() {
+		mMediaType = null;
+		mResolver = null;
+		mTempDir = null;
+		mTempDirParent = null;
+		mPackageEntries = new LinkedList<String>();
+		mZipEntries = new HashMap<String, ZipEntry>();
+		mContentDoms = new HashMap<String, Document>();
+		mContentStreams = new HashMap<String, byte[]>();
+		mTempFiles = new HashMap<String, File>();
+		mManifestList = new LinkedList<String>();
+
+		// get a temp directory for everything
+		String userPropDir = System.getProperty("org.odftoolkit.odfdom.tmpdir");
+		if (userPropDir != null)
+			mTempDirParent = new File(userPropDir);
+
+		// specify whether temporary files are able to used.
+		String userPropTempEnable = System
+				.getProperty("org.odftoolkit.odfdom.tmpfile.disable");
+		mUseTempFile = (userPropTempEnable == null)
+				|| !userPropTempEnable.equalsIgnoreCase("true");
+	}
+
+	public static OdfPackage create() throws Exception {
+		OdfPackage odfPackage = new OdfPackage();
+		odfPackage.mManifestEntries = new HashMap<>();
+		// We just need some dummy XML first
+		odfPackage.insert("<x />".getBytes(), OdfFile.MANIFEST.getPath(),
+				XML_MEDIA_TYPE);
+		return odfPackage;
+	}
+
+	/**
+	 * Creates an OdfPackage from the OpenDocument provided by a filePath.
+	 *
+	 * <p>
+	 * OdfPackage relies on the file being available for read access over the
+	 * whole lifecycle of OdfPackage.
+	 * </p>
+	 *
+	 * @param odfPath
+	 *            - the path to the ODF document.
+	 * @throws java.lang.Exception
+	 *             - if the package could not be created
+	 */
+	private OdfPackage(String odfPath) throws Exception {
+		this();
+		initialize(new File(odfPath));
+	}
+
+	/**
+	 * Creates an OdfPackage from the OpenDocument provided by a File.
+	 *
+	 * <p>
+	 * OdfPackage relies on the file being available for read access over the
+	 * whole lifecycle of OdfPackage.
+	 * </p>
+	 *
+	 * @param odfFile
+	 *            - a file representing the ODF document
+	 * @throws java.lang.Exception
+	 *             - if the package could not be created
+	 */
+	private OdfPackage(File odfFile) throws Exception {
+		this();
+		initialize(odfFile);
+	}
+
+	/**
+	 * Creates an OdfPackage from the OpenDocument provided by a InputStream.
+	 *
+	 * <p>
+	 * Since an InputStream does not provide the arbitrary (non sequentiell)
+	 * read access needed by OdfPackage, the InputStream is cached. This usually
+	 * takes more time compared to the other constructors.
+	 * </p>
+	 *
+	 * @param odfStream
+	 *            - an inputStream representing the ODF package
+	 * @throws java.lang.Exception
+	 *             - if the package could not be created
+	 */
+	private OdfPackage(InputStream odfStream) throws Exception {
+		this();
+		if (mUseTempFile) {
+			File tempFile = newTempSourceFile(odfStream);
+			initialize(tempFile);
+		} else {
+			initialize(odfStream);
+		}
+	}
+
+	/**
+	 * Loads an OdfPackage from the given filePath.
+	 *
+	 * <p>
+	 * OdfPackage relies on the file being available for read access over the
+	 * whole lifecycle of OdfPackage.
+	 * </p>
+	 *
+	 * @param odfPath
+	 *            - the filePath to the ODF package
+	 * @return the OpenDocument document represented as an OdfPackage
+	 * @throws java.lang.Exception
+	 *             - if the package could not be loaded
+	 */
+	public static OdfPackage loadPackage(String odfPath) throws Exception {
+		return new OdfPackage(odfPath);
+	}
+
+	/**
+	 * Loads an OdfPackage from the OpenDocument provided by a File.
+	 *
+	 * <p>
+	 * OdfPackage relies on the file being available for read access over the
+	 * whole lifecycle of OdfPackage.
+	 * </p>
+	 *
+	 * @param odfFile
+	 *            - a File to loadPackage content from
+	 * @return the OpenDocument document represented as an OdfPackage
+	 * @throws java.lang.Exception
+	 *             - if the package could not be loaded
+	 */
+	public static OdfPackage loadPackage(File odfFile) throws Exception {
+		return new OdfPackage(odfFile);
+	}
+
+	/**
+	 * Creates an OdfPackage from the OpenDocument provided by a InputStream.
+	 *
+	 * <p>
+	 * Since an InputStream does not provide the arbitrary (non sequentiell)
+	 * read access needed by OdfPackage, the InputStream is cached. This usually
+	 * takes more time compared to the other loadPackage methods.
+	 * </p>
+	 *
+	 * @param odfStream
+	 *            - an inputStream representing the ODF package
+	 * @return the OpenDocument document represented as an OdfPackage
+	 * @throws java.lang.Exception
+	 *             - if the package could not be loaded
+	 */
+	public static OdfPackage loadPackage(InputStream odfStream)
+			throws Exception {
+		return new OdfPackage(odfStream);
+	}
+
+	// Initialize using memory instead temporary disc
+	private void initialize(InputStream odfStream) throws Exception {
+		ByteArrayOutputStream tempBuf = new ByteArrayOutputStream();
+		StreamHelper.stream(odfStream, tempBuf);
+		byte[] mTempByteBuf = tempBuf.toByteArray();
+		tempBuf.close();
+
+		if (mTempByteBuf.length < 3)
+			throw new IllegalArgumentException(
+					"An empty file was tried to be opened as ODF package!");
+
+		mZipFile = new ZipHelper(mTempByteBuf);
+		Enumeration<? extends ZipEntry> entries = mZipFile.entries();
+		if (!entries.hasMoreElements())
+			throw new IllegalArgumentException(
+					"It was not possible to unzip the file!");
+		do {
+			ZipEntry zipEntry = entries.nextElement();
+			mZipEntries.put(zipEntry.getName(), zipEntry);
+			/*
+			 * TODO: think about if the additional list mPackageEntries is
+			 * necessary - shouldn't everything be part of one of the other
+			 * lists? maybe keep this as "master", rename it?
+			 */
+			mPackageEntries.add(zipEntry.getName());
+			if (zipEntry.getName().equals(
+					OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
+				ByteArrayOutputStream out = new ByteArrayOutputStream();
+				StreamHelper.stream(mZipFile.getInputStream(zipEntry), out);
+				try {
+					mMediaType = new String(out.toByteArray(), 0,
+							out.size(), "UTF-8");
+				} catch (UnsupportedEncodingException ex) {
+					mLog.log(SEVERE, null, ex);
+				}
+			}
+		} while (entries.hasMoreElements());
+	}
+
+	// Initialize using temporary directory on hard disc
+	private void initialize(File odfFile) throws Exception {
+		mBaseURI = getBaseURIFromFile(odfFile);
+
+		if (mTempDirParent == null) {
+			/*
+			 * getParentFile() returns already java.io.tmpdir when package is an
+			 * odfStream
+			 */
+			mTempDirParent = odfFile.getAbsoluteFile().getParentFile();
+			if (!mTempDirParent.canWrite())
+				mTempDirParent = null; // java.io.tmpdir will be used implicitly
+		}
+
+		try {
+			mZipFile = new ZipHelper(new ZipFile(odfFile));
+		} catch (Exception e) {
+			if (odfFile.length() < 3)
+				throw new IllegalArgumentException("The empty file '"
+						+ odfFile.getPath() + "' is no ODF package!", e);
+			throw new IllegalArgumentException("Could not unzip the file "
+					+ odfFile.getPath(), e);
+		}
+		Enumeration<? extends ZipEntry> entries = mZipFile.entries();
+
+		while (entries.hasMoreElements()) {
+			ZipEntry zipEntry = entries.nextElement();
+			mZipEntries.put(zipEntry.getName(), zipEntry);
+			/*
+			 * TODO: think about if the additional list mPackageEntries is
+			 * necessary - shouldn't everything be part of one of the other
+			 * lists? maybe keep this as "master", rename it?
+			 */
+			mPackageEntries.add(zipEntry.getName());
+			if (zipEntry.getName().equals(
+					OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
+				ByteArrayOutputStream out = new ByteArrayOutputStream();
+				StreamHelper.stream(mZipFile.getInputStream(zipEntry), out);
+				try {
+					mMediaType = new String(out.toByteArray(), 0, out.size(),
+							"UTF-8");
+				} catch (UnsupportedEncodingException ex) {
+					mLog.log(Level.SEVERE, null, ex);
+				}
+			}
+		}
+	}
+
+	private File newTempSourceFile(InputStream odfStream) throws Exception {
+		// no idea yet what type of file this will be, so we take .tmp
+		File odfFile = new File(getTempDir(), "theFile.tmp");
+		// copy stream to temp file
+		FileOutputStream os = new FileOutputStream(odfFile);
+		StreamHelper.stream(odfStream, os);
+		os.close();
+		return odfFile;
+	}
+
+	/**
+	 * Set the baseURI for this ODF package. NOTE: Should only be set during
+	 * saving the package.
+	 */
+	void setBaseURI(String baseURI) {
+		mBaseURI = baseURI;
+	}
+
+	/**
+	 * Get the URI, where this ODF package is stored.
+	 *
+	 * @return the URI to the ODF package. Returns null if package is not stored
+	 *         yet.
+	 */
+	public String getBaseURI() {
+		return mBaseURI;
+	}
+
+	/**
+	 * Get the media type of the ODF package (equal to media type of ODF root
+	 * document)
+	 *
+	 * @return the mediaType string of this ODF package
+	 */
+	public String getMediaType() {
+		return mMediaType;
+	}
+
+	/**
+	 * Set the media type of the ODF package (equal to media type of ODF root
+	 * document)
+	 *
+	 * @param mediaType
+	 *            string of this ODF package
+	 */
+	public void setMediaType(String mediaType) {
+		mMediaType = mediaType;
+		mPackageEntries.remove(OdfPackage.OdfFile.MEDIA_TYPE.getPath());
+		if (mMediaType != null)
+			mPackageEntries.add(0, OdfPackage.OdfFile.MEDIA_TYPE.getPath());
+	}
+
+	/**
+	 *
+	 * Get an OdfFileEntry for the packagePath NOTE: This method should be
+	 * better moved to a DOM inherited Manifest class
+	 *
+	 * @param packagePath
+	 *            The relative package path within the ODF package
+	 * @return The manifest file entry will be returned.
+	 */
+	public OdfFileEntry getFileEntry(String packagePath) {
+		packagePath = ensureValidPackagePath(packagePath);
+		return getManifestEntries().get(packagePath);
+	}
+
+	/**
+	 * Get a OdfFileEntries from the manifest file (i.e. /META/manifest.xml")
+	 *
+	 * @return The manifest file entries will be returned.
+	 */
+	public Set<String> getFileEntries() {
+		return getManifestEntries().keySet();
+	}
+
+	/**
+	 *
+	 * Check existence of a file in the package.
+	 *
+	 * @param packagePath
+	 *            The relative package filePath within the ODF package
+	 * @return True if there is an entry and a file for the given filePath
+	 */
+	public boolean contains(String packagePath) {
+		packagePath = ensureValidPackagePath(packagePath);
+		return mPackageEntries.contains(packagePath);
+		/* TODO: return true for later added stuff */
+		// return (mPackageEntries.contains(packagePath) &&
+		// (mTempFiles.get(packagePath) != null ||
+		// mContentStreams.get(packagePath)!=null) && getFileEntry(packagePath)
+		// != null);
+	}
+
+	/**
+	 * Save the package to given filePath.
+	 *
+	 * @param odfPath
+	 *            - the path to the ODF package destination
+	 * @throws java.lang.Exception
+	 *             - if the package could not be saved
+	 */
+	public void save(String odfPath) throws Exception {
+		File f = new File(odfPath);
+		save(f);
+	}
+
+	/**
+	 * Save package to a given File. After saving it is still necessary to close
+	 * the package to have again full access about the file.
+	 *
+	 * @param odfFile
+	 *            - the File to save the ODF package to
+	 * @throws java.lang.Exception
+	 *             - if the package could not be saved
+	 */
+	public void save(File odfFile) throws Exception {
+		String baseURI = odfFile.getCanonicalFile().toURI().toString();
+		if (File.separatorChar == '\\')
+			baseURI = baseURI.replaceAll("\\\\", SLASH);
+		if (baseURI.equals(mBaseURI))
+			/* save to the same file: cache everything first */
+			// TODO: maybe it's better to write to a new file and copy that
+			// to the original one - would be less memory footprint
+			cacheContent();
+		FileOutputStream fos = new FileOutputStream(odfFile);
+		save(fos, baseURI);
+	}
+
+	public void save(OutputStream odfStream) throws Exception {
+		save(odfStream, null);
+	}
+
+	/**
+	 * Save an ODF document to the OutputStream.
+	 *
+	 * @param odfStream
+	 *            - the OutputStream to insert content to
+	 * @param baseURI
+	 *            - a URI for the package to be stored
+	 * @throws java.lang.Exception
+	 *             - if the package could not be saved
+	 */
+	private void save(OutputStream odfStream, String baseURI) throws Exception {
+		mBaseURI = baseURI;
+
+		OdfFileEntry rootEntry = getManifestEntries().get(SLASH);
+		if (rootEntry == null) {
+			rootEntry = new OdfFileEntry(SLASH, mMediaType);
+			mManifestList.add(0, rootEntry.getPath());
+		} else
+			rootEntry.setMediaType(mMediaType);
+
+		ZipOutputStream zos = new ZipOutputStream(odfStream);
+		long modTime = (new java.util.Date()).getTime();
+
+		/*
+		 * move manifest to first place to ensure it is written first into the
+		 * package zip file
+		 */
+		if (mPackageEntries.contains(OdfFile.MEDIA_TYPE.getPath())) {
+			mPackageEntries.remove(OdfFile.MEDIA_TYPE.getPath());
+			mPackageEntries.add(0, OdfFile.MEDIA_TYPE.getPath());
+		}
+
+		Iterator<String> it = mPackageEntries.iterator();
+		while (it.hasNext()) {
+			String key = it.next();
+			byte[] data = getBytes(key);
+
+			ZipEntry ze = mZipEntries.get(key);
+			if (ze == null)
+				ze = new ZipEntry(key);
+			ze.setTime(modTime);
+			ze.setMethod(isFileCompressed(key) ? DEFLATED : STORED);
+
+			CRC32 crc = new CRC32();
+			if (data != null) {
+				crc.update(data);
+				ze.setSize(data.length);
+			} else {
+				ze.setMethod(STORED);
+				ze.setSize(0);
+			}
+			ze.setCrc(crc.getValue());
+			ze.setCompressedSize(-1);
+			zos.putNextEntry(ze);
+			if (data != null)
+				zos.write(data, 0, data.length);
+			zos.closeEntry();
+
+			mZipEntries.put(key, ze);
+		}
+		zos.close();
+		odfStream.flush();
+	}
+
+	/**
+	 * if the file is to be compressed,return true
+	 *
+	 * @param key
+	 *            --file name
+	 * @return false if the file is not to be compressed
+	 */
+	private boolean isFileCompressed(String key) {
+		if (key.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath()))
+			return false;
+		boolean result = true;
+		if (key.lastIndexOf(".") > 0) {
+			String endWith = key.substring(key.lastIndexOf(".") + 1,
+					key.length());
+			if (mCompressedFileTypes.contains(endWith.toLowerCase()))
+				result = false;
+		}
+		return result;
+	}
+
+	/**
+	 * If this file is saved to itself, we have to cache it. It is not possible
+	 * to read and write from the same zip file at the same time, so the content
+	 * must be read and stored in memory.
+	 */
+	private void cacheContent() throws Exception {
+		// read all entries
+		getManifestEntries();
+		Iterator<String> entries = mZipEntries.keySet().iterator();
+		while (entries.hasNext()) {
+			// open all entries once so the data is cached
+			ZipEntry nextElement = mZipEntries.get(entries.next());
+			String entryPath = nextElement.getName();
+			getBytes(entryPath);
+		}
+	}
+
+	/**
+	 * Close the OdfPackage after it is no longer needed. Even after saving it
+	 * is still necessary to close the package to have again full access about
+	 * the file. Closing the OdfPackage will release all temporary created data.
+	 * Do this as the last action to free resources. Closing an already closed
+	 * document has no effect.
+	 */
+	@Override
+	public void close() {
+		if (mTempDir != null)
+			TempDir.deleteTempOdfDirectory(mTempDir);
+		try {
+			if (mZipFile != null)
+				mZipFile.close();
+		} catch (IOException ex) {
+			// log exception and continue
+			Logger.getLogger(OdfPackage.class.getName()).log(INFO, null, ex);
+		}
+		// release all stuff - this class is impossible to use afterwards
+		mZipFile = null;
+		mTempDirParent = null;
+		mTempDir = null;
+		mMediaType = null;
+		mPackageEntries = null;
+		mZipEntries = null;
+		mContentDoms = null;
+		mContentStreams = null;
+		mTempFiles = null;
+		mManifestList = null;
+		mManifestEntries = null;
+		mBaseURI = null;
+		mResolver = null;
+	}
+
+	/**
+	 * Data was updated, update mZipEntry and OdfFileEntry as well
+	 */
+	private void entryUpdate(String packagePath) throws Exception,
+			SAXException, TransformerConfigurationException,
+			TransformerException, ParserConfigurationException {
+
+		byte[] data = getBytes(packagePath);
+		int size = (data == null ? 0 : data.length);
+		OdfFileEntry fileEntry = getManifestEntries().get(packagePath);
+		ZipEntry zipEntry = mZipEntries.get(packagePath);
+
+		if (fileEntry != null) {
+			// if (XML_MEDIA_TYPE.equals(fileEntry.getMediaType())) {
+			// fileEntry.setSize(-1);
+			// } else {
+			fileEntry.setSize(size);
+			// }
+		}
+		if (zipEntry == null)
+			return;
+		zipEntry.setSize(size);
+		CRC32 crc = new CRC32();
+		if ((data != null) && size > 0)
+			crc.update(data);
+		zipEntry.setCrc(crc.getValue());
+		zipEntry.setCompressedSize(-1);
+		long modTime = (new java.util.Date()).getTime();
+		zipEntry.setTime(modTime);
+	}
+
+	/**
+	 * Parse the Manifest file
+	 */
+	void parseManifest() throws Exception {
+		InputStream is = getInputStream(OdfPackage.OdfFile.MANIFEST.packagePath);
+		if (is == null) {
+			mManifestList = null;
+			mManifestEntries = null;
+			return;
+		}
+
+		mManifestList = new LinkedList<>();
+
+		SAXParserFactory factory = SAXParserFactory.newInstance();
+		factory.setNamespaceAware(true);
+		factory.setValidating(false);
+		try {
+			factory.setFeature(
+					"http://apache.org/xml/features/nonvalidating/load-external-dtd",
+					false);
+		} catch (Exception ex) {
+			mLog.log(Level.SEVERE, null, ex);
+		}
+
+		SAXParser parser = factory.newSAXParser();
+		XMLReader xmlReader = parser.getXMLReader();
+		// More details at http://xerces.apache.org/xerces2-j/features.html#namespaces
+		xmlReader.setFeature("http://xml.org/sax/features/namespaces", true);
+		// More details at http://xerces.apache.org/xerces2-j/features.html#namespace-prefixes
+		xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes",
+				true);
+		try {
+			// More details at http://xerces.apache.org/xerces2-j/features.html#xmlns-uris
+			xmlReader.setFeature("http://xml.org/sax/features/xmlns-uris", true);
+		} catch (SAXException ex) {
+			mLog.log(Level.FINE, "Can't set XML reader feature xmlns-uris", ex);
+		}
+
+		String uri = mBaseURI + OdfPackage.OdfFile.MANIFEST.packagePath;
+		xmlReader.setEntityResolver(getEntityResolver());
+		xmlReader.setContentHandler(new ManifestContentHandler());
+
+		InputSource ins = new InputSource(is);
+		ins.setSystemId(uri);
+
+		xmlReader.parse(ins);
+
+		mContentStreams.remove(OdfPackage.OdfFile.MANIFEST.packagePath);
+		entryUpdate(OdfPackage.OdfFile.MANIFEST.packagePath);
+	}
+
+	/**
+	 * Checks if packagePath is not null nor empty and not an external reference
+	 */
+	private String ensureValidPackagePath(String packagePath) {
+		if (packagePath == null) {
+			String errMsg = "The packagePath given by parameter is NULL!";
+			mLog.severe(errMsg);
+			throw new IllegalArgumentException(errMsg);
+		}
+		if (packagePath.equals(EMPTY_STRING)) {
+			String errMsg = "The packagePath given by parameter is an empty string!";
+			mLog.severe(errMsg);
+			throw new IllegalArgumentException(errMsg);
+		}
+
+		if (packagePath.indexOf('\\') != -1)
+			packagePath = packagePath.replace('\\', '/');
+		if (isExternalReference(packagePath)) {
+			String errMsg = "The packagePath given by parameter '"
+					+ packagePath
+					+ "' is not an internal ODF package path!";
+			mLog.severe(errMsg);
+			throw new IllegalArgumentException(errMsg);
+		}
+		return packagePath;
+	}
+
+	/**
+	 * add a directory to the OdfPackage
+	 */
+	private void addDirectory(String packagePath) throws Exception {
+		packagePath = ensureValidPackagePath(packagePath);
+
+		if ((packagePath.length() < 1)
+				|| (packagePath.charAt(packagePath.length() - 1) != '/'))
+			packagePath = packagePath + SLASH;
+		insert((byte[]) null, packagePath, null);
+	}
+
+	/**
+	 * Insert DOM tree into OdfPackage. An existing file will be replaced.
+	 *
+	 * @param fileDOM
+	 *            - XML DOM tree to be inserted as file
+	 * @param packagePath
+	 *            - relative filePath where the DOM tree should be inserted as
+	 *            XML file
+	 * @param mediaType
+	 *            - media type of stream. Set to null if unknown
+	 * @throws java.lang.Exception
+	 *             when the DOM tree could not be inserted
+	 */
+	public void insert(Document fileDOM, String packagePath, String mediaType)
+			throws Exception {
+		packagePath = ensureValidPackagePath(packagePath);
+
+		try {
+			if (mManifestEntries == null)
+				parseManifest();
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+
+		if (mediaType == null)
+			mediaType = XML_MEDIA_TYPE;
+		String d = EMPTY_STRING;
+		StringTokenizer tok = new StringTokenizer(packagePath, SLASH);
+		while (tok.hasMoreTokens()) {
+			String s = tok.nextToken();
+			if (EMPTY_STRING.equals(d))
+				d = s + SLASH;
+			else
+				d = d + s + SLASH;
+			if (tok.hasMoreTokens() && !mPackageEntries.contains(d))
+				addDirectory(d);
+		}
+
+		mContentStreams.remove(packagePath);
+		if (fileDOM == null)
+			mContentDoms.remove(packagePath);
+		else
+			mContentDoms.put(packagePath, fileDOM);
+
+		if (!mPackageEntries.contains(packagePath))
+			mPackageEntries.add(packagePath);
+
+		try {
+			if (!OdfPackage.OdfFile.MANIFEST.packagePath.equals(packagePath)) {
+				if (mManifestEntries != null
+						&& mManifestEntries.get(packagePath) == null) {
+					OdfFileEntry fileEntry = new OdfFileEntry(packagePath,
+							mediaType);
+					mManifestEntries.put(packagePath, fileEntry);
+					mManifestList.add(packagePath);
+				}
+			} else
+				parseManifest();
+			// try to get the package from our cache
+			ZipEntry ze = mZipEntries.get(packagePath);
+			if (ze == null) {
+				ze = new ZipEntry(packagePath);
+				ze.setMethod(DEFLATED);
+				mZipEntries.put(packagePath, ze);
+			}
+			if (!isFileCompressed(packagePath))
+				ze.setMethod(STORED);
+
+			entryUpdate(packagePath);
+		} catch (SAXException se) {
+			throw new Exception("SAXException:" + se.getMessage());
+		} catch (ParserConfigurationException pce) {
+			throw new Exception("ParserConfigurationException:"
+					+ pce.getMessage());
+		} catch (TransformerConfigurationException tce) {
+			throw new Exception("TransformerConfigurationException:"
+					+ tce.getMessage());
+		} catch (TransformerException te) {
+			throw new Exception("TransformerException:" + te.getMessage());
+		}
+	}
+
+	/**
+	 * returns true if a DOM tree has been requested for given sub-content of
+	 * OdfPackage
+	 *
+	 * @param packagePath
+	 *            - a path inside the OdfPackage eg to a content.xml stream
+	 * @return - wether the package class internally has a DOM representation
+	 *         for the given path
+	 */
+	public boolean hasDom(String packagePath) {
+		return (mContentDoms.get(packagePath) != null);
+	}
+
+	/**
+	 * Gets org.w3c.dom.Document for XML file contained in package.
+	 *
+	 * @param packagePath
+	 *            - a path inside the OdfPackage eg to a content.xml stream
+	 * @return an org.w3c.dom.Document
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 * @throws Exception
+	 * @throws IllegalArgumentException
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 */
+	public Document getDom(String packagePath) throws SAXException,
+			ParserConfigurationException, Exception, IllegalArgumentException,
+			TransformerConfigurationException, TransformerException {
+		Document doc = mContentDoms.get(packagePath);
+		if (doc != null)
+			return doc;
+
+		InputStream is = getInputStream(packagePath);
+
+		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+		factory.setNamespaceAware(true);
+		factory.setValidating(false);
+
+		DocumentBuilder builder = factory.newDocumentBuilder();
+		builder.setEntityResolver(getEntityResolver());
+
+		String uri = getBaseURI() + packagePath;
+
+		// if (mErrorHandler != null) {
+		// builder.setErrorHandler(mErrorHandler);
+		// }
+
+		InputSource ins = new InputSource(is);
+		ins.setSystemId(uri);
+
+		doc = builder.parse(ins);
+
+		if (doc != null) {
+			mContentDoms.put(packagePath, doc);
+			// mContentStreams.remove(packagePath);
+		}
+		return doc;
+	}
+
+	/**
+	 * Inserts InputStream into an OdfPackage. An existing file will be
+	 * replaced.
+	 *
+	 * @param sourceURI
+	 *            - the source URI to the file to be inserted into the package.
+	 * @param mediaType
+	 *            - media type of stream. Set to null if unknown
+	 * @param packagePath
+	 *            - relative filePath where the tree should be inserted as XML
+	 *            file
+	 * @throws java.lang.Exception
+	 *             In case the file could not be saved
+	 */
+	public void insert(URI sourceURI, String packagePath, String mediaType)
+			throws Exception {
+		InputStream is = null;
+		if (sourceURI.isAbsolute())
+			// if the URI is absolute it can be converted to URL
+			is = sourceURI.toURL().openStream();
+		else {
+			// otherwise create a file class to open the stream
+			is = new FileInputStream(sourceURI.toString());
+			// TODO: error handling in this case! -> allow method insert(URI,
+			// ppath, mtype)?
+		}
+		insert(is, packagePath, mediaType);
+	}
+
+	/**
+	 * Inserts InputStream into an OdfPackage. An existing file will be
+	 * replaced.
+	 *
+	 * @param fileStream
+	 *            - the stream of the file to be inserted into the ODF package.
+	 * @param mediaType
+	 *            - media type of stream. Set to null if unknown
+	 * @param packagePath
+	 *            - relative filePath where the tree should be inserted as XML
+	 *            file
+	 * @throws java.lang.Exception
+	 *             In case the file could not be saved
+	 */
+	public void insert(InputStream fileStream, String packagePath,
+			String mediaType) throws Exception {
+		packagePath = ensureValidPackagePath(packagePath);
+
+		if (fileStream == null) {
+			insert((byte[]) null, packagePath, mediaType);
+			return;
+		}
+
+		ByteArrayOutputStream baos = new ByteArrayOutputStream();
+		BufferedInputStream bis = new BufferedInputStream(fileStream);
+		StreamHelper.stream(bis, baos);
+		byte[] data = baos.toByteArray();
+		insert(data, packagePath, mediaType);
+		// image should not be stored in memory but on disc
+		if (!packagePath.endsWith(".xml")
+				&& !packagePath.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())
+				&& mUseTempFile) {
+			// insertOutputStream to filesystem
+			File tempFile = new File(getTempDir(), packagePath);
+			File parent = tempFile.getParentFile();
+			parent.mkdirs();
+			try (OutputStream fos = new BufferedOutputStream(
+					new FileOutputStream(tempFile))) {
+				fos.write(data);
+			}
+			mTempFiles.put(packagePath, tempFile);
+			mContentStreams.remove(packagePath);
+		}
+	}
+
+	/**
+	 * Insert byte array into OdfPackage. An existing file will be replaced.
+	 *
+	 * @param fileBytes
+	 *            - data of the file stream to be stored in package
+	 * @param mediaType
+	 *            - media type of stream. Set to null if unknown
+	 * @param packagePath
+	 *            - relative filePath where the DOM tree should be inserted as
+	 *            XML file
+	 * @throws java.lang.Exception
+	 *             when the DOM tree could not be inserted
+	 */
+	public void insert(byte[] fileBytes, String packagePath, String mediaType)
+			throws Exception {
+		packagePath = ensureValidPackagePath(packagePath);
+
+		String d = EMPTY_STRING;
+		// 2DO: Test tokenizer for whitespaces..
+		StringTokenizer tok = new StringTokenizer(packagePath, SLASH);
+		while (tok.hasMoreTokens()) {
+			String s = tok.nextToken();
+			d = (EMPTY_STRING.equals(d) ? s + SLASH : d + s + SLASH);
+			if (tok.hasMoreTokens() && !mPackageEntries.contains(d)) {
+				addDirectory(d);
+				/*
+				 * add manifest entry for folder if not already existing media
+				 * type for folder has to be set for embedded objects
+				 */
+				if (!OdfPackage.OdfFile.MANIFEST.packagePath.equals(d)
+						&& mediaType != null
+						&& getManifestEntries().get(d) == null) {
+					OdfFileEntry fileEntry = new OdfFileEntry(d, mediaType, -1);
+					getManifestEntries().put(d, fileEntry);
+					if (!mManifestList.contains(d))
+						mManifestList.add(d);
+				}
+			}
+		}
+		try {
+			if (OdfPackage.OdfFile.MEDIA_TYPE.getPath().equals(packagePath)) {
+				try {
+					setMediaType(new String(fileBytes, "UTF-8"));
+				} catch (UnsupportedEncodingException useEx) {
+					mLog.log(WARNING,
+							"ODF file could not be created as string!", useEx);
+				}
+				return;
+			}
+			if (fileBytes == null)
+				mContentStreams.remove(packagePath);
+			else
+				mContentStreams.put(packagePath, fileBytes);
+			if (!mPackageEntries.contains(packagePath))
+				mPackageEntries.add(packagePath);
+			if (!OdfPackage.OdfFile.MANIFEST.packagePath.equals(packagePath)) {
+				if (mediaType != null
+						&& getManifestEntries().get(packagePath) == null) {
+					OdfFileEntry fileEntry = new OdfFileEntry(packagePath,
+							mediaType);
+					getManifestEntries().put(packagePath, fileEntry);
+					if (!mManifestList.contains(packagePath))
+						mManifestList.add(packagePath);
+				}
+			} else
+				parseManifest();
+			ZipEntry ze = mZipEntries.get(packagePath);
+			if (ze != null) {
+				ze = new ZipEntry(packagePath);
+				ze.setMethod(DEFLATED);
+				mZipEntries.put(packagePath, ze);
+				// 2DO Svante: No dependency to layer above!
+				if (isFileCompressed(packagePath) == false)
+					ze.setMethod(STORED);
+			}
+			entryUpdate(packagePath);
+		} catch (SAXException se) {
+			throw new Exception("SAXException:" + se.getMessage());
+		} catch (ParserConfigurationException pce) {
+			throw new Exception("ParserConfigurationException:"
+					+ pce.getMessage());
+		} catch (TransformerConfigurationException tce) {
+			throw new Exception("TransformerConfigurationException:"
+					+ tce.getMessage());
+		} catch (TransformerException te) {
+			throw new Exception("TransformerException:" + te.getMessage());
+		}
+	}
+
+	@SuppressWarnings("unused")
+	private void insert(ZipEntry zipe, byte[] content) {
+		if (content != null) {
+			if (zipe.getName().equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
+				try {
+					mMediaType = new String(content, 0, content.length, "UTF-8");
+				} catch (UnsupportedEncodingException ex) {
+					mLog.log(Level.SEVERE, null, ex);
+				}
+			} else
+				mContentStreams.put(zipe.getName(), content);
+		}
+		if (!mPackageEntries.contains(zipe.getName()))
+			mPackageEntries.add(zipe.getName());
+		mZipEntries.put(zipe.getName(), zipe);
+	}
+
+	@SuppressWarnings("unused")
+	private void insert(ZipEntry zipe, File file) {
+		if (file != null)
+			mTempFiles.put(zipe.getName(), file);
+		if (!mPackageEntries.contains(zipe.getName()))
+			mPackageEntries.add(zipe.getName());
+		mZipEntries.put(zipe.getName(), zipe);
+	}
+
+	public HashMap<String, OdfFileEntry> getManifestEntries() {
+		if (mManifestEntries == null)
+			try {
+				parseManifest();
+				if (mManifestEntries == null)
+					return null;
+			} catch (Exception e) {
+				throw new RuntimeException(e);
+			}
+		return mManifestEntries;
+	}
+
+	/**
+	 * Get Manifest as String NOTE: This functionality should better be moved to
+	 * a DOM based Manifest class
+	 *
+	 * @return the /META-INF/manifest.xml as a String
+	 */
+	public String getManifestAsString() {
+		StringBuilder buf = new StringBuilder();
+
+		buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+		buf.append("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
+
+		Iterator<String> it = mManifestList.iterator();
+		while (it.hasNext()) {
+			String key = it.next();
+			String s = null;
+			OdfFileEntry fileEntry = mManifestEntries.get(key);
+			if (fileEntry != null) {
+				buf.append(" <manifest:file-entry");
+				s = fileEntry.getMediaType();
+				if (s == null)
+					s = EMPTY_STRING;
+				buf.append(" manifest:media-type=\"");
+				buf.append(encodeXMLAttributes(s));
+				buf.append("\"");
+				s = fileEntry.getPath();
+
+				if (s == null)
+					s = EMPTY_STRING;
+				buf.append(" manifest:full-path=\"");
+				buf.append(encodeXMLAttributes(s));
+				buf.append("\"");
+				int i = fileEntry.getSize();
+				if (i > 0) {
+					buf.append(" manifest:size=\"");
+					buf.append(i);
+					buf.append("\"");
+				}
+				if (fileEntry.getVersion() != null) { 
+				    buf.append(" manifest:version=\"");
+	                buf.append(encodeXMLAttributes(fileEntry.getVersion()));
+	                buf.append("\"");
+				}
+				
+				EncryptionData enc = fileEntry.getEncryptionData();
+
+				if (enc != null) {
+					buf.append(">\n");
+					buf.append("  <manifest:encryption-data>\n");
+					Algorithm alg = enc.getAlgorithm();
+					if (alg != null) {
+						buf.append("   <manifest:algorithm");
+						s = alg.getName();
+						if (s == null)
+							s = EMPTY_STRING;
+						buf.append(" manifest:algorithm-name=\"");
+						buf.append(encodeXMLAttributes(s));
+						buf.append("\"");
+						s = alg.getInitializationVector();
+						if (s == null)
+							s = EMPTY_STRING;
+						buf.append(" manifest:initialization-vector=\"");
+						buf.append(encodeXMLAttributes(s));
+						buf.append("\"/>\n");
+					}
+					KeyDerivation keyDerivation = enc.getKeyDerivation();
+					if (keyDerivation != null) {
+						buf.append("   <manifest:key-derivation");
+						s = keyDerivation.getName();
+						if (s == null)
+							s = EMPTY_STRING;
+						buf.append(" manifest:key-derivation-name=\"");
+						buf.append(encodeXMLAttributes(s));
+						buf.append("\"");
+						s = keyDerivation.getSalt();
+						if (s == null)
+							s = EMPTY_STRING;
+						buf.append(" manifest:salt=\"");
+						buf.append(encodeXMLAttributes(s));
+						buf.append("\"");
+
+						buf.append(" manifest:iteration-count=\"");
+						buf.append(keyDerivation.getIterationCount());
+						buf.append("\"/>\n");
+					}
+					buf.append("  </manifest:encryption-data>\n");
+					buf.append(" </manifest:file-entry>\n");
+				} else
+					buf.append("/>\n");
+			}
+		}
+		buf.append("</manifest:manifest>");
+
+		return buf.toString();
+	}
+
+	/**
+	 * Get package (sub-) content as byte array
+	 *
+	 * @param packagePath
+	 *            relative filePath to the package content
+	 * @return the unzipped package content as byte array
+	 * @throws java.lang.Exception
+	 */
+	public byte[] getBytes(String packagePath) throws Exception {
+		packagePath = ensureValidPackagePath(packagePath);
+		byte[] data = null;
+
+		if (packagePath == null || packagePath.equals(EMPTY_STRING)) {
+			ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			save(baos, mBaseURI);
+			return baos.toByteArray();
+		}
+		if (packagePath.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
+			if (mMediaType == null)
+				return null;
+			try {
+				data = mMediaType.getBytes("UTF-8");
+			} catch (UnsupportedEncodingException use) {
+				mLog.log(Level.SEVERE, null, use);
+				return null;
+			}
+		} else if (mPackageEntries.contains(packagePath)
+				&& mContentDoms.get(packagePath) != null) {
+			Document doc = mContentDoms.get(packagePath);
+			ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+			// DOMXSImplementationSourceImpl dis = new
+			// org.apache.xerces.dom.DOMXSImplementationSourceImpl();
+			// DOMImplementationLS impl = (DOMImplementationLS)
+			// dis.getDOMImplementation("LS");
+			DOMImplementationLS impl = (DOMImplementationLS) DOMImplementationRegistry
+					.newInstance().getDOMImplementation("LS");
+			LSSerializer writer = impl.createLSSerializer();
+
+			LSOutput output = impl.createLSOutput();
+			output.setByteStream(baos);
+
+			writer.write(doc, output);
+			data = baos.toByteArray();
+		} else if (mPackageEntries.contains(packagePath)
+				&& mTempFiles.get(packagePath) != null) {
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			try (InputStream is = new BufferedInputStream(new FileInputStream(
+					mTempFiles.get(packagePath)))) {
+				StreamHelper.stream(is, os);
+			}
+			os.close();
+			data = os.toByteArray();
+		} else if (mPackageEntries.contains(packagePath)
+				&& mContentStreams.get(packagePath) != null)
+			data = mContentStreams.get(packagePath);
+		else if (packagePath.equals(OdfPackage.OdfFile.MANIFEST.packagePath)) {
+			if (mManifestEntries == null)
+				// manifest was not present
+				return null;
+			String s = getManifestAsString();
+			if (s == null)
+				return null;
+			data = s.getBytes("UTF-8");
+		}
+
+		if (data == null) { // not yet stored data; retrieve it.
+			ZipEntry entry = mZipEntries.get(packagePath);
+			if (entry != null) {
+				InputStream inputStream = mZipFile.getInputStream(entry);
+				if (inputStream != null) {
+					ByteArrayOutputStream out = new ByteArrayOutputStream();
+					StreamHelper.stream(inputStream, out);
+					data = out.toByteArray();
+					// store for further usage; do not care about manifest: that
+					// is handled exclusively
+					mContentStreams.put(packagePath, data);
+					if (!mPackageEntries.contains(packagePath))
+						mPackageEntries.add(packagePath);
+				}
+			}
+		}
+		return data;
+	}
+
+	/**
+	 * Get subcontent as InputStream
+	 *
+	 * @param packagePath
+	 *            of the desired stream.
+	 * @return Inputstream of the ODF file within the package for the given
+	 *         path.
+	 * @throws Exception
+	 */
+	public InputStream getInputStream(String packagePath) throws Exception {
+		packagePath = ensureValidPackagePath(packagePath);
+
+		if (packagePath.equals(OdfPackage.OdfFile.MANIFEST.packagePath)
+				&& (mManifestEntries == null)) {
+			ZipEntry entry = null;
+			if ((entry = mZipEntries.get(packagePath)) != null)
+				return mZipFile.getInputStream(entry);
+		}
+
+		if (mPackageEntries.contains(packagePath)
+				&& mTempFiles.get(packagePath) != null)
+			return new BufferedInputStream(new FileInputStream(
+					mTempFiles.get(packagePath)));
+
+		/*
+		 * else we always cache here and return a ByteArrayInputStream because
+		 * if we would return ZipFile getInputStream(entry) we would not be able
+		 * to read 2 Entries at the same time. This is a limitation of the
+		 * ZipFile class.
+		 * 
+		 * As it would be quite a common thing to read the content.xml and the
+		 * styles.xml simultaneously when using XSLT on OdfPackages we want to
+		 * circumvent this limitation
+		 */
+
+		byte[] data = getBytes(packagePath);
+		if (data != null && data.length != 0)
+			return new ByteArrayInputStream(data);
+		return null;
+	}
+
+	/**
+	 * Gets the InputStream containing whole OdfPackage.
+	 *
+	 * @return the ODF package as input stream
+	 * @throws java.lang.Exception
+	 *             - if the package could not be read
+	 */
+	public InputStream getInputStream() throws Exception {
+		final PipedOutputStream os = new PipedOutputStream();
+		final PipedInputStream is = new PipedInputStream();
+
+		is.connect(os);
+
+		Thread thread1 = new Thread() {
+			@Override
+			public void run() {
+				try {
+					save(os, mBaseURI);
+				} catch (Exception e) {
+				}
+			}
+		};
+
+		Thread thread2 = new Thread() {
+			@Override
+			public void run() {
+				try {
+					BufferedInputStream bis = new BufferedInputStream(is,
+							PAGE_SIZE);
+					BufferedOutputStream bos = new BufferedOutputStream(os,
+							PAGE_SIZE);
+					StreamHelper.stream(bis, bos);
+					is.close();
+					os.close();
+				} catch (Exception ie) {
+				}
+			}
+		};
+
+		thread1.start();
+		thread2.start();
+
+		return is;
+	}
+
+	/**
+	 * Insert the OutputStream for into OdfPackage. An existing file will be
+	 * replaced.
+	 *
+	 * @param packagePath
+	 *            - relative filePath where the DOM tree should be inserted as
+	 *            XML file
+	 * @return outputstream for the data of the file to be stored in package
+	 * @throws java.lang.Exception
+	 *             when the DOM tree could not be inserted
+	 */
+	public OutputStream insertOutputStream(String packagePath) throws Exception {
+		return insertOutputStream(packagePath, null);
+	}
+
+	/**
+	 * Insert the OutputStream - to be filled after method - when stream is
+	 * closed into OdfPackage. An existing file will be replaced.
+	 *
+	 * @param packagePath
+	 *            - relative filePath where the DOM tree should be inserted as
+	 *            XML file
+	 * @param mediaType
+	 *            - media type of stream
+	 * @return outputstream for the data of the file to be stored in package
+	 * @throws java.lang.Exception
+	 *             when the DOM tree could not be inserted
+	 */
+	public OutputStream insertOutputStream(String packagePath, String mediaType)
+			throws Exception {
+		packagePath = ensureValidPackagePath(packagePath);
+		final String fPath = packagePath;
+		final OdfFileEntry fFileEntry = getFileEntry(packagePath);
+		final String fMediaType = mediaType;
+
+		ByteArrayOutputStream baos = new ByteArrayOutputStream() {
+			@Override
+			public void close() {
+				try {
+					byte[] data = this.toByteArray();
+					if (fMediaType == null || fMediaType.length() == 0)
+						insert(data, fPath, fFileEntry == null ? null
+								: fFileEntry.getMediaType());
+					else
+						insert(data, fPath, fMediaType);
+					super.close();
+				} catch (Exception ex) {
+					mLog.log(Level.SEVERE, null, ex);
+				}
+			}
+		};
+		return baos;
+	}
+
+	// /**
+	// * get an InputStream with a specific filePath from the package.
+	// *
+	// * @throws IllegalArgumentException if sub-content is not XML
+	// */
+	// public InputStream getInputStream(String filePath) throws Exception {
+	// return mZipFile.getInputStream(mZipFile.getEntry(filePath));
+	// // OdfPackageStream stream = new OdfPackageStream(this, filePath);
+	// // return stream;
+	// }
+
+	public void remove(String packagePath) {
+		HashMap<String, OdfFileEntry> manifestEntries = getManifestEntries();
+		if (mManifestList != null && mManifestList.contains(packagePath))
+			mManifestList.remove(packagePath);
+		if (manifestEntries != null && manifestEntries.containsKey(packagePath))
+			manifestEntries.remove(packagePath);
+		if (mZipEntries != null && mZipEntries.containsKey(packagePath))
+			mZipEntries.remove(packagePath);
+		if (mTempFiles != null && mTempFiles.containsKey(packagePath))
+			mTempFiles.remove(packagePath).delete();
+		if (mPackageEntries != null && mPackageEntries.contains(packagePath))
+			mPackageEntries.remove(packagePath);
+	}
+
+	/**
+	 * Checks if the given reference is a reference, which points outside the
+	 * ODF package
+	 *
+	 * @param fileRef
+	 *            the file reference to be checked
+	 * @return true if the reference is an package external reference
+	 */
+	public static boolean isExternalReference(String fileRef) {
+		// if the fileReference is...
+		return fileRef.startsWith(TWO_DOTS) // an external relative filePath
+				|| fileRef.startsWith(SLASH) // or absolute filePath
+				|| fileRef.contains(COLON); // or absolute IRI
+	}
+
+	/**
+	 * Get Temp Directory. Create new temp directory on demand and register it
+	 * for removal by garbage collector
+	 */
+	private File getTempDir() throws Exception {
+		if (mTempDir == null) {
+			mTempDir = newTempOdfDirectory("ODF", mTempDirParent);
+			mFinalize = new OdfFinalizablePackage(mTempDir);
+		}
+		return mTempDir;
+	}
+
+	/**
+	 * Encoded XML Attributes
+	 */
+	private String encodeXMLAttributes(String s) {
+		return s.replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
+	}
+
+	private class ManifestContentHandler implements ContentHandler {
+		private OdfFileEntry fileEntry;
+		private EncryptionData encryptionData;
+
+		/**
+		 * Receive an object for locating the origin of SAX document events.
+		 */
+		@Override
+		public void setDocumentLocator(Locator locator) {
+		}
+
+		/**
+		 * Receive notification of the beginning of a document.
+		 */
+		@Override
+		public void startDocument() throws SAXException {
+			mManifestList = new LinkedList<>();
+			mManifestEntries = new HashMap<>();
+		}
+
+		/**
+		 * Receive notification of the end of a document.
+		 */
+		@Override
+		public void endDocument() throws SAXException {
+		}
+
+		/**
+		 * Begin the scope of a prefix-URI Namespace mapping.
+		 */
+		@Override
+		public void startPrefixMapping(String prefix, String uri)
+				throws SAXException {
+		}
+
+		/**
+		 * End the scope of a prefix-URI mapping.
+		 */
+		@Override
+		public void endPrefixMapping(String prefix) throws SAXException {
+		}
+
+		/**
+		 * Receive notification of the beginning of an element.
+		 */
+		@Override
+		public void startElement(String namespaceURI, String localName,
+				String qName, Attributes atts) throws SAXException {
+			switch (localName) {
+			case "file-entry":
+				fileEntry = new OdfFileEntry();
+				fileEntry.setPath(atts.getValue("manifest:full-path"));
+				fileEntry.setMediaType(atts.getValue("manifest:media-type"));
+				try {
+					if (atts.getValue("manifest:size") != null)
+						fileEntry.setSize(Integer.parseInt(atts
+								.getValue("manifest:size")));
+				} catch (NumberFormatException nfe) {
+					throw new SAXException("not a number: "
+							+ atts.getValue("manifest:size"));
+				}
+				fileEntry.setVersion(atts.getValue("manifest:version"));
+				break;
+			case "encryption-data":
+				encryptionData = new EncryptionData();
+				if (fileEntry != null) {
+					encryptionData.setChecksumType(atts
+							.getValue("manifest:checksum-type"));
+					encryptionData.setChecksum(atts
+							.getValue("manifest:checksum"));
+					fileEntry.setEncryptionData(encryptionData);
+				}
+				break;
+			case "algorithm":
+				Algorithm algorithm = new Algorithm();
+				algorithm.setName(atts.getValue("manifest:algorithm-name"));
+				algorithm.setInitializationVector(atts
+						.getValue("manifest:initialization-vector"));
+				if (encryptionData != null)
+					encryptionData.setAlgorithm(algorithm);
+				break;
+			case "key-derivation":
+				KeyDerivation keyDerivation = new KeyDerivation();
+				keyDerivation.setName(atts
+						.getValue("manifest:key-derivation-name"));
+				keyDerivation.setSalt(atts.getValue("manifest:salt"));
+				try {
+					if (atts.getValue("manifest:iteration-count") != null)
+						keyDerivation.setIterationCount(Integer.parseInt(atts
+								.getValue("manifest:iteration-count")));
+				} catch (NumberFormatException nfe) {
+					throw new SAXException("not a number: "
+							+ atts.getValue("manifest:iteration-count"));
+				}
+				if (encryptionData != null)
+					encryptionData.setKeyDerivation(keyDerivation);
+				break;
+			}
+		}
+
+		/**
+		 * Receive notification of the end of an element.
+		 */
+		@Override
+		public void endElement(String namespaceURI, String localName,
+				String qName) throws SAXException {
+			if (localName.equals("file-entry")) {
+				if (fileEntry.getPath() != null)
+					getManifestEntries().put(fileEntry.getPath(),
+							fileEntry);
+				mManifestList.add(fileEntry.getPath());
+				fileEntry = null;
+			} else if (localName.equals("encryption-data"))
+				encryptionData = null;
+		}
+
+		/**
+		 * Receive notification of character data.
+		 */
+		@Override
+		public void characters(char[] ch, int start, int length)
+				throws SAXException {
+		}
+
+		/**
+		 * Receive notification of ignorable whitespace in element content.
+		 */
+		@Override
+		public void ignorableWhitespace(char[] ch, int start, int length)
+				throws SAXException {
+		}
+
+		/**
+		 * Receive notification of a processing instruction.
+		 */
+		@Override
+		public void processingInstruction(String target, String data)
+				throws SAXException {
+		}
+
+		/**
+		 * Receive notification of a skipped entity.
+		 */
+		@Override
+		public void skippedEntity(String name) throws SAXException {
+		}
+	}
+
+	/**
+	 * resolve external entities
+	 */
+	private class Resolver implements EntityResolver, URIResolver {
+		/**
+		 * Resolver constructor.
+		 */
+		public Resolver() {
+		}
+
+		/**
+		 * Allow the application to resolve external entities.
+		 *
+		 * The Parser will call this method before opening any external entity
+		 * except the top-level document entity (including the external DTD
+		 * subset, external entities referenced within the DTD, and external
+		 * entities referenced within the document element): the application may
+		 * request that the parser resolve the entity itself, that it use an
+		 * alternative URI, or that it use an entirely different input source.
+		 */
+		@Override
+		public InputSource resolveEntity(String publicId, String systemId)
+				throws SAXException {
+			// This deactivates the attempt to loadPackage the Math DTD
+			if (publicId != null
+					&& publicId
+							.startsWith("-//OpenOffice.org//DTD Modified W3C MathML"))
+				return new InputSource(new ByteArrayInputStream(
+						"<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
+
+			if (systemId == null)
+				return null;
+
+			if ((mBaseURI != null) && systemId.startsWith(mBaseURI)) {
+				if (systemId.equals(mBaseURI))
+					try {
+						InputStream in = getInputStream();
+						InputSource ins = new InputSource(in);
+						ins.setSystemId(systemId);
+						return ins;
+					} catch (Exception e) {
+						throw new SAXException(e);
+					}
+				else if (systemId.length() > mBaseURI.length() + 1)
+					try (InputStream in = getInputStream(systemId
+							.substring(mBaseURI.length() + 1))) {
+						InputSource ins = new InputSource(in);
+						ins.setSystemId(systemId);
+						return ins;
+					} catch (Exception ex) {
+						mLog.log(Level.SEVERE, null, ex);
+					}
+			} else if (systemId.startsWith("resource:/")) {
+				int i = systemId.indexOf('/');
+				if ((i > 0) && systemId.length() > i + 1) {
+					String res = systemId.substring(i + 1);
+					ClassLoader cl = OdfPackage.class.getClassLoader();
+					InputStream in = cl.getResourceAsStream(res);
+					if (in != null) {
+						InputSource ins = new InputSource(in);
+						ins.setSystemId(systemId);
+						return ins;
+					}
+				}
+			} else if (systemId.startsWith("jar:"))
+				try {
+					URL url = new URL(systemId);
+					JarURLConnection jarConn = (JarURLConnection) url
+							.openConnection();
+					InputSource ins = new InputSource(jarConn.getInputStream());
+					ins.setSystemId(systemId);
+					return ins;
+				} catch (IOException ex) {
+					mLog.log(Level.SEVERE, null, ex);
+				}
+			return null;
+		}
+
+		@Override
+		public Source resolve(String href, String base)
+				throws TransformerException {
+			try {
+				URI uri;
+				if (base != null) {
+					URI baseuri = new URI(base);
+					uri = baseuri.resolve(href);
+				} else
+					uri = new URI(href);
+
+				InputSource ins;
+				try {
+					ins = resolveEntity(null, uri.toString());
+					if (ins == null)
+						return null;
+				} catch (Exception e) {
+					throw new TransformerException(e);
+				}
+
+				InputStream in = ins.getByteStream();
+				StreamSource src = new StreamSource(in);
+				src.setSystemId(uri.toString());
+				return src;
+			} catch (URISyntaxException use) {
+				return null;
+			}
+		}
+	}
+
+	/**
+	 * Get EntityResolver to be used in XML Parsers which can resolve content
+	 * inside the OdfPackage
+	 *
+	 * @return a SAX EntityResolver
+	 */
+	public EntityResolver getEntityResolver() {
+		if (mResolver == null)
+			mResolver = new Resolver();
+		return mResolver;
+	}
+
+	/**
+	 * Get URIResolver to be used in XSL Transformations which can resolve
+	 * content inside the OdfPackage
+	 *
+	 * @return a TraX Resolver
+	 */
+	public URIResolver getURIResolver() {
+		if (mResolver == null)
+			mResolver = new Resolver();
+		return mResolver;
+	}
+
+	private static String getBaseURIFromFile(File file) throws Exception {
+		String baseURI = file.getCanonicalFile().toURI().toString();
+		if (File.separatorChar == '\\')
+			baseURI = baseURI.replaceAll("\\\\", SLASH);
+		return baseURI;
+	}
+
+	private class ZipHelper implements AutoCloseable {
+		private ZipFile mZipFile = null;
+		private byte[] mZipBuffer = null;
+
+		public ZipHelper(ZipFile zipFile) {
+			mZipFile = zipFile;
+			mZipBuffer = null;
+		}
+
+		public ZipHelper(byte[] buffer) {
+			mZipBuffer = buffer;
+			mZipFile = null;
+		}
+
+		public Enumeration<? extends ZipEntry> entries() throws IOException {
+			if (mZipFile != null)
+				return mZipFile.entries();
+
+			Vector<ZipEntry> list = new Vector<>();
+			ZipInputStream inputStream = new ZipInputStream(
+					new ByteArrayInputStream(mZipBuffer));
+			ZipEntry zipEntry = inputStream.getNextEntry();
+			while (zipEntry != null) {
+				list.add(zipEntry);
+				zipEntry = inputStream.getNextEntry();
+			}
+			inputStream.close();
+			return list.elements();
+		}
+
+		@SuppressWarnings("resource")
+		public InputStream getInputStream(ZipEntry entry) throws IOException {
+			if (mZipFile != null)
+				return mZipFile.getInputStream(entry);
+
+			ZipInputStream inputStream = new ZipInputStream(
+					new ByteArrayInputStream(mZipBuffer));
+			ZipEntry zipEntry = inputStream.getNextEntry();
+			while (zipEntry != null) {
+				if (zipEntry.getName().equalsIgnoreCase(entry.getName()))
+					return readAsInputStream(inputStream);
+				zipEntry = inputStream.getNextEntry();
+			}
+			return null;
+		}
+
+		private InputStream readAsInputStream(ZipInputStream inputStream)
+				throws IOException {
+			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+			byte[] buf = new byte[4096];
+			while (true) {
+				int r = inputStream.read(buf);
+				if (r < 0)
+					break;
+				outputStream.write(buf, 0, r);
+			}
+			inputStream.close();
+			return new ByteArrayInputStream(outputStream.toByteArray());
+		}
+
+		@Override
+		public void close() throws IOException {
+			if (mZipFile != null)
+				mZipFile.close();
+			else
+				mZipBuffer = null;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackageStream.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackageStream.java b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackageStream.java
new file mode 100644
index 0000000..5b25929
--- /dev/null
+++ b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfPackageStream.java
@@ -0,0 +1,81 @@
+/************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
+ * 
+ * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
+ * 
+ * Use is subject to license terms.
+ * 
+ * Licensed 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. You can also
+ * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ************************************************************************/
+
+package org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg.manifest.OdfFileEntry;
+
+
+/**
+ * OdfPackageStream is a representation of a stream that is part of an ODF file.
+ * If a stream is written to through the output stream, this will be reflected
+ * when the output stream is closed
+ */
+class OdfPackageStream extends StreamSource {
+	private OdfPackage pkg;
+	private String name;
+	private InputStream inputStream;
+	private OutputStream outputStream = null;
+	private boolean bClosed;
+
+	public OdfPackageStream(OdfPackage pkg, String name) throws Exception {
+		super(pkg.getInputStream(name), pkg.getBaseURI() + "/" + name);
+		this.pkg = pkg;
+		this.name = name;
+	}
+
+	public boolean isOutput() {
+		// denote that the output stream has been requested
+		return outputStream != null;
+	}
+
+	public OutputStream getOutputStream() throws Exception {
+		if (bClosed)
+			throw new IOException("stream already closed");
+		outputStream = pkg.insertOutputStream(name);
+		return outputStream;
+	}
+
+	public OdfFileEntry geFileEntry() {
+		return pkg.getFileEntry(name);
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public OdfPackage getPackage() {
+		return pkg;
+	}
+
+	void close() throws IOException {
+		bClosed = true;
+		inputStream.close();
+		outputStream.close();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfXMLHelper.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfXMLHelper.java b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfXMLHelper.java
new file mode 100644
index 0000000..9d4e152
--- /dev/null
+++ b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/OdfXMLHelper.java
@@ -0,0 +1,370 @@
+/************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
+ *
+ * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Use is subject to license terms.
+ *
+ * Licensed 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. You can also
+ * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ************************************************************************/
+package org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+public class OdfXMLHelper {
+	/**
+	 * create an XMLReader
+	 * with a Resolver set to parse content in a ODF Package
+	 *
+	 * @param pkg the ODF Package
+	 * @return a SAX XMLReader
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public XMLReader newXMLReader(OdfPackage pkg)
+			throws SAXException, ParserConfigurationException {
+		// SAXParserFactory factory = new
+		// org.apache.xerces.jaxp.SAXParserFactoryImpl();
+
+		SAXParserFactory factory = SAXParserFactory.newInstance();
+		factory.setNamespaceAware(true);
+		factory.setValidating(false);
+
+		SAXParser parser = factory.newSAXParser();
+		XMLReader xmlReader = parser.getXMLReader();
+		// More details at http://xerces.apache.org/xerces2-j/features.html#namespaces
+		xmlReader.setFeature("http://xml.org/sax/features/namespaces", true);
+		// More details at http://xerces.apache.org/xerces2-j/features.html#namespace-prefixes
+		xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+		// More details at http://xerces.apache.org/xerces2-j/features.html#xmlns-uris
+		xmlReader.setFeature("http://xml.org/sax/features/xmlns-uris", true);
+		xmlReader.setEntityResolver(pkg.getEntityResolver());
+
+		return xmlReader;
+	}
+
+	/**
+	 * use SAX parser to parse content of package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param contentHandler a SAX Content handler to receive SAX Events
+	 * @param errorHandler a SAX Error handler to be called on errors during parsing
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 */
+	public void parse(OdfPackage pkg, String path, ContentHandler contentHandler, ErrorHandler errorHandler)
+			throws SAXException, ParserConfigurationException, IOException, IllegalArgumentException, TransformerConfigurationException, TransformerException {
+		try (InputStream is = pkg.getInputStream(path)) {
+			XMLReader reader = newXMLReader(pkg);
+
+			String uri = pkg.getBaseURI() + path;
+
+			if (contentHandler != null)
+				reader.setContentHandler(contentHandler);
+			if (errorHandler != null)
+				reader.setErrorHandler(errorHandler);
+
+			InputSource ins = new InputSource(is);
+			ins.setSystemId(uri);
+
+			reader.parse(ins);
+		} catch (Exception ex) {
+			Logger.getLogger(OdfXMLHelper.class.getName()).log(Level.SEVERE, null, ex);
+		}
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param templatePath a path to a file in the filesystem containing an XSL Template
+	 * @param outPath a path in the filesystem for the output of the XSL Transformation
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, String templatePath, String outPath)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException, ParserConfigurationException {
+		transform(pkg, path, new File(templatePath), new File(outPath));
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param templateSource TraX Source of an XSL Transformation Template
+	 * @param outPath path to an output file for the XSL Transformation
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, Source templateSource, String outPath)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException, ParserConfigurationException {
+		transform(pkg, path, templateSource, new File(outPath));
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param templateSource TraX Source of an XSL Transformation
+	 * @param out an output File
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, Source templateSource, File out)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException, ParserConfigurationException {
+		transform(pkg, path, templateSource, new StreamResult(out));
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * insert result back to package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param templatePath path inside the filesystem to an XSL template file
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, String templatePath)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException, ParserConfigurationException {
+		transform(pkg, path, new File(templatePath));
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param template File containing an XSLT Template
+	 * @param out File for the XSLT ouput
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, File template, File out)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException, ParserConfigurationException {
+		TransformerFactory transformerfactory = TransformerFactory.newInstance();
+
+		Templates templates = transformerfactory.newTemplates(new StreamSource(template));
+		transform(pkg, path, templates, new StreamResult(out));
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * insert result back to package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param template a File containing an XSLT Template
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, File template)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException, ParserConfigurationException {
+		TransformerFactory transformerfactory = TransformerFactory.newInstance();
+
+		Templates templates = transformerfactory.newTemplates(new StreamSource(template));
+		transform(pkg, path, templates);
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param templateSource TraX Source of an XSLT Template
+	 * @param result TraX Result of XSL-Tranformation
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, Source templateSource, Result result)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException,
+			ParserConfigurationException {
+		TransformerFactory transformerfactory = TransformerFactory.newInstance();
+		transformerfactory.setURIResolver(pkg.getURIResolver());
+
+		Templates templates = transformerfactory.newTemplates(templateSource);
+		transform(pkg, path, templates, result);
+	}
+
+	/**
+	 * Does an XSL-Transformation on content contained in package.<br/><br/>
+	 *
+	 * There are three default parameteres provided to the transformation:
+	 * There are three default parameteres provided to the transformation:
+	 * <ol>
+	 * <li><b>sourceURL:</b> the URL of the source directory </li>
+	 * <li><b>sourceBaseURL:</b> baseURL of the source file (the package).
+	 * This URL necessary to access any content within the package from the XSLT scripts.
+	 * The relative package path will concatenated after the 'sourceBaseURL'.</li>
+	 * <li><b>targetURL:</b> the URL of the target directory</li>
+	 * <li><b>targetBaseURL:</b>the baseURL of the target file</li>
+	 * </ol>
+	 *
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param templates TraX XSLT Template
+	 * @param result TraX XSLT Result
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	public void transform(OdfPackage pkg, String path, Templates templates, Result result)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException,
+			ParserConfigurationException {
+		try {
+			Source source = null;
+			String uri = pkg.getBaseURI() + path;
+			Document doc = pkg.getDom(path);
+			source = new DOMSource(doc);
+			Transformer transformer = templates.newTransformer();
+			transformer.setURIResolver(pkg.getURIResolver());
+
+			transformer.setParameter("sourceURL", uri);
+			transformer.setParameter("sourceBaseURL", pkg.getBaseURI() + "/");
+
+			uri = result.getSystemId();
+			if (uri != null) {
+				transformer.setParameter("targetURL", uri);
+				int i = uri.lastIndexOf('/');
+				if (i > 0) {
+					uri = uri.substring(0, i + 1);
+					transformer.setParameter("targetBaseURL", uri);
+				}
+			}
+			DocumentType doctype = doc.getDoctype();
+			if (doctype != null) {
+				if (doctype.getPublicId() != null)
+					transformer.setParameter("publicType", doctype.getPublicId());
+				if (doctype.getSystemId() != null)
+					transformer.setParameter("systemType", doctype.getSystemId());
+			}
+
+			transformer.transform(source, result);
+		} catch (Exception ex) {
+			Logger.getLogger(OdfXMLHelper.class.getName()).log(Level.SEVERE, null, ex);
+			ex.printStackTrace();
+		}
+	}
+
+	/**
+	 * Do XSL-Transformation on content contained in package
+	 * and insert result back to package
+	 * @param pkg a OdfPackage
+	 * @param path a path inside the OdfPackage, eg. to a contained content.xml stream
+	 * @param templates Trax XSLT Template
+	 * @throws TransformerConfigurationException
+	 * @throws TransformerException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws SAXException
+	 * @throws ParserConfigurationException
+	 */
+	@SuppressWarnings("null")
+	public void transform(OdfPackage pkg, String path, Templates templates)
+			throws TransformerConfigurationException, TransformerException,
+			IOException, IllegalArgumentException, SAXException,
+			ParserConfigurationException {
+		final boolean useDom = pkg.hasDom(path);
+		Result result = null;
+		ByteArrayOutputStream baos = null;
+
+		if (useDom)
+			result = new DOMResult();
+		else {
+			baos = new ByteArrayOutputStream();
+			result = new StreamResult(baos);
+		}
+
+		transform(pkg, path, templates, result);
+
+		try {
+			if (useDom)
+				pkg.insert((Document) ((DOMResult) result).getNode(), path,
+						null);
+			else
+				pkg.insert(baos.toByteArray(), path, "text/xml");
+		} catch (Exception ex) {
+			Logger.getLogger(OdfXMLHelper.class.getName()).log(Level.SEVERE,
+					null, ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/StreamHelper.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/StreamHelper.java b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/StreamHelper.java
new file mode 100644
index 0000000..19b43dc
--- /dev/null
+++ b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/StreamHelper.java
@@ -0,0 +1,81 @@
+package org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class reads from an InputStream and writes to an OutputStream.
+ * No streams will be closed - calling classes must do this.
+ */
+class StreamHelper {
+    // 4096 is thought to be the minimum page size for most systems;
+    // change this for optimization
+    public static final int PAGE_SIZE = 4096;
+    private InputStream in;
+    private OutputStream out;
+    private boolean mStreamed;
+
+    /**
+     * Read from the input stream and write to the output. This method
+     * does not close any stream; calling methods must take care of that.
+     * @throws IOException when io error happens
+     */
+    static void stream(InputStream in, OutputStream out) throws IOException {
+        StreamHelper s = new StreamHelper(in, out);
+        s.stream();
+    }
+
+    /**
+     * Create a new StreamHelper
+     * @param in the input stream used for reading
+     * @param out the output stream written to
+     */
+    StreamHelper(InputStream in, OutputStream out) {
+        this.in = in;
+        this.out = out;
+        mStreamed = false;
+    }
+
+    /**
+     * Read from the input stream and write to the output. This method can only
+     * be called once. A second call will result in an IOException. This method
+     * does not close any stream; calling methods must take care of that.
+     * @throws IOException when io error happens
+     */
+	void stream() throws IOException {
+		if (mStreamed)
+			throw new IOException();
+		byte[] buf = new byte[PAGE_SIZE];
+		int r = 0;
+		// let npe happen if one of the streams is null
+		while ((r = in.read(buf, 0, PAGE_SIZE)) > -1) {
+			out.write(buf, 0, r);
+		}
+		// free the references
+		in = null;
+		out = null;
+		mStreamed = true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDir.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDir.java b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDir.java
new file mode 100644
index 0000000..220b3aa
--- /dev/null
+++ b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDir.java
@@ -0,0 +1,57 @@
+/************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
+ * 
+ * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
+ * 
+ * Use is subject to license terms.
+ * 
+ * Licensed 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. You can also
+ * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ************************************************************************/
+package org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg;
+
+import java.io.File;
+import java.io.IOException;
+
+class TempDir {
+    /**
+     * Creates a temp directory with a generated name (given a certain prefix) 
+     * in a given directory.
+     * @param prefix The prefix for the directory name; must be three characters
+     * or more.
+     * @param parentDir A directory where the new tmep directory is created;
+     * if this is null, the java,io.tmpdir will be used.
+     */
+    static File newTempOdfDirectory(String prefix, File parentDir)
+            throws IOException {
+        File tempFile = File.createTempFile(prefix, "", parentDir);
+        if (!tempFile.delete())
+            throw new IOException();
+        if (!tempFile.mkdir())
+            throw new IOException();
+        TempDirDeleter.getInstance().add(tempFile);
+        return tempFile;
+    }
+
+    /**
+     * Delete a temporary created directory. As a precaution, only directories
+     * created by this class can be deleted. To avoid memory leaks, this class 
+     * should be used exclusively for deleting.
+     * @param odfDirectory the temp directory to delete.
+     */
+    static void deleteTempOdfDirectory(File odfDirectory) {
+        if (TempDirDeleter.getInstance().remove(odfDirectory))
+            TempDirDeleter.getInstance().deleteDirectory(odfDirectory);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-language/blob/c08405cb/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDirDeleter.java
----------------------------------------------------------------------
diff --git a/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDirDeleter.java b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDirDeleter.java
new file mode 100644
index 0000000..2b2c0de
--- /dev/null
+++ b/taverna-scufl2-ucfpackage/src/main/java/org/apache/taverna/scufl2/ucfpackage/impl/odfdom/pkg/TempDirDeleter.java
@@ -0,0 +1,83 @@
+/************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
+ * 
+ * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
+ * 
+ * Use is subject to license terms.
+ * 
+ * Licensed 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. You can also
+ * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ************************************************************************/
+package org.apache.taverna.scufl2.ucfpackage.impl.odfdom.pkg;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+class TempDirDeleter extends Thread {
+    private static TempDirDeleter deleterThread = null;
+    private ArrayList<File> dirList;
+
+    static TempDirDeleter getInstance() {
+        if (deleterThread == null) {
+            deleterThread = new TempDirDeleter();
+            Runtime.getRuntime().addShutdownHook(deleterThread);
+        }
+        return deleterThread;
+    }
+
+    private TempDirDeleter() {
+        dirList = new ArrayList<>();
+    }
+
+    synchronized boolean add(File dir) {
+        return dirList.add(dir);
+    }
+
+    synchronized boolean remove(File dir) {
+        return dirList.remove(dir);
+    }
+
+    @Override
+    public void run() {
+        synchronized (this) {
+            Iterator<File> iterator = dirList.iterator();
+            while (iterator.hasNext()) {
+                File dir = iterator.next();
+                deleteDirectoryRecursive(dir);
+                iterator.remove();
+            }
+            dirList.clear();
+        }
+    }
+
+    private void deleteDirectoryRecursive(File dir) {
+        if (dir == null)
+            return;
+
+        File[] fileArray = dir.listFiles();
+        if (fileArray != null)
+            for (int i = 0; i < fileArray.length; i++)
+                if (fileArray[i].isDirectory())
+                    deleteDirectoryRecursive(fileArray[i]);
+                else
+                    fileArray[i].delete();
+        dir.delete();
+    }
+
+    void deleteDirectory(File dir) {
+        deleteDirectoryRecursive(dir);
+        dirList.remove(dir);
+    }
+}