You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ye...@apache.org on 2009/01/29 13:44:35 UTC

svn commit: r738842 [2/7] - in /poi/trunk: ./ legal/ maven/ src/documentation/content/xdocs/ src/documentation/content/xdocs/oxml4j/ src/documentation/content/xdocs/spreadsheet/ src/examples/src/org/apache/poi/xssf/eventusermodel/examples/ src/ooxml/ja...

Added: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java?rev=738842&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java Thu Jan 29 12:44:31 2009
@@ -0,0 +1,1396 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.log4j.Logger;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
+import org.apache.poi.openxml4j.opc.internal.PartUnmarshaller;
+import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.marshallers.DefaultMarshaller;
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller;
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.PackagePropertiesUnmarshaller;
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.UnmarshallContext;
+import org.apache.poi.openxml4j.util.Nullable;
+
+/**
+ * Represents a container that can store multiple data objects.
+ * 
+ * @author Julien Chable, CDubet
+ * @version 0.1
+ */
+public abstract class Package implements RelationshipSource {
+
+	/**
+	 * Logger.
+	 */
+	protected static Logger logger = Logger.getLogger("org.openxml4j.opc");
+
+	/**
+	 * Default package access.
+	 */
+	protected static final PackageAccess defaultPackageAccess = PackageAccess.READ_WRITE;
+
+	/**
+	 * Package access.
+	 */
+	private PackageAccess packageAccess;
+
+	/**
+	 * Package parts collection.
+	 */
+	protected PackagePartCollection partList;
+
+	/**
+	 * Package relationships.
+	 */
+	protected PackageRelationshipCollection relationships;
+
+	/**
+	 * Part marshallers by content type.
+	 */
+	protected Hashtable<ContentType, PartMarshaller> partMarshallers;
+
+	/**
+	 * Default part marshaller.
+	 */
+	protected PartMarshaller defaultPartMarshaller;
+
+	/**
+	 * Part unmarshallers by content type.
+	 */
+	protected Hashtable<ContentType, PartUnmarshaller> partUnmarshallers;
+
+	/**
+	 * Core package properties.
+	 */
+	protected PackagePropertiesPart packageProperties;
+
+	/**
+	 * Manage parts content types of this package.
+	 */
+	protected ContentTypeManager contentTypeManager;
+
+	/**
+	 * Flag if a modification is done to the document.
+	 */
+	protected boolean isDirty = false;
+
+	/**
+	 * File path of this package.
+	 */
+	protected String originalPackagePath;
+
+	/**
+	 * Output stream for writing this package.
+	 */
+	protected OutputStream output;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param access
+	 *            Package access.
+	 */
+	protected Package(PackageAccess access) {
+		init();
+		this.packageAccess = access;
+	}
+
+	/**
+	 * Initialize the package instance.
+	 */
+	private void init() {
+		this.partMarshallers = new Hashtable<ContentType, PartMarshaller>(5);
+		this.partUnmarshallers = new Hashtable<ContentType, PartUnmarshaller>(2);
+
+		try {
+			// Add 'default' unmarshaller
+			this.partUnmarshallers.put(new ContentType(
+					ContentTypes.CORE_PROPERTIES_PART),
+					new PackagePropertiesUnmarshaller());
+
+			// Add default marshaller
+			this.defaultPartMarshaller = new DefaultMarshaller();
+			// TODO Delocalize specialized marshallers
+			this.partMarshallers.put(new ContentType(
+					ContentTypes.CORE_PROPERTIES_PART),
+					new ZipPackagePropertiesMarshaller());
+		} catch (InvalidFormatException e) {
+			// Should never happpen
+			throw new OpenXML4JRuntimeException(
+					"Package.init() : this exception should never happen, if you read this message please send a mail to the developers team.");
+		}
+	}
+
+	/**
+	 * Open a package with read/write permission.
+	 * 
+	 * @param path
+	 *            The document path.
+	 * @return A Package object, else <b>null</b>.
+	 * @throws InvalidFormatException
+	 *             If the specified file doesn't exist, and a parsing error
+	 *             occur.
+	 */
+	public static Package open(String path) throws InvalidFormatException {
+		return open(path, defaultPackageAccess);
+	}
+
+	/**
+	 * Open a package.
+	 * 
+	 * @param path
+	 *            The document path.
+	 * @param access
+	 *            Package access.
+	 * @return A Package object, else <b>null</b>.
+	 * @throws InvalidFormatException
+	 *             If the specified file doesn't exist, and a parsing error
+	 *             occur.
+	 */
+	public static Package open(String path, PackageAccess access)
+			throws InvalidFormatException {
+		if (path == null || "".equals(path.trim())
+				|| (new File(path).exists() && new File(path).isDirectory()))
+			throw new IllegalArgumentException("path");
+
+		Package pack = new ZipPackage(path, access);
+		if (pack.partList == null && access != PackageAccess.WRITE) {
+			pack.getParts();
+		}
+		pack.originalPackagePath = new File(path).getAbsolutePath();
+		return pack;
+	}
+
+	/**
+	 * Open a package.
+	 * 
+	 * Note - uses quite a bit more memory than {@link #open(String)}, which
+	 * doesn't need to hold the whole zip file in memory, and can take advantage
+	 * of native methods
+	 * 
+	 * @param in
+	 *            The InputStream to read the package from
+	 * @return A Package object
+	 */
+	public static Package open(InputStream in) throws InvalidFormatException,
+			IOException {
+		Package pack = new ZipPackage(in, PackageAccess.READ);
+		if (pack.partList == null) {
+			pack.getParts();
+		}
+		return pack;
+	}
+
+	/**
+	 * Opens a package if it exists, else it creates one.
+	 * 
+	 * @param file
+	 *            The file to open or to create.
+	 * @return A newly created package if the specified file does not exist,
+	 *         else the package extract from the file.
+	 * @throws InvalidFormatException
+	 *             Throws if the specified file exist and is not valid.
+	 */
+	public static Package openOrCreate(File file) throws InvalidFormatException {
+		Package retPackage = null;
+		if (file.exists()) {
+			retPackage = open(file.getAbsolutePath());
+		} else {
+			retPackage = create(file);
+		}
+		return retPackage;
+	}
+
+	/**
+	 * Creates a new package.
+	 * 
+	 * @param path
+	 *            Path of the document.
+	 * @return A newly created Package ready to use.
+	 */
+	public static Package create(String path) {
+		return create(new File(path));
+	}
+
+	/**
+	 * Creates a new package.
+	 * 
+	 * @param file
+	 *            Path of the document.
+	 * @return A newly created Package ready to use.
+	 */
+	public static Package create(File file) {
+		if (file == null || (file.exists() && file.isDirectory()))
+			throw new IllegalArgumentException("file");
+
+		if (file.exists()) {
+			throw new InvalidOperationException(
+					"This package (or file) already exists : use the open() method or delete the file.");
+		}
+
+		// Creates a new package
+		Package pkg = null;
+		pkg = new ZipPackage();
+		pkg.originalPackagePath = file.getAbsolutePath();
+
+		configurePackage(pkg);
+		return pkg;
+	}
+
+	public static Package create(OutputStream output) {
+		Package pkg = null;
+		pkg = new ZipPackage();
+		pkg.originalPackagePath = null;
+		pkg.output = output;
+
+		configurePackage(pkg);
+		return pkg;
+	}
+
+	/**
+	 * Configure the package.
+	 * 
+	 * @param pkg
+	 */
+	private static void configurePackage(Package pkg) {
+		try {
+			// Content type manager
+			pkg.contentTypeManager = new ZipContentTypeManager(null, pkg);
+			// Add default content types for .xml and .rels
+			pkg.contentTypeManager
+					.addContentType(
+							PackagingURIHelper
+									.createPartName(PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_URI),
+							ContentTypes.RELATIONSHIPS_PART);
+			pkg.contentTypeManager
+					.addContentType(PackagingURIHelper
+							.createPartName("/default.xml"),
+							ContentTypes.PLAIN_OLD_XML);
+
+			// Init some Package properties
+			pkg.packageProperties = new PackagePropertiesPart(pkg,
+					PackagingURIHelper.CORE_PROPERTIES_PART_NAME);
+			pkg.packageProperties.setCreatorProperty("Generated by OpenXML4J");
+			pkg.packageProperties.setCreatedProperty(new Nullable<Date>(
+					new Date()));
+		} catch (InvalidFormatException e) {
+			// Should never happen
+			throw new IllegalStateException(e);
+		}
+	}
+
+	/**
+	 * Flush the package : save all.
+	 * 
+	 * @see #close()
+	 */
+	public void flush() {
+		throwExceptionIfReadOnly();
+
+		if (this.packageProperties != null)
+			((PackagePropertiesPart) this.packageProperties).flush();
+
+		this.flushImpl();
+	}
+
+	/**
+	 * Close the package and save its content.
+	 * 
+	 * @throws IOException
+	 *             If an IO exception occur during the saving process.
+	 */
+	public void close() throws IOException {
+		if (this.packageAccess == PackageAccess.READ) {
+			logger
+					.warn("The close() method is intended to SAVE a package. This package is open in READ ONLY mode, use the revert() method instead !");
+			return;
+		}
+
+		// Save the content
+		ReentrantReadWriteLock l = new ReentrantReadWriteLock();
+		try {
+			l.writeLock().lock();
+			if (this.originalPackagePath != null
+					&& !"".equals(this.originalPackagePath.trim())) {
+				File targetFile = new File(this.originalPackagePath);
+				if (!targetFile.exists()
+						|| !(this.originalPackagePath
+								.equalsIgnoreCase(targetFile.getAbsolutePath()))) {
+					// Case of a package created from scratch
+					save(targetFile);
+				} else {
+					closeImpl();
+				}
+			} else if (this.output != null) {
+				save(this.output);
+			}
+		} finally {
+			l.writeLock().unlock();
+		}
+
+		// Clear
+		this.contentTypeManager.clearAll();
+
+		// Call the garbage collector
+		Runtime.getRuntime().gc();
+	}
+
+	/**
+	 * Close the package WITHOUT saving its content. Reinitialize this package
+	 * and cancel all changes done to it.
+	 */
+	public void revert() {
+		revertImpl();
+	}
+
+	/**
+	 * Add a thumbnail to the package. This method is provided to make easier
+	 * the addition of a thumbnail in a package. You can do the same work by
+	 * using the traditionnal relationship and part mechanism.
+	 * 
+	 * @param path
+	 *            The full path to the image file.
+	 */
+	public void addThumbnail(String path) throws IOException {
+		// Check parameter
+		if ("".equals(path))
+			throw new IllegalArgumentException("path");
+
+		// Get the filename from the path
+		String filename = path
+				.substring(path.lastIndexOf(File.separatorChar) + 1);
+
+		// Create the thumbnail part name
+		String contentType = ContentTypes
+				.getContentTypeFromFileExtension(filename);
+		PackagePartName thumbnailPartName = null;
+		try {
+			thumbnailPartName = PackagingURIHelper.createPartName("/docProps/"
+					+ filename);
+		} catch (InvalidFormatException e) {
+			try {
+				thumbnailPartName = PackagingURIHelper
+						.createPartName("/docProps/thumbnail"
+								+ path.substring(path.lastIndexOf(".") + 1));
+			} catch (InvalidFormatException e2) {
+				throw new InvalidOperationException(
+						"Can't add a thumbnail file named '" + filename + "'");
+			}
+		}
+
+		// Check if part already exist
+		if (this.getPart(thumbnailPartName) != null)
+			throw new InvalidOperationException(
+					"You already add a thumbnail named '" + filename + "'");
+
+		// Add the thumbnail part to this package.
+		PackagePart thumbnailPart = this.createPart(thumbnailPartName,
+				contentType, false);
+
+		// Add the relationship between the package and the thumbnail part
+		this.addRelationship(thumbnailPartName, TargetMode.INTERNAL,
+				PackageRelationshipTypes.THUMBNAIL);
+
+		// Copy file data to the newly created part
+		StreamHelper.copyStream(new FileInputStream(path), thumbnailPart
+				.getOutputStream());
+	}
+
+	/**
+	 * Throws an exception if the package access mode is in read only mode
+	 * (PackageAccess.Read).
+	 * 
+	 * @throws InvalidOperationException
+	 *             Throws if a writing operation is done on a read only package.
+	 * @see org.apache.poi.openxml4j.opc.PackageAccess
+	 */
+	void throwExceptionIfReadOnly() throws InvalidOperationException {
+		if (packageAccess == PackageAccess.READ)
+			throw new InvalidOperationException(
+					"Operation not allowed, document open in read only mode!");
+	}
+
+	/**
+	 * Throws an exception if the package access mode is in write only mode
+	 * (PackageAccess.Write). This method is call when other methods need write
+	 * right.
+	 * 
+	 * @throws InvalidOperationException
+	 *             Throws if a read operation is done on a write only package.
+	 * @see org.apache.poi.openxml4j.opc.PackageAccess
+	 */
+	void throwExceptionIfWriteOnly() throws InvalidOperationException {
+		if (packageAccess == PackageAccess.WRITE)
+			throw new InvalidOperationException(
+					"Operation not allowed, document open in write only mode!");
+	}
+
+	/**
+	 * Retrieves or creates if none exists, core package property part.
+	 * 
+	 * @return The PackageProperties part of this package.
+	 */
+	public PackageProperties getPackageProperties()
+			throws InvalidFormatException {
+		this.throwExceptionIfWriteOnly();
+		// If no properties part has been found then we create one
+		if (this.packageProperties == null) {
+			this.packageProperties = new PackagePropertiesPart(this,
+					PackagingURIHelper.CORE_PROPERTIES_PART_NAME);
+		}
+		return this.packageProperties;
+	}
+
+	/**
+	 * Retrieve a part identified by its name.
+	 * 
+	 * @param partName
+	 *            Part name of the part to retrieve.
+	 * @return The part with the specified name, else <code>null</code>.
+	 */
+	public PackagePart getPart(PackagePartName partName) {
+		throwExceptionIfWriteOnly();
+
+		if (partName == null)
+			throw new IllegalArgumentException("partName");
+
+		// If the partlist is null, then we parse the package.
+		if (partList == null) {
+			try {
+				getParts();
+			} catch (InvalidFormatException e) {
+				return null;
+			}
+		}
+		return getPartImpl(partName);
+	}
+
+	/**
+	 * Retrieve parts by content type.
+	 * 
+	 * @param contentType
+	 *            The content type criteria.
+	 * @return All part associated to the specified content type.
+	 */
+	public ArrayList<PackagePart> getPartsByContentType(String contentType) {
+		ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();
+		for (PackagePart part : partList.values()) {
+			if (part.getContentType().equals(contentType))
+				retArr.add(part);
+		}
+		return retArr;
+	}
+
+	/**
+	 * Retrieve parts by relationship type.
+	 * 
+	 * @param relationshipType
+	 *            Relationship type.
+	 * @return All parts which are the target of a relationship with the
+	 *         specified type, if the method can't retrieve relationships from
+	 *         the package, then return <code>null</code>.
+	 */
+	public ArrayList<PackagePart> getPartsByRelationshipType(
+			String relationshipType) {
+		if (relationshipType == null)
+			throw new IllegalArgumentException("relationshipType");
+		ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();
+		try {
+			for (PackageRelationship rel : getRelationshipsByType(relationshipType)) {
+				retArr.add(getPart(rel));
+			}
+		} catch (OpenXML4JException e) {
+			logger
+					.warn("Can't retrieve parts by relationship type: an exception has been thrown by getRelationshipsByType method");
+			return null;
+		}
+		return retArr;
+	}
+
+	/**
+	 * Get the target part from the specified relationship.
+	 * 
+	 * @param partRel
+	 *            The part relationship uses to retrieve the part.
+	 */
+	public PackagePart getPart(PackageRelationship partRel) {
+		PackagePart retPart = null;
+		ensureRelationships();
+		for (PackageRelationship rel : relationships) {
+			if (rel.getRelationshipType().equals(partRel.getRelationshipType())) {
+				try {
+					retPart = getPart(PackagingURIHelper.createPartName(rel
+							.getTargetURI()));
+				} catch (InvalidFormatException e) {
+					continue;
+				}
+				break;
+			}
+		}
+		return retPart;
+	}
+
+	/**
+	 * Load the parts of the archive if it has not been done yet The
+	 * relationships of each part are not loaded
+	 * 
+	 * @return All this package's parts.
+	 */
+	public ArrayList<PackagePart> getParts() throws InvalidFormatException {
+		throwExceptionIfWriteOnly();
+
+		// If the part list is null, we parse the package to retrieve all parts.
+		if (partList == null) {
+			/* Variables use to validate OPC Compliance */
+
+			// Ensure rule M4.1 -> A format consumer shall consider more than
+			// one core properties relationship for a package to be an error
+			boolean hasCorePropertiesPart = false;
+
+			PackagePart[] parts = this.getPartsImpl();
+			this.partList = new PackagePartCollection();
+			for (PackagePart part : parts) {
+				if (partList.containsKey(part.partName))
+					throw new InvalidFormatException(
+							"A part with the name '"
+									+ part.partName
+									+ "' already exist : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+
+				// Check OPC compliance rule M4.1
+				if (part.getContentType().equals(
+						ContentTypes.CORE_PROPERTIES_PART)) {
+					if (!hasCorePropertiesPart)
+						hasCorePropertiesPart = true;
+					else
+						throw new InvalidFormatException(
+								"OPC Compliance error [M4.1]: there is more than one core properties relationship in the package !");
+				}
+
+				PartUnmarshaller partUnmarshaller = partUnmarshallers
+						.get(part.contentType);
+
+				if (partUnmarshaller != null) {
+					UnmarshallContext context = new UnmarshallContext(this,
+							part.partName);
+					try {
+						PackagePart unmarshallPart = partUnmarshaller
+								.unmarshall(context, part.getInputStream());
+						partList.put(unmarshallPart.partName, unmarshallPart);
+
+						// Core properties case
+						if (unmarshallPart instanceof PackagePropertiesPart)
+							this.packageProperties = (PackagePropertiesPart) unmarshallPart;
+					} catch (IOException ioe) {
+						logger.warn("Unmarshall operation : IOException for "
+								+ part.partName);
+						continue;
+					} catch (InvalidOperationException invoe) {
+						throw new InvalidFormatException(invoe.getMessage());
+					}
+				} else {
+					try {
+						partList.put(part.partName, part);
+					} catch (InvalidOperationException e) {
+						throw new InvalidFormatException(e.getMessage());
+					}
+				}
+			}
+		}
+		return new ArrayList<PackagePart>(partList.values());
+	}
+
+	/**
+	 * Create and add a part, with the specified name and content type, to the
+	 * package.
+	 * 
+	 * @param partName
+	 *            Part name.
+	 * @param contentType
+	 *            Part content type.
+	 * @return The newly created part.
+	 * @throws InvalidFormatException
+	 *             If rule M1.12 is not verified : Packages shall not contain
+	 *             equivalent part names and package implementers shall neither
+	 *             create nor recognize packages with equivalent part names.
+	 * @see {@link#createPartImpl(URI, String)}
+	 */
+	public PackagePart createPart(PackagePartName partName, String contentType) {
+		return this.createPart(partName, contentType, true);
+	}
+
+	/**
+	 * Create and add a part, with the specified name and content type, to the
+	 * package. For general purpose, prefer the overload version of this method
+	 * without the 'loadRelationships' parameter.
+	 * 
+	 * @param partName
+	 *            Part name.
+	 * @param contentType
+	 *            Part content type.
+	 * @param loadRelationships
+	 *            Specify if the existing relationship part, if any, logically
+	 *            associated to the newly created part will be loaded.
+	 * @return The newly created part.
+	 * @throws InvalidFormatException
+	 *             If rule M1.12 is not verified : Packages shall not contain
+	 *             equivalent part names and package implementers shall neither
+	 *             create nor recognize packages with equivalent part names.
+	 * @see {@link#createPartImpl(URI, String)}
+	 */
+	PackagePart createPart(PackagePartName partName, String contentType,
+			boolean loadRelationships) {
+		throwExceptionIfReadOnly();
+		if (partName == null) {
+			throw new IllegalArgumentException("partName");
+		}
+
+		if (contentType == null || contentType == "") {
+			throw new IllegalArgumentException("contentType");
+		}
+
+		// Check if the specified part name already exists
+		if (partList.containsKey(partName)
+				&& !partList.get(partName).isDeleted()) {
+			throw new InvalidOperationException(
+					"A part with the name '"
+							+ partName.getName()
+							+ "' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+		}
+
+		/* Check OPC compliance */
+
+		// Rule [M4.1]: The format designer shall specify and the format
+		// producer
+		// shall create at most one core properties relationship for a package.
+		// A format consumer shall consider more than one core properties
+		// relationship for a package to be an error. If present, the
+		// relationship shall target the Core Properties part.
+		if (contentType == ContentTypes.CORE_PROPERTIES_PART) {
+			if (this.packageProperties != null)
+				throw new InvalidOperationException(
+						"OPC Compliance error [M4.1]: you try to add more than one core properties relationship in the package !");
+		}
+
+		/* End check OPC compliance */
+
+		PackagePart part = this.createPartImpl(partName, contentType,
+				loadRelationships);
+		this.contentTypeManager.addContentType(partName, contentType);
+		this.partList.put(partName, part);
+		this.isDirty = true;
+		return part;
+	}
+
+	/**
+	 * Add a part to the package.
+	 * 
+	 * @param partName
+	 *            Part name of the part to create.
+	 * @param contentType
+	 *            type associated with the file
+	 * @param content
+	 *            the contents to add. In order to have faster operation in
+	 *            document merge, the data are stored in memory not on a hard
+	 *            disk
+	 * 
+	 * @return The new part.
+	 * @see {@link #createPart(PackagePartName, String)}
+	 */
+	public PackagePart createPart(PackagePartName partName, String contentType,
+			ByteArrayOutputStream content) {
+		PackagePart addedPart = this.createPart(partName, contentType);
+		if (addedPart == null) {
+			return null;
+		}
+		// Extract the zip entry content to put it in the part content
+		if (content != null) {
+			try {
+				OutputStream partOutput = addedPart.getOutputStream();
+				if (partOutput == null) {
+					return null;
+				}
+
+				partOutput.write(content.toByteArray(), 0, content.size());
+				partOutput.close();
+
+			} catch (IOException ioe) {
+				return null;
+			}
+		} else {
+			return null;
+		}
+		return addedPart;
+	}
+
+	/**
+	 * Add the specified part to the package. If a part already exists in the
+	 * package with the same name as the one specified, then we replace the old
+	 * part by the specified part.
+	 * 
+	 * @param part
+	 *            The part to add (or replace).
+	 * @return The part added to the package, the same as the one specified.
+	 * @throws InvalidFormatException
+	 *             If rule M1.12 is not verified : Packages shall not contain
+	 *             equivalent part names and package implementers shall neither
+	 *             create nor recognize packages with equivalent part names.
+	 */
+	protected PackagePart addPackagePart(PackagePart part) {
+		throwExceptionIfReadOnly();
+		if (part == null) {
+			throw new IllegalArgumentException("part");
+		}
+
+		if (partList.containsKey(part.partName)) {
+			if (!partList.get(part.partName).isDeleted()) {
+				throw new InvalidOperationException(
+						"A part with the name '"
+								+ part.partName.getName()
+								+ "' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+			} else {
+				// If the specified partis flagged as deleted, we make it
+				// available
+				part.setDeleted(false);
+				// and delete the old part to replace it thereafeter
+				this.partList.remove(part.partName);
+			}
+		}
+		this.partList.put(part.partName, part);
+		this.isDirty = true;
+		return part;
+	}
+
+	/**
+	 * Remove the specified part in this package. If this part is relationship
+	 * part, then delete all relationships in the source part.
+	 * 
+	 * @param part
+	 *            The part to remove. If <code>null</code>, skip the action.
+	 * @see #removePart(PackagePartName)
+	 */
+	public void removePart(PackagePart part) {
+		if (part != null) {
+			removePart(part.getPartName());
+		}
+	}
+
+	/**
+	 * Remove a part in this package. If this part is relationship part, then
+	 * delete all relationships in the source part.
+	 * 
+	 * @param partName
+	 *            The part name of the part to remove.
+	 */
+	public void removePart(PackagePartName partName) {
+		throwExceptionIfReadOnly();
+		if (partName == null || !this.containPart(partName))
+			throw new IllegalArgumentException("partName");
+
+		// Delete the specified part from the package.
+		if (this.partList.containsKey(partName)) {
+			this.partList.get(partName).setDeleted(true);
+			this.removePartImpl(partName);
+			this.partList.remove(partName);
+		} else {
+			this.removePartImpl(partName);
+		}
+
+		// Delete content type
+		this.contentTypeManager.removeContentType(partName);
+
+		// If this part is a relationship part, then delete all relationships of
+		// the source part.
+		if (partName.isRelationshipPartURI()) {
+			URI sourceURI = PackagingURIHelper
+					.getSourcePartUriFromRelationshipPartUri(partName.getURI());
+			PackagePartName sourcePartName;
+			try {
+				sourcePartName = PackagingURIHelper.createPartName(sourceURI);
+			} catch (InvalidFormatException e) {
+				logger
+						.error("Part name URI '"
+								+ sourceURI
+								+ "' is not valid ! This message is not intended to be displayed !");
+				return;
+			}
+			if (sourcePartName.getURI().equals(
+					PackagingURIHelper.PACKAGE_ROOT_URI)) {
+				clearRelationships();
+			} else if (containPart(sourcePartName)) {
+				PackagePart part = getPart(sourcePartName);
+				if (part != null)
+					part.clearRelationships();
+			}
+		}
+
+		this.isDirty = true;
+	}
+
+	/**
+	 * Remove a part from this package as well as its relationship part, if one
+	 * exists, and all parts listed in the relationship part. Be aware that this
+	 * do not delete relationships which target the specified part.
+	 * 
+	 * @param partName
+	 *            The name of the part to delete.
+	 * @throws InvalidFormatException
+	 *             Throws if the associated relationship part of the specified
+	 *             part is not valid.
+	 */
+	public void removePartRecursive(PackagePartName partName)
+			throws InvalidFormatException {
+		// Retrieves relationship part, if one exists
+		PackagePart relPart = this.partList.get(PackagingURIHelper
+				.getRelationshipPartName(partName));
+		// Retrieves PackagePart object from the package
+		PackagePart partToRemove = this.partList.get(partName);
+
+		if (relPart != null) {
+			PackageRelationshipCollection partRels = new PackageRelationshipCollection(
+					partToRemove);
+			for (PackageRelationship rel : partRels) {
+				PackagePartName partNameToRemove = PackagingURIHelper
+						.createPartName(PackagingURIHelper.resolvePartUri(rel
+								.getSourceURI(), rel.getTargetURI()));
+				removePart(partNameToRemove);
+			}
+
+			// Finally delete its relationship part if one exists
+			this.removePart(relPart.partName);
+		}
+
+		// Delete the specified part
+		this.removePart(partToRemove.partName);
+	}
+
+	/**
+	 * Delete the part with the specified name and its associated relationships
+	 * part if one exists. Prefer the use of this method to delete a part in the
+	 * package, compare to the remove() methods that don't remove associated
+	 * relationships part.
+	 * 
+	 * @param partName
+	 *            Name of the part to delete
+	 */
+	public void deletePart(PackagePartName partName) {
+		if (partName == null)
+			throw new IllegalArgumentException("partName");
+
+		// Remove the part
+		this.removePart(partName);
+		// Remove the relationships part
+		this.removePart(PackagingURIHelper.getRelationshipPartName(partName));
+	}
+
+	/**
+	 * Delete the part with the specified name and all part listed in its
+	 * associated relationships part if one exists. This process is recursively
+	 * apply to all parts in the relationships part of the specified part.
+	 * Prefer the use of this method to delete a part in the package, compare to
+	 * the remove() methods that don't remove associated relationships part.
+	 * 
+	 * @param partName
+	 *            Name of the part to delete
+	 */
+	public void deletePartRecursive(PackagePartName partName) {
+		if (partName == null || !this.containPart(partName))
+			throw new IllegalArgumentException("partName");
+
+		PackagePart partToDelete = this.getPart(partName);
+		// Remove the part
+		this.removePart(partName);
+		// Remove all relationship parts associated
+		try {
+			for (PackageRelationship relationship : partToDelete
+					.getRelationships()) {
+				PackagePartName targetPartName = PackagingURIHelper
+						.createPartName(PackagingURIHelper.resolvePartUri(
+								partName.getURI(), relationship.getTargetURI()));
+				this.deletePartRecursive(targetPartName);
+			}
+		} catch (InvalidFormatException e) {
+			logger.warn("An exception occurs while deleting part '"
+					+ partName.getName()
+					+ "'. Some parts may remain in the package. - "
+					+ e.getMessage());
+			return;
+		}
+		// Remove the relationships part
+		PackagePartName relationshipPartName = PackagingURIHelper
+				.getRelationshipPartName(partName);
+		if (relationshipPartName != null && containPart(relationshipPartName))
+			this.removePart(relationshipPartName);
+	}
+
+	/**
+	 * Check if a part already exists in this package from its name.
+	 * 
+	 * @param partName
+	 *            Part name to check.
+	 * @return <i>true</i> if the part is logically added to this package, else
+	 *         <i>false</i>.
+	 */
+	public boolean containPart(PackagePartName partName) {
+		return (this.getPart(partName) != null);
+	}
+
+	/**
+	 * Add a relationship to the package (except relationships part).
+	 * 
+	 * Check rule M4.1 : The format designer shall specify and the format
+	 * producer shall create at most one core properties relationship for a
+	 * package. A format consumer shall consider more than one core properties
+	 * relationship for a package to be an error. If present, the relationship
+	 * shall target the Core Properties part.
+	 * 
+	 * Check rule M1.25: The Relationships part shall not have relationships to
+	 * any other part. Package implementers shall enforce this requirement upon
+	 * the attempt to create such a relationship and shall treat any such
+	 * relationship as invalid.
+	 * 
+	 * @param targetPartName
+	 *            Target part name.
+	 * @param targetMode
+	 *            Target mode, either Internal or External.
+	 * @param relationshipType
+	 *            Relationship type.
+	 * @param relID
+	 *            ID of the relationship.
+	 * @see PackageRelationshipTypes
+	 */
+	public PackageRelationship addRelationship(PackagePartName targetPartName,
+			TargetMode targetMode, String relationshipType, String relID) {
+		/* Check OPC compliance */
+
+		// Check rule M4.1 : The format designer shall specify and the format
+		// producer
+		// shall create at most one core properties relationship for a package.
+		// A format consumer shall consider more than one core properties
+		// relationship for a package to be an error. If present, the
+		// relationship shall target the Core Properties part.
+		if (relationshipType.equals(PackageRelationshipTypes.CORE_PROPERTIES)
+				&& this.packageProperties != null)
+			throw new InvalidOperationException(
+					"OPC Compliance error [M4.1]: can't add another core properties part ! Use the built-in package method instead.");
+
+		/*
+		 * Check rule M1.25: The Relationships part shall not have relationships
+		 * to any other part. Package implementers shall enforce this
+		 * requirement upon the attempt to create such a relationship and shall
+		 * treat any such relationship as invalid.
+		 */
+		if (targetPartName.isRelationshipPartURI()) {
+			throw new InvalidOperationException(
+					"Rule M1.25: The Relationships part shall not have relationships to any other part.");
+		}
+
+		/* End OPC compliance */
+
+		ensureRelationships();
+		PackageRelationship retRel = relationships.addRelationship(
+				targetPartName.getURI(), targetMode, relationshipType, relID);
+		this.isDirty = true;
+		return retRel;
+	}
+
+	/**
+	 * Add a package relationship.
+	 * 
+	 * @param targetPartName
+	 *            Target part name.
+	 * @param targetMode
+	 *            Target mode, either Internal or External.
+	 * @param relationshipType
+	 *            Relationship type.
+	 * @see PackageRelationshipTypes
+	 */
+	public PackageRelationship addRelationship(PackagePartName targetPartName,
+			TargetMode targetMode, String relationshipType) {
+		return this.addRelationship(targetPartName, targetMode,
+				relationshipType, null);
+	}
+
+	/**
+	 * Adds an external relationship to a part (except relationships part).
+	 * 
+	 * The targets of external relationships are not subject to the same
+	 * validity checks that internal ones are, as the contents is potentially
+	 * any file, URL or similar.
+	 * 
+	 * @param target
+	 *            External target of the relationship
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @return The newly created and added relationship
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+	 *      java.lang.String)
+	 */
+	public PackageRelationship addExternalRelationship(String target,
+			String relationshipType) {
+		return addExternalRelationship(target, relationshipType, null);
+	}
+
+	/**
+	 * Adds an external relationship to a part (except relationships part).
+	 * 
+	 * The targets of external relationships are not subject to the same
+	 * validity checks that internal ones are, as the contents is potentially
+	 * any file, URL or similar.
+	 * 
+	 * @param target
+	 *            External target of the relationship
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @param id
+	 *            Relationship unique id.
+	 * @return The newly created and added relationship
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+	 *      java.lang.String)
+	 */
+	public PackageRelationship addExternalRelationship(String target,
+			String relationshipType, String id) {
+		if (target == null) {
+			throw new IllegalArgumentException("target");
+		}
+		if (relationshipType == null) {
+			throw new IllegalArgumentException("relationshipType");
+		}
+
+		URI targetURI;
+		try {
+			targetURI = new URI(target);
+		} catch (URISyntaxException e) {
+			throw new IllegalArgumentException("Invalid target - " + e);
+		}
+
+		ensureRelationships();
+		PackageRelationship retRel = relationships.addRelationship(targetURI,
+				TargetMode.EXTERNAL, relationshipType, id);
+		this.isDirty = true;
+		return retRel;
+	}
+
+	/**
+	 * Delete a relationship from this package.
+	 * 
+	 * @param id
+	 *            Id of the relationship to delete.
+	 */
+	public void removeRelationship(String id) {
+		if (relationships != null) {
+			relationships.removeRelationship(id);
+			this.isDirty = true;
+		}
+	}
+
+	/**
+	 * Retrieves all package relationships.
+	 * 
+	 * @return All package relationships of this package.
+	 * @throws OpenXML4JException
+	 * @see {@link #getRelationshipsHelper(String)}
+	 */
+	public PackageRelationshipCollection getRelationships()
+			throws OpenXML4JException {
+		return getRelationshipsHelper(null);
+	}
+
+	/**
+	 * Retrives all relationships with the specified type.
+	 * 
+	 * @param relationshipType
+	 *            The filter specifying the relationship type.
+	 * @return All relationships with the specified relationship type.
+	 * @throws OpenXML4JException
+	 */
+	public PackageRelationshipCollection getRelationshipsByType(
+			String relationshipType) throws IllegalArgumentException,
+			OpenXML4JException {
+		throwExceptionIfWriteOnly();
+		if (relationshipType == null) {
+			throw new IllegalArgumentException("relationshipType");
+		}
+		return getRelationshipsHelper(relationshipType);
+	}
+
+	/**
+	 * Retrieves all relationships with specified id (normally just ine because
+	 * a relationship id is supposed to be unique).
+	 * 
+	 * @param id
+	 *            Id of the wanted relationship.
+	 * @throws OpenXML4JException
+	 */
+	private PackageRelationshipCollection getRelationshipsHelper(String id)
+			throws OpenXML4JException {
+		throwExceptionIfWriteOnly();
+		ensureRelationships();
+		return this.relationships.getRelationships(id);
+	}
+
+	/**
+	 * Clear package relationships.
+	 */
+	public void clearRelationships() {
+		if (relationships != null) {
+			relationships.clear();
+			this.isDirty = true;
+		}
+	}
+
+	/**
+	 * Ensure that the relationships collection is not null.
+	 */
+	public void ensureRelationships() {
+		if (this.relationships == null) {
+			try {
+				this.relationships = new PackageRelationshipCollection(this);
+			} catch (InvalidFormatException e) {
+				this.relationships = new PackageRelationshipCollection();
+			}
+		}
+	}
+
+	/**
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationship(java.lang.String)
+	 */
+	public PackageRelationship getRelationship(String id) {
+		return this.relationships.getRelationshipByID(id);
+	}
+
+	/**
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#hasRelationships()
+	 */
+	public boolean hasRelationships() {
+		return (relationships.size() > 0);
+	}
+
+	/**
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#isRelationshipExists(org.apache.poi.openxml4j.opc.PackageRelationship)
+	 */
+	@SuppressWarnings("finally")
+	public boolean isRelationshipExists(PackageRelationship rel) {
+		try {
+			for (PackageRelationship r : this.getRelationships()) {
+				if (r == rel)
+					return true;
+			}
+		} finally {
+			return false;
+		}
+	}
+
+	/**
+	 * Add a marshaller.
+	 * 
+	 * @param contentType
+	 *            The content type to bind to the specified marshaller.
+	 * @param marshaller
+	 *            The marshaller to register with the specified content type.
+	 */
+	public void addMarshaller(String contentType, PartMarshaller marshaller) {
+		try {
+			partMarshallers.put(new ContentType(contentType), marshaller);
+		} catch (InvalidFormatException e) {
+			logger.warn("The specified content type is not valid: '"
+					+ e.getMessage() + "'. The marshaller will not be added !");
+		}
+	}
+
+	/**
+	 * Add an unmarshaller.
+	 * 
+	 * @param contentType
+	 *            The content type to bind to the specified unmarshaller.
+	 * @param unmarshaller
+	 *            The unmarshaller to register with the specified content type.
+	 */
+	public void addUnmarshaller(String contentType,
+			PartUnmarshaller unmarshaller) {
+		try {
+			partUnmarshallers.put(new ContentType(contentType), unmarshaller);
+		} catch (InvalidFormatException e) {
+			logger.warn("The specified content type is not valid: '"
+					+ e.getMessage()
+					+ "'. The unmarshaller will not be added !");
+		}
+	}
+
+	/**
+	 * Remove a marshaller by its content type.
+	 * 
+	 * @param contentType
+	 *            The content type associated with the marshaller to remove.
+	 */
+	public void removeMarshaller(String contentType) {
+		partMarshallers.remove(contentType);
+	}
+
+	/**
+	 * Remove an unmarshaller by its content type.
+	 * 
+	 * @param contentType
+	 *            The content type associated with the unmarshaller to remove.
+	 */
+	public void removeUnmarshaller(String contentType) {
+		partUnmarshallers.remove(contentType);
+	}
+
+	/* Accesseurs */
+
+	/**
+	 * Get the package access mode.
+	 * 
+	 * @return the packageAccess The current package access.
+	 */
+	public PackageAccess getPackageAccess() {
+		return packageAccess;
+	}
+
+	/**
+	 * Validates the package compliance with the OPC specifications.
+	 * 
+	 * @return <b>true</b> if the package is valid else <b>false</b>
+	 */
+	public boolean validatePackage(Package pkg) throws InvalidFormatException {
+		throw new InvalidOperationException("Not implemented yet !!!");
+	}
+
+	/**
+	 * Save the document in the specified file.
+	 * 
+	 * @param targetFile
+	 *            Destination file.
+	 * @throws IOException
+	 *             Throws if an IO exception occur.
+	 * @see #save(OutputStream)
+	 */
+	public void save(File targetFile) throws IOException {
+		if (targetFile == null)
+			throw new IllegalArgumentException("targetFile");
+
+		this.throwExceptionIfReadOnly();
+		FileOutputStream fos = null;
+		try {
+			fos = new FileOutputStream(targetFile);
+		} catch (FileNotFoundException e) {
+			throw new IOException(e.getLocalizedMessage());
+		}
+		this.save(fos);
+	}
+
+	/**
+	 * Save the document in the specified output stream.
+	 * 
+	 * @param outputStream
+	 *            The stream to save the package.
+	 * @see #saveImpl(OutputStream)
+	 */
+	public void save(OutputStream outputStream) throws IOException {
+		throwExceptionIfReadOnly();
+		this.saveImpl(outputStream);
+	}
+
+	/**
+	 * Core method to create a package part. This method must be implemented by
+	 * the subclass.
+	 * 
+	 * @param partName
+	 *            URI of the part to create.
+	 * @param contentType
+	 *            Content type of the part to create.
+	 * @return The newly created package part.
+	 */
+	protected abstract PackagePart createPartImpl(PackagePartName partName,
+			String contentType, boolean loadRelationships);
+
+	/**
+	 * Core method to delete a package part. This method must be implemented by
+	 * the subclass.
+	 * 
+	 * @param partName
+	 *            The URI of the part to delete.
+	 */
+	protected abstract void removePartImpl(PackagePartName partName);
+
+	/**
+	 * Flush the package but not save.
+	 */
+	protected abstract void flushImpl();
+
+	/**
+	 * Close the package and cause a save of the package.
+	 * 
+	 */
+	protected abstract void closeImpl() throws IOException;
+
+	/**
+	 * Close the package without saving the document. Discard all changes made
+	 * to this package.
+	 */
+	protected abstract void revertImpl();
+
+	/**
+	 * Save the package into the specified output stream.
+	 * 
+	 * @param outputStream
+	 *            The output stream use to save this package.
+	 */
+	protected abstract void saveImpl(OutputStream outputStream)
+			throws IOException;
+
+	/**
+	 * Get the package part mapped to the specified URI.
+	 * 
+	 * @param partName
+	 *            The URI of the part to retrieve.
+	 * @return The package part located by the specified URI, else <b>null</b>.
+	 */
+	protected abstract PackagePart getPartImpl(PackagePartName partName);
+
+	/**
+	 * Get all parts link to the package.
+	 * 
+	 * @return A list of the part owned by the package.
+	 */
+	protected abstract PackagePart[] getPartsImpl()
+			throws InvalidFormatException;
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java
------------------------------------------------------------------------------
    svn:executable = *

Added: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java?rev=738842&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java Thu Jan 29 12:44:31 2009
@@ -0,0 +1,33 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Specifies package access.
+ * 
+ * @author Julien Chable
+ * @version 1.0
+ */
+public enum PackageAccess {
+    /** Read only. Write not authorized. */
+    READ,
+    /** Write only. Read not authorized. */
+    WRITE,
+    /** Read and Write mode. */
+    READ_WRITE
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java
------------------------------------------------------------------------------
    svn:executable = *

Added: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java?rev=738842&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java Thu Jan 29 12:44:31 2009
@@ -0,0 +1,52 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Open Packaging Convention namespaces URI.
+ * 
+ * @author Julien Chable
+ * @version 1.0
+ */
+public interface PackageNamespaces {
+
+	/**
+	 * Content Types.
+	 */
+	public static final String CONTENT_TYPES = "http://schemas.openxmlformats.org/package/2006/content-types";
+	
+	/**
+	 * Core Properties.
+	 */
+	public static final String CORE_PROPERTIES = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
+	
+	/**
+	 * Digital Signatures.
+	 */
+	public static final String DIGITAL_SIGNATURE = "http://schemas.openxmlformats.org/package/2006/digital-signature";
+	
+	/**
+	 * Relationships.
+	 */
+	public static final String RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships";
+	
+	/**
+	 * Markup Compatibility.
+	 */
+	public static final String MARKUP_COMPATIBILITY = "http://schemas.openxmlformats.org/markup-compatibility/2006";
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java
------------------------------------------------------------------------------
    svn:executable = *

Added: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java?rev=738842&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java Thu Jan 29 12:44:31 2009
@@ -0,0 +1,654 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
+
+/**
+ * Provides a base class for parts stored in a Package.
+ * 
+ * @author Julien Chable
+ * @version 0.9
+ */
+public abstract class PackagePart implements RelationshipSource {
+
+	/**
+	 * This part's container.
+	 */
+	protected Package container;
+
+	/**
+	 * The part name. (required by the specification [M1.1])
+	 */
+	protected PackagePartName partName;
+
+	/**
+	 * The type of content of this part. (required by the specification [M1.2])
+	 */
+	protected ContentType contentType;
+
+	/**
+	 * Flag to know if this part is a relationship.
+	 */
+	private boolean isRelationshipPart;
+
+	/**
+	 * Flag to know if this part has been logically deleted.
+	 */
+	private boolean isDeleted;
+
+	/**
+	 * This part's relationships.
+	 */
+	private PackageRelationshipCollection relationships;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param pack
+	 *            Parent package.
+	 * @param partName
+	 *            The part name, relative to the parent Package root.
+	 * @param contentType
+	 *            The content type.
+	 * @throws InvalidFormatException
+	 *             If the specified URI is not valid.
+	 */
+	protected PackagePart(Package pack, PackagePartName partName,
+			ContentType contentType) throws InvalidFormatException {
+		this(pack, partName, contentType, true);
+	}
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param pack
+	 *            Parent package.
+	 * @param partName
+	 *            The part name, relative to the parent Package root.
+	 * @param contentType
+	 *            The content type.
+	 * @param loadRelationships
+	 *            Specify if the relationships will be loaded
+	 * @throws InvalidFormatException
+	 *             If the specified URI is not valid.
+	 */
+	protected PackagePart(Package pack, PackagePartName partName,
+			ContentType contentType, boolean loadRelationships)
+			throws InvalidFormatException {
+		this.partName = partName;
+		this.contentType = contentType;
+		this.container = (ZipPackage) pack;
+
+		// Check if this part is a relationship part
+		isRelationshipPart = this.partName.isRelationshipPartURI();
+
+		// Load relationships if any
+		if (loadRelationships)
+			loadRelationships();
+	}
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param pack
+	 *            Parent package.
+	 * @param partName
+	 *            The part name, relative to the parent Package root.
+	 * @param contentType
+	 *            The Multipurpose Internet Mail Extensions (MIME) content type
+	 *            of the part's data stream.
+	 */
+	public PackagePart(Package pack, PackagePartName partName,
+			String contentType) throws InvalidFormatException {
+		this(pack, partName, new ContentType(contentType));
+	}
+
+	/**
+	 * Adds an external relationship to a part (except relationships part).
+	 * 
+	 * The targets of external relationships are not subject to the same
+	 * validity checks that internal ones are, as the contents is potentially
+	 * any file, URL or similar.
+	 * 
+	 * @param target
+	 *            External target of the relationship
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @return The newly created and added relationship
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+	 *      java.lang.String)
+	 */
+	public PackageRelationship addExternalRelationship(String target,
+			String relationshipType) {
+		return addExternalRelationship(target, relationshipType, null);
+	}
+
+	/**
+	 * Adds an external relationship to a part (except relationships part).
+	 * 
+	 * The targets of external relationships are not subject to the same
+	 * validity checks that internal ones are, as the contents is potentially
+	 * any file, URL or similar.
+	 * 
+	 * @param target
+	 *            External target of the relationship
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @param id
+	 *            Relationship unique id.
+	 * @return The newly created and added relationship
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+	 *      java.lang.String)
+	 */
+	public PackageRelationship addExternalRelationship(String target,
+			String relationshipType, String id) {
+		if (target == null) {
+			throw new IllegalArgumentException("target");
+		}
+		if (relationshipType == null) {
+			throw new IllegalArgumentException("relationshipType");
+		}
+
+		if (relationships == null) {
+			relationships = new PackageRelationshipCollection();
+		}
+
+		URI targetURI;
+		try {
+			targetURI = new URI(target);
+		} catch (URISyntaxException e) {
+			throw new IllegalArgumentException("Invalid target - " + e);
+		}
+
+		return relationships.addRelationship(targetURI, TargetMode.EXTERNAL,
+				relationshipType, id);
+	}
+
+	/**
+	 * Add a relationship to a part (except relationships part).
+	 * 
+	 * @param targetPartName
+	 *            Name of the target part. This one must be relative to the
+	 *            source root directory of the part.
+	 * @param targetMode
+	 *            Mode [Internal|External].
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @return The newly created and added relationship
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+	 *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String)
+	 */
+	public PackageRelationship addRelationship(PackagePartName targetPartName,
+			TargetMode targetMode, String relationshipType) {
+		return addRelationship(targetPartName, targetMode, relationshipType,
+				null);
+	}
+
+	/**
+	 * Add a relationship to a part (except relationships part).
+	 * 
+	 * Check rule M1.25: The Relationships part shall not have relationships to
+	 * any other part. Package implementers shall enforce this requirement upon
+	 * the attempt to create such a relationship and shall treat any such
+	 * relationship as invalid.
+	 * 
+	 * @param targetPartName
+	 *            Name of the target part. This one must be relative to the
+	 *            source root directory of the part.
+	 * @param targetMode
+	 *            Mode [Internal|External].
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @param id
+	 *            Relationship unique id.
+	 * @return The newly created and added relationship
+	 * 
+	 * @throws InvalidFormatException
+	 *             If the URI point to a relationship part URI.
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+	 *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String, java.lang.String)
+	 */
+	public PackageRelationship addRelationship(PackagePartName targetPartName,
+			TargetMode targetMode, String relationshipType, String id) {
+		container.throwExceptionIfReadOnly();
+
+		if (targetPartName == null) {
+			throw new IllegalArgumentException("targetPartName");
+		}
+		if (targetMode == null) {
+			throw new IllegalArgumentException("targetMode");
+		}
+		if (relationshipType == null) {
+			throw new IllegalArgumentException("relationshipType");
+		}
+
+		if (this.isRelationshipPart || targetPartName.isRelationshipPartURI()) {
+			throw new InvalidOperationException(
+					"Rule M1.25: The Relationships part shall not have relationships to any other part.");
+		}
+
+		if (relationships == null) {
+			relationships = new PackageRelationshipCollection();
+		}
+
+		return relationships.addRelationship(targetPartName.getURI(),
+				targetMode, relationshipType, id);
+	}
+
+	/**
+	 * Add a relationship to a part (except relationships part).
+	 * 
+	 * @param targetURI
+	 *            URI the target part. Must be relative to the source root
+	 *            directory of the part.
+	 * @param targetMode
+	 *            Mode [Internal|External].
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @return The newly created and added relationship
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+	 *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String)
+	 */
+	public PackageRelationship addRelationship(URI targetURI,
+			TargetMode targetMode, String relationshipType) {
+		return addRelationship(targetURI, targetMode, relationshipType, null);
+	}
+
+	/**
+	 * Add a relationship to a part (except relationships part).
+	 * 
+	 * Check rule M1.25: The Relationships part shall not have relationships to
+	 * any other part. Package implementers shall enforce this requirement upon
+	 * the attempt to create such a relationship and shall treat any such
+	 * relationship as invalid.
+	 * 
+	 * @param targetURI
+	 *            URI of the target part. Must be relative to the source root
+	 *            directory of the part.
+	 * @param targetMode
+	 *            Mode [Internal|External].
+	 * @param relationshipType
+	 *            Type of relationship.
+	 * @param id
+	 *            Relationship unique id.
+	 * @return The newly created and added relationship
+	 * 
+	 * @throws InvalidFormatException
+	 *             If the URI point to a relationship part URI.
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+	 *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String, java.lang.String)
+	 */
+	public PackageRelationship addRelationship(URI targetURI,
+			TargetMode targetMode, String relationshipType, String id) {
+		container.throwExceptionIfReadOnly();
+
+		if (targetURI == null) {
+			throw new IllegalArgumentException("targetPartName");
+		}
+		if (targetMode == null) {
+			throw new IllegalArgumentException("targetMode");
+		}
+		if (relationshipType == null) {
+			throw new IllegalArgumentException("relationshipType");
+		}
+
+		// Try to retrieve the target part
+
+		if (this.isRelationshipPart
+				|| PackagingURIHelper.isRelationshipPartURI(targetURI)) {
+			throw new InvalidOperationException(
+					"Rule M1.25: The Relationships part shall not have relationships to any other part.");
+		}
+
+		if (relationships == null) {
+			relationships = new PackageRelationshipCollection();
+		}
+
+		return relationships.addRelationship(targetURI,
+				targetMode, relationshipType, id);
+	}
+
+	/**
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#clearRelationships()
+	 */
+	public void clearRelationships() {
+		if (relationships != null) {
+			relationships.clear();
+		}
+	}
+
+	/**
+	 * Delete the relationship specified by its id.
+	 * 
+	 * @param id
+	 *            The ID identified the part to delete.
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#removeRelationship(java.lang.String)
+	 */
+	public void removeRelationship(String id) {
+		this.container.throwExceptionIfReadOnly();
+		if (this.relationships != null)
+			this.relationships.removeRelationship(id);
+	}
+
+	/**
+	 * Retrieve all the relationships attached to this part.
+	 * 
+	 * @return This part's relationships.
+	 * @throws OpenXML4JException
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationships()
+	 */
+	public PackageRelationshipCollection getRelationships()
+			throws InvalidFormatException {
+		return getRelationshipsCore(null);
+	}
+
+	/**
+	 * Retrieves a package relationship from its id.
+	 * 
+	 * @param id
+	 *            ID of the package relationship to retrieve.
+	 * @return The package relationship
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationship(java.lang.String)
+	 */
+	public PackageRelationship getRelationship(String id) {
+		return this.relationships.getRelationshipByID(id);
+	}
+
+	/**
+	 * Retrieve all relationships attached to this part which have the specified
+	 * type.
+	 * 
+	 * @param relationshipType
+	 *            Relationship type filter.
+	 * @return All relationships from this part that have the specified type.
+	 * @throws InvalidFormatException
+	 *             If an error occurs while parsing the part.
+	 * @throws InvalidOperationException
+	 *             If the package is open in write only mode.
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationshipsByType(java.lang.String)
+	 */
+	public PackageRelationshipCollection getRelationshipsByType(
+			String relationshipType) throws InvalidFormatException {
+		container.throwExceptionIfWriteOnly();
+
+		return getRelationshipsCore(relationshipType);
+	}
+
+	/**
+	 * Implementation of the getRelationships method().
+	 * 
+	 * @param filter
+	 *            Relationship type filter. If <i>null</i> then the filter is
+	 *            disabled and return all the relationships.
+	 * @return All relationships from this part that have the specified type.
+	 * @throws InvalidFormatException
+	 *             Throws if an error occurs during parsing the relationships
+	 *             part.
+	 * @throws InvalidOperationException
+	 *             Throws if the package is open en write only mode.
+	 * @see #getRelationshipsByType(String)
+	 */
+	private PackageRelationshipCollection getRelationshipsCore(String filter)
+			throws InvalidFormatException {
+		this.container.throwExceptionIfWriteOnly();
+		if (relationships == null) {
+			this.throwExceptionIfRelationship();
+			relationships = new PackageRelationshipCollection(this);
+		}
+		return new PackageRelationshipCollection(relationships, filter);
+	}
+
+	/**
+	 * Knows if the part have any relationships.
+	 * 
+	 * @return <b>true</b> if the part have at least one relationship else
+	 *         <b>false</b>.
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#hasRelationships()
+	 */
+	public boolean hasRelationships() {
+		return (!this.isRelationshipPart && (relationships != null && relationships
+				.size() > 0));
+	}
+
+	/**
+	 * Checks if the specified relationship is part of this package part.
+	 * 
+	 * @param rel
+	 *            The relationship to check.
+	 * @return <b>true</b> if the specified relationship exists in this part,
+	 *         else returns <b>false</b>
+	 * @see org.apache.poi.openxml4j.opc.RelationshipSource#isRelationshipExists(org.apache.poi.openxml4j.opc.PackageRelationship)
+	 */
+	@SuppressWarnings("finally")
+	public boolean isRelationshipExists(PackageRelationship rel) {
+		try {
+			for (PackageRelationship r : this.getRelationships()) {
+				if (r == rel)
+					return true;
+			}
+		} finally {
+			return false;
+		}
+	}
+
+	/**
+	 * Get the input stream of this part to read its content.
+	 * 
+	 * @return The input stream of the content of this part, else
+	 *         <code>null</code>.
+	 */
+	public InputStream getInputStream() throws IOException {
+		InputStream inStream = this.getInputStreamImpl();
+		if (inStream == null) {
+			throw new IOException("Can't obtain the input stream from "
+					+ partName.getName());
+		} else
+			return inStream;
+	}
+
+	/**
+	 * Get the output stream of this part. If the part is originally embedded in
+	 * Zip package, it'll be transform intot a <i>MemoryPackagePart</i> in
+	 * order to write inside (the standard Java API doesn't allow to write in
+	 * the file)
+	 * 
+	 * @see org.apache.poi.openxml4j.opc.internal.MemoryPackagePart
+	 */
+	public OutputStream getOutputStream() {
+		OutputStream outStream;
+		// If this part is a zip package part (read only by design) we convert
+		// this part into a MemoryPackagePart instance for write purpose.
+		if (this instanceof ZipPackagePart) {
+			// Delete logically this part
+			this.container.removePart(this.partName);
+
+			// Create a memory part
+			PackagePart part = container.createPart(this.partName,
+					this.contentType.toString(), false);
+			part.relationships = this.relationships;
+			if (part == null) {
+				throw new InvalidOperationException(
+						"Can't create a temporary part !");
+			}
+			outStream = part.getOutputStreamImpl();
+		} else {
+			outStream = this.getOutputStreamImpl();
+		}
+		return outStream;
+	}
+
+	/**
+	 * Throws an exception if this package part is a relationship part.
+	 * 
+	 * @throws InvalidOperationException
+	 *             If this part is a relationship part.
+	 */
+	private void throwExceptionIfRelationship()
+			throws InvalidOperationException {
+		if (this.isRelationshipPart)
+			throw new InvalidOperationException(
+					"Can do this operation on a relationship part !");
+	}
+
+	/**
+	 * Ensure the package relationships collection instance is built.
+	 * 
+	 * @throws InvalidFormatException
+	 *             Throws if
+	 */
+	private void loadRelationships() throws InvalidFormatException {
+		if (this.relationships == null && !this.isRelationshipPart) {
+			this.throwExceptionIfRelationship();
+			relationships = new PackageRelationshipCollection(this);
+		}
+	}
+
+	/*
+	 * Accessors
+	 */
+
+	/**
+	 * @return the uri
+	 */
+	public PackagePartName getPartName() {
+		return partName;
+	}
+
+	/**
+	 * @return the contentType
+	 */
+	public String getContentType() {
+		return contentType.toString();
+	}
+
+	/**
+	 * Set the content type.
+	 * 
+	 * @param contentType
+	 *            the contentType to set
+	 * 
+	 * @throws InvalidFormatException
+	 *             Throws if the content type is not valid.
+	 * @throws InvalidOperationException
+	 *             Throws if you try to change the content type whereas this
+	 *             part is already attached to a package.
+	 */
+	public void setContentType(String contentType)
+			throws InvalidFormatException {
+		if (container == null)
+			this.contentType = new ContentType(contentType);
+		else
+			throw new InvalidOperationException(
+					"You can't change the content type of a part.");
+	}
+
+	public Package getPackage() {
+		return container;
+	}
+
+	/**
+	 * @return
+	 */
+	public boolean isRelationshipPart() {
+		return this.isRelationshipPart;
+	}
+
+	/**
+	 * @return
+	 */
+	public boolean isDeleted() {
+		return isDeleted;
+	}
+
+	/**
+	 * @param isDeleted
+	 *            the isDeleted to set
+	 */
+	public void setDeleted(boolean isDeleted) {
+		this.isDeleted = isDeleted;
+	}
+
+	@Override
+	public String toString() {
+		return "Name: " + this.partName + " - Content Type: "
+				+ this.contentType.toString();
+	}
+
+	/*-------------- Abstract methods ------------- */
+
+	/**
+	 * Abtract method that get the input stream of this part.
+	 * 
+	 * @exception IOException
+	 *                Throws if an IO Exception occur in the implementation
+	 *                method.
+	 */
+	protected abstract InputStream getInputStreamImpl() throws IOException;
+
+	/**
+	 * Abstract method that get the output stream of this part.
+	 */
+	protected abstract OutputStream getOutputStreamImpl();
+
+	/**
+	 * Save the content of this part and the associated relationships part (if
+	 * this part own at least one relationship) into the specified output
+	 * stream.
+	 * 
+	 * @param zos
+	 *            Output stream to save this part.
+	 * @throws OpenXML4JException
+	 *             If any exception occur.
+	 */
+	public abstract boolean save(OutputStream zos) throws OpenXML4JException;
+
+	/**
+	 * Load the content of this part.
+	 * 
+	 * @param ios
+	 *            The input stream of the content to load.
+	 * @return <b>true</b> if the content has been successfully loaded, else
+	 *         <b>false</b>.
+	 * @throws InvalidFormatException
+	 *             Throws if the content format is invalid.
+	 */
+	public abstract boolean load(InputStream ios) throws InvalidFormatException;
+
+	/**
+	 * Close this part : flush this part, close the input stream and output
+	 * stream. After this method call, the part must be available for packaging.
+	 */
+	public abstract void close();
+
+	/**
+	 * Flush the content of this part. If the input stream and/or output stream
+	 * as in a waiting state to read or write, the must to empty their
+	 * respective buffer.
+	 */
+	public abstract void flush();
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
------------------------------------------------------------------------------
    svn:executable = *

Added: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java?rev=738842&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java Thu Jan 29 12:44:31 2009
@@ -0,0 +1,81 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+
+/**
+ * A package part collection.
+ * 
+ * @author Julien Chable
+ * @version 0.1
+ */
+public final class PackagePartCollection extends
+		TreeMap<PackagePartName, PackagePart> {
+
+	private static final long serialVersionUID = 2515031135957635515L;
+
+	/**
+	 * Arraylist use to store this collection part names as string for rule
+	 * M1.11 optimized checking.
+	 */
+	private ArrayList<String> registerPartNameStr = new ArrayList<String>();
+
+	@Override
+	public Object clone() {
+		return super.clone();
+	}
+
+	/**
+	 * Check rule [M1.11]: a package implementer shall neither create nor
+	 * recognize a part with a part name derived from another part name by
+	 * appending segments to it.
+	 * 
+	 * @exception InvalidOperationException
+	 *                Throws if you try to add a part with a name derived from
+	 *                another part name.
+	 */
+	@Override
+	public PackagePart put(PackagePartName partName, PackagePart part) {
+		String[] segments = partName.getURI().toASCIIString().split(
+				PackagingURIHelper.FORWARD_SLASH_STRING);
+		StringBuffer concatSeg = new StringBuffer();
+		for (String seg : segments) {
+			if (!seg.equals(""))
+				concatSeg.append(PackagingURIHelper.FORWARD_SLASH_CHAR);
+			concatSeg.append(seg);
+			if (this.registerPartNameStr.contains(concatSeg.toString())) {
+				throw new InvalidOperationException(
+						"You can't add a part with a part name derived from another part ! [M1.11]");
+			}
+		}
+		this.registerPartNameStr.add(partName.getName());
+		return super.put(partName, part);
+	}
+
+	@Override
+	public PackagePart remove(Object key) {
+		if (key instanceof PackagePartName) {
+			this.registerPartNameStr.remove(((PackagePartName) key).getName());
+		}
+		return super.remove(key);
+	}
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java
------------------------------------------------------------------------------
    svn:executable = *



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org