]> source.dussan.org Git - poi.git/commitdiff
import OpenXML4j codebase
authorYegor Kozlov <yegor@apache.org>
Thu, 29 Jan 2009 12:44:31 +0000 (12:44 +0000)
committerYegor Kozlov <yegor@apache.org>
Thu, 29 Jan 2009 12:44:31 +0000 (12:44 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@738842 13f79535-47bb-0310-9956-ffa450edef68

144 files changed:
build.xml
legal/NOTICE
maven/openxml4j.pom [deleted file]
maven/poi-ooxml.pom
src/documentation/content/xdocs/book.xml
src/documentation/content/xdocs/oxml4j/book.xml [new file with mode: 0755]
src/documentation/content/xdocs/oxml4j/index.xml [new file with mode: 0755]
src/documentation/content/xdocs/spreadsheet/how-to.xml
src/examples/src/org/apache/poi/xssf/eventusermodel/examples/FromHowTo.java
src/ooxml/java/org/apache/poi/POIXMLDocument.java
src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
src/ooxml/java/org/apache/poi/POIXMLFactory.java
src/ooxml/java/org/apache/poi/POIXMLProperties.java
src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java
src/ooxml/java/org/apache/poi/POIXMLTextExtractor.java
src/ooxml/java/org/apache/poi/dev/OOXMLLister.java
src/ooxml/java/org/apache/poi/extractor/ExtractorFactory.java
src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidOperationException.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JException.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JRuntimeException.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/CertificateEmbeddingOption.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/CompressionOption.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/Configuration.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/ContentTypes.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/EncryptionOption.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartName.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackageProperties.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationship.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/PackagingURIHelper.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/RelationshipSource.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/TargetMode.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentType.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentTypeManager.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/FileHelper.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePart.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePartOutputStream.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartMarshaller.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartUnmarshaller.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/DefaultMarshaller.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/PackagePropertiesMarshaller.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalCertificatePart.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalSignatureOriginPart.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/UnmarshallContext.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignature.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignatureManager.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/util/Nullable.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java [new file with mode: 0755]
src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java
src/ooxml/java/org/apache/poi/util/PackageHelper.java
src/ooxml/java/org/apache/poi/xslf/XSLFSlideShow.java
src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java
src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java
src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExcelExtractor.java
src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java
src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java
src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDialogsheet.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShapeGroup.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java
src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFactory.java
src/ooxml/testcases/org/apache/poi/TestEmbeded.java
src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java
src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
src/ooxml/testcases/org/apache/poi/openxml4j/TestCore.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/AllTests.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestFileHelper.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestListParts.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagePartName.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageThumbnail.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagingURIHelper.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestRelationships.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/AllTests.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PackageModel.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PartName.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/AllTests.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docx [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java
src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java
src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java
src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java
src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java
src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java

index 458bf30a231a7c21021378a3275fe2f72cd67849..c04db3d0e09040f026760fa37039657511d412df 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -124,8 +124,6 @@ under the License.
   <property name="ooxml.testokfile" location="build/ooxml-testokfile.txt"/>
 
   <!-- The following jars are downloaded by the fetch-ooxml-jars task --> 
-  <property name="ooxml.openxml4j.jar" location="${ooxml.lib}/openxml4j-1.0-beta.jar"/>
-  <property name="ooxml.openxml4j.url" value="http://mirrors.ibiblio.org/pub/mirrors/maven2/org/apache/poi/openxml4j/1.0-beta/openxml4j-1.0-beta.jar"/>
   <property name="ooxml.dom4j.jar" location="${ooxml.lib}/dom4j-1.6.1.jar"/>
   <property name="ooxml.dom4j.url" value="${repository}/dom4j/jars/dom4j-1.6.1.jar"/>
   <property name="ooxml.xmlbeans.jar" location="${ooxml.lib}/xmlbeans-2.3.0.jar"/>
@@ -144,8 +142,6 @@ under the License.
 
   <property name="maven.ooxml.xsds.version.id" value="1.0"/>
   <property name="maven.ooxml.xsds.jar" value="ooxml-schemas-${maven.ooxml.xsds.version.id}.jar"/>
-  <property name="maven.openxml4j.version.id" value="1.0-beta"/>
-  <property name="maven.openxml4j.jar" value="openxml4j-${maven.openxml4j.version.id}.jar"/>
   
   <property name="build.site" location="build/tmp/site/build/site"/>
   <property name="build.site.src" location="build/tmp/site"/>
@@ -354,7 +350,6 @@ under the License.
                     <available file="${ooxml.xmlbeans.jar}"/>
                     <available file="${ooxml.jsr173.jar}"/>
                     <available file="${ooxml.schemas.jar}"/>
-                    <available file="${ooxml.openxml4j.jar}"/>
                 </and>
                 <isset property="disconnected"/>
             </or>
@@ -377,10 +372,6 @@ under the License.
         <param name="sourcefile" value="${ooxml.schemas.url}"/>
         <param name="destfile" value="${ooxml.schemas.jar}"/>
       </antcall>
-      <antcall target="downloadfile">
-        <param name="sourcefile" value="${ooxml.openxml4j.url}"/>
-        <param name="destfile" value="${ooxml.openxml4j.jar}"/>
-      </antcall>
     </target>
 
          <target name="check-ooxml-xsds">
@@ -524,7 +515,6 @@ under the License.
   </target>
 
   <target name="compile-ooxml" depends="init, compile-main, compile-scratchpad">
-       <!-- openxml4j requires java 1.5, so so must we, for now -->
     <javac target="1.5" source="1.5"
       destdir="${ooxml.output.dir}" debug="on" srcdir="${ooxml.src}">
       <classpath refid="ooxml.classpath"/>
@@ -805,6 +795,9 @@ under the License.
             <sysproperty key="HSLF.testdata.path" file="${scratchpad.src.test}/org/apache/poi/hslf/data"/>
             <sysproperty key="HDGF.testdata.path" file="${scratchpad.src.test}/org/apache/poi/hdgf/data"/>
             <sysproperty key="OOXML.testdata.path" file="${ooxml.src.test}/org/apache/poi/ooxml/data"/> 
+            <sysproperty key="openxml4j.compliance.input" file="${ooxml.src.test}/org/apache/poi/openxml4j/opc/compliance/input"/> 
+            <sysproperty key="openxml4j.testdata.input" file="${ooxml.src.test}/org/apache/poi/openxml4j/opc/INPUT"/> 
+            <sysproperty key="openxml4j.testdata.output" file="${ooxml.src.test}/org/apache/poi/openxml4j/opc/OUTPUT"/> 
             <sysproperty key="java.awt.headless" value="true"/>
             <formatter type="plain"/>
             <formatter type="xml"/>
@@ -812,6 +805,7 @@ under the License.
                 <fileset dir="${ooxml.src.test}">
                     <include name="**/Test*.java"/>
                     <exclude name="**/All*Tests.java"/>
+                    <exclude name="**/TestCore.java"/> <!--non-junit class from OpenXML4j-->
                 </fileset>
             </batchtest>
         </junit>
@@ -1129,16 +1123,7 @@ FORREST_HOME environment variable!</echo>
       </copy>
        </target>
 
-  <target name="maven-ooxml-dependencies" description="Builds the POM files for OpenXml4J and compiled XmlBeans generated from the Ecma supplied xsds">
-    <!-- OpenXml4J -->
-    <copy file="${ooxml.jar6.dir}" tofile="${mavendist.ooxml.dir}/org.openxml4j/jars/${maven.openxml4j.jar}" />
-    <copy file="maven/openxml4j.pom" tofile="${mavendist.ooxml.dir}/org.openxml4j/poms/openxml4j-${maven.openxml4j.version.id}.pom">
-                       <filterchain>
-                               <replacetokens>
-                                       <token key="VERSION" value="${maven.openxml4j.version.id}" />
-                               </replacetokens>
-                       </filterchain>
-               </copy>
+  <target name="maven-ooxml-dependencies" description="Builds the POM files for the compiled XmlBeans generated from the Ecma supplied xsds">
 
     <!-- ooxml-schemas -->
     <copy file="${ooxml.xsds.jar}" tofile="${mavendist.ooxml.dir}/org.apache.poi/jars/${maven.ooxml.xsds.jar}" />
index 5db2ba8589dbc35cdfca9e3533336943fae4d0c2..4ff9390f6c498d339758b947f4233f08027185e4 100644 (file)
@@ -17,6 +17,3 @@ own licensing:
       Apache Licence Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0
  * DOM4J - http://www.dom4j.org/
       BSD Licence - http://www.dom4j.org/license.html
- * OpenXml4J - http://www.openxml4j.org/
-      BSD Licence or Apache Licence Version 2.0 - 
-         http://www.openxml4j.org/Licensing/Default.html
diff --git a/maven/openxml4j.pom b/maven/openxml4j.pom
deleted file mode 100755 (executable)
index 5379a73..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0"?>
-<!--
-
-   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.
-
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.openxml4j</groupId>
-  <artifactId>openxml4j</artifactId>
-  <version>@VERSION@</version>
-  <packaging>jar</packaging>
-  <name>OpenXML4J</name>
-  <url>http://openxml4j.org/</url>
-  <description>Office Open XML File Format library for Java</description>
-
-  <mailingLists>
-    <mailingList>
-      <name>OpenXML4J Users List</name>
-      <archive>http://sourceforge.net/mailarchive/forum.php?forum_name=openxml4j-users</archive>
-    </mailingList>
-    <mailingList>
-      <name>OpenXML4J Developer List</name>
-      <archive>http://sourceforge.net/mailarchive/forum.php?forum_name=openxml4j-devs</archive>
-    </mailingList>
-  </mailingLists>
-
-  <licenses>
-    <license>
-      <name>The Apache Software License, Version 2.0</name>
-      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
-      <distribution>repo</distribution>
-    </license>
-  </licenses>
-
-  <organization>
-    <name>OpenXML4J</name>
-    <url>http://www.openxml4j.org/</url>
-  </organization>
-
-  <dependencies>
-    <dependency>
-        <groupId>dom4j</groupId>
-        <artifactId>dom4j</artifactId>
-        <version>1.6.1</version>
-    </dependency>
-    <dependency>
-        <groupId>log4j</groupId>
-        <artifactId>log4j</artifactId>
-        <version>1.2.8</version>
-    </dependency>
-  </dependencies>
-</project>
index b3e1c0412069ecee2a13a27ba89521060a0f194d..52c1583df1ed82eda951ae8cea12bec20acc55b9 100755 (executable)
 
   <dependencies>
     <dependency>
-      <groupId>commons-logging</groupId>
-      <artifactId>commons-logging</artifactId>
-      <version>1.1</version>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>log4j</groupId>
-      <artifactId>log4j</artifactId>
-      <version>1.2.13</version>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.poi</groupId>
-      <artifactId>openxml4j</artifactId>
-      <version>1.0-beta</version>
+       <groupId>org.apache.poi</groupId>
+       <artifactId>poi</artifactId>
+       <version>@VERSION@</version>
     </dependency>
     <dependency>
       <groupId>org.apache.poi</groupId>
       <artifactId>ooxml-schemas</artifactId>
       <version>1.0</version>
     </dependency>
+    <dependency>
+        <groupId>dom4j</groupId>
+        <artifactId>dom4j</artifactId>
+        <version>1.6.1</version>
+    </dependency>
   </dependencies>
 </project>
index d9bc0e91df1ba7c75532c1e8f86e70867533cba6..ec3206526c4be765fcb720beeaa1ff4a52576453 100644 (file)
@@ -42,7 +42,8 @@
         <menu-item label="HSMF" href="hsmf/index.html"/>
         <menu-item label="HDGF" href="hdgf/index.html"/>
         <menu-item label="HPBF" href="hpbf/index.html"/>
-               <menu-item label="POI-Ruby" href="poi-ruby.html"/>
+        <menu-item label="OpenXML4J" href="oxml4j/index.html"/>
+                   <menu-item label="POI-Ruby" href="poi-ruby.html"/>
         <menu-item label="POI-Utils" href="utils/index.html"/>
         <menu-item label="Text Extraction" href="text-extraction.html"/>
         <menu-item label="Download" href="ext:download"/>
diff --git a/src/documentation/content/xdocs/oxml4j/book.xml b/src/documentation/content/xdocs/oxml4j/book.xml
new file mode 100755 (executable)
index 0000000..bf5af2f
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>\r
+<!--\r
+   ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+   ====================================================================\r
+-->\r
+<!DOCTYPE book PUBLIC "-//APACHE//DTD Cocoon Documentation Book V1.0//EN" "../dtd/book-cocoon-v10.dtd">\r
+\r
+<book software="POI Project"\r
+    title="OpenXML4J"\r
+    copyright="@year@ POI Project">\r
+\r
+    <menu label="Apache POI">\r
+        <menu-item label="Top" href="../index.html"/>\r
+    </menu>\r
+\r
+    <menu label="OpenXML4J">\r
+        <menu-item label="Overview" href="index.html"/>\r
+       </menu>\r
+       \r
+</book>\r
diff --git a/src/documentation/content/xdocs/oxml4j/index.xml b/src/documentation/content/xdocs/oxml4j/index.xml
new file mode 100755 (executable)
index 0000000..25ea1b0
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+   ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+   ====================================================================\r
+-->\r
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "../dtd/document-v11.dtd">\r
+\r
+<document>\r
+    <header>\r
+        <title>POI-OpenXML4J - Java API To Access Office Open XML documents</title>\r
+        <subtitle>Overview</subtitle>\r
+    </header>\r
+\r
+    <body>\r
+        <section>\r
+          <title>Overview</title>\r
+          <p>OpenXML4J is the POI Project's pure Java implementation of the Open Packaging Conventions (OPC) defined in \r
+            <link href="http://www.ecma-international.org/publications/standards/Ecma-376.htm">ECMA-376</link>.</p>\r
+          <p>Every OpenXML file comprises a collection of byte streams called parts, combined into a container called a package. \r
+            POI OpenXML4J provides a physical implementation of the OPC that uses the Zip file format.</p>\r
+        </section>\r
+        <section>\r
+          <title>History</title>\r
+          <p>OpenXML4J was originally developed by <link href="http://openxml4j.org/">http://openxml4j.org/</link> and contributed to POI in 2008. \r
+            Thanks to the support and guidance of Julien Chable</p>\r
+        </section>\r
+    </body>\r
+</document>\r
index d21be707e215ee4d5e731be0ed33a76c76451a2d..fcf88f69fe3018b8d048cb954fca53e022c1fc57 100644 (file)
@@ -509,7 +509,7 @@ import java.util.Iterator;
 
 import org.apache.poi.xssf.eventusermodel.XSSFReader;
 import org.apache.poi.xssf.model.SharedStringsTable;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.Package;
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
index e4fe3fe16986af3527c7345e2562c85d13842f1e..88668093df3ac1180f148b29b2eb72c7f3233b4d 100644 (file)
@@ -22,7 +22,7 @@ import java.util.Iterator;
 import org.apache.poi.xssf.eventusermodel.XSSFReader;
 import org.apache.poi.xssf.model.SharedStringsTable;
 import org.apache.poi.xssf.usermodel.XSSFRichTextString;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.Package;
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.InputSource;
index ab2d04c72e7dfb760a7b88ab54d24e547169feaf..ef333d86dffde4d2db979c9f26a7c8495c1b86d5 100644 (file)
@@ -23,10 +23,10 @@ import org.apache.poi.poifs.common.POIFSConstants;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.PackageHelper;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.InvalidFormatException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.*;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.*;
+import org.apache.poi.openxml4j.opc.Package;
 
 public abstract class POIXMLDocument extends POIXMLDocumentPart{
 
index d529bedf04bc5fdfad121bc370cbf17a2568a591..f01cae8ce41b0828093de8a631f80b62a80e0715 100755 (executable)
@@ -23,15 +23,15 @@ import java.util.List;
 import org.apache.xmlbeans.XmlOptions;\r
 import org.apache.poi.util.POILogger;\r
 import org.apache.poi.util.POILogFactory;\r
-import org.openxml4j.exceptions.OpenXML4JException;\r
-import org.openxml4j.opc.*;\r
-import org.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.*;\r
+import org.apache.poi.openxml4j.opc.Package;\r
 \r
 /**\r
  * Represents an entry of a OOXML package.\r
  *\r
  * <p>\r
- * Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.openxml4j.opc.PackagePart}.\r
+ * Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.apache.poi.openxml4j.opc.PackagePart}.\r
  * </p>\r
  *\r
  * @author Yegor Kozlov\r
index 6341b20842493b32f9448c0da04d1fc334f91dce..f10cf0ca01f1da83422a81cec7039bf070c738e6 100755 (executable)
@@ -16,8 +16,8 @@
 ==================================================================== */\r
 package org.apache.poi;\r
 \r
-import org.openxml4j.opc.PackageRelationship;\r
-import org.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
 \r
 \r
 /**\r
index 894f2f800d32d7c0c0d1545293f4fe8aa8c89901..3d862be8886afbaaeb84e32804e5655bcd704b05 100644 (file)
@@ -19,10 +19,10 @@ package org.apache.poi;
 import java.io.IOException;
 
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackageRelationshipCollection;
-import org.openxml4j.opc.internal.PackagePropertiesPart;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
 
 /**
  * Wrapper around the two different kinds of OOXML properties 
index 320c6e143dc155a9e5761f6413c4da8fd4fbe309..97c9e2d6951d7e025223758eca65f8b74cf6d7e1 100644 (file)
@@ -19,8 +19,8 @@ package org.apache.poi;
 import java.io.IOException;
 
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.internal.PackagePropertiesPart;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
 import org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty;
 
 /**
index c6a99436b5201fdf064433556ad2fedbf3bcbbbe..25310fff3afbc594ba7badb6b6cca9b4fbab0231 100644 (file)
@@ -20,7 +20,7 @@ import java.io.IOException;
 
 import org.apache.poi.POIXMLProperties.*;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
 
 public abstract class POIXMLTextExtractor extends POITextExtractor {
        /** The POIXMLDocument that's open */
index 7084b38f3384ec9196f80c5ec1ba971ebdcc7c89..a55bc2632bdc1e2d65d431d8d7c2f86c6ace821f 100644 (file)
@@ -22,11 +22,11 @@ import java.io.InputStream;
 import java.io.PrintStream;
 import java.util.ArrayList;
 
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackageAccess;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackageAccess;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
 
 /**
  * Prints out the contents of a OOXML container.
index 406a3145e971dda7cb8f5d99315832dd99f4c8b5..7640db845ff0203a88b8d609bf195fdc618c4826 100644 (file)
@@ -41,15 +41,14 @@ import org.apache.poi.xslf.XSLFSlideShow;
 import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor;
 import org.apache.poi.xssf.extractor.XSSFExcelExtractor;
 import org.apache.poi.xssf.usermodel.XSSFRelation;
-import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.apache.poi.xwpf.usermodel.XWPFRelation;
 import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.InvalidFormatException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
 
 /**
  * Figures out the correct POITextExtractor for your supplied
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java
new file mode 100755 (executable)
index 0000000..689f8f3
--- /dev/null
@@ -0,0 +1,27 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== \r
+ */\r
+\r
+package org.apache.poi.openxml4j.exceptions;\r
+\r
+@SuppressWarnings("serial")\r
+public class InvalidFormatException extends OpenXML4JException{\r
+\r
+       public InvalidFormatException(String message){\r
+               super(message);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidOperationException.java b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidOperationException.java
new file mode 100755 (executable)
index 0000000..9e45ccf
--- /dev/null
@@ -0,0 +1,32 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.exceptions;\r
+\r
+/**\r
+ * Throw when an invalid operation is done.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+@SuppressWarnings("serial")\r
+public class InvalidOperationException extends OpenXML4JRuntimeException{\r
+\r
+       public InvalidOperationException(String message){\r
+               super(message);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JException.java b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JException.java
new file mode 100755 (executable)
index 0000000..d2fa0e1
--- /dev/null
@@ -0,0 +1,34 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.exceptions;\r
+\r
+/**\r
+ * Global exception throws when a critical error occurs. (this exception is not\r
+ * set as Runtime in order to force user to manage the exception in a\r
+ * try/catch).\r
+ * \r
+ * @author CDubettier, Julien Chable\r
+ * @version 1.0\r
+ */\r
+@SuppressWarnings("serial")\r
+public class OpenXML4JException extends Exception {\r
+\r
+       public OpenXML4JException(String msg) {\r
+               super(msg);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JRuntimeException.java b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JRuntimeException.java
new file mode 100755 (executable)
index 0000000..c68d85e
--- /dev/null
@@ -0,0 +1,34 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.exceptions;\r
+\r
+/**\r
+ * Global exception throws when a critical error occurs (this exception is\r
+ * set as Runtime in order not to force the user to manage the exception in a\r
+ * try/catch).\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+@SuppressWarnings("serial")\r
+public class OpenXML4JRuntimeException extends RuntimeException {\r
+\r
+       public OpenXML4JRuntimeException(String msg) {\r
+               super(msg);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/CertificateEmbeddingOption.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/CertificateEmbeddingOption.java
new file mode 100755 (executable)
index 0000000..8b946e6
--- /dev/null
@@ -0,0 +1,33 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+/**\r
+ * Specifies the location where the X.509 certificate that is used in signing is stored.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public enum CertificateEmbeddingOption {\r
+       /** The certificate is embedded in its own PackagePart. */\r
+       IN_CERTIFICATE_PART,\r
+       /** The certificate is embedded in the SignaturePart that is created for the signature being added. */\r
+       IN_SIGNATURE_PART,\r
+       /** The certificate in not embedded in the package. */\r
+       NOT_EMBEDDED\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/CompressionOption.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/CompressionOption.java
new file mode 100755 (executable)
index 0000000..b6c5b30
--- /dev/null
@@ -0,0 +1,47 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.util.zip.Deflater;\r
+\r
+/**\r
+ * Specifies the compression level for content that is stored in a PackagePart.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public enum CompressionOption {\r
+       /** Compression is optimized for performance. */\r
+       FAST(Deflater.BEST_SPEED),\r
+       /** Compression is optimized for size. */\r
+       MAXIMUM(Deflater.BEST_COMPRESSION),\r
+       /** Compression is optimized for a balance between size and performance. */\r
+       NORMAL(Deflater.DEFAULT_COMPRESSION),\r
+       /** Compression is turned off. */\r
+       NOT_COMPRESSED(Deflater.NO_COMPRESSION);\r
+\r
+       private final int value;\r
+\r
+       CompressionOption(int value) {\r
+               this.value = value;\r
+       }\r
+\r
+       public int value() {\r
+               return this.value;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/Configuration.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/Configuration.java
new file mode 100755 (executable)
index 0000000..72241b3
--- /dev/null
@@ -0,0 +1,43 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.File;\r
+\r
+/**\r
+ * Storage class for configuration storage parameters.\r
+ * TODO xml syntax checking is no longer done with DOM4j parser -> remove the schema or do it ?\r
+ * \r
+ * @author CDubettier, Julen Chable\r
+ * @version 1.0\r
+ */\r
+public class Configuration {\r
+       // TODO configuration by default. should be clearly stated that it should be\r
+       // changed to match installation path\r
+       // as schemas dir is needed in runtime\r
+       static private String pathForXmlSchema = System.getProperty("user.dir")\r
+                       + File.separator + "src" + File.separator + "schemas";\r
+\r
+       public static String getPathForXmlSchema() {\r
+               return pathForXmlSchema;\r
+       }\r
+\r
+       public static void setPathForXmlSchema(String pathForXmlSchema) {\r
+               Configuration.pathForXmlSchema = pathForXmlSchema;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/ContentTypes.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/ContentTypes.java
new file mode 100755 (executable)
index 0000000..b05d790
--- /dev/null
@@ -0,0 +1,129 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+/**\r
+ * Open Packaging Convention content types (see Annex F : Standard Namespaces\r
+ * and Content Types).\r
+ * \r
+ * @author CDubettier define some constants, Julien Chable\r
+ * @version 0.1\r
+ */\r
+public class ContentTypes {\r
+\r
+       /*\r
+        * Open Packaging Convention (Annex F : Standard Namespaces and Content\r
+        * Types)\r
+        */\r
+\r
+       /**\r
+        * Core Properties part.\r
+        */\r
+       public static final String CORE_PROPERTIES_PART = "application/vnd.openxmlformats-package.core-properties+xml";\r
+\r
+       /**\r
+        * Digital Signature Certificate part.\r
+        */\r
+       public static final String DIGITAL_SIGNATURE_CERTIFICATE_PART = "application/vnd.openxmlformats-package.digital-signature-certificate";\r
+\r
+       /**\r
+        * Digital Signature Origin part.\r
+        */\r
+       public static final String DIGITAL_SIGNATURE_ORIGIN_PART = "application/vnd.openxmlformats-package.digital-signature-origin";\r
+\r
+       /**\r
+        * Digital Signature XML Signature part.\r
+        */\r
+       public static final String DIGITAL_SIGNATURE_XML_SIGNATURE_PART = "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml";\r
+\r
+       /**\r
+        * Relationships part.\r
+        */\r
+       public static final String RELATIONSHIPS_PART = "application/vnd.openxmlformats-package.relationships+xml";\r
+\r
+       /**\r
+        * Custom XML part.\r
+        */\r
+       public static final String CUSTOM_XML_PART = "application/vnd.openxmlformats-officedocument.customXmlProperties+xml";\r
+\r
+       /**\r
+        * Plain old xml. Note - OOXML uses application/xml, and not text/xml!\r
+        */\r
+       public static final String PLAIN_OLD_XML = "application/xml";\r
+\r
+       public static final String IMAGE_JPEG = "image/jpeg";\r
+\r
+       public static final String EXTENSION_JPG_1 = "jpg";\r
+\r
+       public static final String EXTENSION_JPG_2 = "jpeg";\r
+\r
+       // image/png ISO/IEC 15948:2003 http://www.libpng.org/pub/png/spec/\r
+       public static final String IMAGE_PNG = "image/png";\r
+\r
+       public static final String EXTENSION_PNG = "png";\r
+\r
+       // image/gif http://www.w3.org/Graphics/GIF/spec-gif89a.txt\r
+       public static final String IMAGE_GIF = "image/gif";\r
+\r
+       public static final String EXTENSION_GIF = "gif";\r
+\r
+       /**\r
+        * TIFF image format.\r
+        * \r
+        * @see http://partners.adobe.com/public/developer/tiff/index.html#spec\r
+        */\r
+       public static final String IMAGE_TIFF = "image/tiff";\r
+\r
+       public static final String EXTENSION_TIFF = "tiff";\r
+\r
+       /**\r
+        * Pict image format.\r
+        * \r
+        * @see http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-2.html\r
+        */\r
+       public static final String IMAGE_PICT = "image/pict";\r
+\r
+       public static final String EXTENSION_PICT = "tiff";\r
+\r
+       /**\r
+        * XML file.\r
+        */\r
+       public static final String XML = "text/xml";\r
+\r
+       public static final String EXTENSION_XML = "xml";\r
+\r
+       public static String getContentTypeFromFileExtension(String filename) {\r
+               String extension = filename.substring(filename.lastIndexOf(".") + 1)\r
+                               .toLowerCase();\r
+               if (extension.equals(EXTENSION_JPG_1)\r
+                               || extension.equals(EXTENSION_JPG_2))\r
+                       return IMAGE_JPEG;\r
+               else if (extension.equals(EXTENSION_GIF))\r
+                       return IMAGE_GIF;\r
+               else if (extension.equals(EXTENSION_PICT))\r
+                       return IMAGE_PICT;\r
+               else if (extension.equals(EXTENSION_PNG))\r
+                       return IMAGE_PNG;\r
+               else if (extension.equals(EXTENSION_TIFF))\r
+                       return IMAGE_TIFF;\r
+               else if (extension.equals(EXTENSION_XML))\r
+                       return XML;\r
+               else\r
+                       return null;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/EncryptionOption.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/EncryptionOption.java
new file mode 100755 (executable)
index 0000000..0e15332
--- /dev/null
@@ -0,0 +1,29 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+/**\r
+ * Specifies the encryption option for parts in a Package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public enum EncryptionOption {\r
+    /** No encryption. */\r
+    NONE\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java
new file mode 100755 (executable)
index 0000000..663ab26
--- /dev/null
@@ -0,0 +1,1396 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.Hashtable;\r
+import java.util.concurrent.locks.ReentrantReadWriteLock;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;\r
+import org.apache.poi.openxml4j.opc.internal.ContentType;\r
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;\r
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;\r
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.PartUnmarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;\r
+import org.apache.poi.openxml4j.opc.internal.marshallers.DefaultMarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.PackagePropertiesUnmarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.UnmarshallContext;\r
+import org.apache.poi.openxml4j.util.Nullable;\r
+\r
+/**\r
+ * Represents a container that can store multiple data objects.\r
+ * \r
+ * @author Julien Chable, CDubet\r
+ * @version 0.1\r
+ */\r
+public abstract class Package implements RelationshipSource {\r
+\r
+       /**\r
+        * Logger.\r
+        */\r
+       protected static Logger logger = Logger.getLogger("org.openxml4j.opc");\r
+\r
+       /**\r
+        * Default package access.\r
+        */\r
+       protected static final PackageAccess defaultPackageAccess = PackageAccess.READ_WRITE;\r
+\r
+       /**\r
+        * Package access.\r
+        */\r
+       private PackageAccess packageAccess;\r
+\r
+       /**\r
+        * Package parts collection.\r
+        */\r
+       protected PackagePartCollection partList;\r
+\r
+       /**\r
+        * Package relationships.\r
+        */\r
+       protected PackageRelationshipCollection relationships;\r
+\r
+       /**\r
+        * Part marshallers by content type.\r
+        */\r
+       protected Hashtable<ContentType, PartMarshaller> partMarshallers;\r
+\r
+       /**\r
+        * Default part marshaller.\r
+        */\r
+       protected PartMarshaller defaultPartMarshaller;\r
+\r
+       /**\r
+        * Part unmarshallers by content type.\r
+        */\r
+       protected Hashtable<ContentType, PartUnmarshaller> partUnmarshallers;\r
+\r
+       /**\r
+        * Core package properties.\r
+        */\r
+       protected PackagePropertiesPart packageProperties;\r
+\r
+       /**\r
+        * Manage parts content types of this package.\r
+        */\r
+       protected ContentTypeManager contentTypeManager;\r
+\r
+       /**\r
+        * Flag if a modification is done to the document.\r
+        */\r
+       protected boolean isDirty = false;\r
+\r
+       /**\r
+        * File path of this package.\r
+        */\r
+       protected String originalPackagePath;\r
+\r
+       /**\r
+        * Output stream for writing this package.\r
+        */\r
+       protected OutputStream output;\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param access\r
+        *            Package access.\r
+        */\r
+       protected Package(PackageAccess access) {\r
+               init();\r
+               this.packageAccess = access;\r
+       }\r
+\r
+       /**\r
+        * Initialize the package instance.\r
+        */\r
+       private void init() {\r
+               this.partMarshallers = new Hashtable<ContentType, PartMarshaller>(5);\r
+               this.partUnmarshallers = new Hashtable<ContentType, PartUnmarshaller>(2);\r
+\r
+               try {\r
+                       // Add 'default' unmarshaller\r
+                       this.partUnmarshallers.put(new ContentType(\r
+                                       ContentTypes.CORE_PROPERTIES_PART),\r
+                                       new PackagePropertiesUnmarshaller());\r
+\r
+                       // Add default marshaller\r
+                       this.defaultPartMarshaller = new DefaultMarshaller();\r
+                       // TODO Delocalize specialized marshallers\r
+                       this.partMarshallers.put(new ContentType(\r
+                                       ContentTypes.CORE_PROPERTIES_PART),\r
+                                       new ZipPackagePropertiesMarshaller());\r
+               } catch (InvalidFormatException e) {\r
+                       // Should never happpen\r
+                       throw new OpenXML4JRuntimeException(\r
+                                       "Package.init() : this exception should never happen, if you read this message please send a mail to the developers team.");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Open a package with read/write permission.\r
+        * \r
+        * @param path\r
+        *            The document path.\r
+        * @return A Package object, else <b>null</b>.\r
+        * @throws InvalidFormatException\r
+        *             If the specified file doesn't exist, and a parsing error\r
+        *             occur.\r
+        */\r
+       public static Package open(String path) throws InvalidFormatException {\r
+               return open(path, defaultPackageAccess);\r
+       }\r
+\r
+       /**\r
+        * Open a package.\r
+        * \r
+        * @param path\r
+        *            The document path.\r
+        * @param access\r
+        *            Package access.\r
+        * @return A Package object, else <b>null</b>.\r
+        * @throws InvalidFormatException\r
+        *             If the specified file doesn't exist, and a parsing error\r
+        *             occur.\r
+        */\r
+       public static Package open(String path, PackageAccess access)\r
+                       throws InvalidFormatException {\r
+               if (path == null || "".equals(path.trim())\r
+                               || (new File(path).exists() && new File(path).isDirectory()))\r
+                       throw new IllegalArgumentException("path");\r
+\r
+               Package pack = new ZipPackage(path, access);\r
+               if (pack.partList == null && access != PackageAccess.WRITE) {\r
+                       pack.getParts();\r
+               }\r
+               pack.originalPackagePath = new File(path).getAbsolutePath();\r
+               return pack;\r
+       }\r
+\r
+       /**\r
+        * Open a package.\r
+        * \r
+        * Note - uses quite a bit more memory than {@link #open(String)}, which\r
+        * doesn't need to hold the whole zip file in memory, and can take advantage\r
+        * of native methods\r
+        * \r
+        * @param in\r
+        *            The InputStream to read the package from\r
+        * @return A Package object\r
+        */\r
+       public static Package open(InputStream in) throws InvalidFormatException,\r
+                       IOException {\r
+               Package pack = new ZipPackage(in, PackageAccess.READ);\r
+               if (pack.partList == null) {\r
+                       pack.getParts();\r
+               }\r
+               return pack;\r
+       }\r
+\r
+       /**\r
+        * Opens a package if it exists, else it creates one.\r
+        * \r
+        * @param file\r
+        *            The file to open or to create.\r
+        * @return A newly created package if the specified file does not exist,\r
+        *         else the package extract from the file.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the specified file exist and is not valid.\r
+        */\r
+       public static Package openOrCreate(File file) throws InvalidFormatException {\r
+               Package retPackage = null;\r
+               if (file.exists()) {\r
+                       retPackage = open(file.getAbsolutePath());\r
+               } else {\r
+                       retPackage = create(file);\r
+               }\r
+               return retPackage;\r
+       }\r
+\r
+       /**\r
+        * Creates a new package.\r
+        * \r
+        * @param path\r
+        *            Path of the document.\r
+        * @return A newly created Package ready to use.\r
+        */\r
+       public static Package create(String path) {\r
+               return create(new File(path));\r
+       }\r
+\r
+       /**\r
+        * Creates a new package.\r
+        * \r
+        * @param file\r
+        *            Path of the document.\r
+        * @return A newly created Package ready to use.\r
+        */\r
+       public static Package create(File file) {\r
+               if (file == null || (file.exists() && file.isDirectory()))\r
+                       throw new IllegalArgumentException("file");\r
+\r
+               if (file.exists()) {\r
+                       throw new InvalidOperationException(\r
+                                       "This package (or file) already exists : use the open() method or delete the file.");\r
+               }\r
+\r
+               // Creates a new package\r
+               Package pkg = null;\r
+               pkg = new ZipPackage();\r
+               pkg.originalPackagePath = file.getAbsolutePath();\r
+\r
+               configurePackage(pkg);\r
+               return pkg;\r
+       }\r
+\r
+       public static Package create(OutputStream output) {\r
+               Package pkg = null;\r
+               pkg = new ZipPackage();\r
+               pkg.originalPackagePath = null;\r
+               pkg.output = output;\r
+\r
+               configurePackage(pkg);\r
+               return pkg;\r
+       }\r
+\r
+       /**\r
+        * Configure the package.\r
+        * \r
+        * @param pkg\r
+        */\r
+       private static void configurePackage(Package pkg) {\r
+               try {\r
+                       // Content type manager\r
+                       pkg.contentTypeManager = new ZipContentTypeManager(null, pkg);\r
+                       // Add default content types for .xml and .rels\r
+                       pkg.contentTypeManager\r
+                                       .addContentType(\r
+                                                       PackagingURIHelper\r
+                                                                       .createPartName(PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_URI),\r
+                                                       ContentTypes.RELATIONSHIPS_PART);\r
+                       pkg.contentTypeManager\r
+                                       .addContentType(PackagingURIHelper\r
+                                                       .createPartName("/default.xml"),\r
+                                                       ContentTypes.PLAIN_OLD_XML);\r
+\r
+                       // Init some Package properties\r
+                       pkg.packageProperties = new PackagePropertiesPart(pkg,\r
+                                       PackagingURIHelper.CORE_PROPERTIES_PART_NAME);\r
+                       pkg.packageProperties.setCreatorProperty("Generated by OpenXML4J");\r
+                       pkg.packageProperties.setCreatedProperty(new Nullable<Date>(\r
+                                       new Date()));\r
+               } catch (InvalidFormatException e) {\r
+                       // Should never happen\r
+                       throw new IllegalStateException(e);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Flush the package : save all.\r
+        * \r
+        * @see #close()\r
+        */\r
+       public void flush() {\r
+               throwExceptionIfReadOnly();\r
+\r
+               if (this.packageProperties != null)\r
+                       ((PackagePropertiesPart) this.packageProperties).flush();\r
+\r
+               this.flushImpl();\r
+       }\r
+\r
+       /**\r
+        * Close the package and save its content.\r
+        * \r
+        * @throws IOException\r
+        *             If an IO exception occur during the saving process.\r
+        */\r
+       public void close() throws IOException {\r
+               if (this.packageAccess == PackageAccess.READ) {\r
+                       logger\r
+                                       .warn("The close() method is intended to SAVE a package. This package is open in READ ONLY mode, use the revert() method instead !");\r
+                       return;\r
+               }\r
+\r
+               // Save the content\r
+               ReentrantReadWriteLock l = new ReentrantReadWriteLock();\r
+               try {\r
+                       l.writeLock().lock();\r
+                       if (this.originalPackagePath != null\r
+                                       && !"".equals(this.originalPackagePath.trim())) {\r
+                               File targetFile = new File(this.originalPackagePath);\r
+                               if (!targetFile.exists()\r
+                                               || !(this.originalPackagePath\r
+                                                               .equalsIgnoreCase(targetFile.getAbsolutePath()))) {\r
+                                       // Case of a package created from scratch\r
+                                       save(targetFile);\r
+                               } else {\r
+                                       closeImpl();\r
+                               }\r
+                       } else if (this.output != null) {\r
+                               save(this.output);\r
+                       }\r
+               } finally {\r
+                       l.writeLock().unlock();\r
+               }\r
+\r
+               // Clear\r
+               this.contentTypeManager.clearAll();\r
+\r
+               // Call the garbage collector\r
+               Runtime.getRuntime().gc();\r
+       }\r
+\r
+       /**\r
+        * Close the package WITHOUT saving its content. Reinitialize this package\r
+        * and cancel all changes done to it.\r
+        */\r
+       public void revert() {\r
+               revertImpl();\r
+       }\r
+\r
+       /**\r
+        * Add a thumbnail to the package. This method is provided to make easier\r
+        * the addition of a thumbnail in a package. You can do the same work by\r
+        * using the traditionnal relationship and part mechanism.\r
+        * \r
+        * @param path\r
+        *            The full path to the image file.\r
+        */\r
+       public void addThumbnail(String path) throws IOException {\r
+               // Check parameter\r
+               if ("".equals(path))\r
+                       throw new IllegalArgumentException("path");\r
+\r
+               // Get the filename from the path\r
+               String filename = path\r
+                               .substring(path.lastIndexOf(File.separatorChar) + 1);\r
+\r
+               // Create the thumbnail part name\r
+               String contentType = ContentTypes\r
+                               .getContentTypeFromFileExtension(filename);\r
+               PackagePartName thumbnailPartName = null;\r
+               try {\r
+                       thumbnailPartName = PackagingURIHelper.createPartName("/docProps/"\r
+                                       + filename);\r
+               } catch (InvalidFormatException e) {\r
+                       try {\r
+                               thumbnailPartName = PackagingURIHelper\r
+                                               .createPartName("/docProps/thumbnail"\r
+                                                               + path.substring(path.lastIndexOf(".") + 1));\r
+                       } catch (InvalidFormatException e2) {\r
+                               throw new InvalidOperationException(\r
+                                               "Can't add a thumbnail file named '" + filename + "'");\r
+                       }\r
+               }\r
+\r
+               // Check if part already exist\r
+               if (this.getPart(thumbnailPartName) != null)\r
+                       throw new InvalidOperationException(\r
+                                       "You already add a thumbnail named '" + filename + "'");\r
+\r
+               // Add the thumbnail part to this package.\r
+               PackagePart thumbnailPart = this.createPart(thumbnailPartName,\r
+                               contentType, false);\r
+\r
+               // Add the relationship between the package and the thumbnail part\r
+               this.addRelationship(thumbnailPartName, TargetMode.INTERNAL,\r
+                               PackageRelationshipTypes.THUMBNAIL);\r
+\r
+               // Copy file data to the newly created part\r
+               StreamHelper.copyStream(new FileInputStream(path), thumbnailPart\r
+                               .getOutputStream());\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if the package access mode is in read only mode\r
+        * (PackageAccess.Read).\r
+        * \r
+        * @throws InvalidOperationException\r
+        *             Throws if a writing operation is done on a read only package.\r
+        * @see org.apache.poi.openxml4j.opc.PackageAccess\r
+        */\r
+       void throwExceptionIfReadOnly() throws InvalidOperationException {\r
+               if (packageAccess == PackageAccess.READ)\r
+                       throw new InvalidOperationException(\r
+                                       "Operation not allowed, document open in read only mode!");\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if the package access mode is in write only mode\r
+        * (PackageAccess.Write). This method is call when other methods need write\r
+        * right.\r
+        * \r
+        * @throws InvalidOperationException\r
+        *             Throws if a read operation is done on a write only package.\r
+        * @see org.apache.poi.openxml4j.opc.PackageAccess\r
+        */\r
+       void throwExceptionIfWriteOnly() throws InvalidOperationException {\r
+               if (packageAccess == PackageAccess.WRITE)\r
+                       throw new InvalidOperationException(\r
+                                       "Operation not allowed, document open in write only mode!");\r
+       }\r
+\r
+       /**\r
+        * Retrieves or creates if none exists, core package property part.\r
+        * \r
+        * @return The PackageProperties part of this package.\r
+        */\r
+       public PackageProperties getPackageProperties()\r
+                       throws InvalidFormatException {\r
+               this.throwExceptionIfWriteOnly();\r
+               // If no properties part has been found then we create one\r
+               if (this.packageProperties == null) {\r
+                       this.packageProperties = new PackagePropertiesPart(this,\r
+                                       PackagingURIHelper.CORE_PROPERTIES_PART_NAME);\r
+               }\r
+               return this.packageProperties;\r
+       }\r
+\r
+       /**\r
+        * Retrieve a part identified by its name.\r
+        * \r
+        * @param partName\r
+        *            Part name of the part to retrieve.\r
+        * @return The part with the specified name, else <code>null</code>.\r
+        */\r
+       public PackagePart getPart(PackagePartName partName) {\r
+               throwExceptionIfWriteOnly();\r
+\r
+               if (partName == null)\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               // If the partlist is null, then we parse the package.\r
+               if (partList == null) {\r
+                       try {\r
+                               getParts();\r
+                       } catch (InvalidFormatException e) {\r
+                               return null;\r
+                       }\r
+               }\r
+               return getPartImpl(partName);\r
+       }\r
+\r
+       /**\r
+        * Retrieve parts by content type.\r
+        * \r
+        * @param contentType\r
+        *            The content type criteria.\r
+        * @return All part associated to the specified content type.\r
+        */\r
+       public ArrayList<PackagePart> getPartsByContentType(String contentType) {\r
+               ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();\r
+               for (PackagePart part : partList.values()) {\r
+                       if (part.getContentType().equals(contentType))\r
+                               retArr.add(part);\r
+               }\r
+               return retArr;\r
+       }\r
+\r
+       /**\r
+        * Retrieve parts by relationship type.\r
+        * \r
+        * @param relationshipType\r
+        *            Relationship type.\r
+        * @return All parts which are the target of a relationship with the\r
+        *         specified type, if the method can't retrieve relationships from\r
+        *         the package, then return <code>null</code>.\r
+        */\r
+       public ArrayList<PackagePart> getPartsByRelationshipType(\r
+                       String relationshipType) {\r
+               if (relationshipType == null)\r
+                       throw new IllegalArgumentException("relationshipType");\r
+               ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();\r
+               try {\r
+                       for (PackageRelationship rel : getRelationshipsByType(relationshipType)) {\r
+                               retArr.add(getPart(rel));\r
+                       }\r
+               } catch (OpenXML4JException e) {\r
+                       logger\r
+                                       .warn("Can't retrieve parts by relationship type: an exception has been thrown by getRelationshipsByType method");\r
+                       return null;\r
+               }\r
+               return retArr;\r
+       }\r
+\r
+       /**\r
+        * Get the target part from the specified relationship.\r
+        * \r
+        * @param partRel\r
+        *            The part relationship uses to retrieve the part.\r
+        */\r
+       public PackagePart getPart(PackageRelationship partRel) {\r
+               PackagePart retPart = null;\r
+               ensureRelationships();\r
+               for (PackageRelationship rel : relationships) {\r
+                       if (rel.getRelationshipType().equals(partRel.getRelationshipType())) {\r
+                               try {\r
+                                       retPart = getPart(PackagingURIHelper.createPartName(rel\r
+                                                       .getTargetURI()));\r
+                               } catch (InvalidFormatException e) {\r
+                                       continue;\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+               return retPart;\r
+       }\r
+\r
+       /**\r
+        * Load the parts of the archive if it has not been done yet The\r
+        * relationships of each part are not loaded\r
+        * \r
+        * @return All this package's parts.\r
+        */\r
+       public ArrayList<PackagePart> getParts() throws InvalidFormatException {\r
+               throwExceptionIfWriteOnly();\r
+\r
+               // If the part list is null, we parse the package to retrieve all parts.\r
+               if (partList == null) {\r
+                       /* Variables use to validate OPC Compliance */\r
+\r
+                       // Ensure rule M4.1 -> A format consumer shall consider more than\r
+                       // one core properties relationship for a package to be an error\r
+                       boolean hasCorePropertiesPart = false;\r
+\r
+                       PackagePart[] parts = this.getPartsImpl();\r
+                       this.partList = new PackagePartCollection();\r
+                       for (PackagePart part : parts) {\r
+                               if (partList.containsKey(part.partName))\r
+                                       throw new InvalidFormatException(\r
+                                                       "A part with the name '"\r
+                                                                       + part.partName\r
+                                                                       + "' already exist : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");\r
+\r
+                               // Check OPC compliance rule M4.1\r
+                               if (part.getContentType().equals(\r
+                                               ContentTypes.CORE_PROPERTIES_PART)) {\r
+                                       if (!hasCorePropertiesPart)\r
+                                               hasCorePropertiesPart = true;\r
+                                       else\r
+                                               throw new InvalidFormatException(\r
+                                                               "OPC Compliance error [M4.1]: there is more than one core properties relationship in the package !");\r
+                               }\r
+\r
+                               PartUnmarshaller partUnmarshaller = partUnmarshallers\r
+                                               .get(part.contentType);\r
+\r
+                               if (partUnmarshaller != null) {\r
+                                       UnmarshallContext context = new UnmarshallContext(this,\r
+                                                       part.partName);\r
+                                       try {\r
+                                               PackagePart unmarshallPart = partUnmarshaller\r
+                                                               .unmarshall(context, part.getInputStream());\r
+                                               partList.put(unmarshallPart.partName, unmarshallPart);\r
+\r
+                                               // Core properties case\r
+                                               if (unmarshallPart instanceof PackagePropertiesPart)\r
+                                                       this.packageProperties = (PackagePropertiesPart) unmarshallPart;\r
+                                       } catch (IOException ioe) {\r
+                                               logger.warn("Unmarshall operation : IOException for "\r
+                                                               + part.partName);\r
+                                               continue;\r
+                                       } catch (InvalidOperationException invoe) {\r
+                                               throw new InvalidFormatException(invoe.getMessage());\r
+                                       }\r
+                               } else {\r
+                                       try {\r
+                                               partList.put(part.partName, part);\r
+                                       } catch (InvalidOperationException e) {\r
+                                               throw new InvalidFormatException(e.getMessage());\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return new ArrayList<PackagePart>(partList.values());\r
+       }\r
+\r
+       /**\r
+        * Create and add a part, with the specified name and content type, to the\r
+        * package.\r
+        * \r
+        * @param partName\r
+        *            Part name.\r
+        * @param contentType\r
+        *            Part content type.\r
+        * @return The newly created part.\r
+        * @throws InvalidFormatException\r
+        *             If rule M1.12 is not verified : Packages shall not contain\r
+        *             equivalent part names and package implementers shall neither\r
+        *             create nor recognize packages with equivalent part names.\r
+        * @see {@link#createPartImpl(URI, String)}\r
+        */\r
+       public PackagePart createPart(PackagePartName partName, String contentType) {\r
+               return this.createPart(partName, contentType, true);\r
+       }\r
+\r
+       /**\r
+        * Create and add a part, with the specified name and content type, to the\r
+        * package. For general purpose, prefer the overload version of this method\r
+        * without the 'loadRelationships' parameter.\r
+        * \r
+        * @param partName\r
+        *            Part name.\r
+        * @param contentType\r
+        *            Part content type.\r
+        * @param loadRelationships\r
+        *            Specify if the existing relationship part, if any, logically\r
+        *            associated to the newly created part will be loaded.\r
+        * @return The newly created part.\r
+        * @throws InvalidFormatException\r
+        *             If rule M1.12 is not verified : Packages shall not contain\r
+        *             equivalent part names and package implementers shall neither\r
+        *             create nor recognize packages with equivalent part names.\r
+        * @see {@link#createPartImpl(URI, String)}\r
+        */\r
+       PackagePart createPart(PackagePartName partName, String contentType,\r
+                       boolean loadRelationships) {\r
+               throwExceptionIfReadOnly();\r
+               if (partName == null) {\r
+                       throw new IllegalArgumentException("partName");\r
+               }\r
+\r
+               if (contentType == null || contentType == "") {\r
+                       throw new IllegalArgumentException("contentType");\r
+               }\r
+\r
+               // Check if the specified part name already exists\r
+               if (partList.containsKey(partName)\r
+                               && !partList.get(partName).isDeleted()) {\r
+                       throw new InvalidOperationException(\r
+                                       "A part with the name '"\r
+                                                       + partName.getName()\r
+                                                       + "' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");\r
+               }\r
+\r
+               /* Check OPC compliance */\r
+\r
+               // Rule [M4.1]: The format designer shall specify and the format\r
+               // producer\r
+               // shall create at most one core properties relationship for a package.\r
+               // A format consumer shall consider more than one core properties\r
+               // relationship for a package to be an error. If present, the\r
+               // relationship shall target the Core Properties part.\r
+               if (contentType == ContentTypes.CORE_PROPERTIES_PART) {\r
+                       if (this.packageProperties != null)\r
+                               throw new InvalidOperationException(\r
+                                               "OPC Compliance error [M4.1]: you try to add more than one core properties relationship in the package !");\r
+               }\r
+\r
+               /* End check OPC compliance */\r
+\r
+               PackagePart part = this.createPartImpl(partName, contentType,\r
+                               loadRelationships);\r
+               this.contentTypeManager.addContentType(partName, contentType);\r
+               this.partList.put(partName, part);\r
+               this.isDirty = true;\r
+               return part;\r
+       }\r
+\r
+       /**\r
+        * Add a part to the package.\r
+        * \r
+        * @param partName\r
+        *            Part name of the part to create.\r
+        * @param contentType\r
+        *            type associated with the file\r
+        * @param content\r
+        *            the contents to add. In order to have faster operation in\r
+        *            document merge, the data are stored in memory not on a hard\r
+        *            disk\r
+        * \r
+        * @return The new part.\r
+        * @see {@link #createPart(PackagePartName, String)}\r
+        */\r
+       public PackagePart createPart(PackagePartName partName, String contentType,\r
+                       ByteArrayOutputStream content) {\r
+               PackagePart addedPart = this.createPart(partName, contentType);\r
+               if (addedPart == null) {\r
+                       return null;\r
+               }\r
+               // Extract the zip entry content to put it in the part content\r
+               if (content != null) {\r
+                       try {\r
+                               OutputStream partOutput = addedPart.getOutputStream();\r
+                               if (partOutput == null) {\r
+                                       return null;\r
+                               }\r
+\r
+                               partOutput.write(content.toByteArray(), 0, content.size());\r
+                               partOutput.close();\r
+\r
+                       } catch (IOException ioe) {\r
+                               return null;\r
+                       }\r
+               } else {\r
+                       return null;\r
+               }\r
+               return addedPart;\r
+       }\r
+\r
+       /**\r
+        * Add the specified part to the package. If a part already exists in the\r
+        * package with the same name as the one specified, then we replace the old\r
+        * part by the specified part.\r
+        * \r
+        * @param part\r
+        *            The part to add (or replace).\r
+        * @return The part added to the package, the same as the one specified.\r
+        * @throws InvalidFormatException\r
+        *             If rule M1.12 is not verified : Packages shall not contain\r
+        *             equivalent part names and package implementers shall neither\r
+        *             create nor recognize packages with equivalent part names.\r
+        */\r
+       protected PackagePart addPackagePart(PackagePart part) {\r
+               throwExceptionIfReadOnly();\r
+               if (part == null) {\r
+                       throw new IllegalArgumentException("part");\r
+               }\r
+\r
+               if (partList.containsKey(part.partName)) {\r
+                       if (!partList.get(part.partName).isDeleted()) {\r
+                               throw new InvalidOperationException(\r
+                                               "A part with the name '"\r
+                                                               + part.partName.getName()\r
+                                                               + "' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");\r
+                       } else {\r
+                               // If the specified partis flagged as deleted, we make it\r
+                               // available\r
+                               part.setDeleted(false);\r
+                               // and delete the old part to replace it thereafeter\r
+                               this.partList.remove(part.partName);\r
+                       }\r
+               }\r
+               this.partList.put(part.partName, part);\r
+               this.isDirty = true;\r
+               return part;\r
+       }\r
+\r
+       /**\r
+        * Remove the specified part in this package. If this part is relationship\r
+        * part, then delete all relationships in the source part.\r
+        * \r
+        * @param part\r
+        *            The part to remove. If <code>null</code>, skip the action.\r
+        * @see #removePart(PackagePartName)\r
+        */\r
+       public void removePart(PackagePart part) {\r
+               if (part != null) {\r
+                       removePart(part.getPartName());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Remove a part in this package. If this part is relationship part, then\r
+        * delete all relationships in the source part.\r
+        * \r
+        * @param partName\r
+        *            The part name of the part to remove.\r
+        */\r
+       public void removePart(PackagePartName partName) {\r
+               throwExceptionIfReadOnly();\r
+               if (partName == null || !this.containPart(partName))\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               // Delete the specified part from the package.\r
+               if (this.partList.containsKey(partName)) {\r
+                       this.partList.get(partName).setDeleted(true);\r
+                       this.removePartImpl(partName);\r
+                       this.partList.remove(partName);\r
+               } else {\r
+                       this.removePartImpl(partName);\r
+               }\r
+\r
+               // Delete content type\r
+               this.contentTypeManager.removeContentType(partName);\r
+\r
+               // If this part is a relationship part, then delete all relationships of\r
+               // the source part.\r
+               if (partName.isRelationshipPartURI()) {\r
+                       URI sourceURI = PackagingURIHelper\r
+                                       .getSourcePartUriFromRelationshipPartUri(partName.getURI());\r
+                       PackagePartName sourcePartName;\r
+                       try {\r
+                               sourcePartName = PackagingURIHelper.createPartName(sourceURI);\r
+                       } catch (InvalidFormatException e) {\r
+                               logger\r
+                                               .error("Part name URI '"\r
+                                                               + sourceURI\r
+                                                               + "' is not valid ! This message is not intended to be displayed !");\r
+                               return;\r
+                       }\r
+                       if (sourcePartName.getURI().equals(\r
+                                       PackagingURIHelper.PACKAGE_ROOT_URI)) {\r
+                               clearRelationships();\r
+                       } else if (containPart(sourcePartName)) {\r
+                               PackagePart part = getPart(sourcePartName);\r
+                               if (part != null)\r
+                                       part.clearRelationships();\r
+                       }\r
+               }\r
+\r
+               this.isDirty = true;\r
+       }\r
+\r
+       /**\r
+        * Remove a part from this package as well as its relationship part, if one\r
+        * exists, and all parts listed in the relationship part. Be aware that this\r
+        * do not delete relationships which target the specified part.\r
+        * \r
+        * @param partName\r
+        *            The name of the part to delete.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the associated relationship part of the specified\r
+        *             part is not valid.\r
+        */\r
+       public void removePartRecursive(PackagePartName partName)\r
+                       throws InvalidFormatException {\r
+               // Retrieves relationship part, if one exists\r
+               PackagePart relPart = this.partList.get(PackagingURIHelper\r
+                               .getRelationshipPartName(partName));\r
+               // Retrieves PackagePart object from the package\r
+               PackagePart partToRemove = this.partList.get(partName);\r
+\r
+               if (relPart != null) {\r
+                       PackageRelationshipCollection partRels = new PackageRelationshipCollection(\r
+                                       partToRemove);\r
+                       for (PackageRelationship rel : partRels) {\r
+                               PackagePartName partNameToRemove = PackagingURIHelper\r
+                                               .createPartName(PackagingURIHelper.resolvePartUri(rel\r
+                                                               .getSourceURI(), rel.getTargetURI()));\r
+                               removePart(partNameToRemove);\r
+                       }\r
+\r
+                       // Finally delete its relationship part if one exists\r
+                       this.removePart(relPart.partName);\r
+               }\r
+\r
+               // Delete the specified part\r
+               this.removePart(partToRemove.partName);\r
+       }\r
+\r
+       /**\r
+        * Delete the part with the specified name and its associated relationships\r
+        * part if one exists. Prefer the use of this method to delete a part in the\r
+        * package, compare to the remove() methods that don't remove associated\r
+        * relationships part.\r
+        * \r
+        * @param partName\r
+        *            Name of the part to delete\r
+        */\r
+       public void deletePart(PackagePartName partName) {\r
+               if (partName == null)\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               // Remove the part\r
+               this.removePart(partName);\r
+               // Remove the relationships part\r
+               this.removePart(PackagingURIHelper.getRelationshipPartName(partName));\r
+       }\r
+\r
+       /**\r
+        * Delete the part with the specified name and all part listed in its\r
+        * associated relationships part if one exists. This process is recursively\r
+        * apply to all parts in the relationships part of the specified part.\r
+        * Prefer the use of this method to delete a part in the package, compare to\r
+        * the remove() methods that don't remove associated relationships part.\r
+        * \r
+        * @param partName\r
+        *            Name of the part to delete\r
+        */\r
+       public void deletePartRecursive(PackagePartName partName) {\r
+               if (partName == null || !this.containPart(partName))\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               PackagePart partToDelete = this.getPart(partName);\r
+               // Remove the part\r
+               this.removePart(partName);\r
+               // Remove all relationship parts associated\r
+               try {\r
+                       for (PackageRelationship relationship : partToDelete\r
+                                       .getRelationships()) {\r
+                               PackagePartName targetPartName = PackagingURIHelper\r
+                                               .createPartName(PackagingURIHelper.resolvePartUri(\r
+                                                               partName.getURI(), relationship.getTargetURI()));\r
+                               this.deletePartRecursive(targetPartName);\r
+                       }\r
+               } catch (InvalidFormatException e) {\r
+                       logger.warn("An exception occurs while deleting part '"\r
+                                       + partName.getName()\r
+                                       + "'. Some parts may remain in the package. - "\r
+                                       + e.getMessage());\r
+                       return;\r
+               }\r
+               // Remove the relationships part\r
+               PackagePartName relationshipPartName = PackagingURIHelper\r
+                               .getRelationshipPartName(partName);\r
+               if (relationshipPartName != null && containPart(relationshipPartName))\r
+                       this.removePart(relationshipPartName);\r
+       }\r
+\r
+       /**\r
+        * Check if a part already exists in this package from its name.\r
+        * \r
+        * @param partName\r
+        *            Part name to check.\r
+        * @return <i>true</i> if the part is logically added to this package, else\r
+        *         <i>false</i>.\r
+        */\r
+       public boolean containPart(PackagePartName partName) {\r
+               return (this.getPart(partName) != null);\r
+       }\r
+\r
+       /**\r
+        * Add a relationship to the package (except relationships part).\r
+        * \r
+        * Check rule M4.1 : The format designer shall specify and the format\r
+        * producer shall create at most one core properties relationship for a\r
+        * package. A format consumer shall consider more than one core properties\r
+        * relationship for a package to be an error. If present, the relationship\r
+        * shall target the Core Properties part.\r
+        * \r
+        * Check rule M1.25: The Relationships part shall not have relationships to\r
+        * any other part. Package implementers shall enforce this requirement upon\r
+        * the attempt to create such a relationship and shall treat any such\r
+        * relationship as invalid.\r
+        * \r
+        * @param targetPartName\r
+        *            Target part name.\r
+        * @param targetMode\r
+        *            Target mode, either Internal or External.\r
+        * @param relationshipType\r
+        *            Relationship type.\r
+        * @param relID\r
+        *            ID of the relationship.\r
+        * @see PackageRelationshipTypes\r
+        */\r
+       public PackageRelationship addRelationship(PackagePartName targetPartName,\r
+                       TargetMode targetMode, String relationshipType, String relID) {\r
+               /* Check OPC compliance */\r
+\r
+               // Check rule M4.1 : The format designer shall specify and the format\r
+               // producer\r
+               // shall create at most one core properties relationship for a package.\r
+               // A format consumer shall consider more than one core properties\r
+               // relationship for a package to be an error. If present, the\r
+               // relationship shall target the Core Properties part.\r
+               if (relationshipType.equals(PackageRelationshipTypes.CORE_PROPERTIES)\r
+                               && this.packageProperties != null)\r
+                       throw new InvalidOperationException(\r
+                                       "OPC Compliance error [M4.1]: can't add another core properties part ! Use the built-in package method instead.");\r
+\r
+               /*\r
+                * Check rule M1.25: The Relationships part shall not have relationships\r
+                * to any other part. Package implementers shall enforce this\r
+                * requirement upon the attempt to create such a relationship and shall\r
+                * treat any such relationship as invalid.\r
+                */\r
+               if (targetPartName.isRelationshipPartURI()) {\r
+                       throw new InvalidOperationException(\r
+                                       "Rule M1.25: The Relationships part shall not have relationships to any other part.");\r
+               }\r
+\r
+               /* End OPC compliance */\r
+\r
+               ensureRelationships();\r
+               PackageRelationship retRel = relationships.addRelationship(\r
+                               targetPartName.getURI(), targetMode, relationshipType, relID);\r
+               this.isDirty = true;\r
+               return retRel;\r
+       }\r
+\r
+       /**\r
+        * Add a package relationship.\r
+        * \r
+        * @param targetPartName\r
+        *            Target part name.\r
+        * @param targetMode\r
+        *            Target mode, either Internal or External.\r
+        * @param relationshipType\r
+        *            Relationship type.\r
+        * @see PackageRelationshipTypes\r
+        */\r
+       public PackageRelationship addRelationship(PackagePartName targetPartName,\r
+                       TargetMode targetMode, String relationshipType) {\r
+               return this.addRelationship(targetPartName, targetMode,\r
+                               relationshipType, null);\r
+       }\r
+\r
+       /**\r
+        * Adds an external relationship to a part (except relationships part).\r
+        * \r
+        * The targets of external relationships are not subject to the same\r
+        * validity checks that internal ones are, as the contents is potentially\r
+        * any file, URL or similar.\r
+        * \r
+        * @param target\r
+        *            External target of the relationship\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @return The newly created and added relationship\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,\r
+        *      java.lang.String)\r
+        */\r
+       public PackageRelationship addExternalRelationship(String target,\r
+                       String relationshipType) {\r
+               return addExternalRelationship(target, relationshipType, null);\r
+       }\r
+\r
+       /**\r
+        * Adds an external relationship to a part (except relationships part).\r
+        * \r
+        * The targets of external relationships are not subject to the same\r
+        * validity checks that internal ones are, as the contents is potentially\r
+        * any file, URL or similar.\r
+        * \r
+        * @param target\r
+        *            External target of the relationship\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @param id\r
+        *            Relationship unique id.\r
+        * @return The newly created and added relationship\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,\r
+        *      java.lang.String)\r
+        */\r
+       public PackageRelationship addExternalRelationship(String target,\r
+                       String relationshipType, String id) {\r
+               if (target == null) {\r
+                       throw new IllegalArgumentException("target");\r
+               }\r
+               if (relationshipType == null) {\r
+                       throw new IllegalArgumentException("relationshipType");\r
+               }\r
+\r
+               URI targetURI;\r
+               try {\r
+                       targetURI = new URI(target);\r
+               } catch (URISyntaxException e) {\r
+                       throw new IllegalArgumentException("Invalid target - " + e);\r
+               }\r
+\r
+               ensureRelationships();\r
+               PackageRelationship retRel = relationships.addRelationship(targetURI,\r
+                               TargetMode.EXTERNAL, relationshipType, id);\r
+               this.isDirty = true;\r
+               return retRel;\r
+       }\r
+\r
+       /**\r
+        * Delete a relationship from this package.\r
+        * \r
+        * @param id\r
+        *            Id of the relationship to delete.\r
+        */\r
+       public void removeRelationship(String id) {\r
+               if (relationships != null) {\r
+                       relationships.removeRelationship(id);\r
+                       this.isDirty = true;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Retrieves all package relationships.\r
+        * \r
+        * @return All package relationships of this package.\r
+        * @throws OpenXML4JException\r
+        * @see {@link #getRelationshipsHelper(String)}\r
+        */\r
+       public PackageRelationshipCollection getRelationships()\r
+                       throws OpenXML4JException {\r
+               return getRelationshipsHelper(null);\r
+       }\r
+\r
+       /**\r
+        * Retrives all relationships with the specified type.\r
+        * \r
+        * @param relationshipType\r
+        *            The filter specifying the relationship type.\r
+        * @return All relationships with the specified relationship type.\r
+        * @throws OpenXML4JException\r
+        */\r
+       public PackageRelationshipCollection getRelationshipsByType(\r
+                       String relationshipType) throws IllegalArgumentException,\r
+                       OpenXML4JException {\r
+               throwExceptionIfWriteOnly();\r
+               if (relationshipType == null) {\r
+                       throw new IllegalArgumentException("relationshipType");\r
+               }\r
+               return getRelationshipsHelper(relationshipType);\r
+       }\r
+\r
+       /**\r
+        * Retrieves all relationships with specified id (normally just ine because\r
+        * a relationship id is supposed to be unique).\r
+        * \r
+        * @param id\r
+        *            Id of the wanted relationship.\r
+        * @throws OpenXML4JException\r
+        */\r
+       private PackageRelationshipCollection getRelationshipsHelper(String id)\r
+                       throws OpenXML4JException {\r
+               throwExceptionIfWriteOnly();\r
+               ensureRelationships();\r
+               return this.relationships.getRelationships(id);\r
+       }\r
+\r
+       /**\r
+        * Clear package relationships.\r
+        */\r
+       public void clearRelationships() {\r
+               if (relationships != null) {\r
+                       relationships.clear();\r
+                       this.isDirty = true;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Ensure that the relationships collection is not null.\r
+        */\r
+       public void ensureRelationships() {\r
+               if (this.relationships == null) {\r
+                       try {\r
+                               this.relationships = new PackageRelationshipCollection(this);\r
+                       } catch (InvalidFormatException e) {\r
+                               this.relationships = new PackageRelationshipCollection();\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationship(java.lang.String)\r
+        */\r
+       public PackageRelationship getRelationship(String id) {\r
+               return this.relationships.getRelationshipByID(id);\r
+       }\r
+\r
+       /**\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#hasRelationships()\r
+        */\r
+       public boolean hasRelationships() {\r
+               return (relationships.size() > 0);\r
+       }\r
+\r
+       /**\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#isRelationshipExists(org.apache.poi.openxml4j.opc.PackageRelationship)\r
+        */\r
+       @SuppressWarnings("finally")\r
+       public boolean isRelationshipExists(PackageRelationship rel) {\r
+               try {\r
+                       for (PackageRelationship r : this.getRelationships()) {\r
+                               if (r == rel)\r
+                                       return true;\r
+                       }\r
+               } finally {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Add a marshaller.\r
+        * \r
+        * @param contentType\r
+        *            The content type to bind to the specified marshaller.\r
+        * @param marshaller\r
+        *            The marshaller to register with the specified content type.\r
+        */\r
+       public void addMarshaller(String contentType, PartMarshaller marshaller) {\r
+               try {\r
+                       partMarshallers.put(new ContentType(contentType), marshaller);\r
+               } catch (InvalidFormatException e) {\r
+                       logger.warn("The specified content type is not valid: '"\r
+                                       + e.getMessage() + "'. The marshaller will not be added !");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Add an unmarshaller.\r
+        * \r
+        * @param contentType\r
+        *            The content type to bind to the specified unmarshaller.\r
+        * @param unmarshaller\r
+        *            The unmarshaller to register with the specified content type.\r
+        */\r
+       public void addUnmarshaller(String contentType,\r
+                       PartUnmarshaller unmarshaller) {\r
+               try {\r
+                       partUnmarshallers.put(new ContentType(contentType), unmarshaller);\r
+               } catch (InvalidFormatException e) {\r
+                       logger.warn("The specified content type is not valid: '"\r
+                                       + e.getMessage()\r
+                                       + "'. The unmarshaller will not be added !");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Remove a marshaller by its content type.\r
+        * \r
+        * @param contentType\r
+        *            The content type associated with the marshaller to remove.\r
+        */\r
+       public void removeMarshaller(String contentType) {\r
+               partMarshallers.remove(contentType);\r
+       }\r
+\r
+       /**\r
+        * Remove an unmarshaller by its content type.\r
+        * \r
+        * @param contentType\r
+        *            The content type associated with the unmarshaller to remove.\r
+        */\r
+       public void removeUnmarshaller(String contentType) {\r
+               partUnmarshallers.remove(contentType);\r
+       }\r
+\r
+       /* Accesseurs */\r
+\r
+       /**\r
+        * Get the package access mode.\r
+        * \r
+        * @return the packageAccess The current package access.\r
+        */\r
+       public PackageAccess getPackageAccess() {\r
+               return packageAccess;\r
+       }\r
+\r
+       /**\r
+        * Validates the package compliance with the OPC specifications.\r
+        * \r
+        * @return <b>true</b> if the package is valid else <b>false</b>\r
+        */\r
+       public boolean validatePackage(Package pkg) throws InvalidFormatException {\r
+               throw new InvalidOperationException("Not implemented yet !!!");\r
+       }\r
+\r
+       /**\r
+        * Save the document in the specified file.\r
+        * \r
+        * @param targetFile\r
+        *            Destination file.\r
+        * @throws IOException\r
+        *             Throws if an IO exception occur.\r
+        * @see #save(OutputStream)\r
+        */\r
+       public void save(File targetFile) throws IOException {\r
+               if (targetFile == null)\r
+                       throw new IllegalArgumentException("targetFile");\r
+\r
+               this.throwExceptionIfReadOnly();\r
+               FileOutputStream fos = null;\r
+               try {\r
+                       fos = new FileOutputStream(targetFile);\r
+               } catch (FileNotFoundException e) {\r
+                       throw new IOException(e.getLocalizedMessage());\r
+               }\r
+               this.save(fos);\r
+       }\r
+\r
+       /**\r
+        * Save the document in the specified output stream.\r
+        * \r
+        * @param outputStream\r
+        *            The stream to save the package.\r
+        * @see #saveImpl(OutputStream)\r
+        */\r
+       public void save(OutputStream outputStream) throws IOException {\r
+               throwExceptionIfReadOnly();\r
+               this.saveImpl(outputStream);\r
+       }\r
+\r
+       /**\r
+        * Core method to create a package part. This method must be implemented by\r
+        * the subclass.\r
+        * \r
+        * @param partName\r
+        *            URI of the part to create.\r
+        * @param contentType\r
+        *            Content type of the part to create.\r
+        * @return The newly created package part.\r
+        */\r
+       protected abstract PackagePart createPartImpl(PackagePartName partName,\r
+                       String contentType, boolean loadRelationships);\r
+\r
+       /**\r
+        * Core method to delete a package part. This method must be implemented by\r
+        * the subclass.\r
+        * \r
+        * @param partName\r
+        *            The URI of the part to delete.\r
+        */\r
+       protected abstract void removePartImpl(PackagePartName partName);\r
+\r
+       /**\r
+        * Flush the package but not save.\r
+        */\r
+       protected abstract void flushImpl();\r
+\r
+       /**\r
+        * Close the package and cause a save of the package.\r
+        * \r
+        */\r
+       protected abstract void closeImpl() throws IOException;\r
+\r
+       /**\r
+        * Close the package without saving the document. Discard all changes made\r
+        * to this package.\r
+        */\r
+       protected abstract void revertImpl();\r
+\r
+       /**\r
+        * Save the package into the specified output stream.\r
+        * \r
+        * @param outputStream\r
+        *            The output stream use to save this package.\r
+        */\r
+       protected abstract void saveImpl(OutputStream outputStream)\r
+                       throws IOException;\r
+\r
+       /**\r
+        * Get the package part mapped to the specified URI.\r
+        * \r
+        * @param partName\r
+        *            The URI of the part to retrieve.\r
+        * @return The package part located by the specified URI, else <b>null</b>.\r
+        */\r
+       protected abstract PackagePart getPartImpl(PackagePartName partName);\r
+\r
+       /**\r
+        * Get all parts link to the package.\r
+        * \r
+        * @return A list of the part owned by the package.\r
+        */\r
+       protected abstract PackagePart[] getPartsImpl()\r
+                       throws InvalidFormatException;\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java
new file mode 100755 (executable)
index 0000000..a40f4ac
--- /dev/null
@@ -0,0 +1,33 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+/**\r
+ * Specifies package access.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public enum PackageAccess {\r
+    /** Read only. Write not authorized. */\r
+    READ,\r
+    /** Write only. Read not authorized. */\r
+    WRITE,\r
+    /** Read and Write mode. */\r
+    READ_WRITE\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java
new file mode 100755 (executable)
index 0000000..5f53781
--- /dev/null
@@ -0,0 +1,52 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+/**\r
+ * Open Packaging Convention namespaces URI.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public interface PackageNamespaces {\r
+\r
+       /**\r
+        * Content Types.\r
+        */\r
+       public static final String CONTENT_TYPES = "http://schemas.openxmlformats.org/package/2006/content-types";\r
+       \r
+       /**\r
+        * Core Properties.\r
+        */\r
+       public static final String CORE_PROPERTIES = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";\r
+       \r
+       /**\r
+        * Digital Signatures.\r
+        */\r
+       public static final String DIGITAL_SIGNATURE = "http://schemas.openxmlformats.org/package/2006/digital-signature";\r
+       \r
+       /**\r
+        * Relationships.\r
+        */\r
+       public static final String RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships";\r
+       \r
+       /**\r
+        * Markup Compatibility.\r
+        */\r
+       public static final String MARKUP_COMPATIBILITY = "http://schemas.openxmlformats.org/markup-compatibility/2006";\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
new file mode 100755 (executable)
index 0000000..34f2682
--- /dev/null
@@ -0,0 +1,654 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.internal.ContentType;\r
+\r
+/**\r
+ * Provides a base class for parts stored in a Package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.9\r
+ */\r
+public abstract class PackagePart implements RelationshipSource {\r
+\r
+       /**\r
+        * This part's container.\r
+        */\r
+       protected Package container;\r
+\r
+       /**\r
+        * The part name. (required by the specification [M1.1])\r
+        */\r
+       protected PackagePartName partName;\r
+\r
+       /**\r
+        * The type of content of this part. (required by the specification [M1.2])\r
+        */\r
+       protected ContentType contentType;\r
+\r
+       /**\r
+        * Flag to know if this part is a relationship.\r
+        */\r
+       private boolean isRelationshipPart;\r
+\r
+       /**\r
+        * Flag to know if this part has been logically deleted.\r
+        */\r
+       private boolean isDeleted;\r
+\r
+       /**\r
+        * This part's relationships.\r
+        */\r
+       private PackageRelationshipCollection relationships;\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param pack\r
+        *            Parent package.\r
+        * @param partName\r
+        *            The part name, relative to the parent Package root.\r
+        * @param contentType\r
+        *            The content type.\r
+        * @throws InvalidFormatException\r
+        *             If the specified URI is not valid.\r
+        */\r
+       protected PackagePart(Package pack, PackagePartName partName,\r
+                       ContentType contentType) throws InvalidFormatException {\r
+               this(pack, partName, contentType, true);\r
+       }\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param pack\r
+        *            Parent package.\r
+        * @param partName\r
+        *            The part name, relative to the parent Package root.\r
+        * @param contentType\r
+        *            The content type.\r
+        * @param loadRelationships\r
+        *            Specify if the relationships will be loaded\r
+        * @throws InvalidFormatException\r
+        *             If the specified URI is not valid.\r
+        */\r
+       protected PackagePart(Package pack, PackagePartName partName,\r
+                       ContentType contentType, boolean loadRelationships)\r
+                       throws InvalidFormatException {\r
+               this.partName = partName;\r
+               this.contentType = contentType;\r
+               this.container = (ZipPackage) pack;\r
+\r
+               // Check if this part is a relationship part\r
+               isRelationshipPart = this.partName.isRelationshipPartURI();\r
+\r
+               // Load relationships if any\r
+               if (loadRelationships)\r
+                       loadRelationships();\r
+       }\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param pack\r
+        *            Parent package.\r
+        * @param partName\r
+        *            The part name, relative to the parent Package root.\r
+        * @param contentType\r
+        *            The Multipurpose Internet Mail Extensions (MIME) content type\r
+        *            of the part's data stream.\r
+        */\r
+       public PackagePart(Package pack, PackagePartName partName,\r
+                       String contentType) throws InvalidFormatException {\r
+               this(pack, partName, new ContentType(contentType));\r
+       }\r
+\r
+       /**\r
+        * Adds an external relationship to a part (except relationships part).\r
+        * \r
+        * The targets of external relationships are not subject to the same\r
+        * validity checks that internal ones are, as the contents is potentially\r
+        * any file, URL or similar.\r
+        * \r
+        * @param target\r
+        *            External target of the relationship\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @return The newly created and added relationship\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,\r
+        *      java.lang.String)\r
+        */\r
+       public PackageRelationship addExternalRelationship(String target,\r
+                       String relationshipType) {\r
+               return addExternalRelationship(target, relationshipType, null);\r
+       }\r
+\r
+       /**\r
+        * Adds an external relationship to a part (except relationships part).\r
+        * \r
+        * The targets of external relationships are not subject to the same\r
+        * validity checks that internal ones are, as the contents is potentially\r
+        * any file, URL or similar.\r
+        * \r
+        * @param target\r
+        *            External target of the relationship\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @param id\r
+        *            Relationship unique id.\r
+        * @return The newly created and added relationship\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,\r
+        *      java.lang.String)\r
+        */\r
+       public PackageRelationship addExternalRelationship(String target,\r
+                       String relationshipType, String id) {\r
+               if (target == null) {\r
+                       throw new IllegalArgumentException("target");\r
+               }\r
+               if (relationshipType == null) {\r
+                       throw new IllegalArgumentException("relationshipType");\r
+               }\r
+\r
+               if (relationships == null) {\r
+                       relationships = new PackageRelationshipCollection();\r
+               }\r
+\r
+               URI targetURI;\r
+               try {\r
+                       targetURI = new URI(target);\r
+               } catch (URISyntaxException e) {\r
+                       throw new IllegalArgumentException("Invalid target - " + e);\r
+               }\r
+\r
+               return relationships.addRelationship(targetURI, TargetMode.EXTERNAL,\r
+                               relationshipType, id);\r
+       }\r
+\r
+       /**\r
+        * Add a relationship to a part (except relationships part).\r
+        * \r
+        * @param targetPartName\r
+        *            Name of the target part. This one must be relative to the\r
+        *            source root directory of the part.\r
+        * @param targetMode\r
+        *            Mode [Internal|External].\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @return The newly created and added relationship\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,\r
+        *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String)\r
+        */\r
+       public PackageRelationship addRelationship(PackagePartName targetPartName,\r
+                       TargetMode targetMode, String relationshipType) {\r
+               return addRelationship(targetPartName, targetMode, relationshipType,\r
+                               null);\r
+       }\r
+\r
+       /**\r
+        * Add a relationship to a part (except relationships part).\r
+        * \r
+        * Check rule M1.25: The Relationships part shall not have relationships to\r
+        * any other part. Package implementers shall enforce this requirement upon\r
+        * the attempt to create such a relationship and shall treat any such\r
+        * relationship as invalid.\r
+        * \r
+        * @param targetPartName\r
+        *            Name of the target part. This one must be relative to the\r
+        *            source root directory of the part.\r
+        * @param targetMode\r
+        *            Mode [Internal|External].\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @param id\r
+        *            Relationship unique id.\r
+        * @return The newly created and added relationship\r
+        * \r
+        * @throws InvalidFormatException\r
+        *             If the URI point to a relationship part URI.\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,\r
+        *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String, java.lang.String)\r
+        */\r
+       public PackageRelationship addRelationship(PackagePartName targetPartName,\r
+                       TargetMode targetMode, String relationshipType, String id) {\r
+               container.throwExceptionIfReadOnly();\r
+\r
+               if (targetPartName == null) {\r
+                       throw new IllegalArgumentException("targetPartName");\r
+               }\r
+               if (targetMode == null) {\r
+                       throw new IllegalArgumentException("targetMode");\r
+               }\r
+               if (relationshipType == null) {\r
+                       throw new IllegalArgumentException("relationshipType");\r
+               }\r
+\r
+               if (this.isRelationshipPart || targetPartName.isRelationshipPartURI()) {\r
+                       throw new InvalidOperationException(\r
+                                       "Rule M1.25: The Relationships part shall not have relationships to any other part.");\r
+               }\r
+\r
+               if (relationships == null) {\r
+                       relationships = new PackageRelationshipCollection();\r
+               }\r
+\r
+               return relationships.addRelationship(targetPartName.getURI(),\r
+                               targetMode, relationshipType, id);\r
+       }\r
+\r
+       /**\r
+        * Add a relationship to a part (except relationships part).\r
+        * \r
+        * @param targetURI\r
+        *            URI the target part. Must be relative to the source root\r
+        *            directory of the part.\r
+        * @param targetMode\r
+        *            Mode [Internal|External].\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @return The newly created and added relationship\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,\r
+        *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String)\r
+        */\r
+       public PackageRelationship addRelationship(URI targetURI,\r
+                       TargetMode targetMode, String relationshipType) {\r
+               return addRelationship(targetURI, targetMode, relationshipType, null);\r
+       }\r
+\r
+       /**\r
+        * Add a relationship to a part (except relationships part).\r
+        * \r
+        * Check rule M1.25: The Relationships part shall not have relationships to\r
+        * any other part. Package implementers shall enforce this requirement upon\r
+        * the attempt to create such a relationship and shall treat any such\r
+        * relationship as invalid.\r
+        * \r
+        * @param targetURI\r
+        *            URI of the target part. Must be relative to the source root\r
+        *            directory of the part.\r
+        * @param targetMode\r
+        *            Mode [Internal|External].\r
+        * @param relationshipType\r
+        *            Type of relationship.\r
+        * @param id\r
+        *            Relationship unique id.\r
+        * @return The newly created and added relationship\r
+        * \r
+        * @throws InvalidFormatException\r
+        *             If the URI point to a relationship part URI.\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,\r
+        *      org.apache.poi.openxml4j.opc.TargetMode, java.lang.String, java.lang.String)\r
+        */\r
+       public PackageRelationship addRelationship(URI targetURI,\r
+                       TargetMode targetMode, String relationshipType, String id) {\r
+               container.throwExceptionIfReadOnly();\r
+\r
+               if (targetURI == null) {\r
+                       throw new IllegalArgumentException("targetPartName");\r
+               }\r
+               if (targetMode == null) {\r
+                       throw new IllegalArgumentException("targetMode");\r
+               }\r
+               if (relationshipType == null) {\r
+                       throw new IllegalArgumentException("relationshipType");\r
+               }\r
+\r
+               // Try to retrieve the target part\r
+\r
+               if (this.isRelationshipPart\r
+                               || PackagingURIHelper.isRelationshipPartURI(targetURI)) {\r
+                       throw new InvalidOperationException(\r
+                                       "Rule M1.25: The Relationships part shall not have relationships to any other part.");\r
+               }\r
+\r
+               if (relationships == null) {\r
+                       relationships = new PackageRelationshipCollection();\r
+               }\r
+\r
+               return relationships.addRelationship(targetURI,\r
+                               targetMode, relationshipType, id);\r
+       }\r
+\r
+       /**\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#clearRelationships()\r
+        */\r
+       public void clearRelationships() {\r
+               if (relationships != null) {\r
+                       relationships.clear();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Delete the relationship specified by its id.\r
+        * \r
+        * @param id\r
+        *            The ID identified the part to delete.\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#removeRelationship(java.lang.String)\r
+        */\r
+       public void removeRelationship(String id) {\r
+               this.container.throwExceptionIfReadOnly();\r
+               if (this.relationships != null)\r
+                       this.relationships.removeRelationship(id);\r
+       }\r
+\r
+       /**\r
+        * Retrieve all the relationships attached to this part.\r
+        * \r
+        * @return This part's relationships.\r
+        * @throws OpenXML4JException\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationships()\r
+        */\r
+       public PackageRelationshipCollection getRelationships()\r
+                       throws InvalidFormatException {\r
+               return getRelationshipsCore(null);\r
+       }\r
+\r
+       /**\r
+        * Retrieves a package relationship from its id.\r
+        * \r
+        * @param id\r
+        *            ID of the package relationship to retrieve.\r
+        * @return The package relationship\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationship(java.lang.String)\r
+        */\r
+       public PackageRelationship getRelationship(String id) {\r
+               return this.relationships.getRelationshipByID(id);\r
+       }\r
+\r
+       /**\r
+        * Retrieve all relationships attached to this part which have the specified\r
+        * type.\r
+        * \r
+        * @param relationshipType\r
+        *            Relationship type filter.\r
+        * @return All relationships from this part that have the specified type.\r
+        * @throws InvalidFormatException\r
+        *             If an error occurs while parsing the part.\r
+        * @throws InvalidOperationException\r
+        *             If the package is open in write only mode.\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationshipsByType(java.lang.String)\r
+        */\r
+       public PackageRelationshipCollection getRelationshipsByType(\r
+                       String relationshipType) throws InvalidFormatException {\r
+               container.throwExceptionIfWriteOnly();\r
+\r
+               return getRelationshipsCore(relationshipType);\r
+       }\r
+\r
+       /**\r
+        * Implementation of the getRelationships method().\r
+        * \r
+        * @param filter\r
+        *            Relationship type filter. If <i>null</i> then the filter is\r
+        *            disabled and return all the relationships.\r
+        * @return All relationships from this part that have the specified type.\r
+        * @throws InvalidFormatException\r
+        *             Throws if an error occurs during parsing the relationships\r
+        *             part.\r
+        * @throws InvalidOperationException\r
+        *             Throws if the package is open en write only mode.\r
+        * @see #getRelationshipsByType(String)\r
+        */\r
+       private PackageRelationshipCollection getRelationshipsCore(String filter)\r
+                       throws InvalidFormatException {\r
+               this.container.throwExceptionIfWriteOnly();\r
+               if (relationships == null) {\r
+                       this.throwExceptionIfRelationship();\r
+                       relationships = new PackageRelationshipCollection(this);\r
+               }\r
+               return new PackageRelationshipCollection(relationships, filter);\r
+       }\r
+\r
+       /**\r
+        * Knows if the part have any relationships.\r
+        * \r
+        * @return <b>true</b> if the part have at least one relationship else\r
+        *         <b>false</b>.\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#hasRelationships()\r
+        */\r
+       public boolean hasRelationships() {\r
+               return (!this.isRelationshipPart && (relationships != null && relationships\r
+                               .size() > 0));\r
+       }\r
+\r
+       /**\r
+        * Checks if the specified relationship is part of this package part.\r
+        * \r
+        * @param rel\r
+        *            The relationship to check.\r
+        * @return <b>true</b> if the specified relationship exists in this part,\r
+        *         else returns <b>false</b>\r
+        * @see org.apache.poi.openxml4j.opc.RelationshipSource#isRelationshipExists(org.apache.poi.openxml4j.opc.PackageRelationship)\r
+        */\r
+       @SuppressWarnings("finally")\r
+       public boolean isRelationshipExists(PackageRelationship rel) {\r
+               try {\r
+                       for (PackageRelationship r : this.getRelationships()) {\r
+                               if (r == rel)\r
+                                       return true;\r
+                       }\r
+               } finally {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Get the input stream of this part to read its content.\r
+        * \r
+        * @return The input stream of the content of this part, else\r
+        *         <code>null</code>.\r
+        */\r
+       public InputStream getInputStream() throws IOException {\r
+               InputStream inStream = this.getInputStreamImpl();\r
+               if (inStream == null) {\r
+                       throw new IOException("Can't obtain the input stream from "\r
+                                       + partName.getName());\r
+               } else\r
+                       return inStream;\r
+       }\r
+\r
+       /**\r
+        * Get the output stream of this part. If the part is originally embedded in\r
+        * Zip package, it'll be transform intot a <i>MemoryPackagePart</i> in\r
+        * order to write inside (the standard Java API doesn't allow to write in\r
+        * the file)\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.internal.MemoryPackagePart\r
+        */\r
+       public OutputStream getOutputStream() {\r
+               OutputStream outStream;\r
+               // If this part is a zip package part (read only by design) we convert\r
+               // this part into a MemoryPackagePart instance for write purpose.\r
+               if (this instanceof ZipPackagePart) {\r
+                       // Delete logically this part\r
+                       this.container.removePart(this.partName);\r
+\r
+                       // Create a memory part\r
+                       PackagePart part = container.createPart(this.partName,\r
+                                       this.contentType.toString(), false);\r
+                       part.relationships = this.relationships;\r
+                       if (part == null) {\r
+                               throw new InvalidOperationException(\r
+                                               "Can't create a temporary part !");\r
+                       }\r
+                       outStream = part.getOutputStreamImpl();\r
+               } else {\r
+                       outStream = this.getOutputStreamImpl();\r
+               }\r
+               return outStream;\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if this package part is a relationship part.\r
+        * \r
+        * @throws InvalidOperationException\r
+        *             If this part is a relationship part.\r
+        */\r
+       private void throwExceptionIfRelationship()\r
+                       throws InvalidOperationException {\r
+               if (this.isRelationshipPart)\r
+                       throw new InvalidOperationException(\r
+                                       "Can do this operation on a relationship part !");\r
+       }\r
+\r
+       /**\r
+        * Ensure the package relationships collection instance is built.\r
+        * \r
+        * @throws InvalidFormatException\r
+        *             Throws if\r
+        */\r
+       private void loadRelationships() throws InvalidFormatException {\r
+               if (this.relationships == null && !this.isRelationshipPart) {\r
+                       this.throwExceptionIfRelationship();\r
+                       relationships = new PackageRelationshipCollection(this);\r
+               }\r
+       }\r
+\r
+       /*\r
+        * Accessors\r
+        */\r
+\r
+       /**\r
+        * @return the uri\r
+        */\r
+       public PackagePartName getPartName() {\r
+               return partName;\r
+       }\r
+\r
+       /**\r
+        * @return the contentType\r
+        */\r
+       public String getContentType() {\r
+               return contentType.toString();\r
+       }\r
+\r
+       /**\r
+        * Set the content type.\r
+        * \r
+        * @param contentType\r
+        *            the contentType to set\r
+        * \r
+        * @throws InvalidFormatException\r
+        *             Throws if the content type is not valid.\r
+        * @throws InvalidOperationException\r
+        *             Throws if you try to change the content type whereas this\r
+        *             part is already attached to a package.\r
+        */\r
+       public void setContentType(String contentType)\r
+                       throws InvalidFormatException {\r
+               if (container == null)\r
+                       this.contentType = new ContentType(contentType);\r
+               else\r
+                       throw new InvalidOperationException(\r
+                                       "You can't change the content type of a part.");\r
+       }\r
+\r
+       public Package getPackage() {\r
+               return container;\r
+       }\r
+\r
+       /**\r
+        * @return\r
+        */\r
+       public boolean isRelationshipPart() {\r
+               return this.isRelationshipPart;\r
+       }\r
+\r
+       /**\r
+        * @return\r
+        */\r
+       public boolean isDeleted() {\r
+               return isDeleted;\r
+       }\r
+\r
+       /**\r
+        * @param isDeleted\r
+        *            the isDeleted to set\r
+        */\r
+       public void setDeleted(boolean isDeleted) {\r
+               this.isDeleted = isDeleted;\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               return "Name: " + this.partName + " - Content Type: "\r
+                               + this.contentType.toString();\r
+       }\r
+\r
+       /*-------------- Abstract methods ------------- */\r
+\r
+       /**\r
+        * Abtract method that get the input stream of this part.\r
+        * \r
+        * @exception IOException\r
+        *                Throws if an IO Exception occur in the implementation\r
+        *                method.\r
+        */\r
+       protected abstract InputStream getInputStreamImpl() throws IOException;\r
+\r
+       /**\r
+        * Abstract method that get the output stream of this part.\r
+        */\r
+       protected abstract OutputStream getOutputStreamImpl();\r
+\r
+       /**\r
+        * Save the content of this part and the associated relationships part (if\r
+        * this part own at least one relationship) into the specified output\r
+        * stream.\r
+        * \r
+        * @param zos\r
+        *            Output stream to save this part.\r
+        * @throws OpenXML4JException\r
+        *             If any exception occur.\r
+        */\r
+       public abstract boolean save(OutputStream zos) throws OpenXML4JException;\r
+\r
+       /**\r
+        * Load the content of this part.\r
+        * \r
+        * @param ios\r
+        *            The input stream of the content to load.\r
+        * @return <b>true</b> if the content has been successfully loaded, else\r
+        *         <b>false</b>.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the content format is invalid.\r
+        */\r
+       public abstract boolean load(InputStream ios) throws InvalidFormatException;\r
+\r
+       /**\r
+        * Close this part : flush this part, close the input stream and output\r
+        * stream. After this method call, the part must be available for packaging.\r
+        */\r
+       public abstract void close();\r
+\r
+       /**\r
+        * Flush the content of this part. If the input stream and/or output stream\r
+        * as in a waiting state to read or write, the must to empty their\r
+        * respective buffer.\r
+        */\r
+       public abstract void flush();\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java
new file mode 100755 (executable)
index 0000000..5402130
--- /dev/null
@@ -0,0 +1,81 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.util.ArrayList;\r
+import java.util.TreeMap;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+\r
+/**\r
+ * A package part collection.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public final class PackagePartCollection extends\r
+               TreeMap<PackagePartName, PackagePart> {\r
+\r
+       private static final long serialVersionUID = 2515031135957635515L;\r
+\r
+       /**\r
+        * Arraylist use to store this collection part names as string for rule\r
+        * M1.11 optimized checking.\r
+        */\r
+       private ArrayList<String> registerPartNameStr = new ArrayList<String>();\r
+\r
+       @Override\r
+       public Object clone() {\r
+               return super.clone();\r
+       }\r
+\r
+       /**\r
+        * Check rule [M1.11]: a package implementer shall neither create nor\r
+        * recognize a part with a part name derived from another part name by\r
+        * appending segments to it.\r
+        * \r
+        * @exception InvalidOperationException\r
+        *                Throws if you try to add a part with a name derived from\r
+        *                another part name.\r
+        */\r
+       @Override\r
+       public PackagePart put(PackagePartName partName, PackagePart part) {\r
+               String[] segments = partName.getURI().toASCIIString().split(\r
+                               PackagingURIHelper.FORWARD_SLASH_STRING);\r
+               StringBuffer concatSeg = new StringBuffer();\r
+               for (String seg : segments) {\r
+                       if (!seg.equals(""))\r
+                               concatSeg.append(PackagingURIHelper.FORWARD_SLASH_CHAR);\r
+                       concatSeg.append(seg);\r
+                       if (this.registerPartNameStr.contains(concatSeg.toString())) {\r
+                               throw new InvalidOperationException(\r
+                                               "You can't add a part with a part name derived from another part ! [M1.11]");\r
+                       }\r
+               }\r
+               this.registerPartNameStr.add(partName.getName());\r
+               return super.put(partName, part);\r
+       }\r
+\r
+       @Override\r
+       public PackagePart remove(Object key) {\r
+               if (key instanceof PackagePartName) {\r
+                       this.registerPartNameStr.remove(((PackagePartName) key).getName());\r
+               }\r
+               return super.remove(key);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartName.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartName.java
new file mode 100755 (executable)
index 0000000..414fd41
--- /dev/null
@@ -0,0 +1,509 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;\r
+\r
+/**\r
+ * An immutable Open Packaging Convention compliant part name.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ * \r
+ * @see http://www.ietf.org/rfc/rfc3986.txt\r
+ */\r
+public final class PackagePartName implements Comparable<PackagePartName> {\r
+\r
+       /**\r
+        * Part name stored as an URI.\r
+        */\r
+       private URI partNameURI;\r
+\r
+       /*\r
+        * URI Characters definition (RFC 3986)\r
+        */\r
+\r
+       /**\r
+        * Reserved characters for sub delimitations.\r
+        */\r
+       private static String[] RFC3986_PCHAR_SUB_DELIMS = { "!", "$", "&", "'",\r
+                       "(", ")", "*", "+", ",", ";", "=" };\r
+\r
+       /**\r
+        * Unreserved character (+ ALPHA & DIGIT).\r
+        */\r
+       private static String[] RFC3986_PCHAR_UNRESERVED_SUP = { "-", ".", "_", "~" };\r
+\r
+       /**\r
+        * Authorized reserved characters for pChar.\r
+        */\r
+       private static String[] RFC3986_PCHAR_AUTHORIZED_SUP = { ":", "@" };\r
+\r
+       /**\r
+        * Flag to know if this part name is from a relationship part name.\r
+        */\r
+       private boolean isRelationship;\r
+\r
+       /**\r
+        * Constructor. Makes a ValidPartName object from a java.net.URI\r
+        * \r
+        * @param uri\r
+        *            The URI to validate and to transform into ValidPartName.\r
+        * @param checkConformance\r
+        *            Flag to specify if the contructor have to validate the OPC\r
+        *            conformance. Must be always <code>true</code> except for\r
+        *            special URI like '/' which is needed for internal use by\r
+        *            OpenXML4J but is not valid.\r
+        * @throws InvalidFormatException\r
+        *             Throw if the specified part name is not conform to Open\r
+        *             Packaging Convention specifications.\r
+        * @see java.net.URI\r
+        */\r
+       PackagePartName(URI uri, boolean checkConformance)\r
+                       throws InvalidFormatException {\r
+               if (checkConformance) {\r
+                       throwExceptionIfInvalidPartUri(uri);\r
+               } else {\r
+                       if (!PackagingURIHelper.PACKAGE_ROOT_URI.equals(uri)) {\r
+                               throw new OpenXML4JRuntimeException(\r
+                                               "OCP conformance must be check for ALL part name except special cases : ['/']");\r
+                       }\r
+               }\r
+               this.partNameURI = uri;\r
+               this.isRelationship = isRelationshipPartURI(this.partNameURI);\r
+       }\r
+\r
+       /**\r
+        * Constructor. Makes a ValidPartName object from a String part name.\r
+        * \r
+        * @param partName\r
+        *            Part name to valid and to create.\r
+        * @param checkConformance\r
+        *            Flag to specify if the contructor have to validate the OPC\r
+        *            conformance. Must be always <code>true</code> except for\r
+        *            special URI like '/' which is needed for internal use by\r
+        *            OpenXML4J but is not valid.\r
+        * @throws InvalidFormatException\r
+        *             Throw if the specified part name is not conform to Open\r
+        *             Packaging Convention specifications.\r
+        */\r
+       PackagePartName(String partName, boolean checkConformance)\r
+                       throws InvalidFormatException {\r
+               URI partURI;\r
+               try {\r
+                       partURI = new URI(partName);\r
+               } catch (URISyntaxException e) {\r
+                       throw new IllegalArgumentException(\r
+                                       "partName argmument is not a valid OPC part name !");\r
+               }\r
+\r
+               if (checkConformance) {\r
+                       throwExceptionIfInvalidPartUri(partURI);\r
+               } else {\r
+                       if (!PackagingURIHelper.PACKAGE_ROOT_URI.equals(partURI)) {\r
+                               throw new OpenXML4JRuntimeException(\r
+                                               "OCP conformance must be check for ALL part name except special cases : ['/']");\r
+                       }\r
+               }\r
+               this.partNameURI = partURI;\r
+               this.isRelationship = isRelationshipPartURI(this.partNameURI);\r
+       }\r
+\r
+       /**\r
+        * Check if the specified part name is a relationship part name.\r
+        * \r
+        * @param partUri\r
+        *            The URI to check.\r
+        * @return <code>true</code> if this part name respect the relationship\r
+        *         part naming convention else <code>false</code>.\r
+        */\r
+       private boolean isRelationshipPartURI(URI partUri) {\r
+               if (partUri == null)\r
+                       throw new IllegalArgumentException("partUri");\r
+\r
+               return partUri.getPath().matches(\r
+                               "^.*/" + PackagingURIHelper.RELATIONSHIP_PART_SEGMENT_NAME + "/.*\\"\r
+                                               + PackagingURIHelper.RELATIONSHIP_PART_EXTENSION_NAME\r
+                                               + "$");\r
+       }\r
+\r
+       /**\r
+        * Know if this part name is a relationship part name.\r
+        * \r
+        * @return <code>true</code> if this part name respect the relationship\r
+        *         part naming convention else <code>false</code>.\r
+        */\r
+       public boolean isRelationshipPartURI() {\r
+               return this.isRelationship;\r
+       }\r
+\r
+       /**\r
+        * Throws an exception (of any kind) if the specified part name does not\r
+        * follow the Open Packaging Convention specifications naming rules.\r
+        * \r
+        * @param partUri\r
+        *            The part name to check.\r
+        * @throws Exception\r
+        *             Throws if the part name is invalid.\r
+        */\r
+       private static void throwExceptionIfInvalidPartUri(URI partUri)\r
+                       throws InvalidFormatException {\r
+               if (partUri == null)\r
+                       throw new IllegalArgumentException("partUri");\r
+               // Check if the part name URI is empty [M1.1]\r
+               throwExceptionIfEmptyURI(partUri);\r
+\r
+               // Check if the part name URI is absolute\r
+               throwExceptionIfAbsoluteUri(partUri);\r
+\r
+               // Check if the part name URI starts with a forward slash [M1.4]\r
+               throwExceptionIfPartNameNotStartsWithForwardSlashChar(partUri);\r
+\r
+               // Check if the part name URI ends with a forward slash [M1.5]\r
+               throwExceptionIfPartNameEndsWithForwardSlashChar(partUri);\r
+\r
+               // Check if the part name does not have empty segments. [M1.3]\r
+               // Check if a segment ends with a dot ('.') character. [M1.9]\r
+               throwExceptionIfPartNameHaveInvalidSegments(partUri);\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if the specified URI is empty. [M1.1]\r
+        * \r
+        * @param partURI\r
+        *            Part URI to check.\r
+        * @throws InvalidFormatException\r
+        *             If the specified URI is empty.\r
+        */\r
+       private static void throwExceptionIfEmptyURI(URI partURI)\r
+                       throws InvalidFormatException {\r
+               if (partURI == null)\r
+                       throw new IllegalArgumentException("partURI");\r
+\r
+               String uriPath = partURI.getPath();\r
+               if (uriPath.length() == 0\r
+                               || ((uriPath.length() == 1) && (uriPath.charAt(0) == PackagingURIHelper.FORWARD_SLASH_CHAR)))\r
+                       throw new InvalidFormatException(\r
+                                       "A part name shall not be empty [M1.1]: "\r
+                                                       + partURI.getPath());\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if the part name has empty segments. [M1.3]\r
+        * \r
+        * Throws an exception if a segment any characters other than pchar\r
+        * characters. [M1.6]\r
+        * \r
+        * Throws an exception if a segment contain percent-encoded forward slash\r
+        * ('/'), or backward slash ('\') characters. [M1.7]\r
+        * \r
+        * Throws an exception if a segment contain percent-encoded unreserved\r
+        * characters. [M1.8]\r
+        * \r
+        * Throws an exception if the specified part name's segments end with a dot\r
+        * ('.') character. [M1.9]\r
+        * \r
+        * Throws an exception if a segment doesn't include at least one non-dot\r
+        * character. [M1.10]\r
+        * \r
+        * @param partUri\r
+        *            The part name to check.\r
+        * @throws InvalidFormatException\r
+        *             if the specified URI contain an empty segments or if one the\r
+        *             segments contained in the part name, ends with a dot ('.')\r
+        *             character.\r
+        */\r
+       private static void throwExceptionIfPartNameHaveInvalidSegments(URI partUri)\r
+                       throws InvalidFormatException {\r
+               if (partUri == null || "".equals(partUri)) {\r
+                       throw new IllegalArgumentException("partUri");\r
+               }\r
+\r
+               // Split the URI into several part and analyze each\r
+               String[] segments = partUri.toASCIIString().split("/");\r
+               if (segments.length <= 1 || !segments[0].equals(""))\r
+                       throw new InvalidFormatException(\r
+                                       "A part name shall not have empty segments [M1.3]: "\r
+                                                       + partUri.getPath());\r
+\r
+               for (int i = 1; i < segments.length; ++i) {\r
+                       String seg = segments[i];\r
+                       if (seg == null || "".equals(seg)) {\r
+                               throw new InvalidFormatException(\r
+                                               "A part name shall not have empty segments [M1.3]: "\r
+                                                               + partUri.getPath());\r
+                       }\r
+\r
+                       if (seg.endsWith(".")) {\r
+                               throw new InvalidFormatException(\r
+                                               "A segment shall not end with a dot ('.') character [M1.9]: "\r
+                                                               + partUri.getPath());\r
+                       }\r
+\r
+                       if ("".equals(seg.replaceAll("\\\\.", ""))) {\r
+                               // Normally will never been invoked with the previous\r
+                               // implementation rule [M1.9]\r
+                               throw new InvalidFormatException(\r
+                                               "A segment shall include at least one non-dot character. [M1.10]: "\r
+                                                               + partUri.getPath());\r
+                       }\r
+\r
+                       /*\r
+                        * Check for rule M1.6, M1.7, M1.8\r
+                        */\r
+                       checkPCharCompliance(seg);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if a segment any characters other than pchar\r
+        * characters. [M1.6]\r
+        * \r
+        * Throws an exception if a segment contain percent-encoded forward slash\r
+        * ('/'), or backward slash ('\') characters. [M1.7]\r
+        * \r
+        * Throws an exception if a segment contain percent-encoded unreserved\r
+        * characters. [M1.8]\r
+        * \r
+        * @param segment\r
+        *            The segment to check\r
+        */\r
+       private static void checkPCharCompliance(String segment)\r
+                       throws InvalidFormatException {\r
+               boolean errorFlag;\r
+               for (int i = 0; i < segment.length(); ++i) {\r
+                       char c = segment.charAt(i);\r
+                       errorFlag = true;\r
+\r
+                       /* Check rule M1.6 */\r
+\r
+                       // Check for digit or letter\r
+                       if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')\r
+                                       || (c >= '0' && c <= '9')) {\r
+                               errorFlag = false;\r
+                       } else {\r
+                               // Check "-", ".", "_", "~"\r
+                               for (int j = 0; j < RFC3986_PCHAR_UNRESERVED_SUP.length; ++j) {\r
+                                       if (c == RFC3986_PCHAR_UNRESERVED_SUP[j].charAt(0)) {\r
+                                               errorFlag = false;\r
+                                               break;\r
+                                       }\r
+                               }\r
+\r
+                               // Check ":", "@"\r
+                               for (int j = 0; errorFlag\r
+                                               && j < RFC3986_PCHAR_AUTHORIZED_SUP.length; ++j) {\r
+                                       if (c == RFC3986_PCHAR_AUTHORIZED_SUP[j].charAt(0)) {\r
+                                               errorFlag = false;\r
+                                       }\r
+                               }\r
+\r
+                               // Check "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="\r
+                               for (int j = 0; errorFlag\r
+                                               && j < RFC3986_PCHAR_SUB_DELIMS.length; ++j) {\r
+                                       if (c == RFC3986_PCHAR_SUB_DELIMS[j].charAt(0)) {\r
+                                               errorFlag = false;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (errorFlag && c == '%') {\r
+                               // We certainly found an encoded character, check for length\r
+                               // now ( '%' HEXDIGIT HEXDIGIT)\r
+                               if (((segment.length() - i) < 2)) {\r
+                                       throw new InvalidFormatException("The segment " + segment\r
+                                                       + " contain invalid encoded character !");\r
+                               }\r
+\r
+                               // If not percent encoded character error occur then reset the\r
+                               // flag -> the character is valid\r
+                               errorFlag = false;\r
+\r
+                               // Decode the encoded character\r
+                               char decodedChar = (char) Integer.parseInt(segment.substring(\r
+                                               i + 1, i + 3), 16);\r
+                               i += 2;\r
+\r
+                               /* Check rule M1.7 */\r
+                               if (decodedChar == '/' || decodedChar == '\\')\r
+                                       throw new InvalidFormatException(\r
+                                                       "A segment shall not contain percent-encoded forward slash ('/'), or backward slash ('\') characters. [M1.7]");\r
+\r
+                               /* Check rule M1.8 */\r
+\r
+                               // Check for unreserved character like define in RFC3986\r
+                               if ((decodedChar >= 'A' && decodedChar <= 'Z')\r
+                                               || (decodedChar >= 'a' && decodedChar <= 'z')\r
+                                               || (decodedChar >= '0' && decodedChar <= '9'))\r
+                                       errorFlag = true;\r
+\r
+                               // Check for unreserved character "-", ".", "_", "~"\r
+                               for (int j = 0; !errorFlag\r
+                                               && j < RFC3986_PCHAR_UNRESERVED_SUP.length; ++j) {\r
+                                       if (c == RFC3986_PCHAR_UNRESERVED_SUP[j].charAt(0)) {\r
+                                               errorFlag = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                               if (errorFlag)\r
+                                       throw new InvalidFormatException(\r
+                                                       "A segment shall not contain percent-encoded unreserved characters. [M1.8]");\r
+                       }\r
+\r
+                       if (errorFlag)\r
+                               throw new InvalidFormatException(\r
+                                               "A segment shall not hold any characters other than pchar characters. [M1.6]");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if the specified part name doesn't start with a\r
+        * forward slash character '/'. [M1.4]\r
+        * \r
+        * @param partUri\r
+        *            The part name to check.\r
+        * @throws InvalidFormatException\r
+        *             If the specified part name doesn't start with a forward slash\r
+        *             character '/'.\r
+        */\r
+       private static void throwExceptionIfPartNameNotStartsWithForwardSlashChar(\r
+                       URI partUri) throws InvalidFormatException {\r
+               String uriPath = partUri.getPath();\r
+               if (uriPath.length() > 0\r
+                               && uriPath.charAt(0) != PackagingURIHelper.FORWARD_SLASH_CHAR)\r
+                       throw new InvalidFormatException(\r
+                                       "A part name shall start with a forward slash ('/') character [M1.4]: "\r
+                                                       + partUri.getPath());\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if the specified part name ends with a forwar slash\r
+        * character '/'. [M1.5]\r
+        * \r
+        * @param partUri\r
+        *            The part name to check.\r
+        * @throws InvalidFormatException\r
+        *             If the specified part name ends with a forwar slash character\r
+        *             '/'.\r
+        */\r
+       private static void throwExceptionIfPartNameEndsWithForwardSlashChar(\r
+                       URI partUri) throws InvalidFormatException {\r
+               String uriPath = partUri.getPath();\r
+               if (uriPath.length() > 0\r
+                               && uriPath.charAt(uriPath.length() - 1) == PackagingURIHelper.FORWARD_SLASH_CHAR)\r
+                       throw new InvalidFormatException(\r
+                                       "A part name shall not have a forward slash as the last character [M1.5]: "\r
+                                                       + partUri.getPath());\r
+       }\r
+\r
+       /**\r
+        * Throws an exception if the specified URI is absolute.\r
+        * \r
+        * @param partUri\r
+        *            The URI to check.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the specified URI is absolute.\r
+        */\r
+       private static void throwExceptionIfAbsoluteUri(URI partUri)\r
+                       throws InvalidFormatException {\r
+               if (partUri.isAbsolute())\r
+                       throw new InvalidFormatException("Absolute URI forbidden: "\r
+                                       + partUri);\r
+       }\r
+\r
+       /**\r
+        * Compare two part name following the rule M1.12 :\r
+        * \r
+        * Part name equivalence is determined by comparing part names as\r
+        * case-insensitive ASCII strings. Packages shall not contain equivalent\r
+        * part names and package implementers shall neither create nor recognize\r
+        * packages with equivalent part names. [M1.12]\r
+        */\r
+       public int compareTo(PackagePartName otherPartName) {\r
+               if (otherPartName == null)\r
+                       return -1;\r
+               return this.partNameURI.toASCIIString().toLowerCase().compareTo(\r
+                               otherPartName.partNameURI.toASCIIString().toLowerCase());\r
+       }\r
+\r
+       /**\r
+        * Retrieves the extension of the part name if any. If there is no extension\r
+        * returns an empty String. Example : '/document/content.xml' => 'xml'\r
+        * \r
+        * @return The extension of the part name.\r
+        */\r
+       public String getExtension() {\r
+               String fragment = this.partNameURI.getPath();\r
+               if (fragment.length() > 0) {\r
+                       int i = fragment.lastIndexOf(".");\r
+                       if (i > -1)\r
+                               return fragment.substring(i + 1);\r
+               }\r
+               return "";\r
+       }\r
+\r
+       /**\r
+        * Get this part name.\r
+        * \r
+        * @return The name of this part name.\r
+        */\r
+       public String getName() {\r
+               return this.partNameURI.toASCIIString();\r
+       }\r
+\r
+       /**\r
+        * Part name equivalence is determined by comparing part names as\r
+        * case-insensitive ASCII strings. Packages shall not contain equivalent\r
+        * part names and package implementers shall neither create nor recognize\r
+        * packages with equivalent part names. [M1.12]\r
+        */\r
+       @Override\r
+       public boolean equals(Object otherPartName) {\r
+               if (otherPartName == null\r
+                               || !(otherPartName instanceof PackagePartName))\r
+                       return false;\r
+               return this.partNameURI.toASCIIString().toLowerCase().equals(\r
+                               ((PackagePartName) otherPartName).partNameURI.toASCIIString()\r
+                                               .toLowerCase());\r
+       }\r
+\r
+       @Override\r
+       public int hashCode() {\r
+               return this.partNameURI.toASCIIString().toLowerCase().hashCode();\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               return getName();\r
+       }\r
+\r
+       /* Getters and setters */\r
+\r
+       /**\r
+        * Part name property getter.\r
+        * \r
+        * @return This part name URI.\r
+        */\r
+       public URI getURI() {\r
+               return this.partNameURI;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageProperties.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageProperties.java
new file mode 100755 (executable)
index 0000000..5ab80dd
--- /dev/null
@@ -0,0 +1,227 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.util.Date;\r
+\r
+import org.apache.poi.openxml4j.util.Nullable;\r
+\r
+/**\r
+ * Represents the core properties of an OPC package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ * @see org.apache.poi.openxml4j.opc.Package\r
+ */\r
+public interface PackageProperties {\r
+       \r
+       /**\r
+        * Dublin Core Terms URI.\r
+        */\r
+       public final static String NAMESPACE_DCTERMS = "http://purl.org/dc/terms/";\r
+       \r
+       /**\r
+        * Dublin Core namespace URI.\r
+        */\r
+       public final static String NAMESPACE_DC = "http://purl.org/dc/elements/1.1/";\r
+\r
+       /* Getters and setters */\r
+\r
+       /**\r
+        * Set the category of the content of this package.\r
+        */\r
+       public abstract Nullable<String> getCategoryProperty();\r
+\r
+       /**\r
+        * Set the category of the content of this package.\r
+        */\r
+       public abstract void setCategoryProperty(String category);\r
+\r
+       /**\r
+        * Set the status of the content.\r
+        */\r
+       public abstract Nullable<String> getContentStatusProperty();\r
+\r
+       /**\r
+        * Get the status of the content.\r
+        */\r
+       public abstract void setContentStatusProperty(String contentStatus);\r
+\r
+       /**\r
+        * Get the type of content represented, generally defined by a specific use\r
+        * and intended audience.\r
+        */\r
+       public abstract Nullable<String> getContentTypeProperty();\r
+\r
+       /**\r
+        * Set the type of content represented, generally defined by a specific use\r
+        * and intended audience.\r
+        */\r
+       public abstract void setContentTypeProperty(String contentType);\r
+\r
+       /**\r
+        * Get the date of creation of the resource.\r
+        */\r
+       public abstract Nullable<Date> getCreatedProperty();\r
+\r
+       /**\r
+        * Set the date of creation of the resource.\r
+        */\r
+       public abstract void setCreatedProperty(String created);\r
+       \r
+       /**\r
+        * Set the date of creation of the resource.\r
+        */\r
+       public abstract void setCreatedProperty(Nullable<Date> created);\r
+\r
+       /**\r
+        * Get the entity primarily responsible for making the content of the\r
+        * resource.\r
+        */\r
+       public abstract Nullable<String> getCreatorProperty();\r
+\r
+       /**\r
+        * Set the entity primarily responsible for making the content of the\r
+        * resource.\r
+        */\r
+       public abstract void setCreatorProperty(String creator);\r
+\r
+       /**\r
+        * Get the explanation of the content of the resource.\r
+        */\r
+       public abstract Nullable<String> getDescriptionProperty();\r
+\r
+       /**\r
+        * Set the explanation of the content of the resource.\r
+        */\r
+       public abstract void setDescriptionProperty(String description);\r
+\r
+       /**\r
+        * Get an unambiguous reference to the resource within a given context.\r
+        */\r
+       public abstract Nullable<String> getIdentifierProperty();\r
+\r
+       /**\r
+        * Set an unambiguous reference to the resource within a given context.\r
+        */\r
+       public abstract void setIdentifierProperty(String identifier);\r
+\r
+       /**\r
+        * Get a delimited set of keywords to support searching and indexing. This\r
+        * is typically a list of terms that are not available elsewhere in the\r
+        * properties\r
+        */\r
+       public abstract Nullable<String> getKeywordsProperty();\r
+\r
+       /**\r
+        * Set a delimited set of keywords to support searching and indexing. This\r
+        * is typically a list of terms that are not available elsewhere in the\r
+        * properties\r
+        */\r
+       public abstract void setKeywordsProperty(String keywords);\r
+\r
+       /**\r
+        * Get the language of the intellectual content of the resource.\r
+        */\r
+       public abstract Nullable<String> getLanguageProperty();\r
+\r
+       /**\r
+        * Set the language of the intellectual content of the resource.\r
+        */\r
+       public abstract void setLanguageProperty(String language);\r
+\r
+       /**\r
+        * Get the user who performed the last modification.\r
+        */\r
+       public abstract Nullable<String> getLastModifiedByProperty();\r
+\r
+       /**\r
+        * Set the user who performed the last modification.\r
+        */\r
+       public abstract void setLastModifiedByProperty(String lastModifiedBy);\r
+\r
+       /**\r
+        * Get the date and time of the last printing.\r
+        */\r
+       public abstract Nullable<Date> getLastPrintedProperty();\r
+\r
+       /**\r
+        * Set the date and time of the last printing.\r
+        */\r
+       public abstract void setLastPrintedProperty(String lastPrinted);\r
+       \r
+       /**\r
+        * Set the date and time of the last printing.\r
+        */\r
+       public abstract void setLastPrintedProperty(Nullable<Date> lastPrinted);\r
+\r
+       /**\r
+        * Get the date on which the resource was changed.\r
+        */\r
+       public abstract Nullable<Date> getModifiedProperty();\r
+\r
+       /**\r
+        * Set the date on which the resource was changed.\r
+        */\r
+       public abstract void setModifiedProperty(String modified);\r
+       \r
+       /**\r
+        * Set the date on which the resource was changed.\r
+        */\r
+       public abstract void setModifiedProperty(Nullable<Date> modified);\r
+\r
+       /**\r
+        * Get the revision number.\r
+        */\r
+       public abstract Nullable<String> getRevisionProperty();\r
+\r
+       /**\r
+        * Set the revision number.\r
+        */\r
+       public abstract void setRevisionProperty(String revision);\r
+\r
+       /**\r
+        * Get the topic of the content of the resource.\r
+        */\r
+       public abstract Nullable<String> getSubjectProperty();\r
+\r
+       /**\r
+        * Set the topic of the content of the resource.\r
+        */\r
+       public abstract void setSubjectProperty(String subject);\r
+\r
+       /**\r
+        * Get the name given to the resource.\r
+        */\r
+       public abstract Nullable<String> getTitleProperty();\r
+\r
+       /**\r
+        * Set the name given to the resource.\r
+        */\r
+       public abstract void setTitleProperty(String title);\r
+\r
+       /**\r
+        * Get the version number.\r
+        */\r
+       public abstract Nullable<String> getVersionProperty();\r
+\r
+       /**\r
+        * Set the version number.\r
+        */\r
+       public abstract void setVersionProperty(String version);\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationship.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationship.java
new file mode 100755 (executable)
index 0000000..efaa093
--- /dev/null
@@ -0,0 +1,227 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+\r
+/**\r
+ * A part relationship.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public final class PackageRelationship {\r
+\r
+       private static URI containerRelationshipPart;\r
+\r
+       static {\r
+               try {\r
+                       containerRelationshipPart = new URI("/_rels/.rels");\r
+               } catch (URISyntaxException e) {\r
+                       // Do nothing\r
+               }\r
+       }\r
+\r
+       /* XML markup */\r
+\r
+       public static final String ID_ATTRIBUTE_NAME = "Id";\r
+\r
+       public static final String RELATIONSHIPS_TAG_NAME = "Relationships";\r
+\r
+       public static final String RELATIONSHIP_TAG_NAME = "Relationship";\r
+\r
+       public static final String TARGET_ATTRIBUTE_NAME = "Target";\r
+\r
+       public static final String TARGET_MODE_ATTRIBUTE_NAME = "TargetMode";\r
+\r
+       public static final String TYPE_ATTRIBUTE_NAME = "Type";\r
+\r
+       /* End XML markup */\r
+\r
+       /**\r
+        * L'ID de la relation.\r
+        */\r
+       private String id;\r
+\r
+       /**\r
+        * R�f�rence vers le package.\r
+        */\r
+       private Package container;\r
+\r
+       /**\r
+        * Type de relation.\r
+        */\r
+       private String relationshipType;\r
+\r
+       /**\r
+        * Partie source de cette relation.\r
+        */\r
+       private PackagePart source;\r
+\r
+       /**\r
+        * Le mode de ciblage [Internal|External]\r
+        */\r
+       private TargetMode targetMode;\r
+\r
+       /**\r
+        * URI de la partie cible.\r
+        */\r
+       private URI targetUri;\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param packageParent\r
+        * @param sourcePart\r
+        * @param targetUri\r
+        * @param targetMode\r
+        * @param relationshipType\r
+        * @param id\r
+        */\r
+       public PackageRelationship(Package pkg, PackagePart sourcePart,\r
+                       URI targetUri, TargetMode targetMode, String relationshipType,\r
+                       String id) {\r
+               if (pkg == null)\r
+                       throw new IllegalArgumentException("pkg");\r
+               if (targetUri == null)\r
+                       throw new IllegalArgumentException("targetUri");\r
+               if (relationshipType == null)\r
+                       throw new IllegalArgumentException("relationshipType");\r
+               if (id == null)\r
+                       throw new IllegalArgumentException("id");\r
+\r
+               this.container = pkg;\r
+               this.source = sourcePart;\r
+               this.targetUri = targetUri;\r
+               this.targetMode = targetMode;\r
+               this.relationshipType = relationshipType;\r
+               this.id = id;\r
+       }\r
+\r
+       @Override\r
+       public boolean equals(Object obj) {\r
+               if (!(obj instanceof PackageRelationship)) {\r
+                       return false;\r
+               }\r
+               PackageRelationship rel = (PackageRelationship) obj;\r
+               return (this.id == rel.id\r
+                               && this.relationshipType == rel.relationshipType\r
+                               && (rel.source != null ? rel.source.equals(this.source) : true)\r
+                               && this.targetMode == rel.targetMode && this.targetUri\r
+                               .equals(rel.targetUri));\r
+       }\r
+\r
+       @Override\r
+       public int hashCode() {\r
+               return this.id.hashCode() + this.relationshipType.hashCode()\r
+                               + this.source.hashCode() + this.targetMode.hashCode()\r
+                               + this.targetUri.hashCode();\r
+       }\r
+\r
+       /* Getters */\r
+\r
+       public URI getContainerPartRelationship() {\r
+               return containerRelationshipPart;\r
+       }\r
+\r
+       /**\r
+        * @return the container\r
+        */\r
+       public Package getPackage() {\r
+               return container;\r
+       }\r
+\r
+       /**\r
+        * @return the id\r
+        */\r
+       public String getId() {\r
+               return id;\r
+       }\r
+\r
+       /**\r
+        * @return the relationshipType\r
+        */\r
+       public String getRelationshipType() {\r
+               return relationshipType;\r
+       }\r
+\r
+       /**\r
+        * @return the source\r
+        */\r
+       public PackagePart getSource() {\r
+               return source;\r
+       }\r
+\r
+       /**\r
+        * \r
+        * @return\r
+        */\r
+       public URI getSourceURI() {\r
+               if (source == null) {\r
+                       return PackagingURIHelper.PACKAGE_ROOT_URI;\r
+               }\r
+               return source.partName.getURI();\r
+       }\r
+\r
+       /**\r
+        * public URI getSourceUri(){ }\r
+        * \r
+        * @return the targetMode\r
+        */\r
+       public TargetMode getTargetMode() {\r
+               return targetMode;\r
+       }\r
+\r
+       /**\r
+        * @return the targetUri\r
+        */\r
+       public URI getTargetURI() {\r
+               // If it's an external target, we don't\r
+               //  need to apply our normal validation rules\r
+               if(targetMode == TargetMode.EXTERNAL) {\r
+                       return targetUri;\r
+               }\r
+               \r
+               // Internal target\r
+               // If it isn't absolute, resolve it relative\r
+               //  to ourselves\r
+               if (!targetUri.toASCIIString().startsWith("/")) {\r
+                       // So it's a relative part name, try to resolve it\r
+                       return PackagingURIHelper.resolvePartUri(getSourceURI(), targetUri);\r
+               }\r
+               return targetUri;\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append(id == null ? "id=null" : "id=" + id);\r
+               sb.append(container == null ? " - container=null" : " - container="\r
+                               + container.toString());\r
+               sb.append(relationshipType == null ? " - relationshipType=null"\r
+                               : " - relationshipType=" + relationshipType.toString());\r
+               sb.append(source == null ? " - source=null" : " - source="\r
+                               + getSourceURI().toASCIIString());\r
+               sb.append(targetUri == null ? " - target=null" : " - target="\r
+                               + getTargetURI().toASCIIString());\r
+               sb.append(targetMode == null ? ",targetMode=null" : ",targetMode="\r
+                               + targetMode.toString());\r
+               return sb.toString();\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java
new file mode 100755 (executable)
index 0000000..daf2e7e
--- /dev/null
@@ -0,0 +1,450 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.TreeMap;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.dom4j.Attribute;\r
+import org.dom4j.Document;\r
+import org.dom4j.Element;\r
+import org.dom4j.io.SAXReader;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+\r
+/**\r
+ * Represents a collection of PackageRelationship elements that are owned by a\r
+ * given PackagePart or the Package.\r
+ * \r
+ * @author Julien Chable, CDubettier\r
+ * @version 0.1\r
+ */\r
+public final class PackageRelationshipCollection implements\r
+               Iterable<PackageRelationship> {\r
+\r
+       private static Logger logger = Logger.getLogger("org.openxml4j.opc");\r
+\r
+       /**\r
+        * Package relationships ordered by ID.\r
+        */\r
+       private TreeMap<String, PackageRelationship> relationshipsByID;\r
+\r
+       /**\r
+        * Package relationships ordered by type.\r
+        */\r
+       private TreeMap<String, PackageRelationship> relationshipsByType;\r
+\r
+       /**\r
+        * This relationshipPart.\r
+        */\r
+       private PackagePart relationshipPart;\r
+\r
+       /**\r
+        * Source part.\r
+        */\r
+       private PackagePart sourcePart;\r
+\r
+       /**\r
+        * This part name.\r
+        */\r
+       private PackagePartName partName;\r
+\r
+       /**\r
+        * Reference to the package.\r
+        */\r
+       private Package container;\r
+\r
+       /**\r
+        * Constructor.\r
+        */\r
+       PackageRelationshipCollection() {\r
+               relationshipsByID = new TreeMap<String, PackageRelationship>();\r
+               relationshipsByType = new TreeMap<String, PackageRelationship>();\r
+       }\r
+\r
+       /**\r
+        * Copy constructor.\r
+        * \r
+        * This collection will contain only elements from the specified collection\r
+        * for which the type is compatible with the specified relationship type\r
+        * filter.\r
+        * \r
+        * @param coll\r
+        *            Collection to import.\r
+        * @param filter\r
+        *            Relationship type filter.\r
+        */\r
+       public PackageRelationshipCollection(PackageRelationshipCollection coll,\r
+                       String filter) {\r
+               this();\r
+               for (PackageRelationship rel : coll.relationshipsByID.values()) {\r
+                       if (filter == null || rel.getRelationshipType().equals(filter))\r
+                               addRelationship(rel);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Constructor.\r
+        */\r
+       public PackageRelationshipCollection(Package container)\r
+                       throws InvalidFormatException {\r
+               this(container, null);\r
+       }\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @throws InvalidFormatException\r
+        *             Throws if the format of the content part is invalid.\r
+        * \r
+        * @throws InvalidOperationException\r
+        *             Throws if the specified part is a relationship part.\r
+        */\r
+       public PackageRelationshipCollection(PackagePart part)\r
+                       throws InvalidFormatException {\r
+               this(part.container, part);\r
+       }\r
+\r
+       /**\r
+        * Constructor. Parse the existing package relationship part if one exists.\r
+        * \r
+        * @param container\r
+        *            The parent package.\r
+        * @param part\r
+        *            The part that own this relationships collection. If <b>null</b>\r
+        *            then this part is considered as the package root.\r
+        * @throws InvalidFormatException\r
+        *             If an error occurs during the parsing of the relatinships\r
+        *             part fo the specified part.\r
+        */\r
+       public PackageRelationshipCollection(Package container, PackagePart part)\r
+                       throws InvalidFormatException {\r
+               this();\r
+\r
+               if (container == null)\r
+                       throw new IllegalArgumentException("container");\r
+\r
+               // Check if the specified part is not a relationship part\r
+               if (part != null && part.isRelationshipPart())\r
+                       throw new IllegalArgumentException("part");\r
+\r
+               this.container = container;\r
+               this.sourcePart = part;\r
+               this.partName = getRelationshipPartName(part);\r
+               if ((container.getPackageAccess() != PackageAccess.WRITE)\r
+                               && container.containPart(this.partName)) {\r
+                       relationshipPart = container.getPart(this.partName);\r
+                       parseRelationshipsPart(relationshipPart);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Get the relationship part name of the specified part.\r
+        * \r
+        * @param part\r
+        *            The part .\r
+        * @return The relationship part name of the specified part. Be careful,\r
+        *         only the correct name is returned, this method does not check if\r
+        *         the part really exist in a package !\r
+        * @throws InvalidOperationException\r
+        *             Throws if the specified part is a relationship part.\r
+        */\r
+       private static PackagePartName getRelationshipPartName(PackagePart part)\r
+                       throws InvalidOperationException {\r
+               PackagePartName partName;\r
+               if (part == null) {\r
+                       partName = PackagingURIHelper.PACKAGE_ROOT_PART_NAME;\r
+               } else {\r
+                       partName = part.getPartName();\r
+               }\r
+               return PackagingURIHelper.getRelationshipPartName(partName);\r
+       }\r
+\r
+       /**\r
+        * Add the specified relationship to the collection.\r
+        * \r
+        * @param relPart\r
+        *            The relationship to add.\r
+        */\r
+       public void addRelationship(PackageRelationship relPart) {\r
+               relationshipsByID.put(relPart.getId(), relPart);\r
+               relationshipsByType.put(relPart.getRelationshipType(), relPart);\r
+       }\r
+\r
+       /**\r
+        * Add a relationship to the collection.\r
+        * \r
+        * @param targetUri\r
+        *            Target URI.\r
+        * @param targetMode\r
+        *            The target mode : INTERNAL or EXTERNAL\r
+        * @param relationshipType\r
+        *            Relationship type.\r
+        * @param id\r
+        *            Relationship ID.\r
+        * @return The newly created relationship.\r
+        * @see PackageAccess\r
+        */\r
+       public PackageRelationship addRelationship(URI targetUri,\r
+                       TargetMode targetMode, String relationshipType, String id) {\r
+\r
+               if (id == null) {\r
+                       // Generate a unique ID is id parameter is null.\r
+                       int i = 0;\r
+                       do {\r
+                               id = "rId" + ++i;\r
+                       } while (relationshipsByID.get(id) != null);\r
+               }\r
+\r
+               PackageRelationship rel = new PackageRelationship(container,\r
+                               sourcePart, targetUri, targetMode, relationshipType, id);\r
+               relationshipsByID.put(rel.getId(), rel);\r
+               relationshipsByType.put(rel.getRelationshipType(), rel);\r
+               return rel;\r
+       }\r
+\r
+       /**\r
+        * Remove a relationship by its ID.\r
+        * \r
+        * @param id\r
+        *            The relationship ID to remove.\r
+        */\r
+       public void removeRelationship(String id) {\r
+               if (relationshipsByID != null && relationshipsByType != null) {\r
+                       PackageRelationship rel = relationshipsByID.get(id);\r
+                       if (rel != null) {\r
+                               relationshipsByID.remove(rel.getId());\r
+                               relationshipsByType.values().remove(rel);\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Remove a relationship by its reference.\r
+        * \r
+        * @param rel\r
+        *            The relationship to delete.\r
+        */\r
+       public void removeRelationship(PackageRelationship rel) {\r
+               if (rel == null)\r
+                       throw new IllegalArgumentException("rel");\r
+\r
+               relationshipsByID.values().remove(rel);\r
+               relationshipsByType.values().remove(rel);\r
+       }\r
+\r
+       /**\r
+        * Retrieves a relationship by its index in the collection.\r
+        * \r
+        * @param index\r
+        *            Must be a value between [0-relationships_count-1]\r
+        */\r
+       public PackageRelationship getRelationship(int index) {\r
+               if (index < 0 || index > relationshipsByID.values().size())\r
+                       throw new IllegalArgumentException("index");\r
+\r
+               PackageRelationship retRel = null;\r
+               int i = 0;\r
+               for (PackageRelationship rel : relationshipsByID.values()) {\r
+                       if (index == i++)\r
+                               return rel;\r
+               }\r
+               return retRel;\r
+       }\r
+\r
+       /**\r
+        * Retrieves a package relationship based on its id.\r
+        * \r
+        * @param id\r
+        *            ID of the package relationship to retrieve.\r
+        * @return The package relationship identified by the specified id.\r
+        */\r
+       public PackageRelationship getRelationshipByID(String id) {\r
+               return relationshipsByID.get(id);\r
+       }\r
+\r
+       /**\r
+        * Get the numbe rof relationships in the collection.\r
+        */\r
+       public int size() {\r
+               return relationshipsByID.values().size();\r
+       }\r
+\r
+       /**\r
+        * Parse the relationship part and add all relationship in this collection.\r
+        * \r
+        * @param relPart\r
+        *            The package part to parse.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the relationship part is invalid.\r
+        */\r
+       private void parseRelationshipsPart(PackagePart relPart)\r
+                       throws InvalidFormatException {\r
+               try {\r
+                       SAXReader reader = new SAXReader();\r
+                       logger.debug("Parsing relationship: " + relPart.getPartName());\r
+                       Document xmlRelationshipsDoc = reader\r
+                                       .read(relPart.getInputStream());\r
+\r
+                       // Browse default types\r
+                       Element root = xmlRelationshipsDoc.getRootElement();\r
+\r
+                       // Check OPC compliance M4.1 rule\r
+                       boolean fCorePropertiesRelationship = false;\r
+\r
+                       for (Iterator i = root\r
+                                       .elementIterator(PackageRelationship.RELATIONSHIP_TAG_NAME); i\r
+                                       .hasNext();) {\r
+                               Element element = (Element) i.next();\r
+                               // Relationship ID\r
+                               String id = element.attribute(\r
+                                               PackageRelationship.ID_ATTRIBUTE_NAME).getValue();\r
+                               // Relationship type\r
+                               String type = element.attribute(\r
+                                               PackageRelationship.TYPE_ATTRIBUTE_NAME).getValue();\r
+\r
+                               /* Check OPC Compliance */\r
+                               // Check Rule M4.1\r
+                               if (type.equals(PackageRelationshipTypes.CORE_PROPERTIES))\r
+                                       if (!fCorePropertiesRelationship)\r
+                                               fCorePropertiesRelationship = true;\r
+                                       else\r
+                                               throw new InvalidFormatException(\r
+                                                               "OPC Compliance error [M4.1]: there is more than one core properties relationship in the package !");\r
+\r
+                               /* End OPC Compliance */\r
+\r
+                               // TargetMode (default value "Internal")\r
+                               Attribute targetModeAttr = element\r
+                                               .attribute(PackageRelationship.TARGET_MODE_ATTRIBUTE_NAME);\r
+                               TargetMode targetMode = TargetMode.INTERNAL;\r
+                               if (targetModeAttr != null) {\r
+                                       targetMode = targetModeAttr.getValue().toLowerCase()\r
+                                                       .equals("internal") ? TargetMode.INTERNAL\r
+                                                       : TargetMode.EXTERNAL;\r
+                               }\r
+\r
+                               // Target converted in URI\r
+                               URI target;\r
+                               String value = "";\r
+                               try {\r
+                                       value = element.attribute(\r
+                                                       PackageRelationship.TARGET_ATTRIBUTE_NAME)\r
+                                                       .getValue();\r
+\r
+                                       if (value.indexOf("\\") != -1) {\r
+                                               logger\r
+                                                               .info("target contains \\ therefore not a valid URI"\r
+                                                                               + value + " replaced by /");\r
+                                               value = value.replaceAll("\\\\", "/");\r
+                                               // word can save external relationship with a \ instead\r
+                                               // of /\r
+                                       }\r
+\r
+                                       target = new URI(value);\r
+                               } catch (URISyntaxException e) {\r
+                                       logger.error("Cannot convert " + value\r
+                                                       + " in a valid relationship URI-> ignored", e);\r
+                                       continue;\r
+                               }\r
+                               addRelationship(target, targetMode, type, id);\r
+                       }\r
+               } catch (Exception e) {\r
+                       logger.error(e);\r
+                       throw new InvalidFormatException(e.getMessage());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Retrieves all relations with the specified type.\r
+        * \r
+        * @param typeFilter\r
+        *            Relationship type filter. If <b>null</b> then all\r
+        *            relationships are returned.\r
+        * @return All relationships of the type specified by the filter.\r
+        */\r
+       public PackageRelationshipCollection getRelationships(String typeFilter) {\r
+               PackageRelationshipCollection coll = new PackageRelationshipCollection(\r
+                               this, typeFilter);\r
+               return coll;\r
+       }\r
+\r
+       /**\r
+        * Get this collection's iterator.\r
+        */\r
+       public Iterator<PackageRelationship> iterator() {\r
+               return relationshipsByID.values().iterator();\r
+       }\r
+\r
+       /**\r
+        * Get an iterator of a collection with all relationship with the specified\r
+        * type.\r
+        * \r
+        * @param typeFilter\r
+        *            Type filter.\r
+        * @return An iterator to a collection containing all relationships with the\r
+        *         specified type contain in this collection.\r
+        */\r
+       public Iterator<PackageRelationship> iterator(String typeFilter) {\r
+               ArrayList<PackageRelationship> retArr = new ArrayList<PackageRelationship>();\r
+               for (PackageRelationship rel : relationshipsByID.values()) {\r
+                       if (rel.getRelationshipType().equals(typeFilter))\r
+                               retArr.add(rel);\r
+               }\r
+               return retArr.iterator();\r
+       }\r
+\r
+       /**\r
+        * Clear all relationships.\r
+        */\r
+       public void clear() {\r
+               relationshipsByID.clear();\r
+               relationshipsByType.clear();\r
+       }\r
+\r
+       @Override\r
+       public String toString() {\r
+               String str;\r
+               if (relationshipsByID == null) {\r
+                       str = "relationshipsByID=null";\r
+               } else {\r
+                       str = relationshipsByID.size() + " relationship(s) = [";\r
+               }\r
+               if ((relationshipPart != null) && (relationshipPart.partName != null)) {\r
+                       str = str + "," + relationshipPart.partName;\r
+               } else {\r
+                       str = str + ",relationshipPart=null";\r
+               }\r
+\r
+               // Source of this relationship\r
+               if ((sourcePart != null) && (sourcePart.partName != null)) {\r
+                       str = str + "," + sourcePart.partName;\r
+               } else {\r
+                       str = str + ",sourcePart=null";\r
+               }\r
+               if (partName != null) {\r
+                       str = str + "," + partName;\r
+               } else {\r
+                       str = str + ",uri=null)";\r
+               }\r
+               return str + "]";\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java
new file mode 100755 (executable)
index 0000000..e5ecaac
--- /dev/null
@@ -0,0 +1,77 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+/**\r
+ * Relationship types.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.2\r
+ */\r
+public interface PackageRelationshipTypes {\r
+\r
+       /**\r
+        * Core properties relationship type.\r
+        */\r
+       String CORE_PROPERTIES = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";\r
+\r
+       /**\r
+        * Digital signature relationship type.\r
+        */\r
+       String DIGITAL_SIGNATURE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";\r
+\r
+       /**\r
+        * Digital signature certificate relationship type.\r
+        */\r
+       String DIGITAL_SIGNATURE_CERTIFICATE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/certificate";\r
+\r
+       /**\r
+        * Digital signature origin relationship type.\r
+        */\r
+       String DIGITAL_SIGNATURE_ORIGIN = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";\r
+\r
+       /**\r
+        * Thumbnail relationship type.\r
+        */\r
+       String THUMBNAIL = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail";\r
+\r
+       /**\r
+        * Extended properties relationship type.\r
+        */\r
+       String EXTENDED_PROPERTIES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties";\r
+\r
+       /**\r
+        * Core properties relationship type.\r
+        */\r
+       String CORE_DOCUMENT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";\r
+       \r
+       /**\r
+        * Custom XML relationship type.\r
+        */\r
+       String CUSTOM_XML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml";\r
+\r
+       /**\r
+        * Image type.\r
+        */\r
+       String IMAGE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";\r
+\r
+       /**\r
+        * Style type.\r
+        */\r
+       String STYLE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagingURIHelper.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagingURIHelper.java
new file mode 100755 (executable)
index 0000000..2a63f47
--- /dev/null
@@ -0,0 +1,623 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+\r
+/**\r
+ * Helper for part and pack URI.\r
+ * \r
+ * @author Julien Chable, CDubet, Kim Ung\r
+ * @version 0.1\r
+ */\r
+public final class PackagingURIHelper {\r
+\r
+       /**\r
+        * Package root URI.\r
+        */\r
+       private static URI packageRootUri;\r
+\r
+       /**\r
+        * Extension name of a relationship part.\r
+        */\r
+       public static final String RELATIONSHIP_PART_EXTENSION_NAME;\r
+\r
+       /**\r
+        * Segment name of a relationship part.\r
+        */\r
+       public static final String RELATIONSHIP_PART_SEGMENT_NAME;\r
+\r
+       /**\r
+        * Segment name of the package properties folder.\r
+        */\r
+       public static final String PACKAGE_PROPERTIES_SEGMENT_NAME;\r
+\r
+       /**\r
+        * Core package properties art name.\r
+        */\r
+       public static final String PACKAGE_CORE_PROPERTIES_NAME;\r
+\r
+       /**\r
+        * Forward slash URI separator.\r
+        */\r
+       public static final char FORWARD_SLASH_CHAR;\r
+\r
+       /**\r
+        * Forward slash URI separator.\r
+        */\r
+       public static final String FORWARD_SLASH_STRING;\r
+\r
+       /**\r
+        * Package relationships part URI\r
+        */\r
+       public static final URI PACKAGE_RELATIONSHIPS_ROOT_URI;\r
+\r
+       /**\r
+        * Package relationships part name.\r
+        */\r
+       public static final PackagePartName PACKAGE_RELATIONSHIPS_ROOT_PART_NAME;\r
+\r
+       /**\r
+        * Core properties part URI.\r
+        */\r
+       public static final URI CORE_PROPERTIES_URI;\r
+\r
+       /**\r
+        * Core properties partname.\r
+        */\r
+       public static final PackagePartName CORE_PROPERTIES_PART_NAME;\r
+\r
+       /**\r
+        * Root package URI.\r
+        */\r
+       public static final URI PACKAGE_ROOT_URI;\r
+\r
+       /**\r
+        * Root package part name.\r
+        */\r
+       public static final PackagePartName PACKAGE_ROOT_PART_NAME;\r
+\r
+       /* Static initialization */\r
+       static {\r
+               RELATIONSHIP_PART_SEGMENT_NAME = "_rels";\r
+               RELATIONSHIP_PART_EXTENSION_NAME = ".rels";\r
+               FORWARD_SLASH_CHAR = '/';\r
+               FORWARD_SLASH_STRING = "/";\r
+               PACKAGE_PROPERTIES_SEGMENT_NAME = "docProps";\r
+               PACKAGE_CORE_PROPERTIES_NAME = "core.xml";\r
+\r
+               // Make URI\r
+               URI uriPACKAGE_ROOT_URI = null;\r
+               URI uriPACKAGE_RELATIONSHIPS_ROOT_URI = null;\r
+               URI uriPACKAGE_PROPERTIES_URI = null;\r
+               try {\r
+                       uriPACKAGE_ROOT_URI = new URI("/");\r
+                       uriPACKAGE_RELATIONSHIPS_ROOT_URI = new URI(FORWARD_SLASH_CHAR\r
+                                       + RELATIONSHIP_PART_SEGMENT_NAME + FORWARD_SLASH_CHAR\r
+                                       + RELATIONSHIP_PART_EXTENSION_NAME);\r
+                       packageRootUri = new URI("/");\r
+                       uriPACKAGE_PROPERTIES_URI = new URI(FORWARD_SLASH_CHAR\r
+                                       + PACKAGE_PROPERTIES_SEGMENT_NAME + FORWARD_SLASH_CHAR\r
+                                       + PACKAGE_CORE_PROPERTIES_NAME);\r
+               } catch (URISyntaxException e) {\r
+                       // Should never happen in production as all data are fixed\r
+               }\r
+               PACKAGE_ROOT_URI = uriPACKAGE_ROOT_URI;\r
+               PACKAGE_RELATIONSHIPS_ROOT_URI = uriPACKAGE_RELATIONSHIPS_ROOT_URI;\r
+               CORE_PROPERTIES_URI = uriPACKAGE_PROPERTIES_URI;\r
+\r
+               // Make part name from previous URI\r
+               PackagePartName tmpPACKAGE_ROOT_PART_NAME = null;\r
+               PackagePartName tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME = null;\r
+               PackagePartName tmpCORE_PROPERTIES_URI = null;\r
+               try {\r
+                       tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME = createPartName(PACKAGE_RELATIONSHIPS_ROOT_URI);\r
+                       tmpCORE_PROPERTIES_URI = createPartName(CORE_PROPERTIES_URI);\r
+                       tmpPACKAGE_ROOT_PART_NAME = new PackagePartName(PACKAGE_ROOT_URI,\r
+                                       false);\r
+               } catch (InvalidFormatException e) {\r
+                       // Should never happen in production as all data are fixed\r
+               }\r
+               PACKAGE_RELATIONSHIPS_ROOT_PART_NAME = tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME;\r
+               CORE_PROPERTIES_PART_NAME = tmpCORE_PROPERTIES_URI;\r
+               PACKAGE_ROOT_PART_NAME = tmpPACKAGE_ROOT_PART_NAME;\r
+       }\r
+\r
+       /**\r
+        * Gets the URI for the package root.\r
+        * \r
+        * @return URI of the package root.\r
+        */\r
+       public static URI getPackageRootUri() {\r
+               return packageRootUri;\r
+       }\r
+\r
+       /**\r
+        * Know if the specified URI is a relationship part name.\r
+        * \r
+        * @param partUri\r
+        *            URI to check.\r
+        * @return <i>true</i> if the URI <i>false</i>.\r
+        */\r
+       public static boolean isRelationshipPartURI(URI partUri) {\r
+               if (partUri == null)\r
+                       throw new IllegalArgumentException("partUri");\r
+\r
+               return partUri.getPath().matches(\r
+                               ".*" + RELATIONSHIP_PART_SEGMENT_NAME + ".*"\r
+                                               + RELATIONSHIP_PART_EXTENSION_NAME + "$");\r
+       }\r
+\r
+       /**\r
+        * Get file name from the specified URI.\r
+        */\r
+       public static String getFilename(URI uri) {\r
+               if (uri != null) {\r
+                       String path = uri.getPath();\r
+                       int len = path.length();\r
+                       int num2 = len;\r
+                       while (--num2 >= 0) {\r
+                               char ch1 = path.charAt(num2);\r
+                               if (ch1 == PackagingURIHelper.FORWARD_SLASH_CHAR)\r
+                                       return path.substring(num2 + 1, len);\r
+                       }\r
+               }\r
+               return "";\r
+       }\r
+\r
+       /**\r
+        * Get the file name without the trailing extension.\r
+        */\r
+       public static String getFilenameWithoutExtension(URI uri) {\r
+               String filename = getFilename(uri);\r
+               int dotIndex = filename.lastIndexOf(".");\r
+               if (dotIndex == -1)\r
+                       return filename;\r
+               return filename.substring(0, dotIndex);\r
+       }\r
+\r
+       /**\r
+        * Get the directory path from the specified URI.\r
+        */\r
+       public static URI getPath(URI uri) {\r
+               if (uri != null) {\r
+                       String path = uri.getPath();\r
+                       int len = path.length();\r
+                       int num2 = len;\r
+                       while (--num2 >= 0) {\r
+                               char ch1 = path.charAt(num2);\r
+                               if (ch1 == PackagingURIHelper.FORWARD_SLASH_CHAR) {\r
+                                       try {\r
+                                               return new URI(path.substring(0, num2));\r
+                                       } catch (URISyntaxException e) {\r
+                                               return null;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Combine les deux URI.\r
+        * \r
+        * @param prefix\r
+        *            L'URI de pr�fixe.\r
+        * @param suffix\r
+        *            L'URI de suffixe.\r
+        * @return\r
+        */\r
+       public static URI combine(URI prefix, URI suffix) {\r
+               URI retUri = null;\r
+               try {\r
+                       retUri = new URI(combine(prefix.getPath(), suffix.getPath()));\r
+               } catch (URISyntaxException e) {\r
+                       throw new IllegalArgumentException(\r
+                                       "Prefix and suffix can't be combine !");\r
+               }\r
+               return retUri;\r
+       }\r
+\r
+       /**\r
+        * Combine a string URI with a prefix and a suffix.\r
+        */\r
+       public static String combine(String prefix, String suffix) {\r
+               if (!prefix.endsWith("" + FORWARD_SLASH_CHAR)\r
+                               && !suffix.startsWith("" + FORWARD_SLASH_CHAR))\r
+                       return prefix + FORWARD_SLASH_CHAR + suffix;\r
+               else if ((!prefix.endsWith("" + FORWARD_SLASH_CHAR)\r
+                               && suffix.startsWith("" + FORWARD_SLASH_CHAR) || (prefix\r
+                               .endsWith("" + FORWARD_SLASH_CHAR) && !suffix.startsWith(""\r
+                               + FORWARD_SLASH_CHAR))))\r
+                       return prefix + suffix;\r
+               else\r
+                       return "";\r
+       }\r
+\r
+       /**\r
+        * Fully relativize the source part URI against the target part URI.\r
+        * \r
+        * @param sourceURI\r
+        *            The source part URI.\r
+        * @param targetURI\r
+        *            The target part URI.\r
+        * @return A fully relativize part name URI ('word/media/image1.gif',\r
+        *         '/word/document.xml' => 'media/image1.gif') else\r
+        *         <code>null</code>.\r
+        */\r
+       public static URI relativizeURI(URI sourceURI, URI targetURI) {\r
+               StringBuilder retVal = new StringBuilder();\r
+               String[] segmentsSource = sourceURI.getPath().split("/", -1);\r
+               String[] segmentsTarget = targetURI.getPath().split("/", -1);\r
+\r
+               // If the source URI is empty\r
+               if (segmentsSource.length == 0) {\r
+                       throw new IllegalArgumentException(\r
+                                       "Can't relativize an empty source URI !");\r
+               }\r
+\r
+               // If target URI is empty\r
+               if (segmentsTarget.length == 0) {\r
+                       throw new IllegalArgumentException(\r
+                                       "Can't relativize an empty target URI !");\r
+               }\r
+               \r
+               // If the source is the root, then the relativized\r
+               //  form must actually be an absolute URI\r
+               if(sourceURI.toString().equals("/")) {\r
+                       return targetURI;\r
+               }\r
+\r
+\r
+               // Relativize the source URI against the target URI.\r
+               // First up, figure out how many steps along we can go\r
+               // and still have them be the same\r
+               int segmentsTheSame = 0;\r
+               for (int i = 0; i < segmentsSource.length && i < segmentsTarget.length; i++) {\r
+                       if (segmentsSource[i].equals(segmentsTarget[i])) {\r
+                               // Match so far, good\r
+                               segmentsTheSame++;\r
+                       } else {\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               // If we didn't have a good match or at least except a first empty element\r
+               if ((segmentsTheSame == 0 || segmentsTheSame == 1) && \r
+                               segmentsSource[0].equals("") && segmentsTarget[0].equals("")) {\r
+                       for (int i = 0; i < segmentsSource.length - 2; i++) {\r
+                               retVal.append("../");\r
+                       }\r
+                       for (int i = 0; i < segmentsTarget.length; i++) {\r
+                               if (segmentsTarget[i].equals(""))\r
+                                       continue;\r
+                               retVal.append(segmentsTarget[i]);\r
+                               if (i != segmentsTarget.length - 1)\r
+                                       retVal.append("/");\r
+                       }\r
+\r
+                       try {\r
+                               return new URI(retVal.toString());\r
+                       } catch (Exception e) {\r
+                               System.err.println(e);\r
+                               return null;\r
+                       }\r
+               }\r
+\r
+               // Special case for where the two are the same\r
+               if (segmentsTheSame == segmentsSource.length\r
+                               && segmentsTheSame == segmentsTarget.length) {\r
+                       retVal.append("");\r
+               } else {\r
+                       // Matched for so long, but no more\r
+\r
+                       // Do we need to go up a directory or two from\r
+                       // the source to get here?\r
+                       // (If it's all the way up, then don't bother!)\r
+                       if (segmentsTheSame == 1) {\r
+                               retVal.append("/");\r
+                       } else {\r
+                               for (int j = segmentsTheSame; j < segmentsSource.length - 1; j++) {\r
+                                       retVal.append("../");\r
+                               }\r
+                       }\r
+\r
+                       // Now go from here on down\r
+                       for (int j = segmentsTheSame; j < segmentsTarget.length; j++) {\r
+                               if (retVal.length() > 0\r
+                                               && retVal.charAt(retVal.length() - 1) != '/') {\r
+                                       retVal.append("/");\r
+                               }\r
+                               retVal.append(segmentsTarget[j]);\r
+                       }\r
+               }\r
+\r
+               try {\r
+                       return new URI(retVal.toString());\r
+               } catch (Exception e) {\r
+                       System.err.println(e);\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Resolve a source uri against a target.\r
+        * \r
+        * @param sourcePartUri\r
+        *            The source URI.\r
+        * @param targetUri\r
+        *            The target URI.\r
+        * @return The resolved URI.\r
+        */\r
+       public static URI resolvePartUri(URI sourcePartUri, URI targetUri) {\r
+               if (sourcePartUri == null || sourcePartUri.isAbsolute()) {\r
+                       throw new IllegalArgumentException("sourcePartUri invalid - "\r
+                                       + sourcePartUri);\r
+               }\r
+\r
+               if (targetUri == null || targetUri.isAbsolute()) {\r
+                       throw new IllegalArgumentException("targetUri invalid - "\r
+                                       + targetUri);\r
+               }\r
+\r
+               return sourcePartUri.resolve(targetUri);\r
+       }\r
+\r
+       /**\r
+        * Get URI from a string path.\r
+        */\r
+       public static URI getURIFromPath(String path) {\r
+               URI retUri = null;\r
+               try {\r
+                       retUri = new URI(path);\r
+               } catch (URISyntaxException e) {\r
+                       throw new IllegalArgumentException("path");\r
+               }\r
+               return retUri;\r
+       }\r
+\r
+       /**\r
+        * Get the source part URI from a specified relationships part.\r
+        * \r
+        * @param relationshipPartUri\r
+        *            The relationship part use to retrieve the source part.\r
+        * @return The source part URI from the specified relationships part.\r
+        */\r
+       public static URI getSourcePartUriFromRelationshipPartUri(\r
+                       URI relationshipPartUri) {\r
+               if (relationshipPartUri == null)\r
+                       throw new IllegalArgumentException(\r
+                                       "Le param�tre relationshipPartUri ne doit pas ï¿½tre null !");\r
+\r
+               if (!isRelationshipPartURI(relationshipPartUri))\r
+                       throw new IllegalArgumentException(\r
+                                       "L'URI ne doit pas ï¿½tre celle d'une partie de type relation.");\r
+\r
+               if (relationshipPartUri.compareTo(PACKAGE_RELATIONSHIPS_ROOT_URI) == 0)\r
+                       return PACKAGE_ROOT_URI;\r
+\r
+               String filename = relationshipPartUri.getPath();\r
+               String filenameWithoutExtension = getFilenameWithoutExtension(relationshipPartUri);\r
+               filename = filename\r
+                               .substring(0, ((filename.length() - filenameWithoutExtension\r
+                                               .length()) - RELATIONSHIP_PART_EXTENSION_NAME.length()));\r
+               filename = filename.substring(0, filename.length()\r
+                               - RELATIONSHIP_PART_SEGMENT_NAME.length() - 1);\r
+               filename = combine(filename, filenameWithoutExtension);\r
+               return getURIFromPath(filename);\r
+       }\r
+\r
+       /**\r
+        * Create an OPC compliant part name by throwing an exception if the URI is\r
+        * not valid.\r
+        * \r
+        * @param partUri\r
+        *            The part name URI to validate.\r
+        * @return A valid part name object, else <code>null</code>.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the specified URI is not OPC compliant.\r
+        */\r
+       public static PackagePartName createPartName(URI partUri)\r
+                       throws InvalidFormatException {\r
+               if (partUri == null)\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               return new PackagePartName(partUri, true);\r
+       }\r
+\r
+       /**\r
+        * Create an OPC compliant part name.\r
+        * \r
+        * @param partName\r
+        *            The part name to validate.\r
+        * @return The correspondant part name if valid, else <code>null</code>.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the specified part name is not OPC compliant.\r
+        * @see #createPartName(URI)\r
+        */\r
+       public static PackagePartName createPartName(String partName)\r
+                       throws InvalidFormatException {\r
+               URI partNameURI;\r
+               try {\r
+                       partNameURI = new URI(partName);\r
+               } catch (URISyntaxException e) {\r
+                       throw new InvalidFormatException(e.getMessage());\r
+               }\r
+               return createPartName(partNameURI);\r
+       }\r
+\r
+       /**\r
+        * Create an OPC compliant part name by resolving it using a base part.\r
+        * \r
+        * @param partName\r
+        *            The part name to validate.\r
+        * @param relativePart\r
+        *            The relative base part.\r
+        * @return The correspondant part name if valid, else <code>null</code>.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the specified part name is not OPC compliant.\r
+        * @see #createPartName(URI)\r
+        */\r
+       public static PackagePartName createPartName(String partName,\r
+                       PackagePart relativePart) throws InvalidFormatException {\r
+               URI newPartNameURI;\r
+               try {\r
+                       newPartNameURI = resolvePartUri(\r
+                                       relativePart.getPartName().getURI(), new URI(partName));\r
+               } catch (URISyntaxException e) {\r
+                       throw new InvalidFormatException(e.getMessage());\r
+               }\r
+               return createPartName(newPartNameURI);\r
+       }\r
+\r
+       /**\r
+        * Create an OPC compliant part name by resolving it using a base part.\r
+        * \r
+        * @param partName\r
+        *            The part name URI to validate.\r
+        * @param relativePart\r
+        *            The relative base part.\r
+        * @return The correspondant part name if valid, else <code>null</code>.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the specified part name is not OPC compliant.\r
+        * @see #createPartName(URI)\r
+        */\r
+       public static PackagePartName createPartName(URI partName,\r
+                       PackagePart relativePart) throws InvalidFormatException {\r
+               URI newPartNameURI = resolvePartUri(\r
+                               relativePart.getPartName().getURI(), partName);\r
+               return createPartName(newPartNameURI);\r
+       }\r
+\r
+       /**\r
+        * Validate a part URI by returning a boolean.\r
+        * ([M1.1],[M1.3],[M1.4],[M1.5],[M1.6])\r
+        * \r
+        * (OPC Specifications 8.1.1 Part names) :\r
+        * \r
+        * Part Name Syntax\r
+        * \r
+        * The part name grammar is defined as follows:\r
+        * \r
+        * <i>part_name = 1*( "/" segment )\r
+        * \r
+        * segment = 1*( pchar )</i>\r
+        * \r
+        * \r
+        * (pchar is defined in RFC 3986)\r
+        * \r
+        * @param partUri\r
+        *            The URI to validate.\r
+        * @return <b>true</b> if the URI is valid to the OPC Specifications, else\r
+        *         <b>false</b>\r
+        * \r
+        * @see #createPartName(URI)\r
+        */\r
+       public static boolean isValidPartName(URI partUri) {\r
+               if (partUri == null)\r
+                       throw new IllegalArgumentException("partUri");\r
+\r
+               try {\r
+                       createPartName(partUri);\r
+                       return true;\r
+               } catch (Exception e) {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Decode a URI by converting all percent encoded character into a String\r
+        * character.\r
+        * \r
+        * @param uri\r
+        *            The URI to decode.\r
+        * @return The specified URI in a String with converted percent encoded\r
+        *         characters.\r
+        */\r
+       public static String decodeURI(URI uri) {\r
+               StringBuffer retVal = new StringBuffer();\r
+               String uriStr = uri.toASCIIString();\r
+               char c;\r
+               for (int i = 0; i < uriStr.length(); ++i) {\r
+                       c = uriStr.charAt(i);\r
+                       if (c == '%') {\r
+                               // We certainly found an encoded character, check for length\r
+                               // now ( '%' HEXDIGIT HEXDIGIT)\r
+                               if (((uriStr.length() - i) < 2)) {\r
+                                       throw new IllegalArgumentException("The uri " + uriStr\r
+                                                       + " contain invalid encoded character !");\r
+                               }\r
+\r
+                               // Decode the encoded character\r
+                               char decodedChar = (char) Integer.parseInt(uriStr.substring(\r
+                                               i + 1, i + 3), 16);\r
+                               retVal.append(decodedChar);\r
+                               i += 2;\r
+                               continue;\r
+                       }\r
+                       retVal.append(c);\r
+               }\r
+               return retVal.toString();\r
+       }\r
+\r
+       /**\r
+        * Build a part name where the relationship should be stored ((ex\r
+        * /word/document.xml -> /word/_rels/document.xml.rels)\r
+        * \r
+        * @param partName\r
+        *            Source part URI\r
+        * @return the full path (as URI) of the relation file\r
+        * @throws InvalidOperationException\r
+        *             Throws if the specified URI is a relationshp part.\r
+        */\r
+       public static PackagePartName getRelationshipPartName(\r
+                       PackagePartName partName) {\r
+               if (partName == null)\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               if (PackagingURIHelper.PACKAGE_ROOT_URI.getPath() == partName.getURI()\r
+                               .getPath())\r
+                       return PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_PART_NAME;\r
+\r
+               if (partName.isRelationshipPartURI())\r
+                       throw new InvalidOperationException("Can't be a relationship part");\r
+\r
+               String fullPath = partName.getURI().getPath();\r
+               String filename = getFilename(partName.getURI());\r
+               fullPath = fullPath.substring(0, fullPath.length() - filename.length());\r
+               fullPath = combine(fullPath,\r
+                               PackagingURIHelper.RELATIONSHIP_PART_SEGMENT_NAME);\r
+               fullPath = combine(fullPath, filename);\r
+               fullPath = fullPath\r
+                               + PackagingURIHelper.RELATIONSHIP_PART_EXTENSION_NAME;\r
+\r
+               PackagePartName retPartName;\r
+               try {\r
+                       retPartName = createPartName(fullPath);\r
+               } catch (InvalidFormatException e) {\r
+                       // Should never happen in production as all data are fixed but in\r
+                       // case of return null:\r
+                       return null;\r
+               }\r
+               return retPartName;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/RelationshipSource.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/RelationshipSource.java
new file mode 100755 (executable)
index 0000000..ce9d115
--- /dev/null
@@ -0,0 +1,166 @@
+/* ====================================================================
+   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 org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+
+public interface RelationshipSource {
+
+       /**
+        * 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
+        */
+       public abstract PackageRelationship addRelationship(
+                       PackagePartName targetPartName, TargetMode targetMode,
+                       String relationshipType);
+
+       /**
+        * 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.
+        */
+       public abstract PackageRelationship addRelationship(
+                       PackagePartName targetPartName, TargetMode targetMode,
+                       String relationshipType, String id);
+
+       /**
+        * 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);
+       
+       /**
+        * 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);
+       
+       /**
+        * Delete all the relationships attached to this.
+        */
+       public abstract void clearRelationships();
+
+       /**
+        * Delete the relationship specified by its id.
+        * 
+        * @param id
+        *            The ID identified the part to delete.
+        */
+       public abstract void removeRelationship(String id);
+
+       /**
+        * Retrieve all the relationships attached to this.
+        * 
+        * @return This part's relationships.
+        * @throws OpenXML4JException
+        */
+       public abstract PackageRelationshipCollection getRelationships()
+                       throws InvalidFormatException, OpenXML4JException;
+
+       /**
+        * Retrieves a package relationship from its id.
+        * 
+        * @param id
+        *            ID of the package relationship to retrieve.
+        * @return The package relationship
+        */
+       public abstract PackageRelationship getRelationship(String 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.
+        */
+       public abstract PackageRelationshipCollection getRelationshipsByType(
+                       String relationshipType) throws InvalidFormatException, 
+                       IllegalArgumentException, OpenXML4JException;
+
+       /**
+        * Knows if the part have any relationships.
+        * 
+        * @return <b>true</b> if the part have at least one relationship else
+        *         <b>false</b>.
+        */
+       public abstract boolean hasRelationships();
+
+       /**
+        * 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>
+        */
+       @SuppressWarnings("finally")
+       public abstract boolean isRelationshipExists(PackageRelationship rel);
+
+}
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
new file mode 100755 (executable)
index 0000000..1d55c7b
--- /dev/null
@@ -0,0 +1,77 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+\r
+import org.dom4j.Document;\r
+import org.dom4j.io.OutputFormat;\r
+import org.dom4j.io.XMLWriter;\r
+\r
+public final class StreamHelper {\r
+\r
+       private StreamHelper() {\r
+               // Do nothing\r
+       }\r
+\r
+       /**\r
+        * Turning the DOM4j object in the specified output stream.\r
+        * \r
+        * @param xmlContent\r
+        *            The XML document.\r
+        * @param outStream\r
+        *            The OutputStream in which the XML document will be written.\r
+        * @return <b>true</b> if the xml is successfully written in the stream,\r
+        *         else <b>false</b>.\r
+        */\r
+       public static boolean saveXmlInStream(Document xmlContent,\r
+                       OutputStream outStream) {\r
+               try {\r
+                       OutputFormat outformat = OutputFormat.createPrettyPrint();\r
+                       outformat.setEncoding("UTF-8");\r
+                       XMLWriter writer = new XMLWriter(outStream, outformat);\r
+                       writer.write(xmlContent);\r
+               } catch (Exception e) {\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Copy the input stream into the output stream.\r
+        * \r
+        * @param inStream\r
+        *            The source stream.\r
+        * @param outStream\r
+        *            The destination stream.\r
+        * @return <b>true</b> if the operation succeed, else return <b>false</b>.\r
+        */\r
+       public static boolean copyStream(InputStream inStream, OutputStream outStream) {\r
+               try {\r
+                       byte[] buffer = new byte[1024];\r
+                       int bytesRead;\r
+                       while ((bytesRead = inStream.read(buffer)) >= 0) {\r
+                               outStream.write(buffer, 0, bytesRead);\r
+                       }\r
+               } catch (Exception e) {\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/TargetMode.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/TargetMode.java
new file mode 100755 (executable)
index 0000000..12a5a55
--- /dev/null
@@ -0,0 +1,32 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+/**\r
+ * Specifies whether the target of a PackageRelationship is inside or outside a\r
+ * Package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public enum TargetMode {\r
+       /** The relationship references a resource that is external to the package. */\r
+       INTERNAL,\r
+       /** The relationship references a part that is inside the package. */\r
+       EXTERNAL\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java
new file mode 100755 (executable)
index 0000000..f0db2ab
--- /dev/null
@@ -0,0 +1,464 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.net.URI;\r
+import java.util.Enumeration;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipFile;\r
+import java.util.zip.ZipInputStream;\r
+import java.util.zip.ZipOutputStream;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;\r
+import org.apache.poi.openxml4j.opc.internal.FileHelper;\r
+import org.apache.poi.openxml4j.opc.internal.MemoryPackagePart;\r
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;\r
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;\r
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;\r
+import org.apache.poi.openxml4j.util.ZipEntrySource;\r
+import org.apache.poi.openxml4j.util.ZipFileZipEntrySource;\r
+import org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource;\r
+\r
+/**\r
+ * Physical zip package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.2\r
+ */\r
+public final class ZipPackage extends Package {\r
+\r
+       private static Logger logger = Logger.getLogger("org.openxml4j");\r
+\r
+       /**\r
+        * Zip archive, as either a file on disk,\r
+        *  or a stream \r
+        */\r
+       private final ZipEntrySource zipArchive;\r
+\r
+       /**\r
+        * Constructor. Creates a new ZipPackage.\r
+        */\r
+       public ZipPackage() {\r
+               super(defaultPackageAccess);\r
+               this.zipArchive = null;\r
+       }\r
+\r
+       /**\r
+        * Constructor. <b>Operation not supported.</b>\r
+        * \r
+        * @param in\r
+        *            Zip input stream to load.\r
+        * @param access\r
+        * @throws IllegalArgumentException\r
+        *             If the specified input stream not an instance of\r
+        *             ZipInputStream.\r
+        */\r
+       ZipPackage(InputStream in, PackageAccess access) throws IOException {\r
+               super(access);\r
+               this.zipArchive = new ZipInputStreamZipEntrySource(\r
+                               new ZipInputStream(in)\r
+               );\r
+       }\r
+\r
+       /**\r
+        * Constructor. Opens a Zip based Open XML document.\r
+        * \r
+        * @param path\r
+        *            The path of the file to open or create.\r
+        * @param access\r
+        *            The package access mode.\r
+        * @throws InvalidFormatException\r
+        *             If the content type part parsing encounters an error.\r
+        */\r
+       ZipPackage(String path, PackageAccess access) throws InvalidFormatException {\r
+               super(access);\r
+               \r
+               ZipFile zipFile = ZipHelper.openZipFile(path);\r
+               if (zipFile == null)\r
+                       throw new InvalidOperationException(\r
+                                       "Can't open the specified file: '" + path + "'");\r
+               this.zipArchive = new ZipFileZipEntrySource(zipFile); \r
+       }\r
+       \r
+       /**\r
+        * Retrieves the parts from this package. We assume that the package has not\r
+        * been yet inspect to retrieve all the parts, this method will open the\r
+        * archive and look for all parts contain inside it. If the package part\r
+        * list is not empty, it will be emptied.\r
+        * \r
+        * @return All parts contain in this package.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the package is not valid.\r
+        */\r
+       @Override\r
+       protected PackagePart[] getPartsImpl() throws InvalidFormatException {\r
+               if (this.partList == null) {\r
+                       // The package has just been created, we create an empty part\r
+                       // list.\r
+                       this.partList = new PackagePartCollection();\r
+               }\r
+\r
+               if (this.zipArchive == null) {\r
+                       return this.partList.values().toArray(\r
+                                       new PackagePart[this.partList.values().size()]);\r
+               } else {\r
+                       // First we need to parse the content type part\r
+                       Enumeration<? extends ZipEntry> entries = this.zipArchive.getEntries();\r
+                       while (entries.hasMoreElements()) {\r
+                               ZipEntry entry = entries.nextElement();\r
+                               if (entry.getName().equals(\r
+                                               ContentTypeManager.CONTENT_TYPES_PART_NAME)) {\r
+                                       try {\r
+                                               this.contentTypeManager = new ZipContentTypeManager(\r
+                                                               getZipArchive().getInputStream(entry), this);\r
+                                       } catch (IOException e) {\r
+                                               throw new InvalidFormatException(e.getMessage());\r
+                                       }\r
+                                       break;\r
+                               }\r
+                       }\r
+\r
+                       // At this point, we should have loaded the content type part\r
+                       if (this.contentTypeManager == null) {\r
+                               throw new InvalidFormatException(\r
+                                               "Package should contain a content type part [M1.13]");\r
+                       }\r
+                       \r
+                       // Now create all the relationships\r
+                       // (Need to create relationships before other\r
+                       //  parts, otherwise we might create a part before\r
+                       //  its relationship exists, and then it won't tie up)\r
+                       entries = this.zipArchive.getEntries();\r
+                       while (entries.hasMoreElements()) {\r
+                               ZipEntry entry = (ZipEntry) entries.nextElement();\r
+                               PackagePartName partName = buildPartName(entry);\r
+                               if(partName == null) continue;\r
+                               \r
+                               // Only proceed for Relationships at this stage\r
+                               String contentType = contentTypeManager.getContentType(partName);\r
+                               if (contentType != null && contentType.equals(ContentTypes.RELATIONSHIPS_PART)) {\r
+                                       try {\r
+                                               partList.put(partName, new ZipPackagePart(this, entry,\r
+                                                       partName, contentType));\r
+                                       } catch (InvalidOperationException e) {\r
+                                               throw new InvalidFormatException(e.getMessage());\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       // Then we can go through all the other parts\r
+                       entries = this.zipArchive.getEntries();\r
+                       while (entries.hasMoreElements()) {\r
+                               ZipEntry entry = (ZipEntry) entries.nextElement();\r
+                               PackagePartName partName = buildPartName(entry);\r
+                               if(partName == null) continue;\r
+\r
+                               String contentType = contentTypeManager\r
+                                               .getContentType(partName);\r
+                               if (contentType != null && contentType.equals(ContentTypes.RELATIONSHIPS_PART)) {\r
+                                       // Already handled\r
+                               }\r
+                               else if (contentType != null) {\r
+                                       try {\r
+                                               partList.put(partName, new ZipPackagePart(this, entry,\r
+                                                               partName, contentType));\r
+                                       } catch (InvalidOperationException e) {\r
+                                               throw new InvalidFormatException(e.getMessage());\r
+                                       }\r
+                               } else {\r
+                                       throw new InvalidFormatException(\r
+                                                       "The part "\r
+                                                                       + partName.getURI().getPath()\r
+                                                                       + " does not have any content type ! Rule: Package require content types when retrieving a part from a package. [M.1.14]");\r
+                               }\r
+                       }\r
+                       \r
+                       return (ZipPackagePart[]) partList.values().toArray(\r
+                                       new ZipPackagePart[partList.size()]);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Builds a PackagePartName for the given ZipEntry,\r
+        *  or null if it's the content types / invalid part\r
+        */\r
+       private PackagePartName buildPartName(ZipEntry entry) {\r
+               try {\r
+                       // We get an error when we parse [Content_Types].xml\r
+                       // because it's not a valid URI.\r
+                       if (entry.getName().equals(\r
+                                       ContentTypeManager.CONTENT_TYPES_PART_NAME)) {\r
+                               return null;\r
+                       } else {\r
+                               return PackagingURIHelper.createPartName(ZipHelper\r
+                                               .getOPCNameFromZipItemName(entry.getName()));\r
+                       }\r
+               } catch (Exception e) {\r
+                       // We assume we can continue, even in degraded mode ...\r
+                       logger.warn("Entry "\r
+                                                       + entry.getName()\r
+                                                       + " is not valid, so this part won't be add to the package.");\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Create a new MemoryPackagePart from the specified URI and content type\r
+        * \r
+        * \r
+        * aram partName The part URI.\r
+        * \r
+        * @param contentType\r
+        *            The part content type.\r
+        * @return The newly created zip package part, else <b>null</b>.\r
+        */\r
+       @Override\r
+       protected PackagePart createPartImpl(PackagePartName partName,\r
+                       String contentType, boolean loadRelationships) {\r
+               if (contentType == null)\r
+                       throw new IllegalArgumentException("contentType");\r
+\r
+               if (partName == null)\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               try {\r
+                       return new MemoryPackagePart(this, partName, contentType,\r
+                                       loadRelationships);\r
+               } catch (InvalidFormatException e) {\r
+                       System.err.println(e);\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Delete a part from the package\r
+        * \r
+        * @throws IllegalArgumentException\r
+        *             Throws if the part URI is nulll or invalid.\r
+        */\r
+       @Override\r
+       protected void removePartImpl(PackagePartName partName) {\r
+               if (partName == null)\r
+                       throw new IllegalArgumentException("partUri");\r
+       }\r
+\r
+       /**\r
+        * Flush the package. Do nothing.\r
+        */\r
+       @Override\r
+       protected void flushImpl() {\r
+               // Do nothing\r
+       }\r
+\r
+       /**\r
+        * Close and save the package.\r
+        * \r
+        * @see #close()\r
+        */\r
+       @Override\r
+       protected void closeImpl() throws IOException {\r
+               // Flush the package\r
+               flush();\r
+\r
+               // Save the content\r
+               if (this.originalPackagePath != null\r
+                               && !"".equals(this.originalPackagePath)) {\r
+                       File targetFile = new File(this.originalPackagePath);\r
+                       if (targetFile.exists()) {\r
+                               // Case of a package previously open\r
+\r
+                               File tempFile = File.createTempFile(\r
+                                               generateTempFileName(FileHelper\r
+                                                               .getDirectory(targetFile)), ".tmp");\r
+\r
+                               // Save the final package to a temporary file\r
+                               try {\r
+                                       save(tempFile);\r
+                                       this.zipArchive.close(); // Close the zip archive to be\r
+                                       // able to delete it\r
+                                       FileHelper.copyFile(tempFile, targetFile);\r
+                               } finally {\r
+                                       // Either the save operation succeed or not, we delete the\r
+                                       // temporary file\r
+                                       if (!tempFile.delete()) {\r
+                                               logger\r
+                                                               .warn("The temporary file: '"\r
+                                                                               + targetFile.getAbsolutePath()\r
+                                                                               + "' cannot be deleted ! Make sure that no other application use it.");\r
+                                       }\r
+                               }\r
+                       } else {\r
+                               throw new InvalidOperationException(\r
+                                               "Can't close a package not previously open with the open() method !");\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Create a unique identifier to be use as a temp file name.\r
+        * \r
+        * @return A unique identifier use to be use as a temp file name.\r
+        */\r
+       private synchronized String generateTempFileName(File directory) {\r
+               File tmpFilename;\r
+               do {\r
+                       tmpFilename = new File(directory.getAbsoluteFile() + File.separator\r
+                                       + "OpenXML4J" + System.nanoTime());\r
+               } while (tmpFilename.exists());\r
+               return FileHelper.getFilename(tmpFilename.getAbsoluteFile());\r
+       }\r
+\r
+       /**\r
+        * Close the package without saving the document. Discard all the changes\r
+        * made to this package.\r
+        */\r
+       @Override\r
+       protected void revertImpl() {\r
+               try {\r
+                       if (this.zipArchive != null)\r
+                               this.zipArchive.close();\r
+               } catch (IOException e) {\r
+                       // Do nothing, user dont have to know\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Implement the getPart() method to retrieve a part from its URI in the\r
+        * current package\r
+        * \r
+        * \r
+        * @see #getPart(URI)\r
+        */\r
+       @Override\r
+       protected PackagePart getPartImpl(PackagePartName partName) {\r
+               if (partList.containsKey(partName)) {\r
+                       return partList.get(partName);\r
+               }\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Save this package into the specified stream\r
+        * \r
+        * \r
+        * @param outputStream\r
+        *            The stream use to save this package.\r
+        * \r
+        * @see #save(OutputStream)\r
+        * @see #saveInZip(ZipOutputStream)\r
+        */\r
+       @Override\r
+       public void saveImpl(OutputStream outputStream) {\r
+               // Check that the document was open in write mode\r
+               throwExceptionIfReadOnly();\r
+               ZipOutputStream zos = null;\r
+\r
+               try {\r
+                       if (!(outputStream instanceof ZipOutputStream))\r
+                               zos = new ZipOutputStream(outputStream);\r
+                       else\r
+                               zos = (ZipOutputStream) outputStream;\r
+\r
+                       // If the core properties part does not exist in the part list,\r
+                       // we save it as well\r
+                       if (this.getPartsByRelationshipType(\r
+                                       PackageRelationshipTypes.CORE_PROPERTIES).size() == 0) {\r
+                               logger.debug("Save core properties part");\r
+\r
+                               // We have to save the core properties part ...\r
+                               new ZipPackagePropertiesMarshaller().marshall(\r
+                                               this.packageProperties, zos);\r
+                               // ... and to add its relationship ...\r
+                               this.relationships.addRelationship(this.packageProperties\r
+                                               .getPartName().getURI(), TargetMode.INTERNAL,\r
+                                               PackageRelationshipTypes.CORE_PROPERTIES, null);\r
+                               // ... and the content if it has not been added yet.\r
+                               if (!this.contentTypeManager\r
+                                               .isContentTypeRegister(ContentTypes.CORE_PROPERTIES_PART)) {\r
+                                       this.contentTypeManager.addContentType(\r
+                                                       this.packageProperties.getPartName(),\r
+                                                       ContentTypes.CORE_PROPERTIES_PART);\r
+                               }\r
+                       }\r
+\r
+                       // Save package relationships part.\r
+                       logger.debug("Save package relationships");\r
+                       ZipPartMarshaller.marshallRelationshipPart(this.getRelationships(),\r
+                                       PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_PART_NAME,\r
+                                       zos);\r
+\r
+                       // Save content type part.\r
+                       logger.debug("Save content types part");\r
+                       this.contentTypeManager.save(zos);\r
+\r
+                       // Save parts.\r
+                       for (PackagePart part : getParts()) {\r
+                               // If the part is a relationship part, we don't save it, it's\r
+                               // the source part that will do the job.\r
+                               if (part.isRelationshipPart())\r
+                                       continue;\r
+\r
+                               logger.debug("Save part '"\r
+                                               + ZipHelper.getZipItemNameFromOPCName(part\r
+                                                               .getPartName().getName()) + "'");\r
+                               PartMarshaller marshaller = partMarshallers\r
+                                               .get(part.contentType);\r
+                               if (marshaller != null) {\r
+                                       if (!marshaller.marshall(part, zos)) {\r
+                                               throw new OpenXML4JException(\r
+                                                               "The part "\r
+                                                                               + part.getPartName().getURI()\r
+                                                                               + " fail to be saved in the stream with marshaller "\r
+                                                                               + marshaller);\r
+                                       }\r
+                               } else {\r
+                                       if (!defaultPartMarshaller.marshall(part, zos))\r
+                                               throw new OpenXML4JException(\r
+                                                               "The part "\r
+                                                                               + part.getPartName().getURI()\r
+                                                                               + " fail to be saved in the stream with marshaller "\r
+                                                                               + defaultPartMarshaller);\r
+                               }\r
+                       }\r
+                       zos.close();\r
+               } catch (Exception e) {\r
+                       logger\r
+                                       .error("Fail to save: an error occurs while saving the package : "\r
+                                                       + e.getMessage());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Get the zip archive\r
+        * \r
+        * @return The zip archive.\r
+        */\r
+       public ZipEntrySource getZipArchive() {\r
+               return zipArchive;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java
new file mode 100755 (executable)
index 0000000..2c05898
--- /dev/null
@@ -0,0 +1,135 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.zip.ZipEntry;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;\r
+\r
+/**\r
+ * Zip implementation of a PackagePart.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ * @see PackagePart\r
+ */\r
+public class ZipPackagePart extends PackagePart {\r
+\r
+       /**\r
+        * The zip entry corresponding to this part.\r
+        */\r
+       private ZipEntry zipEntry;\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param container\r
+        *            The container package.\r
+        * @param partName\r
+        *            Part name.\r
+        * @param contentType\r
+        *            Content type.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the content of this part invalid.\r
+        */\r
+       public ZipPackagePart(Package container, PackagePartName partName,\r
+                       String contentType) throws InvalidFormatException {\r
+               super(container, partName, contentType);\r
+       }\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param container\r
+        *            The container package.\r
+        * @param zipEntry\r
+        *            The zip entry corresponding to this part.\r
+        * @param partName\r
+        *            The part name.\r
+        * @param contentType\r
+        *            Content type.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the content of this part is invalid.\r
+        */\r
+       public ZipPackagePart(Package container, ZipEntry zipEntry,\r
+                       PackagePartName partName, String contentType)\r
+                       throws InvalidFormatException {\r
+               super(container, partName, contentType);\r
+               this.zipEntry = zipEntry;\r
+       }\r
+\r
+       /**\r
+        * Get the zip entry of this part.\r
+        * \r
+        * @return The zip entry in the zip structure coresponding to this part.\r
+        */\r
+       public ZipEntry getZipArchive() {\r
+               return zipEntry;\r
+       }\r
+\r
+       /**\r
+        * Implementation of the getInputStream() which return the inputStream of\r
+        * this part zip entry.\r
+        * \r
+        * @return Input stream of this part zip entry.\r
+        */\r
+       @Override\r
+       protected InputStream getInputStreamImpl() throws IOException {\r
+               // We use the getInputStream() method from java.util.zip.ZipFile\r
+               // class which return an InputStream to this part zip entry.\r
+               return ((ZipPackage) container).getZipArchive()\r
+                               .getInputStream(zipEntry);\r
+       }\r
+\r
+       /**\r
+        * Implementation of the getOutputStream(). Return <b>null</b>. Normally\r
+        * will never be called since the MemoryPackage is use instead.\r
+        * \r
+        * @return <b>null</b>\r
+        */\r
+       @Override\r
+       protected OutputStream getOutputStreamImpl() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public boolean save(OutputStream os) throws OpenXML4JException {\r
+               return new ZipPartMarshaller().marshall(this, os);\r
+       }\r
+\r
+       @Override\r
+       public boolean load(InputStream ios) throws InvalidFormatException {\r
+               throw new InvalidOperationException("Method not implemented !");\r
+       }\r
+\r
+       @Override\r
+       public void close() {\r
+               throw new InvalidOperationException("Method not implemented !");\r
+       }\r
+\r
+       @Override\r
+       public void flush() {\r
+               throw new InvalidOperationException("Method not implemented !");\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentType.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentType.java
new file mode 100755 (executable)
index 0000000..18a6ca4
--- /dev/null
@@ -0,0 +1,224 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.util.Hashtable;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+\r
+/**\r
+ * Represents a immutable MIME ContentType value (RFC 2616 ï¿½3.7)\r
+ * \r
+ * media-type = type "/" subtype *( ";" parameter ) type = token<br>\r
+ * subtype = token<br>\r
+ * \r
+ * Rule M1.13 : Package implementers shall only create and only recognize parts\r
+ * with a content type; format designers shall specify a content type for each\r
+ * part included in the format. Content types for package parts shall fit the\r
+ * definition and syntax for media types as specified in RFC 2616,��3.7.\r
+ * \r
+ * Rule M1.14: Content types shall not use linear white space either between the\r
+ * type and subtype or between an attribute and its value. Content types also\r
+ * shall not have leading or trailing white spaces. Package implementers shall\r
+ * create only such content types and shall require such content types when\r
+ * retrieving a part from a package; format designers shall specify only such\r
+ * content types for inclusion in the format.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ * \r
+ * @see http://www.ietf.org/rfc/rfc2045.txt\r
+ * @see http://www.ietf.org/rfc/rfc2616.txt\r
+ */\r
+public final class ContentType {\r
+\r
+       /**\r
+        * Type in Type/Subtype.\r
+        */\r
+       private String type;\r
+\r
+       /**\r
+        * Subtype\r
+        */\r
+       private String subType;\r
+\r
+       /**\r
+        * Parameters\r
+        */\r
+       private Hashtable<String, String> parameters;\r
+\r
+       /**\r
+        * Media type compiled pattern for parameters.\r
+        */\r
+       private final static Pattern patternMediaType;\r
+\r
+       static {\r
+               /*\r
+                * token = 1*<any CHAR except CTLs or separators>\r
+                * \r
+                * separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" |\r
+                * <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT\r
+                * \r
+                * CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>\r
+                * \r
+                * CHAR = <any US-ASCII character (octets 0 - 127)>\r
+                */\r
+               String token = "[\\x21-\\x7E&&[^\\(\\)<>@,;:\\\\/\"\\[\\]\\?={}\\x20\\x09]]";\r
+\r
+               /*\r
+                * parameter = attribute "=" value\r
+                * \r
+                * attribute = token\r
+                * \r
+                * value = token | quoted-string\r
+                */\r
+               // Keep for future use with parameter:\r
+               // String parameter = "(" + token + "+)=(\"?" + token + "+\"?)";\r
+               /*\r
+                * Pattern for media type.\r
+                * \r
+                * Don't allow comment, rule M1.15: The package implementer shall\r
+                * require a content type that does not include comments and the format\r
+                * designer shall specify such a content type.\r
+                * \r
+                * comment = "(" *( ctext | quoted-pair | comment ) ")"\r
+                * \r
+                * ctext = <any TEXT excluding "(" and ")">\r
+                * \r
+                * TEXT = <any OCTET except CTLs, but including LWS>\r
+                * \r
+                * LWS = [CRLF] 1*( SP | HT )\r
+                * \r
+                * CR = <US-ASCII CR, carriage return (13)>\r
+                * \r
+                * LF = <US-ASCII LF, linefeed (10)>\r
+                * \r
+                * SP = <US-ASCII SP, space (32)>\r
+                * \r
+                * HT = <US-ASCII HT, horizontal-tab (9)>\r
+                * \r
+                * quoted-pair = "\" CHAR\r
+                */\r
+\r
+               // Keep for future use with parameter:\r
+               // patternMediaType = Pattern.compile("^(" + token + "+)/(" + token\r
+               // + "+)(;" + parameter + ")*$");\r
+               patternMediaType = Pattern.compile("^(" + token + "+)/(" + token\r
+                               + "+)$");\r
+       }\r
+\r
+       /**\r
+        * Constructor. Check the input with the RFC 2616 grammar.\r
+        * \r
+        * @param contentType\r
+        *            The content type to store.\r
+        * @throws InvalidFormatException\r
+        *             If the specified content type is not valid with RFC 2616.\r
+        */\r
+       public ContentType(String contentType) throws InvalidFormatException {\r
+               // Conversion en US-ASCII\r
+               String contentTypeASCII = null;\r
+               try {\r
+                       contentTypeASCII = new String(contentType.getBytes(), "US-ASCII");\r
+               } catch (UnsupportedEncodingException e) {\r
+                       throw new InvalidFormatException(\r
+                                       "The specified content type is not an ASCII value.");\r
+               }\r
+\r
+               Matcher mMediaType = patternMediaType.matcher(contentTypeASCII);\r
+               if (!mMediaType.matches())\r
+                       throw new InvalidFormatException(\r
+                                       "The specified content type '"\r
+                                                       + contentType\r
+                                                       + "' is not compliant with RFC 2616: malformed content type.");\r
+\r
+               // Type/subtype\r
+               if (mMediaType.groupCount() >= 2) {\r
+                       this.type = mMediaType.group(1);\r
+                       this.subType = mMediaType.group(2);\r
+                       // Parameters\r
+                       this.parameters = new Hashtable<String, String>(1);\r
+                       for (int i = 4; i <= mMediaType.groupCount()\r
+                                       && (mMediaType.group(i) != null); i += 2) {\r
+                               this.parameters.put(mMediaType.group(i), mMediaType\r
+                                               .group(i + 1));\r
+                       }\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public final String toString() {\r
+               StringBuffer retVal = new StringBuffer();\r
+               retVal.append(this.getType());\r
+               retVal.append("/");\r
+               retVal.append(this.getSubType());\r
+               // Keep for future implementation if needed\r
+               // for (String key : parameters.keySet()) {\r
+               // retVal.append(";");\r
+               // retVal.append(key);\r
+               // retVal.append("=");\r
+               // retVal.append(parameters.get(key));\r
+               // }\r
+               return retVal.toString();\r
+       }\r
+\r
+       @Override\r
+       public boolean equals(Object obj) {\r
+               return (!(obj instanceof ContentType))\r
+                               || (this.toString().equalsIgnoreCase(obj.toString()));\r
+       }\r
+\r
+       @Override\r
+       public int hashCode() {\r
+               return this.toString().hashCode();\r
+       }\r
+\r
+       /* Getters */\r
+\r
+       /**\r
+        * Get the subtype.\r
+        * \r
+        * @return The subtype of this content type.\r
+        */\r
+       public String getSubType() {\r
+               return this.subType;\r
+       }\r
+\r
+       /**\r
+        * Get the type.\r
+        * \r
+        * @return The type of this content type.\r
+        */\r
+       public String getType() {\r
+               return this.type;\r
+       }\r
+\r
+       /**\r
+        * Gets the value associated to the specified key.\r
+        * \r
+        * @param key\r
+        *            The key of the key/value pair.\r
+        * @return The value associated to the specified key.\r
+        */\r
+       public String getParameters(String key) {\r
+               return parameters.get(key);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentTypeManager.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentTypeManager.java
new file mode 100755 (executable)
index 0000000..5f96d18
--- /dev/null
@@ -0,0 +1,498 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.TreeMap;\r
+import java.util.Map.Entry;\r
+import java.util.zip.ZipOutputStream;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.dom4j.Document;\r
+import org.dom4j.DocumentException;\r
+import org.dom4j.DocumentHelper;\r
+import org.dom4j.Element;\r
+import org.dom4j.Namespace;\r
+import org.dom4j.QName;\r
+import org.dom4j.io.SAXReader;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+\r
+/**\r
+ * Manage package content types ([Content_Types].xml part).\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public abstract class ContentTypeManager {\r
+\r
+       protected static Logger logger = Logger.getLogger("org.openxml4j");\r
+\r
+       /**\r
+        * Reference to the package using this content type manager.\r
+        */\r
+       protected Package container;\r
+\r
+       /**\r
+        * Content type part name.\r
+        */\r
+       public static final String CONTENT_TYPES_PART_NAME = "[Content_Types].xml";\r
+\r
+       /**\r
+        * Content type namespace\r
+        */\r
+       public static final String TYPES_NAMESPACE_URI = "http://schemas.openxmlformats.org/package/2006/content-types";\r
+\r
+       /* Xml elements in content type part */\r
+\r
+       private static final String TYPES_TAG_NAME = "Types";\r
+\r
+       private static final String DEFAULT_TAG_NAME = "Default";\r
+\r
+       private static final String EXTENSION_ATTRIBUTE_NAME = "Extension";\r
+\r
+       private static final String CONTENT_TYPE_ATTRIBUTE_NAME = "ContentType";\r
+\r
+       private static final String OVERRIDE_TAG_NAME = "Override";\r
+\r
+       private static final String PART_NAME_ATTRIBUTE_NAME = "PartName";\r
+\r
+       /**\r
+        * Default content type tree. <Extension, ContentType>\r
+        */\r
+       private TreeMap<String, String> defaultContentType;\r
+\r
+       /**\r
+        * Override content type tree.\r
+        */\r
+       private TreeMap<PackagePartName, String> overrideContentType;\r
+\r
+       /**\r
+        * Constructor. Parses the content of the specified input stream.\r
+        * \r
+        * @param archive\r
+        *            If different of <i>null</i> then the content types part is\r
+        *            retrieve and parse.\r
+        * @throws InvalidFormatException\r
+        *             If the content types part content is not valid.\r
+        */\r
+       public ContentTypeManager(InputStream in, Package pkg)\r
+                       throws InvalidFormatException {\r
+               this.container = pkg;\r
+               this.defaultContentType = new TreeMap<String, String>();\r
+               if (in != null) {\r
+                       try {\r
+                               parseContentTypesFile(in);\r
+                       } catch (InvalidFormatException e) {\r
+                               throw new InvalidFormatException(\r
+                                               "Can't read content types part !");\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Build association extention-> content type (will be stored in\r
+        * [Content_Types].xml) for example ContentType="image/png" Extension="png"\r
+        * \r
+        * [M2.8]: When adding a new part to a package, the package implementer\r
+        * shall ensure that a content type for that part is specified in the\r
+        * Content Types stream; the package implementer shall perform the steps\r
+        * described in��9.1.2.3:\r
+        * \r
+        * 1. Get the extension from the part name by taking the substring to the\r
+        * right of the rightmost occurrence of the dot character (.) from the\r
+        * rightmost segment.\r
+        * \r
+        * 2. If a part name has no extension, a corresponding Override element\r
+        * shall be added to the Content Types stream.\r
+        * \r
+        * 3. Compare the resulting extension with the values specified for the\r
+        * Extension attributes of the Default elements in the Content Types stream.\r
+        * The comparison shall be case-insensitive ASCII.\r
+        * \r
+        * 4. If there is a Default element with a matching Extension attribute,\r
+        * then the content type of the new part shall be compared with the value of\r
+        * the ContentType attribute. The comparison might be case-sensitive and\r
+        * include every character regardless of the role it plays in the\r
+        * content-type grammar of RFC 2616, or it might follow the grammar of RFC\r
+        * 2616.\r
+        * \r
+        * a. If the content types match, no further action is required.\r
+        * \r
+        * b. If the content types do not match, a new Override element shall be\r
+        * added to the Content Types stream. .\r
+        * \r
+        * 5. If there is no Default element with a matching Extension attribute, a\r
+        * new Default element or Override element shall be added to the Content\r
+        * Types stream.\r
+        * \r
+        * \r
+        * @param partUri\r
+        *            the uri that will be stored\r
+        * @return <b>false</b> if an error occured.\r
+        */\r
+       public void addContentType(PackagePartName partName, String contentType) {\r
+               boolean defaultCTExists = false;\r
+               String extension = partName.getExtension().toLowerCase();\r
+               if ((extension.length() == 0)\r
+                               || (this.defaultContentType.containsKey(extension) && !(defaultCTExists = this.defaultContentType\r
+                                               .containsValue(contentType))))\r
+                       this.addOverrideContentType(partName, contentType);\r
+               else if (!defaultCTExists)\r
+                       this.addDefaultContentType(extension, contentType);\r
+       }\r
+\r
+       /**\r
+        * Add an override content type for a specific part.\r
+        * \r
+        * @param partName\r
+        *            Name of the part.\r
+        * @param contentType\r
+        *            Content type of the part.\r
+        */\r
+       private void addOverrideContentType(PackagePartName partName,\r
+                       String contentType) {\r
+               if (overrideContentType == null)\r
+                       overrideContentType = new TreeMap<PackagePartName, String>();\r
+               overrideContentType.put(partName, contentType);\r
+       }\r
+\r
+       /**\r
+        * Add a content type associated with the specified extension.\r
+        * \r
+        * @param extension\r
+        *            The part name extension to bind to a content type.\r
+        * @param contentType\r
+        *            The content type associated with the specified extension.\r
+        */\r
+       private void addDefaultContentType(String extension, String contentType) {\r
+               // Remark : Originally the latest parameter was :\r
+               // contentType.toLowerCase(). Change due to a request ID 1996748.\r
+               defaultContentType.put(extension.toLowerCase(), contentType);\r
+       }\r
+\r
+       /**\r
+        * Delete a content type based on the specified part name. If the specified\r
+        * part name is register with an override content type, then this content\r
+        * type is remove, else the content type is remove in the default content\r
+        * type list if it exists and if no part is associated with it yet.\r
+        * \r
+        * Check rule M2.4: The package implementer shall require that the Content\r
+        * Types stream contain one of the following for every part in the package:\r
+        * One matching Default element One matching Override element Both a\r
+        * matching Default element and a matching Override element, in which case\r
+        * the Override element takes precedence.\r
+        * \r
+        * @param partUri\r
+        *            The part URI associated with the override content type to\r
+        *            delete.\r
+        * @exception InvalidOperationException\r
+        *                Throws if\r
+        */\r
+       public void removeContentType(PackagePartName partName)\r
+                       throws InvalidOperationException {\r
+               if (partName == null)\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               /* Override content type */\r
+               if (this.overrideContentType != null\r
+                               && (this.overrideContentType.get(partName) != null)) {\r
+                       // Remove the override definition for the specified part.\r
+                       this.overrideContentType.remove(partName);\r
+                       return;\r
+               }\r
+\r
+               /* Default content type */\r
+               String extensionToDelete = partName.getExtension();\r
+               boolean deleteDefaultContentTypeFlag = true;\r
+               if (this.container != null) {\r
+                       try {\r
+                               for (PackagePart part : this.container.getParts()) {\r
+                                       if (!part.getPartName().equals(partName)\r
+                                                       && part.getPartName().getExtension()\r
+                                                                       .equalsIgnoreCase(extensionToDelete)) {\r
+                                               deleteDefaultContentTypeFlag = false;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       } catch (InvalidFormatException e) {\r
+                               throw new InvalidOperationException(e.getMessage());\r
+                       }\r
+               }\r
+\r
+               // Remove the default content type, no other part use this content type.\r
+               if (deleteDefaultContentTypeFlag) {\r
+                       this.defaultContentType.remove(extensionToDelete);\r
+               }\r
+\r
+               /*\r
+                * Check rule 2.4: The package implementer shall require that the\r
+                * Content Types stream contain one of the following for every part in\r
+                * the package: One matching Default element One matching Override\r
+                * element Both a matching Default element and a matching Override\r
+                * element, in which case the Override element takes precedence.\r
+                */\r
+               if (this.container != null) {\r
+                       try {\r
+                               for (PackagePart part : this.container.getParts()) {\r
+                                       if (!part.getPartName().equals(partName)\r
+                                                       && this.getContentType(part.getPartName()) == null)\r
+                                               throw new InvalidOperationException(\r
+                                                               "Rule M2.4 is not respected: Nor a default element or override element is associated with the part: "\r
+                                                                               + part.getPartName().getName());\r
+                               }\r
+                       } catch (InvalidFormatException e) {\r
+                               throw new InvalidOperationException(e.getMessage());\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Check if the specified content type is already register.\r
+        * \r
+        * @param contentType\r
+        *            The content type to check.\r
+        * @return <code>true</code> if the specified content type is already\r
+        *         register, then <code>false</code>.\r
+        */\r
+       public boolean isContentTypeRegister(String contentType) {\r
+               if (contentType == null)\r
+                       throw new IllegalArgumentException("contentType");\r
+\r
+               return (this.defaultContentType.values().contains(contentType) || (this.overrideContentType != null && this.overrideContentType\r
+                               .values().contains(contentType)));\r
+       }\r
+\r
+       /**\r
+        * Get the content type for the specified part, if any.\r
+        * \r
+        * Rule [M2.9]: To get the content type of a part, the package implementer\r
+        * shall perform the steps described in��9.1.2.4:\r
+        * \r
+        * 1. Compare the part name with the values specified for the PartName\r
+        * attribute of the Override elements. The comparison shall be\r
+        * case-insensitive ASCII.\r
+        * \r
+        * 2. If there is an Override element with a matching PartName attribute,\r
+        * return the value of its ContentType attribute. No further action is\r
+        * required.\r
+        * \r
+        * 3. If there is no Override element with a matching PartName attribute,\r
+        * then a. Get the extension from the part name by taking the substring to\r
+        * the right of the rightmost occurrence of the dot character (.) from the\r
+        * rightmost segment. b. Check the Default elements of the Content Types\r
+        * stream, comparing the extension with the value of the Extension\r
+        * attribute. The comparison shall be case-insensitive ASCII.\r
+        * \r
+        * 4. If there is a Default element with a matching Extension attribute,\r
+        * return the value of its ContentType attribute. No further action is\r
+        * required.\r
+        * \r
+        * 5. If neither Override nor Default elements with matching attributes are\r
+        * found for the specified part name, the implementation shall not map this\r
+        * part name to a part.\r
+        * \r
+        * @param partUri\r
+        *            The URI part to check.\r
+        * @return The content type associated with the URI (in case of an override\r
+        *         content type) or the extension (in case of default content type),\r
+        *         else <code>null</code>.\r
+        * \r
+        * @exception OpenXML4JRuntimeException\r
+        *                Throws if the content type manager is not able to find the\r
+        *                content from an existing part.\r
+        */\r
+       public String getContentType(PackagePartName partName) {\r
+               if (partName == null)\r
+                       throw new IllegalArgumentException("partName");\r
+\r
+               if ((this.overrideContentType != null)\r
+                               && this.overrideContentType.containsKey(partName))\r
+                       return this.overrideContentType.get(partName);\r
+\r
+               String extension = partName.getExtension().toLowerCase();\r
+               if (this.defaultContentType.containsKey(extension))\r
+                       return this.defaultContentType.get(extension);\r
+\r
+               /*\r
+                * [M2.4] : The package implementer shall require that the Content Types\r
+                * stream contain one of the following for every part in the package:\r
+                * One matching Default element, One matching Override element, Both a\r
+                * matching Default element and a matching Override element, in which\r
+                * case the Override element takes precedence.\r
+                */\r
+               if (this.container != null && this.container.getPart(partName) != null) {\r
+                       throw new OpenXML4JRuntimeException(\r
+                                       "Rule M2.4 exception : this error should NEVER happen, if so please send a mail to the developers team, thanks !");\r
+               } else {\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Clear all content types.\r
+        */\r
+       public void clearAll() {\r
+               this.defaultContentType.clear();\r
+               if (this.overrideContentType != null)\r
+                       this.overrideContentType.clear();\r
+       }\r
+\r
+       /**\r
+        * Clear all override content types.\r
+        * \r
+        */\r
+       public void clearOverrideContentTypes() {\r
+               if (this.overrideContentType != null)\r
+                       this.overrideContentType.clear();\r
+       }\r
+\r
+       /**\r
+        * Parse the content types part.\r
+        * \r
+        * @throws InvalidFormatException\r
+        *             Throws if the content type doesn't exist or the XML format is\r
+        *             invalid.\r
+        */\r
+       private void parseContentTypesFile(InputStream in)\r
+                       throws InvalidFormatException {\r
+               try {\r
+                       SAXReader xmlReader = new SAXReader();\r
+                       Document xmlContentTypetDoc = xmlReader.read(in);\r
+\r
+                       // Default content types\r
+                       List defaultTypes = xmlContentTypetDoc.getRootElement().elements(\r
+                                       DEFAULT_TAG_NAME);\r
+                       Iterator elementIteratorDefault = defaultTypes.iterator();\r
+                       while (elementIteratorDefault.hasNext()) {\r
+                               Element element = (Element) elementIteratorDefault.next();\r
+                               String extension = element.attribute(EXTENSION_ATTRIBUTE_NAME)\r
+                                               .getValue();\r
+                               String contentType = element.attribute(\r
+                                               CONTENT_TYPE_ATTRIBUTE_NAME).getValue();\r
+                               addDefaultContentType(extension, contentType);\r
+                       }\r
+\r
+                       // Overriden content types\r
+                       List overrideTypes = xmlContentTypetDoc.getRootElement().elements(\r
+                                       OVERRIDE_TAG_NAME);\r
+                       Iterator elementIteratorOverride = overrideTypes.iterator();\r
+                       while (elementIteratorOverride.hasNext()) {\r
+                               Element element = (Element) elementIteratorOverride.next();\r
+                               URI uri = new URI(element.attribute(PART_NAME_ATTRIBUTE_NAME)\r
+                                               .getValue());\r
+                               PackagePartName partName = PackagingURIHelper\r
+                                               .createPartName(uri);\r
+                               String contentType = element.attribute(\r
+                                               CONTENT_TYPE_ATTRIBUTE_NAME).getValue();\r
+                               addOverrideContentType(partName, contentType);\r
+                       }\r
+               } catch (URISyntaxException urie) {\r
+                       throw new InvalidFormatException(urie.getMessage());\r
+               } catch (DocumentException e) {\r
+                       throw new InvalidFormatException(e.getMessage());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Save the contents type part.\r
+        * \r
+        * @param outStream\r
+        *            The output stream use to save the XML content of the content\r
+        *            types part.\r
+        * @return <b>true</b> if the operation success, else <b>false</b>.\r
+        */\r
+       public boolean save(OutputStream outStream) {\r
+               Document xmlOutDoc = DocumentHelper.createDocument();\r
+\r
+               // Building namespace\r
+               Namespace dfNs = Namespace.get("", TYPES_NAMESPACE_URI);\r
+               Element typesElem = xmlOutDoc\r
+                               .addElement(new QName(TYPES_TAG_NAME, dfNs));\r
+\r
+               // Adding default types\r
+               for (Entry<String, String> entry : defaultContentType.entrySet()) {\r
+                       appendDefaultType(typesElem, entry);\r
+               }\r
+\r
+               // Adding specific types if any exist\r
+               if (overrideContentType != null) {\r
+                       for (Entry<PackagePartName, String> entry : overrideContentType\r
+                                       .entrySet()) {\r
+                               appendSpecificTypes(typesElem, entry);\r
+                       }\r
+               }\r
+               xmlOutDoc.normalize();\r
+\r
+               // Save content in the specified output stream\r
+               return this.saveImpl(xmlOutDoc, outStream);\r
+       }\r
+\r
+       /**\r
+        * Use to append specific type XML elements, use by the save() method.\r
+        * \r
+        * @param root\r
+        *            XML parent element use to append this override type element.\r
+        * @param entry\r
+        *            The values to append.\r
+        * @see #save(ZipOutputStream)\r
+        */\r
+       private void appendSpecificTypes(Element root,\r
+                       Entry<PackagePartName, String> entry) {\r
+               root.addElement(OVERRIDE_TAG_NAME).addAttribute(\r
+                               PART_NAME_ATTRIBUTE_NAME,\r
+                               ((PackagePartName) entry.getKey()).getName()).addAttribute(\r
+                               CONTENT_TYPE_ATTRIBUTE_NAME, (String) entry.getValue());\r
+       }\r
+\r
+       /**\r
+        * Use to append default types XML elements, use by the save() metid.\r
+        * \r
+        * @param root\r
+        *            XML parent element use to append this default type element.\r
+        * @param entry\r
+        *            The values to append.\r
+        * @see #save(ZipOutputStream)\r
+        */\r
+       private void appendDefaultType(Element root, Entry<String, String> entry) {\r
+               root.addElement(DEFAULT_TAG_NAME).addAttribute(\r
+                               EXTENSION_ATTRIBUTE_NAME, (String) entry.getKey())\r
+                               .addAttribute(CONTENT_TYPE_ATTRIBUTE_NAME,\r
+                                               (String) entry.getValue());\r
+\r
+       }\r
+\r
+       /**\r
+        * Specific implementation of the save method. Call by the save() method,\r
+        * call before exiting.\r
+        * \r
+        * @param out\r
+        *            The output stream use to write the content type XML.\r
+        */\r
+       public abstract boolean saveImpl(Document content, OutputStream out);\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/FileHelper.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/FileHelper.java
new file mode 100755 (executable)
index 0000000..ad31157
--- /dev/null
@@ -0,0 +1,91 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.nio.channels.FileChannel;\r
+\r
+/**\r
+ * Provide useful method to manage file.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public final class FileHelper {\r
+\r
+       /**\r
+        * Get the directory part of the specified file path.\r
+        * \r
+        * @param f\r
+        *            File to process.\r
+        * @return The directory path from the specified\r
+        */\r
+       public static File getDirectory(File f) {\r
+               if (f != null) {\r
+                       String path = f.getPath();\r
+                       int len = path.length();\r
+                       int num2 = len;\r
+                       while (--num2 >= 0) {\r
+                               char ch1 = path.charAt(num2);\r
+                               if (ch1 == File.separatorChar) {\r
+                                       return new File(path.substring(0, num2));\r
+                               }\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Copy a file.\r
+        * \r
+        * @param in\r
+        *            The source file.\r
+        * @param out\r
+        *            The target location.\r
+        * @throws IOException\r
+        *             If an I/O error occur.\r
+        */\r
+       public static void copyFile(File in, File out) throws IOException {\r
+               FileChannel sourceChannel = new FileInputStream(in).getChannel();\r
+               FileChannel destinationChannel = new FileOutputStream(out).getChannel();\r
+               sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);\r
+               sourceChannel.close();\r
+               destinationChannel.close();\r
+       }\r
+\r
+       /**\r
+        * Get file name from the specified File object.\r
+        */\r
+       public static String getFilename(File file) {\r
+               if (file != null) {\r
+                       String path = file.getPath();\r
+                       int len = path.length();\r
+                       int num2 = len;\r
+                       while (--num2 >= 0) {\r
+                               char ch1 = path.charAt(num2);\r
+                               if (ch1 == File.separatorChar)\r
+                                       return path.substring(num2 + 1, len);\r
+                       }\r
+               }\r
+               return "";\r
+       }\r
+\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePart.java
new file mode 100755 (executable)
index 0000000..8f29a45
--- /dev/null
@@ -0,0 +1,126 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;\r
+\r
+/**\r
+ * Memory version of a package part. Use to\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public final class MemoryPackagePart extends PackagePart {\r
+\r
+       /**\r
+        * Storage for the part data.\r
+        */\r
+       protected byte[] data;\r
+\r
+       /**\r
+        * Size of data.\r
+        */\r
+       protected int length;\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param pack\r
+        *            The owner package.\r
+        * @param partName\r
+        *            The part name.\r
+        * @param contentType\r
+        *            The content type.\r
+        * @throws InvalidFormatException\r
+        *             If the specified URI is not OPC compliant.\r
+        */\r
+       public MemoryPackagePart(Package pack, PackagePartName partName,\r
+                       String contentType) throws InvalidFormatException {\r
+               super(pack, partName, contentType);\r
+       }\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param pack\r
+        *            The owner package.\r
+        * @param partName\r
+        *            The part name.\r
+        * @param contentType\r
+        *            The content type.\r
+        * @param loadRelationships\r
+        *            Specify if the relationships will be loaded.\r
+        * @throws InvalidFormatException\r
+        *             If the specified URI is not OPC compliant.\r
+        */\r
+       public MemoryPackagePart(Package pack, PackagePartName partName,\r
+                       String contentType, boolean loadRelationships)\r
+                       throws InvalidFormatException {\r
+               super(pack, partName, new ContentType(contentType), loadRelationships);\r
+       }\r
+\r
+       @Override\r
+       protected InputStream getInputStreamImpl() {\r
+               // If this part has been created from scratch and/or the data buffer is\r
+               // not\r
+               // initialize, so we do it now.\r
+               if (data == null) {\r
+                       data = new byte[0];\r
+               }\r
+               return new ByteArrayInputStream(data);\r
+       }\r
+\r
+       @Override\r
+       protected OutputStream getOutputStreamImpl() {\r
+               return new MemoryPackagePartOutputStream(this);\r
+       }\r
+\r
+       public void clear() {\r
+               data = null;\r
+               length = 0;\r
+       }\r
+\r
+       @Override\r
+       public boolean save(OutputStream os) throws OpenXML4JException {\r
+               return new ZipPartMarshaller().marshall(this, os);\r
+       }\r
+\r
+       @Override\r
+       public boolean load(InputStream ios) throws InvalidFormatException {\r
+               throw new InvalidFormatException("Method not implemented");\r
+       }\r
+\r
+       @Override\r
+       public void close() {\r
+               // Do nothing\r
+       }\r
+\r
+       @Override\r
+       public void flush() {\r
+               // Do nothing\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePartOutputStream.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePartOutputStream.java
new file mode 100755 (executable)
index 0000000..debe3d1
--- /dev/null
@@ -0,0 +1,96 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+/**\r
+ * Build an output stream for MemoryPackagePart.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public final class MemoryPackagePartOutputStream extends OutputStream {\r
+\r
+       private MemoryPackagePart part;\r
+\r
+       private ByteArrayOutputStream buff;\r
+\r
+       public MemoryPackagePartOutputStream(MemoryPackagePart part) {\r
+               this.part = part;\r
+               buff = new ByteArrayOutputStream();\r
+       }\r
+\r
+       @Override\r
+       public void write(int b) throws IOException {\r
+               buff.write(b);\r
+       }\r
+\r
+       /**\r
+        * Close this stream and flush the content.\r
+        * @see #flush() \r
+        */\r
+       @Override\r
+       public void close() throws IOException {\r
+               this.flush();\r
+       }\r
+\r
+       /**\r
+        * Flush this output stream. This method is called by the close() method.\r
+        * Warning : don't call this method for output consistency.\r
+        * @see #close()\r
+        */\r
+       @Override\r
+       public void flush() throws IOException {\r
+               buff.flush();\r
+               if (part.data != null) {\r
+                       byte[] newArray = new byte[part.data.length + buff.size()];\r
+                       // copy the previous contents of part.data in newArray\r
+                       System.arraycopy(part.data, 0, newArray, 0, part.data.length);\r
+\r
+                       // append the newly added data\r
+                       byte[] buffArr = buff.toByteArray();\r
+                       System.arraycopy(buffArr, 0, newArray, part.data.length,\r
+                                       buffArr.length);\r
+\r
+                       // save the result as new data\r
+                       part.data = newArray;\r
+               } else {\r
+                       // was empty, just fill it\r
+                       part.data = buff.toByteArray();\r
+               }\r
+               \r
+               /* \r
+                * Clear this streams buffer, in case flush() is called a second time\r
+                * Fix bug 1921637 - provided by Rainer Schwarze\r
+                */\r
+               buff.reset();\r
+       }\r
+\r
+       @Override\r
+       public void write(byte[] b, int off, int len) throws IOException {\r
+               buff.write(b, off, len);\r
+       }\r
+\r
+       @Override\r
+       public void write(byte[] b) throws IOException {\r
+               buff.write(b);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java
new file mode 100755 (executable)
index 0000000..a9f60b0
--- /dev/null
@@ -0,0 +1,621 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.text.ParsePosition;\r
+import java.text.SimpleDateFormat;\r
+import java.util.Date;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.ContentTypes;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackageProperties;\r
+import org.apache.poi.openxml4j.util.Nullable;\r
+\r
+/**\r
+ * Represents the core properties part of a package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public class PackagePropertiesPart extends PackagePart implements\r
+               PackageProperties {\r
+\r
+       public final static String NAMESPACE_DC_URI = "http://purl.org/dc/elements/1.1/";\r
+\r
+       public final static String NAMESPACE_CP_URI = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";\r
+\r
+       public final static String NAMESPACE_DCTERMS_URI = "http://purl.org/dc/terms/";\r
+\r
+       public final static String NAMESPACE_XSI_URI = "http://www.w3.org/2001/XMLSchema-instance";\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param pack\r
+        *            Container package.\r
+        * @param partName\r
+        *            Name of this part.\r
+        * @throws InvalidFormatException\r
+        *             Throws if the content is invalid.\r
+        */\r
+       public PackagePropertiesPart(Package pack, PackagePartName partName)\r
+                       throws InvalidFormatException {\r
+               super(pack, partName, ContentTypes.CORE_PROPERTIES_PART);\r
+       }\r
+\r
+       /**\r
+        * A categorization of the content of this package.\r
+        * \r
+        * [Example: Example values for this property might include: Resume, Letter,\r
+        * Financial Forecast, Proposal, Technical Presentation, and so on. This\r
+        * value might be used by an application's user interface to facilitate\r
+        * navigation of a large set of documents. end example]\r
+        */\r
+       protected Nullable<String> category = new Nullable<String>();\r
+\r
+       /**\r
+        * The status of the content.\r
+        * \r
+        * [Example: Values might include "Draft", "Reviewed", and "Final". end\r
+        * example]\r
+        */\r
+       protected Nullable<String> contentStatus = new Nullable<String>();\r
+\r
+       /**\r
+        * The type of content represented, generally defined by a specific use and\r
+        * intended audience.\r
+        * \r
+        * [Example: Values might include "Whitepaper", "Security Bulletin", and\r
+        * "Exam". end example] [Note: This property is distinct from MIME content\r
+        * types as defined in RFC 2616. end note]\r
+        */\r
+       protected Nullable<String> contentType = new Nullable<String>();\r
+\r
+       /**\r
+        * Date of creation of the resource.\r
+        */\r
+       protected Nullable<Date> created = new Nullable<Date>();\r
+\r
+       /**\r
+        * An entity primarily responsible for making the content of the resource.\r
+        */\r
+       protected Nullable<String> creator = new Nullable<String>();\r
+\r
+       /**\r
+        * An explanation of the content of the resource.\r
+        * \r
+        * [Example: Values might include an abstract, table of contents, reference\r
+        * to a graphical representation of content, and a free-text account of the\r
+        * content. end example]\r
+        */\r
+       protected Nullable<String> description = new Nullable<String>();\r
+\r
+       /**\r
+        * An unambiguous reference to the resource within a given context.\r
+        */\r
+       protected Nullable<String> identifier = new Nullable<String>();\r
+\r
+       /**\r
+        * A delimited set of keywords to support searching and indexing. This is\r
+        * typically a list of terms that are not available elsewhere in the\r
+        * properties.\r
+        */\r
+       protected Nullable<String> keywords = new Nullable<String>();\r
+\r
+       /**\r
+        * The language of the intellectual content of the resource.\r
+        * \r
+        * [Note: IETF RFC 3066 provides guidance on encoding to represent\r
+        * languages. end note]\r
+        */\r
+       protected Nullable<String> language = new Nullable<String>();\r
+\r
+       /**\r
+        * The user who performed the last modification. The identification is\r
+        * environment-specific.\r
+        * \r
+        * [Example: A name, email address, or employee ID. end example] It is\r
+        * recommended that this value be as concise as possible.\r
+        */\r
+       protected Nullable<String> lastModifiedBy = new Nullable<String>();\r
+\r
+       /**\r
+        * The date and time of the last printing.\r
+        */\r
+       protected Nullable<Date> lastPrinted = new Nullable<Date>();\r
+\r
+       /**\r
+        * Date on which the resource was changed.\r
+        */\r
+       protected Nullable<Date> modified = new Nullable<Date>();\r
+\r
+       /**\r
+        * The revision number.\r
+        * \r
+        * [Example: This value might indicate the number of saves or revisions,\r
+        * provided the application updates it after each revision. end example]\r
+        */\r
+       protected Nullable<String> revision = new Nullable<String>();\r
+\r
+       /**\r
+        * The topic of the content of the resource.\r
+        */\r
+       protected Nullable<String> subject = new Nullable<String>();\r
+\r
+       /**\r
+        * The name given to the resource.\r
+        */\r
+       protected Nullable<String> title = new Nullable<String>();\r
+\r
+       /**\r
+        * The version number. This value is set by the user or by the application.\r
+        */\r
+       protected Nullable<String> version = new Nullable<String>();\r
+\r
+       /*\r
+        * Getters and setters\r
+        */\r
+\r
+       /**\r
+        * Get the category property.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getCategoryProperty()\r
+        */\r
+       public Nullable<String> getCategoryProperty() {\r
+               return category;\r
+       }\r
+\r
+       /**\r
+        * Get content status.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getContentStatusProperty()\r
+        */\r
+       public Nullable<String> getContentStatusProperty() {\r
+               return contentStatus;\r
+       }\r
+\r
+       /**\r
+        * Get content type.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getContentTypeProperty()\r
+        */\r
+       public Nullable<String> getContentTypeProperty() {\r
+               return contentType;\r
+       }\r
+\r
+       /**\r
+        * Get created date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getCreatedProperty()\r
+        */\r
+       public Nullable<Date> getCreatedProperty() {\r
+               return created;\r
+       }\r
+\r
+       /**\r
+        * Get created date formated into a String.\r
+        * \r
+        * @return A string representation of the created date.\r
+        */\r
+       public String getCreatedPropertyString() {\r
+               return getDateValue(created);\r
+       }\r
+\r
+       /**\r
+        * Get creator.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getCreatorProperty()\r
+        */\r
+       public Nullable<String> getCreatorProperty() {\r
+               return creator;\r
+       }\r
+\r
+       /**\r
+        * Get description.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getDescriptionProperty()\r
+        */\r
+       public Nullable<String> getDescriptionProperty() {\r
+               return description;\r
+       }\r
+\r
+       /**\r
+        * Get identifier.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getIdentifierProperty()\r
+        */\r
+       public Nullable<String> getIdentifierProperty() {\r
+               return identifier;\r
+       }\r
+\r
+       /**\r
+        * Get keywords.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getKeywordsProperty()\r
+        */\r
+       public Nullable<String> getKeywordsProperty() {\r
+               return keywords;\r
+       }\r
+\r
+       /**\r
+        * Get the language.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getLanguageProperty()\r
+        */\r
+       public Nullable<String> getLanguageProperty() {\r
+               return language;\r
+       }\r
+\r
+       /**\r
+        * Get the author of last modifications.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getLastModifiedByProperty()\r
+        */\r
+       public Nullable<String> getLastModifiedByProperty() {\r
+               return lastModifiedBy;\r
+       }\r
+\r
+       /**\r
+        * Get last printed date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getLastPrintedProperty()\r
+        */\r
+       public Nullable<Date> getLastPrintedProperty() {\r
+               return lastPrinted;\r
+       }\r
+\r
+       /**\r
+        * Get last printed date formated into a String.\r
+        * \r
+        * @return A string representation of the last printed date.\r
+        */\r
+       public String getLastPrintedPropertyString() {\r
+               return getDateValue(created);\r
+       }\r
+\r
+       /**\r
+        * Get modified date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getModifiedProperty()\r
+        */\r
+       public Nullable<Date> getModifiedProperty() {\r
+               return modified;\r
+       }\r
+\r
+       /**\r
+        * Get modified date formated into a String.\r
+        * \r
+        * @return A string representation of the modified date.\r
+        */\r
+       public String getModifiedPropertyString() {\r
+               if (!modified.hasValue())\r
+                       return getDateValue(new Nullable<Date>(new Date()));\r
+               else\r
+                       return getDateValue(modified);\r
+       }\r
+\r
+       /**\r
+        * Get revision.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getRevisionProperty()\r
+        */\r
+       public Nullable<String> getRevisionProperty() {\r
+               return revision;\r
+       }\r
+\r
+       /**\r
+        * Get subject.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getSubjectProperty()\r
+        */\r
+       public Nullable<String> getSubjectProperty() {\r
+               return subject;\r
+       }\r
+\r
+       /**\r
+        * Get title.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getTitleProperty()\r
+        */\r
+       public Nullable<String> getTitleProperty() {\r
+               return title;\r
+       }\r
+\r
+       /**\r
+        * Get version.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#getVersionProperty()\r
+        */\r
+       public Nullable<String> getVersionProperty() {\r
+               return version;\r
+       }\r
+\r
+       /**\r
+        * Set the category.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setCategoryProperty(java.lang.String)\r
+        */\r
+       public void setCategoryProperty(String category) {\r
+               this.category = setStringValue(category);\r
+       }\r
+\r
+       /**\r
+        * Set the content status.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setContentStatusProperty(java.lang.String)\r
+        */\r
+       public void setContentStatusProperty(String contentStatus) {\r
+               this.contentStatus = setStringValue(contentStatus);\r
+       }\r
+\r
+       /**\r
+        * Set the content type.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setContentTypeProperty(java.lang.String)\r
+        */\r
+       public void setContentTypeProperty(String contentType) {\r
+               this.contentType = setStringValue(contentType);\r
+       }\r
+\r
+       /**\r
+        * Set the created date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setCreatedProperty(org.apache.poi.openxml4j.util.Nullable)\r
+        */\r
+       public void setCreatedProperty(String created) {\r
+               try {\r
+                       this.created = setDateValue(created);\r
+               } catch (InvalidFormatException e) {\r
+                       new IllegalArgumentException("created  : "\r
+                                       + e.getLocalizedMessage());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Set the created date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setCreatedProperty(org.apache.poi.openxml4j.util.Nullable)\r
+        */\r
+       public void setCreatedProperty(Nullable<Date> created) {\r
+               if (created.hasValue())\r
+                       this.created = created;\r
+       }\r
+\r
+       /**\r
+        * Set the creator.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setCreatorProperty(java.lang.String)\r
+        */\r
+       public void setCreatorProperty(String creator) {\r
+               this.creator = setStringValue(creator);\r
+       }\r
+\r
+       /**\r
+        * Set the description.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setDescriptionProperty(java.lang.String)\r
+        */\r
+       public void setDescriptionProperty(String description) {\r
+               this.description = setStringValue(description);\r
+       }\r
+\r
+       /**\r
+        * Set identifier.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setIdentifierProperty(java.lang.String)\r
+        */\r
+       public void setIdentifierProperty(String identifier) {\r
+               this.identifier = setStringValue(identifier);\r
+       }\r
+\r
+       /**\r
+        * Set keywords.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setKeywordsProperty(java.lang.String)\r
+        */\r
+       public void setKeywordsProperty(String keywords) {\r
+               this.keywords = setStringValue(keywords);\r
+       }\r
+\r
+       /**\r
+        * Set language.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setLanguageProperty(java.lang.String)\r
+        */\r
+       public void setLanguageProperty(String language) {\r
+               this.language = setStringValue(language);\r
+       }\r
+\r
+       /**\r
+        * Set last modifications author.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setLastModifiedByProperty(java.lang.String)\r
+        */\r
+       public void setLastModifiedByProperty(String lastModifiedBy) {\r
+               this.lastModifiedBy = setStringValue(lastModifiedBy);\r
+       }\r
+\r
+       /**\r
+        * Set last printed date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setLastPrintedProperty(org.apache.poi.openxml4j.util.Nullable)\r
+        */\r
+       public void setLastPrintedProperty(String lastPrinted) {\r
+               try {\r
+                       this.lastPrinted = setDateValue(lastPrinted);\r
+               } catch (InvalidFormatException e) {\r
+                       new IllegalArgumentException("lastPrinted  : "\r
+                                       + e.getLocalizedMessage());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Set last printed date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setLastPrintedProperty(org.apache.poi.openxml4j.util.Nullable)\r
+        */\r
+       public void setLastPrintedProperty(Nullable<Date> lastPrinted) {\r
+               if (lastPrinted.hasValue())\r
+                       this.lastPrinted = lastPrinted;\r
+       }\r
+\r
+       /**\r
+        * Set last modification date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setModifiedProperty(org.apache.poi.openxml4j.util.Nullable)\r
+        */\r
+       public void setModifiedProperty(String modified) {\r
+               try {\r
+                       this.modified = setDateValue(modified);\r
+               } catch (InvalidFormatException e) {\r
+                       new IllegalArgumentException("modified  : "\r
+                                       + e.getLocalizedMessage());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Set last modification date.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setModifiedProperty(org.apache.poi.openxml4j.util.Nullable)\r
+        */\r
+       public void setModifiedProperty(Nullable<Date> modified) {\r
+               if (modified.hasValue())\r
+                       this.modified = modified;\r
+       }\r
+\r
+       /**\r
+        * Set revision.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setRevisionProperty(java.lang.String)\r
+        */\r
+       public void setRevisionProperty(String revision) {\r
+               this.revision = setStringValue(revision);\r
+       }\r
+\r
+       /**\r
+        * Set subject.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setSubjectProperty(java.lang.String)\r
+        */\r
+       public void setSubjectProperty(String subject) {\r
+               this.subject = setStringValue(subject);\r
+       }\r
+\r
+       /**\r
+        * Set title.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setTitleProperty(java.lang.String)\r
+        */\r
+       public void setTitleProperty(String title) {\r
+               this.title = setStringValue(title);\r
+       }\r
+\r
+       /**\r
+        * Set version.\r
+        * \r
+        * @see org.apache.poi.openxml4j.opc.PackageProperties#setVersionProperty(java.lang.String)\r
+        */\r
+       public void setVersionProperty(String version) {\r
+               this.version = setStringValue(version);\r
+       }\r
+\r
+       /**\r
+        * Convert a strig value into a Nullable<String>\r
+        */\r
+       private Nullable<String> setStringValue(String s) {\r
+               if (s == null || s.equals(""))\r
+                       return new Nullable<String>();\r
+               else\r
+                       return new Nullable<String>(s);\r
+       }\r
+\r
+       /**\r
+        * Convert a string value represented a date into a Nullable<Date>.\r
+        * \r
+        * @throws InvalidFormatException\r
+        *             Throws if the date format isnot valid.\r
+        */\r
+       private Nullable<Date> setDateValue(String s) throws InvalidFormatException {\r
+               if (s == null || s.equals(""))\r
+                       return new Nullable<Date>();\r
+               else {\r
+                       SimpleDateFormat df = new SimpleDateFormat(\r
+                                       "yyyy-MM-dd'T'HH:mm:ss'Z'");\r
+                       Date d = df.parse(s, new ParsePosition(0));\r
+                       if (d == null)\r
+                               throw new InvalidFormatException("Date not well formated");\r
+                       return new Nullable<Date>(d);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Convert a Nullable<Date> into a String.\r
+        * \r
+        * @param d\r
+        *            The Date to convert.\r
+        * @return The formated date or null.\r
+        * @see java.util.SimpleDateFormat\r
+        */\r
+       private String getDateValue(Nullable<Date> d) {\r
+               if (d == null || d.equals(""))\r
+                       return "";\r
+               else {\r
+                       SimpleDateFormat df = new SimpleDateFormat(\r
+                                       "yyyy-MM-dd'T'HH:mm:ss'Z'");\r
+                       return df.format(d.getValue());\r
+               }\r
+       }\r
+\r
+       @Override\r
+       protected InputStream getInputStreamImpl() {\r
+               throw new InvalidOperationException("Operation not authorized");\r
+       }\r
+\r
+       @Override\r
+       protected OutputStream getOutputStreamImpl() {\r
+               throw new InvalidOperationException(\r
+                               "Can't use output stream to set properties !");\r
+       }\r
+\r
+       @Override\r
+       public boolean save(OutputStream zos) throws OpenXML4JException {\r
+               throw new InvalidOperationException("Operation not authorized");\r
+       }\r
+\r
+       @Override\r
+       public boolean load(InputStream ios) throws InvalidFormatException {\r
+               throw new InvalidOperationException("Operation not authorized");\r
+       }\r
+\r
+       @Override\r
+       public void close() {\r
+               // Do nothing\r
+       }\r
+\r
+       @Override\r
+       public void flush() {\r
+               // Do nothing\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartMarshaller.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartMarshaller.java
new file mode 100755 (executable)
index 0000000..70bbcae
--- /dev/null
@@ -0,0 +1,49 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+\r
+/**\r
+ * Object implemented this interface are considered as part marshaller. A part\r
+ * marshaller is responsible to marshall a part in order to be save in a\r
+ * package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public interface PartMarshaller {\r
+\r
+       /**\r
+        * Save the content of the package in the stream\r
+        * \r
+        * @param part\r
+        *            Part to marshall.\r
+        * @param out\r
+        *            The output stream into which the part will be marshall.\r
+        * @return <b>false</b> if any marshall error occurs, else <b>true</b>\r
+        * @throws OpenXML4JException\r
+        *             Throws only if any other exceptions are thrown by inner\r
+        *             methods.\r
+        */\r
+       public boolean marshall(PackagePart part, OutputStream out)\r
+                       throws OpenXML4JException;\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartUnmarshaller.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartUnmarshaller.java
new file mode 100755 (executable)
index 0000000..0b17cb8
--- /dev/null
@@ -0,0 +1,50 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.UnmarshallContext;\r
+\r
+/**\r
+ * Object implemented this interface are considered as part unmarshaller. A part\r
+ * unmarshaller is responsible to unmarshall a part in order to load it from a\r
+ * package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public interface PartUnmarshaller {\r
+\r
+       /**\r
+        * Save the content of the package in the stream\r
+        * \r
+        * @param in\r
+        *            The input stream from which the part will be unmarshall.\r
+        * @return The part freshly unmarshall from the input stream.\r
+        * @throws OpenXML4JException\r
+        *             Throws only if any other exceptions are thrown by inner\r
+        *             methods.\r
+        */\r
+       public PackagePart unmarshall(UnmarshallContext context, InputStream in)\r
+                       throws InvalidFormatException, IOException;\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java
new file mode 100755 (executable)
index 0000000..5f894f2
--- /dev/null
@@ -0,0 +1,90 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipOutputStream;\r
+\r
+import org.dom4j.Document;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.StreamHelper;\r
+\r
+/**\r
+ * Zip implementation of the ContentTypeManager.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ * @see ContentTypeManager\r
+ */\r
+public class ZipContentTypeManager extends ContentTypeManager {\r
+\r
+       /**\r
+        * Delegate constructor to the super constructor.\r
+        * \r
+        * @param in\r
+        *            The input stream to parse to fill internal content type\r
+        *            collections.\r
+        * @throws InvalidFormatException\r
+        *             If the content types part content is not valid.\r
+        */\r
+       public ZipContentTypeManager(InputStream in, Package pkg)\r
+                       throws InvalidFormatException {\r
+               super(in, pkg);\r
+       }\r
+\r
+       @Override\r
+       public boolean saveImpl(Document content, OutputStream out) {\r
+               ZipOutputStream zos = null;\r
+               if (out instanceof ZipOutputStream)\r
+                       zos = (ZipOutputStream) out;\r
+               else\r
+                       zos = new ZipOutputStream(out);\r
+\r
+               ZipEntry partEntry = new ZipEntry(CONTENT_TYPES_PART_NAME);\r
+               try {\r
+                       // Referenced in ZIP\r
+                       zos.putNextEntry(partEntry);\r
+                       // Saving data in the ZIP file\r
+                       ByteArrayOutputStream outTemp = new ByteArrayOutputStream();\r
+                       StreamHelper.saveXmlInStream(content, out);\r
+                       InputStream ins = new ByteArrayInputStream(outTemp.toByteArray());\r
+                       byte[] buff = new byte[ZipHelper.READ_WRITE_FILE_BUFFER_SIZE];\r
+                       while (ins.available() > 0) {\r
+                               int resultRead = ins.read(buff);\r
+                               if (resultRead == -1) {\r
+                                       // end of file reached\r
+                                       break;\r
+                               } else {\r
+                                       zos.write(buff, 0, resultRead);\r
+                               }\r
+                       }\r
+                       zos.closeEntry();\r
+               } catch (IOException ioe) {\r
+                       logger.error("Cannot write: " + CONTENT_TYPES_PART_NAME\r
+                                       + " in Zip !", ioe);\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java
new file mode 100755 (executable)
index 0000000..10b2339
--- /dev/null
@@ -0,0 +1,163 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+import java.util.Enumeration;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipFile;\r
+\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
+import org.apache.poi.openxml4j.opc.ZipPackage;\r
+\r
+public final class ZipHelper {\r
+\r
+       /**\r
+        * Forward slash use to convert part name between OPC and zip item naming\r
+        * conventions.\r
+        */\r
+       private final static String FORWARD_SLASH = "/";\r
+       \r
+       /**\r
+        * Buffer to read data from file. Use big buffer to improve performaces. the\r
+        * InputStream class is reading only 8192 bytes per read call (default value\r
+        * set by sun)\r
+        */\r
+       public static final int READ_WRITE_FILE_BUFFER_SIZE = 8192;\r
+\r
+       /**\r
+        * Prevent this class to be instancied.\r
+        */\r
+       private ZipHelper() {\r
+               // Do nothing\r
+       }\r
+\r
+       /**\r
+        * Retrieve the zip entry of the core properties part.\r
+        * \r
+        * @throws OpenXML4JException\r
+        *             Throws if internal error occurs.\r
+        */\r
+       public static ZipEntry getCorePropertiesZipEntry(ZipPackage pkg)\r
+                       throws OpenXML4JException {\r
+               PackageRelationship corePropsRel = pkg.getRelationshipsByType(\r
+                               PackageRelationshipTypes.CORE_PROPERTIES).getRelationship(0);\r
+\r
+               if (corePropsRel == null)\r
+                       return null;\r
+\r
+               return new ZipEntry(corePropsRel.getTargetURI().getPath());\r
+       }\r
+\r
+       /**\r
+        * Retrieve the Zip entry of the content types part.\r
+        */\r
+       public static ZipEntry getContentTypeZipEntry(ZipPackage pkg) {\r
+               Enumeration entries = pkg.getZipArchive().getEntries();\r
+               // Enumerate through the Zip entries until we find the one named\r
+               // '[Content_Types].xml'.\r
+               while (entries.hasMoreElements()) {\r
+                       ZipEntry entry = (ZipEntry) entries.nextElement();\r
+                       if (entry.getName().equals(\r
+                                       ContentTypeManager.CONTENT_TYPES_PART_NAME))\r
+                               return entry;\r
+               }\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Convert a zip name into an OPC name by adding a leading forward slash to\r
+        * the specified item name.\r
+        * \r
+        * @param zipItemName\r
+        *            Zip item name to convert.\r
+        * @return An OPC compliant name.\r
+        */\r
+       public static String getOPCNameFromZipItemName(String zipItemName) {\r
+               if (zipItemName == null)\r
+                       throw new IllegalArgumentException("zipItemName");\r
+               if (zipItemName.startsWith(FORWARD_SLASH))\r
+                       return zipItemName;\r
+               else\r
+                       return FORWARD_SLASH + zipItemName;\r
+       }\r
+\r
+       /**\r
+        * Convert an OPC item name into a zip item name by removing any leading\r
+        * forward slash if it exist.\r
+        * \r
+        * @param opcItemName\r
+        *            The OPC item name to convert.\r
+        * @return A zip item name without any leading slashes.\r
+        */\r
+       public static String getZipItemNameFromOPCName(String opcItemName) {\r
+               if (opcItemName == null)\r
+                       throw new IllegalArgumentException("opcItemName");\r
+\r
+               String retVal = new String(opcItemName);\r
+               while (retVal.startsWith(FORWARD_SLASH))\r
+                       retVal = retVal.substring(1);\r
+               return retVal;\r
+       }\r
+\r
+       /**\r
+        * Convert an OPC item name into a zip URI by removing any leading forward\r
+        * slash if it exist.\r
+        * \r
+        * @param opcItemName\r
+        *            The OPC item name to convert.\r
+        * @return A zip URI without any leading slashes.\r
+        */\r
+       public static URI getZipURIFromOPCName(String opcItemName) {\r
+               if (opcItemName == null)\r
+                       throw new IllegalArgumentException("opcItemName");\r
+\r
+               String retVal = new String(opcItemName);\r
+               while (retVal.startsWith(FORWARD_SLASH))\r
+                       retVal = retVal.substring(1);\r
+               try {\r
+                       return new URI(retVal);\r
+               } catch (URISyntaxException e) {\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Retrieve and open a zip file with the specified path.\r
+        * \r
+        * @param path\r
+        *            The file path.\r
+        * @return The zip archive freshly open.\r
+        */\r
+       public static ZipFile openZipFile(String path) {\r
+               File f = new File(path);\r
+               try {\r
+                       if (!f.exists()) {\r
+                               return null;\r
+                       }\r
+                       return new ZipFile(f);\r
+               } catch (IOException ioe) {\r
+                       return null;\r
+               }\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/DefaultMarshaller.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/DefaultMarshaller.java
new file mode 100755 (executable)
index 0000000..8138cda
--- /dev/null
@@ -0,0 +1,45 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.marshallers;\r
+\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;\r
+\r
+/**\r
+ * Default marshaller that specified that the part is responsible to marshall its content.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ * @see PartMarshaller\r
+ */\r
+public class DefaultMarshaller implements PartMarshaller {\r
+\r
+       /**\r
+        * Save part in the output stream by using the save() method of the part.\r
+        * \r
+        * @throws OpenXML4JException\r
+        *             If any error occur.\r
+        */\r
+       public boolean marshall(PackagePart part, OutputStream out)\r
+                       throws OpenXML4JException {\r
+               return part.save(out);\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/PackagePropertiesMarshaller.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/PackagePropertiesMarshaller.java
new file mode 100755 (executable)
index 0000000..438cc5d
--- /dev/null
@@ -0,0 +1,434 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.marshallers;\r
+\r
+import java.io.OutputStream;\r
+\r
+import org.dom4j.Document;\r
+import org.dom4j.DocumentHelper;\r
+import org.dom4j.Element;\r
+import org.dom4j.Namespace;\r
+import org.dom4j.QName;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;\r
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;\r
+\r
+/**\r
+ * Package properties marshaller.\r
+ * \r
+ * @author CDubet, Julien Chable\r
+ * @version 1.0\r
+ */\r
+public class PackagePropertiesMarshaller implements PartMarshaller {\r
+\r
+       private final static Namespace namespaceDC = new Namespace("dc",\r
+                       PackagePropertiesPart.NAMESPACE_DC_URI);\r
+\r
+       private final static Namespace namespaceCoreProperties = new Namespace("",\r
+                       PackagePropertiesPart.NAMESPACE_CP_URI);\r
+\r
+       private final static Namespace namespaceDcTerms = new Namespace("dcterms",\r
+                       PackagePropertiesPart.NAMESPACE_DCTERMS_URI);\r
+\r
+       private final static Namespace namespaceXSI = new Namespace("xsi",\r
+                       PackagePropertiesPart.NAMESPACE_XSI_URI);\r
+\r
+       protected static final String KEYWORD_CATEGORY = "category";\r
+\r
+       protected static final String KEYWORD_CONTENT_STATUS = "contentStatus";\r
+\r
+       protected static final String KEYWORD_CONTENT_TYPE = "contentType";\r
+\r
+       protected static final String KEYWORD_CREATED = "created";\r
+\r
+       protected static final String KEYWORD_CREATOR = "creator";\r
+\r
+       protected static final String KEYWORD_DESCRIPTION = "description";\r
+\r
+       protected static final String KEYWORD_IDENTIFIER = "identifier";\r
+\r
+       protected static final String KEYWORD_KEYWORDS = "keywords";\r
+\r
+       protected static final String KEYWORD_LANGUAGE = "language";\r
+\r
+       protected static final String KEYWORD_LAST_MODIFIED_BY = "lastModifiedBy";\r
+\r
+       protected static final String KEYWORD_LAST_PRINTED = "lastPrinted";\r
+\r
+       protected static final String KEYWORD_MODIFIED = "modified";\r
+\r
+       protected static final String KEYWORD_REVISION = "revision";\r
+\r
+       protected static final String KEYWORD_SUBJECT = "subject";\r
+\r
+       protected static final String KEYWORD_TITLE = "title";\r
+\r
+       protected static final String KEYWORD_VERSION = "version";\r
+\r
+       PackagePropertiesPart propsPart;\r
+\r
+       // The document\r
+       Document xmlDoc = null;\r
+\r
+       /**\r
+        * Marshall package core properties to an XML document. Always return\r
+        * <code>true</code>.\r
+        */\r
+       public boolean marshall(PackagePart part, OutputStream out)\r
+                       throws OpenXML4JException {\r
+               if (!(part instanceof PackagePropertiesPart))\r
+                       throw new IllegalArgumentException(\r
+                                       "'part' must be a PackagePropertiesPart instance.");\r
+               propsPart = (PackagePropertiesPart) part;\r
+\r
+               // Configure the document\r
+               xmlDoc = DocumentHelper.createDocument();\r
+               Element rootElem = xmlDoc.addElement(new QName("coreProperties",\r
+                               namespaceCoreProperties));\r
+               rootElem.addNamespace("cp", PackagePropertiesPart.NAMESPACE_CP_URI);\r
+               rootElem.addNamespace("dc", PackagePropertiesPart.NAMESPACE_DC_URI);\r
+               rootElem.addNamespace("dcterms",\r
+                               PackagePropertiesPart.NAMESPACE_DCTERMS_URI);\r
+               rootElem.addNamespace("xsi", PackagePropertiesPart.NAMESPACE_XSI_URI);\r
+\r
+               addCategory();\r
+               addContentStatus();\r
+               addContentType();\r
+               addCreated();\r
+               addCreator();\r
+               addDescription();\r
+               addIdentifier();\r
+               addKeywords();\r
+               addLanguage();\r
+               addLastModifiedBy();\r
+               addLastPrinted();\r
+               addModified();\r
+               addRevision();\r
+               addSubject();\r
+               addTitle();\r
+               addVersion();\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Add category property element if needed.\r
+        */\r
+       private void addCategory() {\r
+               if (!propsPart.getCategoryProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CATEGORY, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // Missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_CATEGORY, namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getCategoryProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add content status property element if needed.\r
+        */\r
+       private void addContentStatus() {\r
+               if (!propsPart.getContentStatusProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CONTENT_STATUS, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // Missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_CONTENT_STATUS, namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getContentStatusProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add content type property element if needed.\r
+        */\r
+       private void addContentType() {\r
+               if (!propsPart.getContentTypeProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CONTENT_TYPE, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // Missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_CONTENT_TYPE, namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getContentTypeProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add created property element if needed.\r
+        */\r
+       private void addCreated() {\r
+               if (!propsPart.getCreatedProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CREATED, namespaceDcTerms));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_CREATED, namespaceDcTerms));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addAttribute(new QName("type", namespaceXSI), "dcterms:W3CDTF");\r
+               elem.addText(propsPart.getCreatedPropertyString());\r
+       }\r
+\r
+       /**\r
+        * Add creator property element if needed.\r
+        */\r
+       private void addCreator() {\r
+               if (!propsPart.getCreatorProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CREATOR, namespaceDC));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_CREATOR, namespaceDC));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getCreatorProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add description property element if needed.\r
+        */\r
+       private void addDescription() {\r
+               if (!propsPart.getDescriptionProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_DESCRIPTION, namespaceDC));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_DESCRIPTION, namespaceDC));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getDescriptionProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add identifier property element if needed.\r
+        */\r
+       private void addIdentifier() {\r
+               if (!propsPart.getIdentifierProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_IDENTIFIER, namespaceDC));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_IDENTIFIER, namespaceDC));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getIdentifierProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add keywords property element if needed.\r
+        */\r
+       private void addKeywords() {\r
+               if (!propsPart.getKeywordsProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_KEYWORDS, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_KEYWORDS, namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getKeywordsProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add language property element if needed.\r
+        */\r
+       private void addLanguage() {\r
+               if (!propsPart.getLanguageProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_LANGUAGE, namespaceDC));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_LANGUAGE, namespaceDC));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getLanguageProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add 'last modified by' property if needed.\r
+        */\r
+       private void addLastModifiedBy() {\r
+               if (!propsPart.getLastModifiedByProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_LAST_MODIFIED_BY, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement()\r
+                                       .addElement(\r
+                                                       new QName(KEYWORD_LAST_MODIFIED_BY,\r
+                                                                       namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getLastModifiedByProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add 'last printed' property if needed.\r
+        * \r
+        */\r
+       private void addLastPrinted() {\r
+               if (!propsPart.getLastPrintedProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_LAST_PRINTED, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_LAST_PRINTED, namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getLastPrintedPropertyString());\r
+       }\r
+\r
+       /**\r
+        * Add modified property element if needed.\r
+        */\r
+       private void addModified() {\r
+               if (!propsPart.getModifiedProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_MODIFIED, namespaceDcTerms));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_MODIFIED, namespaceDcTerms));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addAttribute(new QName("type", namespaceXSI), "dcterms:W3CDTF");\r
+               elem.addText(propsPart.getModifiedPropertyString());\r
+       }\r
+\r
+       /**\r
+        * Add revision property if needed.\r
+        */\r
+       private void addRevision() {\r
+               if (!propsPart.getRevisionProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_REVISION, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_REVISION, namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getRevisionProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add subject property if needed.\r
+        */\r
+       private void addSubject() {\r
+               if (!propsPart.getSubjectProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_SUBJECT, namespaceDC));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_SUBJECT, namespaceDC));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getSubjectProperty().getValue());\r
+       }\r
+\r
+       /**\r
+        * Add title property if needed.\r
+        */\r
+       private void addTitle() {\r
+               if (!propsPart.getTitleProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_TITLE, namespaceDC));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_TITLE, namespaceDC));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getTitleProperty().getValue());\r
+       }\r
+\r
+       private void addVersion() {\r
+               if (!propsPart.getVersionProperty().hasValue())\r
+                       return;\r
+\r
+               Element elem = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_VERSION, namespaceCoreProperties));\r
+               if (elem == null) {\r
+                       // missing, we add it\r
+                       elem = xmlDoc.getRootElement().addElement(\r
+                                       new QName(KEYWORD_VERSION, namespaceCoreProperties));\r
+               } else {\r
+                       elem.clearContent();// clear the old value\r
+               }\r
+               elem.addText(propsPart.getVersionProperty().getValue());\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java
new file mode 100755 (executable)
index 0000000..39e8fa3
--- /dev/null
@@ -0,0 +1,64 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.marshallers;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipOutputStream;\r
+\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.StreamHelper;\r
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;\r
+\r
+/**\r
+ * Package core properties marshaller specialized for zipped package.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public class ZipPackagePropertiesMarshaller extends PackagePropertiesMarshaller {\r
+\r
+       @Override\r
+       public boolean marshall(PackagePart part, OutputStream out)\r
+                       throws OpenXML4JException {\r
+               if (!(out instanceof ZipOutputStream)) {\r
+                       throw new IllegalArgumentException("ZipOutputStream expected!");\r
+               }\r
+               ZipOutputStream zos = (ZipOutputStream) out;\r
+\r
+               // Saving the part in the zip file\r
+               ZipEntry ctEntry = new ZipEntry(ZipHelper\r
+                               .getZipItemNameFromOPCName(part.getPartName().getURI()\r
+                                               .toString()));\r
+               try {\r
+                       // Save in ZIP\r
+                       zos.putNextEntry(ctEntry); // Add entry in ZIP\r
+                       super.marshall(part, out); // Marshall the properties inside a XML\r
+                       // Document\r
+                       if (!StreamHelper.saveXmlInStream(xmlDoc, out)) {\r
+                               return false;\r
+                       }\r
+                       zos.closeEntry();\r
+               } catch (IOException e) {\r
+                       throw new OpenXML4JException(e.getLocalizedMessage());\r
+               }\r
+               return true;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
new file mode 100755 (executable)
index 0000000..a54bef5
--- /dev/null
@@ -0,0 +1,193 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.marshallers;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.net.URI;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipOutputStream;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.dom4j.Document;\r
+import org.dom4j.DocumentHelper;\r
+import org.dom4j.Element;\r
+import org.dom4j.Namespace;\r
+import org.dom4j.QName;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackageNamespaces;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+import org.apache.poi.openxml4j.opc.StreamHelper;\r
+import org.apache.poi.openxml4j.opc.TargetMode;\r
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;\r
+\r
+/**\r
+ * Zip part marshaller. This marshaller is use to save any part in a zip stream.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public class ZipPartMarshaller implements PartMarshaller {\r
+       private static Logger logger = Logger.getLogger("org.openxml4j");\r
+\r
+       /**\r
+        * Save the specified part.\r
+        * \r
+        * @throws OpenXML4JException\r
+        *             Throws if an internal exception is thrown.\r
+        */\r
+       public boolean marshall(PackagePart part, OutputStream os)\r
+                       throws OpenXML4JException {\r
+               if (!(os instanceof ZipOutputStream)) {\r
+                       logger.error("Unexpected class " + os.getClass().getName());\r
+                       throw new OpenXML4JException("ZipOutputStream expected !");\r
+                       // Normally should happen only in developpement phase, so just throw\r
+                       // exception\r
+               }\r
+\r
+               ZipOutputStream zos = (ZipOutputStream) os;\r
+               ZipEntry partEntry = new ZipEntry(ZipHelper\r
+                               .getZipItemNameFromOPCName(part.getPartName().getURI()\r
+                                               .getPath()));\r
+               try {\r
+                       // Create next zip entry\r
+                       zos.putNextEntry(partEntry);\r
+\r
+                       // Saving data in the ZIP file\r
+                       InputStream ins = part.getInputStream();\r
+                       byte[] buff = new byte[ZipHelper.READ_WRITE_FILE_BUFFER_SIZE];\r
+                       while (ins.available() > 0) {\r
+                               int resultRead = ins.read(buff);\r
+                               if (resultRead == -1) {\r
+                                       // End of file reached\r
+                                       break;\r
+                               } else {\r
+                                       zos.write(buff, 0, resultRead);\r
+                               }\r
+                       }\r
+                       zos.closeEntry();\r
+               } catch (IOException ioe) {\r
+                       logger.error("Cannot write: " + part.getPartName() + ": in ZIP",\r
+                                       ioe);\r
+                       return false;\r
+               }\r
+\r
+               // Saving relationship part\r
+               if (part.hasRelationships()) {\r
+                       PackagePartName relationshipPartName = PackagingURIHelper\r
+                                       .getRelationshipPartName(part.getPartName());\r
+\r
+                       marshallRelationshipPart(part.getRelationships(),\r
+                                       relationshipPartName, zos);\r
+\r
+               }\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * Save relationships into the part.\r
+        * \r
+        * @param rels\r
+        *            The relationships collection to marshall.\r
+        * @param relPartURI\r
+        *            Part name of the relationship part to marshall.\r
+        * @param zos\r
+        *            Zip output stream in which to save the XML content of the\r
+        *            relationships serialization.\r
+        */\r
+       public static boolean marshallRelationshipPart(\r
+                       PackageRelationshipCollection rels, PackagePartName relPartName,\r
+                       ZipOutputStream zos) {\r
+               // Building xml\r
+               Document xmlOutDoc = DocumentHelper.createDocument();\r
+               // make something like <Relationships\r
+               // xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\r
+               Namespace dfNs = Namespace.get("", PackageNamespaces.RELATIONSHIPS);\r
+               Element root = xmlOutDoc.addElement(new QName(\r
+                               PackageRelationship.RELATIONSHIPS_TAG_NAME, dfNs));\r
+\r
+               // <Relationship\r
+               // TargetMode="External"\r
+               // Id="rIdx"\r
+               // Target="http://www.custom.com/images/pic1.jpg"\r
+               // Type="http://www.custom.com/external-resource"/>\r
+\r
+               URI sourcePartURI = PackagingURIHelper\r
+                               .getSourcePartUriFromRelationshipPartUri(relPartName.getURI());\r
+\r
+               for (PackageRelationship rel : rels) {\r
+                       // L'�l�ment de la relation\r
+                       Element relElem = root\r
+                                       .addElement(PackageRelationship.RELATIONSHIP_TAG_NAME);\r
+\r
+                       // L'attribut ID\r
+                       relElem.addAttribute(PackageRelationship.ID_ATTRIBUTE_NAME, rel\r
+                                       .getId());\r
+\r
+                       // L'attribut Type\r
+                       relElem.addAttribute(PackageRelationship.TYPE_ATTRIBUTE_NAME, rel\r
+                                       .getRelationshipType());\r
+\r
+                       // L'attribut Target\r
+                       String targetValue;\r
+                       URI uri = rel.getTargetURI();\r
+                       if (rel.getTargetMode() == TargetMode.EXTERNAL) {\r
+                               // Save the target as-is - we don't need to validate it,\r
+                               //  alter it etc\r
+                targetValue = uri.toString();\r
+\r
+                               // add TargetMode attribut (as it is external link external)\r
+                               relElem.addAttribute(\r
+                                               PackageRelationship.TARGET_MODE_ATTRIBUTE_NAME,\r
+                                               "External");\r
+                       } else {\r
+                               targetValue = PackagingURIHelper.relativizeURI(\r
+                                               sourcePartURI, rel.getTargetURI()).getPath();\r
+                       }\r
+                       relElem.addAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME,\r
+                                       targetValue);\r
+               }\r
+\r
+               xmlOutDoc.normalize();\r
+\r
+               // String schemaFilename = Configuration.getPathForXmlSchema()+\r
+               // File.separator + "opc-relationships.xsd";\r
+\r
+               // Save part in zip\r
+               ZipEntry ctEntry = new ZipEntry(ZipHelper.getZipURIFromOPCName(\r
+                               relPartName.getURI().toASCIIString()).getPath());\r
+               try {\r
+                       // Cr�ation de l'entr�e dans le fichier ZIP\r
+                       zos.putNextEntry(ctEntry);\r
+                       if (!StreamHelper.saveXmlInStream(xmlOutDoc, zos)) {\r
+                               return false;\r
+                       }\r
+                       zos.closeEntry();\r
+               } catch (IOException e) {\r
+                       logger.error("Cannot create zip entry " + relPartName, e);\r
+                       return false;\r
+               }\r
+               return true; // success\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalCertificatePart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalCertificatePart.java
new file mode 100755 (executable)
index 0000000..0cb1fba
--- /dev/null
@@ -0,0 +1,79 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.signature;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.internal.ContentType;\r
+\r
+/**\r
+ * Digital certificate part.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public final class DigitalCertificatePart extends PackagePart  {\r
+\r
+       public DigitalCertificatePart() throws InvalidFormatException{\r
+               super(null, null, new ContentType(""));\r
+               // Review constructor\r
+       }\r
+       \r
+       @Override\r
+       public void close() {\r
+               // TODO Auto-generated method stub\r
+               \r
+       }\r
+\r
+       @Override\r
+       public void flush() {\r
+               // TODO Auto-generated method stub\r
+               \r
+       }\r
+\r
+       @Override\r
+       protected InputStream getInputStreamImpl() throws IOException {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       protected OutputStream getOutputStreamImpl() {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public boolean load(InputStream ios) throws InvalidFormatException {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public boolean save(OutputStream zos) throws OpenXML4JException {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+\r
+       // TODO Introduire le concept de partie typ�e d�s cette partie\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalSignatureOriginPart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalSignatureOriginPart.java
new file mode 100755 (executable)
index 0000000..718e78b
--- /dev/null
@@ -0,0 +1,28 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.signature;\r
+\r
+/**\r
+ * Represents a digital signature origin part.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.1\r
+ */\r
+public class DigitalSignatureOriginPart {\r
+\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java
new file mode 100755 (executable)
index 0000000..f11b969
--- /dev/null
@@ -0,0 +1,390 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.unmarshallers;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.zip.ZipEntry;\r
+\r
+import org.dom4j.Attribute;\r
+import org.dom4j.Document;\r
+import org.dom4j.DocumentException;\r
+import org.dom4j.Element;\r
+import org.dom4j.Namespace;\r
+import org.dom4j.QName;\r
+import org.dom4j.io.SAXReader;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackageNamespaces;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackageProperties;\r
+import org.apache.poi.openxml4j.opc.ZipPackage;\r
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;\r
+import org.apache.poi.openxml4j.opc.internal.PartUnmarshaller;\r
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;\r
+\r
+/**\r
+ * Package properties unmarshaller.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public class PackagePropertiesUnmarshaller implements PartUnmarshaller {\r
+\r
+       private final static Namespace namespaceDC = new Namespace("dc",\r
+                       PackageProperties.NAMESPACE_DC);\r
+\r
+       private final static Namespace namespaceCP = new Namespace("cp",\r
+                       PackageNamespaces.CORE_PROPERTIES);\r
+\r
+       private final static Namespace namespaceDcTerms = new Namespace("dcterms",\r
+                       PackageProperties.NAMESPACE_DCTERMS);\r
+\r
+       private final static Namespace namespaceXML = new Namespace("xml",\r
+                       "http://www.w3.org/XML/1998/namespace");\r
+\r
+       private final static Namespace namespaceXSI = new Namespace("xsi",\r
+                       "http://www.w3.org/2001/XMLSchema-instance");\r
+\r
+       protected static final String KEYWORD_CATEGORY = "category";\r
+\r
+       protected static final String KEYWORD_CONTENT_STATUS = "contentStatus";\r
+\r
+       protected static final String KEYWORD_CONTENT_TYPE = "contentType";\r
+\r
+       protected static final String KEYWORD_CREATED = "created";\r
+\r
+       protected static final String KEYWORD_CREATOR = "creator";\r
+\r
+       protected static final String KEYWORD_DESCRIPTION = "description";\r
+\r
+       protected static final String KEYWORD_IDENTIFIER = "identifier";\r
+\r
+       protected static final String KEYWORD_KEYWORDS = "keywords";\r
+\r
+       protected static final String KEYWORD_LANGUAGE = "language";\r
+\r
+       protected static final String KEYWORD_LAST_MODIFIED_BY = "lastModifiedBy";\r
+\r
+       protected static final String KEYWORD_LAST_PRINTED = "lastPrinted";\r
+\r
+       protected static final String KEYWORD_MODIFIED = "modified";\r
+\r
+       protected static final String KEYWORD_REVISION = "revision";\r
+\r
+       protected static final String KEYWORD_SUBJECT = "subject";\r
+\r
+       protected static final String KEYWORD_TITLE = "title";\r
+\r
+       protected static final String KEYWORD_VERSION = "version";\r
+\r
+       // TODO Load element with XMLBeans or dynamic table\r
+       // TODO Check every element/namespace for compliance\r
+       public PackagePart unmarshall(UnmarshallContext context, InputStream in)\r
+                       throws InvalidFormatException, IOException {\r
+               PackagePropertiesPart coreProps = new PackagePropertiesPart(context\r
+                               .getPackage(), context.getPartName());\r
+\r
+               // If the input stream is null then we try to get it from the\r
+               // package.\r
+               if (in == null) {\r
+                       if (context.getZipEntry() != null) {\r
+                               in = ((ZipPackage) context.getPackage()).getZipArchive()\r
+                                               .getInputStream(context.getZipEntry());\r
+                       } else if (context.getPackage() != null) {\r
+                               // Try to retrieve the part inputstream from the URI\r
+                               ZipEntry zipEntry;\r
+                               try {\r
+                                       zipEntry = ZipHelper\r
+                                                       .getCorePropertiesZipEntry((ZipPackage) context\r
+                                                                       .getPackage());\r
+                               } catch (OpenXML4JException e) {\r
+                                       throw new IOException(\r
+                                                       "Error while trying to get the part input stream.");\r
+                               }\r
+                               in = ((ZipPackage) context.getPackage()).getZipArchive()\r
+                                               .getInputStream(zipEntry);\r
+                       } else\r
+                               throw new IOException(\r
+                                               "Error while trying to get the part input stream.");\r
+               }\r
+\r
+               SAXReader xmlReader = new SAXReader();\r
+               Document xmlDoc;\r
+               try {\r
+                       xmlDoc = xmlReader.read(in);\r
+\r
+                       /* Check OPC compliance */\r
+\r
+                       // Rule M4.2, M4.3, M4.4 and M4.5/\r
+                       checkElementForOPCCompliance(xmlDoc.getRootElement());\r
+\r
+                       /* End OPC compliance */\r
+\r
+               } catch (DocumentException e) {\r
+                       throw new IOException(e.getMessage());\r
+               }\r
+\r
+               coreProps.setCategoryProperty(loadCategory(xmlDoc));\r
+               coreProps.setContentStatusProperty(loadContentStatus(xmlDoc));\r
+               coreProps.setContentTypeProperty(loadContentType(xmlDoc));\r
+               coreProps.setCreatedProperty(loadCreated(xmlDoc));\r
+               coreProps.setCreatorProperty(loadCreator(xmlDoc));\r
+               coreProps.setDescriptionProperty(loadDescription(xmlDoc));\r
+               coreProps.setIdentifierProperty(loadIdentifier(xmlDoc));\r
+               coreProps.setKeywordsProperty(loadKeywords(xmlDoc));\r
+               coreProps.setLanguageProperty(loadLanguage(xmlDoc));\r
+               coreProps.setLastModifiedByProperty(loadLastModifiedBy(xmlDoc));\r
+               coreProps.setLastPrintedProperty(loadLastPrinted(xmlDoc));\r
+               coreProps.setModifiedProperty(loadModified(xmlDoc));\r
+               coreProps.setRevisionProperty(loadRevision(xmlDoc));\r
+               coreProps.setSubjectProperty(loadSubject(xmlDoc));\r
+               coreProps.setTitleProperty(loadTitle(xmlDoc));\r
+               coreProps.setVersionProperty(loadVersion(xmlDoc));\r
+\r
+               return coreProps;\r
+       }\r
+\r
+       private String loadCategory(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CATEGORY, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadContentStatus(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CONTENT_STATUS, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadContentType(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CONTENT_TYPE, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadCreated(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CREATED, namespaceDcTerms));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadCreator(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_CREATOR, namespaceDC));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadDescription(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_DESCRIPTION, namespaceDC));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadIdentifier(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_IDENTIFIER, namespaceDC));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadKeywords(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_KEYWORDS, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadLanguage(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_LANGUAGE, namespaceDC));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadLastModifiedBy(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_LAST_MODIFIED_BY, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadLastPrinted(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_LAST_PRINTED, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadModified(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_MODIFIED, namespaceDcTerms));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadRevision(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_REVISION, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadSubject(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_SUBJECT, namespaceDC));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadTitle(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_TITLE, namespaceDC));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       private String loadVersion(Document xmlDoc) {\r
+               Element el = xmlDoc.getRootElement().element(\r
+                               new QName(KEYWORD_VERSION, namespaceCP));\r
+               if (el != null)\r
+                       return el.getStringValue();\r
+               else\r
+                       return null;\r
+       }\r
+\r
+       /* OPC Compliance methods */\r
+\r
+       /**\r
+        * Check the element for the following OPC compliance rules:\r
+        * \r
+        * Rule M4.2: A format consumer shall consider the use of the Markup\r
+        * Compatibility namespace to be an error.\r
+        * \r
+        * Rule M4.3: Producers shall not create a document element that contains\r
+        * refinements to the Dublin Core elements, except for the two specified in\r
+        * the schema: <dcterms:created> and <dcterms:modified> Consumers shall\r
+        * consider a document element that violates this constraint to be an error.\r
+        * \r
+        * Rule M4.4: Producers shall not create a document element that contains\r
+        * the xml:lang attribute. Consumers shall consider a document element that\r
+        * violates this constraint to be an error.\r
+        * \r
+        * Rule M4.5: Producers shall not create a document element that contains\r
+        * the xsi:type attribute, except for a <dcterms:created> or\r
+        * <dcterms:modified> element where the xsi:type attribute shall be present\r
+        * and shall hold the value dcterms:W3CDTF, where dcterms is the namespace\r
+        * prefix of the Dublin Core namespace. Consumers shall consider a document\r
+        * element that violates this constraint to be an error.\r
+        */\r
+       public void checkElementForOPCCompliance(Element el)\r
+                       throws InvalidFormatException {\r
+               // Check the current element\r
+               List declaredNamespaces = el.declaredNamespaces();\r
+               Iterator itNS = declaredNamespaces.iterator();\r
+               while (itNS.hasNext()) {\r
+                       Namespace ns = (Namespace) itNS.next();\r
+\r
+                       // Rule M4.2\r
+                       if (ns.getURI().equals(PackageNamespaces.MARKUP_COMPATIBILITY))\r
+                               throw new InvalidFormatException(\r
+                                               "OPC Compliance error [M4.2]: A format consumer shall consider the use of the Markup Compatibility namespace to be an error.");\r
+               }\r
+\r
+               // Rule M4.3\r
+               if (el.getNamespace().getURI().equals(\r
+                               PackageProperties.NAMESPACE_DCTERMS)\r
+                               && !(el.getName().equals(KEYWORD_CREATED) || el.getName()\r
+                                               .equals(KEYWORD_MODIFIED)))\r
+                       throw new InvalidFormatException(\r
+                                       "OPC Compliance error [M4.3]: Producers shall not create a document element that contains refinements to the Dublin Core elements, except for the two specified in the schema: <dcterms:created> and <dcterms:modified> Consumers shall consider a document element that violates this constraint to be an error.");\r
+\r
+               // Rule M4.4\r
+               if (el.attribute(new QName("lang", namespaceXML)) != null)\r
+                       throw new InvalidFormatException(\r
+                                       "OPC Compliance error [M4.4]: Producers shall not create a document element that contains the xml:lang attribute. Consumers shall consider a document element that violates this constraint to be an error.");\r
+\r
+               // Rule M4.5\r
+               if (el.getNamespace().getURI().equals(\r
+                               PackageProperties.NAMESPACE_DCTERMS)) {\r
+                       // DCTerms namespace only use with 'created' and 'modified' elements\r
+                       String elName = el.getName();\r
+                       if (!(elName.equals(KEYWORD_CREATED) || elName\r
+                                       .equals(KEYWORD_MODIFIED)))\r
+                               throw new InvalidFormatException("Namespace error : " + elName\r
+                                               + " shouldn't have the following naemspace -> "\r
+                                               + PackageProperties.NAMESPACE_DCTERMS);\r
+\r
+                       // Check for the 'xsi:type' attribute\r
+                       Attribute typeAtt = el.attribute(new QName("type", namespaceXSI));\r
+                       if (typeAtt == null)\r
+                               throw new InvalidFormatException("The element '" + elName\r
+                                               + "' must have the '" + namespaceXSI.getPrefix()\r
+                                               + ":type' attribute present !");\r
+\r
+                       // Check for the attribute value => 'dcterms:W3CDTF'\r
+                       if (!typeAtt.getValue().equals("dcterms:W3CDTF"))\r
+                               throw new InvalidFormatException("The element '" + elName\r
+                                               + "' must have the '" + namespaceXSI.getPrefix()\r
+                                               + ":type' attribute with the value 'dcterms:W3CDTF' !");\r
+               }\r
+\r
+               // Check its children\r
+               Iterator itChildren = el.elementIterator();\r
+               while (itChildren.hasNext())\r
+                       checkElementForOPCCompliance((Element) itChildren.next());\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/UnmarshallContext.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/UnmarshallContext.java
new file mode 100755 (executable)
index 0000000..689a5c6
--- /dev/null
@@ -0,0 +1,96 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal.unmarshallers;\r
+\r
+import java.util.zip.ZipEntry;\r
+\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+\r
+/**\r
+ * Context needed for the unmarshall process of a part. This class is immutable.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public final class UnmarshallContext {\r
+\r
+       private Package _package;\r
+\r
+       private PackagePartName partName;\r
+\r
+       private ZipEntry zipEntry;\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param targetPackage\r
+        *            Container.\r
+        * @param partName\r
+        *            Name of the part to unmarshall.\r
+        */\r
+       public UnmarshallContext(Package targetPackage, PackagePartName partName) {\r
+               this._package = targetPackage;\r
+               this.partName = partName;\r
+       }\r
+\r
+       /**\r
+        * @return the container\r
+        */\r
+       Package getPackage() {\r
+               return _package;\r
+       }\r
+\r
+       /**\r
+        * @param container\r
+        *            the container to set\r
+        */\r
+       public void setPackage(Package container) {\r
+               this._package = container;\r
+       }\r
+\r
+       /**\r
+        * @return the partName\r
+        */\r
+       PackagePartName getPartName() {\r
+               return partName;\r
+       }\r
+\r
+       /**\r
+        * @param partName\r
+        *            the partName to set\r
+        */\r
+       public void setPartName(PackagePartName partName) {\r
+               this.partName = partName;\r
+       }\r
+\r
+       /**\r
+        * @return the zipEntry\r
+        */\r
+       ZipEntry getZipEntry() {\r
+               return zipEntry;\r
+       }\r
+\r
+       /**\r
+        * @param zipEntry\r
+        *            the zipEntry to set\r
+        */\r
+       public void setZipEntry(ZipEntry zipEntry) {\r
+               this.zipEntry = zipEntry;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignature.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignature.java
new file mode 100755 (executable)
index 0000000..8922641
--- /dev/null
@@ -0,0 +1,70 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.signature;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.internal.ContentType;\r
+\r
+public class PackageDigitalSignature extends PackagePart {\r
+\r
+       public PackageDigitalSignature() throws InvalidFormatException {\r
+               super(null, null, new ContentType(""));\r
+       }\r
+\r
+       @Override\r
+       public void close() {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       @Override\r
+       public void flush() {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       @Override\r
+       protected InputStream getInputStreamImpl() throws IOException {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       protected OutputStream getOutputStreamImpl() {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public boolean load(InputStream ios) throws InvalidFormatException {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public boolean save(OutputStream zos) throws OpenXML4JException {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignatureManager.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignatureManager.java
new file mode 100755 (executable)
index 0000000..0e5136d
--- /dev/null
@@ -0,0 +1,22 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.signature;\r
+\r
+public class PackageDigitalSignatureManager {\r
+\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/util/Nullable.java b/src/ooxml/java/org/apache/poi/openxml4j/util/Nullable.java
new file mode 100755 (executable)
index 0000000..45374dc
--- /dev/null
@@ -0,0 +1,71 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.openxml4j.util;\r
+\r
+/**\r
+ * An immutable object that could be defined as null.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 0.9\r
+ */\r
+public final class Nullable<E> {\r
+\r
+       private E value;\r
+\r
+       /**\r
+        * Constructor.\r
+        */\r
+       public Nullable() {\r
+               // Do nothing\r
+       }\r
+\r
+       /**\r
+        * Constructor.\r
+        * \r
+        * @param value\r
+        *            The value to set to this nullable.\r
+        */\r
+       public Nullable(E value) {\r
+               this.value = value;\r
+       }\r
+\r
+       /**\r
+        * Get the store value if any.\r
+        * \r
+        * @return\r
+        */\r
+       public E getValue() {\r
+               return value;\r
+       }\r
+\r
+       /**\r
+        * Get the status of this nullable.\r
+        * \r
+        * @return <b>true</b> if the nullable store a value (empty string is\r
+        *         considered to be a value) else <b>false</>.\r
+        */\r
+       public boolean hasValue() {\r
+               return value != null;\r
+       }\r
+\r
+       /**\r
+        * Set the stored value to <i>null</i>.\r
+        */\r
+       public void nullify() {\r
+               value = null;\r
+       }\r
+}\r
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java
new file mode 100755 (executable)
index 0000000..1d64ffe
--- /dev/null
@@ -0,0 +1,48 @@
+/* ====================================================================
+   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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+
+/**
+ * An Interface to make getting the different bits
+ *  of a Zip File easy.
+ * Allows you to get at the ZipEntries, without
+ *  needing to worry about ZipFile vs ZipInputStream
+ *  being annoyingly very different.
+ */
+public interface ZipEntrySource {
+       /**
+        * Returns an Enumeration of all the Entries
+        */
+       public Enumeration<? extends ZipEntry> getEntries();
+       
+       /**
+        * Returns an InputStream of the decompressed 
+        *  data that makes up the entry
+        */
+       public InputStream getInputStream(ZipEntry entry) throws IOException;
+       
+       /**
+        * Indicates we are done with reading, and 
+        *  resources may be freed
+        */
+       public void close() throws IOException;
+}
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java
new file mode 100755 (executable)
index 0000000..1a0d366
--- /dev/null
@@ -0,0 +1,48 @@
+/* ====================================================================
+   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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * A ZipEntrySource wrapper around a ZipFile.
+ * Should be as low in terms of memory as a
+ *  normal ZipFile implementation is.
+ */
+public class ZipFileZipEntrySource implements ZipEntrySource {
+       private ZipFile zipArchive;
+       public ZipFileZipEntrySource(ZipFile zipFile) {
+               this.zipArchive = zipFile;
+       }
+       
+       public void close() throws IOException {
+               zipArchive.close();
+               zipArchive = null;
+       }
+       
+       public Enumeration<? extends ZipEntry> getEntries() {
+               return zipArchive.entries();
+       }
+       
+       public InputStream getInputStream(ZipEntry entry) throws IOException {
+               return zipArchive.getInputStream(entry);
+       }
+}
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
new file mode 100755 (executable)
index 0000000..0b9822e
--- /dev/null
@@ -0,0 +1,125 @@
+/* ====================================================================
+   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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Provides a way to get at all the ZipEntries
+ *  from a ZipInputStream, as many times as required.
+ * Allows a ZipInputStream to be treated much like
+ *  a ZipFile, for a price in terms of memory.
+ * Be sure to call {@link #close()} as soon as you're
+ *  done, to free up that memory!
+ */
+public class ZipInputStreamZipEntrySource implements ZipEntrySource {
+       private ArrayList<FakeZipEntry> zipEntries;
+       
+       /**
+        * Reads all the entries from the ZipInputStream 
+        *  into memory, and closes the source stream.
+        * We'll then eat lots of memory, but be able to
+        *  work with the entries at-will.
+        */
+       public ZipInputStreamZipEntrySource(ZipInputStream inp) throws IOException {
+               zipEntries = new ArrayList<FakeZipEntry>();
+               
+               boolean going = true;
+               while(going) {
+                       ZipEntry zipEntry = inp.getNextEntry();
+                       if(zipEntry == null) {
+                               going = false;
+                       } else {
+                               FakeZipEntry entry = new FakeZipEntry(zipEntry, inp);
+                               inp.closeEntry();
+                               
+                               zipEntries.add(entry);
+                       }
+               }
+               inp.close();
+       }
+
+       public Enumeration<? extends ZipEntry> getEntries() {
+               return new EntryEnumerator();
+       }
+       
+       public InputStream getInputStream(ZipEntry zipEntry) {
+               FakeZipEntry entry = (FakeZipEntry)zipEntry;
+               return entry.getInputStream();
+       }
+       
+       public void close() {
+               // Free the memory
+               zipEntries = null;
+       }
+       
+       /**
+        * Why oh why oh why are Iterator and Enumeration
+        *  still not compatible?
+        */
+       private class EntryEnumerator implements Enumeration<ZipEntry> {
+               private Iterator<? extends ZipEntry> iterator;
+               
+               private EntryEnumerator() {
+                       iterator = zipEntries.iterator();
+               }
+               
+               public boolean hasMoreElements() {
+                       return iterator.hasNext();
+               }
+
+               public ZipEntry nextElement() {
+                       return iterator.next();
+               }
+       }
+
+       /**
+        * So we can close the real zip entry and still
+        *  effectively work with it.
+        * Holds the (decompressed!) data in memory, so
+        *  close this as soon as you can! 
+        */
+       public static class FakeZipEntry extends ZipEntry {
+               private byte[] data;
+               
+               public FakeZipEntry(ZipEntry entry, ZipInputStream inp) throws IOException {
+                       super(entry.getName());
+                       
+                       // Grab the de-compressed contents for later
+                       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                       byte[] buffer = new byte[4096];
+                       int read = 0;
+                       while( (read = inp.read(buffer)) != -1 ) {
+                               baos.write(buffer, 0, read);
+                       }
+                       
+                       data = baos.toByteArray();
+               }
+               
+               public InputStream getInputStream() {
+                       return new ByteArrayInputStream(data);
+               }
+       }
+}
index d7984d3b192abc476818ffe7a72729c44632c9bd..d2f0f43e2792d65b78d426fd81ffa8d82d40d43d 100644 (file)
@@ -24,7 +24,7 @@ import org.apache.poi.POIXMLDocument;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.Package;
 
 /**
  * Factory for creating the appropriate kind of Workbook
index 258e5b8d1ef14435762d1035471e8e13331da4ac..5b1b7d7476e50f5ddf99df38326a1e74fe740217 100755 (executable)
 ==================================================================== */\r
 package org.apache.poi.util;\r
 \r
-import org.openxml4j.opc.*;\r
-import org.openxml4j.opc.Package;\r
-import org.openxml4j.opc.internal.PackagePropertiesPart;\r
-import org.openxml4j.opc.internal.marshallers.PackagePropertiesMarshaller;\r
-import org.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.opc.*;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
 import org.apache.poi.util.IOUtils;\r
 \r
 import java.io.*;\r
-import java.util.ArrayList;\r
-import java.lang.reflect.Method;\r
 \r
 /**\r
  * Provides handy methods to work with OOXML packages\r
index 53851177bd59d4bd437b1b7b23bf1673fba0e225..7b66a3d5a4f007704a4f04ff33118be98abad17d 100644 (file)
@@ -22,12 +22,12 @@ import java.util.LinkedList;
 
 import org.apache.poi.POIXMLDocument;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.InvalidFormatException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTCommentList;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesSlide;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation;
index 4366f82e7658a3d437bf8dbdc5045b7f500ef87b..ad820bd2fbfaf887fe92276206aa9f39b2c227aa 100644 (file)
@@ -23,8 +23,8 @@ import org.apache.poi.xslf.XSLFSlideShow;
 import org.apache.poi.xslf.usermodel.XMLSlideShow;
 import org.apache.poi.xslf.usermodel.XSLFSlide;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
index ceeaa0f441686beab0634d15ccb26100af797a92..29f3bd0832476b1528de0b795310e7acf6eb2263 100644 (file)
@@ -25,14 +25,14 @@ import org.apache.poi.xssf.model.StylesTable;
 import org.apache.poi.xssf.usermodel.XSSFRelation;
 import org.apache.poi.POIXMLException;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.InvalidFormatException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackagePartName;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackageRelationshipTypes;
-import org.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet;
index 33614edc0ec36580949449277524561c91932f25..80ba653118cce852cdd367551616e305c252027e 100644 (file)
@@ -29,8 +29,8 @@ import org.apache.poi.xssf.usermodel.XSSFCell;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
 
 /**
  * Helper class to extract text from an OOXML Excel file
index b5fabd1a61065d30e73a463c6efeffd717e3ba24..6caec6e734e5aa80da3079c768ef9f0d9520d45c 100644 (file)
@@ -29,8 +29,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCommentList;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CommentsDocument;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
 
 public class CommentsTable extends POIXMLDocumentPart {
     protected CTComments comments;
index edc81866276c49eaa7d44f16183f93f4707a8719..8287df55b4271ec7689197e59494d8ea548d3462 100644 (file)
@@ -31,9 +31,8 @@ import org.apache.poi.POIXMLDocumentPart;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSst;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.SstDocument;
-import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
 
 
 /**
index 872eadb44d49ba4df0a9548fe3ceb1a8e43ea7f2..8355f4f9ba1780059aa103b74ee5c77945e4bfa0 100644 (file)
@@ -48,8 +48,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPatternType;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.StyleSheetDocument;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
 
 
 /**
index d16cf9a1bdfb7f00760568d344c5b9c9b114e2a9..1a0a6555fec3d9f0d3e84d18593993360310c44c 100644 (file)
@@ -17,7 +17,7 @@
 package org.apache.poi.xssf.model;
 
 import org.apache.poi.xssf.usermodel.XSSFRelation;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePart;
 
 /**
  * Common interface for XSSF models, which have (typically
index 300f27cb9dcb15837e9e5cf6aa7a2b81a8a70fd3..af7d6456765aafd02eeb4646d69ab796216002e6 100644 (file)
@@ -7,7 +7,7 @@ import org.apache.poi.ss.usermodel.PictureData;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.xssf.model.XSSFWritableModel;
 import org.apache.poi.POIXMLException;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePart;
 
 public class XSSFActiveXData implements PictureData, XSSFWritableModel {
 
index 08e2a943cb7fa59fbe8e00b93e91595f0cce2d6c..d0c93cc19dcb44a64df6d23e6e262a57553a10fd 100644 (file)
 
 package org.apache.poi.xssf.usermodel;
 
-import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.POIXMLException;
-import org.apache.xmlbeans.XmlException;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
-
-import java.io.IOException;
 
 //YK: TODO: this is only a prototype
 public class XSSFDialogsheet extends XSSFSheet implements Sheet{
index f6e0a6c3259be2762356edf88c942a8542c5c552..92be5e373ccc67ffb6d7815fea5492e8fe47a4f6 100755 (executable)
@@ -21,7 +21,7 @@ import org.apache.poi.ss.usermodel.Drawing;
 import org.apache.poi.ss.usermodel.ClientAnchor;\r
 import org.apache.xmlbeans.XmlException;\r
 import org.apache.xmlbeans.XmlOptions;\r
-import org.openxml4j.opc.*;\r
+import org.apache.poi.openxml4j.opc.*;\r
 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*;\r
 import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;\r
 \r
index 9317d39520e581e86d1323050d44d4b1a8846628..4736ecbc4a448be03b31c0b7329f0c115b5f044c 100755 (executable)
@@ -22,8 +22,8 @@ import org.apache.poi.POIXMLException;
 import org.apache.poi.POIXMLRelation;\r
 import org.apache.poi.util.POILogger;\r
 import org.apache.poi.util.POILogFactory;\r
-import org.openxml4j.opc.PackageRelationship;\r
-import org.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
 \r
 import java.lang.reflect.Constructor;\r
 \r
index 4dbb72f716f9d7dfee18a36725ac665c5d930eaa..671e6f402fe364a6acc05ab0296d98c87d1a7907 100644 (file)
@@ -22,8 +22,8 @@ import org.apache.poi.ss.usermodel.Hyperlink;
 import org.apache.poi.ss.util.CellReference;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink;
 
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
 
 
 /**
index 8e12e8606d3a409563714abe07fef451da837205..3fc0e52e8ee3020778650f17ee4922c1c421503e 100755 (executable)
@@ -23,8 +23,8 @@ import org.apache.poi.ss.usermodel.Picture;
 import org.apache.poi.util.POILogger;\r
 import org.apache.poi.util.POILogFactory;\r
 import org.apache.poi.POIXMLDocumentPart;\r
-import org.openxml4j.opc.PackageRelationship;\r
-import org.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
 import org.w3c.dom.NodeList;\r
 import org.w3c.dom.Element;\r
 import javax.imageio.ImageIO;\r
index 736eea053c2718f31dd63320bad00fb4d2de98c1..05bb5983f90e46daa8b607792cdacc6a37fd9af9 100644 (file)
@@ -24,8 +24,8 @@ import org.apache.poi.util.IOUtils;
 import org.apache.poi.POIXMLDocumentPart;
 import org.apache.poi.POIXMLException;
 import org.apache.poi.POIXMLRelation;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
 
 /**
  * Raw picture data, normally attached to a SpreadsheetML Drawing.
index ef9bf7fbb16789b0f4a514439a52fb6b14206ecb..b985d644068426b11cf85abe58d7326974cd3510 100644 (file)
@@ -30,12 +30,12 @@ import org.apache.poi.xssf.model.SharedStringsTable;
 import org.apache.poi.xssf.model.CommentsTable;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
-import org.openxml4j.exceptions.InvalidFormatException;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackagePartName;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackageRelationshipCollection;
-import org.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 
 /**
  * 
index 686a8f03789e33c5a34068fe33542b27e2e66239..ac6b1268baaec339b98e30326147183f7e69d72a 100755 (executable)
@@ -18,7 +18,7 @@ package org.apache.poi.xssf.usermodel;
 \r
 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*;\r
 import org.openxmlformats.schemas.drawingml.x2006.main.*;\r
-import org.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
 \r
 /**\r
  * This object specifies a group shape that represents many shapes grouped together. This shape is to be treated\r
index 10391170a121817b79dd657e19f5ab525f982ce3..aa35007ff4f9b987222e263081ef943094a31690 100644 (file)
@@ -39,10 +39,10 @@ import org.apache.poi.util.POILogger;
 import org.apache.poi.util.POILogFactory;
 import org.apache.xmlbeans.XmlOptions;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackageRelationshipCollection;
-import org.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
 import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
 
index 7594e6aa86e492d18622ae2b881e3f128cce6ec1..9a68eb7fb6344006adb5a6633aba93a9334f1d23 100644 (file)
@@ -50,14 +50,14 @@ import org.apache.poi.xssf.model.StylesTable;
 import org.apache.xmlbeans.XmlException;
 import org.apache.xmlbeans.XmlObject;
 import org.apache.xmlbeans.XmlOptions;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackagePartName;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackageRelationshipTypes;
-import org.openxml4j.opc.PackagingURIHelper;
-import org.openxml4j.opc.TargetMode;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.TargetMode;
 import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookView;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTBookViews;
index 91d52a72ca7cc05bfe3bc3038f368eba364e5e96..1e97b129ef787970ff2c011483991f2fe59dc2e2 100644 (file)
@@ -29,8 +29,8 @@ import org.apache.poi.xwpf.model.XWPFParagraphDecorator;
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
 import org.apache.poi.xwpf.usermodel.XWPFTable;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.Package;
 
 /**
  * Helper class to extract text from an OOXML Word file
index a92972acf8717e1ac174fa6161447d8ca8d55a7e..3657c1fa670d164fe853dd0e54e53161afc85b27 100644 (file)
@@ -22,7 +22,7 @@ import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.apache.poi.xwpf.usermodel.XWPFFooter;
 import org.apache.poi.xwpf.usermodel.XWPFHeader;
 import org.apache.xmlbeans.XmlException;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePart;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHdrFtrRef;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.FtrDocument;
index b5f132affae8e1c6cd1b04ab8ecc4dc13b36f7a4..f1921783d88096047b96d346739435fa42fe3043 100644 (file)
@@ -27,10 +27,10 @@ import org.apache.poi.util.PackageHelper;
 import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
 import org.apache.xmlbeans.XmlException;
 import org.apache.xmlbeans.XmlOptions;
-import org.openxml4j.exceptions.InvalidFormatException;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.*;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.*;
+import org.apache.poi.openxml4j.opc.Package;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment;
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1;
index 3761241bfdd7d530475fe00fffb8776054d202e3..04bf13d113f55b44af992b208b5cd7ef5c789d48 100755 (executable)
@@ -22,8 +22,8 @@ import org.apache.poi.POIXMLException;
 import org.apache.poi.POIXMLRelation;\r
 import org.apache.poi.util.POILogger;\r
 import org.apache.poi.util.POILogFactory;\r
-import org.openxml4j.opc.PackageRelationship;\r
-import org.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
 \r
 import java.lang.reflect.Constructor;\r
 \r
index f6049c396d9c76b3a38811e2b4bba0d15b96492d..6a427bbdc00c50c44d5dd4e68b753e3b406dc1b4 100644 (file)
@@ -25,8 +25,8 @@ import org.apache.poi.util.IOUtils;
 import org.apache.poi.xslf.XSLFSlideShow;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
 
 import junit.framework.TestCase;
 
index 3d670dd9de839e9c658e3093eb1fe5b5b19fc651..33c11436e52831f95f196a45aef9edec79300b00 100644 (file)
@@ -20,7 +20,7 @@ import java.io.File;
 
 import org.apache.poi.xssf.extractor.XSSFExcelExtractor;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.Package;
 
 import junit.framework.TestCase;
 
@@ -33,7 +33,7 @@ public class TestXMLPropertiesTextExtractor extends TestCase {
        }
        
        public void testGetFromMainExtractor() throws Exception {
-               org.openxml4j.opc.Package pkg = Package.open(
+               org.apache.poi.openxml4j.opc.Package pkg = Package.open(
                                (new File(dirname, "ExcelWithAttachments.xlsx")).toString()
                );
                XSSFWorkbook wb = new XSSFWorkbook(pkg);
@@ -54,7 +54,7 @@ public class TestXMLPropertiesTextExtractor extends TestCase {
        }
 
        public void testCore() throws Exception {
-               org.openxml4j.opc.Package pkg = Package.open(
+               org.apache.poi.openxml4j.opc.Package pkg = Package.open(
                                (new File(dirname, "ExcelWithAttachments.xlsx")).toString()
                );
                XSSFWorkbook wb = new XSSFWorkbook(pkg);
@@ -71,7 +71,7 @@ public class TestXMLPropertiesTextExtractor extends TestCase {
        }
        
        public void testExtended() throws Exception {
-               org.openxml4j.opc.Package pkg = Package.open(
+               org.apache.poi.openxml4j.opc.Package pkg = Package.open(
                                (new File(dirname, "ExcelWithAttachments.xlsx")).toString()
                );
                XSSFWorkbook wb = new XSSFWorkbook(pkg);
index de5c07fd9dee0955e16467cb5ad8d0d70eac9d49..e75ba3c9e6d47fe02105e2486085ba58045383df 100644 (file)
@@ -33,8 +33,8 @@ import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
 
 import junit.framework.TestCase;
 
-import org.openxml4j.exceptions.InvalidOperationException;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.opc.Package;
 
 /**
  * Test that the extractor factory plays nicely
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/TestCore.java b/src/ooxml/testcases/org/apache/poi/openxml4j/TestCore.java
new file mode 100755 (executable)
index 0000000..26bf0ce
--- /dev/null
@@ -0,0 +1,102 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j;\r
+\r
+import java.io.File;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.apache.log4j.PropertyConfigurator;\r
+\r
+/**\r
+ * Core helper for tests.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public class TestCore {\r
+\r
+       private String testRootPath; // Test root path\r
+\r
+       /**\r
+        * All sample document are normally located at this place.\r
+        */\r
+       private static String pathRootProject; // Project root path\r
+\r
+       /**\r
+        * Demo logger\r
+        */\r
+       private static Logger logger = Logger.getLogger("org.apache.poi.openxml4j.test");\r
+\r
+       static {\r
+               pathRootProject = System.getProperty("user.dir") + File.separator + "bin";\r
+\r
+               // Log4j configuration\r
+               //PropertyConfigurator.configure(pathRootProject + File.separator\r
+               //              + "config.log4j");\r
+       }\r
+\r
+       /**\r
+        * Constructor. Initialize the demo.\r
+        * \r
+        */\r
+       public TestCore(Class cl) {\r
+               init(cl);\r
+       }\r
+\r
+       /**\r
+        * Initialize the test root path\r
+        */\r
+       public void init(Class cl) {\r
+               String packageName = cl.getPackage().getName();\r
+               // replace . by /\r
+               String sep = File.separator;\r
+               if (sep.equals("\\")) {\r
+                       sep = "\\\\";\r
+               }\r
+               testRootPath = pathRootProject + File.separator\r
+                               + packageName.replaceAll("\\.", sep)\r
+                               + File.separator;\r
+       }\r
+\r
+       // Accessors\r
+\r
+       /**\r
+        * Gets the test root path.\r
+        * \r
+        * @return The test root path.\r
+        */\r
+       public String getTestRootPath() {\r
+               return testRootPath;\r
+       }\r
+\r
+       /**\r
+        * Sets the test root path.\r
+        * \r
+        * @param testRoot\r
+        */\r
+       public void setTestRootPath(String testRoot) {\r
+               this.testRootPath = testRoot;\r
+       }\r
+\r
+       /**\r
+        * @return the logger\r
+        */\r
+       public static Logger getLogger() {\r
+               return logger;\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/AllTests.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/AllTests.java
new file mode 100755 (executable)
index 0000000..4b2cc45
--- /dev/null
@@ -0,0 +1,38 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import junit.framework.Test;\r
+import junit.framework.TestSuite;\r
+\r
+public class AllTests {\r
+\r
+       public static Test suite() {\r
+               TestSuite suite = new TestSuite(\r
+                               "Functional tests for org.apache.poi.openxml4j.opc");\r
+               suite.addTestSuite(TestListParts.class);\r
+               suite.addTestSuite(TestFileHelper.class);\r
+               suite.addTestSuite(TestPackage.class);\r
+               suite.addTestSuite(TestPackageCoreProperties.class);\r
+               suite.addTestSuite(TestPackagePartName.class);\r
+               suite.addTestSuite(TestPackagingURIHelper.class);\r
+               suite.addTestSuite(TestContentType.class);\r
+               suite.addTestSuite(TestPackageThumbnail.class);\r
+               return suite;\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsx
new file mode 100755 (executable)
index 0000000..ba5ed27
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx
new file mode 100755 (executable)
index 0000000..dc105f0
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx
new file mode 100755 (executable)
index 0000000..771cd18
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx
new file mode 100755 (executable)
index 0000000..2d8bb35
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx
new file mode 100755 (executable)
index 0000000..2d8bb35
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx
new file mode 100755 (executable)
index 0000000..ccc990d
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx
new file mode 100755 (executable)
index 0000000..8e77521
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx
new file mode 100755 (executable)
index 0000000..a275cf4
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg
new file mode 100755 (executable)
index 0000000..5119960
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx
new file mode 100755 (executable)
index 0000000..3da1dae
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx
new file mode 100755 (executable)
index 0000000..c8c43dc
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx
new file mode 100755 (executable)
index 0000000..a41daa5
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java
new file mode 100755 (executable)
index 0000000..9f70f6f
--- /dev/null
@@ -0,0 +1,117 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.internal.ContentType;\r
+\r
+/**\r
+ * Tests for content type (ContentType class).\r
+ * \r
+ * @author Julien Chable\r
+ */\r
+public class TestContentType extends TestCase {\r
+\r
+       /**\r
+        * Check rule M1.13: Package implementers shall only create and only\r
+        * recognize parts with a content type; format designers shall specify a\r
+        * content type for each part included in the format. Content types for\r
+        * package parts shall fit the definition and syntax for media types as\r
+        * specified in RFC 2616, §3.7.\r
+        */\r
+       public void testContentTypeValidation() throws InvalidFormatException {\r
+               String[] contentTypesToTest = new String[] { "text/xml",\r
+                               "application/pgp-key", "application/vnd.hp-PCLXL",\r
+                               "application/vnd.lotus-1-2-3" };\r
+               for (int i = 0; i < contentTypesToTest.length; ++i) {\r
+                       new ContentType(contentTypesToTest[i]);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Check rule M1.13 : Package implementers shall only create and only\r
+        * recognize parts with a content type; format designers shall specify a\r
+        * content type for each part included in the format. Content types for\r
+        * package parts shall fit the definition and syntax for media types as\r
+        * specified in RFC 2616, §3.7.\r
+        * \r
+        * Check rule M1.14: Content types shall not use linear white space either\r
+        * between the type and subtype or between an attribute and its value.\r
+        * Content types also shall not have leading or trailing white spaces.\r
+        * Package implementers shall create only such content types and shall\r
+        * require such content types when retrieving a part from a package; format\r
+        * designers shall specify only such content types for inclusion in the\r
+        * format.\r
+        */\r
+       public void testContentTypeValidationFailure() {\r
+               String[] contentTypesToTest = new String[] { "text/xml/app", "",\r
+                               "test", "text(xml/xml", "text)xml/xml", "text<xml/xml",\r
+                               "text>/xml", "text@/xml", "text,/xml", "text;/xml",\r
+                               "text:/xml", "text\\/xml", "t/ext/xml", "t\"ext/xml",\r
+                               "text[/xml", "text]/xml", "text?/xml", "tex=t/xml",\r
+                               "te{xt/xml", "tex}t/xml", "te xt/xml",\r
+                               "text" + (char) 9 + "/xml", "text xml", " text/xml " };\r
+               for (int i = 0; i < contentTypesToTest.length; ++i) {\r
+                       try {\r
+                               new ContentType(contentTypesToTest[i]);\r
+                       } catch (InvalidFormatException e) {\r
+                               continue;\r
+                       }\r
+                       fail("Must have fail for content type: '" + contentTypesToTest[i]\r
+                                       + "' !");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Check rule [O1.2]: Format designers might restrict the usage of\r
+        * parameters for content types.\r
+        */\r
+       public void testContentTypeParameterFailure() {\r
+               String[] contentTypesToTest = new String[] { "mail/toto;titi=tata",\r
+                               "text/xml;a=b;c=d", "mail/toto;\"titi=tata\"" };\r
+               for (int i = 0; i < contentTypesToTest.length; ++i) {\r
+                       try {\r
+                               new ContentType(contentTypesToTest[i]);\r
+                       } catch (InvalidFormatException e) {\r
+                               continue;\r
+                       }\r
+                       fail("Must have fail for content type: '" + contentTypesToTest[i]\r
+                                       + "' !");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Check rule M1.15: The package implementer shall require a content type\r
+        * that does not include comments and the format designer shall specify such\r
+        * a content type.\r
+        */\r
+       public void testContentTypeCommentFailure() {\r
+               String[] contentTypesToTest = new String[] { "text/xml(comment)" };\r
+               for (int i = 0; i < contentTypesToTest.length; ++i) {\r
+                       try {\r
+                               new ContentType(contentTypesToTest[i]);\r
+                       } catch (InvalidFormatException e) {\r
+                               continue;\r
+                       }\r
+                       fail("Must have fail for content type: '" + contentTypesToTest[i]\r
+                                       + "' !");\r
+               }\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestFileHelper.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestFileHelper.java
new file mode 100755 (executable)
index 0000000..280d6d9
--- /dev/null
@@ -0,0 +1,45 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.File;\r
+import java.util.TreeMap;\r
+\r
+import org.apache.poi.openxml4j.opc.internal.FileHelper;\r
+\r
+import junit.framework.TestCase;\r
+\r
+/**\r
+ * Test TestFileHelper class.\r
+ * \r
+ * @author Julien Chable\r
+ */\r
+public class TestFileHelper extends TestCase {\r
+\r
+       public void testGetDirectory() {\r
+               TreeMap<String, String> expectedValue = new TreeMap<String, String>();\r
+               expectedValue.put("c:\\test\\test.doc", "c:\\test");\r
+               expectedValue.put("d:\\test\\test2\\test.doc.xml", "d:\\test\\test2");\r
+\r
+               for (String filename : expectedValue.keySet()) {\r
+                       assertTrue(expectedValue.get(filename).equalsIgnoreCase(\r
+                                       FileHelper.getDirectory(new File(filename))\r
+                                                       .getAbsolutePath()));\r
+               }\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestListParts.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestListParts.java
new file mode 100755 (executable)
index 0000000..359c02f
--- /dev/null
@@ -0,0 +1,104 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.File;\r
+import java.util.TreeMap;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackageAccess;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+\r
+import org.apache.poi.openxml4j.TestCore;\r
+\r
+public class TestListParts extends TestCase {\r
+\r
+       TestCore testCore = new TestCore(this.getClass());\r
+\r
+       TreeMap<PackagePartName, String> expectedValues;\r
+\r
+       TreeMap<PackagePartName, String> values;\r
+\r
+       @Override\r
+       protected void setUp() throws Exception {\r
+               values = new TreeMap<PackagePartName, String>();\r
+\r
+               // Expected values\r
+               expectedValues = new TreeMap<PackagePartName, String>();\r
+               expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),\r
+                               "application/vnd.openxmlformats-package.relationships+xml");\r
+\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/docProps/app.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.extended-properties+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/docProps/core.xml"),\r
+                               "application/vnd.openxmlformats-package.core-properties+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/word/_rels/document.xml.rels"),\r
+                               "application/vnd.openxmlformats-package.relationships+xml");\r
+               expectedValues\r
+                               .put(\r
+                                               PackagingURIHelper.createPartName("/word/document.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/word/fontTable.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/word/media/image1.gif"), "image/gif");\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/word/settings.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml");\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/word/styles.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/word/theme/theme1.xml"),\r
+                               "application/vnd.openxmlformats-officedocument.theme+xml");\r
+               expectedValues\r
+                               .put(\r
+                                               PackagingURIHelper\r
+                                                               .createPartName("/word/webSettings.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml");\r
+       }\r
+\r
+       /**\r
+        * List all parts of a package.\r
+        */\r
+       public void testListParts() throws InvalidFormatException {\r
+               String filepath = System.getProperty("openxml4j.testdata.input") + File.separator\r
+                               + "sample.docx";\r
+\r
+               Package p = Package.open(filepath, PackageAccess.READ);\r
+               for (PackagePart part : p.getParts()) {\r
+                       values.put(part.getPartName(), part.getContentType());\r
+                       TestCore.getLogger().debug(part.getPartName());\r
+               }\r
+\r
+               // Compare expected values with values return by the package\r
+               for (PackagePartName partName : expectedValues.keySet()) {\r
+                       assertNotNull(values.get(partName));\r
+                       assertEquals(expectedValues.get(partName), values.get(partName));\r
+               }\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
new file mode 100755 (executable)
index 0000000..8d4e331
--- /dev/null
@@ -0,0 +1,440 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.lang.reflect.Field;\r
+import java.net.URI;\r
+import java.util.TreeMap;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.dom4j.Document;\r
+import org.dom4j.DocumentHelper;\r
+import org.dom4j.Element;\r
+import org.dom4j.Namespace;\r
+import org.dom4j.QName;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;\r
+import org.apache.poi.openxml4j.opc.internal.FileHelper;\r
+\r
+import org.apache.poi.openxml4j.TestCore;\r
+\r
+public class TestPackage extends TestCase {\r
+\r
+       TestCore testCore = new TestCore(this.getClass());\r
+\r
+       /**\r
+        * Test that just opening and closing the file doesn't alter the document.\r
+        */\r
+       public void testOpenSave() throws Exception {\r
+               File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator\r
+                               + "TestPackageCommon.docx");\r
+               File targetFile = new File(System.getProperty("openxml4j.testdata.output")\r
+                               + File.separator + "TestPackageOpenSaveTMP.docx");\r
+               assertTrue("Source file " + originalFile + " doesn't exist!", originalFile.exists());\r
+\r
+               Package p = Package.open(originalFile.getAbsolutePath(),\r
+                               PackageAccess.READ_WRITE);\r
+               p.save(targetFile.getAbsoluteFile());\r
+\r
+               // Compare the original and newly saved document\r
+               assertTrue(targetFile.exists());\r
+               //ZipFileAssert.assertEquals(originalFile, targetFile);\r
+               assertTrue(targetFile.delete());\r
+       }\r
+       \r
+       /**\r
+        * Test that when we create a new Package, we give it\r
+        *  the correct default content types\r
+        */\r
+       public void testCreateGetsContentTypes() throws Exception {\r
+               File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator \r
+                               + "TestCreatePackageTMP.docx");\r
+               \r
+               // Zap the target file, in case of an earlier run\r
+               if(targetFile.exists()) targetFile.delete();\r
+               \r
+               Package pkg = Package.create(targetFile);\r
+               \r
+               // Check it has content types for rels and xml\r
+               ContentTypeManager ctm = getContentTypeManager(pkg);\r
+               assertEquals(\r
+                               "application/xml",\r
+                               ctm.getContentType(\r
+                                               PackagingURIHelper.createPartName("/foo.xml")\r
+                               )\r
+               );\r
+               assertEquals(\r
+                               ContentTypes.RELATIONSHIPS_PART,\r
+                               ctm.getContentType(\r
+                                               PackagingURIHelper.createPartName("/foo.rels")\r
+                               )\r
+               );\r
+               assertNull(\r
+                               ctm.getContentType(\r
+                                               PackagingURIHelper.createPartName("/foo.txt")\r
+                               )\r
+               );\r
+       }\r
+\r
+       /**\r
+        * Test package creation.\r
+        */\r
+       public void testCreatePackageAddPart() throws Exception {\r
+               File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator\r
+                               + "TestCreatePackageTMP.docx");\r
+\r
+               File expectedFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator\r
+                               + "TestCreatePackageOUTPUT.docx");\r
+\r
+               // Zap the target file, in case of an earlier run\r
+               if(targetFile.exists()) targetFile.delete();\r
+               \r
+               // Create a package\r
+               Package pkg = Package.create(targetFile);\r
+               PackagePartName corePartName = PackagingURIHelper\r
+                               .createPartName("/word/document.xml");\r
+\r
+               pkg.addRelationship(corePartName, TargetMode.INTERNAL,\r
+                               PackageRelationshipTypes.CORE_DOCUMENT, "rId1");\r
+\r
+               PackagePart corePart = pkg\r
+                               .createPart(\r
+                                               corePartName,\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");\r
+\r
+               Document doc = DocumentHelper.createDocument();\r
+               Namespace nsWordprocessinML = new Namespace("w",\r
+                               "http://schemas.openxmlformats.org/wordprocessingml/2006/main");\r
+               Element elDocument = doc.addElement(new QName("document",\r
+                               nsWordprocessinML));\r
+               Element elBody = elDocument.addElement(new QName("body",\r
+                               nsWordprocessinML));\r
+               Element elParagraph = elBody.addElement(new QName("p",\r
+                               nsWordprocessinML));\r
+               Element elRun = elParagraph\r
+                               .addElement(new QName("r", nsWordprocessinML));\r
+               Element elText = elRun.addElement(new QName("t", nsWordprocessinML));\r
+               elText.setText("Hello Open XML !");\r
+\r
+               StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());\r
+               pkg.close();\r
+\r
+               //ZipFileAssert.assertEquals(expectedFile, targetFile);\r
+               assertTrue(targetFile.delete());\r
+       }\r
+       \r
+       /**\r
+        * Tests that we can create a new package, add a core\r
+        *  document and another part, save and re-load and\r
+        *  have everything setup as expected\r
+        */\r
+       public void testCreatePackageWithCoreDocument() throws Exception {\r
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
+               Package pkg = Package.create(baos);\r
+               \r
+               // Add a core document\r
+        PackagePartName corePartName = PackagingURIHelper.createPartName("/xl/workbook.xml");\r
+        // Create main part relationship\r
+        pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT, "rId1");\r
+        // Create main document part\r
+        PackagePart corePart = pkg.createPart(corePartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");\r
+        // Put in some dummy content\r
+        OutputStream coreOut = corePart.getOutputStream();\r
+        coreOut.write("<dummy-xml />".getBytes());\r
+        coreOut.close();\r
+               \r
+               // And another bit\r
+        PackagePartName sheetPartName = PackagingURIHelper.createPartName("/xl/worksheets/sheet1.xml");\r
+        PackageRelationship rel =\r
+                corePart.addRelationship(sheetPartName, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", "rSheet1");\r
+        PackagePart part = pkg.createPart(sheetPartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");\r
+        // Dummy content again\r
+        coreOut = corePart.getOutputStream();\r
+        coreOut.write("<dummy-xml2 />".getBytes());\r
+        coreOut.close();\r
+        \r
+        \r
+        // Check things are as expected\r
+        PackageRelationshipCollection coreRels =\r
+               pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);\r
+        assertEquals(1, coreRels.size());\r
+        PackageRelationship coreRel = coreRels.getRelationship(0);\r
+        assertEquals("/", coreRel.getSourceURI().toString());\r
+        assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());\r
+        assertNotNull(pkg.getPart(coreRel));\r
+        \r
+        \r
+        // Save and re-load\r
+        pkg.close();\r
+        FileOutputStream fout = new FileOutputStream(File.createTempFile("testCreatePackageWithCoreDocument", ".zip"));\r
+        fout.write(baos.toByteArray());\r
+        fout.close();\r
+        pkg = Package.open(new ByteArrayInputStream(baos.toByteArray()));\r
+        \r
+        \r
+        // Check still right\r
+        coreRels = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);\r
+        assertEquals(1, coreRels.size());\r
+        coreRel = coreRels.getRelationship(0);\r
+        assertEquals("/", coreRel.getSourceURI().toString());\r
+        assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());\r
+        assertNotNull(pkg.getPart(coreRel));\r
+       }\r
+\r
+       /**\r
+        * Test package opening.\r
+        */\r
+       public void testOpenPackage() throws Exception {\r
+               File targetFile = new File(System.getProperty("openxml4j.testdata.output")\r
+                               + File.separator + "TestOpenPackageTMP.docx");\r
+\r
+               File inputFile = new File(System.getProperty("openxml4j.testdata.input")\r
+                               + File.separator + "TestOpenPackageINPUT.docx");\r
+\r
+               File expectedFile = new File(System.getProperty("openxml4j.testdata.output")\r
+                + File.separator + "TestOpenPackageOUTPUT.docx");\r
+\r
+               // Copy the input file in the output directory\r
+               FileHelper.copyFile(inputFile, targetFile);\r
+\r
+               // Create a package\r
+               Package pkg = Package.open(targetFile.getAbsolutePath());\r
+\r
+               // Modify core part\r
+               PackagePartName corePartName = PackagingURIHelper\r
+                               .createPartName("/word/document.xml");\r
+\r
+               PackagePart corePart = pkg.getPart(corePartName);\r
+\r
+               // Delete some part to have a valid document\r
+               for (PackageRelationship rel : corePart.getRelationships()) {\r
+                       corePart.removeRelationship(rel.getId());\r
+                       pkg.removePart(PackagingURIHelper.createPartName(PackagingURIHelper\r
+                                       .resolvePartUri(corePart.getPartName().getURI(), rel\r
+                                                       .getTargetURI())));\r
+               }\r
+\r
+               // Create a content\r
+               Document doc = DocumentHelper.createDocument();\r
+               Namespace nsWordprocessinML = new Namespace("w",\r
+                               "http://schemas.openxmlformats.org/wordprocessingml/2006/main");\r
+               Element elDocument = doc.addElement(new QName("document",\r
+                               nsWordprocessinML));\r
+               Element elBody = elDocument.addElement(new QName("body",\r
+                               nsWordprocessinML));\r
+               Element elParagraph = elBody.addElement(new QName("p",\r
+                               nsWordprocessinML));\r
+               Element elRun = elParagraph\r
+                               .addElement(new QName("r", nsWordprocessinML));\r
+               Element elText = elRun.addElement(new QName("t", nsWordprocessinML));\r
+               elText.setText("Hello Open XML !");\r
+\r
+               StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());\r
+\r
+               // Save and close\r
+               try {\r
+                       pkg.close();\r
+               } catch (IOException e) {\r
+                       fail();\r
+               }\r
+\r
+               //ZipFileAssert.assertEquals(expectedFile, targetFile);\r
+               assertTrue(targetFile.delete());\r
+       }\r
+       \r
+       /**\r
+        * Checks that we can write a package to a simple\r
+        *  OutputStream, in addition to the normal writing\r
+        *  to a file\r
+        */\r
+       public void testSaveToOutputStream() throws Exception {\r
+               File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator\r
+                               + "TestPackageCommon.docx");\r
+               File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator\r
+                + "TestPackageOpenSaveTMP.docx");\r
+               assertTrue("Source file " + originalFile + " doesn't exist!", originalFile.exists());\r
+\r
+               Package p = Package.open(originalFile.getAbsolutePath(),\r
+                               PackageAccess.READ_WRITE);\r
+               FileOutputStream fout = new FileOutputStream(targetFile);\r
+               p.save(fout);\r
+               fout.close();\r
+\r
+               // Compare the original and newly saved document\r
+               assertTrue(targetFile.exists());\r
+               //ZipFileAssert.assertEquals(originalFile, targetFile);\r
+               assertTrue(targetFile.delete());\r
+       }\r
+\r
+       /**\r
+        * Checks that we can open+read a package from a\r
+        *  simple InputStream, in addition to the normal\r
+        *  reading from a file\r
+        */\r
+       public void testOpenFromInputStream() throws Exception {\r
+               File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator\r
+                               + "TestPackageCommon.docx");\r
+               assertTrue("Source file " + originalFile + " doesn't exist!", originalFile.exists());\r
+               \r
+               FileInputStream finp = new FileInputStream(originalFile);\r
+               \r
+               Package p = Package.open(finp);\r
+               \r
+               assertNotNull(p);\r
+               assertNotNull(p.getRelationships());\r
+               assertEquals(12, p.getParts().size());\r
+               \r
+               // Check it has the usual bits\r
+               assertTrue(p.hasRelationships());\r
+               assertTrue(p.containPart(PackagingURIHelper.createPartName("/_rels/.rels")));\r
+       }\r
+\r
+    /**\r
+     * TODO: fix and unable\r
+     */\r
+    public void disabled_testRemovePartRecursive() throws Exception {\r
+               File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator\r
+                               + "TestPackageCommon.docx");\r
+               File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator\r
+                               + "TestPackageRemovePartRecursiveOUTPUT.docx");\r
+               File tempFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator\r
+                               + "TestPackageRemovePartRecursiveTMP.docx");\r
+\r
+               Package p = Package.open(originalFile.getAbsolutePath(),\r
+                               PackageAccess.READ_WRITE);\r
+               p.removePartRecursive(PackagingURIHelper.createPartName(new URI(\r
+                               "/word/document.xml")));\r
+               p.save(tempFile.getAbsoluteFile());\r
+\r
+               // Compare the original and newly saved document\r
+               assertTrue(targetFile.exists());\r
+               //ZipFileAssert.assertEquals(targetFile, tempFile);\r
+               assertTrue(targetFile.delete());\r
+       }\r
+\r
+       public void testDeletePart() throws InvalidFormatException {\r
+               TreeMap<PackagePartName, String> expectedValues;\r
+               TreeMap<PackagePartName, String> values;\r
+\r
+               values = new TreeMap<PackagePartName, String>();\r
+\r
+               // Expected values\r
+               expectedValues = new TreeMap<PackagePartName, String>();\r
+               expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),\r
+                               "application/vnd.openxmlformats-package.relationships+xml");\r
+\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/docProps/app.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.extended-properties+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/docProps/core.xml"),\r
+                               "application/vnd.openxmlformats-package.core-properties+xml");\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/word/fontTable.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/word/media/image1.gif"), "image/gif");\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/word/settings.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml");\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/word/styles.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/word/theme/theme1.xml"),\r
+                               "application/vnd.openxmlformats-officedocument.theme+xml");\r
+               expectedValues\r
+                               .put(\r
+                                               PackagingURIHelper\r
+                                                               .createPartName("/word/webSettings.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml");\r
+\r
+               String filepath = System.getProperty("openxml4j.testdata.input") + File.separator\r
+                               + "sample.docx";\r
+\r
+               Package p = Package.open(filepath, PackageAccess.READ_WRITE);\r
+               // Remove the core part\r
+               p.deletePart(PackagingURIHelper.createPartName("/word/document.xml"));\r
+\r
+               for (PackagePart part : p.getParts()) {\r
+                       values.put(part.getPartName(), part.getContentType());\r
+                       TestCore.getLogger().debug(part.getPartName());\r
+               }\r
+\r
+               // Compare expected values with values return by the package\r
+               for (PackagePartName partName : expectedValues.keySet()) {\r
+                       assertNotNull(values.get(partName));\r
+                       assertEquals(expectedValues.get(partName), values.get(partName));\r
+               }\r
+               // Don't save modfications\r
+               p.revert();\r
+       }\r
+       \r
+       public void testDeletePartRecursive() throws InvalidFormatException {\r
+               TreeMap<PackagePartName, String> expectedValues;\r
+               TreeMap<PackagePartName, String> values;\r
+\r
+               values = new TreeMap<PackagePartName, String>();\r
+\r
+               // Expected values\r
+               expectedValues = new TreeMap<PackagePartName, String>();\r
+               expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),\r
+                               "application/vnd.openxmlformats-package.relationships+xml");\r
+\r
+               expectedValues\r
+                               .put(PackagingURIHelper.createPartName("/docProps/app.xml"),\r
+                                               "application/vnd.openxmlformats-officedocument.extended-properties+xml");\r
+               expectedValues.put(PackagingURIHelper\r
+                               .createPartName("/docProps/core.xml"),\r
+                               "application/vnd.openxmlformats-package.core-properties+xml");\r
+\r
+               String filepath = System.getProperty("openxml4j.testdata.input") + File.separator\r
+                               + "sample.docx";\r
+\r
+               Package p = Package.open(filepath, PackageAccess.READ_WRITE);\r
+               // Remove the core part\r
+               p.deletePartRecursive(PackagingURIHelper.createPartName("/word/document.xml"));\r
+\r
+               for (PackagePart part : p.getParts()) {\r
+                       values.put(part.getPartName(), part.getContentType());\r
+                       TestCore.getLogger().debug(part.getPartName());\r
+               }\r
+\r
+               // Compare expected values with values return by the package\r
+               for (PackagePartName partName : expectedValues.keySet()) {\r
+                       assertNotNull(values.get(partName));\r
+                       assertEquals(expectedValues.get(partName), values.get(partName));\r
+               }\r
+               // Don't save modfications\r
+               p.revert();\r
+       }\r
+       \r
+       private static ContentTypeManager getContentTypeManager(Package pkg) throws Exception {\r
+               Field f = Package.class.getDeclaredField("contentTypeManager");\r
+               f.setAccessible(true);\r
+               return (ContentTypeManager)f.get(pkg);\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java
new file mode 100755 (executable)
index 0000000..acb7cd8
--- /dev/null
@@ -0,0 +1,125 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.File;\r
+import java.text.ParsePosition;\r
+import java.text.SimpleDateFormat;\r
+import java.util.Date;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;\r
+import org.apache.poi.openxml4j.util.Nullable;\r
+\r
+import org.apache.poi.openxml4j.TestCore;\r
+import org.apache.log4j.Logger;\r
+\r
+public class TestPackageCoreProperties extends TestCase {\r
+\r
+       TestCore testCore = new TestCore(this.getClass());\r
+\r
+       /**\r
+        * Test package core properties getters.\r
+        */\r
+       public void testGetProperties() {\r
+               try {\r
+                       // Open the package\r
+                       Package p = Package.open(System.getProperty("openxml4j.testdata.input") + File.separator\r
+                                       + "TestPackageCoreProperiesGetters.docx",\r
+                                       PackageAccess.READ);\r
+                       compareProperties(p);\r
+                       p.revert();\r
+               } catch (OpenXML4JException e) {\r
+                       Logger.getLogger("org.apache.poi.openxml4j.demo").debug(e.getMessage());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test package core properties setters.\r
+        */\r
+       public void testSetProperties() throws Exception {\r
+               String inputPath = System.getProperty("openxml4j.testdata.input")\r
+                               + File.separator + "TestPackageCoreProperiesSetters.docx";\r
+\r
+               String outputFilename = System.getProperty("openxml4j.testdata.input")\r
+                               + File.separator + "TestPackageCoreProperiesSettersOUTPUT.docx";\r
+\r
+               // Open package\r
+               Package p = Package.open(inputPath, PackageAccess.READ_WRITE);\r
+\r
+               SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");\r
+               Date dateToInsert = df.parse("2007-05-12T08:00:00Z", new ParsePosition(\r
+                               0));\r
+\r
+               PackageProperties props = p.getPackageProperties();\r
+               props.setCategoryProperty("MyCategory");\r
+               props.setContentStatusProperty("MyContentStatus");\r
+               props.setContentTypeProperty("MyContentType");\r
+               props.setCreatedProperty(new Nullable<Date>(dateToInsert));\r
+               props.setCreatorProperty("MyCreator");\r
+               props.setDescriptionProperty("MyDescription");\r
+               props.setIdentifierProperty("MyIdentifier");\r
+               props.setKeywordsProperty("MyKeywords");\r
+               props.setLanguageProperty("MyLanguage");\r
+               props.setLastModifiedByProperty("Julien Chable");\r
+               props.setLastPrintedProperty(new Nullable<Date>(dateToInsert));\r
+               props.setModifiedProperty(new Nullable<Date>(dateToInsert));\r
+               props.setRevisionProperty("2");\r
+               props.setTitleProperty("MyTitle");\r
+               props.setSubjectProperty("MySubject");\r
+               props.setVersionProperty("2");\r
+               // Save the package in the output directory\r
+               p.save(new File(outputFilename));\r
+\r
+               // Open the newly created file to check core properties saved values.\r
+               File fOut = new File(outputFilename);\r
+               Package p2 = Package.open(outputFilename, PackageAccess.READ);\r
+               compareProperties(p2);\r
+               p2.revert();\r
+               fOut.delete();\r
+       }\r
+\r
+       private void compareProperties(Package p) throws InvalidFormatException {\r
+               SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");\r
+               Date expectedDate = df.parse("2007-05-12T08:00:00Z", new ParsePosition(\r
+                               0));\r
+\r
+               // Gets the core properties\r
+               PackageProperties props = p.getPackageProperties();\r
+               assertEquals("MyCategory", props.getCategoryProperty().getValue());\r
+               assertEquals("MyContentStatus", props.getContentStatusProperty()\r
+                               .getValue());\r
+               assertEquals("MyContentType", props.getContentTypeProperty().getValue());\r
+               assertEquals(expectedDate, props.getCreatedProperty().getValue());\r
+               assertEquals("MyCreator", props.getCreatorProperty().getValue());\r
+               assertEquals("MyDescription", props.getDescriptionProperty().getValue());\r
+               assertEquals("MyIdentifier", props.getIdentifierProperty().getValue());\r
+               assertEquals("MyKeywords", props.getKeywordsProperty().getValue());\r
+               assertEquals("MyLanguage", props.getLanguageProperty().getValue());\r
+               assertEquals("Julien Chable", props.getLastModifiedByProperty()\r
+                               .getValue());\r
+               assertEquals(expectedDate, props.getLastPrintedProperty().getValue());\r
+               assertEquals(expectedDate, props.getModifiedProperty().getValue());\r
+               assertEquals("2", props.getRevisionProperty().getValue());\r
+               assertEquals("MySubject", props.getSubjectProperty().getValue());\r
+               assertEquals("MyTitle", props.getTitleProperty().getValue());\r
+               assertEquals("2", props.getVersionProperty().getValue());\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagePartName.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagePartName.java
new file mode 100755 (executable)
index 0000000..e1ec879
--- /dev/null
@@ -0,0 +1,35 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+\r
+import junit.framework.TestCase;\r
+\r
+public class TestPackagePartName extends TestCase {\r
+\r
+       /**\r
+        * Test method getExtension().\r
+        */\r
+       public void testGetExtension() throws Exception{\r
+               PackagePartName name1 = PackagingURIHelper.createPartName("/doc/props/document.xml");\r
+               PackagePartName name2 = PackagingURIHelper.createPartName("/root/document");\r
+               assertEquals("xml", name1.getExtension());\r
+               assertEquals("", name2.getExtension());\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageThumbnail.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageThumbnail.java
new file mode 100755 (executable)
index 0000000..bbf5402
--- /dev/null
@@ -0,0 +1,67 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.io.File;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackageAccess;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
+\r
+import org.apache.poi.openxml4j.TestCore;\r
+\r
+/**\r
+ * Test the addition of thumbnail in a package.\r
+ * \r
+ * @author Julien Chable\r
+ */\r
+public class TestPackageThumbnail extends TestCase {\r
+\r
+       TestCore testCore = new TestCore(this.getClass());\r
+\r
+       /**\r
+        * Test package addThumbnail() method.\r
+        */\r
+       public void testSetProperties() throws Exception {\r
+               String inputPath = System.getProperty("openxml4j.testdata.input")\r
+                               + File.separator + "TestPackageThumbnail.docx";\r
+\r
+               String imagePath = System.getProperty("openxml4j.testdata.input")\r
+                               + File.separator + "thumbnail.jpg";\r
+\r
+               String outputFilename = System.getProperty("openxml4j.testdata.output")\r
+                               + File.separator + "TestPackageThumbnailOUTPUT.docx";\r
+\r
+               // Open package\r
+               Package p = Package.open(inputPath, PackageAccess.READ_WRITE);\r
+               p.addThumbnail(imagePath);\r
+               // Save the package in the output directory\r
+               p.save(new File(outputFilename));\r
+\r
+               // Open the newly created file to check core properties saved values.\r
+               File fOut = new File(outputFilename);\r
+               Package p2 = Package.open(outputFilename, PackageAccess.READ);\r
+               if (p2.getRelationshipsByType(PackageRelationshipTypes.THUMBNAIL)\r
+                               .size() == 0)\r
+                       fail("Thumbnail not added to the package !");\r
+               p2.revert();\r
+               //fOut.delete();\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagingURIHelper.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagingURIHelper.java
new file mode 100755 (executable)
index 0000000..478552f
--- /dev/null
@@ -0,0 +1,117 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.openxml4j.opc;\r
+\r
+import java.net.URI;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.ContentTypes;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+\r
+public class TestPackagingURIHelper extends TestCase {\r
+\r
+       /**\r
+        * Test relativizePartName() method.\r
+     *\r
+     * TODO: fix and unable\r
+        */\r
+       public void testRelativizeURI() throws Exception {\r
+               URI uri1 = new URI("/word/document.xml");\r
+               URI uri2 = new URI("/word/media/image1.gif");\r
+               \r
+               // Document to image is down a directory\r
+               URI retURI1to2 = PackagingURIHelper.relativizeURI(uri1, uri2);\r
+               assertEquals("media/image1.gif", retURI1to2.getPath());\r
+               // Image to document is up a directory\r
+               URI retURI2to1 = PackagingURIHelper.relativizeURI(uri2, uri1);\r
+               assertEquals("../document.xml", retURI2to1.getPath());\r
+               \r
+               // Document and CustomXML parts totally different [Julien C.]\r
+               URI uriCustomXml = new URI("/customXml/item1.xml");\r
+               URI uriRes = PackagingURIHelper.relativizeURI(uri1, uriCustomXml);\r
+               assertEquals("../customXml/item1.xml", uriRes.toString());\r
+\r
+               // Document to itself is the same place (empty URI)\r
+               URI retURI2 = PackagingURIHelper.relativizeURI(uri1, uri1);\r
+               assertEquals("", retURI2.getPath());\r
+\r
+               // Document and root totally different\r
+               URI uri4 = new URI("/");\r
+               try {\r
+                       PackagingURIHelper.relativizeURI(uri1, uri4);\r
+                       //TODO: figure oout why the assertion fails\r
+            //fail("Must throw an exception ! Can't relativize with an empty URI");\r
+               } catch (Exception e) {\r
+                       // Do nothing\r
+               }\r
+               try {\r
+                       PackagingURIHelper.relativizeURI(uri4, uri1);\r
+            //TODO: figure oout why the assertion fails\r
+                       //fail("Must throw an exception ! Can't relativize with an empty URI");\r
+               } catch (Exception e) {\r
+                       // Do nothing\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test createPartName(String, y)\r
+        */\r
+       public void testCreatePartNameRelativeString()\r
+                       throws InvalidFormatException {\r
+               PackagePartName partNameToValid = PackagingURIHelper\r
+                               .createPartName("/word/media/image1.gif");\r
+\r
+               Package pkg = Package.create("DELETEIFEXISTS.docx");\r
+               // Base part\r
+               PackagePartName nameBase = PackagingURIHelper\r
+                               .createPartName("/word/document.xml");\r
+               PackagePart partBase = pkg.createPart(nameBase, ContentTypes.XML);\r
+               // Relative part name\r
+               PackagePartName relativeName = PackagingURIHelper.createPartName(\r
+                               "media/image1.gif", partBase);\r
+               assertTrue("The part name must be equal to "\r
+                               + partNameToValid.getName(), partNameToValid\r
+                               .equals(relativeName));\r
+               pkg.revert();\r
+       }\r
+\r
+       /**\r
+        * Test createPartName(URI, y)\r
+        */\r
+       public void testCreatePartNameRelativeURI() throws Exception {\r
+               PackagePartName partNameToValid = PackagingURIHelper\r
+                               .createPartName("/word/media/image1.gif");\r
+\r
+               Package pkg = Package.create("DELETEIFEXISTS.docx");\r
+               // Base part\r
+               PackagePartName nameBase = PackagingURIHelper\r
+                               .createPartName("/word/document.xml");\r
+               PackagePart partBase = pkg.createPart(nameBase, ContentTypes.XML);\r
+               // Relative part name\r
+               PackagePartName relativeName = PackagingURIHelper.createPartName(\r
+                               new URI("media/image1.gif"), partBase);\r
+               assertTrue("The part name must be equal to "\r
+                               + partNameToValid.getName(), partNameToValid\r
+                               .equals(relativeName));\r
+               pkg.revert();\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestRelationships.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestRelationships.java
new file mode 100755 (executable)
index 0000000..3ca1281
--- /dev/null
@@ -0,0 +1,273 @@
+/* ====================================================================
+   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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackageAccess;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.TargetMode;
+
+import org.apache.poi.openxml4j.TestCore;
+
+
+public class TestRelationships extends TestCase {
+       public static final String HYPERLINK_REL_TYPE =
+               "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
+       public static final String COMMENTS_REL_TYPE =
+               "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
+       public static final String SHEET_WITH_COMMENTS =
+               "/xl/worksheets/sheet1.xml";
+
+    TestCore testCore = new TestCore(this.getClass());
+
+    /**
+     * Test relationships are correctly loaded. This at the moment fails (as of r499)
+     * whenever a document is loaded before its correspondig .rels file has been found.
+     * The code in this case assumes there are no relationships defined, but it should
+     * really look also for not yet loaded parts.
+     */
+    public void testLoadRelationships() throws Exception {
+        String filepath = System.getProperty("openxml4j.testdata.input") + File.separator
+            + "sample.xlsx";
+        Package pkg = Package.open(filepath, PackageAccess.READ);
+        TestCore.getLogger().debug("1: " + pkg);
+        PackageRelationshipCollection rels = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);
+        PackageRelationship coreDocRelationship = rels.getRelationship(0);
+        PackagePart corePart = pkg.getPart(coreDocRelationship);
+        String relIds[] = { "rId1", "rId2", "rId3" };
+        for (String relId : relIds) {
+            PackageRelationship rel = corePart.getRelationship(relId);
+            assertNotNull(rel);
+            PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI());
+            PackagePart sheetPart = pkg.getPart(relName);
+            assertEquals("Number of relationships1 for " + sheetPart.getPartName(), 1, sheetPart.getRelationships().size());
+        }
+    }
+    
+    /**
+     * Checks that we can fetch a collection of relations by
+     *  type, then grab from within there by id
+     */
+    public void testFetchFromCollection() throws Exception {
+        String filepath = System.getProperty("openxml4j.testdata.input") + File.separator
+               + "ExcelWithHyperlinks.xlsx";
+       
+        Package pkg = Package.open(filepath, PackageAccess.READ);
+        PackagePart sheet = pkg.getPart(
+                       PackagingURIHelper.createPartName(SHEET_WITH_COMMENTS));
+        assertNotNull(sheet);
+        
+        assertTrue(sheet.hasRelationships());
+        assertEquals(6, sheet.getRelationships().size());
+        
+        // Should have three hyperlinks, and one comment
+        PackageRelationshipCollection hyperlinks =
+               sheet.getRelationshipsByType(HYPERLINK_REL_TYPE);
+        PackageRelationshipCollection comments =
+               sheet.getRelationshipsByType(COMMENTS_REL_TYPE);
+        assertEquals(3, hyperlinks.size());
+        assertEquals(1, comments.size());
+        
+        // Check we can get bits out by id
+        // Hyperlinks are rId1, rId2 and rId3
+        // Comment is rId6
+        assertNotNull(hyperlinks.getRelationshipByID("rId1"));
+        assertNotNull(hyperlinks.getRelationshipByID("rId2"));
+        assertNotNull(hyperlinks.getRelationshipByID("rId3"));
+        assertNull(hyperlinks.getRelationshipByID("rId6"));
+        
+        assertNull(comments.getRelationshipByID("rId1"));
+        assertNull(comments.getRelationshipByID("rId2"));
+        assertNull(comments.getRelationshipByID("rId3"));
+        assertNotNull(comments.getRelationshipByID("rId6"));
+        
+        assertNotNull(sheet.getRelationship("rId1"));
+        assertNotNull(sheet.getRelationship("rId2"));
+        assertNotNull(sheet.getRelationship("rId3"));
+        assertNotNull(sheet.getRelationship("rId6"));
+    }
+    
+    /**
+     * Excel uses relations on sheets to store the details of 
+     *  external hyperlinks. Check we can load these ok.
+     */
+    public void testLoadExcelHyperlinkRelations() throws Exception {
+        String filepath = System.getProperty("openxml4j.testdata.input") + File.separator
+               + "ExcelWithHyperlinks.xlsx";
+               
+           Package pkg = Package.open(filepath, PackageAccess.READ);
+           PackagePart sheet = pkg.getPart(
+                       PackagingURIHelper.createPartName(SHEET_WITH_COMMENTS));
+           assertNotNull(sheet);
+
+           // rId1 is url
+           PackageRelationship url = sheet.getRelationship("rId1");
+           assertNotNull(url);
+           assertEquals("rId1", url.getId());
+           assertEquals("/xl/worksheets/sheet1.xml", url.getSourceURI().toString());
+           assertEquals("http://poi.apache.org/", url.getTargetURI().toString());
+           
+           // rId2 is file
+           PackageRelationship file = sheet.getRelationship("rId2");
+           assertNotNull(file);
+           assertEquals("rId2", file.getId());
+           assertEquals("/xl/worksheets/sheet1.xml", file.getSourceURI().toString());
+           assertEquals("WithVariousData.xlsx", file.getTargetURI().toString());
+           
+           // rId3 is mailto
+           PackageRelationship mailto = sheet.getRelationship("rId3");
+           assertNotNull(mailto);
+           assertEquals("rId3", mailto.getId());
+           assertEquals("/xl/worksheets/sheet1.xml", mailto.getSourceURI().toString());
+           assertEquals("mailto:dev@poi.apache.org?subject=XSSF%20Hyperlinks", mailto.getTargetURI().toString());
+    }
+    
+    /*
+     * Excel uses relations on sheets to store the details of 
+     *  external hyperlinks. Check we can create these ok, 
+     *  then still read them later
+     */
+    public void testCreateExcelHyperlinkRelations() throws Exception {
+        String filepath = System.getProperty("openxml4j.testdata.input") + File.separator
+               + "ExcelWithHyperlinks.xlsx";
+               
+           Package pkg = Package.open(filepath, PackageAccess.READ_WRITE);
+           PackagePart sheet = pkg.getPart(
+                       PackagingURIHelper.createPartName(SHEET_WITH_COMMENTS));
+           assertNotNull(sheet);
+           
+           assertEquals(3, sheet.getRelationshipsByType(HYPERLINK_REL_TYPE).size());
+           
+           // Add three new ones
+           PackageRelationship openxml4j =
+               sheet.addExternalRelationship("http://www.openxml4j.org/", HYPERLINK_REL_TYPE);
+           PackageRelationship sf =
+               sheet.addExternalRelationship("http://openxml4j.sf.net/", HYPERLINK_REL_TYPE);
+           PackageRelationship file =
+               sheet.addExternalRelationship("MyDocument.docx", HYPERLINK_REL_TYPE);
+           
+           // Check they were added properly
+           assertNotNull(openxml4j);
+           assertNotNull(sf);
+           assertNotNull(file);
+           
+           assertEquals(6, sheet.getRelationshipsByType(HYPERLINK_REL_TYPE).size());
+           
+           assertEquals("http://www.openxml4j.org/", openxml4j.getTargetURI().toString());
+           assertEquals("/xl/worksheets/sheet1.xml", openxml4j.getSourceURI().toString());
+           assertEquals(HYPERLINK_REL_TYPE, openxml4j.getRelationshipType());
+           
+           assertEquals("http://openxml4j.sf.net/", sf.getTargetURI().toString());
+           assertEquals("/xl/worksheets/sheet1.xml", sf.getSourceURI().toString());
+           assertEquals(HYPERLINK_REL_TYPE, sf.getRelationshipType());
+           
+           assertEquals("MyDocument.docx", file.getTargetURI().toString());
+           assertEquals("/xl/worksheets/sheet1.xml", file.getSourceURI().toString());
+           assertEquals(HYPERLINK_REL_TYPE, file.getRelationshipType());
+           
+           // Will get ids 7, 8 and 9, as we already have 1-6
+           assertEquals("rId7", openxml4j.getId());
+           assertEquals("rId8", sf.getId());
+           assertEquals("rId9", file.getId());
+           
+           
+           // Write out and re-load
+           ByteArrayOutputStream baos = new ByteArrayOutputStream();
+           pkg.save(baos);
+           ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+           pkg = Package.open(bais);
+           
+           // Check again
+           sheet = pkg.getPart(
+                       PackagingURIHelper.createPartName(SHEET_WITH_COMMENTS));
+           
+           assertEquals(6, sheet.getRelationshipsByType(HYPERLINK_REL_TYPE).size());
+           
+           assertEquals("http://poi.apache.org/",
+                       sheet.getRelationship("rId1").getTargetURI().toString());
+           assertEquals("mailto:dev@poi.apache.org?subject=XSSF%20Hyperlinks",
+                       sheet.getRelationship("rId3").getTargetURI().toString());
+           
+           assertEquals("http://www.openxml4j.org/",
+                       sheet.getRelationship("rId7").getTargetURI().toString());
+           assertEquals("http://openxml4j.sf.net/",
+                       sheet.getRelationship("rId8").getTargetURI().toString());
+           assertEquals("MyDocument.docx",
+                       sheet.getRelationship("rId9").getTargetURI().toString());
+    }
+
+    public void testCreateRelationsFromScratch() throws Exception {
+       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+       Package pkg = Package.create(baos);
+       
+       PackagePart partA =
+               pkg.createPart(PackagingURIHelper.createPartName("/partA"), "text/plain");
+       PackagePart partB =
+               pkg.createPart(PackagingURIHelper.createPartName("/partB"), "image/png");
+       assertNotNull(partA);
+       assertNotNull(partB);
+       
+       // Internal
+       partA.addRelationship(partB.getPartName(), TargetMode.INTERNAL, "http://example/Rel");
+       
+       // External
+       partA.addExternalRelationship("http://poi.apache.org/", "http://example/poi");
+       partB.addExternalRelationship("http://poi.apache.org/ss/", "http://example/poi/ss");
+
+       // Check as expected currently
+       assertEquals("/partB", partA.getRelationship("rId1").getTargetURI().toString());
+       assertEquals("http://poi.apache.org/", 
+                       partA.getRelationship("rId2").getTargetURI().toString());
+       assertEquals("http://poi.apache.org/ss/", 
+                       partB.getRelationship("rId1").getTargetURI().toString());
+       
+       
+       // Save, and re-load
+       pkg.close();
+       ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+       pkg = Package.open(bais);
+       
+       partA = pkg.getPart(PackagingURIHelper.createPartName("/partA"));
+       partB = pkg.getPart(PackagingURIHelper.createPartName("/partB"));
+       
+       
+       // Check the relations
+       assertEquals(2, partA.getRelationships().size());
+       assertEquals(1, partB.getRelationships().size());
+       
+       assertEquals("/partB", partA.getRelationship("rId1").getTargetURI().toString());
+       assertEquals("http://poi.apache.org/", 
+                       partA.getRelationship("rId2").getTargetURI().toString());
+       assertEquals("http://poi.apache.org/ss/", 
+                       partB.getRelationship("rId1").getTargetURI().toString());
+       // Check core too
+       assertEquals("/docProps/core.xml",
+                       pkg.getRelationshipsByType("http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties").getRelationship(0).getTargetURI().toString());
+    }
+}
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/AllTests.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/AllTests.java
new file mode 100755 (executable)
index 0000000..56974b0
--- /dev/null
@@ -0,0 +1,36 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.compliance;\r
+\r
+import junit.framework.Test;\r
+import junit.framework.TestSuite;\r
+\r
+public class AllTests {\r
+\r
+       public static Test suite() {\r
+               TestSuite suite = new TestSuite(\r
+                               "Test for test.org.apache.poi.openxml4j.opc.compliance");\r
+               // $JUnit-BEGIN$\r
+               suite.addTestSuite(OPCCompliance_PartName.class);\r
+               suite.addTestSuite(OPCCompliance_CoreProperties.class);\r
+               suite.addTestSuite(OPCCompliance_PackageModel.class);\r
+               // $JUnit-END$\r
+               return suite;\r
+       }\r
+\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx
new file mode 100755 (executable)
index 0000000..89533f0
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx
new file mode 100755 (executable)
index 0000000..63555e4
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx
new file mode 100755 (executable)
index 0000000..304e5ce
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx
new file mode 100755 (executable)
index 0000000..7b4f765
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx
new file mode 100755 (executable)
index 0000000..7a63ee1
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx
new file mode 100755 (executable)
index 0000000..3ee9fc3
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx
new file mode 100755 (executable)
index 0000000..7a63ee1
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx
new file mode 100755 (executable)
index 0000000..3984aea
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx
new file mode 100755 (executable)
index 0000000..8ccfb61
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java
new file mode 100755 (executable)
index 0000000..a26fd30
--- /dev/null
@@ -0,0 +1,258 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.compliance;\r
+\r
+import java.io.File;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.opc.ContentTypes;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+import org.apache.poi.openxml4j.opc.TargetMode;\r
+\r
+import org.apache.poi.openxml4j.TestCore;\r
+import junit.framework.TestCase;\r
+\r
+/**\r
+ * Test core properties Open Packaging Convention compliance.\r
+ * \r
+ * M4.1: The format designer shall specify and the format producer shall create\r
+ * at most one core properties relationship for a package. A format consumer\r
+ * shall consider more than one core properties relationship for a package to be\r
+ * an error. If present, the relationship shall target the Core Properties part.\r
+ * \r
+ * M4.2: The format designer shall not specify and the format producer shall not\r
+ * create Core Properties that use the Markup Compatibility namespace as defined\r
+ * in Annex F, "Standard Namespaces and Content Types". A format consumer shall\r
+ * consider the use of the Markup Compatibility namespace to be an error.\r
+ * \r
+ * M4.3: Producers shall not create a document element that contains refinements\r
+ * to the Dublin Core elements, except for the two specified in the schema:\r
+ * <dcterms:created> and <dcterms:modified> Consumers shall consider a document\r
+ * element that violates this constraint to be an error.\r
+ * \r
+ * M4.4: Producers shall not create a document element that contains the\r
+ * xml:lang attribute. Consumers shall consider a document element that violates\r
+ * this constraint to be an error.\r
+ * \r
+ * M4.5: Producers shall not create a document element that contains the\r
+ * xsi:type attribute, except for a <dcterms:created> or <dcterms:modified>\r
+ * element where the xsi:type attribute shall be present and shall hold the\r
+ * value dcterms:W3CDTF, where dcterms is the namespace prefix of the Dublin\r
+ * Core namespace. Consumers shall consider a document element that violates\r
+ * this constraint to be an error.\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public class OPCCompliance_CoreProperties extends TestCase {\r
+\r
+       TestCore testCore = new TestCore(this.getClass());\r
+\r
+       public void testCorePropertiesPart() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.compliance.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx";\r
+                       pkg = Package.open(filepath);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+               } catch (InvalidFormatException e) {\r
+                       fail("OPC compliance failure: the core properties is considered as invalid than it's not !");\r
+               } finally {\r
+                       pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.1 rule.\r
+        */\r
+       public void testOnlyOneCorePropertiesPart() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.compliance.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx";\r
+                       pkg = Package.open(filepath);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.1 -> A format consumer shall consider more than one core properties relationship for a package to be an error.");\r
+               } catch (InvalidFormatException e) {\r
+                       // DO nothing, it's the normal behavior\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.1 rule.\r
+        */\r
+       public void testOnlyOneCorePropertiesPart_AddRelationship() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.testdata.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx";\r
+                       pkg = Package.open(filepath);\r
+                       pkg.addRelationship(PackagingURIHelper.createPartName(new URI(\r
+                                       "/docProps/core2.xml")), TargetMode.INTERNAL,\r
+                                       PackageRelationshipTypes.CORE_PROPERTIES);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.1 -> A format consumer shall consider more than one core properties relationship for a package to be an error.");\r
+               } catch (InvalidOperationException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } catch (InvalidFormatException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } catch (URISyntaxException e) {\r
+                       // Should never happen\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.1 rule.\r
+        */\r
+       public void testOnlyOneCorePropertiesPart_AddPart() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.testdata.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx";\r
+                       pkg = Package.open(filepath);\r
+                       pkg.createPart(PackagingURIHelper.createPartName(new URI(\r
+                                       "/docProps/core2.xml")), ContentTypes.CORE_PROPERTIES_PART);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.1 -> A format consumer shall consider more than one core properties relationship for a package to be an error.");\r
+               } catch (InvalidFormatException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } catch (InvalidOperationException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } catch (URISyntaxException e) {\r
+                       // Should never happen\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.2 rule.\r
+        */\r
+       public void testDoNotUseCompatibilityMarkup() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.compliance.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx";\r
+                       pkg = Package.open(filepath);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.2 -> A format consumer shall consider the use of the Markup Compatibility namespace to be an error.");\r
+               } catch (InvalidFormatException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.3 rule.\r
+        */\r
+       public void testDCTermsNamespaceLimitedUse() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.compliance.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx";\r
+                       pkg = Package.open(filepath);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.3 -> Producers shall not create a document element that contains refinements to the Dublin Core elements, except for the two specified in the schema: <dcterms:created> and <dcterms:modified> Consumers shall consider a document element that violates this constraint to be an error.");\r
+               } catch (InvalidFormatException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.4 rule.\r
+        */\r
+       public void testUnauthorizedXMLLangAttribute() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.compliance.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx";\r
+                       pkg = Package.open(filepath);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.4 -> Producers shall not create a document element that contains refinements to the Dublin Core elements, except for the two specified in the schema: <dcterms:created> and <dcterms:modified> Consumers shall consider a document element that violates this constraint to be an error.");\r
+               } catch (InvalidFormatException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.5 rule.\r
+        */\r
+       public void testLimitedXSITypeAttribute_NotPresent() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.compliance.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx";\r
+                       pkg = Package.open(filepath);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.5 -> Producers shall not create a document element that contains refinements to the Dublin Core elements, except for the two specified in the schema: <dcterms:created> and <dcterms:modified> Consumers shall consider a document element that violates this constraint to be an error.");\r
+               } catch (InvalidFormatException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test M4.5 rule.\r
+        */\r
+       public void testLimitedXSITypeAttribute_PresentWithUnauthorizedValue() {\r
+               Package pkg = null;\r
+               try {\r
+                       String filepath = System.getProperty("openxml4j.compliance.input")\r
+                                       + File.separator\r
+                                       + "OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx";\r
+                       pkg = Package.open(filepath);\r
+                       // Normally must thrown an InvalidFormatException exception.\r
+                       fail("OPC compliance failure: M4.5 -> Producers shall not create a document element that contains refinements to the Dublin Core elements, except for the two specified in the schema: <dcterms:created> and <dcterms:modified> Consumers shall consider a document element that violates this constraint to be an error.");\r
+               } catch (InvalidFormatException e) {\r
+                       // Do nothing, it's the normal behavior\r
+               } finally {\r
+                       if (pkg != null)\r
+                               pkg.revert();\r
+               }\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PackageModel.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PackageModel.java
new file mode 100755 (executable)
index 0000000..f54c294
--- /dev/null
@@ -0,0 +1,167 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.compliance;\r
+\r
+import java.io.File;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;\r
+import org.apache.poi.openxml4j.opc.ContentTypes;\r
+import org.apache.poi.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+import org.apache.poi.openxml4j.opc.TargetMode;\r
+\r
+import org.apache.poi.openxml4j.TestCore;\r
+\r
+/**\r
+ * Test Open Packaging Convention package model compliance.\r
+ * \r
+ * M1.11 : A package implementer shall neither create nor recognize a part with\r
+ * a part name derived from another part name by appending segments to it.\r
+ * \r
+ * @author Julien Chable\r
+ */\r
+public class OPCCompliance_PackageModel extends TestCase {\r
+\r
+       TestCore testCore = new TestCore(this.getClass());\r
+\r
+       public OPCCompliance_PackageModel(String name) {\r
+               super(name);\r
+       }\r
+\r
+       /**\r
+        * A package implementer shall neither create nor recognize a part with a\r
+        * part name derived from another part name by appending segments to it.\r
+        * [M1.11]\r
+        */\r
+       public void testPartNameDerivationAdditionFailure() {\r
+               Package pkg = null;\r
+               try {\r
+                       pkg = Package.create("TODELETEIFEXIST.docx");\r
+                       PackagePartName name = PackagingURIHelper\r
+                                       .createPartName("/word/document.xml");\r
+                       PackagePartName nameDerived = PackagingURIHelper\r
+                                       .createPartName("/word/document.xml/image1.gif");\r
+                       pkg.createPart(name, ContentTypes.XML);\r
+                       pkg.createPart(nameDerived, ContentTypes.EXTENSION_GIF);\r
+               } catch (InvalidOperationException e) {\r
+                       pkg.revert();\r
+                       return;\r
+               } catch (InvalidFormatException e) {\r
+                       fail(e.getMessage());\r
+               }\r
+               fail("A package implementer shall neither create nor recognize a part with a"\r
+                               + " part name derived from another part name by appending segments to it."\r
+                               + " [M1.11]");\r
+       }\r
+\r
+       /**\r
+        * A package implementer shall neither create nor recognize a part with a\r
+        * part name derived from another part name by appending segments to it.\r
+        * [M1.11]\r
+        */\r
+       public void testPartNameDerivationReadingFailure() {\r
+               String filepath = System.getProperty("openxml4j.compliance.input")\r
+                               + File.separator + "OPCCompliance_DerivedPartNameFAIL.docx";\r
+               try {\r
+                       Package.open(filepath);\r
+               } catch (InvalidFormatException e) {\r
+                       return;\r
+               }\r
+               fail("A package implementer shall neither create nor recognize a part with a"\r
+                               + " part name derived from another part name by appending segments to it."\r
+                               + " [M1.11]");\r
+       }\r
+\r
+       /**\r
+        * Rule M1.12 : Packages shall not contain equivalent part names and package\r
+        * implementers shall neither create nor recognize packages with equivalent\r
+        * part names.\r
+        */\r
+       public void testAddPackageAlreadyAddFailure() throws Exception {\r
+               Package pkg = Package.create("DELETEIFEXISTS.docx");\r
+               PackagePartName name1 = null;\r
+               PackagePartName name2 = null;\r
+               try {\r
+                       name1 = PackagingURIHelper.createPartName("/word/document.xml");\r
+                       name2 = PackagingURIHelper.createPartName("/word/document.xml");\r
+               } catch (InvalidFormatException e) {\r
+                       throw new Exception(e.getMessage());\r
+               }\r
+               pkg.createPart(name1, ContentTypes.XML);\r
+               try {\r
+                       pkg.createPart(name2, ContentTypes.XML);\r
+               } catch (InvalidOperationException e) {\r
+                       return;\r
+               }\r
+               fail("Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");\r
+       }\r
+\r
+       /**\r
+        * Rule M1.12 : Packages shall not contain equivalent part names and package\r
+        * implementers shall neither create nor recognize packages with equivalent\r
+        * part names.\r
+        */\r
+       public void testAddPackageAlreadyAddFailure2() throws Exception {\r
+               Package pkg = Package.create("DELETEIFEXISTS.docx");\r
+               PackagePartName partName = null;\r
+               try {\r
+                       partName = PackagingURIHelper.createPartName("/word/document.xml");\r
+               } catch (InvalidFormatException e) {\r
+                       throw new Exception(e.getMessage());\r
+               }\r
+               pkg.createPart(partName, ContentTypes.XML);\r
+               try {\r
+                       pkg.createPart(partName, ContentTypes.XML);\r
+               } catch (InvalidOperationException e) {\r
+                       return;\r
+               }\r
+               fail("Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");\r
+       }\r
+\r
+       /**\r
+        * Try to add a relationship to a relationship part.\r
+        * \r
+        * Check rule M1.25: The Relationships part shall not have relationships to\r
+        * any other part. Package implementers shall enforce this requirement upon\r
+        * the attempt to create such a relationship and shall treat any such\r
+        * relationship as invalid.\r
+        */\r
+       public void testAddRelationshipRelationshipsPartFailure() {\r
+               Package pkg = Package.create("DELETEIFEXISTS.docx");\r
+               PackagePartName name1 = null;\r
+               try {\r
+                       name1 = PackagingURIHelper\r
+                                       .createPartName("/test/_rels/document.xml.rels");\r
+               } catch (InvalidFormatException e) {\r
+                       fail("This exception should never happen !");\r
+               }\r
+\r
+               try {\r
+                       pkg.addRelationship(name1, TargetMode.INTERNAL,\r
+                                       PackageRelationshipTypes.CORE_DOCUMENT);\r
+               } catch (InvalidOperationException e) {\r
+                       return;\r
+               }\r
+               fail("Fail test -> M1.25: The Relationships part shall not have relationships to any other part");\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PartName.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PartName.java
new file mode 100755 (executable)
index 0000000..80f6be7
--- /dev/null
@@ -0,0 +1,253 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.compliance;\r
+\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+\r
+/**\r
+ * Test part name Open Packaging Convention compliance.\r
+ * \r
+ * (Open Packaging Convention 8.1.1 Part names) :\r
+ * \r
+ * The part name grammar is defined as follows:\r
+ * \r
+ * part_name = 1*( "/" segment )\r
+ * \r
+ * segment = 1*( pchar )\r
+ * \r
+ * pchar is defined in RFC 3986.\r
+ * \r
+ * The part name grammar implies the following constraints. The package\r
+ * implementer shall neither create any part that violates these constraints nor\r
+ * retrieve any data from a package as a part if the purported part name\r
+ * violates these constraints.\r
+ * \r
+ * A part name shall not be empty. [M1.1]\r
+ * \r
+ * A part name shall not have empty segments. [M1.3]\r
+ * \r
+ * A part name shall start with a forward slash ("/") character. [M1.4]\r
+ * \r
+ * A part name shall not have a forward slash as the last character. [M1.5]\r
+ * \r
+ * A segment shall not hold any characters other than pchar characters. [M1.6]\r
+ * \r
+ * Part segments have the following additional constraints. The package\r
+ * implementer shall neither create any part with a part name comprised of a\r
+ * segment that violates these constraints nor retrieve any data from a package\r
+ * as a part if the purported part name contains a segment that violates these\r
+ * constraints.\r
+ * \r
+ * A segment shall not contain percent-encoded forward slash ("/"), or backward\r
+ * slash ("\") characters. [M1.7]\r
+ * \r
+ * A segment shall not contain percent-encoded unreserved characters. [M1.8]\r
+ * \r
+ * A segment shall not end with a dot (".") character. [M1.9]\r
+ * \r
+ * A segment shall include at least one non-dot character. [M1.10]\r
+ * \r
+ * A package implementer shall neither create nor recognize a part with a part\r
+ * name derived from another part name by appending segments to it. [M1.11]\r
+ * \r
+ * Part name equivalence is determined by comparing part names as\r
+ * case-insensitive ASCII strings. [M1.12]\r
+ * \r
+ * @author Julien Chable\r
+ * @version 1.0\r
+ */\r
+public class OPCCompliance_PartName extends TestCase {\r
+\r
+       public OPCCompliance_PartName(String name) {\r
+               super(name);\r
+       }\r
+\r
+       /**\r
+        * Test some common invalid names.\r
+        * \r
+        * A segment shall not contain percent-encoded unreserved characters. [M1.8]\r
+        */\r
+       public void testInvalidPartNames() {\r
+               String[] invalidNames = { "/", "/xml./doc.xml", "[Content_Types].xml", "//xml/." };\r
+               for (String s : invalidNames) {\r
+                       URI uri = null;\r
+                       try {\r
+                               uri = new URI(s);\r
+                       } catch (URISyntaxException e) {\r
+                               assertTrue(s == "[Content_Types].xml");\r
+                               continue;\r
+                       }\r
+                       assertFalse("This part name SHOULD NOT be valid: " + s,\r
+                                       PackagingURIHelper.isValidPartName(uri));\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Test some common valid names.\r
+        */\r
+       public void testValidPartNames() throws URISyntaxException {\r
+               String[] validNames = { "/xml/item1.xml", "/document.xml",\r
+                               "/a/%D1%86.xml" };\r
+               for (String s : validNames)\r
+                       assertTrue("This part name SHOULD be valid: " + s,\r
+                                       PackagingURIHelper.isValidPartName(new URI(s)));\r
+       }\r
+\r
+       /**\r
+        * A part name shall not be empty. [M1.1]\r
+        */\r
+       public void testEmptyPartNameFailure() throws URISyntaxException {\r
+               try {\r
+                       PackagingURIHelper.createPartName(new URI(""));\r
+                       fail("A part name shall not be empty. [M1.1]");\r
+               } catch (InvalidFormatException e) {\r
+                       // Normal behaviour\r
+               }\r
+       }\r
+\r
+       /**\r
+        * A part name shall not have empty segments. [M1.3]\r
+        * \r
+        * A segment shall not end with a dot ('.') character. [M1.9]\r
+        * \r
+        * A segment shall include at least one non-dot character. [M1.10]\r
+        */\r
+       public void testPartNameWithInvalidSegmentsFailure() {\r
+               String[] invalidNames = { "//document.xml", "//word/document.xml",\r
+                               "/word//document.rels", "/word//rels//document.rels",\r
+                               "/xml./doc.xml", "/document.", "/./document.xml",\r
+                               "/word/./doc.rels", "/%2F/document.xml" };\r
+               try {\r
+                       for (String s : invalidNames)\r
+                               assertFalse(\r
+                                               "A part name shall not have empty segments. [M1.3]",\r
+                                               PackagingURIHelper.isValidPartName(new URI(s)));\r
+               } catch (URISyntaxException e) {\r
+                       fail();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * A segment shall not hold any characters other than pchar characters.\r
+        * [M1.6].\r
+        */\r
+       public void testPartNameWithNonPCharCharacters() {\r
+               String[] invalidNames = { "/doc�&.xml" };\r
+               try {\r
+                       for (String s : invalidNames)\r
+                               assertTrue(\r
+                                               "A segment shall not contain non pchar characters [M1.6] : "\r
+                                                               + s, PackagingURIHelper\r
+                                                               .isValidPartName(new URI(s)));\r
+               } catch (URISyntaxException e) {\r
+                       fail();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * A segment shall not contain percent-encoded unreserved characters [M1.8].\r
+        */\r
+       public void testPartNameWithUnreservedEncodedCharactersFailure() {\r
+               String[] invalidNames = { "/a/docum%65nt.xml" };\r
+               try {\r
+                       for (String s : invalidNames)\r
+                               assertFalse(\r
+                                               "A segment shall not contain percent-encoded unreserved characters [M1.8] : "\r
+                                                               + s, PackagingURIHelper\r
+                                                               .isValidPartName(new URI(s)));\r
+               } catch (URISyntaxException e) {\r
+                       fail();\r
+               }\r
+       }\r
+\r
+       /**\r
+        * A part name shall start with a forward slash ('/') character. [M1.4]\r
+        */\r
+       public void testPartNameStartsWithAForwardSlashFailure()\r
+                       throws URISyntaxException {\r
+               try {\r
+                       PackagingURIHelper.createPartName(new URI("document.xml"));\r
+                       fail("A part name shall start with a forward slash ('/') character. [M1.4]");\r
+               } catch (InvalidFormatException e) {\r
+                       // Normal behaviour\r
+               }\r
+       }\r
+\r
+       /**\r
+        * A part name shall not have a forward slash as the last character. [M1.5]\r
+        */\r
+       public void testPartNameEndsWithAForwardSlashFailure()\r
+                       throws URISyntaxException {\r
+               try {\r
+                       PackagingURIHelper.createPartName(new URI("/document.xml/"));\r
+                       fail("A part name shall not have a forward slash as the last character. [M1.5]");\r
+               } catch (InvalidFormatException e) {\r
+                       // Normal behaviour\r
+               }\r
+\r
+       }\r
+\r
+       /**\r
+        * Part name equivalence is determined by comparing part names as\r
+        * case-insensitive ASCII strings. [M1.12]\r
+        */\r
+       public void testPartNameComparaison() throws Exception {\r
+               String[] partName1 = { "/word/document.xml", "/docProps/core.xml",\r
+                               "/rels/.rels" };\r
+               String[] partName2 = { "/WORD/DocUment.XML", "/docProps/core.xml",\r
+                               "/rels/.rels" };\r
+               for (int i = 0; i < partName1.length || i < partName2.length; ++i) {\r
+                       PackagePartName p1 = PackagingURIHelper\r
+                                       .createPartName(partName1[i]);\r
+                       PackagePartName p2 = PackagingURIHelper\r
+                                       .createPartName(partName2[i]);\r
+                       assertTrue(p1.equals(p2));\r
+                       assertTrue(p1.compareTo(p2) == 0);\r
+                       assertTrue(p1.hashCode() == p2.hashCode());\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Part name equivalence is determined by comparing part names as\r
+        * case-insensitive ASCII strings. [M1.12].\r
+        * \r
+        * All the comparaisons MUST FAIL !\r
+        */\r
+       public void testPartNameComparaisonFailure() throws Exception {\r
+               String[] partName1 = { "/word/document.xml", "/docProps/core.xml",\r
+                               "/rels/.rels" };\r
+               String[] partName2 = { "/WORD/DocUment.XML2", "/docProp/core.xml",\r
+                               "/rels/rels" };\r
+               for (int i = 0; i < partName1.length || i < partName2.length; ++i) {\r
+                       PackagePartName p1 = PackagingURIHelper\r
+                                       .createPartName(partName1[i]);\r
+                       PackagePartName p2 = PackagingURIHelper\r
+                                       .createPartName(partName2[i]);\r
+                       assertFalse(p1.equals(p2));\r
+                       assertFalse(p1.compareTo(p2) == 0);\r
+                       assertFalse(p1.hashCode() == p2.hashCode());\r
+               }\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/AllTests.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/AllTests.java
new file mode 100755 (executable)
index 0000000..25d140c
--- /dev/null
@@ -0,0 +1,33 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import junit.framework.Test;\r
+import junit.framework.TestSuite;\r
+\r
+public class AllTests {\r
+\r
+       public static Test suite() {\r
+               TestSuite suite = new TestSuite(\r
+                               "Test for test.org.apache.poi.openxml4j.opc.internal");\r
+               //$JUnit-BEGIN$\r
+               suite.addTestSuite(TestContentTypeManager.class);\r
+               //$JUnit-END$\r
+               return suite;\r
+       }\r
+}\r
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docx
new file mode 100755 (executable)
index 0000000..dc87bc8
Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docx differ
diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java
new file mode 100755 (executable)
index 0000000..6af3e6f
--- /dev/null
@@ -0,0 +1,122 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.openxml4j.opc.internal;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;\r
+import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;\r
+\r
+import org.apache.poi.openxml4j.TestCore;\r
+\r
+public class TestContentTypeManager extends TestCase {\r
+\r
+       TestCore testCore = new TestCore(this.getClass());\r
+\r
+       /**\r
+        * Test the properties part content parsing.\r
+        */\r
+       public void testContentType() throws Exception {\r
+               // File originalFile = new File(testCore.getTestRootPath() +\r
+               // File.separator +\r
+               // "sample.docx");\r
+               //\r
+               // // Retrieves core properties part\r
+               // Package p = Package.open(originalFile.getAbsolutePath(),\r
+               // PackageAccess.READ);\r
+               // PackageRelationship corePropertiesRelationship = p\r
+               // .getRelationshipsByType(\r
+               // PackageRelationshipTypes.CORE_PROPERTIES)\r
+               // .getRelationship(0);\r
+               // PackagePart coreDocument = p.getPart(corePropertiesRelationship);\r
+               //\r
+               // ContentTypeManager ctm = new ZipContentTypeManager(coreDocument\r
+               // .getInputStream());\r
+               //\r
+               // // TODO\r
+               //fail();\r
+       }\r
+\r
+       /**\r
+        * Test the addition of several default and override content types.\r
+        */\r
+       public void testContentTypeAddition() throws Exception {\r
+               ContentTypeManager ctm = new ZipContentTypeManager(null, null);\r
+\r
+               PackagePartName name1 = PackagingURIHelper\r
+                               .createPartName("/foo/foo.XML");\r
+               PackagePartName name2 = PackagingURIHelper\r
+                               .createPartName("/foo/foo2.xml");\r
+               PackagePartName name3 = PackagingURIHelper\r
+                               .createPartName("/foo/doc.rels");\r
+               PackagePartName name4 = PackagingURIHelper\r
+                               .createPartName("/foo/doc.RELS");\r
+\r
+               // Add content types\r
+               ctm.addContentType(name1, "foo-type1");\r
+               ctm.addContentType(name2, "foo-type2");\r
+               ctm.addContentType(name3, "text/xml+rel");\r
+               ctm.addContentType(name4, "text/xml+rel");\r
+\r
+               assertEquals(ctm.getContentType(name1), "foo-type1");\r
+               assertEquals(ctm.getContentType(name2), "foo-type2");\r
+               assertEquals(ctm.getContentType(name3), "text/xml+rel");\r
+               assertEquals(ctm.getContentType(name3), "text/xml+rel");\r
+       }\r
+\r
+       /**\r
+        * Test the addition then removal of content types.\r
+        */\r
+       public void testContentTypeRemoval() throws Exception {\r
+               ContentTypeManager ctm = new ZipContentTypeManager(null, null);\r
+\r
+               PackagePartName name1 = PackagingURIHelper\r
+                               .createPartName("/foo/foo.xml");\r
+               PackagePartName name2 = PackagingURIHelper\r
+                               .createPartName("/foo/foo2.xml");\r
+               PackagePartName name3 = PackagingURIHelper\r
+                               .createPartName("/foo/doc.rels");\r
+               PackagePartName name4 = PackagingURIHelper\r
+                               .createPartName("/foo/doc.RELS");\r
+\r
+               // Add content types\r
+               ctm.addContentType(name1, "foo-type1");\r
+               ctm.addContentType(name2, "foo-type2");\r
+               ctm.addContentType(name3, "text/xml+rel");\r
+               ctm.addContentType(name4, "text/xml+rel");\r
+               ctm.removeContentType(name2);\r
+               ctm.removeContentType(name3);\r
+\r
+               assertEquals(ctm.getContentType(name1), "foo-type1");\r
+               assertEquals(ctm.getContentType(name2), "foo-type1");\r
+               assertEquals(ctm.getContentType(name3), null);\r
+\r
+               ctm.removeContentType(name1);\r
+               assertEquals(ctm.getContentType(name1), null);\r
+               assertEquals(ctm.getContentType(name2), null);\r
+       }\r
+\r
+       /**\r
+        * Test the addition then removal of content types in a package.\r
+        */\r
+       public void testContentTypeRemovalPackage() throws Exception {\r
+               // TODO\r
+       }\r
+}\r
index 789d3206c9a618bc3899255814e7a3413ff86c9d..f1553e0e16aa725394bdb36faccbb3ab69b75a53 100644 (file)
@@ -24,7 +24,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.ss.usermodel.WorkbookFactory;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.Package;
 
 import junit.framework.TestCase;
 
index 682fb97574185c95dde13fc01675d4d150cd1dbb..13971f7efe094771281d1efbf0431a5c07c61dcd 100644 (file)
@@ -19,8 +19,8 @@ package org.apache.poi.xslf;
 import java.io.File;
 
 import org.apache.poi.POIXMLDocument;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdListEntry;
 
index 1fe0b120c16660d1f90d149676ad78b7b3375bb3..5214a22a2e76e32eee7a1f27a1880e28d0553c61 100644 (file)
@@ -23,8 +23,8 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
 import org.apache.poi.ss.usermodel.Workbook;\r
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;\r
-import org.openxml4j.exceptions.InvalidFormatException;\r
-import org.openxml4j.opc.Package;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.Package;\r
 \r
 /**\r
  * Centralises logic for finding/opening sample files in the src/testcases/org/apache/poi/hssf/hssf/data folder. \r
index f2eccca85749c7669a368d1a23c5042038a40d97..9ad4af7baaed9c1d66e5d351a6ccf303d5a66bdc 100644 (file)
@@ -25,7 +25,7 @@ import junit.framework.TestCase;
 
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.xssf.usermodel.XSSFRichTextString;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.Package;
 
 /**
  * Tests for XSSFReader
index a5cd76c56980a4025b76ae09bf7c9fe47bbf5047..062a93e7c07da6facab3398238ddc3d710d56893 100644 (file)
@@ -20,7 +20,6 @@ package org.apache.poi.xssf.model;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.util.List;
 
 import org.apache.poi.ss.usermodel.Cell;
@@ -33,9 +32,7 @@ import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.apache.poi.xssf.XSSFTestDataSamples;
 import org.apache.poi.POIXMLDocumentPart;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.Package;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
 
 import junit.framework.TestCase;
index 9464c05b8fbd7b60966f10423018add8f858070b..01160f23f5f3c0b351d9194cafe53ee5f4315160 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.poi.ss.usermodel.CellValue;
 import org.apache.poi.ss.usermodel.FormulaEvaluator;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.Package;
 
 /**
  * Performs much the same role as {@link TestFormulasFromSpreadsheet},
index bf5f330cf98675b340dbe06e6140087078caf090..b35d59c5b480745db8e57885adf6521ee9d6316c 100644 (file)
@@ -21,9 +21,9 @@ import java.io.File;
 
 import junit.framework.TestCase;
 
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.apache.poi.xssf.XSSFTestDataSamples;
 
 public class TestXSSFBugs extends TestCase {
index 34d49cf924df1b93ff94bd1d7952547ac62c292c..e4d6bc091fbd6c4146329c20eae7d3bb447fd508 100755 (executable)
@@ -19,10 +19,8 @@ package org.apache.poi.xssf.usermodel;
 import junit.framework.TestCase;\r
 import org.apache.poi.xssf.XSSFTestDataSamples;\r
 import org.apache.poi.POIXMLDocumentPart;\r
-import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;\r
 \r
 import java.util.List;\r
-import java.io.IOException;\r
 \r
 /**\r
  * @author Yegor Kozlov\r
@@ -73,7 +71,7 @@ public class TestXSSFDrawing extends TestCase {
             XSSFSheet sheet = wb.createSheet();\r
             XSSFDrawing drawing = sheet.createDrawingPatriarch();\r
         }\r
-        org.openxml4j.opc.Package pkg = wb.getPackage();\r
+        org.apache.poi.openxml4j.opc.Package pkg = wb.getPackage();\r
         assertEquals(3, pkg.getPartsByContentType(XSSFRelation.DRAWINGS.getContentType()).size());\r
     }\r
 }\r
index 0e5141db78fe9fd18ab6defa2626d5aa0b1b88e6..a256b192ee636b7d33ae77b72b32609be9950c3e 100644 (file)
@@ -26,7 +26,6 @@ import org.apache.poi.ss.usermodel.CreationHelper;
 import org.apache.poi.ss.usermodel.Hyperlink;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.xssf.XSSFTestDataSamples;
-import org.openxml4j.opc.Package;
 
 public class TestXSSFHyperlink extends TestCase {
     public TestXSSFHyperlink(String name) {
index 4b8ebaca7b02fc09e195a27f1cd69e71135e6ef0..09acaac09e46e7ef220d18eb3fe2490b2cf7ac22 100644 (file)
@@ -28,10 +28,10 @@ import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.xssf.XSSFTestDataSamples;
 import org.apache.poi.xssf.model.StylesTable;
-import org.openxml4j.opc.ContentTypes;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
-import org.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.ContentTypes;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet;
 import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
 
index 321048fd11545f5306476f7c3280c80e80b51993..24fbfcc8952bc62c32e60e07db0772d4e1187404 100644 (file)
@@ -21,8 +21,8 @@ import java.io.File;
 import org.apache.poi.POIXMLDocument;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.apache.poi.xwpf.usermodel.XWPFRelation;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePart;
 
 import junit.framework.TestCase;