aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.xml25
-rw-r--r--legal/NOTICE3
-rwxr-xr-xmaven/openxml4j.pom70
-rwxr-xr-xmaven/poi-ooxml.pom23
-rw-r--r--src/documentation/content/xdocs/book.xml3
-rwxr-xr-xsrc/documentation/content/xdocs/oxml4j/book.xml34
-rwxr-xr-xsrc/documentation/content/xdocs/oxml4j/index.xml42
-rw-r--r--src/documentation/content/xdocs/spreadsheet/how-to.xml2
-rw-r--r--src/examples/src/org/apache/poi/xssf/eventusermodel/examples/FromHowTo.java2
-rw-r--r--src/ooxml/java/org/apache/poi/POIXMLDocument.java8
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/POIXMLDocumentPart.java8
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/POIXMLFactory.java4
-rw-r--r--src/ooxml/java/org/apache/poi/POIXMLProperties.java8
-rw-r--r--src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java4
-rw-r--r--src/ooxml/java/org/apache/poi/POIXMLTextExtractor.java2
-rw-r--r--src/ooxml/java/org/apache/poi/dev/OOXMLLister.java10
-rw-r--r--src/ooxml/java/org/apache/poi/extractor/ExtractorFactory.java11
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java27
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidOperationException.java32
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JException.java34
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JRuntimeException.java34
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/CertificateEmbeddingOption.java33
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/CompressionOption.java47
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/Configuration.java43
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/ContentTypes.java129
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/EncryptionOption.java29
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/Package.java1396
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java33
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java52
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java654
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java81
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartName.java509
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackageProperties.java227
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationship.java227
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java450
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java77
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/PackagingURIHelper.java623
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/RelationshipSource.java166
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java77
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/TargetMode.java32
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java464
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java135
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentType.java224
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentTypeManager.java498
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/FileHelper.java91
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePart.java126
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePartOutputStream.java96
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java621
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartMarshaller.java49
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartUnmarshaller.java50
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java90
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java163
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/DefaultMarshaller.java45
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/PackagePropertiesMarshaller.java434
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java64
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java193
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalCertificatePart.java79
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalSignatureOriginPart.java28
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java390
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/UnmarshallContext.java96
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignature.java70
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignatureManager.java22
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/util/Nullable.java71
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java48
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java48
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java125
-rw-r--r--src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java2
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/util/PackageHelper.java10
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/XSLFSlideShow.java12
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java16
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExcelExtractor.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java5
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java2
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java2
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDialogsheet.java7
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java2
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java4
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java12
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShapeGroup.java2
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java8
-rw-r--r--src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java16
-rw-r--r--src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java4
-rw-r--r--src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java2
-rw-r--r--src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java8
-rwxr-xr-xsrc/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFactory.java4
-rw-r--r--src/ooxml/testcases/org/apache/poi/TestEmbeded.java4
-rw-r--r--src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java8
-rw-r--r--src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java4
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/TestCore.java102
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/AllTests.java38
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsxbin0 -> 11409 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docxbin0 -> 10079 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docxbin0 -> 24745 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docxbin0 -> 9859 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docxbin0 -> 9859 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docxbin0 -> 13925 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docxbin0 -> 14470 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsxbin0 -> 12050 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpgbin0 -> 8550 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docxbin0 -> 1014 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docxbin0 -> 10245 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docxbin0 -> 12068 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java117
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestFileHelper.java45
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestListParts.java104
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java440
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java125
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagePartName.java35
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageThumbnail.java67
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagingURIHelper.java117
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/TestRelationships.java273
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/AllTests.java36
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docxbin0 -> 9788 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docxbin0 -> 9801 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docxbin0 -> 9786 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docxbin0 -> 9787 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docxbin0 -> 10058 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docxbin0 -> 24250 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docxbin0 -> 10058 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docxbin0 -> 9814 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docxbin0 -> 24759 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java258
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PackageModel.java167
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PartName.java253
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/AllTests.java33
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docxbin0 -> 14279 bytes
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java122
-rw-r--r--src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java2
-rw-r--r--src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java4
-rw-r--r--src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java4
-rw-r--r--src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java2
-rw-r--r--src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java5
-rw-r--r--src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java2
-rw-r--r--src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java6
-rwxr-xr-xsrc/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java4
-rw-r--r--src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java1
-rw-r--r--src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java8
-rw-r--r--src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java4
144 files changed, 11877 insertions, 250 deletions
diff --git a/build.xml b/build.xml
index 458bf30a23..c04db3d0e0 100644
--- 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}" />
diff --git a/legal/NOTICE b/legal/NOTICE
index 5db2ba8589..4ff9390f6c 100644
--- a/legal/NOTICE
+++ b/legal/NOTICE
@@ -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
index 5379a73246..0000000000
--- a/maven/openxml4j.pom
+++ /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>
diff --git a/maven/poi-ooxml.pom b/maven/poi-ooxml.pom
index b3e1c04120..52c1583df1 100755
--- a/maven/poi-ooxml.pom
+++ b/maven/poi-ooxml.pom
@@ -60,26 +60,19 @@
<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>
diff --git a/src/documentation/content/xdocs/book.xml b/src/documentation/content/xdocs/book.xml
index d9bc0e91df..ec3206526c 100644
--- a/src/documentation/content/xdocs/book.xml
+++ b/src/documentation/content/xdocs/book.xml
@@ -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
index 0000000000..bf5af2f95a
--- /dev/null
+++ b/src/documentation/content/xdocs/oxml4j/book.xml
@@ -0,0 +1,34 @@
+<?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.
+ ====================================================================
+-->
+<!DOCTYPE book PUBLIC "-//APACHE//DTD Cocoon Documentation Book V1.0//EN" "../dtd/book-cocoon-v10.dtd">
+
+<book software="POI Project"
+ title="OpenXML4J"
+ copyright="@year@ POI Project">
+
+ <menu label="Apache POI">
+ <menu-item label="Top" href="../index.html"/>
+ </menu>
+
+ <menu label="OpenXML4J">
+ <menu-item label="Overview" href="index.html"/>
+ </menu>
+
+</book>
diff --git a/src/documentation/content/xdocs/oxml4j/index.xml b/src/documentation/content/xdocs/oxml4j/index.xml
new file mode 100755
index 0000000000..25ea1b06e5
--- /dev/null
+++ b/src/documentation/content/xdocs/oxml4j/index.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ====================================================================
+-->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "../dtd/document-v11.dtd">
+
+<document>
+ <header>
+ <title>POI-OpenXML4J - Java API To Access Office Open XML documents</title>
+ <subtitle>Overview</subtitle>
+ </header>
+
+ <body>
+ <section>
+ <title>Overview</title>
+ <p>OpenXML4J is the POI Project's pure Java implementation of the Open Packaging Conventions (OPC) defined in
+ <link href="http://www.ecma-international.org/publications/standards/Ecma-376.htm">ECMA-376</link>.</p>
+ <p>Every OpenXML file comprises a collection of byte streams called parts, combined into a container called a package.
+ POI OpenXML4J provides a physical implementation of the OPC that uses the Zip file format.</p>
+ </section>
+ <section>
+ <title>History</title>
+ <p>OpenXML4J was originally developed by <link href="http://openxml4j.org/">http://openxml4j.org/</link> and contributed to POI in 2008.
+ Thanks to the support and guidance of Julien Chable</p>
+ </section>
+ </body>
+</document>
diff --git a/src/documentation/content/xdocs/spreadsheet/how-to.xml b/src/documentation/content/xdocs/spreadsheet/how-to.xml
index d21be707e2..fcf88f69fe 100644
--- a/src/documentation/content/xdocs/spreadsheet/how-to.xml
+++ b/src/documentation/content/xdocs/spreadsheet/how-to.xml
@@ -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;
diff --git a/src/examples/src/org/apache/poi/xssf/eventusermodel/examples/FromHowTo.java b/src/examples/src/org/apache/poi/xssf/eventusermodel/examples/FromHowTo.java
index e4fe3fe169..88668093df 100644
--- a/src/examples/src/org/apache/poi/xssf/eventusermodel/examples/FromHowTo.java
+++ b/src/examples/src/org/apache/poi/xssf/eventusermodel/examples/FromHowTo.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocument.java b/src/ooxml/java/org/apache/poi/POIXMLDocument.java
index ab2d04c72e..ef333d86df 100644
--- a/src/ooxml/java/org/apache/poi/POIXMLDocument.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLDocument.java
@@ -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{
diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
index d529bedf04..f01cae8ce4 100755
--- a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
@@ -23,15 +23,15 @@ import java.util.List;
import org.apache.xmlbeans.XmlOptions;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory;
-import org.openxml4j.exceptions.OpenXML4JException;
-import org.openxml4j.opc.*;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.*;
+import org.apache.poi.openxml4j.opc.Package;
/**
* Represents an entry of a OOXML package.
*
* <p>
- * Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.openxml4j.opc.PackagePart}.
+ * Each POIXMLDocumentPart keeps a reference to the underlying a {@link org.apache.poi.openxml4j.opc.PackagePart}.
* </p>
*
* @author Yegor Kozlov
diff --git a/src/ooxml/java/org/apache/poi/POIXMLFactory.java b/src/ooxml/java/org/apache/poi/POIXMLFactory.java
index 6341b20842..f10cf0ca01 100755
--- a/src/ooxml/java/org/apache/poi/POIXMLFactory.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLFactory.java
@@ -16,8 +16,8 @@
==================================================================== */
package org.apache.poi;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
/**
diff --git a/src/ooxml/java/org/apache/poi/POIXMLProperties.java b/src/ooxml/java/org/apache/poi/POIXMLProperties.java
index 894f2f800d..3d862be888 100644
--- a/src/ooxml/java/org/apache/poi/POIXMLProperties.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLProperties.java
@@ -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
diff --git a/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java b/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java
index 320c6e143d..97c9e2d695 100644
--- a/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLPropertiesTextExtractor.java
@@ -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;
/**
diff --git a/src/ooxml/java/org/apache/poi/POIXMLTextExtractor.java b/src/ooxml/java/org/apache/poi/POIXMLTextExtractor.java
index c6a99436b5..25310fff3a 100644
--- a/src/ooxml/java/org/apache/poi/POIXMLTextExtractor.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLTextExtractor.java
@@ -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 */
diff --git a/src/ooxml/java/org/apache/poi/dev/OOXMLLister.java b/src/ooxml/java/org/apache/poi/dev/OOXMLLister.java
index 7084b38f33..a55bc2632b 100644
--- a/src/ooxml/java/org/apache/poi/dev/OOXMLLister.java
+++ b/src/ooxml/java/org/apache/poi/dev/OOXMLLister.java
@@ -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.
diff --git a/src/ooxml/java/org/apache/poi/extractor/ExtractorFactory.java b/src/ooxml/java/org/apache/poi/extractor/ExtractorFactory.java
index 406a3145e9..7640db845f 100644
--- a/src/ooxml/java/org/apache/poi/extractor/ExtractorFactory.java
+++ b/src/ooxml/java/org/apache/poi/extractor/ExtractorFactory.java
@@ -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
index 0000000000..689f8f33db
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java
@@ -0,0 +1,27 @@
+/* ====================================================================
+ 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.exceptions;
+
+@SuppressWarnings("serial")
+public class InvalidFormatException extends OpenXML4JException{
+
+ public InvalidFormatException(String message){
+ super(message);
+ }
+}
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
index 0000000000..9e45ccf4b3
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidOperationException.java
@@ -0,0 +1,32 @@
+/* ====================================================================
+ 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.exceptions;
+
+/**
+ * Throw when an invalid operation is done.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+@SuppressWarnings("serial")
+public class InvalidOperationException extends OpenXML4JRuntimeException{
+
+ public InvalidOperationException(String message){
+ super(message);
+ }
+}
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
index 0000000000..d2fa0e1446
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JException.java
@@ -0,0 +1,34 @@
+/* ====================================================================
+ 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.exceptions;
+
+/**
+ * Global exception throws when a critical error occurs. (this exception is not
+ * set as Runtime in order to force user to manage the exception in a
+ * try/catch).
+ *
+ * @author CDubettier, Julien Chable
+ * @version 1.0
+ */
+@SuppressWarnings("serial")
+public class OpenXML4JException extends Exception {
+
+ public OpenXML4JException(String msg) {
+ super(msg);
+ }
+}
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
index 0000000000..c68d85ecbb
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JRuntimeException.java
@@ -0,0 +1,34 @@
+/* ====================================================================
+ 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.exceptions;
+
+/**
+ * Global exception throws when a critical error occurs (this exception is
+ * set as Runtime in order not to force the user to manage the exception in a
+ * try/catch).
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+@SuppressWarnings("serial")
+public class OpenXML4JRuntimeException extends RuntimeException {
+
+ public OpenXML4JRuntimeException(String msg) {
+ super(msg);
+ }
+}
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
index 0000000000..8b946e6d08
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/CertificateEmbeddingOption.java
@@ -0,0 +1,33 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Specifies the location where the X.509 certificate that is used in signing is stored.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public enum CertificateEmbeddingOption {
+ /** The certificate is embedded in its own PackagePart. */
+ IN_CERTIFICATE_PART,
+ /** The certificate is embedded in the SignaturePart that is created for the signature being added. */
+ IN_SIGNATURE_PART,
+ /** The certificate in not embedded in the package. */
+ NOT_EMBEDDED
+}
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
index 0000000000..b6c5b30f7d
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/CompressionOption.java
@@ -0,0 +1,47 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.util.zip.Deflater;
+
+/**
+ * Specifies the compression level for content that is stored in a PackagePart.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public enum CompressionOption {
+ /** Compression is optimized for performance. */
+ FAST(Deflater.BEST_SPEED),
+ /** Compression is optimized for size. */
+ MAXIMUM(Deflater.BEST_COMPRESSION),
+ /** Compression is optimized for a balance between size and performance. */
+ NORMAL(Deflater.DEFAULT_COMPRESSION),
+ /** Compression is turned off. */
+ NOT_COMPRESSED(Deflater.NO_COMPRESSION);
+
+ private final int value;
+
+ CompressionOption(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return this.value;
+ }
+}
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
index 0000000000..72241b38d9
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/Configuration.java
@@ -0,0 +1,43 @@
+/* ====================================================================
+ 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.File;
+
+/**
+ * Storage class for configuration storage parameters.
+ * TODO xml syntax checking is no longer done with DOM4j parser -> remove the schema or do it ?
+ *
+ * @author CDubettier, Julen Chable
+ * @version 1.0
+ */
+public class Configuration {
+ // TODO configuration by default. should be clearly stated that it should be
+ // changed to match installation path
+ // as schemas dir is needed in runtime
+ static private String pathForXmlSchema = System.getProperty("user.dir")
+ + File.separator + "src" + File.separator + "schemas";
+
+ public static String getPathForXmlSchema() {
+ return pathForXmlSchema;
+ }
+
+ public static void setPathForXmlSchema(String pathForXmlSchema) {
+ Configuration.pathForXmlSchema = pathForXmlSchema;
+ }
+}
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
index 0000000000..b05d7903e5
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/ContentTypes.java
@@ -0,0 +1,129 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Open Packaging Convention content types (see Annex F : Standard Namespaces
+ * and Content Types).
+ *
+ * @author CDubettier define some constants, Julien Chable
+ * @version 0.1
+ */
+public class ContentTypes {
+
+ /*
+ * Open Packaging Convention (Annex F : Standard Namespaces and Content
+ * Types)
+ */
+
+ /**
+ * Core Properties part.
+ */
+ public static final String CORE_PROPERTIES_PART = "application/vnd.openxmlformats-package.core-properties+xml";
+
+ /**
+ * Digital Signature Certificate part.
+ */
+ public static final String DIGITAL_SIGNATURE_CERTIFICATE_PART = "application/vnd.openxmlformats-package.digital-signature-certificate";
+
+ /**
+ * Digital Signature Origin part.
+ */
+ public static final String DIGITAL_SIGNATURE_ORIGIN_PART = "application/vnd.openxmlformats-package.digital-signature-origin";
+
+ /**
+ * Digital Signature XML Signature part.
+ */
+ public static final String DIGITAL_SIGNATURE_XML_SIGNATURE_PART = "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml";
+
+ /**
+ * Relationships part.
+ */
+ public static final String RELATIONSHIPS_PART = "application/vnd.openxmlformats-package.relationships+xml";
+
+ /**
+ * Custom XML part.
+ */
+ public static final String CUSTOM_XML_PART = "application/vnd.openxmlformats-officedocument.customXmlProperties+xml";
+
+ /**
+ * Plain old xml. Note - OOXML uses application/xml, and not text/xml!
+ */
+ public static final String PLAIN_OLD_XML = "application/xml";
+
+ public static final String IMAGE_JPEG = "image/jpeg";
+
+ public static final String EXTENSION_JPG_1 = "jpg";
+
+ public static final String EXTENSION_JPG_2 = "jpeg";
+
+ // image/png ISO/IEC 15948:2003 http://www.libpng.org/pub/png/spec/
+ public static final String IMAGE_PNG = "image/png";
+
+ public static final String EXTENSION_PNG = "png";
+
+ // image/gif http://www.w3.org/Graphics/GIF/spec-gif89a.txt
+ public static final String IMAGE_GIF = "image/gif";
+
+ public static final String EXTENSION_GIF = "gif";
+
+ /**
+ * TIFF image format.
+ *
+ * @see http://partners.adobe.com/public/developer/tiff/index.html#spec
+ */
+ public static final String IMAGE_TIFF = "image/tiff";
+
+ public static final String EXTENSION_TIFF = "tiff";
+
+ /**
+ * Pict image format.
+ *
+ * @see http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-2.html
+ */
+ public static final String IMAGE_PICT = "image/pict";
+
+ public static final String EXTENSION_PICT = "tiff";
+
+ /**
+ * XML file.
+ */
+ public static final String XML = "text/xml";
+
+ public static final String EXTENSION_XML = "xml";
+
+ public static String getContentTypeFromFileExtension(String filename) {
+ String extension = filename.substring(filename.lastIndexOf(".") + 1)
+ .toLowerCase();
+ if (extension.equals(EXTENSION_JPG_1)
+ || extension.equals(EXTENSION_JPG_2))
+ return IMAGE_JPEG;
+ else if (extension.equals(EXTENSION_GIF))
+ return IMAGE_GIF;
+ else if (extension.equals(EXTENSION_PICT))
+ return IMAGE_PICT;
+ else if (extension.equals(EXTENSION_PNG))
+ return IMAGE_PNG;
+ else if (extension.equals(EXTENSION_TIFF))
+ return IMAGE_TIFF;
+ else if (extension.equals(EXTENSION_XML))
+ return XML;
+ else
+ return null;
+ }
+}
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
index 0000000000..0e15332f32
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/EncryptionOption.java
@@ -0,0 +1,29 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Specifies the encryption option for parts in a Package.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public enum EncryptionOption {
+ /** No encryption. */
+ NONE
+}
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
index 0000000000..663ab266f9
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java
@@ -0,0 +1,1396 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.log4j.Logger;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
+import org.apache.poi.openxml4j.opc.internal.PartUnmarshaller;
+import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.marshallers.DefaultMarshaller;
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller;
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.PackagePropertiesUnmarshaller;
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.UnmarshallContext;
+import org.apache.poi.openxml4j.util.Nullable;
+
+/**
+ * Represents a container that can store multiple data objects.
+ *
+ * @author Julien Chable, CDubet
+ * @version 0.1
+ */
+public abstract class Package implements RelationshipSource {
+
+ /**
+ * Logger.
+ */
+ protected static Logger logger = Logger.getLogger("org.openxml4j.opc");
+
+ /**
+ * Default package access.
+ */
+ protected static final PackageAccess defaultPackageAccess = PackageAccess.READ_WRITE;
+
+ /**
+ * Package access.
+ */
+ private PackageAccess packageAccess;
+
+ /**
+ * Package parts collection.
+ */
+ protected PackagePartCollection partList;
+
+ /**
+ * Package relationships.
+ */
+ protected PackageRelationshipCollection relationships;
+
+ /**
+ * Part marshallers by content type.
+ */
+ protected Hashtable<ContentType, PartMarshaller> partMarshallers;
+
+ /**
+ * Default part marshaller.
+ */
+ protected PartMarshaller defaultPartMarshaller;
+
+ /**
+ * Part unmarshallers by content type.
+ */
+ protected Hashtable<ContentType, PartUnmarshaller> partUnmarshallers;
+
+ /**
+ * Core package properties.
+ */
+ protected PackagePropertiesPart packageProperties;
+
+ /**
+ * Manage parts content types of this package.
+ */
+ protected ContentTypeManager contentTypeManager;
+
+ /**
+ * Flag if a modification is done to the document.
+ */
+ protected boolean isDirty = false;
+
+ /**
+ * File path of this package.
+ */
+ protected String originalPackagePath;
+
+ /**
+ * Output stream for writing this package.
+ */
+ protected OutputStream output;
+
+ /**
+ * Constructor.
+ *
+ * @param access
+ * Package access.
+ */
+ protected Package(PackageAccess access) {
+ init();
+ this.packageAccess = access;
+ }
+
+ /**
+ * Initialize the package instance.
+ */
+ private void init() {
+ this.partMarshallers = new Hashtable<ContentType, PartMarshaller>(5);
+ this.partUnmarshallers = new Hashtable<ContentType, PartUnmarshaller>(2);
+
+ try {
+ // Add 'default' unmarshaller
+ this.partUnmarshallers.put(new ContentType(
+ ContentTypes.CORE_PROPERTIES_PART),
+ new PackagePropertiesUnmarshaller());
+
+ // Add default marshaller
+ this.defaultPartMarshaller = new DefaultMarshaller();
+ // TODO Delocalize specialized marshallers
+ this.partMarshallers.put(new ContentType(
+ ContentTypes.CORE_PROPERTIES_PART),
+ new ZipPackagePropertiesMarshaller());
+ } catch (InvalidFormatException e) {
+ // Should never happpen
+ throw new OpenXML4JRuntimeException(
+ "Package.init() : this exception should never happen, if you read this message please send a mail to the developers team.");
+ }
+ }
+
+ /**
+ * Open a package with read/write permission.
+ *
+ * @param path
+ * The document path.
+ * @return A Package object, else <b>null</b>.
+ * @throws InvalidFormatException
+ * If the specified file doesn't exist, and a parsing error
+ * occur.
+ */
+ public static Package open(String path) throws InvalidFormatException {
+ return open(path, defaultPackageAccess);
+ }
+
+ /**
+ * Open a package.
+ *
+ * @param path
+ * The document path.
+ * @param access
+ * Package access.
+ * @return A Package object, else <b>null</b>.
+ * @throws InvalidFormatException
+ * If the specified file doesn't exist, and a parsing error
+ * occur.
+ */
+ public static Package open(String path, PackageAccess access)
+ throws InvalidFormatException {
+ if (path == null || "".equals(path.trim())
+ || (new File(path).exists() && new File(path).isDirectory()))
+ throw new IllegalArgumentException("path");
+
+ Package pack = new ZipPackage(path, access);
+ if (pack.partList == null && access != PackageAccess.WRITE) {
+ pack.getParts();
+ }
+ pack.originalPackagePath = new File(path).getAbsolutePath();
+ return pack;
+ }
+
+ /**
+ * Open a package.
+ *
+ * Note - uses quite a bit more memory than {@link #open(String)}, which
+ * doesn't need to hold the whole zip file in memory, and can take advantage
+ * of native methods
+ *
+ * @param in
+ * The InputStream to read the package from
+ * @return A Package object
+ */
+ public static Package open(InputStream in) throws InvalidFormatException,
+ IOException {
+ Package pack = new ZipPackage(in, PackageAccess.READ);
+ if (pack.partList == null) {
+ pack.getParts();
+ }
+ return pack;
+ }
+
+ /**
+ * Opens a package if it exists, else it creates one.
+ *
+ * @param file
+ * The file to open or to create.
+ * @return A newly created package if the specified file does not exist,
+ * else the package extract from the file.
+ * @throws InvalidFormatException
+ * Throws if the specified file exist and is not valid.
+ */
+ public static Package openOrCreate(File file) throws InvalidFormatException {
+ Package retPackage = null;
+ if (file.exists()) {
+ retPackage = open(file.getAbsolutePath());
+ } else {
+ retPackage = create(file);
+ }
+ return retPackage;
+ }
+
+ /**
+ * Creates a new package.
+ *
+ * @param path
+ * Path of the document.
+ * @return A newly created Package ready to use.
+ */
+ public static Package create(String path) {
+ return create(new File(path));
+ }
+
+ /**
+ * Creates a new package.
+ *
+ * @param file
+ * Path of the document.
+ * @return A newly created Package ready to use.
+ */
+ public static Package create(File file) {
+ if (file == null || (file.exists() && file.isDirectory()))
+ throw new IllegalArgumentException("file");
+
+ if (file.exists()) {
+ throw new InvalidOperationException(
+ "This package (or file) already exists : use the open() method or delete the file.");
+ }
+
+ // Creates a new package
+ Package pkg = null;
+ pkg = new ZipPackage();
+ pkg.originalPackagePath = file.getAbsolutePath();
+
+ configurePackage(pkg);
+ return pkg;
+ }
+
+ public static Package create(OutputStream output) {
+ Package pkg = null;
+ pkg = new ZipPackage();
+ pkg.originalPackagePath = null;
+ pkg.output = output;
+
+ configurePackage(pkg);
+ return pkg;
+ }
+
+ /**
+ * Configure the package.
+ *
+ * @param pkg
+ */
+ private static void configurePackage(Package pkg) {
+ try {
+ // Content type manager
+ pkg.contentTypeManager = new ZipContentTypeManager(null, pkg);
+ // Add default content types for .xml and .rels
+ pkg.contentTypeManager
+ .addContentType(
+ PackagingURIHelper
+ .createPartName(PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_URI),
+ ContentTypes.RELATIONSHIPS_PART);
+ pkg.contentTypeManager
+ .addContentType(PackagingURIHelper
+ .createPartName("/default.xml"),
+ ContentTypes.PLAIN_OLD_XML);
+
+ // Init some Package properties
+ pkg.packageProperties = new PackagePropertiesPart(pkg,
+ PackagingURIHelper.CORE_PROPERTIES_PART_NAME);
+ pkg.packageProperties.setCreatorProperty("Generated by OpenXML4J");
+ pkg.packageProperties.setCreatedProperty(new Nullable<Date>(
+ new Date()));
+ } catch (InvalidFormatException e) {
+ // Should never happen
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Flush the package : save all.
+ *
+ * @see #close()
+ */
+ public void flush() {
+ throwExceptionIfReadOnly();
+
+ if (this.packageProperties != null)
+ ((PackagePropertiesPart) this.packageProperties).flush();
+
+ this.flushImpl();
+ }
+
+ /**
+ * Close the package and save its content.
+ *
+ * @throws IOException
+ * If an IO exception occur during the saving process.
+ */
+ public void close() throws IOException {
+ if (this.packageAccess == PackageAccess.READ) {
+ logger
+ .warn("The close() method is intended to SAVE a package. This package is open in READ ONLY mode, use the revert() method instead !");
+ return;
+ }
+
+ // Save the content
+ ReentrantReadWriteLock l = new ReentrantReadWriteLock();
+ try {
+ l.writeLock().lock();
+ if (this.originalPackagePath != null
+ && !"".equals(this.originalPackagePath.trim())) {
+ File targetFile = new File(this.originalPackagePath);
+ if (!targetFile.exists()
+ || !(this.originalPackagePath
+ .equalsIgnoreCase(targetFile.getAbsolutePath()))) {
+ // Case of a package created from scratch
+ save(targetFile);
+ } else {
+ closeImpl();
+ }
+ } else if (this.output != null) {
+ save(this.output);
+ }
+ } finally {
+ l.writeLock().unlock();
+ }
+
+ // Clear
+ this.contentTypeManager.clearAll();
+
+ // Call the garbage collector
+ Runtime.getRuntime().gc();
+ }
+
+ /**
+ * Close the package WITHOUT saving its content. Reinitialize this package
+ * and cancel all changes done to it.
+ */
+ public void revert() {
+ revertImpl();
+ }
+
+ /**
+ * Add a thumbnail to the package. This method is provided to make easier
+ * the addition of a thumbnail in a package. You can do the same work by
+ * using the traditionnal relationship and part mechanism.
+ *
+ * @param path
+ * The full path to the image file.
+ */
+ public void addThumbnail(String path) throws IOException {
+ // Check parameter
+ if ("".equals(path))
+ throw new IllegalArgumentException("path");
+
+ // Get the filename from the path
+ String filename = path
+ .substring(path.lastIndexOf(File.separatorChar) + 1);
+
+ // Create the thumbnail part name
+ String contentType = ContentTypes
+ .getContentTypeFromFileExtension(filename);
+ PackagePartName thumbnailPartName = null;
+ try {
+ thumbnailPartName = PackagingURIHelper.createPartName("/docProps/"
+ + filename);
+ } catch (InvalidFormatException e) {
+ try {
+ thumbnailPartName = PackagingURIHelper
+ .createPartName("/docProps/thumbnail"
+ + path.substring(path.lastIndexOf(".") + 1));
+ } catch (InvalidFormatException e2) {
+ throw new InvalidOperationException(
+ "Can't add a thumbnail file named '" + filename + "'");
+ }
+ }
+
+ // Check if part already exist
+ if (this.getPart(thumbnailPartName) != null)
+ throw new InvalidOperationException(
+ "You already add a thumbnail named '" + filename + "'");
+
+ // Add the thumbnail part to this package.
+ PackagePart thumbnailPart = this.createPart(thumbnailPartName,
+ contentType, false);
+
+ // Add the relationship between the package and the thumbnail part
+ this.addRelationship(thumbnailPartName, TargetMode.INTERNAL,
+ PackageRelationshipTypes.THUMBNAIL);
+
+ // Copy file data to the newly created part
+ StreamHelper.copyStream(new FileInputStream(path), thumbnailPart
+ .getOutputStream());
+ }
+
+ /**
+ * Throws an exception if the package access mode is in read only mode
+ * (PackageAccess.Read).
+ *
+ * @throws InvalidOperationException
+ * Throws if a writing operation is done on a read only package.
+ * @see org.apache.poi.openxml4j.opc.PackageAccess
+ */
+ void throwExceptionIfReadOnly() throws InvalidOperationException {
+ if (packageAccess == PackageAccess.READ)
+ throw new InvalidOperationException(
+ "Operation not allowed, document open in read only mode!");
+ }
+
+ /**
+ * Throws an exception if the package access mode is in write only mode
+ * (PackageAccess.Write). This method is call when other methods need write
+ * right.
+ *
+ * @throws InvalidOperationException
+ * Throws if a read operation is done on a write only package.
+ * @see org.apache.poi.openxml4j.opc.PackageAccess
+ */
+ void throwExceptionIfWriteOnly() throws InvalidOperationException {
+ if (packageAccess == PackageAccess.WRITE)
+ throw new InvalidOperationException(
+ "Operation not allowed, document open in write only mode!");
+ }
+
+ /**
+ * Retrieves or creates if none exists, core package property part.
+ *
+ * @return The PackageProperties part of this package.
+ */
+ public PackageProperties getPackageProperties()
+ throws InvalidFormatException {
+ this.throwExceptionIfWriteOnly();
+ // If no properties part has been found then we create one
+ if (this.packageProperties == null) {
+ this.packageProperties = new PackagePropertiesPart(this,
+ PackagingURIHelper.CORE_PROPERTIES_PART_NAME);
+ }
+ return this.packageProperties;
+ }
+
+ /**
+ * Retrieve a part identified by its name.
+ *
+ * @param partName
+ * Part name of the part to retrieve.
+ * @return The part with the specified name, else <code>null</code>.
+ */
+ public PackagePart getPart(PackagePartName partName) {
+ throwExceptionIfWriteOnly();
+
+ if (partName == null)
+ throw new IllegalArgumentException("partName");
+
+ // If the partlist is null, then we parse the package.
+ if (partList == null) {
+ try {
+ getParts();
+ } catch (InvalidFormatException e) {
+ return null;
+ }
+ }
+ return getPartImpl(partName);
+ }
+
+ /**
+ * Retrieve parts by content type.
+ *
+ * @param contentType
+ * The content type criteria.
+ * @return All part associated to the specified content type.
+ */
+ public ArrayList<PackagePart> getPartsByContentType(String contentType) {
+ ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();
+ for (PackagePart part : partList.values()) {
+ if (part.getContentType().equals(contentType))
+ retArr.add(part);
+ }
+ return retArr;
+ }
+
+ /**
+ * Retrieve parts by relationship type.
+ *
+ * @param relationshipType
+ * Relationship type.
+ * @return All parts which are the target of a relationship with the
+ * specified type, if the method can't retrieve relationships from
+ * the package, then return <code>null</code>.
+ */
+ public ArrayList<PackagePart> getPartsByRelationshipType(
+ String relationshipType) {
+ if (relationshipType == null)
+ throw new IllegalArgumentException("relationshipType");
+ ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();
+ try {
+ for (PackageRelationship rel : getRelationshipsByType(relationshipType)) {
+ retArr.add(getPart(rel));
+ }
+ } catch (OpenXML4JException e) {
+ logger
+ .warn("Can't retrieve parts by relationship type: an exception has been thrown by getRelationshipsByType method");
+ return null;
+ }
+ return retArr;
+ }
+
+ /**
+ * Get the target part from the specified relationship.
+ *
+ * @param partRel
+ * The part relationship uses to retrieve the part.
+ */
+ public PackagePart getPart(PackageRelationship partRel) {
+ PackagePart retPart = null;
+ ensureRelationships();
+ for (PackageRelationship rel : relationships) {
+ if (rel.getRelationshipType().equals(partRel.getRelationshipType())) {
+ try {
+ retPart = getPart(PackagingURIHelper.createPartName(rel
+ .getTargetURI()));
+ } catch (InvalidFormatException e) {
+ continue;
+ }
+ break;
+ }
+ }
+ return retPart;
+ }
+
+ /**
+ * Load the parts of the archive if it has not been done yet The
+ * relationships of each part are not loaded
+ *
+ * @return All this package's parts.
+ */
+ public ArrayList<PackagePart> getParts() throws InvalidFormatException {
+ throwExceptionIfWriteOnly();
+
+ // If the part list is null, we parse the package to retrieve all parts.
+ if (partList == null) {
+ /* Variables use to validate OPC Compliance */
+
+ // Ensure rule M4.1 -> A format consumer shall consider more than
+ // one core properties relationship for a package to be an error
+ boolean hasCorePropertiesPart = false;
+
+ PackagePart[] parts = this.getPartsImpl();
+ this.partList = new PackagePartCollection();
+ for (PackagePart part : parts) {
+ if (partList.containsKey(part.partName))
+ throw new InvalidFormatException(
+ "A part with the name '"
+ + part.partName
+ + "' already exist : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+
+ // Check OPC compliance rule M4.1
+ if (part.getContentType().equals(
+ ContentTypes.CORE_PROPERTIES_PART)) {
+ if (!hasCorePropertiesPart)
+ hasCorePropertiesPart = true;
+ else
+ throw new InvalidFormatException(
+ "OPC Compliance error [M4.1]: there is more than one core properties relationship in the package !");
+ }
+
+ PartUnmarshaller partUnmarshaller = partUnmarshallers
+ .get(part.contentType);
+
+ if (partUnmarshaller != null) {
+ UnmarshallContext context = new UnmarshallContext(this,
+ part.partName);
+ try {
+ PackagePart unmarshallPart = partUnmarshaller
+ .unmarshall(context, part.getInputStream());
+ partList.put(unmarshallPart.partName, unmarshallPart);
+
+ // Core properties case
+ if (unmarshallPart instanceof PackagePropertiesPart)
+ this.packageProperties = (PackagePropertiesPart) unmarshallPart;
+ } catch (IOException ioe) {
+ logger.warn("Unmarshall operation : IOException for "
+ + part.partName);
+ continue;
+ } catch (InvalidOperationException invoe) {
+ throw new InvalidFormatException(invoe.getMessage());
+ }
+ } else {
+ try {
+ partList.put(part.partName, part);
+ } catch (InvalidOperationException e) {
+ throw new InvalidFormatException(e.getMessage());
+ }
+ }
+ }
+ }
+ return new ArrayList<PackagePart>(partList.values());
+ }
+
+ /**
+ * Create and add a part, with the specified name and content type, to the
+ * package.
+ *
+ * @param partName
+ * Part name.
+ * @param contentType
+ * Part content type.
+ * @return The newly created part.
+ * @throws InvalidFormatException
+ * If rule M1.12 is not verified : Packages shall not contain
+ * equivalent part names and package implementers shall neither
+ * create nor recognize packages with equivalent part names.
+ * @see {@link#createPartImpl(URI, String)}
+ */
+ public PackagePart createPart(PackagePartName partName, String contentType) {
+ return this.createPart(partName, contentType, true);
+ }
+
+ /**
+ * Create and add a part, with the specified name and content type, to the
+ * package. For general purpose, prefer the overload version of this method
+ * without the 'loadRelationships' parameter.
+ *
+ * @param partName
+ * Part name.
+ * @param contentType
+ * Part content type.
+ * @param loadRelationships
+ * Specify if the existing relationship part, if any, logically
+ * associated to the newly created part will be loaded.
+ * @return The newly created part.
+ * @throws InvalidFormatException
+ * If rule M1.12 is not verified : Packages shall not contain
+ * equivalent part names and package implementers shall neither
+ * create nor recognize packages with equivalent part names.
+ * @see {@link#createPartImpl(URI, String)}
+ */
+ PackagePart createPart(PackagePartName partName, String contentType,
+ boolean loadRelationships) {
+ throwExceptionIfReadOnly();
+ if (partName == null) {
+ throw new IllegalArgumentException("partName");
+ }
+
+ if (contentType == null || contentType == "") {
+ throw new IllegalArgumentException("contentType");
+ }
+
+ // Check if the specified part name already exists
+ if (partList.containsKey(partName)
+ && !partList.get(partName).isDeleted()) {
+ throw new InvalidOperationException(
+ "A part with the name '"
+ + partName.getName()
+ + "' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+ }
+
+ /* Check OPC compliance */
+
+ // Rule [M4.1]: The format designer shall specify and the format
+ // producer
+ // shall create at most one core properties relationship for a package.
+ // A format consumer shall consider more than one core properties
+ // relationship for a package to be an error. If present, the
+ // relationship shall target the Core Properties part.
+ if (contentType == ContentTypes.CORE_PROPERTIES_PART) {
+ if (this.packageProperties != null)
+ throw new InvalidOperationException(
+ "OPC Compliance error [M4.1]: you try to add more than one core properties relationship in the package !");
+ }
+
+ /* End check OPC compliance */
+
+ PackagePart part = this.createPartImpl(partName, contentType,
+ loadRelationships);
+ this.contentTypeManager.addContentType(partName, contentType);
+ this.partList.put(partName, part);
+ this.isDirty = true;
+ return part;
+ }
+
+ /**
+ * Add a part to the package.
+ *
+ * @param partName
+ * Part name of the part to create.
+ * @param contentType
+ * type associated with the file
+ * @param content
+ * the contents to add. In order to have faster operation in
+ * document merge, the data are stored in memory not on a hard
+ * disk
+ *
+ * @return The new part.
+ * @see {@link #createPart(PackagePartName, String)}
+ */
+ public PackagePart createPart(PackagePartName partName, String contentType,
+ ByteArrayOutputStream content) {
+ PackagePart addedPart = this.createPart(partName, contentType);
+ if (addedPart == null) {
+ return null;
+ }
+ // Extract the zip entry content to put it in the part content
+ if (content != null) {
+ try {
+ OutputStream partOutput = addedPart.getOutputStream();
+ if (partOutput == null) {
+ return null;
+ }
+
+ partOutput.write(content.toByteArray(), 0, content.size());
+ partOutput.close();
+
+ } catch (IOException ioe) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ return addedPart;
+ }
+
+ /**
+ * Add the specified part to the package. If a part already exists in the
+ * package with the same name as the one specified, then we replace the old
+ * part by the specified part.
+ *
+ * @param part
+ * The part to add (or replace).
+ * @return The part added to the package, the same as the one specified.
+ * @throws InvalidFormatException
+ * If rule M1.12 is not verified : Packages shall not contain
+ * equivalent part names and package implementers shall neither
+ * create nor recognize packages with equivalent part names.
+ */
+ protected PackagePart addPackagePart(PackagePart part) {
+ throwExceptionIfReadOnly();
+ if (part == null) {
+ throw new IllegalArgumentException("part");
+ }
+
+ if (partList.containsKey(part.partName)) {
+ if (!partList.get(part.partName).isDeleted()) {
+ throw new InvalidOperationException(
+ "A part with the name '"
+ + part.partName.getName()
+ + "' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+ } else {
+ // If the specified partis flagged as deleted, we make it
+ // available
+ part.setDeleted(false);
+ // and delete the old part to replace it thereafeter
+ this.partList.remove(part.partName);
+ }
+ }
+ this.partList.put(part.partName, part);
+ this.isDirty = true;
+ return part;
+ }
+
+ /**
+ * Remove the specified part in this package. If this part is relationship
+ * part, then delete all relationships in the source part.
+ *
+ * @param part
+ * The part to remove. If <code>null</code>, skip the action.
+ * @see #removePart(PackagePartName)
+ */
+ public void removePart(PackagePart part) {
+ if (part != null) {
+ removePart(part.getPartName());
+ }
+ }
+
+ /**
+ * Remove a part in this package. If this part is relationship part, then
+ * delete all relationships in the source part.
+ *
+ * @param partName
+ * The part name of the part to remove.
+ */
+ public void removePart(PackagePartName partName) {
+ throwExceptionIfReadOnly();
+ if (partName == null || !this.containPart(partName))
+ throw new IllegalArgumentException("partName");
+
+ // Delete the specified part from the package.
+ if (this.partList.containsKey(partName)) {
+ this.partList.get(partName).setDeleted(true);
+ this.removePartImpl(partName);
+ this.partList.remove(partName);
+ } else {
+ this.removePartImpl(partName);
+ }
+
+ // Delete content type
+ this.contentTypeManager.removeContentType(partName);
+
+ // If this part is a relationship part, then delete all relationships of
+ // the source part.
+ if (partName.isRelationshipPartURI()) {
+ URI sourceURI = PackagingURIHelper
+ .getSourcePartUriFromRelationshipPartUri(partName.getURI());
+ PackagePartName sourcePartName;
+ try {
+ sourcePartName = PackagingURIHelper.createPartName(sourceURI);
+ } catch (InvalidFormatException e) {
+ logger
+ .error("Part name URI '"
+ + sourceURI
+ + "' is not valid ! This message is not intended to be displayed !");
+ return;
+ }
+ if (sourcePartName.getURI().equals(
+ PackagingURIHelper.PACKAGE_ROOT_URI)) {
+ clearRelationships();
+ } else if (containPart(sourcePartName)) {
+ PackagePart part = getPart(sourcePartName);
+ if (part != null)
+ part.clearRelationships();
+ }
+ }
+
+ this.isDirty = true;
+ }
+
+ /**
+ * Remove a part from this package as well as its relationship part, if one
+ * exists, and all parts listed in the relationship part. Be aware that this
+ * do not delete relationships which target the specified part.
+ *
+ * @param partName
+ * The name of the part to delete.
+ * @throws InvalidFormatException
+ * Throws if the associated relationship part of the specified
+ * part is not valid.
+ */
+ public void removePartRecursive(PackagePartName partName)
+ throws InvalidFormatException {
+ // Retrieves relationship part, if one exists
+ PackagePart relPart = this.partList.get(PackagingURIHelper
+ .getRelationshipPartName(partName));
+ // Retrieves PackagePart object from the package
+ PackagePart partToRemove = this.partList.get(partName);
+
+ if (relPart != null) {
+ PackageRelationshipCollection partRels = new PackageRelationshipCollection(
+ partToRemove);
+ for (PackageRelationship rel : partRels) {
+ PackagePartName partNameToRemove = PackagingURIHelper
+ .createPartName(PackagingURIHelper.resolvePartUri(rel
+ .getSourceURI(), rel.getTargetURI()));
+ removePart(partNameToRemove);
+ }
+
+ // Finally delete its relationship part if one exists
+ this.removePart(relPart.partName);
+ }
+
+ // Delete the specified part
+ this.removePart(partToRemove.partName);
+ }
+
+ /**
+ * Delete the part with the specified name and its associated relationships
+ * part if one exists. Prefer the use of this method to delete a part in the
+ * package, compare to the remove() methods that don't remove associated
+ * relationships part.
+ *
+ * @param partName
+ * Name of the part to delete
+ */
+ public void deletePart(PackagePartName partName) {
+ if (partName == null)
+ throw new IllegalArgumentException("partName");
+
+ // Remove the part
+ this.removePart(partName);
+ // Remove the relationships part
+ this.removePart(PackagingURIHelper.getRelationshipPartName(partName));
+ }
+
+ /**
+ * Delete the part with the specified name and all part listed in its
+ * associated relationships part if one exists. This process is recursively
+ * apply to all parts in the relationships part of the specified part.
+ * Prefer the use of this method to delete a part in the package, compare to
+ * the remove() methods that don't remove associated relationships part.
+ *
+ * @param partName
+ * Name of the part to delete
+ */
+ public void deletePartRecursive(PackagePartName partName) {
+ if (partName == null || !this.containPart(partName))
+ throw new IllegalArgumentException("partName");
+
+ PackagePart partToDelete = this.getPart(partName);
+ // Remove the part
+ this.removePart(partName);
+ // Remove all relationship parts associated
+ try {
+ for (PackageRelationship relationship : partToDelete
+ .getRelationships()) {
+ PackagePartName targetPartName = PackagingURIHelper
+ .createPartName(PackagingURIHelper.resolvePartUri(
+ partName.getURI(), relationship.getTargetURI()));
+ this.deletePartRecursive(targetPartName);
+ }
+ } catch (InvalidFormatException e) {
+ logger.warn("An exception occurs while deleting part '"
+ + partName.getName()
+ + "'. Some parts may remain in the package. - "
+ + e.getMessage());
+ return;
+ }
+ // Remove the relationships part
+ PackagePartName relationshipPartName = PackagingURIHelper
+ .getRelationshipPartName(partName);
+ if (relationshipPartName != null && containPart(relationshipPartName))
+ this.removePart(relationshipPartName);
+ }
+
+ /**
+ * Check if a part already exists in this package from its name.
+ *
+ * @param partName
+ * Part name to check.
+ * @return <i>true</i> if the part is logically added to this package, else
+ * <i>false</i>.
+ */
+ public boolean containPart(PackagePartName partName) {
+ return (this.getPart(partName) != null);
+ }
+
+ /**
+ * Add a relationship to the package (except relationships part).
+ *
+ * Check rule M4.1 : The format designer shall specify and the format
+ * producer shall create at most one core properties relationship for a
+ * package. A format consumer shall consider more than one core properties
+ * relationship for a package to be an error. If present, the relationship
+ * shall target the Core Properties part.
+ *
+ * Check rule M1.25: The Relationships part shall not have relationships to
+ * any other part. Package implementers shall enforce this requirement upon
+ * the attempt to create such a relationship and shall treat any such
+ * relationship as invalid.
+ *
+ * @param targetPartName
+ * Target part name.
+ * @param targetMode
+ * Target mode, either Internal or External.
+ * @param relationshipType
+ * Relationship type.
+ * @param relID
+ * ID of the relationship.
+ * @see PackageRelationshipTypes
+ */
+ public PackageRelationship addRelationship(PackagePartName targetPartName,
+ TargetMode targetMode, String relationshipType, String relID) {
+ /* Check OPC compliance */
+
+ // Check rule M4.1 : The format designer shall specify and the format
+ // producer
+ // shall create at most one core properties relationship for a package.
+ // A format consumer shall consider more than one core properties
+ // relationship for a package to be an error. If present, the
+ // relationship shall target the Core Properties part.
+ if (relationshipType.equals(PackageRelationshipTypes.CORE_PROPERTIES)
+ && this.packageProperties != null)
+ throw new InvalidOperationException(
+ "OPC Compliance error [M4.1]: can't add another core properties part ! Use the built-in package method instead.");
+
+ /*
+ * Check rule M1.25: The Relationships part shall not have relationships
+ * to any other part. Package implementers shall enforce this
+ * requirement upon the attempt to create such a relationship and shall
+ * treat any such relationship as invalid.
+ */
+ if (targetPartName.isRelationshipPartURI()) {
+ throw new InvalidOperationException(
+ "Rule M1.25: The Relationships part shall not have relationships to any other part.");
+ }
+
+ /* End OPC compliance */
+
+ ensureRelationships();
+ PackageRelationship retRel = relationships.addRelationship(
+ targetPartName.getURI(), targetMode, relationshipType, relID);
+ this.isDirty = true;
+ return retRel;
+ }
+
+ /**
+ * Add a package relationship.
+ *
+ * @param targetPartName
+ * Target part name.
+ * @param targetMode
+ * Target mode, either Internal or External.
+ * @param relationshipType
+ * Relationship type.
+ * @see PackageRelationshipTypes
+ */
+ public PackageRelationship addRelationship(PackagePartName targetPartName,
+ TargetMode targetMode, String relationshipType) {
+ return this.addRelationship(targetPartName, targetMode,
+ relationshipType, null);
+ }
+
+ /**
+ * Adds an external relationship to a part (except relationships part).
+ *
+ * The targets of external relationships are not subject to the same
+ * validity checks that internal ones are, as the contents is potentially
+ * any file, URL or similar.
+ *
+ * @param target
+ * External target of the relationship
+ * @param relationshipType
+ * Type of relationship.
+ * @return The newly created and added relationship
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+ * java.lang.String)
+ */
+ public PackageRelationship addExternalRelationship(String target,
+ String relationshipType) {
+ return addExternalRelationship(target, relationshipType, null);
+ }
+
+ /**
+ * Adds an external relationship to a part (except relationships part).
+ *
+ * The targets of external relationships are not subject to the same
+ * validity checks that internal ones are, as the contents is potentially
+ * any file, URL or similar.
+ *
+ * @param target
+ * External target of the relationship
+ * @param relationshipType
+ * Type of relationship.
+ * @param id
+ * Relationship unique id.
+ * @return The newly created and added relationship
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+ * java.lang.String)
+ */
+ public PackageRelationship addExternalRelationship(String target,
+ String relationshipType, String id) {
+ if (target == null) {
+ throw new IllegalArgumentException("target");
+ }
+ if (relationshipType == null) {
+ throw new IllegalArgumentException("relationshipType");
+ }
+
+ URI targetURI;
+ try {
+ targetURI = new URI(target);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Invalid target - " + e);
+ }
+
+ ensureRelationships();
+ PackageRelationship retRel = relationships.addRelationship(targetURI,
+ TargetMode.EXTERNAL, relationshipType, id);
+ this.isDirty = true;
+ return retRel;
+ }
+
+ /**
+ * Delete a relationship from this package.
+ *
+ * @param id
+ * Id of the relationship to delete.
+ */
+ public void removeRelationship(String id) {
+ if (relationships != null) {
+ relationships.removeRelationship(id);
+ this.isDirty = true;
+ }
+ }
+
+ /**
+ * Retrieves all package relationships.
+ *
+ * @return All package relationships of this package.
+ * @throws OpenXML4JException
+ * @see {@link #getRelationshipsHelper(String)}
+ */
+ public PackageRelationshipCollection getRelationships()
+ throws OpenXML4JException {
+ return getRelationshipsHelper(null);
+ }
+
+ /**
+ * Retrives all relationships with the specified type.
+ *
+ * @param relationshipType
+ * The filter specifying the relationship type.
+ * @return All relationships with the specified relationship type.
+ * @throws OpenXML4JException
+ */
+ public PackageRelationshipCollection getRelationshipsByType(
+ String relationshipType) throws IllegalArgumentException,
+ OpenXML4JException {
+ throwExceptionIfWriteOnly();
+ if (relationshipType == null) {
+ throw new IllegalArgumentException("relationshipType");
+ }
+ return getRelationshipsHelper(relationshipType);
+ }
+
+ /**
+ * Retrieves all relationships with specified id (normally just ine because
+ * a relationship id is supposed to be unique).
+ *
+ * @param id
+ * Id of the wanted relationship.
+ * @throws OpenXML4JException
+ */
+ private PackageRelationshipCollection getRelationshipsHelper(String id)
+ throws OpenXML4JException {
+ throwExceptionIfWriteOnly();
+ ensureRelationships();
+ return this.relationships.getRelationships(id);
+ }
+
+ /**
+ * Clear package relationships.
+ */
+ public void clearRelationships() {
+ if (relationships != null) {
+ relationships.clear();
+ this.isDirty = true;
+ }
+ }
+
+ /**
+ * Ensure that the relationships collection is not null.
+ */
+ public void ensureRelationships() {
+ if (this.relationships == null) {
+ try {
+ this.relationships = new PackageRelationshipCollection(this);
+ } catch (InvalidFormatException e) {
+ this.relationships = new PackageRelationshipCollection();
+ }
+ }
+ }
+
+ /**
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationship(java.lang.String)
+ */
+ public PackageRelationship getRelationship(String id) {
+ return this.relationships.getRelationshipByID(id);
+ }
+
+ /**
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#hasRelationships()
+ */
+ public boolean hasRelationships() {
+ return (relationships.size() > 0);
+ }
+
+ /**
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#isRelationshipExists(org.apache.poi.openxml4j.opc.PackageRelationship)
+ */
+ @SuppressWarnings("finally")
+ public boolean isRelationshipExists(PackageRelationship rel) {
+ try {
+ for (PackageRelationship r : this.getRelationships()) {
+ if (r == rel)
+ return true;
+ }
+ } finally {
+ return false;
+ }
+ }
+
+ /**
+ * Add a marshaller.
+ *
+ * @param contentType
+ * The content type to bind to the specified marshaller.
+ * @param marshaller
+ * The marshaller to register with the specified content type.
+ */
+ public void addMarshaller(String contentType, PartMarshaller marshaller) {
+ try {
+ partMarshallers.put(new ContentType(contentType), marshaller);
+ } catch (InvalidFormatException e) {
+ logger.warn("The specified content type is not valid: '"
+ + e.getMessage() + "'. The marshaller will not be added !");
+ }
+ }
+
+ /**
+ * Add an unmarshaller.
+ *
+ * @param contentType
+ * The content type to bind to the specified unmarshaller.
+ * @param unmarshaller
+ * The unmarshaller to register with the specified content type.
+ */
+ public void addUnmarshaller(String contentType,
+ PartUnmarshaller unmarshaller) {
+ try {
+ partUnmarshallers.put(new ContentType(contentType), unmarshaller);
+ } catch (InvalidFormatException e) {
+ logger.warn("The specified content type is not valid: '"
+ + e.getMessage()
+ + "'. The unmarshaller will not be added !");
+ }
+ }
+
+ /**
+ * Remove a marshaller by its content type.
+ *
+ * @param contentType
+ * The content type associated with the marshaller to remove.
+ */
+ public void removeMarshaller(String contentType) {
+ partMarshallers.remove(contentType);
+ }
+
+ /**
+ * Remove an unmarshaller by its content type.
+ *
+ * @param contentType
+ * The content type associated with the unmarshaller to remove.
+ */
+ public void removeUnmarshaller(String contentType) {
+ partUnmarshallers.remove(contentType);
+ }
+
+ /* Accesseurs */
+
+ /**
+ * Get the package access mode.
+ *
+ * @return the packageAccess The current package access.
+ */
+ public PackageAccess getPackageAccess() {
+ return packageAccess;
+ }
+
+ /**
+ * Validates the package compliance with the OPC specifications.
+ *
+ * @return <b>true</b> if the package is valid else <b>false</b>
+ */
+ public boolean validatePackage(Package pkg) throws InvalidFormatException {
+ throw new InvalidOperationException("Not implemented yet !!!");
+ }
+
+ /**
+ * Save the document in the specified file.
+ *
+ * @param targetFile
+ * Destination file.
+ * @throws IOException
+ * Throws if an IO exception occur.
+ * @see #save(OutputStream)
+ */
+ public void save(File targetFile) throws IOException {
+ if (targetFile == null)
+ throw new IllegalArgumentException("targetFile");
+
+ this.throwExceptionIfReadOnly();
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(targetFile);
+ } catch (FileNotFoundException e) {
+ throw new IOException(e.getLocalizedMessage());
+ }
+ this.save(fos);
+ }
+
+ /**
+ * Save the document in the specified output stream.
+ *
+ * @param outputStream
+ * The stream to save the package.
+ * @see #saveImpl(OutputStream)
+ */
+ public void save(OutputStream outputStream) throws IOException {
+ throwExceptionIfReadOnly();
+ this.saveImpl(outputStream);
+ }
+
+ /**
+ * Core method to create a package part. This method must be implemented by
+ * the subclass.
+ *
+ * @param partName
+ * URI of the part to create.
+ * @param contentType
+ * Content type of the part to create.
+ * @return The newly created package part.
+ */
+ protected abstract PackagePart createPartImpl(PackagePartName partName,
+ String contentType, boolean loadRelationships);
+
+ /**
+ * Core method to delete a package part. This method must be implemented by
+ * the subclass.
+ *
+ * @param partName
+ * The URI of the part to delete.
+ */
+ protected abstract void removePartImpl(PackagePartName partName);
+
+ /**
+ * Flush the package but not save.
+ */
+ protected abstract void flushImpl();
+
+ /**
+ * Close the package and cause a save of the package.
+ *
+ */
+ protected abstract void closeImpl() throws IOException;
+
+ /**
+ * Close the package without saving the document. Discard all changes made
+ * to this package.
+ */
+ protected abstract void revertImpl();
+
+ /**
+ * Save the package into the specified output stream.
+ *
+ * @param outputStream
+ * The output stream use to save this package.
+ */
+ protected abstract void saveImpl(OutputStream outputStream)
+ throws IOException;
+
+ /**
+ * Get the package part mapped to the specified URI.
+ *
+ * @param partName
+ * The URI of the part to retrieve.
+ * @return The package part located by the specified URI, else <b>null</b>.
+ */
+ protected abstract PackagePart getPartImpl(PackagePartName partName);
+
+ /**
+ * Get all parts link to the package.
+ *
+ * @return A list of the part owned by the package.
+ */
+ protected abstract PackagePart[] getPartsImpl()
+ throws InvalidFormatException;
+}
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
index 0000000000..a40f4ac436
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java
@@ -0,0 +1,33 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Specifies package access.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public enum PackageAccess {
+ /** Read only. Write not authorized. */
+ READ,
+ /** Write only. Read not authorized. */
+ WRITE,
+ /** Read and Write mode. */
+ READ_WRITE
+}
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
index 0000000000..5f53781eb8
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java
@@ -0,0 +1,52 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Open Packaging Convention namespaces URI.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public interface PackageNamespaces {
+
+ /**
+ * Content Types.
+ */
+ public static final String CONTENT_TYPES = "http://schemas.openxmlformats.org/package/2006/content-types";
+
+ /**
+ * Core Properties.
+ */
+ public static final String CORE_PROPERTIES = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
+
+ /**
+ * Digital Signatures.
+ */
+ public static final String DIGITAL_SIGNATURE = "http://schemas.openxmlformats.org/package/2006/digital-signature";
+
+ /**
+ * Relationships.
+ */
+ public static final String RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships";
+
+ /**
+ * Markup Compatibility.
+ */
+ public static final String MARKUP_COMPATIBILITY = "http://schemas.openxmlformats.org/markup-compatibility/2006";
+}
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
index 0000000000..34f26821f5
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
@@ -0,0 +1,654 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
+
+/**
+ * Provides a base class for parts stored in a Package.
+ *
+ * @author Julien Chable
+ * @version 0.9
+ */
+public abstract class PackagePart implements RelationshipSource {
+
+ /**
+ * This part's container.
+ */
+ protected Package container;
+
+ /**
+ * The part name. (required by the specification [M1.1])
+ */
+ protected PackagePartName partName;
+
+ /**
+ * The type of content of this part. (required by the specification [M1.2])
+ */
+ protected ContentType contentType;
+
+ /**
+ * Flag to know if this part is a relationship.
+ */
+ private boolean isRelationshipPart;
+
+ /**
+ * Flag to know if this part has been logically deleted.
+ */
+ private boolean isDeleted;
+
+ /**
+ * This part's relationships.
+ */
+ private PackageRelationshipCollection relationships;
+
+ /**
+ * Constructor.
+ *
+ * @param pack
+ * Parent package.
+ * @param partName
+ * The part name, relative to the parent Package root.
+ * @param contentType
+ * The content type.
+ * @throws InvalidFormatException
+ * If the specified URI is not valid.
+ */
+ protected PackagePart(Package pack, PackagePartName partName,
+ ContentType contentType) throws InvalidFormatException {
+ this(pack, partName, contentType, true);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param pack
+ * Parent package.
+ * @param partName
+ * The part name, relative to the parent Package root.
+ * @param contentType
+ * The content type.
+ * @param loadRelationships
+ * Specify if the relationships will be loaded
+ * @throws InvalidFormatException
+ * If the specified URI is not valid.
+ */
+ protected PackagePart(Package pack, PackagePartName partName,
+ ContentType contentType, boolean loadRelationships)
+ throws InvalidFormatException {
+ this.partName = partName;
+ this.contentType = contentType;
+ this.container = (ZipPackage) pack;
+
+ // Check if this part is a relationship part
+ isRelationshipPart = this.partName.isRelationshipPartURI();
+
+ // Load relationships if any
+ if (loadRelationships)
+ loadRelationships();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param pack
+ * Parent package.
+ * @param partName
+ * The part name, relative to the parent Package root.
+ * @param contentType
+ * The Multipurpose Internet Mail Extensions (MIME) content type
+ * of the part's data stream.
+ */
+ public PackagePart(Package pack, PackagePartName partName,
+ String contentType) throws InvalidFormatException {
+ this(pack, partName, new ContentType(contentType));
+ }
+
+ /**
+ * Adds an external relationship to a part (except relationships part).
+ *
+ * The targets of external relationships are not subject to the same
+ * validity checks that internal ones are, as the contents is potentially
+ * any file, URL or similar.
+ *
+ * @param target
+ * External target of the relationship
+ * @param relationshipType
+ * Type of relationship.
+ * @return The newly created and added relationship
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+ * java.lang.String)
+ */
+ public PackageRelationship addExternalRelationship(String target,
+ String relationshipType) {
+ return addExternalRelationship(target, relationshipType, null);
+ }
+
+ /**
+ * Adds an external relationship to a part (except relationships part).
+ *
+ * The targets of external relationships are not subject to the same
+ * validity checks that internal ones are, as the contents is potentially
+ * any file, URL or similar.
+ *
+ * @param target
+ * External target of the relationship
+ * @param relationshipType
+ * Type of relationship.
+ * @param id
+ * Relationship unique id.
+ * @return The newly created and added relationship
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
+ * java.lang.String)
+ */
+ public PackageRelationship addExternalRelationship(String target,
+ String relationshipType, String id) {
+ if (target == null) {
+ throw new IllegalArgumentException("target");
+ }
+ if (relationshipType == null) {
+ throw new IllegalArgumentException("relationshipType");
+ }
+
+ if (relationships == null) {
+ relationships = new PackageRelationshipCollection();
+ }
+
+ URI targetURI;
+ try {
+ targetURI = new URI(target);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Invalid target - " + e);
+ }
+
+ return relationships.addRelationship(targetURI, TargetMode.EXTERNAL,
+ relationshipType, id);
+ }
+
+ /**
+ * Add a relationship to a part (except relationships part).
+ *
+ * @param targetPartName
+ * Name of the target part. This one must be relative to the
+ * source root directory of the part.
+ * @param targetMode
+ * Mode [Internal|External].
+ * @param relationshipType
+ * Type of relationship.
+ * @return The newly created and added relationship
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+ * org.apache.poi.openxml4j.opc.TargetMode, java.lang.String)
+ */
+ public PackageRelationship addRelationship(PackagePartName targetPartName,
+ TargetMode targetMode, String relationshipType) {
+ return addRelationship(targetPartName, targetMode, relationshipType,
+ null);
+ }
+
+ /**
+ * Add a relationship to a part (except relationships part).
+ *
+ * Check rule M1.25: The Relationships part shall not have relationships to
+ * any other part. Package implementers shall enforce this requirement upon
+ * the attempt to create such a relationship and shall treat any such
+ * relationship as invalid.
+ *
+ * @param targetPartName
+ * Name of the target part. This one must be relative to the
+ * source root directory of the part.
+ * @param targetMode
+ * Mode [Internal|External].
+ * @param relationshipType
+ * Type of relationship.
+ * @param id
+ * Relationship unique id.
+ * @return The newly created and added relationship
+ *
+ * @throws InvalidFormatException
+ * If the URI point to a relationship part URI.
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+ * org.apache.poi.openxml4j.opc.TargetMode, java.lang.String, java.lang.String)
+ */
+ public PackageRelationship addRelationship(PackagePartName targetPartName,
+ TargetMode targetMode, String relationshipType, String id) {
+ container.throwExceptionIfReadOnly();
+
+ if (targetPartName == null) {
+ throw new IllegalArgumentException("targetPartName");
+ }
+ if (targetMode == null) {
+ throw new IllegalArgumentException("targetMode");
+ }
+ if (relationshipType == null) {
+ throw new IllegalArgumentException("relationshipType");
+ }
+
+ if (this.isRelationshipPart || targetPartName.isRelationshipPartURI()) {
+ throw new InvalidOperationException(
+ "Rule M1.25: The Relationships part shall not have relationships to any other part.");
+ }
+
+ if (relationships == null) {
+ relationships = new PackageRelationshipCollection();
+ }
+
+ return relationships.addRelationship(targetPartName.getURI(),
+ targetMode, relationshipType, id);
+ }
+
+ /**
+ * Add a relationship to a part (except relationships part).
+ *
+ * @param targetURI
+ * URI the target part. Must be relative to the source root
+ * directory of the part.
+ * @param targetMode
+ * Mode [Internal|External].
+ * @param relationshipType
+ * Type of relationship.
+ * @return The newly created and added relationship
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+ * org.apache.poi.openxml4j.opc.TargetMode, java.lang.String)
+ */
+ public PackageRelationship addRelationship(URI targetURI,
+ TargetMode targetMode, String relationshipType) {
+ return addRelationship(targetURI, targetMode, relationshipType, null);
+ }
+
+ /**
+ * Add a relationship to a part (except relationships part).
+ *
+ * Check rule M1.25: The Relationships part shall not have relationships to
+ * any other part. Package implementers shall enforce this requirement upon
+ * the attempt to create such a relationship and shall treat any such
+ * relationship as invalid.
+ *
+ * @param targetURI
+ * URI of the target part. Must be relative to the source root
+ * directory of the part.
+ * @param targetMode
+ * Mode [Internal|External].
+ * @param relationshipType
+ * Type of relationship.
+ * @param id
+ * Relationship unique id.
+ * @return The newly created and added relationship
+ *
+ * @throws InvalidFormatException
+ * If the URI point to a relationship part URI.
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#addRelationship(org.apache.poi.openxml4j.opc.PackagePartName,
+ * org.apache.poi.openxml4j.opc.TargetMode, java.lang.String, java.lang.String)
+ */
+ public PackageRelationship addRelationship(URI targetURI,
+ TargetMode targetMode, String relationshipType, String id) {
+ container.throwExceptionIfReadOnly();
+
+ if (targetURI == null) {
+ throw new IllegalArgumentException("targetPartName");
+ }
+ if (targetMode == null) {
+ throw new IllegalArgumentException("targetMode");
+ }
+ if (relationshipType == null) {
+ throw new IllegalArgumentException("relationshipType");
+ }
+
+ // Try to retrieve the target part
+
+ if (this.isRelationshipPart
+ || PackagingURIHelper.isRelationshipPartURI(targetURI)) {
+ throw new InvalidOperationException(
+ "Rule M1.25: The Relationships part shall not have relationships to any other part.");
+ }
+
+ if (relationships == null) {
+ relationships = new PackageRelationshipCollection();
+ }
+
+ return relationships.addRelationship(targetURI,
+ targetMode, relationshipType, id);
+ }
+
+ /**
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#clearRelationships()
+ */
+ public void clearRelationships() {
+ if (relationships != null) {
+ relationships.clear();
+ }
+ }
+
+ /**
+ * Delete the relationship specified by its id.
+ *
+ * @param id
+ * The ID identified the part to delete.
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#removeRelationship(java.lang.String)
+ */
+ public void removeRelationship(String id) {
+ this.container.throwExceptionIfReadOnly();
+ if (this.relationships != null)
+ this.relationships.removeRelationship(id);
+ }
+
+ /**
+ * Retrieve all the relationships attached to this part.
+ *
+ * @return This part's relationships.
+ * @throws OpenXML4JException
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationships()
+ */
+ public PackageRelationshipCollection getRelationships()
+ throws InvalidFormatException {
+ return getRelationshipsCore(null);
+ }
+
+ /**
+ * Retrieves a package relationship from its id.
+ *
+ * @param id
+ * ID of the package relationship to retrieve.
+ * @return The package relationship
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationship(java.lang.String)
+ */
+ public PackageRelationship getRelationship(String id) {
+ return this.relationships.getRelationshipByID(id);
+ }
+
+ /**
+ * Retrieve all relationships attached to this part which have the specified
+ * type.
+ *
+ * @param relationshipType
+ * Relationship type filter.
+ * @return All relationships from this part that have the specified type.
+ * @throws InvalidFormatException
+ * If an error occurs while parsing the part.
+ * @throws InvalidOperationException
+ * If the package is open in write only mode.
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationshipsByType(java.lang.String)
+ */
+ public PackageRelationshipCollection getRelationshipsByType(
+ String relationshipType) throws InvalidFormatException {
+ container.throwExceptionIfWriteOnly();
+
+ return getRelationshipsCore(relationshipType);
+ }
+
+ /**
+ * Implementation of the getRelationships method().
+ *
+ * @param filter
+ * Relationship type filter. If <i>null</i> then the filter is
+ * disabled and return all the relationships.
+ * @return All relationships from this part that have the specified type.
+ * @throws InvalidFormatException
+ * Throws if an error occurs during parsing the relationships
+ * part.
+ * @throws InvalidOperationException
+ * Throws if the package is open en write only mode.
+ * @see #getRelationshipsByType(String)
+ */
+ private PackageRelationshipCollection getRelationshipsCore(String filter)
+ throws InvalidFormatException {
+ this.container.throwExceptionIfWriteOnly();
+ if (relationships == null) {
+ this.throwExceptionIfRelationship();
+ relationships = new PackageRelationshipCollection(this);
+ }
+ return new PackageRelationshipCollection(relationships, filter);
+ }
+
+ /**
+ * Knows if the part have any relationships.
+ *
+ * @return <b>true</b> if the part have at least one relationship else
+ * <b>false</b>.
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#hasRelationships()
+ */
+ public boolean hasRelationships() {
+ return (!this.isRelationshipPart && (relationships != null && relationships
+ .size() > 0));
+ }
+
+ /**
+ * Checks if the specified relationship is part of this package part.
+ *
+ * @param rel
+ * The relationship to check.
+ * @return <b>true</b> if the specified relationship exists in this part,
+ * else returns <b>false</b>
+ * @see org.apache.poi.openxml4j.opc.RelationshipSource#isRelationshipExists(org.apache.poi.openxml4j.opc.PackageRelationship)
+ */
+ @SuppressWarnings("finally")
+ public boolean isRelationshipExists(PackageRelationship rel) {
+ try {
+ for (PackageRelationship r : this.getRelationships()) {
+ if (r == rel)
+ return true;
+ }
+ } finally {
+ return false;
+ }
+ }
+
+ /**
+ * Get the input stream of this part to read its content.
+ *
+ * @return The input stream of the content of this part, else
+ * <code>null</code>.
+ */
+ public InputStream getInputStream() throws IOException {
+ InputStream inStream = this.getInputStreamImpl();
+ if (inStream == null) {
+ throw new IOException("Can't obtain the input stream from "
+ + partName.getName());
+ } else
+ return inStream;
+ }
+
+ /**
+ * Get the output stream of this part. If the part is originally embedded in
+ * Zip package, it'll be transform intot a <i>MemoryPackagePart</i> in
+ * order to write inside (the standard Java API doesn't allow to write in
+ * the file)
+ *
+ * @see org.apache.poi.openxml4j.opc.internal.MemoryPackagePart
+ */
+ public OutputStream getOutputStream() {
+ OutputStream outStream;
+ // If this part is a zip package part (read only by design) we convert
+ // this part into a MemoryPackagePart instance for write purpose.
+ if (this instanceof ZipPackagePart) {
+ // Delete logically this part
+ this.container.removePart(this.partName);
+
+ // Create a memory part
+ PackagePart part = container.createPart(this.partName,
+ this.contentType.toString(), false);
+ part.relationships = this.relationships;
+ if (part == null) {
+ throw new InvalidOperationException(
+ "Can't create a temporary part !");
+ }
+ outStream = part.getOutputStreamImpl();
+ } else {
+ outStream = this.getOutputStreamImpl();
+ }
+ return outStream;
+ }
+
+ /**
+ * Throws an exception if this package part is a relationship part.
+ *
+ * @throws InvalidOperationException
+ * If this part is a relationship part.
+ */
+ private void throwExceptionIfRelationship()
+ throws InvalidOperationException {
+ if (this.isRelationshipPart)
+ throw new InvalidOperationException(
+ "Can do this operation on a relationship part !");
+ }
+
+ /**
+ * Ensure the package relationships collection instance is built.
+ *
+ * @throws InvalidFormatException
+ * Throws if
+ */
+ private void loadRelationships() throws InvalidFormatException {
+ if (this.relationships == null && !this.isRelationshipPart) {
+ this.throwExceptionIfRelationship();
+ relationships = new PackageRelationshipCollection(this);
+ }
+ }
+
+ /*
+ * Accessors
+ */
+
+ /**
+ * @return the uri
+ */
+ public PackagePartName getPartName() {
+ return partName;
+ }
+
+ /**
+ * @return the contentType
+ */
+ public String getContentType() {
+ return contentType.toString();
+ }
+
+ /**
+ * Set the content type.
+ *
+ * @param contentType
+ * the contentType to set
+ *
+ * @throws InvalidFormatException
+ * Throws if the content type is not valid.
+ * @throws InvalidOperationException
+ * Throws if you try to change the content type whereas this
+ * part is already attached to a package.
+ */
+ public void setContentType(String contentType)
+ throws InvalidFormatException {
+ if (container == null)
+ this.contentType = new ContentType(contentType);
+ else
+ throw new InvalidOperationException(
+ "You can't change the content type of a part.");
+ }
+
+ public Package getPackage() {
+ return container;
+ }
+
+ /**
+ * @return
+ */
+ public boolean isRelationshipPart() {
+ return this.isRelationshipPart;
+ }
+
+ /**
+ * @return
+ */
+ public boolean isDeleted() {
+ return isDeleted;
+ }
+
+ /**
+ * @param isDeleted
+ * the isDeleted to set
+ */
+ public void setDeleted(boolean isDeleted) {
+ this.isDeleted = isDeleted;
+ }
+
+ @Override
+ public String toString() {
+ return "Name: " + this.partName + " - Content Type: "
+ + this.contentType.toString();
+ }
+
+ /*-------------- Abstract methods ------------- */
+
+ /**
+ * Abtract method that get the input stream of this part.
+ *
+ * @exception IOException
+ * Throws if an IO Exception occur in the implementation
+ * method.
+ */
+ protected abstract InputStream getInputStreamImpl() throws IOException;
+
+ /**
+ * Abstract method that get the output stream of this part.
+ */
+ protected abstract OutputStream getOutputStreamImpl();
+
+ /**
+ * Save the content of this part and the associated relationships part (if
+ * this part own at least one relationship) into the specified output
+ * stream.
+ *
+ * @param zos
+ * Output stream to save this part.
+ * @throws OpenXML4JException
+ * If any exception occur.
+ */
+ public abstract boolean save(OutputStream zos) throws OpenXML4JException;
+
+ /**
+ * Load the content of this part.
+ *
+ * @param ios
+ * The input stream of the content to load.
+ * @return <b>true</b> if the content has been successfully loaded, else
+ * <b>false</b>.
+ * @throws InvalidFormatException
+ * Throws if the content format is invalid.
+ */
+ public abstract boolean load(InputStream ios) throws InvalidFormatException;
+
+ /**
+ * Close this part : flush this part, close the input stream and output
+ * stream. After this method call, the part must be available for packaging.
+ */
+ public abstract void close();
+
+ /**
+ * Flush the content of this part. If the input stream and/or output stream
+ * as in a waiting state to read or write, the must to empty their
+ * respective buffer.
+ */
+ public abstract void flush();
+}
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
index 0000000000..54021302bf
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java
@@ -0,0 +1,81 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+
+/**
+ * A package part collection.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public final class PackagePartCollection extends
+ TreeMap<PackagePartName, PackagePart> {
+
+ private static final long serialVersionUID = 2515031135957635515L;
+
+ /**
+ * Arraylist use to store this collection part names as string for rule
+ * M1.11 optimized checking.
+ */
+ private ArrayList<String> registerPartNameStr = new ArrayList<String>();
+
+ @Override
+ public Object clone() {
+ return super.clone();
+ }
+
+ /**
+ * Check rule [M1.11]: a package implementer shall neither create nor
+ * recognize a part with a part name derived from another part name by
+ * appending segments to it.
+ *
+ * @exception InvalidOperationException
+ * Throws if you try to add a part with a name derived from
+ * another part name.
+ */
+ @Override
+ public PackagePart put(PackagePartName partName, PackagePart part) {
+ String[] segments = partName.getURI().toASCIIString().split(
+ PackagingURIHelper.FORWARD_SLASH_STRING);
+ StringBuffer concatSeg = new StringBuffer();
+ for (String seg : segments) {
+ if (!seg.equals(""))
+ concatSeg.append(PackagingURIHelper.FORWARD_SLASH_CHAR);
+ concatSeg.append(seg);
+ if (this.registerPartNameStr.contains(concatSeg.toString())) {
+ throw new InvalidOperationException(
+ "You can't add a part with a part name derived from another part ! [M1.11]");
+ }
+ }
+ this.registerPartNameStr.add(partName.getName());
+ return super.put(partName, part);
+ }
+
+ @Override
+ public PackagePart remove(Object key) {
+ if (key instanceof PackagePartName) {
+ this.registerPartNameStr.remove(((PackagePartName) key).getName());
+ }
+ return super.remove(key);
+ }
+}
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
index 0000000000..414fd41aaa
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartName.java
@@ -0,0 +1,509 @@
+/* ====================================================================
+ 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.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
+
+/**
+ * An immutable Open Packaging Convention compliant part name.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ *
+ * @see http://www.ietf.org/rfc/rfc3986.txt
+ */
+public final class PackagePartName implements Comparable<PackagePartName> {
+
+ /**
+ * Part name stored as an URI.
+ */
+ private URI partNameURI;
+
+ /*
+ * URI Characters definition (RFC 3986)
+ */
+
+ /**
+ * Reserved characters for sub delimitations.
+ */
+ private static String[] RFC3986_PCHAR_SUB_DELIMS = { "!", "$", "&", "'",
+ "(", ")", "*", "+", ",", ";", "=" };
+
+ /**
+ * Unreserved character (+ ALPHA & DIGIT).
+ */
+ private static String[] RFC3986_PCHAR_UNRESERVED_SUP = { "-", ".", "_", "~" };
+
+ /**
+ * Authorized reserved characters for pChar.
+ */
+ private static String[] RFC3986_PCHAR_AUTHORIZED_SUP = { ":", "@" };
+
+ /**
+ * Flag to know if this part name is from a relationship part name.
+ */
+ private boolean isRelationship;
+
+ /**
+ * Constructor. Makes a ValidPartName object from a java.net.URI
+ *
+ * @param uri
+ * The URI to validate and to transform into ValidPartName.
+ * @param checkConformance
+ * Flag to specify if the contructor have to validate the OPC
+ * conformance. Must be always <code>true</code> except for
+ * special URI like '/' which is needed for internal use by
+ * OpenXML4J but is not valid.
+ * @throws InvalidFormatException
+ * Throw if the specified part name is not conform to Open
+ * Packaging Convention specifications.
+ * @see java.net.URI
+ */
+ PackagePartName(URI uri, boolean checkConformance)
+ throws InvalidFormatException {
+ if (checkConformance) {
+ throwExceptionIfInvalidPartUri(uri);
+ } else {
+ if (!PackagingURIHelper.PACKAGE_ROOT_URI.equals(uri)) {
+ throw new OpenXML4JRuntimeException(
+ "OCP conformance must be check for ALL part name except special cases : ['/']");
+ }
+ }
+ this.partNameURI = uri;
+ this.isRelationship = isRelationshipPartURI(this.partNameURI);
+ }
+
+ /**
+ * Constructor. Makes a ValidPartName object from a String part name.
+ *
+ * @param partName
+ * Part name to valid and to create.
+ * @param checkConformance
+ * Flag to specify if the contructor have to validate the OPC
+ * conformance. Must be always <code>true</code> except for
+ * special URI like '/' which is needed for internal use by
+ * OpenXML4J but is not valid.
+ * @throws InvalidFormatException
+ * Throw if the specified part name is not conform to Open
+ * Packaging Convention specifications.
+ */
+ PackagePartName(String partName, boolean checkConformance)
+ throws InvalidFormatException {
+ URI partURI;
+ try {
+ partURI = new URI(partName);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(
+ "partName argmument is not a valid OPC part name !");
+ }
+
+ if (checkConformance) {
+ throwExceptionIfInvalidPartUri(partURI);
+ } else {
+ if (!PackagingURIHelper.PACKAGE_ROOT_URI.equals(partURI)) {
+ throw new OpenXML4JRuntimeException(
+ "OCP conformance must be check for ALL part name except special cases : ['/']");
+ }
+ }
+ this.partNameURI = partURI;
+ this.isRelationship = isRelationshipPartURI(this.partNameURI);
+ }
+
+ /**
+ * Check if the specified part name is a relationship part name.
+ *
+ * @param partUri
+ * The URI to check.
+ * @return <code>true</code> if this part name respect the relationship
+ * part naming convention else <code>false</code>.
+ */
+ private boolean isRelationshipPartURI(URI partUri) {
+ if (partUri == null)
+ throw new IllegalArgumentException("partUri");
+
+ return partUri.getPath().matches(
+ "^.*/" + PackagingURIHelper.RELATIONSHIP_PART_SEGMENT_NAME + "/.*\\"
+ + PackagingURIHelper.RELATIONSHIP_PART_EXTENSION_NAME
+ + "$");
+ }
+
+ /**
+ * Know if this part name is a relationship part name.
+ *
+ * @return <code>true</code> if this part name respect the relationship
+ * part naming convention else <code>false</code>.
+ */
+ public boolean isRelationshipPartURI() {
+ return this.isRelationship;
+ }
+
+ /**
+ * Throws an exception (of any kind) if the specified part name does not
+ * follow the Open Packaging Convention specifications naming rules.
+ *
+ * @param partUri
+ * The part name to check.
+ * @throws Exception
+ * Throws if the part name is invalid.
+ */
+ private static void throwExceptionIfInvalidPartUri(URI partUri)
+ throws InvalidFormatException {
+ if (partUri == null)
+ throw new IllegalArgumentException("partUri");
+ // Check if the part name URI is empty [M1.1]
+ throwExceptionIfEmptyURI(partUri);
+
+ // Check if the part name URI is absolute
+ throwExceptionIfAbsoluteUri(partUri);
+
+ // Check if the part name URI starts with a forward slash [M1.4]
+ throwExceptionIfPartNameNotStartsWithForwardSlashChar(partUri);
+
+ // Check if the part name URI ends with a forward slash [M1.5]
+ throwExceptionIfPartNameEndsWithForwardSlashChar(partUri);
+
+ // Check if the part name does not have empty segments. [M1.3]
+ // Check if a segment ends with a dot ('.') character. [M1.9]
+ throwExceptionIfPartNameHaveInvalidSegments(partUri);
+ }
+
+ /**
+ * Throws an exception if the specified URI is empty. [M1.1]
+ *
+ * @param partURI
+ * Part URI to check.
+ * @throws InvalidFormatException
+ * If the specified URI is empty.
+ */
+ private static void throwExceptionIfEmptyURI(URI partURI)
+ throws InvalidFormatException {
+ if (partURI == null)
+ throw new IllegalArgumentException("partURI");
+
+ String uriPath = partURI.getPath();
+ if (uriPath.length() == 0
+ || ((uriPath.length() == 1) && (uriPath.charAt(0) == PackagingURIHelper.FORWARD_SLASH_CHAR)))
+ throw new InvalidFormatException(
+ "A part name shall not be empty [M1.1]: "
+ + partURI.getPath());
+ }
+
+ /**
+ * Throws an exception if the part name has empty segments. [M1.3]
+ *
+ * Throws an exception if a segment any characters other than pchar
+ * characters. [M1.6]
+ *
+ * Throws an exception if a segment contain percent-encoded forward slash
+ * ('/'), or backward slash ('\') characters. [M1.7]
+ *
+ * Throws an exception if a segment contain percent-encoded unreserved
+ * characters. [M1.8]
+ *
+ * Throws an exception if the specified part name's segments end with a dot
+ * ('.') character. [M1.9]
+ *
+ * Throws an exception if a segment doesn't include at least one non-dot
+ * character. [M1.10]
+ *
+ * @param partUri
+ * The part name to check.
+ * @throws InvalidFormatException
+ * if the specified URI contain an empty segments or if one the
+ * segments contained in the part name, ends with a dot ('.')
+ * character.
+ */
+ private static void throwExceptionIfPartNameHaveInvalidSegments(URI partUri)
+ throws InvalidFormatException {
+ if (partUri == null || "".equals(partUri)) {
+ throw new IllegalArgumentException("partUri");
+ }
+
+ // Split the URI into several part and analyze each
+ String[] segments = partUri.toASCIIString().split("/");
+ if (segments.length <= 1 || !segments[0].equals(""))
+ throw new InvalidFormatException(
+ "A part name shall not have empty segments [M1.3]: "
+ + partUri.getPath());
+
+ for (int i = 1; i < segments.length; ++i) {
+ String seg = segments[i];
+ if (seg == null || "".equals(seg)) {
+ throw new InvalidFormatException(
+ "A part name shall not have empty segments [M1.3]: "
+ + partUri.getPath());
+ }
+
+ if (seg.endsWith(".")) {
+ throw new InvalidFormatException(
+ "A segment shall not end with a dot ('.') character [M1.9]: "
+ + partUri.getPath());
+ }
+
+ if ("".equals(seg.replaceAll("\\\\.", ""))) {
+ // Normally will never been invoked with the previous
+ // implementation rule [M1.9]
+ throw new InvalidFormatException(
+ "A segment shall include at least one non-dot character. [M1.10]: "
+ + partUri.getPath());
+ }
+
+ /*
+ * Check for rule M1.6, M1.7, M1.8
+ */
+ checkPCharCompliance(seg);
+ }
+ }
+
+ /**
+ * Throws an exception if a segment any characters other than pchar
+ * characters. [M1.6]
+ *
+ * Throws an exception if a segment contain percent-encoded forward slash
+ * ('/'), or backward slash ('\') characters. [M1.7]
+ *
+ * Throws an exception if a segment contain percent-encoded unreserved
+ * characters. [M1.8]
+ *
+ * @param segment
+ * The segment to check
+ */
+ private static void checkPCharCompliance(String segment)
+ throws InvalidFormatException {
+ boolean errorFlag;
+ for (int i = 0; i < segment.length(); ++i) {
+ char c = segment.charAt(i);
+ errorFlag = true;
+
+ /* Check rule M1.6 */
+
+ // Check for digit or letter
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9')) {
+ errorFlag = false;
+ } else {
+ // Check "-", ".", "_", "~"
+ for (int j = 0; j < RFC3986_PCHAR_UNRESERVED_SUP.length; ++j) {
+ if (c == RFC3986_PCHAR_UNRESERVED_SUP[j].charAt(0)) {
+ errorFlag = false;
+ break;
+ }
+ }
+
+ // Check ":", "@"
+ for (int j = 0; errorFlag
+ && j < RFC3986_PCHAR_AUTHORIZED_SUP.length; ++j) {
+ if (c == RFC3986_PCHAR_AUTHORIZED_SUP[j].charAt(0)) {
+ errorFlag = false;
+ }
+ }
+
+ // Check "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
+ for (int j = 0; errorFlag
+ && j < RFC3986_PCHAR_SUB_DELIMS.length; ++j) {
+ if (c == RFC3986_PCHAR_SUB_DELIMS[j].charAt(0)) {
+ errorFlag = false;
+ }
+ }
+ }
+
+ if (errorFlag && c == '%') {
+ // We certainly found an encoded character, check for length
+ // now ( '%' HEXDIGIT HEXDIGIT)
+ if (((segment.length() - i) < 2)) {
+ throw new InvalidFormatException("The segment " + segment
+ + " contain invalid encoded character !");
+ }
+
+ // If not percent encoded character error occur then reset the
+ // flag -> the character is valid
+ errorFlag = false;
+
+ // Decode the encoded character
+ char decodedChar = (char) Integer.parseInt(segment.substring(
+ i + 1, i + 3), 16);
+ i += 2;
+
+ /* Check rule M1.7 */
+ if (decodedChar == '/' || decodedChar == '\\')
+ throw new InvalidFormatException(
+ "A segment shall not contain percent-encoded forward slash ('/'), or backward slash ('\') characters. [M1.7]");
+
+ /* Check rule M1.8 */
+
+ // Check for unreserved character like define in RFC3986
+ if ((decodedChar >= 'A' && decodedChar <= 'Z')
+ || (decodedChar >= 'a' && decodedChar <= 'z')
+ || (decodedChar >= '0' && decodedChar <= '9'))
+ errorFlag = true;
+
+ // Check for unreserved character "-", ".", "_", "~"
+ for (int j = 0; !errorFlag
+ && j < RFC3986_PCHAR_UNRESERVED_SUP.length; ++j) {
+ if (c == RFC3986_PCHAR_UNRESERVED_SUP[j].charAt(0)) {
+ errorFlag = true;
+ break;
+ }
+ }
+ if (errorFlag)
+ throw new InvalidFormatException(
+ "A segment shall not contain percent-encoded unreserved characters. [M1.8]");
+ }
+
+ if (errorFlag)
+ throw new InvalidFormatException(
+ "A segment shall not hold any characters other than pchar characters. [M1.6]");
+ }
+ }
+
+ /**
+ * Throws an exception if the specified part name doesn't start with a
+ * forward slash character '/'. [M1.4]
+ *
+ * @param partUri
+ * The part name to check.
+ * @throws InvalidFormatException
+ * If the specified part name doesn't start with a forward slash
+ * character '/'.
+ */
+ private static void throwExceptionIfPartNameNotStartsWithForwardSlashChar(
+ URI partUri) throws InvalidFormatException {
+ String uriPath = partUri.getPath();
+ if (uriPath.length() > 0
+ && uriPath.charAt(0) != PackagingURIHelper.FORWARD_SLASH_CHAR)
+ throw new InvalidFormatException(
+ "A part name shall start with a forward slash ('/') character [M1.4]: "
+ + partUri.getPath());
+ }
+
+ /**
+ * Throws an exception if the specified part name ends with a forwar slash
+ * character '/'. [M1.5]
+ *
+ * @param partUri
+ * The part name to check.
+ * @throws InvalidFormatException
+ * If the specified part name ends with a forwar slash character
+ * '/'.
+ */
+ private static void throwExceptionIfPartNameEndsWithForwardSlashChar(
+ URI partUri) throws InvalidFormatException {
+ String uriPath = partUri.getPath();
+ if (uriPath.length() > 0
+ && uriPath.charAt(uriPath.length() - 1) == PackagingURIHelper.FORWARD_SLASH_CHAR)
+ throw new InvalidFormatException(
+ "A part name shall not have a forward slash as the last character [M1.5]: "
+ + partUri.getPath());
+ }
+
+ /**
+ * Throws an exception if the specified URI is absolute.
+ *
+ * @param partUri
+ * The URI to check.
+ * @throws InvalidFormatException
+ * Throws if the specified URI is absolute.
+ */
+ private static void throwExceptionIfAbsoluteUri(URI partUri)
+ throws InvalidFormatException {
+ if (partUri.isAbsolute())
+ throw new InvalidFormatException("Absolute URI forbidden: "
+ + partUri);
+ }
+
+ /**
+ * Compare two part name following the rule M1.12 :
+ *
+ * Part name equivalence is determined by comparing part names as
+ * case-insensitive ASCII strings. Packages shall not contain equivalent
+ * part names and package implementers shall neither create nor recognize
+ * packages with equivalent part names. [M1.12]
+ */
+ public int compareTo(PackagePartName otherPartName) {
+ if (otherPartName == null)
+ return -1;
+ return this.partNameURI.toASCIIString().toLowerCase().compareTo(
+ otherPartName.partNameURI.toASCIIString().toLowerCase());
+ }
+
+ /**
+ * Retrieves the extension of the part name if any. If there is no extension
+ * returns an empty String. Example : '/document/content.xml' => 'xml'
+ *
+ * @return The extension of the part name.
+ */
+ public String getExtension() {
+ String fragment = this.partNameURI.getPath();
+ if (fragment.length() > 0) {
+ int i = fragment.lastIndexOf(".");
+ if (i > -1)
+ return fragment.substring(i + 1);
+ }
+ return "";
+ }
+
+ /**
+ * Get this part name.
+ *
+ * @return The name of this part name.
+ */
+ public String getName() {
+ return this.partNameURI.toASCIIString();
+ }
+
+ /**
+ * Part name equivalence is determined by comparing part names as
+ * case-insensitive ASCII strings. Packages shall not contain equivalent
+ * part names and package implementers shall neither create nor recognize
+ * packages with equivalent part names. [M1.12]
+ */
+ @Override
+ public boolean equals(Object otherPartName) {
+ if (otherPartName == null
+ || !(otherPartName instanceof PackagePartName))
+ return false;
+ return this.partNameURI.toASCIIString().toLowerCase().equals(
+ ((PackagePartName) otherPartName).partNameURI.toASCIIString()
+ .toLowerCase());
+ }
+
+ @Override
+ public int hashCode() {
+ return this.partNameURI.toASCIIString().toLowerCase().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+ /* Getters and setters */
+
+ /**
+ * Part name property getter.
+ *
+ * @return This part name URI.
+ */
+ public URI getURI() {
+ return this.partNameURI;
+ }
+}
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
index 0000000000..5ab80dd0f8
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageProperties.java
@@ -0,0 +1,227 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.util.Date;
+
+import org.apache.poi.openxml4j.util.Nullable;
+
+/**
+ * Represents the core properties of an OPC package.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ * @see org.apache.poi.openxml4j.opc.Package
+ */
+public interface PackageProperties {
+
+ /**
+ * Dublin Core Terms URI.
+ */
+ public final static String NAMESPACE_DCTERMS = "http://purl.org/dc/terms/";
+
+ /**
+ * Dublin Core namespace URI.
+ */
+ public final static String NAMESPACE_DC = "http://purl.org/dc/elements/1.1/";
+
+ /* Getters and setters */
+
+ /**
+ * Set the category of the content of this package.
+ */
+ public abstract Nullable<String> getCategoryProperty();
+
+ /**
+ * Set the category of the content of this package.
+ */
+ public abstract void setCategoryProperty(String category);
+
+ /**
+ * Set the status of the content.
+ */
+ public abstract Nullable<String> getContentStatusProperty();
+
+ /**
+ * Get the status of the content.
+ */
+ public abstract void setContentStatusProperty(String contentStatus);
+
+ /**
+ * Get the type of content represented, generally defined by a specific use
+ * and intended audience.
+ */
+ public abstract Nullable<String> getContentTypeProperty();
+
+ /**
+ * Set the type of content represented, generally defined by a specific use
+ * and intended audience.
+ */
+ public abstract void setContentTypeProperty(String contentType);
+
+ /**
+ * Get the date of creation of the resource.
+ */
+ public abstract Nullable<Date> getCreatedProperty();
+
+ /**
+ * Set the date of creation of the resource.
+ */
+ public abstract void setCreatedProperty(String created);
+
+ /**
+ * Set the date of creation of the resource.
+ */
+ public abstract void setCreatedProperty(Nullable<Date> created);
+
+ /**
+ * Get the entity primarily responsible for making the content of the
+ * resource.
+ */
+ public abstract Nullable<String> getCreatorProperty();
+
+ /**
+ * Set the entity primarily responsible for making the content of the
+ * resource.
+ */
+ public abstract void setCreatorProperty(String creator);
+
+ /**
+ * Get the explanation of the content of the resource.
+ */
+ public abstract Nullable<String> getDescriptionProperty();
+
+ /**
+ * Set the explanation of the content of the resource.
+ */
+ public abstract void setDescriptionProperty(String description);
+
+ /**
+ * Get an unambiguous reference to the resource within a given context.
+ */
+ public abstract Nullable<String> getIdentifierProperty();
+
+ /**
+ * Set an unambiguous reference to the resource within a given context.
+ */
+ public abstract void setIdentifierProperty(String identifier);
+
+ /**
+ * Get a delimited set of keywords to support searching and indexing. This
+ * is typically a list of terms that are not available elsewhere in the
+ * properties
+ */
+ public abstract Nullable<String> getKeywordsProperty();
+
+ /**
+ * Set a delimited set of keywords to support searching and indexing. This
+ * is typically a list of terms that are not available elsewhere in the
+ * properties
+ */
+ public abstract void setKeywordsProperty(String keywords);
+
+ /**
+ * Get the language of the intellectual content of the resource.
+ */
+ public abstract Nullable<String> getLanguageProperty();
+
+ /**
+ * Set the language of the intellectual content of the resource.
+ */
+ public abstract void setLanguageProperty(String language);
+
+ /**
+ * Get the user who performed the last modification.
+ */
+ public abstract Nullable<String> getLastModifiedByProperty();
+
+ /**
+ * Set the user who performed the last modification.
+ */
+ public abstract void setLastModifiedByProperty(String lastModifiedBy);
+
+ /**
+ * Get the date and time of the last printing.
+ */
+ public abstract Nullable<Date> getLastPrintedProperty();
+
+ /**
+ * Set the date and time of the last printing.
+ */
+ public abstract void setLastPrintedProperty(String lastPrinted);
+
+ /**
+ * Set the date and time of the last printing.
+ */
+ public abstract void setLastPrintedProperty(Nullable<Date> lastPrinted);
+
+ /**
+ * Get the date on which the resource was changed.
+ */
+ public abstract Nullable<Date> getModifiedProperty();
+
+ /**
+ * Set the date on which the resource was changed.
+ */
+ public abstract void setModifiedProperty(String modified);
+
+ /**
+ * Set the date on which the resource was changed.
+ */
+ public abstract void setModifiedProperty(Nullable<Date> modified);
+
+ /**
+ * Get the revision number.
+ */
+ public abstract Nullable<String> getRevisionProperty();
+
+ /**
+ * Set the revision number.
+ */
+ public abstract void setRevisionProperty(String revision);
+
+ /**
+ * Get the topic of the content of the resource.
+ */
+ public abstract Nullable<String> getSubjectProperty();
+
+ /**
+ * Set the topic of the content of the resource.
+ */
+ public abstract void setSubjectProperty(String subject);
+
+ /**
+ * Get the name given to the resource.
+ */
+ public abstract Nullable<String> getTitleProperty();
+
+ /**
+ * Set the name given to the resource.
+ */
+ public abstract void setTitleProperty(String title);
+
+ /**
+ * Get the version number.
+ */
+ public abstract Nullable<String> getVersionProperty();
+
+ /**
+ * Set the version number.
+ */
+ public abstract void setVersionProperty(String version);
+}
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
index 0000000000..efaa093245
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationship.java
@@ -0,0 +1,227 @@
+/* ====================================================================
+ 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.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * A part relationship.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public final class PackageRelationship {
+
+ private static URI containerRelationshipPart;
+
+ static {
+ try {
+ containerRelationshipPart = new URI("/_rels/.rels");
+ } catch (URISyntaxException e) {
+ // Do nothing
+ }
+ }
+
+ /* XML markup */
+
+ public static final String ID_ATTRIBUTE_NAME = "Id";
+
+ public static final String RELATIONSHIPS_TAG_NAME = "Relationships";
+
+ public static final String RELATIONSHIP_TAG_NAME = "Relationship";
+
+ public static final String TARGET_ATTRIBUTE_NAME = "Target";
+
+ public static final String TARGET_MODE_ATTRIBUTE_NAME = "TargetMode";
+
+ public static final String TYPE_ATTRIBUTE_NAME = "Type";
+
+ /* End XML markup */
+
+ /**
+ * L'ID de la relation.
+ */
+ private String id;
+
+ /**
+ * R�f�rence vers le package.
+ */
+ private Package container;
+
+ /**
+ * Type de relation.
+ */
+ private String relationshipType;
+
+ /**
+ * Partie source de cette relation.
+ */
+ private PackagePart source;
+
+ /**
+ * Le mode de ciblage [Internal|External]
+ */
+ private TargetMode targetMode;
+
+ /**
+ * URI de la partie cible.
+ */
+ private URI targetUri;
+
+ /**
+ * Constructor.
+ *
+ * @param packageParent
+ * @param sourcePart
+ * @param targetUri
+ * @param targetMode
+ * @param relationshipType
+ * @param id
+ */
+ public PackageRelationship(Package pkg, PackagePart sourcePart,
+ URI targetUri, TargetMode targetMode, String relationshipType,
+ String id) {
+ if (pkg == null)
+ throw new IllegalArgumentException("pkg");
+ if (targetUri == null)
+ throw new IllegalArgumentException("targetUri");
+ if (relationshipType == null)
+ throw new IllegalArgumentException("relationshipType");
+ if (id == null)
+ throw new IllegalArgumentException("id");
+
+ this.container = pkg;
+ this.source = sourcePart;
+ this.targetUri = targetUri;
+ this.targetMode = targetMode;
+ this.relationshipType = relationshipType;
+ this.id = id;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PackageRelationship)) {
+ return false;
+ }
+ PackageRelationship rel = (PackageRelationship) obj;
+ return (this.id == rel.id
+ && this.relationshipType == rel.relationshipType
+ && (rel.source != null ? rel.source.equals(this.source) : true)
+ && this.targetMode == rel.targetMode && this.targetUri
+ .equals(rel.targetUri));
+ }
+
+ @Override
+ public int hashCode() {
+ return this.id.hashCode() + this.relationshipType.hashCode()
+ + this.source.hashCode() + this.targetMode.hashCode()
+ + this.targetUri.hashCode();
+ }
+
+ /* Getters */
+
+ public URI getContainerPartRelationship() {
+ return containerRelationshipPart;
+ }
+
+ /**
+ * @return the container
+ */
+ public Package getPackage() {
+ return container;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @return the relationshipType
+ */
+ public String getRelationshipType() {
+ return relationshipType;
+ }
+
+ /**
+ * @return the source
+ */
+ public PackagePart getSource() {
+ return source;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public URI getSourceURI() {
+ if (source == null) {
+ return PackagingURIHelper.PACKAGE_ROOT_URI;
+ }
+ return source.partName.getURI();
+ }
+
+ /**
+ * public URI getSourceUri(){ }
+ *
+ * @return the targetMode
+ */
+ public TargetMode getTargetMode() {
+ return targetMode;
+ }
+
+ /**
+ * @return the targetUri
+ */
+ public URI getTargetURI() {
+ // If it's an external target, we don't
+ // need to apply our normal validation rules
+ if(targetMode == TargetMode.EXTERNAL) {
+ return targetUri;
+ }
+
+ // Internal target
+ // If it isn't absolute, resolve it relative
+ // to ourselves
+ if (!targetUri.toASCIIString().startsWith("/")) {
+ // So it's a relative part name, try to resolve it
+ return PackagingURIHelper.resolvePartUri(getSourceURI(), targetUri);
+ }
+ return targetUri;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(id == null ? "id=null" : "id=" + id);
+ sb.append(container == null ? " - container=null" : " - container="
+ + container.toString());
+ sb.append(relationshipType == null ? " - relationshipType=null"
+ : " - relationshipType=" + relationshipType.toString());
+ sb.append(source == null ? " - source=null" : " - source="
+ + getSourceURI().toASCIIString());
+ sb.append(targetUri == null ? " - target=null" : " - target="
+ + getTargetURI().toASCIIString());
+ sb.append(targetMode == null ? ",targetMode=null" : ",targetMode="
+ + targetMode.toString());
+ return sb.toString();
+ }
+}
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
index 0000000000..daf2e7e090
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java
@@ -0,0 +1,450 @@
+/* ====================================================================
+ 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.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+import org.dom4j.Attribute;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+
+/**
+ * Represents a collection of PackageRelationship elements that are owned by a
+ * given PackagePart or the Package.
+ *
+ * @author Julien Chable, CDubettier
+ * @version 0.1
+ */
+public final class PackageRelationshipCollection implements
+ Iterable<PackageRelationship> {
+
+ private static Logger logger = Logger.getLogger("org.openxml4j.opc");
+
+ /**
+ * Package relationships ordered by ID.
+ */
+ private TreeMap<String, PackageRelationship> relationshipsByID;
+
+ /**
+ * Package relationships ordered by type.
+ */
+ private TreeMap<String, PackageRelationship> relationshipsByType;
+
+ /**
+ * This relationshipPart.
+ */
+ private PackagePart relationshipPart;
+
+ /**
+ * Source part.
+ */
+ private PackagePart sourcePart;
+
+ /**
+ * This part name.
+ */
+ private PackagePartName partName;
+
+ /**
+ * Reference to the package.
+ */
+ private Package container;
+
+ /**
+ * Constructor.
+ */
+ PackageRelationshipCollection() {
+ relationshipsByID = new TreeMap<String, PackageRelationship>();
+ relationshipsByType = new TreeMap<String, PackageRelationship>();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * This collection will contain only elements from the specified collection
+ * for which the type is compatible with the specified relationship type
+ * filter.
+ *
+ * @param coll
+ * Collection to import.
+ * @param filter
+ * Relationship type filter.
+ */
+ public PackageRelationshipCollection(PackageRelationshipCollection coll,
+ String filter) {
+ this();
+ for (PackageRelationship rel : coll.relationshipsByID.values()) {
+ if (filter == null || rel.getRelationshipType().equals(filter))
+ addRelationship(rel);
+ }
+ }
+
+ /**
+ * Constructor.
+ */
+ public PackageRelationshipCollection(Package container)
+ throws InvalidFormatException {
+ this(container, null);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @throws InvalidFormatException
+ * Throws if the format of the content part is invalid.
+ *
+ * @throws InvalidOperationException
+ * Throws if the specified part is a relationship part.
+ */
+ public PackageRelationshipCollection(PackagePart part)
+ throws InvalidFormatException {
+ this(part.container, part);
+ }
+
+ /**
+ * Constructor. Parse the existing package relationship part if one exists.
+ *
+ * @param container
+ * The parent package.
+ * @param part
+ * The part that own this relationships collection. If <b>null</b>
+ * then this part is considered as the package root.
+ * @throws InvalidFormatException
+ * If an error occurs during the parsing of the relatinships
+ * part fo the specified part.
+ */
+ public PackageRelationshipCollection(Package container, PackagePart part)
+ throws InvalidFormatException {
+ this();
+
+ if (container == null)
+ throw new IllegalArgumentException("container");
+
+ // Check if the specified part is not a relationship part
+ if (part != null && part.isRelationshipPart())
+ throw new IllegalArgumentException("part");
+
+ this.container = container;
+ this.sourcePart = part;
+ this.partName = getRelationshipPartName(part);
+ if ((container.getPackageAccess() != PackageAccess.WRITE)
+ && container.containPart(this.partName)) {
+ relationshipPart = container.getPart(this.partName);
+ parseRelationshipsPart(relationshipPart);
+ }
+ }
+
+ /**
+ * Get the relationship part name of the specified part.
+ *
+ * @param part
+ * The part .
+ * @return The relationship part name of the specified part. Be careful,
+ * only the correct name is returned, this method does not check if
+ * the part really exist in a package !
+ * @throws InvalidOperationException
+ * Throws if the specified part is a relationship part.
+ */
+ private static PackagePartName getRelationshipPartName(PackagePart part)
+ throws InvalidOperationException {
+ PackagePartName partName;
+ if (part == null) {
+ partName = PackagingURIHelper.PACKAGE_ROOT_PART_NAME;
+ } else {
+ partName = part.getPartName();
+ }
+ return PackagingURIHelper.getRelationshipPartName(partName);
+ }
+
+ /**
+ * Add the specified relationship to the collection.
+ *
+ * @param relPart
+ * The relationship to add.
+ */
+ public void addRelationship(PackageRelationship relPart) {
+ relationshipsByID.put(relPart.getId(), relPart);
+ relationshipsByType.put(relPart.getRelationshipType(), relPart);
+ }
+
+ /**
+ * Add a relationship to the collection.
+ *
+ * @param targetUri
+ * Target URI.
+ * @param targetMode
+ * The target mode : INTERNAL or EXTERNAL
+ * @param relationshipType
+ * Relationship type.
+ * @param id
+ * Relationship ID.
+ * @return The newly created relationship.
+ * @see PackageAccess
+ */
+ public PackageRelationship addRelationship(URI targetUri,
+ TargetMode targetMode, String relationshipType, String id) {
+
+ if (id == null) {
+ // Generate a unique ID is id parameter is null.
+ int i = 0;
+ do {
+ id = "rId" + ++i;
+ } while (relationshipsByID.get(id) != null);
+ }
+
+ PackageRelationship rel = new PackageRelationship(container,
+ sourcePart, targetUri, targetMode, relationshipType, id);
+ relationshipsByID.put(rel.getId(), rel);
+ relationshipsByType.put(rel.getRelationshipType(), rel);
+ return rel;
+ }
+
+ /**
+ * Remove a relationship by its ID.
+ *
+ * @param id
+ * The relationship ID to remove.
+ */
+ public void removeRelationship(String id) {
+ if (relationshipsByID != null && relationshipsByType != null) {
+ PackageRelationship rel = relationshipsByID.get(id);
+ if (rel != null) {
+ relationshipsByID.remove(rel.getId());
+ relationshipsByType.values().remove(rel);
+ }
+ }
+ }
+
+ /**
+ * Remove a relationship by its reference.
+ *
+ * @param rel
+ * The relationship to delete.
+ */
+ public void removeRelationship(PackageRelationship rel) {
+ if (rel == null)
+ throw new IllegalArgumentException("rel");
+
+ relationshipsByID.values().remove(rel);
+ relationshipsByType.values().remove(rel);
+ }
+
+ /**
+ * Retrieves a relationship by its index in the collection.
+ *
+ * @param index
+ * Must be a value between [0-relationships_count-1]
+ */
+ public PackageRelationship getRelationship(int index) {
+ if (index < 0 || index > relationshipsByID.values().size())
+ throw new IllegalArgumentException("index");
+
+ PackageRelationship retRel = null;
+ int i = 0;
+ for (PackageRelationship rel : relationshipsByID.values()) {
+ if (index == i++)
+ return rel;
+ }
+ return retRel;
+ }
+
+ /**
+ * Retrieves a package relationship based on its id.
+ *
+ * @param id
+ * ID of the package relationship to retrieve.
+ * @return The package relationship identified by the specified id.
+ */
+ public PackageRelationship getRelationshipByID(String id) {
+ return relationshipsByID.get(id);
+ }
+
+ /**
+ * Get the numbe rof relationships in the collection.
+ */
+ public int size() {
+ return relationshipsByID.values().size();
+ }
+
+ /**
+ * Parse the relationship part and add all relationship in this collection.
+ *
+ * @param relPart
+ * The package part to parse.
+ * @throws InvalidFormatException
+ * Throws if the relationship part is invalid.
+ */
+ private void parseRelationshipsPart(PackagePart relPart)
+ throws InvalidFormatException {
+ try {
+ SAXReader reader = new SAXReader();
+ logger.debug("Parsing relationship: " + relPart.getPartName());
+ Document xmlRelationshipsDoc = reader
+ .read(relPart.getInputStream());
+
+ // Browse default types
+ Element root = xmlRelationshipsDoc.getRootElement();
+
+ // Check OPC compliance M4.1 rule
+ boolean fCorePropertiesRelationship = false;
+
+ for (Iterator i = root
+ .elementIterator(PackageRelationship.RELATIONSHIP_TAG_NAME); i
+ .hasNext();) {
+ Element element = (Element) i.next();
+ // Relationship ID
+ String id = element.attribute(
+ PackageRelationship.ID_ATTRIBUTE_NAME).getValue();
+ // Relationship type
+ String type = element.attribute(
+ PackageRelationship.TYPE_ATTRIBUTE_NAME).getValue();
+
+ /* Check OPC Compliance */
+ // Check Rule M4.1
+ if (type.equals(PackageRelationshipTypes.CORE_PROPERTIES))
+ if (!fCorePropertiesRelationship)
+ fCorePropertiesRelationship = true;
+ else
+ throw new InvalidFormatException(
+ "OPC Compliance error [M4.1]: there is more than one core properties relationship in the package !");
+
+ /* End OPC Compliance */
+
+ // TargetMode (default value "Internal")
+ Attribute targetModeAttr = element
+ .attribute(PackageRelationship.TARGET_MODE_ATTRIBUTE_NAME);
+ TargetMode targetMode = TargetMode.INTERNAL;
+ if (targetModeAttr != null) {
+ targetMode = targetModeAttr.getValue().toLowerCase()
+ .equals("internal") ? TargetMode.INTERNAL
+ : TargetMode.EXTERNAL;
+ }
+
+ // Target converted in URI
+ URI target;
+ String value = "";
+ try {
+ value = element.attribute(
+ PackageRelationship.TARGET_ATTRIBUTE_NAME)
+ .getValue();
+
+ if (value.indexOf("\\") != -1) {
+ logger
+ .info("target contains \\ therefore not a valid URI"
+ + value + " replaced by /");
+ value = value.replaceAll("\\\\", "/");
+ // word can save external relationship with a \ instead
+ // of /
+ }
+
+ target = new URI(value);
+ } catch (URISyntaxException e) {
+ logger.error("Cannot convert " + value
+ + " in a valid relationship URI-> ignored", e);
+ continue;
+ }
+ addRelationship(target, targetMode, type, id);
+ }
+ } catch (Exception e) {
+ logger.error(e);
+ throw new InvalidFormatException(e.getMessage());
+ }
+ }
+
+ /**
+ * Retrieves all relations with the specified type.
+ *
+ * @param typeFilter
+ * Relationship type filter. If <b>null</b> then all
+ * relationships are returned.
+ * @return All relationships of the type specified by the filter.
+ */
+ public PackageRelationshipCollection getRelationships(String typeFilter) {
+ PackageRelationshipCollection coll = new PackageRelationshipCollection(
+ this, typeFilter);
+ return coll;
+ }
+
+ /**
+ * Get this collection's iterator.
+ */
+ public Iterator<PackageRelationship> iterator() {
+ return relationshipsByID.values().iterator();
+ }
+
+ /**
+ * Get an iterator of a collection with all relationship with the specified
+ * type.
+ *
+ * @param typeFilter
+ * Type filter.
+ * @return An iterator to a collection containing all relationships with the
+ * specified type contain in this collection.
+ */
+ public Iterator<PackageRelationship> iterator(String typeFilter) {
+ ArrayList<PackageRelationship> retArr = new ArrayList<PackageRelationship>();
+ for (PackageRelationship rel : relationshipsByID.values()) {
+ if (rel.getRelationshipType().equals(typeFilter))
+ retArr.add(rel);
+ }
+ return retArr.iterator();
+ }
+
+ /**
+ * Clear all relationships.
+ */
+ public void clear() {
+ relationshipsByID.clear();
+ relationshipsByType.clear();
+ }
+
+ @Override
+ public String toString() {
+ String str;
+ if (relationshipsByID == null) {
+ str = "relationshipsByID=null";
+ } else {
+ str = relationshipsByID.size() + " relationship(s) = [";
+ }
+ if ((relationshipPart != null) && (relationshipPart.partName != null)) {
+ str = str + "," + relationshipPart.partName;
+ } else {
+ str = str + ",relationshipPart=null";
+ }
+
+ // Source of this relationship
+ if ((sourcePart != null) && (sourcePart.partName != null)) {
+ str = str + "," + sourcePart.partName;
+ } else {
+ str = str + ",sourcePart=null";
+ }
+ if (partName != null) {
+ str = str + "," + partName;
+ } else {
+ str = str + ",uri=null)";
+ }
+ return str + "]";
+ }
+}
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
index 0000000000..e5ecaacc45
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java
@@ -0,0 +1,77 @@
+/* ====================================================================
+ 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;
+
+/**
+ * Relationship types.
+ *
+ * @author Julien Chable
+ * @version 0.2
+ */
+public interface PackageRelationshipTypes {
+
+ /**
+ * Core properties relationship type.
+ */
+ String CORE_PROPERTIES = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
+
+ /**
+ * Digital signature relationship type.
+ */
+ String DIGITAL_SIGNATURE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";
+
+ /**
+ * Digital signature certificate relationship type.
+ */
+ String DIGITAL_SIGNATURE_CERTIFICATE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/certificate";
+
+ /**
+ * Digital signature origin relationship type.
+ */
+ String DIGITAL_SIGNATURE_ORIGIN = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
+
+ /**
+ * Thumbnail relationship type.
+ */
+ String THUMBNAIL = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail";
+
+ /**
+ * Extended properties relationship type.
+ */
+ String EXTENDED_PROPERTIES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties";
+
+ /**
+ * Core properties relationship type.
+ */
+ String CORE_DOCUMENT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
+
+ /**
+ * Custom XML relationship type.
+ */
+ String CUSTOM_XML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml";
+
+ /**
+ * Image type.
+ */
+ String IMAGE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
+
+ /**
+ * Style type.
+ */
+ String STYLE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
+}
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
index 0000000000..2a63f47bcf
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagingURIHelper.java
@@ -0,0 +1,623 @@
+/* ====================================================================
+ 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.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+
+/**
+ * Helper for part and pack URI.
+ *
+ * @author Julien Chable, CDubet, Kim Ung
+ * @version 0.1
+ */
+public final class PackagingURIHelper {
+
+ /**
+ * Package root URI.
+ */
+ private static URI packageRootUri;
+
+ /**
+ * Extension name of a relationship part.
+ */
+ public static final String RELATIONSHIP_PART_EXTENSION_NAME;
+
+ /**
+ * Segment name of a relationship part.
+ */
+ public static final String RELATIONSHIP_PART_SEGMENT_NAME;
+
+ /**
+ * Segment name of the package properties folder.
+ */
+ public static final String PACKAGE_PROPERTIES_SEGMENT_NAME;
+
+ /**
+ * Core package properties art name.
+ */
+ public static final String PACKAGE_CORE_PROPERTIES_NAME;
+
+ /**
+ * Forward slash URI separator.
+ */
+ public static final char FORWARD_SLASH_CHAR;
+
+ /**
+ * Forward slash URI separator.
+ */
+ public static final String FORWARD_SLASH_STRING;
+
+ /**
+ * Package relationships part URI
+ */
+ public static final URI PACKAGE_RELATIONSHIPS_ROOT_URI;
+
+ /**
+ * Package relationships part name.
+ */
+ public static final PackagePartName PACKAGE_RELATIONSHIPS_ROOT_PART_NAME;
+
+ /**
+ * Core properties part URI.
+ */
+ public static final URI CORE_PROPERTIES_URI;
+
+ /**
+ * Core properties partname.
+ */
+ public static final PackagePartName CORE_PROPERTIES_PART_NAME;
+
+ /**
+ * Root package URI.
+ */
+ public static final URI PACKAGE_ROOT_URI;
+
+ /**
+ * Root package part name.
+ */
+ public static final PackagePartName PACKAGE_ROOT_PART_NAME;
+
+ /* Static initialization */
+ static {
+ RELATIONSHIP_PART_SEGMENT_NAME = "_rels";
+ RELATIONSHIP_PART_EXTENSION_NAME = ".rels";
+ FORWARD_SLASH_CHAR = '/';
+ FORWARD_SLASH_STRING = "/";
+ PACKAGE_PROPERTIES_SEGMENT_NAME = "docProps";
+ PACKAGE_CORE_PROPERTIES_NAME = "core.xml";
+
+ // Make URI
+ URI uriPACKAGE_ROOT_URI = null;
+ URI uriPACKAGE_RELATIONSHIPS_ROOT_URI = null;
+ URI uriPACKAGE_PROPERTIES_URI = null;
+ try {
+ uriPACKAGE_ROOT_URI = new URI("/");
+ uriPACKAGE_RELATIONSHIPS_ROOT_URI = new URI(FORWARD_SLASH_CHAR
+ + RELATIONSHIP_PART_SEGMENT_NAME + FORWARD_SLASH_CHAR
+ + RELATIONSHIP_PART_EXTENSION_NAME);
+ packageRootUri = new URI("/");
+ uriPACKAGE_PROPERTIES_URI = new URI(FORWARD_SLASH_CHAR
+ + PACKAGE_PROPERTIES_SEGMENT_NAME + FORWARD_SLASH_CHAR
+ + PACKAGE_CORE_PROPERTIES_NAME);
+ } catch (URISyntaxException e) {
+ // Should never happen in production as all data are fixed
+ }
+ PACKAGE_ROOT_URI = uriPACKAGE_ROOT_URI;
+ PACKAGE_RELATIONSHIPS_ROOT_URI = uriPACKAGE_RELATIONSHIPS_ROOT_URI;
+ CORE_PROPERTIES_URI = uriPACKAGE_PROPERTIES_URI;
+
+ // Make part name from previous URI
+ PackagePartName tmpPACKAGE_ROOT_PART_NAME = null;
+ PackagePartName tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME = null;
+ PackagePartName tmpCORE_PROPERTIES_URI = null;
+ try {
+ tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME = createPartName(PACKAGE_RELATIONSHIPS_ROOT_URI);
+ tmpCORE_PROPERTIES_URI = createPartName(CORE_PROPERTIES_URI);
+ tmpPACKAGE_ROOT_PART_NAME = new PackagePartName(PACKAGE_ROOT_URI,
+ false);
+ } catch (InvalidFormatException e) {
+ // Should never happen in production as all data are fixed
+ }
+ PACKAGE_RELATIONSHIPS_ROOT_PART_NAME = tmpPACKAGE_RELATIONSHIPS_ROOT_PART_NAME;
+ CORE_PROPERTIES_PART_NAME = tmpCORE_PROPERTIES_URI;
+ PACKAGE_ROOT_PART_NAME = tmpPACKAGE_ROOT_PART_NAME;
+ }
+
+ /**
+ * Gets the URI for the package root.
+ *
+ * @return URI of the package root.
+ */
+ public static URI getPackageRootUri() {
+ return packageRootUri;
+ }
+
+ /**
+ * Know if the specified URI is a relationship part name.
+ *
+ * @param partUri
+ * URI to check.
+ * @return <i>true</i> if the URI <i>false</i>.
+ */
+ public static boolean isRelationshipPartURI(URI partUri) {
+ if (partUri == null)
+ throw new IllegalArgumentException("partUri");
+
+ return partUri.getPath().matches(
+ ".*" + RELATIONSHIP_PART_SEGMENT_NAME + ".*"
+ + RELATIONSHIP_PART_EXTENSION_NAME + "$");
+ }
+
+ /**
+ * Get file name from the specified URI.
+ */
+ public static String getFilename(URI uri) {
+ if (uri != null) {
+ String path = uri.getPath();
+ int len = path.length();
+ int num2 = len;
+ while (--num2 >= 0) {
+ char ch1 = path.charAt(num2);
+ if (ch1 == PackagingURIHelper.FORWARD_SLASH_CHAR)
+ return path.substring(num2 + 1, len);
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Get the file name without the trailing extension.
+ */
+ public static String getFilenameWithoutExtension(URI uri) {
+ String filename = getFilename(uri);
+ int dotIndex = filename.lastIndexOf(".");
+ if (dotIndex == -1)
+ return filename;
+ return filename.substring(0, dotIndex);
+ }
+
+ /**
+ * Get the directory path from the specified URI.
+ */
+ public static URI getPath(URI uri) {
+ if (uri != null) {
+ String path = uri.getPath();
+ int len = path.length();
+ int num2 = len;
+ while (--num2 >= 0) {
+ char ch1 = path.charAt(num2);
+ if (ch1 == PackagingURIHelper.FORWARD_SLASH_CHAR) {
+ try {
+ return new URI(path.substring(0, num2));
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Combine les deux URI.
+ *
+ * @param prefix
+ * L'URI de pr�fixe.
+ * @param suffix
+ * L'URI de suffixe.
+ * @return
+ */
+ public static URI combine(URI prefix, URI suffix) {
+ URI retUri = null;
+ try {
+ retUri = new URI(combine(prefix.getPath(), suffix.getPath()));
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(
+ "Prefix and suffix can't be combine !");
+ }
+ return retUri;
+ }
+
+ /**
+ * Combine a string URI with a prefix and a suffix.
+ */
+ public static String combine(String prefix, String suffix) {
+ if (!prefix.endsWith("" + FORWARD_SLASH_CHAR)
+ && !suffix.startsWith("" + FORWARD_SLASH_CHAR))
+ return prefix + FORWARD_SLASH_CHAR + suffix;
+ else if ((!prefix.endsWith("" + FORWARD_SLASH_CHAR)
+ && suffix.startsWith("" + FORWARD_SLASH_CHAR) || (prefix
+ .endsWith("" + FORWARD_SLASH_CHAR) && !suffix.startsWith(""
+ + FORWARD_SLASH_CHAR))))
+ return prefix + suffix;
+ else
+ return "";
+ }
+
+ /**
+ * Fully relativize the source part URI against the target part URI.
+ *
+ * @param sourceURI
+ * The source part URI.
+ * @param targetURI
+ * The target part URI.
+ * @return A fully relativize part name URI ('word/media/image1.gif',
+ * '/word/document.xml' => 'media/image1.gif') else
+ * <code>null</code>.
+ */
+ public static URI relativizeURI(URI sourceURI, URI targetURI) {
+ StringBuilder retVal = new StringBuilder();
+ String[] segmentsSource = sourceURI.getPath().split("/", -1);
+ String[] segmentsTarget = targetURI.getPath().split("/", -1);
+
+ // If the source URI is empty
+ if (segmentsSource.length == 0) {
+ throw new IllegalArgumentException(
+ "Can't relativize an empty source URI !");
+ }
+
+ // If target URI is empty
+ if (segmentsTarget.length == 0) {
+ throw new IllegalArgumentException(
+ "Can't relativize an empty target URI !");
+ }
+
+ // If the source is the root, then the relativized
+ // form must actually be an absolute URI
+ if(sourceURI.toString().equals("/")) {
+ return targetURI;
+ }
+
+
+ // Relativize the source URI against the target URI.
+ // First up, figure out how many steps along we can go
+ // and still have them be the same
+ int segmentsTheSame = 0;
+ for (int i = 0; i < segmentsSource.length && i < segmentsTarget.length; i++) {
+ if (segmentsSource[i].equals(segmentsTarget[i])) {
+ // Match so far, good
+ segmentsTheSame++;
+ } else {
+ break;
+ }
+ }
+
+ // If we didn't have a good match or at least except a first empty element
+ if ((segmentsTheSame == 0 || segmentsTheSame == 1) &&
+ segmentsSource[0].equals("") && segmentsTarget[0].equals("")) {
+ for (int i = 0; i < segmentsSource.length - 2; i++) {
+ retVal.append("../");
+ }
+ for (int i = 0; i < segmentsTarget.length; i++) {
+ if (segmentsTarget[i].equals(""))
+ continue;
+ retVal.append(segmentsTarget[i]);
+ if (i != segmentsTarget.length - 1)
+ retVal.append("/");
+ }
+
+ try {
+ return new URI(retVal.toString());
+ } catch (Exception e) {
+ System.err.println(e);
+ return null;
+ }
+ }
+
+ // Special case for where the two are the same
+ if (segmentsTheSame == segmentsSource.length
+ && segmentsTheSame == segmentsTarget.length) {
+ retVal.append("");
+ } else {
+ // Matched for so long, but no more
+
+ // Do we need to go up a directory or two from
+ // the source to get here?
+ // (If it's all the way up, then don't bother!)
+ if (segmentsTheSame == 1) {
+ retVal.append("/");
+ } else {
+ for (int j = segmentsTheSame; j < segmentsSource.length - 1; j++) {
+ retVal.append("../");
+ }
+ }
+
+ // Now go from here on down
+ for (int j = segmentsTheSame; j < segmentsTarget.length; j++) {
+ if (retVal.length() > 0
+ && retVal.charAt(retVal.length() - 1) != '/') {
+ retVal.append("/");
+ }
+ retVal.append(segmentsTarget[j]);
+ }
+ }
+
+ try {
+ return new URI(retVal.toString());
+ } catch (Exception e) {
+ System.err.println(e);
+ return null;
+ }
+ }
+
+ /**
+ * Resolve a source uri against a target.
+ *
+ * @param sourcePartUri
+ * The source URI.
+ * @param targetUri
+ * The target URI.
+ * @return The resolved URI.
+ */
+ public static URI resolvePartUri(URI sourcePartUri, URI targetUri) {
+ if (sourcePartUri == null || sourcePartUri.isAbsolute()) {
+ throw new IllegalArgumentException("sourcePartUri invalid - "
+ + sourcePartUri);
+ }
+
+ if (targetUri == null || targetUri.isAbsolute()) {
+ throw new IllegalArgumentException("targetUri invalid - "
+ + targetUri);
+ }
+
+ return sourcePartUri.resolve(targetUri);
+ }
+
+ /**
+ * Get URI from a string path.
+ */
+ public static URI getURIFromPath(String path) {
+ URI retUri = null;
+ try {
+ retUri = new URI(path);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("path");
+ }
+ return retUri;
+ }
+
+ /**
+ * Get the source part URI from a specified relationships part.
+ *
+ * @param relationshipPartUri
+ * The relationship part use to retrieve the source part.
+ * @return The source part URI from the specified relationships part.
+ */
+ public static URI getSourcePartUriFromRelationshipPartUri(
+ URI relationshipPartUri) {
+ if (relationshipPartUri == null)
+ throw new IllegalArgumentException(
+ "Le param�tre relationshipPartUri ne doit pas �tre null !");
+
+ if (!isRelationshipPartURI(relationshipPartUri))
+ throw new IllegalArgumentException(
+ "L'URI ne doit pas �tre celle d'une partie de type relation.");
+
+ if (relationshipPartUri.compareTo(PACKAGE_RELATIONSHIPS_ROOT_URI) == 0)
+ return PACKAGE_ROOT_URI;
+
+ String filename = relationshipPartUri.getPath();
+ String filenameWithoutExtension = getFilenameWithoutExtension(relationshipPartUri);
+ filename = filename
+ .substring(0, ((filename.length() - filenameWithoutExtension
+ .length()) - RELATIONSHIP_PART_EXTENSION_NAME.length()));
+ filename = filename.substring(0, filename.length()
+ - RELATIONSHIP_PART_SEGMENT_NAME.length() - 1);
+ filename = combine(filename, filenameWithoutExtension);
+ return getURIFromPath(filename);
+ }
+
+ /**
+ * Create an OPC compliant part name by throwing an exception if the URI is
+ * not valid.
+ *
+ * @param partUri
+ * The part name URI to validate.
+ * @return A valid part name object, else <code>null</code>.
+ * @throws InvalidFormatException
+ * Throws if the specified URI is not OPC compliant.
+ */
+ public static PackagePartName createPartName(URI partUri)
+ throws InvalidFormatException {
+ if (partUri == null)
+ throw new IllegalArgumentException("partName");
+
+ return new PackagePartName(partUri, true);
+ }
+
+ /**
+ * Create an OPC compliant part name.
+ *
+ * @param partName
+ * The part name to validate.
+ * @return The correspondant part name if valid, else <code>null</code>.
+ * @throws InvalidFormatException
+ * Throws if the specified part name is not OPC compliant.
+ * @see #createPartName(URI)
+ */
+ public static PackagePartName createPartName(String partName)
+ throws InvalidFormatException {
+ URI partNameURI;
+ try {
+ partNameURI = new URI(partName);
+ } catch (URISyntaxException e) {
+ throw new InvalidFormatException(e.getMessage());
+ }
+ return createPartName(partNameURI);
+ }
+
+ /**
+ * Create an OPC compliant part name by resolving it using a base part.
+ *
+ * @param partName
+ * The part name to validate.
+ * @param relativePart
+ * The relative base part.
+ * @return The correspondant part name if valid, else <code>null</code>.
+ * @throws InvalidFormatException
+ * Throws if the specified part name is not OPC compliant.
+ * @see #createPartName(URI)
+ */
+ public static PackagePartName createPartName(String partName,
+ PackagePart relativePart) throws InvalidFormatException {
+ URI newPartNameURI;
+ try {
+ newPartNameURI = resolvePartUri(
+ relativePart.getPartName().getURI(), new URI(partName));
+ } catch (URISyntaxException e) {
+ throw new InvalidFormatException(e.getMessage());
+ }
+ return createPartName(newPartNameURI);
+ }
+
+ /**
+ * Create an OPC compliant part name by resolving it using a base part.
+ *
+ * @param partName
+ * The part name URI to validate.
+ * @param relativePart
+ * The relative base part.
+ * @return The correspondant part name if valid, else <code>null</code>.
+ * @throws InvalidFormatException
+ * Throws if the specified part name is not OPC compliant.
+ * @see #createPartName(URI)
+ */
+ public static PackagePartName createPartName(URI partName,
+ PackagePart relativePart) throws InvalidFormatException {
+ URI newPartNameURI = resolvePartUri(
+ relativePart.getPartName().getURI(), partName);
+ return createPartName(newPartNameURI);
+ }
+
+ /**
+ * Validate a part URI by returning a boolean.
+ * ([M1.1],[M1.3],[M1.4],[M1.5],[M1.6])
+ *
+ * (OPC Specifications 8.1.1 Part names) :
+ *
+ * Part Name Syntax
+ *
+ * The part name grammar is defined as follows:
+ *
+ * <i>part_name = 1*( "/" segment )
+ *
+ * segment = 1*( pchar )</i>
+ *
+ *
+ * (pchar is defined in RFC 3986)
+ *
+ * @param partUri
+ * The URI to validate.
+ * @return <b>true</b> if the URI is valid to the OPC Specifications, else
+ * <b>false</b>
+ *
+ * @see #createPartName(URI)
+ */
+ public static boolean isValidPartName(URI partUri) {
+ if (partUri == null)
+ throw new IllegalArgumentException("partUri");
+
+ try {
+ createPartName(partUri);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * Decode a URI by converting all percent encoded character into a String
+ * character.
+ *
+ * @param uri
+ * The URI to decode.
+ * @return The specified URI in a String with converted percent encoded
+ * characters.
+ */
+ public static String decodeURI(URI uri) {
+ StringBuffer retVal = new StringBuffer();
+ String uriStr = uri.toASCIIString();
+ char c;
+ for (int i = 0; i < uriStr.length(); ++i) {
+ c = uriStr.charAt(i);
+ if (c == '%') {
+ // We certainly found an encoded character, check for length
+ // now ( '%' HEXDIGIT HEXDIGIT)
+ if (((uriStr.length() - i) < 2)) {
+ throw new IllegalArgumentException("The uri " + uriStr
+ + " contain invalid encoded character !");
+ }
+
+ // Decode the encoded character
+ char decodedChar = (char) Integer.parseInt(uriStr.substring(
+ i + 1, i + 3), 16);
+ retVal.append(decodedChar);
+ i += 2;
+ continue;
+ }
+ retVal.append(c);
+ }
+ return retVal.toString();
+ }
+
+ /**
+ * Build a part name where the relationship should be stored ((ex
+ * /word/document.xml -> /word/_rels/document.xml.rels)
+ *
+ * @param partName
+ * Source part URI
+ * @return the full path (as URI) of the relation file
+ * @throws InvalidOperationException
+ * Throws if the specified URI is a relationshp part.
+ */
+ public static PackagePartName getRelationshipPartName(
+ PackagePartName partName) {
+ if (partName == null)
+ throw new IllegalArgumentException("partName");
+
+ if (PackagingURIHelper.PACKAGE_ROOT_URI.getPath() == partName.getURI()
+ .getPath())
+ return PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_PART_NAME;
+
+ if (partName.isRelationshipPartURI())
+ throw new InvalidOperationException("Can't be a relationship part");
+
+ String fullPath = partName.getURI().getPath();
+ String filename = getFilename(partName.getURI());
+ fullPath = fullPath.substring(0, fullPath.length() - filename.length());
+ fullPath = combine(fullPath,
+ PackagingURIHelper.RELATIONSHIP_PART_SEGMENT_NAME);
+ fullPath = combine(fullPath, filename);
+ fullPath = fullPath
+ + PackagingURIHelper.RELATIONSHIP_PART_EXTENSION_NAME;
+
+ PackagePartName retPartName;
+ try {
+ retPartName = createPartName(fullPath);
+ } catch (InvalidFormatException e) {
+ // Should never happen in production as all data are fixed but in
+ // case of return null:
+ return null;
+ }
+ return retPartName;
+ }
+}
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
index 0000000000..ce9d1159c7
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/RelationshipSource.java
@@ -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
index 0000000000..1d55c7bb7f
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
@@ -0,0 +1,77 @@
+/* ====================================================================
+ 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.InputStream;
+import java.io.OutputStream;
+
+import org.dom4j.Document;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+
+public final class StreamHelper {
+
+ private StreamHelper() {
+ // Do nothing
+ }
+
+ /**
+ * Turning the DOM4j object in the specified output stream.
+ *
+ * @param xmlContent
+ * The XML document.
+ * @param outStream
+ * The OutputStream in which the XML document will be written.
+ * @return <b>true</b> if the xml is successfully written in the stream,
+ * else <b>false</b>.
+ */
+ public static boolean saveXmlInStream(Document xmlContent,
+ OutputStream outStream) {
+ try {
+ OutputFormat outformat = OutputFormat.createPrettyPrint();
+ outformat.setEncoding("UTF-8");
+ XMLWriter writer = new XMLWriter(outStream, outformat);
+ writer.write(xmlContent);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Copy the input stream into the output stream.
+ *
+ * @param inStream
+ * The source stream.
+ * @param outStream
+ * The destination stream.
+ * @return <b>true</b> if the operation succeed, else return <b>false</b>.
+ */
+ public static boolean copyStream(InputStream inStream, OutputStream outStream) {
+ try {
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = inStream.read(buffer)) >= 0) {
+ outStream.write(buffer, 0, bytesRead);
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+}
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
index 0000000000..12a5a55ffa
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/TargetMode.java
@@ -0,0 +1,32 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Specifies whether the target of a PackageRelationship is inside or outside a
+ * Package.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public enum TargetMode {
+ /** The relationship references a resource that is external to the package. */
+ INTERNAL,
+ /** The relationship references a part that is inside the package. */
+ EXTERNAL
+}
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
index 0000000000..f0db2ab283
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java
@@ -0,0 +1,464 @@
+/* ====================================================================
+ 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.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.Logger;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.FileHelper;
+import org.apache.poi.openxml4j.opc.internal.MemoryPackagePart;
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
+import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller;
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
+import org.apache.poi.openxml4j.util.ZipEntrySource;
+import org.apache.poi.openxml4j.util.ZipFileZipEntrySource;
+import org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource;
+
+/**
+ * Physical zip package.
+ *
+ * @author Julien Chable
+ * @version 0.2
+ */
+public final class ZipPackage extends Package {
+
+ private static Logger logger = Logger.getLogger("org.openxml4j");
+
+ /**
+ * Zip archive, as either a file on disk,
+ * or a stream
+ */
+ private final ZipEntrySource zipArchive;
+
+ /**
+ * Constructor. Creates a new ZipPackage.
+ */
+ public ZipPackage() {
+ super(defaultPackageAccess);
+ this.zipArchive = null;
+ }
+
+ /**
+ * Constructor. <b>Operation not supported.</b>
+ *
+ * @param in
+ * Zip input stream to load.
+ * @param access
+ * @throws IllegalArgumentException
+ * If the specified input stream not an instance of
+ * ZipInputStream.
+ */
+ ZipPackage(InputStream in, PackageAccess access) throws IOException {
+ super(access);
+ this.zipArchive = new ZipInputStreamZipEntrySource(
+ new ZipInputStream(in)
+ );
+ }
+
+ /**
+ * Constructor. Opens a Zip based Open XML document.
+ *
+ * @param path
+ * The path of the file to open or create.
+ * @param access
+ * The package access mode.
+ * @throws InvalidFormatException
+ * If the content type part parsing encounters an error.
+ */
+ ZipPackage(String path, PackageAccess access) throws InvalidFormatException {
+ super(access);
+
+ ZipFile zipFile = ZipHelper.openZipFile(path);
+ if (zipFile == null)
+ throw new InvalidOperationException(
+ "Can't open the specified file: '" + path + "'");
+ this.zipArchive = new ZipFileZipEntrySource(zipFile);
+ }
+
+ /**
+ * Retrieves the parts from this package. We assume that the package has not
+ * been yet inspect to retrieve all the parts, this method will open the
+ * archive and look for all parts contain inside it. If the package part
+ * list is not empty, it will be emptied.
+ *
+ * @return All parts contain in this package.
+ * @throws InvalidFormatException
+ * Throws if the package is not valid.
+ */
+ @Override
+ protected PackagePart[] getPartsImpl() throws InvalidFormatException {
+ if (this.partList == null) {
+ // The package has just been created, we create an empty part
+ // list.
+ this.partList = new PackagePartCollection();
+ }
+
+ if (this.zipArchive == null) {
+ return this.partList.values().toArray(
+ new PackagePart[this.partList.values().size()]);
+ } else {
+ // First we need to parse the content type part
+ Enumeration<? extends ZipEntry> entries = this.zipArchive.getEntries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (entry.getName().equals(
+ ContentTypeManager.CONTENT_TYPES_PART_NAME)) {
+ try {
+ this.contentTypeManager = new ZipContentTypeManager(
+ getZipArchive().getInputStream(entry), this);
+ } catch (IOException e) {
+ throw new InvalidFormatException(e.getMessage());
+ }
+ break;
+ }
+ }
+
+ // At this point, we should have loaded the content type part
+ if (this.contentTypeManager == null) {
+ throw new InvalidFormatException(
+ "Package should contain a content type part [M1.13]");
+ }
+
+ // Now create all the relationships
+ // (Need to create relationships before other
+ // parts, otherwise we might create a part before
+ // its relationship exists, and then it won't tie up)
+ entries = this.zipArchive.getEntries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ PackagePartName partName = buildPartName(entry);
+ if(partName == null) continue;
+
+ // Only proceed for Relationships at this stage
+ String contentType = contentTypeManager.getContentType(partName);
+ if (contentType != null && contentType.equals(ContentTypes.RELATIONSHIPS_PART)) {
+ try {
+ partList.put(partName, new ZipPackagePart(this, entry,
+ partName, contentType));
+ } catch (InvalidOperationException e) {
+ throw new InvalidFormatException(e.getMessage());
+ }
+ }
+ }
+
+ // Then we can go through all the other parts
+ entries = this.zipArchive.getEntries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ PackagePartName partName = buildPartName(entry);
+ if(partName == null) continue;
+
+ String contentType = contentTypeManager
+ .getContentType(partName);
+ if (contentType != null && contentType.equals(ContentTypes.RELATIONSHIPS_PART)) {
+ // Already handled
+ }
+ else if (contentType != null) {
+ try {
+ partList.put(partName, new ZipPackagePart(this, entry,
+ partName, contentType));
+ } catch (InvalidOperationException e) {
+ throw new InvalidFormatException(e.getMessage());
+ }
+ } else {
+ throw new InvalidFormatException(
+ "The part "
+ + partName.getURI().getPath()
+ + " does not have any content type ! Rule: Package require content types when retrieving a part from a package. [M.1.14]");
+ }
+ }
+
+ return (ZipPackagePart[]) partList.values().toArray(
+ new ZipPackagePart[partList.size()]);
+ }
+ }
+
+ /**
+ * Builds a PackagePartName for the given ZipEntry,
+ * or null if it's the content types / invalid part
+ */
+ private PackagePartName buildPartName(ZipEntry entry) {
+ try {
+ // We get an error when we parse [Content_Types].xml
+ // because it's not a valid URI.
+ if (entry.getName().equals(
+ ContentTypeManager.CONTENT_TYPES_PART_NAME)) {
+ return null;
+ } else {
+ return PackagingURIHelper.createPartName(ZipHelper
+ .getOPCNameFromZipItemName(entry.getName()));
+ }
+ } catch (Exception e) {
+ // We assume we can continue, even in degraded mode ...
+ logger.warn("Entry "
+ + entry.getName()
+ + " is not valid, so this part won't be add to the package.");
+ return null;
+ }
+ }
+
+ /**
+ * Create a new MemoryPackagePart from the specified URI and content type
+ *
+ *
+ * aram partName The part URI.
+ *
+ * @param contentType
+ * The part content type.
+ * @return The newly created zip package part, else <b>null</b>.
+ */
+ @Override
+ protected PackagePart createPartImpl(PackagePartName partName,
+ String contentType, boolean loadRelationships) {
+ if (contentType == null)
+ throw new IllegalArgumentException("contentType");
+
+ if (partName == null)
+ throw new IllegalArgumentException("partName");
+
+ try {
+ return new MemoryPackagePart(this, partName, contentType,
+ loadRelationships);
+ } catch (InvalidFormatException e) {
+ System.err.println(e);
+ return null;
+ }
+ }
+
+ /**
+ * Delete a part from the package
+ *
+ * @throws IllegalArgumentException
+ * Throws if the part URI is nulll or invalid.
+ */
+ @Override
+ protected void removePartImpl(PackagePartName partName) {
+ if (partName == null)
+ throw new IllegalArgumentException("partUri");
+ }
+
+ /**
+ * Flush the package. Do nothing.
+ */
+ @Override
+ protected void flushImpl() {
+ // Do nothing
+ }
+
+ /**
+ * Close and save the package.
+ *
+ * @see #close()
+ */
+ @Override
+ protected void closeImpl() throws IOException {
+ // Flush the package
+ flush();
+
+ // Save the content
+ if (this.originalPackagePath != null
+ && !"".equals(this.originalPackagePath)) {
+ File targetFile = new File(this.originalPackagePath);
+ if (targetFile.exists()) {
+ // Case of a package previously open
+
+ File tempFile = File.createTempFile(
+ generateTempFileName(FileHelper
+ .getDirectory(targetFile)), ".tmp");
+
+ // Save the final package to a temporary file
+ try {
+ save(tempFile);
+ this.zipArchive.close(); // Close the zip archive to be
+ // able to delete it
+ FileHelper.copyFile(tempFile, targetFile);
+ } finally {
+ // Either the save operation succeed or not, we delete the
+ // temporary file
+ if (!tempFile.delete()) {
+ logger
+ .warn("The temporary file: '"
+ + targetFile.getAbsolutePath()
+ + "' cannot be deleted ! Make sure that no other application use it.");
+ }
+ }
+ } else {
+ throw new InvalidOperationException(
+ "Can't close a package not previously open with the open() method !");
+ }
+ }
+ }
+
+ /**
+ * Create a unique identifier to be use as a temp file name.
+ *
+ * @return A unique identifier use to be use as a temp file name.
+ */
+ private synchronized String generateTempFileName(File directory) {
+ File tmpFilename;
+ do {
+ tmpFilename = new File(directory.getAbsoluteFile() + File.separator
+ + "OpenXML4J" + System.nanoTime());
+ } while (tmpFilename.exists());
+ return FileHelper.getFilename(tmpFilename.getAbsoluteFile());
+ }
+
+ /**
+ * Close the package without saving the document. Discard all the changes
+ * made to this package.
+ */
+ @Override
+ protected void revertImpl() {
+ try {
+ if (this.zipArchive != null)
+ this.zipArchive.close();
+ } catch (IOException e) {
+ // Do nothing, user dont have to know
+ }
+ }
+
+ /**
+ * Implement the getPart() method to retrieve a part from its URI in the
+ * current package
+ *
+ *
+ * @see #getPart(URI)
+ */
+ @Override
+ protected PackagePart getPartImpl(PackagePartName partName) {
+ if (partList.containsKey(partName)) {
+ return partList.get(partName);
+ }
+ return null;
+ }
+
+ /**
+ * Save this package into the specified stream
+ *
+ *
+ * @param outputStream
+ * The stream use to save this package.
+ *
+ * @see #save(OutputStream)
+ * @see #saveInZip(ZipOutputStream)
+ */
+ @Override
+ public void saveImpl(OutputStream outputStream) {
+ // Check that the document was open in write mode
+ throwExceptionIfReadOnly();
+ ZipOutputStream zos = null;
+
+ try {
+ if (!(outputStream instanceof ZipOutputStream))
+ zos = new ZipOutputStream(outputStream);
+ else
+ zos = (ZipOutputStream) outputStream;
+
+ // If the core properties part does not exist in the part list,
+ // we save it as well
+ if (this.getPartsByRelationshipType(
+ PackageRelationshipTypes.CORE_PROPERTIES).size() == 0) {
+ logger.debug("Save core properties part");
+
+ // We have to save the core properties part ...
+ new ZipPackagePropertiesMarshaller().marshall(
+ this.packageProperties, zos);
+ // ... and to add its relationship ...
+ this.relationships.addRelationship(this.packageProperties
+ .getPartName().getURI(), TargetMode.INTERNAL,
+ PackageRelationshipTypes.CORE_PROPERTIES, null);
+ // ... and the content if it has not been added yet.
+ if (!this.contentTypeManager
+ .isContentTypeRegister(ContentTypes.CORE_PROPERTIES_PART)) {
+ this.contentTypeManager.addContentType(
+ this.packageProperties.getPartName(),
+ ContentTypes.CORE_PROPERTIES_PART);
+ }
+ }
+
+ // Save package relationships part.
+ logger.debug("Save package relationships");
+ ZipPartMarshaller.marshallRelationshipPart(this.getRelationships(),
+ PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_PART_NAME,
+ zos);
+
+ // Save content type part.
+ logger.debug("Save content types part");
+ this.contentTypeManager.save(zos);
+
+ // Save parts.
+ for (PackagePart part : getParts()) {
+ // If the part is a relationship part, we don't save it, it's
+ // the source part that will do the job.
+ if (part.isRelationshipPart())
+ continue;
+
+ logger.debug("Save part '"
+ + ZipHelper.getZipItemNameFromOPCName(part
+ .getPartName().getName()) + "'");
+ PartMarshaller marshaller = partMarshallers
+ .get(part.contentType);
+ if (marshaller != null) {
+ if (!marshaller.marshall(part, zos)) {
+ throw new OpenXML4JException(
+ "The part "
+ + part.getPartName().getURI()
+ + " fail to be saved in the stream with marshaller "
+ + marshaller);
+ }
+ } else {
+ if (!defaultPartMarshaller.marshall(part, zos))
+ throw new OpenXML4JException(
+ "The part "
+ + part.getPartName().getURI()
+ + " fail to be saved in the stream with marshaller "
+ + defaultPartMarshaller);
+ }
+ }
+ zos.close();
+ } catch (Exception e) {
+ logger
+ .error("Fail to save: an error occurs while saving the package : "
+ + e.getMessage());
+ }
+ }
+
+ /**
+ * Get the zip archive
+ *
+ * @return The zip archive.
+ */
+ public ZipEntrySource getZipArchive() {
+ return zipArchive;
+ }
+}
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
index 0000000000..2c05898d14
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java
@@ -0,0 +1,135 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
+
+/**
+ * Zip implementation of a PackagePart.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ * @see PackagePart
+ */
+public class ZipPackagePart extends PackagePart {
+
+ /**
+ * The zip entry corresponding to this part.
+ */
+ private ZipEntry zipEntry;
+
+ /**
+ * Constructor.
+ *
+ * @param container
+ * The container package.
+ * @param partName
+ * Part name.
+ * @param contentType
+ * Content type.
+ * @throws InvalidFormatException
+ * Throws if the content of this part invalid.
+ */
+ public ZipPackagePart(Package container, PackagePartName partName,
+ String contentType) throws InvalidFormatException {
+ super(container, partName, contentType);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param container
+ * The container package.
+ * @param zipEntry
+ * The zip entry corresponding to this part.
+ * @param partName
+ * The part name.
+ * @param contentType
+ * Content type.
+ * @throws InvalidFormatException
+ * Throws if the content of this part is invalid.
+ */
+ public ZipPackagePart(Package container, ZipEntry zipEntry,
+ PackagePartName partName, String contentType)
+ throws InvalidFormatException {
+ super(container, partName, contentType);
+ this.zipEntry = zipEntry;
+ }
+
+ /**
+ * Get the zip entry of this part.
+ *
+ * @return The zip entry in the zip structure coresponding to this part.
+ */
+ public ZipEntry getZipArchive() {
+ return zipEntry;
+ }
+
+ /**
+ * Implementation of the getInputStream() which return the inputStream of
+ * this part zip entry.
+ *
+ * @return Input stream of this part zip entry.
+ */
+ @Override
+ protected InputStream getInputStreamImpl() throws IOException {
+ // We use the getInputStream() method from java.util.zip.ZipFile
+ // class which return an InputStream to this part zip entry.
+ return ((ZipPackage) container).getZipArchive()
+ .getInputStream(zipEntry);
+ }
+
+ /**
+ * Implementation of the getOutputStream(). Return <b>null</b>. Normally
+ * will never be called since the MemoryPackage is use instead.
+ *
+ * @return <b>null</b>
+ */
+ @Override
+ protected OutputStream getOutputStreamImpl() {
+ return null;
+ }
+
+ @Override
+ public boolean save(OutputStream os) throws OpenXML4JException {
+ return new ZipPartMarshaller().marshall(this, os);
+ }
+
+ @Override
+ public boolean load(InputStream ios) throws InvalidFormatException {
+ throw new InvalidOperationException("Method not implemented !");
+ }
+
+ @Override
+ public void close() {
+ throw new InvalidOperationException("Method not implemented !");
+ }
+
+ @Override
+ public void flush() {
+ throw new InvalidOperationException("Method not implemented !");
+ }
+}
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
index 0000000000..18a6ca4ab4
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentType.java
@@ -0,0 +1,224 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Hashtable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+
+/**
+ * Represents a immutable MIME ContentType value (RFC 2616 �3.7)
+ *
+ * media-type = type "/" subtype *( ";" parameter ) type = token<br>
+ * subtype = token<br>
+ *
+ * Rule M1.13 : Package implementers shall only create and only recognize parts
+ * with a content type; format designers shall specify a content type for each
+ * part included in the format. Content types for package parts shall fit the
+ * definition and syntax for media types as specified in RFC 2616,��3.7.
+ *
+ * Rule M1.14: Content types shall not use linear white space either between the
+ * type and subtype or between an attribute and its value. Content types also
+ * shall not have leading or trailing white spaces. Package implementers shall
+ * create only such content types and shall require such content types when
+ * retrieving a part from a package; format designers shall specify only such
+ * content types for inclusion in the format.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ *
+ * @see http://www.ietf.org/rfc/rfc2045.txt
+ * @see http://www.ietf.org/rfc/rfc2616.txt
+ */
+public final class ContentType {
+
+ /**
+ * Type in Type/Subtype.
+ */
+ private String type;
+
+ /**
+ * Subtype
+ */
+ private String subType;
+
+ /**
+ * Parameters
+ */
+ private Hashtable<String, String> parameters;
+
+ /**
+ * Media type compiled pattern for parameters.
+ */
+ private final static Pattern patternMediaType;
+
+ static {
+ /*
+ * token = 1*<any CHAR except CTLs or separators>
+ *
+ * separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" |
+ * <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
+ *
+ * CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
+ *
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ */
+ String token = "[\\x21-\\x7E&&[^\\(\\)<>@,;:\\\\/\"\\[\\]\\?={}\\x20\\x09]]";
+
+ /*
+ * parameter = attribute "=" value
+ *
+ * attribute = token
+ *
+ * value = token | quoted-string
+ */
+ // Keep for future use with parameter:
+ // String parameter = "(" + token + "+)=(\"?" + token + "+\"?)";
+ /*
+ * Pattern for media type.
+ *
+ * Don't allow comment, rule M1.15: The package implementer shall
+ * require a content type that does not include comments and the format
+ * designer shall specify such a content type.
+ *
+ * comment = "(" *( ctext | quoted-pair | comment ) ")"
+ *
+ * ctext = <any TEXT excluding "(" and ")">
+ *
+ * TEXT = <any OCTET except CTLs, but including LWS>
+ *
+ * LWS = [CRLF] 1*( SP | HT )
+ *
+ * CR = <US-ASCII CR, carriage return (13)>
+ *
+ * LF = <US-ASCII LF, linefeed (10)>
+ *
+ * SP = <US-ASCII SP, space (32)>
+ *
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ *
+ * quoted-pair = "\" CHAR
+ */
+
+ // Keep for future use with parameter:
+ // patternMediaType = Pattern.compile("^(" + token + "+)/(" + token
+ // + "+)(;" + parameter + ")*$");
+ patternMediaType = Pattern.compile("^(" + token + "+)/(" + token
+ + "+)$");
+ }
+
+ /**
+ * Constructor. Check the input with the RFC 2616 grammar.
+ *
+ * @param contentType
+ * The content type to store.
+ * @throws InvalidFormatException
+ * If the specified content type is not valid with RFC 2616.
+ */
+ public ContentType(String contentType) throws InvalidFormatException {
+ // Conversion en US-ASCII
+ String contentTypeASCII = null;
+ try {
+ contentTypeASCII = new String(contentType.getBytes(), "US-ASCII");
+ } catch (UnsupportedEncodingException e) {
+ throw new InvalidFormatException(
+ "The specified content type is not an ASCII value.");
+ }
+
+ Matcher mMediaType = patternMediaType.matcher(contentTypeASCII);
+ if (!mMediaType.matches())
+ throw new InvalidFormatException(
+ "The specified content type '"
+ + contentType
+ + "' is not compliant with RFC 2616: malformed content type.");
+
+ // Type/subtype
+ if (mMediaType.groupCount() >= 2) {
+ this.type = mMediaType.group(1);
+ this.subType = mMediaType.group(2);
+ // Parameters
+ this.parameters = new Hashtable<String, String>(1);
+ for (int i = 4; i <= mMediaType.groupCount()
+ && (mMediaType.group(i) != null); i += 2) {
+ this.parameters.put(mMediaType.group(i), mMediaType
+ .group(i + 1));
+ }
+ }
+ }
+
+ @Override
+ public final String toString() {
+ StringBuffer retVal = new StringBuffer();
+ retVal.append(this.getType());
+ retVal.append("/");
+ retVal.append(this.getSubType());
+ // Keep for future implementation if needed
+ // for (String key : parameters.keySet()) {
+ // retVal.append(";");
+ // retVal.append(key);
+ // retVal.append("=");
+ // retVal.append(parameters.get(key));
+ // }
+ return retVal.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (!(obj instanceof ContentType))
+ || (this.toString().equalsIgnoreCase(obj.toString()));
+ }
+
+ @Override
+ public int hashCode() {
+ return this.toString().hashCode();
+ }
+
+ /* Getters */
+
+ /**
+ * Get the subtype.
+ *
+ * @return The subtype of this content type.
+ */
+ public String getSubType() {
+ return this.subType;
+ }
+
+ /**
+ * Get the type.
+ *
+ * @return The type of this content type.
+ */
+ public String getType() {
+ return this.type;
+ }
+
+ /**
+ * Gets the value associated to the specified key.
+ *
+ * @param key
+ * The key of the key/value pair.
+ * @return The value associated to the specified key.
+ */
+ public String getParameters(String key) {
+ return parameters.get(key);
+ }
+}
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
index 0000000000..5f96d180e4
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentTypeManager.java
@@ -0,0 +1,498 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.Logger;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+import org.dom4j.QName;
+import org.dom4j.io.SAXReader;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
+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.PackagingURIHelper;
+
+/**
+ * Manage package content types ([Content_Types].xml part).
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public abstract class ContentTypeManager {
+
+ protected static Logger logger = Logger.getLogger("org.openxml4j");
+
+ /**
+ * Reference to the package using this content type manager.
+ */
+ protected Package container;
+
+ /**
+ * Content type part name.
+ */
+ public static final String CONTENT_TYPES_PART_NAME = "[Content_Types].xml";
+
+ /**
+ * Content type namespace
+ */
+ public static final String TYPES_NAMESPACE_URI = "http://schemas.openxmlformats.org/package/2006/content-types";
+
+ /* Xml elements in content type part */
+
+ private static final String TYPES_TAG_NAME = "Types";
+
+ private static final String DEFAULT_TAG_NAME = "Default";
+
+ private static final String EXTENSION_ATTRIBUTE_NAME = "Extension";
+
+ private static final String CONTENT_TYPE_ATTRIBUTE_NAME = "ContentType";
+
+ private static final String OVERRIDE_TAG_NAME = "Override";
+
+ private static final String PART_NAME_ATTRIBUTE_NAME = "PartName";
+
+ /**
+ * Default content type tree. <Extension, ContentType>
+ */
+ private TreeMap<String, String> defaultContentType;
+
+ /**
+ * Override content type tree.
+ */
+ private TreeMap<PackagePartName, String> overrideContentType;
+
+ /**
+ * Constructor. Parses the content of the specified input stream.
+ *
+ * @param archive
+ * If different of <i>null</i> then the content types part is
+ * retrieve and parse.
+ * @throws InvalidFormatException
+ * If the content types part content is not valid.
+ */
+ public ContentTypeManager(InputStream in, Package pkg)
+ throws InvalidFormatException {
+ this.container = pkg;
+ this.defaultContentType = new TreeMap<String, String>();
+ if (in != null) {
+ try {
+ parseContentTypesFile(in);
+ } catch (InvalidFormatException e) {
+ throw new InvalidFormatException(
+ "Can't read content types part !");
+ }
+ }
+ }
+
+ /**
+ * Build association extention-> content type (will be stored in
+ * [Content_Types].xml) for example ContentType="image/png" Extension="png"
+ *
+ * [M2.8]: When adding a new part to a package, the package implementer
+ * shall ensure that a content type for that part is specified in the
+ * Content Types stream; the package implementer shall perform the steps
+ * described in��9.1.2.3:
+ *
+ * 1. Get the extension from the part name by taking the substring to the
+ * right of the rightmost occurrence of the dot character (.) from the
+ * rightmost segment.
+ *
+ * 2. If a part name has no extension, a corresponding Override element
+ * shall be added to the Content Types stream.
+ *
+ * 3. Compare the resulting extension with the values specified for the
+ * Extension attributes of the Default elements in the Content Types stream.
+ * The comparison shall be case-insensitive ASCII.
+ *
+ * 4. If there is a Default element with a matching Extension attribute,
+ * then the content type of the new part shall be compared with the value of
+ * the ContentType attribute. The comparison might be case-sensitive and
+ * include every character regardless of the role it plays in the
+ * content-type grammar of RFC 2616, or it might follow the grammar of RFC
+ * 2616.
+ *
+ * a. If the content types match, no further action is required.
+ *
+ * b. If the content types do not match, a new Override element shall be
+ * added to the Content Types stream. .
+ *
+ * 5. If there is no Default element with a matching Extension attribute, a
+ * new Default element or Override element shall be added to the Content
+ * Types stream.
+ *
+ *
+ * @param partUri
+ * the uri that will be stored
+ * @return <b>false</b> if an error occured.
+ */
+ public void addContentType(PackagePartName partName, String contentType) {
+ boolean defaultCTExists = false;
+ String extension = partName.getExtension().toLowerCase();
+ if ((extension.length() == 0)
+ || (this.defaultContentType.containsKey(extension) && !(defaultCTExists = this.defaultContentType
+ .containsValue(contentType))))
+ this.addOverrideContentType(partName, contentType);
+ else if (!defaultCTExists)
+ this.addDefaultContentType(extension, contentType);
+ }
+
+ /**
+ * Add an override content type for a specific part.
+ *
+ * @param partName
+ * Name of the part.
+ * @param contentType
+ * Content type of the part.
+ */
+ private void addOverrideContentType(PackagePartName partName,
+ String contentType) {
+ if (overrideContentType == null)
+ overrideContentType = new TreeMap<PackagePartName, String>();
+ overrideContentType.put(partName, contentType);
+ }
+
+ /**
+ * Add a content type associated with the specified extension.
+ *
+ * @param extension
+ * The part name extension to bind to a content type.
+ * @param contentType
+ * The content type associated with the specified extension.
+ */
+ private void addDefaultContentType(String extension, String contentType) {
+ // Remark : Originally the latest parameter was :
+ // contentType.toLowerCase(). Change due to a request ID 1996748.
+ defaultContentType.put(extension.toLowerCase(), contentType);
+ }
+
+ /**
+ * Delete a content type based on the specified part name. If the specified
+ * part name is register with an override content type, then this content
+ * type is remove, else the content type is remove in the default content
+ * type list if it exists and if no part is associated with it yet.
+ *
+ * Check rule M2.4: The package implementer shall require that the Content
+ * Types stream contain one of the following for every part in the package:
+ * One matching Default element One matching Override element Both a
+ * matching Default element and a matching Override element, in which case
+ * the Override element takes precedence.
+ *
+ * @param partUri
+ * The part URI associated with the override content type to
+ * delete.
+ * @exception InvalidOperationException
+ * Throws if
+ */
+ public void removeContentType(PackagePartName partName)
+ throws InvalidOperationException {
+ if (partName == null)
+ throw new IllegalArgumentException("partName");
+
+ /* Override content type */
+ if (this.overrideContentType != null
+ && (this.overrideContentType.get(partName) != null)) {
+ // Remove the override definition for the specified part.
+ this.overrideContentType.remove(partName);
+ return;
+ }
+
+ /* Default content type */
+ String extensionToDelete = partName.getExtension();
+ boolean deleteDefaultContentTypeFlag = true;
+ if (this.container != null) {
+ try {
+ for (PackagePart part : this.container.getParts()) {
+ if (!part.getPartName().equals(partName)
+ && part.getPartName().getExtension()
+ .equalsIgnoreCase(extensionToDelete)) {
+ deleteDefaultContentTypeFlag = false;
+ break;
+ }
+ }
+ } catch (InvalidFormatException e) {
+ throw new InvalidOperationException(e.getMessage());
+ }
+ }
+
+ // Remove the default content type, no other part use this content type.
+ if (deleteDefaultContentTypeFlag) {
+ this.defaultContentType.remove(extensionToDelete);
+ }
+
+ /*
+ * Check rule 2.4: The package implementer shall require that the
+ * Content Types stream contain one of the following for every part in
+ * the package: One matching Default element One matching Override
+ * element Both a matching Default element and a matching Override
+ * element, in which case the Override element takes precedence.
+ */
+ if (this.container != null) {
+ try {
+ for (PackagePart part : this.container.getParts()) {
+ if (!part.getPartName().equals(partName)
+ && this.getContentType(part.getPartName()) == null)
+ throw new InvalidOperationException(
+ "Rule M2.4 is not respected: Nor a default element or override element is associated with the part: "
+ + part.getPartName().getName());
+ }
+ } catch (InvalidFormatException e) {
+ throw new InvalidOperationException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Check if the specified content type is already register.
+ *
+ * @param contentType
+ * The content type to check.
+ * @return <code>true</code> if the specified content type is already
+ * register, then <code>false</code>.
+ */
+ public boolean isContentTypeRegister(String contentType) {
+ if (contentType == null)
+ throw new IllegalArgumentException("contentType");
+
+ return (this.defaultContentType.values().contains(contentType) || (this.overrideContentType != null && this.overrideContentType
+ .values().contains(contentType)));
+ }
+
+ /**
+ * Get the content type for the specified part, if any.
+ *
+ * Rule [M2.9]: To get the content type of a part, the package implementer
+ * shall perform the steps described in��9.1.2.4:
+ *
+ * 1. Compare the part name with the values specified for the PartName
+ * attribute of the Override elements. The comparison shall be
+ * case-insensitive ASCII.
+ *
+ * 2. If there is an Override element with a matching PartName attribute,
+ * return the value of its ContentType attribute. No further action is
+ * required.
+ *
+ * 3. If there is no Override element with a matching PartName attribute,
+ * then a. Get the extension from the part name by taking the substring to
+ * the right of the rightmost occurrence of the dot character (.) from the
+ * rightmost segment. b. Check the Default elements of the Content Types
+ * stream, comparing the extension with the value of the Extension
+ * attribute. The comparison shall be case-insensitive ASCII.
+ *
+ * 4. If there is a Default element with a matching Extension attribute,
+ * return the value of its ContentType attribute. No further action is
+ * required.
+ *
+ * 5. If neither Override nor Default elements with matching attributes are
+ * found for the specified part name, the implementation shall not map this
+ * part name to a part.
+ *
+ * @param partUri
+ * The URI part to check.
+ * @return The content type associated with the URI (in case of an override
+ * content type) or the extension (in case of default content type),
+ * else <code>null</code>.
+ *
+ * @exception OpenXML4JRuntimeException
+ * Throws if the content type manager is not able to find the
+ * content from an existing part.
+ */
+ public String getContentType(PackagePartName partName) {
+ if (partName == null)
+ throw new IllegalArgumentException("partName");
+
+ if ((this.overrideContentType != null)
+ && this.overrideContentType.containsKey(partName))
+ return this.overrideContentType.get(partName);
+
+ String extension = partName.getExtension().toLowerCase();
+ if (this.defaultContentType.containsKey(extension))
+ return this.defaultContentType.get(extension);
+
+ /*
+ * [M2.4] : The package implementer shall require that the Content Types
+ * stream contain one of the following for every part in the package:
+ * One matching Default element, One matching Override element, Both a
+ * matching Default element and a matching Override element, in which
+ * case the Override element takes precedence.
+ */
+ if (this.container != null && this.container.getPart(partName) != null) {
+ throw new OpenXML4JRuntimeException(
+ "Rule M2.4 exception : this error should NEVER happen, if so please send a mail to the developers team, thanks !");
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Clear all content types.
+ */
+ public void clearAll() {
+ this.defaultContentType.clear();
+ if (this.overrideContentType != null)
+ this.overrideContentType.clear();
+ }
+
+ /**
+ * Clear all override content types.
+ *
+ */
+ public void clearOverrideContentTypes() {
+ if (this.overrideContentType != null)
+ this.overrideContentType.clear();
+ }
+
+ /**
+ * Parse the content types part.
+ *
+ * @throws InvalidFormatException
+ * Throws if the content type doesn't exist or the XML format is
+ * invalid.
+ */
+ private void parseContentTypesFile(InputStream in)
+ throws InvalidFormatException {
+ try {
+ SAXReader xmlReader = new SAXReader();
+ Document xmlContentTypetDoc = xmlReader.read(in);
+
+ // Default content types
+ List defaultTypes = xmlContentTypetDoc.getRootElement().elements(
+ DEFAULT_TAG_NAME);
+ Iterator elementIteratorDefault = defaultTypes.iterator();
+ while (elementIteratorDefault.hasNext()) {
+ Element element = (Element) elementIteratorDefault.next();
+ String extension = element.attribute(EXTENSION_ATTRIBUTE_NAME)
+ .getValue();
+ String contentType = element.attribute(
+ CONTENT_TYPE_ATTRIBUTE_NAME).getValue();
+ addDefaultContentType(extension, contentType);
+ }
+
+ // Overriden content types
+ List overrideTypes = xmlContentTypetDoc.getRootElement().elements(
+ OVERRIDE_TAG_NAME);
+ Iterator elementIteratorOverride = overrideTypes.iterator();
+ while (elementIteratorOverride.hasNext()) {
+ Element element = (Element) elementIteratorOverride.next();
+ URI uri = new URI(element.attribute(PART_NAME_ATTRIBUTE_NAME)
+ .getValue());
+ PackagePartName partName = PackagingURIHelper
+ .createPartName(uri);
+ String contentType = element.attribute(
+ CONTENT_TYPE_ATTRIBUTE_NAME).getValue();
+ addOverrideContentType(partName, contentType);
+ }
+ } catch (URISyntaxException urie) {
+ throw new InvalidFormatException(urie.getMessage());
+ } catch (DocumentException e) {
+ throw new InvalidFormatException(e.getMessage());
+ }
+ }
+
+ /**
+ * Save the contents type part.
+ *
+ * @param outStream
+ * The output stream use to save the XML content of the content
+ * types part.
+ * @return <b>true</b> if the operation success, else <b>false</b>.
+ */
+ public boolean save(OutputStream outStream) {
+ Document xmlOutDoc = DocumentHelper.createDocument();
+
+ // Building namespace
+ Namespace dfNs = Namespace.get("", TYPES_NAMESPACE_URI);
+ Element typesElem = xmlOutDoc
+ .addElement(new QName(TYPES_TAG_NAME, dfNs));
+
+ // Adding default types
+ for (Entry<String, String> entry : defaultContentType.entrySet()) {
+ appendDefaultType(typesElem, entry);
+ }
+
+ // Adding specific types if any exist
+ if (overrideContentType != null) {
+ for (Entry<PackagePartName, String> entry : overrideContentType
+ .entrySet()) {
+ appendSpecificTypes(typesElem, entry);
+ }
+ }
+ xmlOutDoc.normalize();
+
+ // Save content in the specified output stream
+ return this.saveImpl(xmlOutDoc, outStream);
+ }
+
+ /**
+ * Use to append specific type XML elements, use by the save() method.
+ *
+ * @param root
+ * XML parent element use to append this override type element.
+ * @param entry
+ * The values to append.
+ * @see #save(ZipOutputStream)
+ */
+ private void appendSpecificTypes(Element root,
+ Entry<PackagePartName, String> entry) {
+ root.addElement(OVERRIDE_TAG_NAME).addAttribute(
+ PART_NAME_ATTRIBUTE_NAME,
+ ((PackagePartName) entry.getKey()).getName()).addAttribute(
+ CONTENT_TYPE_ATTRIBUTE_NAME, (String) entry.getValue());
+ }
+
+ /**
+ * Use to append default types XML elements, use by the save() metid.
+ *
+ * @param root
+ * XML parent element use to append this default type element.
+ * @param entry
+ * The values to append.
+ * @see #save(ZipOutputStream)
+ */
+ private void appendDefaultType(Element root, Entry<String, String> entry) {
+ root.addElement(DEFAULT_TAG_NAME).addAttribute(
+ EXTENSION_ATTRIBUTE_NAME, (String) entry.getKey())
+ .addAttribute(CONTENT_TYPE_ATTRIBUTE_NAME,
+ (String) entry.getValue());
+
+ }
+
+ /**
+ * Specific implementation of the save method. Call by the save() method,
+ * call before exiting.
+ *
+ * @param out
+ * The output stream use to write the content type XML.
+ */
+ public abstract boolean saveImpl(Document content, OutputStream out);
+}
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
index 0000000000..ad31157372
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/FileHelper.java
@@ -0,0 +1,91 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+/**
+ * Provide useful method to manage file.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public final class FileHelper {
+
+ /**
+ * Get the directory part of the specified file path.
+ *
+ * @param f
+ * File to process.
+ * @return The directory path from the specified
+ */
+ public static File getDirectory(File f) {
+ if (f != null) {
+ String path = f.getPath();
+ int len = path.length();
+ int num2 = len;
+ while (--num2 >= 0) {
+ char ch1 = path.charAt(num2);
+ if (ch1 == File.separatorChar) {
+ return new File(path.substring(0, num2));
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Copy a file.
+ *
+ * @param in
+ * The source file.
+ * @param out
+ * The target location.
+ * @throws IOException
+ * If an I/O error occur.
+ */
+ public static void copyFile(File in, File out) throws IOException {
+ FileChannel sourceChannel = new FileInputStream(in).getChannel();
+ FileChannel destinationChannel = new FileOutputStream(out).getChannel();
+ sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);
+ sourceChannel.close();
+ destinationChannel.close();
+ }
+
+ /**
+ * Get file name from the specified File object.
+ */
+ public static String getFilename(File file) {
+ if (file != null) {
+ String path = file.getPath();
+ int len = path.length();
+ int num2 = len;
+ while (--num2 >= 0) {
+ char ch1 = path.charAt(num2);
+ if (ch1 == File.separatorChar)
+ return path.substring(num2 + 1, len);
+ }
+ }
+ return "";
+ }
+
+}
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
index 0000000000..8f29a45875
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePart.java
@@ -0,0 +1,126 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+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.internal.marshallers.ZipPartMarshaller;
+
+/**
+ * Memory version of a package part. Use to
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public final class MemoryPackagePart extends PackagePart {
+
+ /**
+ * Storage for the part data.
+ */
+ protected byte[] data;
+
+ /**
+ * Size of data.
+ */
+ protected int length;
+
+ /**
+ * Constructor.
+ *
+ * @param pack
+ * The owner package.
+ * @param partName
+ * The part name.
+ * @param contentType
+ * The content type.
+ * @throws InvalidFormatException
+ * If the specified URI is not OPC compliant.
+ */
+ public MemoryPackagePart(Package pack, PackagePartName partName,
+ String contentType) throws InvalidFormatException {
+ super(pack, partName, contentType);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param pack
+ * The owner package.
+ * @param partName
+ * The part name.
+ * @param contentType
+ * The content type.
+ * @param loadRelationships
+ * Specify if the relationships will be loaded.
+ * @throws InvalidFormatException
+ * If the specified URI is not OPC compliant.
+ */
+ public MemoryPackagePart(Package pack, PackagePartName partName,
+ String contentType, boolean loadRelationships)
+ throws InvalidFormatException {
+ super(pack, partName, new ContentType(contentType), loadRelationships);
+ }
+
+ @Override
+ protected InputStream getInputStreamImpl() {
+ // If this part has been created from scratch and/or the data buffer is
+ // not
+ // initialize, so we do it now.
+ if (data == null) {
+ data = new byte[0];
+ }
+ return new ByteArrayInputStream(data);
+ }
+
+ @Override
+ protected OutputStream getOutputStreamImpl() {
+ return new MemoryPackagePartOutputStream(this);
+ }
+
+ public void clear() {
+ data = null;
+ length = 0;
+ }
+
+ @Override
+ public boolean save(OutputStream os) throws OpenXML4JException {
+ return new ZipPartMarshaller().marshall(this, os);
+ }
+
+ @Override
+ public boolean load(InputStream ios) throws InvalidFormatException {
+ throw new InvalidFormatException("Method not implemented");
+ }
+
+ @Override
+ public void close() {
+ // Do nothing
+ }
+
+ @Override
+ public void flush() {
+ // Do nothing
+ }
+}
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
index 0000000000..debe3d185e
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePartOutputStream.java
@@ -0,0 +1,96 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Build an output stream for MemoryPackagePart.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public final class MemoryPackagePartOutputStream extends OutputStream {
+
+ private MemoryPackagePart part;
+
+ private ByteArrayOutputStream buff;
+
+ public MemoryPackagePartOutputStream(MemoryPackagePart part) {
+ this.part = part;
+ buff = new ByteArrayOutputStream();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ buff.write(b);
+ }
+
+ /**
+ * Close this stream and flush the content.
+ * @see #flush()
+ */
+ @Override
+ public void close() throws IOException {
+ this.flush();
+ }
+
+ /**
+ * Flush this output stream. This method is called by the close() method.
+ * Warning : don't call this method for output consistency.
+ * @see #close()
+ */
+ @Override
+ public void flush() throws IOException {
+ buff.flush();
+ if (part.data != null) {
+ byte[] newArray = new byte[part.data.length + buff.size()];
+ // copy the previous contents of part.data in newArray
+ System.arraycopy(part.data, 0, newArray, 0, part.data.length);
+
+ // append the newly added data
+ byte[] buffArr = buff.toByteArray();
+ System.arraycopy(buffArr, 0, newArray, part.data.length,
+ buffArr.length);
+
+ // save the result as new data
+ part.data = newArray;
+ } else {
+ // was empty, just fill it
+ part.data = buff.toByteArray();
+ }
+
+ /*
+ * Clear this streams buffer, in case flush() is called a second time
+ * Fix bug 1921637 - provided by Rainer Schwarze
+ */
+ buff.reset();
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ buff.write(b, off, len);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ buff.write(b);
+ }
+}
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
index 0000000000..a9f60b011a
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java
@@ -0,0 +1,621 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.ContentTypes;
+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.PackageProperties;
+import org.apache.poi.openxml4j.util.Nullable;
+
+/**
+ * Represents the core properties part of a package.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public class PackagePropertiesPart extends PackagePart implements
+ PackageProperties {
+
+ public final static String NAMESPACE_DC_URI = "http://purl.org/dc/elements/1.1/";
+
+ public final static String NAMESPACE_CP_URI = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
+
+ public final static String NAMESPACE_DCTERMS_URI = "http://purl.org/dc/terms/";
+
+ public final static String NAMESPACE_XSI_URI = "http://www.w3.org/2001/XMLSchema-instance";
+
+ /**
+ * Constructor.
+ *
+ * @param pack
+ * Container package.
+ * @param partName
+ * Name of this part.
+ * @throws InvalidFormatException
+ * Throws if the content is invalid.
+ */
+ public PackagePropertiesPart(Package pack, PackagePartName partName)
+ throws InvalidFormatException {
+ super(pack, partName, ContentTypes.CORE_PROPERTIES_PART);
+ }
+
+ /**
+ * A categorization of the content of this package.
+ *
+ * [Example: Example values for this property might include: Resume, Letter,
+ * Financial Forecast, Proposal, Technical Presentation, and so on. This
+ * value might be used by an application's user interface to facilitate
+ * navigation of a large set of documents. end example]
+ */
+ protected Nullable<String> category = new Nullable<String>();
+
+ /**
+ * The status of the content.
+ *
+ * [Example: Values might include "Draft", "Reviewed", and "Final". end
+ * example]
+ */
+ protected Nullable<String> contentStatus = new Nullable<String>();
+
+ /**
+ * The type of content represented, generally defined by a specific use and
+ * intended audience.
+ *
+ * [Example: Values might include "Whitepaper", "Security Bulletin", and
+ * "Exam". end example] [Note: This property is distinct from MIME content
+ * types as defined in RFC 2616. end note]
+ */
+ protected Nullable<String> contentType = new Nullable<String>();
+
+ /**
+ * Date of creation of the resource.
+ */
+ protected Nullable<Date> created = new Nullable<Date>();
+
+ /**
+ * An entity primarily responsible for making the content of the resource.
+ */
+ protected Nullable<String> creator = new Nullable<String>();
+
+ /**
+ * An explanation of the content of the resource.
+ *
+ * [Example: Values might include an abstract, table of contents, reference
+ * to a graphical representation of content, and a free-text account of the
+ * content. end example]
+ */
+ protected Nullable<String> description = new Nullable<String>();
+
+ /**
+ * An unambiguous reference to the resource within a given context.
+ */
+ protected Nullable<String> identifier = new Nullable<String>();
+
+ /**
+ * A delimited set of keywords to support searching and indexing. This is
+ * typically a list of terms that are not available elsewhere in the
+ * properties.
+ */
+ protected Nullable<String> keywords = new Nullable<String>();
+
+ /**
+ * The language of the intellectual content of the resource.
+ *
+ * [Note: IETF RFC 3066 provides guidance on encoding to represent
+ * languages. end note]
+ */
+ protected Nullable<String> language = new Nullable<String>();
+
+ /**
+ * The user who performed the last modification. The identification is
+ * environment-specific.
+ *
+ * [Example: A name, email address, or employee ID. end example] It is
+ * recommended that this value be as concise as possible.
+ */
+ protected Nullable<String> lastModifiedBy = new Nullable<String>();
+
+ /**
+ * The date and time of the last printing.
+ */
+ protected Nullable<Date> lastPrinted = new Nullable<Date>();
+
+ /**
+ * Date on which the resource was changed.
+ */
+ protected Nullable<Date> modified = new Nullable<Date>();
+
+ /**
+ * The revision number.
+ *
+ * [Example: This value might indicate the number of saves or revisions,
+ * provided the application updates it after each revision. end example]
+ */
+ protected Nullable<String> revision = new Nullable<String>();
+
+ /**
+ * The topic of the content of the resource.
+ */
+ protected Nullable<String> subject = new Nullable<String>();
+
+ /**
+ * The name given to the resource.
+ */
+ protected Nullable<String> title = new Nullable<String>();
+
+ /**
+ * The version number. This value is set by the user or by the application.
+ */
+ protected Nullable<String> version = new Nullable<String>();
+
+ /*
+ * Getters and setters
+ */
+
+ /**
+ * Get the category property.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getCategoryProperty()
+ */
+ public Nullable<String> getCategoryProperty() {
+ return category;
+ }
+
+ /**
+ * Get content status.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getContentStatusProperty()
+ */
+ public Nullable<String> getContentStatusProperty() {
+ return contentStatus;
+ }
+
+ /**
+ * Get content type.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getContentTypeProperty()
+ */
+ public Nullable<String> getContentTypeProperty() {
+ return contentType;
+ }
+
+ /**
+ * Get created date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getCreatedProperty()
+ */
+ public Nullable<Date> getCreatedProperty() {
+ return created;
+ }
+
+ /**
+ * Get created date formated into a String.
+ *
+ * @return A string representation of the created date.
+ */
+ public String getCreatedPropertyString() {
+ return getDateValue(created);
+ }
+
+ /**
+ * Get creator.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getCreatorProperty()
+ */
+ public Nullable<String> getCreatorProperty() {
+ return creator;
+ }
+
+ /**
+ * Get description.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getDescriptionProperty()
+ */
+ public Nullable<String> getDescriptionProperty() {
+ return description;
+ }
+
+ /**
+ * Get identifier.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getIdentifierProperty()
+ */
+ public Nullable<String> getIdentifierProperty() {
+ return identifier;
+ }
+
+ /**
+ * Get keywords.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getKeywordsProperty()
+ */
+ public Nullable<String> getKeywordsProperty() {
+ return keywords;
+ }
+
+ /**
+ * Get the language.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getLanguageProperty()
+ */
+ public Nullable<String> getLanguageProperty() {
+ return language;
+ }
+
+ /**
+ * Get the author of last modifications.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getLastModifiedByProperty()
+ */
+ public Nullable<String> getLastModifiedByProperty() {
+ return lastModifiedBy;
+ }
+
+ /**
+ * Get last printed date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getLastPrintedProperty()
+ */
+ public Nullable<Date> getLastPrintedProperty() {
+ return lastPrinted;
+ }
+
+ /**
+ * Get last printed date formated into a String.
+ *
+ * @return A string representation of the last printed date.
+ */
+ public String getLastPrintedPropertyString() {
+ return getDateValue(created);
+ }
+
+ /**
+ * Get modified date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getModifiedProperty()
+ */
+ public Nullable<Date> getModifiedProperty() {
+ return modified;
+ }
+
+ /**
+ * Get modified date formated into a String.
+ *
+ * @return A string representation of the modified date.
+ */
+ public String getModifiedPropertyString() {
+ if (!modified.hasValue())
+ return getDateValue(new Nullable<Date>(new Date()));
+ else
+ return getDateValue(modified);
+ }
+
+ /**
+ * Get revision.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getRevisionProperty()
+ */
+ public Nullable<String> getRevisionProperty() {
+ return revision;
+ }
+
+ /**
+ * Get subject.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getSubjectProperty()
+ */
+ public Nullable<String> getSubjectProperty() {
+ return subject;
+ }
+
+ /**
+ * Get title.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getTitleProperty()
+ */
+ public Nullable<String> getTitleProperty() {
+ return title;
+ }
+
+ /**
+ * Get version.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#getVersionProperty()
+ */
+ public Nullable<String> getVersionProperty() {
+ return version;
+ }
+
+ /**
+ * Set the category.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setCategoryProperty(java.lang.String)
+ */
+ public void setCategoryProperty(String category) {
+ this.category = setStringValue(category);
+ }
+
+ /**
+ * Set the content status.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setContentStatusProperty(java.lang.String)
+ */
+ public void setContentStatusProperty(String contentStatus) {
+ this.contentStatus = setStringValue(contentStatus);
+ }
+
+ /**
+ * Set the content type.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setContentTypeProperty(java.lang.String)
+ */
+ public void setContentTypeProperty(String contentType) {
+ this.contentType = setStringValue(contentType);
+ }
+
+ /**
+ * Set the created date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setCreatedProperty(org.apache.poi.openxml4j.util.Nullable)
+ */
+ public void setCreatedProperty(String created) {
+ try {
+ this.created = setDateValue(created);
+ } catch (InvalidFormatException e) {
+ new IllegalArgumentException("created : "
+ + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Set the created date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setCreatedProperty(org.apache.poi.openxml4j.util.Nullable)
+ */
+ public void setCreatedProperty(Nullable<Date> created) {
+ if (created.hasValue())
+ this.created = created;
+ }
+
+ /**
+ * Set the creator.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setCreatorProperty(java.lang.String)
+ */
+ public void setCreatorProperty(String creator) {
+ this.creator = setStringValue(creator);
+ }
+
+ /**
+ * Set the description.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setDescriptionProperty(java.lang.String)
+ */
+ public void setDescriptionProperty(String description) {
+ this.description = setStringValue(description);
+ }
+
+ /**
+ * Set identifier.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setIdentifierProperty(java.lang.String)
+ */
+ public void setIdentifierProperty(String identifier) {
+ this.identifier = setStringValue(identifier);
+ }
+
+ /**
+ * Set keywords.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setKeywordsProperty(java.lang.String)
+ */
+ public void setKeywordsProperty(String keywords) {
+ this.keywords = setStringValue(keywords);
+ }
+
+ /**
+ * Set language.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setLanguageProperty(java.lang.String)
+ */
+ public void setLanguageProperty(String language) {
+ this.language = setStringValue(language);
+ }
+
+ /**
+ * Set last modifications author.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setLastModifiedByProperty(java.lang.String)
+ */
+ public void setLastModifiedByProperty(String lastModifiedBy) {
+ this.lastModifiedBy = setStringValue(lastModifiedBy);
+ }
+
+ /**
+ * Set last printed date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setLastPrintedProperty(org.apache.poi.openxml4j.util.Nullable)
+ */
+ public void setLastPrintedProperty(String lastPrinted) {
+ try {
+ this.lastPrinted = setDateValue(lastPrinted);
+ } catch (InvalidFormatException e) {
+ new IllegalArgumentException("lastPrinted : "
+ + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Set last printed date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setLastPrintedProperty(org.apache.poi.openxml4j.util.Nullable)
+ */
+ public void setLastPrintedProperty(Nullable<Date> lastPrinted) {
+ if (lastPrinted.hasValue())
+ this.lastPrinted = lastPrinted;
+ }
+
+ /**
+ * Set last modification date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setModifiedProperty(org.apache.poi.openxml4j.util.Nullable)
+ */
+ public void setModifiedProperty(String modified) {
+ try {
+ this.modified = setDateValue(modified);
+ } catch (InvalidFormatException e) {
+ new IllegalArgumentException("modified : "
+ + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Set last modification date.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setModifiedProperty(org.apache.poi.openxml4j.util.Nullable)
+ */
+ public void setModifiedProperty(Nullable<Date> modified) {
+ if (modified.hasValue())
+ this.modified = modified;
+ }
+
+ /**
+ * Set revision.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setRevisionProperty(java.lang.String)
+ */
+ public void setRevisionProperty(String revision) {
+ this.revision = setStringValue(revision);
+ }
+
+ /**
+ * Set subject.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setSubjectProperty(java.lang.String)
+ */
+ public void setSubjectProperty(String subject) {
+ this.subject = setStringValue(subject);
+ }
+
+ /**
+ * Set title.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setTitleProperty(java.lang.String)
+ */
+ public void setTitleProperty(String title) {
+ this.title = setStringValue(title);
+ }
+
+ /**
+ * Set version.
+ *
+ * @see org.apache.poi.openxml4j.opc.PackageProperties#setVersionProperty(java.lang.String)
+ */
+ public void setVersionProperty(String version) {
+ this.version = setStringValue(version);
+ }
+
+ /**
+ * Convert a strig value into a Nullable<String>
+ */
+ private Nullable<String> setStringValue(String s) {
+ if (s == null || s.equals(""))
+ return new Nullable<String>();
+ else
+ return new Nullable<String>(s);
+ }
+
+ /**
+ * Convert a string value represented a date into a Nullable<Date>.
+ *
+ * @throws InvalidFormatException
+ * Throws if the date format isnot valid.
+ */
+ private Nullable<Date> setDateValue(String s) throws InvalidFormatException {
+ if (s == null || s.equals(""))
+ return new Nullable<Date>();
+ else {
+ SimpleDateFormat df = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss'Z'");
+ Date d = df.parse(s, new ParsePosition(0));
+ if (d == null)
+ throw new InvalidFormatException("Date not well formated");
+ return new Nullable<Date>(d);
+ }
+ }
+
+ /**
+ * Convert a Nullable<Date> into a String.
+ *
+ * @param d
+ * The Date to convert.
+ * @return The formated date or null.
+ * @see java.util.SimpleDateFormat
+ */
+ private String getDateValue(Nullable<Date> d) {
+ if (d == null || d.equals(""))
+ return "";
+ else {
+ SimpleDateFormat df = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss'Z'");
+ return df.format(d.getValue());
+ }
+ }
+
+ @Override
+ protected InputStream getInputStreamImpl() {
+ throw new InvalidOperationException("Operation not authorized");
+ }
+
+ @Override
+ protected OutputStream getOutputStreamImpl() {
+ throw new InvalidOperationException(
+ "Can't use output stream to set properties !");
+ }
+
+ @Override
+ public boolean save(OutputStream zos) throws OpenXML4JException {
+ throw new InvalidOperationException("Operation not authorized");
+ }
+
+ @Override
+ public boolean load(InputStream ios) throws InvalidFormatException {
+ throw new InvalidOperationException("Operation not authorized");
+ }
+
+ @Override
+ public void close() {
+ // Do nothing
+ }
+
+ @Override
+ public void flush() {
+ // Do nothing
+ }
+}
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
index 0000000000..70bbcae6b6
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartMarshaller.java
@@ -0,0 +1,49 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.OutputStream;
+
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+
+/**
+ * Object implemented this interface are considered as part marshaller. A part
+ * marshaller is responsible to marshall a part in order to be save in a
+ * package.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public interface PartMarshaller {
+
+ /**
+ * Save the content of the package in the stream
+ *
+ * @param part
+ * Part to marshall.
+ * @param out
+ * The output stream into which the part will be marshall.
+ * @return <b>false</b> if any marshall error occurs, else <b>true</b>
+ * @throws OpenXML4JException
+ * Throws only if any other exceptions are thrown by inner
+ * methods.
+ */
+ public boolean marshall(PackagePart part, OutputStream out)
+ throws OpenXML4JException;
+}
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
index 0000000000..0b17cb8926
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartUnmarshaller.java
@@ -0,0 +1,50 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.internal.unmarshallers.UnmarshallContext;
+
+/**
+ * Object implemented this interface are considered as part unmarshaller. A part
+ * unmarshaller is responsible to unmarshall a part in order to load it from a
+ * package.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public interface PartUnmarshaller {
+
+ /**
+ * Save the content of the package in the stream
+ *
+ * @param in
+ * The input stream from which the part will be unmarshall.
+ * @return The part freshly unmarshall from the input stream.
+ * @throws OpenXML4JException
+ * Throws only if any other exceptions are thrown by inner
+ * methods.
+ */
+ public PackagePart unmarshall(UnmarshallContext context, InputStream in)
+ throws InvalidFormatException, IOException;
+}
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
index 0000000000..5f894f2510
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java
@@ -0,0 +1,90 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.dom4j.Document;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.StreamHelper;
+
+/**
+ * Zip implementation of the ContentTypeManager.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ * @see ContentTypeManager
+ */
+public class ZipContentTypeManager extends ContentTypeManager {
+
+ /**
+ * Delegate constructor to the super constructor.
+ *
+ * @param in
+ * The input stream to parse to fill internal content type
+ * collections.
+ * @throws InvalidFormatException
+ * If the content types part content is not valid.
+ */
+ public ZipContentTypeManager(InputStream in, Package pkg)
+ throws InvalidFormatException {
+ super(in, pkg);
+ }
+
+ @Override
+ public boolean saveImpl(Document content, OutputStream out) {
+ ZipOutputStream zos = null;
+ if (out instanceof ZipOutputStream)
+ zos = (ZipOutputStream) out;
+ else
+ zos = new ZipOutputStream(out);
+
+ ZipEntry partEntry = new ZipEntry(CONTENT_TYPES_PART_NAME);
+ try {
+ // Referenced in ZIP
+ zos.putNextEntry(partEntry);
+ // Saving data in the ZIP file
+ ByteArrayOutputStream outTemp = new ByteArrayOutputStream();
+ StreamHelper.saveXmlInStream(content, out);
+ InputStream ins = new ByteArrayInputStream(outTemp.toByteArray());
+ byte[] buff = new byte[ZipHelper.READ_WRITE_FILE_BUFFER_SIZE];
+ while (ins.available() > 0) {
+ int resultRead = ins.read(buff);
+ if (resultRead == -1) {
+ // end of file reached
+ break;
+ } else {
+ zos.write(buff, 0, resultRead);
+ }
+ }
+ zos.closeEntry();
+ } catch (IOException ioe) {
+ logger.error("Cannot write: " + CONTENT_TYPES_PART_NAME
+ + " in Zip !", ioe);
+ return false;
+ }
+ return true;
+ }
+}
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
index 0000000000..10b2339ec4
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java
@@ -0,0 +1,163 @@
+/* ====================================================================
+ 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.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+import org.apache.poi.openxml4j.opc.ZipPackage;
+
+public final class ZipHelper {
+
+ /**
+ * Forward slash use to convert part name between OPC and zip item naming
+ * conventions.
+ */
+ private final static String FORWARD_SLASH = "/";
+
+ /**
+ * Buffer to read data from file. Use big buffer to improve performaces. the
+ * InputStream class is reading only 8192 bytes per read call (default value
+ * set by sun)
+ */
+ public static final int READ_WRITE_FILE_BUFFER_SIZE = 8192;
+
+ /**
+ * Prevent this class to be instancied.
+ */
+ private ZipHelper() {
+ // Do nothing
+ }
+
+ /**
+ * Retrieve the zip entry of the core properties part.
+ *
+ * @throws OpenXML4JException
+ * Throws if internal error occurs.
+ */
+ public static ZipEntry getCorePropertiesZipEntry(ZipPackage pkg)
+ throws OpenXML4JException {
+ PackageRelationship corePropsRel = pkg.getRelationshipsByType(
+ PackageRelationshipTypes.CORE_PROPERTIES).getRelationship(0);
+
+ if (corePropsRel == null)
+ return null;
+
+ return new ZipEntry(corePropsRel.getTargetURI().getPath());
+ }
+
+ /**
+ * Retrieve the Zip entry of the content types part.
+ */
+ public static ZipEntry getContentTypeZipEntry(ZipPackage pkg) {
+ Enumeration entries = pkg.getZipArchive().getEntries();
+ // Enumerate through the Zip entries until we find the one named
+ // '[Content_Types].xml'.
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ if (entry.getName().equals(
+ ContentTypeManager.CONTENT_TYPES_PART_NAME))
+ return entry;
+ }
+ return null;
+ }
+
+ /**
+ * Convert a zip name into an OPC name by adding a leading forward slash to
+ * the specified item name.
+ *
+ * @param zipItemName
+ * Zip item name to convert.
+ * @return An OPC compliant name.
+ */
+ public static String getOPCNameFromZipItemName(String zipItemName) {
+ if (zipItemName == null)
+ throw new IllegalArgumentException("zipItemName");
+ if (zipItemName.startsWith(FORWARD_SLASH))
+ return zipItemName;
+ else
+ return FORWARD_SLASH + zipItemName;
+ }
+
+ /**
+ * Convert an OPC item name into a zip item name by removing any leading
+ * forward slash if it exist.
+ *
+ * @param opcItemName
+ * The OPC item name to convert.
+ * @return A zip item name without any leading slashes.
+ */
+ public static String getZipItemNameFromOPCName(String opcItemName) {
+ if (opcItemName == null)
+ throw new IllegalArgumentException("opcItemName");
+
+ String retVal = new String(opcItemName);
+ while (retVal.startsWith(FORWARD_SLASH))
+ retVal = retVal.substring(1);
+ return retVal;
+ }
+
+ /**
+ * Convert an OPC item name into a zip URI by removing any leading forward
+ * slash if it exist.
+ *
+ * @param opcItemName
+ * The OPC item name to convert.
+ * @return A zip URI without any leading slashes.
+ */
+ public static URI getZipURIFromOPCName(String opcItemName) {
+ if (opcItemName == null)
+ throw new IllegalArgumentException("opcItemName");
+
+ String retVal = new String(opcItemName);
+ while (retVal.startsWith(FORWARD_SLASH))
+ retVal = retVal.substring(1);
+ try {
+ return new URI(retVal);
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieve and open a zip file with the specified path.
+ *
+ * @param path
+ * The file path.
+ * @return The zip archive freshly open.
+ */
+ public static ZipFile openZipFile(String path) {
+ File f = new File(path);
+ try {
+ if (!f.exists()) {
+ return null;
+ }
+ return new ZipFile(f);
+ } catch (IOException ioe) {
+ return null;
+ }
+ }
+}
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
index 0000000000..8138cda8be
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/DefaultMarshaller.java
@@ -0,0 +1,45 @@
+/* ====================================================================
+ 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.internal.marshallers;
+
+import java.io.OutputStream;
+
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
+
+/**
+ * Default marshaller that specified that the part is responsible to marshall its content.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ * @see PartMarshaller
+ */
+public class DefaultMarshaller implements PartMarshaller {
+
+ /**
+ * Save part in the output stream by using the save() method of the part.
+ *
+ * @throws OpenXML4JException
+ * If any error occur.
+ */
+ public boolean marshall(PackagePart part, OutputStream out)
+ throws OpenXML4JException {
+ return part.save(out);
+ }
+}
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
index 0000000000..438cc5dcbb
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/PackagePropertiesMarshaller.java
@@ -0,0 +1,434 @@
+/* ====================================================================
+ 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.internal.marshallers;
+
+import java.io.OutputStream;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+import org.dom4j.QName;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
+
+/**
+ * Package properties marshaller.
+ *
+ * @author CDubet, Julien Chable
+ * @version 1.0
+ */
+public class PackagePropertiesMarshaller implements PartMarshaller {
+
+ private final static Namespace namespaceDC = new Namespace("dc",
+ PackagePropertiesPart.NAMESPACE_DC_URI);
+
+ private final static Namespace namespaceCoreProperties = new Namespace("",
+ PackagePropertiesPart.NAMESPACE_CP_URI);
+
+ private final static Namespace namespaceDcTerms = new Namespace("dcterms",
+ PackagePropertiesPart.NAMESPACE_DCTERMS_URI);
+
+ private final static Namespace namespaceXSI = new Namespace("xsi",
+ PackagePropertiesPart.NAMESPACE_XSI_URI);
+
+ protected static final String KEYWORD_CATEGORY = "category";
+
+ protected static final String KEYWORD_CONTENT_STATUS = "contentStatus";
+
+ protected static final String KEYWORD_CONTENT_TYPE = "contentType";
+
+ protected static final String KEYWORD_CREATED = "created";
+
+ protected static final String KEYWORD_CREATOR = "creator";
+
+ protected static final String KEYWORD_DESCRIPTION = "description";
+
+ protected static final String KEYWORD_IDENTIFIER = "identifier";
+
+ protected static final String KEYWORD_KEYWORDS = "keywords";
+
+ protected static final String KEYWORD_LANGUAGE = "language";
+
+ protected static final String KEYWORD_LAST_MODIFIED_BY = "lastModifiedBy";
+
+ protected static final String KEYWORD_LAST_PRINTED = "lastPrinted";
+
+ protected static final String KEYWORD_MODIFIED = "modified";
+
+ protected static final String KEYWORD_REVISION = "revision";
+
+ protected static final String KEYWORD_SUBJECT = "subject";
+
+ protected static final String KEYWORD_TITLE = "title";
+
+ protected static final String KEYWORD_VERSION = "version";
+
+ PackagePropertiesPart propsPart;
+
+ // The document
+ Document xmlDoc = null;
+
+ /**
+ * Marshall package core properties to an XML document. Always return
+ * <code>true</code>.
+ */
+ public boolean marshall(PackagePart part, OutputStream out)
+ throws OpenXML4JException {
+ if (!(part instanceof PackagePropertiesPart))
+ throw new IllegalArgumentException(
+ "'part' must be a PackagePropertiesPart instance.");
+ propsPart = (PackagePropertiesPart) part;
+
+ // Configure the document
+ xmlDoc = DocumentHelper.createDocument();
+ Element rootElem = xmlDoc.addElement(new QName("coreProperties",
+ namespaceCoreProperties));
+ rootElem.addNamespace("cp", PackagePropertiesPart.NAMESPACE_CP_URI);
+ rootElem.addNamespace("dc", PackagePropertiesPart.NAMESPACE_DC_URI);
+ rootElem.addNamespace("dcterms",
+ PackagePropertiesPart.NAMESPACE_DCTERMS_URI);
+ rootElem.addNamespace("xsi", PackagePropertiesPart.NAMESPACE_XSI_URI);
+
+ addCategory();
+ addContentStatus();
+ addContentType();
+ addCreated();
+ addCreator();
+ addDescription();
+ addIdentifier();
+ addKeywords();
+ addLanguage();
+ addLastModifiedBy();
+ addLastPrinted();
+ addModified();
+ addRevision();
+ addSubject();
+ addTitle();
+ addVersion();
+ return true;
+ }
+
+ /**
+ * Add category property element if needed.
+ */
+ private void addCategory() {
+ if (!propsPart.getCategoryProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CATEGORY, namespaceCoreProperties));
+ if (elem == null) {
+ // Missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_CATEGORY, namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getCategoryProperty().getValue());
+ }
+
+ /**
+ * Add content status property element if needed.
+ */
+ private void addContentStatus() {
+ if (!propsPart.getContentStatusProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CONTENT_STATUS, namespaceCoreProperties));
+ if (elem == null) {
+ // Missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_CONTENT_STATUS, namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getContentStatusProperty().getValue());
+ }
+
+ /**
+ * Add content type property element if needed.
+ */
+ private void addContentType() {
+ if (!propsPart.getContentTypeProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CONTENT_TYPE, namespaceCoreProperties));
+ if (elem == null) {
+ // Missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_CONTENT_TYPE, namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getContentTypeProperty().getValue());
+ }
+
+ /**
+ * Add created property element if needed.
+ */
+ private void addCreated() {
+ if (!propsPart.getCreatedProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CREATED, namespaceDcTerms));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_CREATED, namespaceDcTerms));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addAttribute(new QName("type", namespaceXSI), "dcterms:W3CDTF");
+ elem.addText(propsPart.getCreatedPropertyString());
+ }
+
+ /**
+ * Add creator property element if needed.
+ */
+ private void addCreator() {
+ if (!propsPart.getCreatorProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CREATOR, namespaceDC));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_CREATOR, namespaceDC));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getCreatorProperty().getValue());
+ }
+
+ /**
+ * Add description property element if needed.
+ */
+ private void addDescription() {
+ if (!propsPart.getDescriptionProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_DESCRIPTION, namespaceDC));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_DESCRIPTION, namespaceDC));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getDescriptionProperty().getValue());
+ }
+
+ /**
+ * Add identifier property element if needed.
+ */
+ private void addIdentifier() {
+ if (!propsPart.getIdentifierProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_IDENTIFIER, namespaceDC));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_IDENTIFIER, namespaceDC));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getIdentifierProperty().getValue());
+ }
+
+ /**
+ * Add keywords property element if needed.
+ */
+ private void addKeywords() {
+ if (!propsPart.getKeywordsProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_KEYWORDS, namespaceCoreProperties));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_KEYWORDS, namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getKeywordsProperty().getValue());
+ }
+
+ /**
+ * Add language property element if needed.
+ */
+ private void addLanguage() {
+ if (!propsPart.getLanguageProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_LANGUAGE, namespaceDC));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_LANGUAGE, namespaceDC));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getLanguageProperty().getValue());
+ }
+
+ /**
+ * Add 'last modified by' property if needed.
+ */
+ private void addLastModifiedBy() {
+ if (!propsPart.getLastModifiedByProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_LAST_MODIFIED_BY, namespaceCoreProperties));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement()
+ .addElement(
+ new QName(KEYWORD_LAST_MODIFIED_BY,
+ namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getLastModifiedByProperty().getValue());
+ }
+
+ /**
+ * Add 'last printed' property if needed.
+ *
+ */
+ private void addLastPrinted() {
+ if (!propsPart.getLastPrintedProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_LAST_PRINTED, namespaceCoreProperties));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_LAST_PRINTED, namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getLastPrintedPropertyString());
+ }
+
+ /**
+ * Add modified property element if needed.
+ */
+ private void addModified() {
+ if (!propsPart.getModifiedProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_MODIFIED, namespaceDcTerms));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_MODIFIED, namespaceDcTerms));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addAttribute(new QName("type", namespaceXSI), "dcterms:W3CDTF");
+ elem.addText(propsPart.getModifiedPropertyString());
+ }
+
+ /**
+ * Add revision property if needed.
+ */
+ private void addRevision() {
+ if (!propsPart.getRevisionProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_REVISION, namespaceCoreProperties));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_REVISION, namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getRevisionProperty().getValue());
+ }
+
+ /**
+ * Add subject property if needed.
+ */
+ private void addSubject() {
+ if (!propsPart.getSubjectProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_SUBJECT, namespaceDC));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_SUBJECT, namespaceDC));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getSubjectProperty().getValue());
+ }
+
+ /**
+ * Add title property if needed.
+ */
+ private void addTitle() {
+ if (!propsPart.getTitleProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_TITLE, namespaceDC));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_TITLE, namespaceDC));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getTitleProperty().getValue());
+ }
+
+ private void addVersion() {
+ if (!propsPart.getVersionProperty().hasValue())
+ return;
+
+ Element elem = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_VERSION, namespaceCoreProperties));
+ if (elem == null) {
+ // missing, we add it
+ elem = xmlDoc.getRootElement().addElement(
+ new QName(KEYWORD_VERSION, namespaceCoreProperties));
+ } else {
+ elem.clearContent();// clear the old value
+ }
+ elem.addText(propsPart.getVersionProperty().getValue());
+ }
+}
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
index 0000000000..39e8fa3f3d
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java
@@ -0,0 +1,64 @@
+/* ====================================================================
+ 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.internal.marshallers;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.StreamHelper;
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;
+
+/**
+ * Package core properties marshaller specialized for zipped package.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public class ZipPackagePropertiesMarshaller extends PackagePropertiesMarshaller {
+
+ @Override
+ public boolean marshall(PackagePart part, OutputStream out)
+ throws OpenXML4JException {
+ if (!(out instanceof ZipOutputStream)) {
+ throw new IllegalArgumentException("ZipOutputStream expected!");
+ }
+ ZipOutputStream zos = (ZipOutputStream) out;
+
+ // Saving the part in the zip file
+ ZipEntry ctEntry = new ZipEntry(ZipHelper
+ .getZipItemNameFromOPCName(part.getPartName().getURI()
+ .toString()));
+ try {
+ // Save in ZIP
+ zos.putNextEntry(ctEntry); // Add entry in ZIP
+ super.marshall(part, out); // Marshall the properties inside a XML
+ // Document
+ if (!StreamHelper.saveXmlInStream(xmlDoc, out)) {
+ return false;
+ }
+ zos.closeEntry();
+ } catch (IOException e) {
+ throw new OpenXML4JException(e.getLocalizedMessage());
+ }
+ return true;
+ }
+}
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
index 0000000000..a54bef5745
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
@@ -0,0 +1,193 @@
+/* ====================================================================
+ 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.internal.marshallers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.Logger;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+import org.dom4j.QName;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackageNamespaces;
+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;
+import org.apache.poi.openxml4j.opc.StreamHelper;
+import org.apache.poi.openxml4j.opc.TargetMode;
+import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;
+
+/**
+ * Zip part marshaller. This marshaller is use to save any part in a zip stream.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public class ZipPartMarshaller implements PartMarshaller {
+ private static Logger logger = Logger.getLogger("org.openxml4j");
+
+ /**
+ * Save the specified part.
+ *
+ * @throws OpenXML4JException
+ * Throws if an internal exception is thrown.
+ */
+ public boolean marshall(PackagePart part, OutputStream os)
+ throws OpenXML4JException {
+ if (!(os instanceof ZipOutputStream)) {
+ logger.error("Unexpected class " + os.getClass().getName());
+ throw new OpenXML4JException("ZipOutputStream expected !");
+ // Normally should happen only in developpement phase, so just throw
+ // exception
+ }
+
+ ZipOutputStream zos = (ZipOutputStream) os;
+ ZipEntry partEntry = new ZipEntry(ZipHelper
+ .getZipItemNameFromOPCName(part.getPartName().getURI()
+ .getPath()));
+ try {
+ // Create next zip entry
+ zos.putNextEntry(partEntry);
+
+ // Saving data in the ZIP file
+ InputStream ins = part.getInputStream();
+ byte[] buff = new byte[ZipHelper.READ_WRITE_FILE_BUFFER_SIZE];
+ while (ins.available() > 0) {
+ int resultRead = ins.read(buff);
+ if (resultRead == -1) {
+ // End of file reached
+ break;
+ } else {
+ zos.write(buff, 0, resultRead);
+ }
+ }
+ zos.closeEntry();
+ } catch (IOException ioe) {
+ logger.error("Cannot write: " + part.getPartName() + ": in ZIP",
+ ioe);
+ return false;
+ }
+
+ // Saving relationship part
+ if (part.hasRelationships()) {
+ PackagePartName relationshipPartName = PackagingURIHelper
+ .getRelationshipPartName(part.getPartName());
+
+ marshallRelationshipPart(part.getRelationships(),
+ relationshipPartName, zos);
+
+ }
+ return true;
+ }
+
+ /**
+ * Save relationships into the part.
+ *
+ * @param rels
+ * The relationships collection to marshall.
+ * @param relPartURI
+ * Part name of the relationship part to marshall.
+ * @param zos
+ * Zip output stream in which to save the XML content of the
+ * relationships serialization.
+ */
+ public static boolean marshallRelationshipPart(
+ PackageRelationshipCollection rels, PackagePartName relPartName,
+ ZipOutputStream zos) {
+ // Building xml
+ Document xmlOutDoc = DocumentHelper.createDocument();
+ // make something like <Relationships
+ // xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
+ Namespace dfNs = Namespace.get("", PackageNamespaces.RELATIONSHIPS);
+ Element root = xmlOutDoc.addElement(new QName(
+ PackageRelationship.RELATIONSHIPS_TAG_NAME, dfNs));
+
+ // <Relationship
+ // TargetMode="External"
+ // Id="rIdx"
+ // Target="http://www.custom.com/images/pic1.jpg"
+ // Type="http://www.custom.com/external-resource"/>
+
+ URI sourcePartURI = PackagingURIHelper
+ .getSourcePartUriFromRelationshipPartUri(relPartName.getURI());
+
+ for (PackageRelationship rel : rels) {
+ // L'�l�ment de la relation
+ Element relElem = root
+ .addElement(PackageRelationship.RELATIONSHIP_TAG_NAME);
+
+ // L'attribut ID
+ relElem.addAttribute(PackageRelationship.ID_ATTRIBUTE_NAME, rel
+ .getId());
+
+ // L'attribut Type
+ relElem.addAttribute(PackageRelationship.TYPE_ATTRIBUTE_NAME, rel
+ .getRelationshipType());
+
+ // L'attribut Target
+ String targetValue;
+ URI uri = rel.getTargetURI();
+ if (rel.getTargetMode() == TargetMode.EXTERNAL) {
+ // Save the target as-is - we don't need to validate it,
+ // alter it etc
+ targetValue = uri.toString();
+
+ // add TargetMode attribut (as it is external link external)
+ relElem.addAttribute(
+ PackageRelationship.TARGET_MODE_ATTRIBUTE_NAME,
+ "External");
+ } else {
+ targetValue = PackagingURIHelper.relativizeURI(
+ sourcePartURI, rel.getTargetURI()).getPath();
+ }
+ relElem.addAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME,
+ targetValue);
+ }
+
+ xmlOutDoc.normalize();
+
+ // String schemaFilename = Configuration.getPathForXmlSchema()+
+ // File.separator + "opc-relationships.xsd";
+
+ // Save part in zip
+ ZipEntry ctEntry = new ZipEntry(ZipHelper.getZipURIFromOPCName(
+ relPartName.getURI().toASCIIString()).getPath());
+ try {
+ // Cr�ation de l'entr�e dans le fichier ZIP
+ zos.putNextEntry(ctEntry);
+ if (!StreamHelper.saveXmlInStream(xmlOutDoc, zos)) {
+ return false;
+ }
+ zos.closeEntry();
+ } catch (IOException e) {
+ logger.error("Cannot create zip entry " + relPartName, e);
+ return false;
+ }
+ return true; // success
+ }
+}
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
index 0000000000..0cb1fba941
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalCertificatePart.java
@@ -0,0 +1,79 @@
+/* ====================================================================
+ 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.internal.signature;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
+
+/**
+ * Digital certificate part.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public final class DigitalCertificatePart extends PackagePart {
+
+ public DigitalCertificatePart() throws InvalidFormatException{
+ super(null, null, new ContentType(""));
+ // Review constructor
+ }
+
+ @Override
+ public void close() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void flush() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected InputStream getInputStreamImpl() throws IOException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected OutputStream getOutputStreamImpl() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean load(InputStream ios) throws InvalidFormatException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean save(OutputStream zos) throws OpenXML4JException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ // TODO Introduire le concept de partie typ�e d�s cette partie
+}
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
index 0000000000..718e78b765
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalSignatureOriginPart.java
@@ -0,0 +1,28 @@
+/* ====================================================================
+ 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.internal.signature;
+
+/**
+ * Represents a digital signature origin part.
+ *
+ * @author Julien Chable
+ * @version 0.1
+ */
+public class DigitalSignatureOriginPart {
+
+}
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
index 0000000000..f11b969c00
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java
@@ -0,0 +1,390 @@
+/* ====================================================================
+ 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.internal.unmarshallers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
+import java.util.zip.ZipEntry;
+
+import org.dom4j.Attribute;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+import org.dom4j.QName;
+import org.dom4j.io.SAXReader;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackageNamespaces;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageProperties;
+import org.apache.poi.openxml4j.opc.ZipPackage;
+import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
+import org.apache.poi.openxml4j.opc.internal.PartUnmarshaller;
+import org.apache.poi.openxml4j.opc.internal.ZipHelper;
+
+/**
+ * Package properties unmarshaller.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public class PackagePropertiesUnmarshaller implements PartUnmarshaller {
+
+ private final static Namespace namespaceDC = new Namespace("dc",
+ PackageProperties.NAMESPACE_DC);
+
+ private final static Namespace namespaceCP = new Namespace("cp",
+ PackageNamespaces.CORE_PROPERTIES);
+
+ private final static Namespace namespaceDcTerms = new Namespace("dcterms",
+ PackageProperties.NAMESPACE_DCTERMS);
+
+ private final static Namespace namespaceXML = new Namespace("xml",
+ "http://www.w3.org/XML/1998/namespace");
+
+ private final static Namespace namespaceXSI = new Namespace("xsi",
+ "http://www.w3.org/2001/XMLSchema-instance");
+
+ protected static final String KEYWORD_CATEGORY = "category";
+
+ protected static final String KEYWORD_CONTENT_STATUS = "contentStatus";
+
+ protected static final String KEYWORD_CONTENT_TYPE = "contentType";
+
+ protected static final String KEYWORD_CREATED = "created";
+
+ protected static final String KEYWORD_CREATOR = "creator";
+
+ protected static final String KEYWORD_DESCRIPTION = "description";
+
+ protected static final String KEYWORD_IDENTIFIER = "identifier";
+
+ protected static final String KEYWORD_KEYWORDS = "keywords";
+
+ protected static final String KEYWORD_LANGUAGE = "language";
+
+ protected static final String KEYWORD_LAST_MODIFIED_BY = "lastModifiedBy";
+
+ protected static final String KEYWORD_LAST_PRINTED = "lastPrinted";
+
+ protected static final String KEYWORD_MODIFIED = "modified";
+
+ protected static final String KEYWORD_REVISION = "revision";
+
+ protected static final String KEYWORD_SUBJECT = "subject";
+
+ protected static final String KEYWORD_TITLE = "title";
+
+ protected static final String KEYWORD_VERSION = "version";
+
+ // TODO Load element with XMLBeans or dynamic table
+ // TODO Check every element/namespace for compliance
+ public PackagePart unmarshall(UnmarshallContext context, InputStream in)
+ throws InvalidFormatException, IOException {
+ PackagePropertiesPart coreProps = new PackagePropertiesPart(context
+ .getPackage(), context.getPartName());
+
+ // If the input stream is null then we try to get it from the
+ // package.
+ if (in == null) {
+ if (context.getZipEntry() != null) {
+ in = ((ZipPackage) context.getPackage()).getZipArchive()
+ .getInputStream(context.getZipEntry());
+ } else if (context.getPackage() != null) {
+ // Try to retrieve the part inputstream from the URI
+ ZipEntry zipEntry;
+ try {
+ zipEntry = ZipHelper
+ .getCorePropertiesZipEntry((ZipPackage) context
+ .getPackage());
+ } catch (OpenXML4JException e) {
+ throw new IOException(
+ "Error while trying to get the part input stream.");
+ }
+ in = ((ZipPackage) context.getPackage()).getZipArchive()
+ .getInputStream(zipEntry);
+ } else
+ throw new IOException(
+ "Error while trying to get the part input stream.");
+ }
+
+ SAXReader xmlReader = new SAXReader();
+ Document xmlDoc;
+ try {
+ xmlDoc = xmlReader.read(in);
+
+ /* Check OPC compliance */
+
+ // Rule M4.2, M4.3, M4.4 and M4.5/
+ checkElementForOPCCompliance(xmlDoc.getRootElement());
+
+ /* End OPC compliance */
+
+ } catch (DocumentException e) {
+ throw new IOException(e.getMessage());
+ }
+
+ coreProps.setCategoryProperty(loadCategory(xmlDoc));
+ coreProps.setContentStatusProperty(loadContentStatus(xmlDoc));
+ coreProps.setContentTypeProperty(loadContentType(xmlDoc));
+ coreProps.setCreatedProperty(loadCreated(xmlDoc));
+ coreProps.setCreatorProperty(loadCreator(xmlDoc));
+ coreProps.setDescriptionProperty(loadDescription(xmlDoc));
+ coreProps.setIdentifierProperty(loadIdentifier(xmlDoc));
+ coreProps.setKeywordsProperty(loadKeywords(xmlDoc));
+ coreProps.setLanguageProperty(loadLanguage(xmlDoc));
+ coreProps.setLastModifiedByProperty(loadLastModifiedBy(xmlDoc));
+ coreProps.setLastPrintedProperty(loadLastPrinted(xmlDoc));
+ coreProps.setModifiedProperty(loadModified(xmlDoc));
+ coreProps.setRevisionProperty(loadRevision(xmlDoc));
+ coreProps.setSubjectProperty(loadSubject(xmlDoc));
+ coreProps.setTitleProperty(loadTitle(xmlDoc));
+ coreProps.setVersionProperty(loadVersion(xmlDoc));
+
+ return coreProps;
+ }
+
+ private String loadCategory(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CATEGORY, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadContentStatus(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CONTENT_STATUS, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadContentType(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CONTENT_TYPE, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadCreated(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CREATED, namespaceDcTerms));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadCreator(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_CREATOR, namespaceDC));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadDescription(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_DESCRIPTION, namespaceDC));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadIdentifier(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_IDENTIFIER, namespaceDC));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadKeywords(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_KEYWORDS, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadLanguage(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_LANGUAGE, namespaceDC));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadLastModifiedBy(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_LAST_MODIFIED_BY, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadLastPrinted(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_LAST_PRINTED, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadModified(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_MODIFIED, namespaceDcTerms));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadRevision(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_REVISION, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadSubject(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_SUBJECT, namespaceDC));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadTitle(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_TITLE, namespaceDC));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ private String loadVersion(Document xmlDoc) {
+ Element el = xmlDoc.getRootElement().element(
+ new QName(KEYWORD_VERSION, namespaceCP));
+ if (el != null)
+ return el.getStringValue();
+ else
+ return null;
+ }
+
+ /* OPC Compliance methods */
+
+ /**
+ * Check the element for the following OPC compliance rules:
+ *
+ * Rule M4.2: A format consumer shall consider the use of the Markup
+ * Compatibility namespace to be an error.
+ *
+ * Rule 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.
+ *
+ * Rule 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.
+ *
+ * Rule M4.5: Producers shall not create a document element that contains
+ * the xsi:type attribute, except for a <dcterms:created> or
+ * <dcterms:modified> element where the xsi:type attribute shall be present
+ * and shall hold the value dcterms:W3CDTF, where dcterms is the namespace
+ * prefix of the Dublin Core namespace. Consumers shall consider a document
+ * element that violates this constraint to be an error.
+ */
+ public void checkElementForOPCCompliance(Element el)
+ throws InvalidFormatException {
+ // Check the current element
+ List declaredNamespaces = el.declaredNamespaces();
+ Iterator itNS = declaredNamespaces.iterator();
+ while (itNS.hasNext()) {
+ Namespace ns = (Namespace) itNS.next();
+
+ // Rule M4.2
+ if (ns.getURI().equals(PackageNamespaces.MARKUP_COMPATIBILITY))
+ throw new InvalidFormatException(
+ "OPC Compliance error [M4.2]: A format consumer shall consider the use of the Markup Compatibility namespace to be an error.");
+ }
+
+ // Rule M4.3
+ if (el.getNamespace().getURI().equals(
+ PackageProperties.NAMESPACE_DCTERMS)
+ && !(el.getName().equals(KEYWORD_CREATED) || el.getName()
+ .equals(KEYWORD_MODIFIED)))
+ throw new InvalidFormatException(
+ "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.");
+
+ // Rule M4.4
+ if (el.attribute(new QName("lang", namespaceXML)) != null)
+ throw new InvalidFormatException(
+ "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.");
+
+ // Rule M4.5
+ if (el.getNamespace().getURI().equals(
+ PackageProperties.NAMESPACE_DCTERMS)) {
+ // DCTerms namespace only use with 'created' and 'modified' elements
+ String elName = el.getName();
+ if (!(elName.equals(KEYWORD_CREATED) || elName
+ .equals(KEYWORD_MODIFIED)))
+ throw new InvalidFormatException("Namespace error : " + elName
+ + " shouldn't have the following naemspace -> "
+ + PackageProperties.NAMESPACE_DCTERMS);
+
+ // Check for the 'xsi:type' attribute
+ Attribute typeAtt = el.attribute(new QName("type", namespaceXSI));
+ if (typeAtt == null)
+ throw new InvalidFormatException("The element '" + elName
+ + "' must have the '" + namespaceXSI.getPrefix()
+ + ":type' attribute present !");
+
+ // Check for the attribute value => 'dcterms:W3CDTF'
+ if (!typeAtt.getValue().equals("dcterms:W3CDTF"))
+ throw new InvalidFormatException("The element '" + elName
+ + "' must have the '" + namespaceXSI.getPrefix()
+ + ":type' attribute with the value 'dcterms:W3CDTF' !");
+ }
+
+ // Check its children
+ Iterator itChildren = el.elementIterator();
+ while (itChildren.hasNext())
+ checkElementForOPCCompliance((Element) itChildren.next());
+ }
+}
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
index 0000000000..689a5c67da
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/UnmarshallContext.java
@@ -0,0 +1,96 @@
+/* ====================================================================
+ 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.internal.unmarshallers;
+
+import java.util.zip.ZipEntry;
+
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+
+/**
+ * Context needed for the unmarshall process of a part. This class is immutable.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public final class UnmarshallContext {
+
+ private Package _package;
+
+ private PackagePartName partName;
+
+ private ZipEntry zipEntry;
+
+ /**
+ * Constructor.
+ *
+ * @param targetPackage
+ * Container.
+ * @param partName
+ * Name of the part to unmarshall.
+ */
+ public UnmarshallContext(Package targetPackage, PackagePartName partName) {
+ this._package = targetPackage;
+ this.partName = partName;
+ }
+
+ /**
+ * @return the container
+ */
+ Package getPackage() {
+ return _package;
+ }
+
+ /**
+ * @param container
+ * the container to set
+ */
+ public void setPackage(Package container) {
+ this._package = container;
+ }
+
+ /**
+ * @return the partName
+ */
+ PackagePartName getPartName() {
+ return partName;
+ }
+
+ /**
+ * @param partName
+ * the partName to set
+ */
+ public void setPartName(PackagePartName partName) {
+ this.partName = partName;
+ }
+
+ /**
+ * @return the zipEntry
+ */
+ ZipEntry getZipEntry() {
+ return zipEntry;
+ }
+
+ /**
+ * @param zipEntry
+ * the zipEntry to set
+ */
+ public void setZipEntry(ZipEntry zipEntry) {
+ this.zipEntry = zipEntry;
+ }
+}
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
index 0000000000..8922641ce6
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignature.java
@@ -0,0 +1,70 @@
+/* ====================================================================
+ 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.signature;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
+
+public class PackageDigitalSignature extends PackagePart {
+
+ public PackageDigitalSignature() throws InvalidFormatException {
+ super(null, null, new ContentType(""));
+ }
+
+ @Override
+ public void close() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void flush() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected InputStream getInputStreamImpl() throws IOException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected OutputStream getOutputStreamImpl() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean load(InputStream ios) throws InvalidFormatException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean save(OutputStream zos) throws OpenXML4JException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
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
index 0000000000..0e5136d3ac
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignatureManager.java
@@ -0,0 +1,22 @@
+/* ====================================================================
+ 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.signature;
+
+public class PackageDigitalSignatureManager {
+
+}
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
index 0000000000..45374dcf7c
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/util/Nullable.java
@@ -0,0 +1,71 @@
+/* ====================================================================
+ 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;
+
+/**
+ * An immutable object that could be defined as null.
+ *
+ * @author Julien Chable
+ * @version 0.9
+ */
+public final class Nullable<E> {
+
+ private E value;
+
+ /**
+ * Constructor.
+ */
+ public Nullable() {
+ // Do nothing
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param value
+ * The value to set to this nullable.
+ */
+ public Nullable(E value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the store value if any.
+ *
+ * @return
+ */
+ public E getValue() {
+ return value;
+ }
+
+ /**
+ * Get the status of this nullable.
+ *
+ * @return <b>true</b> if the nullable store a value (empty string is
+ * considered to be a value) else <b>false</>.
+ */
+ public boolean hasValue() {
+ return value != null;
+ }
+
+ /**
+ * Set the stored value to <i>null</i>.
+ */
+ public void nullify() {
+ value = null;
+ }
+}
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
index 0000000000..1d64ffe4ea
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java
@@ -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
index 0000000000..1a0d36695d
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java
@@ -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
index 0000000000..0b9822eab1
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
@@ -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);
+ }
+ }
+}
diff --git a/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java b/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java
index d7984d3b19..d2f0f43e27 100644
--- a/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java
+++ b/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java
@@ -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
diff --git a/src/ooxml/java/org/apache/poi/util/PackageHelper.java b/src/ooxml/java/org/apache/poi/util/PackageHelper.java
index 258e5b8d1e..5b1b7d7476 100755
--- a/src/ooxml/java/org/apache/poi/util/PackageHelper.java
+++ b/src/ooxml/java/org/apache/poi/util/PackageHelper.java
@@ -16,16 +16,12 @@
==================================================================== */
package org.apache.poi.util;
-import org.openxml4j.opc.*;
-import org.openxml4j.opc.Package;
-import org.openxml4j.opc.internal.PackagePropertiesPart;
-import org.openxml4j.opc.internal.marshallers.PackagePropertiesMarshaller;
-import org.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.*;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.util.IOUtils;
import java.io.*;
-import java.util.ArrayList;
-import java.lang.reflect.Method;
/**
* Provides handy methods to work with OOXML packages
diff --git a/src/ooxml/java/org/apache/poi/xslf/XSLFSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/XSLFSlideShow.java
index 53851177bd..7b66a3d5a4 100644
--- a/src/ooxml/java/org/apache/poi/xslf/XSLFSlideShow.java
+++ b/src/ooxml/java/org/apache/poi/xslf/XSLFSlideShow.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java b/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java
index 4366f82e76..ad820bd2fb 100644
--- a/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java
+++ b/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java b/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java
index ceeaa0f441..29f3bd0832 100644
--- a/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java
+++ b/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFReader.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExcelExtractor.java b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExcelExtractor.java
index 33614edc0e..80ba653118 100644
--- a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExcelExtractor.java
+++ b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFExcelExtractor.java
@@ -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
diff --git a/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java b/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java
index b5fabd1a61..6caec6e734 100644
--- a/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java
+++ b/src/ooxml/java/org/apache/poi/xssf/model/CommentsTable.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java b/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
index edc8186627..8287df55b4 100644
--- a/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
+++ b/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
@@ -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;
/**
diff --git a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java
index 872eadb44d..8355f4f9ba 100644
--- a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java
+++ b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java
@@ -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;
/**
diff --git a/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java b/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java
index d16cf9a1bd..1a0a6555fe 100644
--- a/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java
+++ b/src/ooxml/java/org/apache/poi/xssf/model/XSSFChildContainingModel.java
@@ -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
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java
index 300f27cb9d..af7d645676 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFActiveXData.java
@@ -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 {
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDialogsheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDialogsheet.java
index 08e2a943cb..d0c93cc19d 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDialogsheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDialogsheet.java
@@ -17,15 +17,8 @@
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{
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
index f6e0a6c325..92be5e373c 100755
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
@@ -21,7 +21,7 @@ import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
-import org.openxml4j.opc.*;
+import org.apache.poi.openxml4j.opc.*;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*;
import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java
index 9317d39520..4736ecbc4a 100755
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java
@@ -22,8 +22,8 @@ import org.apache.poi.POIXMLException;
import org.apache.poi.POIXMLRelation;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
import java.lang.reflect.Constructor;
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java
index 4dbb72f716..671e6f402f 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFHyperlink.java
@@ -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;
/**
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
index 8e12e8606d..3fc0e52e8e 100755
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
@@ -23,8 +23,8 @@ import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.POIXMLDocumentPart;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import javax.imageio.ImageIO;
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java
index 736eea053c..05bb5983f9 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java
@@ -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.
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
index ef9bf7fbb1..b985d64406 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
@@ -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;
/**
*
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShapeGroup.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShapeGroup.java
index 686a8f0378..ac6b1268ba 100755
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShapeGroup.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShapeGroup.java
@@ -18,7 +18,7 @@ package org.apache.poi.xssf.usermodel;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
-import org.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
/**
* This object specifies a group shape that represents many shapes grouped together. This shape is to be treated
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
index 10391170a1..aa35007ff4 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
index 7594e6aa86..9a68eb7fb6 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java b/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java
index 91d52a72ca..1e97b129ef 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/extractor/XWPFWordExtractor.java
@@ -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
diff --git a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java
index a92972acf8..3657c1fa67 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/model/XWPFHeaderFooterPolicy.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
index b5f132affa..f1921783d8 100644
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java
@@ -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;
diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFactory.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFactory.java
index 3761241bfd..04bf13d113 100755
--- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFactory.java
+++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFFactory.java
@@ -22,8 +22,8 @@ import org.apache.poi.POIXMLException;
import org.apache.poi.POIXMLRelation;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory;
-import org.openxml4j.opc.PackageRelationship;
-import org.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackagePart;
import java.lang.reflect.Constructor;
diff --git a/src/ooxml/testcases/org/apache/poi/TestEmbeded.java b/src/ooxml/testcases/org/apache/poi/TestEmbeded.java
index f6049c396d..6a427bbdc0 100644
--- a/src/ooxml/testcases/org/apache/poi/TestEmbeded.java
+++ b/src/ooxml/testcases/org/apache/poi/TestEmbeded.java
@@ -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;
diff --git a/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java b/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java
index 3d670dd9de..33c11436e5 100644
--- a/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java
+++ b/src/ooxml/testcases/org/apache/poi/TestXMLPropertiesTextExtractor.java
@@ -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);
diff --git a/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java b/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
index de5c07fd9d..e75ba3c9e6 100644
--- a/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
+++ b/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java
@@ -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
index 0000000000..26bf0ce0fe
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/TestCore.java
@@ -0,0 +1,102 @@
+/* ====================================================================
+ 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;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+/**
+ * Core helper for tests.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public class TestCore {
+
+ private String testRootPath; // Test root path
+
+ /**
+ * All sample document are normally located at this place.
+ */
+ private static String pathRootProject; // Project root path
+
+ /**
+ * Demo logger
+ */
+ private static Logger logger = Logger.getLogger("org.apache.poi.openxml4j.test");
+
+ static {
+ pathRootProject = System.getProperty("user.dir") + File.separator + "bin";
+
+ // Log4j configuration
+ //PropertyConfigurator.configure(pathRootProject + File.separator
+ // + "config.log4j");
+ }
+
+ /**
+ * Constructor. Initialize the demo.
+ *
+ */
+ public TestCore(Class cl) {
+ init(cl);
+ }
+
+ /**
+ * Initialize the test root path
+ */
+ public void init(Class cl) {
+ String packageName = cl.getPackage().getName();
+ // replace . by /
+ String sep = File.separator;
+ if (sep.equals("\\")) {
+ sep = "\\\\";
+ }
+ testRootPath = pathRootProject + File.separator
+ + packageName.replaceAll("\\.", sep)
+ + File.separator;
+ }
+
+ // Accessors
+
+ /**
+ * Gets the test root path.
+ *
+ * @return The test root path.
+ */
+ public String getTestRootPath() {
+ return testRootPath;
+ }
+
+ /**
+ * Sets the test root path.
+ *
+ * @param testRoot
+ */
+ public void setTestRootPath(String testRoot) {
+ this.testRootPath = testRoot;
+ }
+
+ /**
+ * @return the logger
+ */
+ public static Logger getLogger() {
+ return logger;
+ }
+}
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
index 0000000000..4b2cc45a56
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/AllTests.java
@@ -0,0 +1,38 @@
+/* ====================================================================
+ 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 junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(
+ "Functional tests for org.apache.poi.openxml4j.opc");
+ suite.addTestSuite(TestListParts.class);
+ suite.addTestSuite(TestFileHelper.class);
+ suite.addTestSuite(TestPackage.class);
+ suite.addTestSuite(TestPackageCoreProperties.class);
+ suite.addTestSuite(TestPackagePartName.class);
+ suite.addTestSuite(TestPackagingURIHelper.class);
+ suite.addTestSuite(TestContentType.class);
+ suite.addTestSuite(TestPackageThumbnail.class);
+ return suite;
+ }
+}
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
index 0000000000..ba5ed27d23
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsx
Binary files 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
index 0000000000..dc105f0046
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx
Binary files 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
index 0000000000..771cd18145
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx
Binary files 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
index 0000000000..2d8bb359b5
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx
Binary files 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
index 0000000000..2d8bb359b5
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx
Binary files 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
index 0000000000..ccc990df33
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx
Binary files 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
index 0000000000..8e77521434
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx
Binary files 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
index 0000000000..a275cf417e
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx
Binary files 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
index 0000000000..5119960023
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg
Binary files 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
index 0000000000..3da1daeb98
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx
Binary files 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
index 0000000000..c8c43dce69
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx
Binary files 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
index 0000000000..a41daa5ec5
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx
Binary files 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
index 0000000000..9f70f6f023
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java
@@ -0,0 +1,117 @@
+/* ====================================================================
+ 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 junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
+
+/**
+ * Tests for content type (ContentType class).
+ *
+ * @author Julien Chable
+ */
+public class TestContentType extends TestCase {
+
+ /**
+ * Check rule M1.13: Package implementers shall only create and only
+ * recognize parts with a content type; format designers shall specify a
+ * content type for each part included in the format. Content types for
+ * package parts shall fit the definition and syntax for media types as
+ * specified in RFC 2616, §3.7.
+ */
+ public void testContentTypeValidation() throws InvalidFormatException {
+ String[] contentTypesToTest = new String[] { "text/xml",
+ "application/pgp-key", "application/vnd.hp-PCLXL",
+ "application/vnd.lotus-1-2-3" };
+ for (int i = 0; i < contentTypesToTest.length; ++i) {
+ new ContentType(contentTypesToTest[i]);
+ }
+ }
+
+ /**
+ * Check rule M1.13 : Package implementers shall only create and only
+ * recognize parts with a content type; format designers shall specify a
+ * content type for each part included in the format. Content types for
+ * package parts shall fit the definition and syntax for media types as
+ * specified in RFC 2616, §3.7.
+ *
+ * Check rule M1.14: Content types shall not use linear white space either
+ * between the type and subtype or between an attribute and its value.
+ * Content types also shall not have leading or trailing white spaces.
+ * Package implementers shall create only such content types and shall
+ * require such content types when retrieving a part from a package; format
+ * designers shall specify only such content types for inclusion in the
+ * format.
+ */
+ public void testContentTypeValidationFailure() {
+ String[] contentTypesToTest = new String[] { "text/xml/app", "",
+ "test", "text(xml/xml", "text)xml/xml", "text<xml/xml",
+ "text>/xml", "text@/xml", "text,/xml", "text;/xml",
+ "text:/xml", "text\\/xml", "t/ext/xml", "t\"ext/xml",
+ "text[/xml", "text]/xml", "text?/xml", "tex=t/xml",
+ "te{xt/xml", "tex}t/xml", "te xt/xml",
+ "text" + (char) 9 + "/xml", "text xml", " text/xml " };
+ for (int i = 0; i < contentTypesToTest.length; ++i) {
+ try {
+ new ContentType(contentTypesToTest[i]);
+ } catch (InvalidFormatException e) {
+ continue;
+ }
+ fail("Must have fail for content type: '" + contentTypesToTest[i]
+ + "' !");
+ }
+ }
+
+ /**
+ * Check rule [O1.2]: Format designers might restrict the usage of
+ * parameters for content types.
+ */
+ public void testContentTypeParameterFailure() {
+ String[] contentTypesToTest = new String[] { "mail/toto;titi=tata",
+ "text/xml;a=b;c=d", "mail/toto;\"titi=tata\"" };
+ for (int i = 0; i < contentTypesToTest.length; ++i) {
+ try {
+ new ContentType(contentTypesToTest[i]);
+ } catch (InvalidFormatException e) {
+ continue;
+ }
+ fail("Must have fail for content type: '" + contentTypesToTest[i]
+ + "' !");
+ }
+ }
+
+ /**
+ * Check rule M1.15: The package implementer shall require a content type
+ * that does not include comments and the format designer shall specify such
+ * a content type.
+ */
+ public void testContentTypeCommentFailure() {
+ String[] contentTypesToTest = new String[] { "text/xml(comment)" };
+ for (int i = 0; i < contentTypesToTest.length; ++i) {
+ try {
+ new ContentType(contentTypesToTest[i]);
+ } catch (InvalidFormatException e) {
+ continue;
+ }
+ fail("Must have fail for content type: '" + contentTypesToTest[i]
+ + "' !");
+ }
+ }
+}
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
index 0000000000..280d6d9922
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestFileHelper.java
@@ -0,0 +1,45 @@
+/* ====================================================================
+ 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.File;
+import java.util.TreeMap;
+
+import org.apache.poi.openxml4j.opc.internal.FileHelper;
+
+import junit.framework.TestCase;
+
+/**
+ * Test TestFileHelper class.
+ *
+ * @author Julien Chable
+ */
+public class TestFileHelper extends TestCase {
+
+ public void testGetDirectory() {
+ TreeMap<String, String> expectedValue = new TreeMap<String, String>();
+ expectedValue.put("c:\\test\\test.doc", "c:\\test");
+ expectedValue.put("d:\\test\\test2\\test.doc.xml", "d:\\test\\test2");
+
+ for (String filename : expectedValue.keySet()) {
+ assertTrue(expectedValue.get(filename).equalsIgnoreCase(
+ FileHelper.getDirectory(new File(filename))
+ .getAbsolutePath()));
+ }
+ }
+}
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
index 0000000000..359c02faf8
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestListParts.java
@@ -0,0 +1,104 @@
+/* ====================================================================
+ 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.File;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+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.PackagingURIHelper;
+
+import org.apache.poi.openxml4j.TestCore;
+
+public class TestListParts extends TestCase {
+
+ TestCore testCore = new TestCore(this.getClass());
+
+ TreeMap<PackagePartName, String> expectedValues;
+
+ TreeMap<PackagePartName, String> values;
+
+ @Override
+ protected void setUp() throws Exception {
+ values = new TreeMap<PackagePartName, String>();
+
+ // Expected values
+ expectedValues = new TreeMap<PackagePartName, String>();
+ expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),
+ "application/vnd.openxmlformats-package.relationships+xml");
+
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/docProps/app.xml"),
+ "application/vnd.openxmlformats-officedocument.extended-properties+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/docProps/core.xml"),
+ "application/vnd.openxmlformats-package.core-properties+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/word/_rels/document.xml.rels"),
+ "application/vnd.openxmlformats-package.relationships+xml");
+ expectedValues
+ .put(
+ PackagingURIHelper.createPartName("/word/document.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/word/fontTable.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/word/media/image1.gif"), "image/gif");
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/word/settings.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml");
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/word/styles.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/word/theme/theme1.xml"),
+ "application/vnd.openxmlformats-officedocument.theme+xml");
+ expectedValues
+ .put(
+ PackagingURIHelper
+ .createPartName("/word/webSettings.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml");
+ }
+
+ /**
+ * List all parts of a package.
+ */
+ public void testListParts() throws InvalidFormatException {
+ String filepath = System.getProperty("openxml4j.testdata.input") + File.separator
+ + "sample.docx";
+
+ Package p = Package.open(filepath, PackageAccess.READ);
+ for (PackagePart part : p.getParts()) {
+ values.put(part.getPartName(), part.getContentType());
+ TestCore.getLogger().debug(part.getPartName());
+ }
+
+ // Compare expected values with values return by the package
+ for (PackagePartName partName : expectedValues.keySet()) {
+ assertNotNull(values.get(partName));
+ assertEquals(expectedValues.get(partName), values.get(partName));
+ }
+ }
+}
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
index 0000000000..8d4e3313e2
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
@@ -0,0 +1,440 @@
+/* ====================================================================
+ 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 java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.net.URI;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.Namespace;
+import org.dom4j.QName;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.FileHelper;
+
+import org.apache.poi.openxml4j.TestCore;
+
+public class TestPackage extends TestCase {
+
+ TestCore testCore = new TestCore(this.getClass());
+
+ /**
+ * Test that just opening and closing the file doesn't alter the document.
+ */
+ public void testOpenSave() throws Exception {
+ File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator
+ + "TestPackageCommon.docx");
+ File targetFile = new File(System.getProperty("openxml4j.testdata.output")
+ + File.separator + "TestPackageOpenSaveTMP.docx");
+ assertTrue("Source file " + originalFile + " doesn't exist!", originalFile.exists());
+
+ Package p = Package.open(originalFile.getAbsolutePath(),
+ PackageAccess.READ_WRITE);
+ p.save(targetFile.getAbsoluteFile());
+
+ // Compare the original and newly saved document
+ assertTrue(targetFile.exists());
+ //ZipFileAssert.assertEquals(originalFile, targetFile);
+ assertTrue(targetFile.delete());
+ }
+
+ /**
+ * Test that when we create a new Package, we give it
+ * the correct default content types
+ */
+ public void testCreateGetsContentTypes() throws Exception {
+ File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator
+ + "TestCreatePackageTMP.docx");
+
+ // Zap the target file, in case of an earlier run
+ if(targetFile.exists()) targetFile.delete();
+
+ Package pkg = Package.create(targetFile);
+
+ // Check it has content types for rels and xml
+ ContentTypeManager ctm = getContentTypeManager(pkg);
+ assertEquals(
+ "application/xml",
+ ctm.getContentType(
+ PackagingURIHelper.createPartName("/foo.xml")
+ )
+ );
+ assertEquals(
+ ContentTypes.RELATIONSHIPS_PART,
+ ctm.getContentType(
+ PackagingURIHelper.createPartName("/foo.rels")
+ )
+ );
+ assertNull(
+ ctm.getContentType(
+ PackagingURIHelper.createPartName("/foo.txt")
+ )
+ );
+ }
+
+ /**
+ * Test package creation.
+ */
+ public void testCreatePackageAddPart() throws Exception {
+ File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator
+ + "TestCreatePackageTMP.docx");
+
+ File expectedFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator
+ + "TestCreatePackageOUTPUT.docx");
+
+ // Zap the target file, in case of an earlier run
+ if(targetFile.exists()) targetFile.delete();
+
+ // Create a package
+ Package pkg = Package.create(targetFile);
+ PackagePartName corePartName = PackagingURIHelper
+ .createPartName("/word/document.xml");
+
+ pkg.addRelationship(corePartName, TargetMode.INTERNAL,
+ PackageRelationshipTypes.CORE_DOCUMENT, "rId1");
+
+ PackagePart corePart = pkg
+ .createPart(
+ corePartName,
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml");
+
+ Document doc = DocumentHelper.createDocument();
+ Namespace nsWordprocessinML = new Namespace("w",
+ "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
+ Element elDocument = doc.addElement(new QName("document",
+ nsWordprocessinML));
+ Element elBody = elDocument.addElement(new QName("body",
+ nsWordprocessinML));
+ Element elParagraph = elBody.addElement(new QName("p",
+ nsWordprocessinML));
+ Element elRun = elParagraph
+ .addElement(new QName("r", nsWordprocessinML));
+ Element elText = elRun.addElement(new QName("t", nsWordprocessinML));
+ elText.setText("Hello Open XML !");
+
+ StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());
+ pkg.close();
+
+ //ZipFileAssert.assertEquals(expectedFile, targetFile);
+ assertTrue(targetFile.delete());
+ }
+
+ /**
+ * Tests that we can create a new package, add a core
+ * document and another part, save and re-load and
+ * have everything setup as expected
+ */
+ public void testCreatePackageWithCoreDocument() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Package pkg = Package.create(baos);
+
+ // Add a core document
+ PackagePartName corePartName = PackagingURIHelper.createPartName("/xl/workbook.xml");
+ // Create main part relationship
+ pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT, "rId1");
+ // Create main document part
+ PackagePart corePart = pkg.createPart(corePartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
+ // Put in some dummy content
+ OutputStream coreOut = corePart.getOutputStream();
+ coreOut.write("<dummy-xml />".getBytes());
+ coreOut.close();
+
+ // And another bit
+ PackagePartName sheetPartName = PackagingURIHelper.createPartName("/xl/worksheets/sheet1.xml");
+ PackageRelationship rel =
+ corePart.addRelationship(sheetPartName, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", "rSheet1");
+ PackagePart part = pkg.createPart(sheetPartName, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
+ // Dummy content again
+ coreOut = corePart.getOutputStream();
+ coreOut.write("<dummy-xml2 />".getBytes());
+ coreOut.close();
+
+
+ // Check things are as expected
+ PackageRelationshipCollection coreRels =
+ pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);
+ assertEquals(1, coreRels.size());
+ PackageRelationship coreRel = coreRels.getRelationship(0);
+ assertEquals("/", coreRel.getSourceURI().toString());
+ assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());
+ assertNotNull(pkg.getPart(coreRel));
+
+
+ // Save and re-load
+ pkg.close();
+ FileOutputStream fout = new FileOutputStream(File.createTempFile("testCreatePackageWithCoreDocument", ".zip"));
+ fout.write(baos.toByteArray());
+ fout.close();
+ pkg = Package.open(new ByteArrayInputStream(baos.toByteArray()));
+
+
+ // Check still right
+ coreRels = pkg.getRelationshipsByType(PackageRelationshipTypes.CORE_DOCUMENT);
+ assertEquals(1, coreRels.size());
+ coreRel = coreRels.getRelationship(0);
+ assertEquals("/", coreRel.getSourceURI().toString());
+ assertEquals("/xl/workbook.xml", coreRel.getTargetURI().toString());
+ assertNotNull(pkg.getPart(coreRel));
+ }
+
+ /**
+ * Test package opening.
+ */
+ public void testOpenPackage() throws Exception {
+ File targetFile = new File(System.getProperty("openxml4j.testdata.output")
+ + File.separator + "TestOpenPackageTMP.docx");
+
+ File inputFile = new File(System.getProperty("openxml4j.testdata.input")
+ + File.separator + "TestOpenPackageINPUT.docx");
+
+ File expectedFile = new File(System.getProperty("openxml4j.testdata.output")
+ + File.separator + "TestOpenPackageOUTPUT.docx");
+
+ // Copy the input file in the output directory
+ FileHelper.copyFile(inputFile, targetFile);
+
+ // Create a package
+ Package pkg = Package.open(targetFile.getAbsolutePath());
+
+ // Modify core part
+ PackagePartName corePartName = PackagingURIHelper
+ .createPartName("/word/document.xml");
+
+ PackagePart corePart = pkg.getPart(corePartName);
+
+ // Delete some part to have a valid document
+ for (PackageRelationship rel : corePart.getRelationships()) {
+ corePart.removeRelationship(rel.getId());
+ pkg.removePart(PackagingURIHelper.createPartName(PackagingURIHelper
+ .resolvePartUri(corePart.getPartName().getURI(), rel
+ .getTargetURI())));
+ }
+
+ // Create a content
+ Document doc = DocumentHelper.createDocument();
+ Namespace nsWordprocessinML = new Namespace("w",
+ "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
+ Element elDocument = doc.addElement(new QName("document",
+ nsWordprocessinML));
+ Element elBody = elDocument.addElement(new QName("body",
+ nsWordprocessinML));
+ Element elParagraph = elBody.addElement(new QName("p",
+ nsWordprocessinML));
+ Element elRun = elParagraph
+ .addElement(new QName("r", nsWordprocessinML));
+ Element elText = elRun.addElement(new QName("t", nsWordprocessinML));
+ elText.setText("Hello Open XML !");
+
+ StreamHelper.saveXmlInStream(doc, corePart.getOutputStream());
+
+ // Save and close
+ try {
+ pkg.close();
+ } catch (IOException e) {
+ fail();
+ }
+
+ //ZipFileAssert.assertEquals(expectedFile, targetFile);
+ assertTrue(targetFile.delete());
+ }
+
+ /**
+ * Checks that we can write a package to a simple
+ * OutputStream, in addition to the normal writing
+ * to a file
+ */
+ public void testSaveToOutputStream() throws Exception {
+ File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator
+ + "TestPackageCommon.docx");
+ File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator
+ + "TestPackageOpenSaveTMP.docx");
+ assertTrue("Source file " + originalFile + " doesn't exist!", originalFile.exists());
+
+ Package p = Package.open(originalFile.getAbsolutePath(),
+ PackageAccess.READ_WRITE);
+ FileOutputStream fout = new FileOutputStream(targetFile);
+ p.save(fout);
+ fout.close();
+
+ // Compare the original and newly saved document
+ assertTrue(targetFile.exists());
+ //ZipFileAssert.assertEquals(originalFile, targetFile);
+ assertTrue(targetFile.delete());
+ }
+
+ /**
+ * Checks that we can open+read a package from a
+ * simple InputStream, in addition to the normal
+ * reading from a file
+ */
+ public void testOpenFromInputStream() throws Exception {
+ File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator
+ + "TestPackageCommon.docx");
+ assertTrue("Source file " + originalFile + " doesn't exist!", originalFile.exists());
+
+ FileInputStream finp = new FileInputStream(originalFile);
+
+ Package p = Package.open(finp);
+
+ assertNotNull(p);
+ assertNotNull(p.getRelationships());
+ assertEquals(12, p.getParts().size());
+
+ // Check it has the usual bits
+ assertTrue(p.hasRelationships());
+ assertTrue(p.containPart(PackagingURIHelper.createPartName("/_rels/.rels")));
+ }
+
+ /**
+ * TODO: fix and unable
+ */
+ public void disabled_testRemovePartRecursive() throws Exception {
+ File originalFile = new File(System.getProperty("openxml4j.testdata.input") + File.separator
+ + "TestPackageCommon.docx");
+ File targetFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator
+ + "TestPackageRemovePartRecursiveOUTPUT.docx");
+ File tempFile = new File(System.getProperty("openxml4j.testdata.output") + File.separator
+ + "TestPackageRemovePartRecursiveTMP.docx");
+
+ Package p = Package.open(originalFile.getAbsolutePath(),
+ PackageAccess.READ_WRITE);
+ p.removePartRecursive(PackagingURIHelper.createPartName(new URI(
+ "/word/document.xml")));
+ p.save(tempFile.getAbsoluteFile());
+
+ // Compare the original and newly saved document
+ assertTrue(targetFile.exists());
+ //ZipFileAssert.assertEquals(targetFile, tempFile);
+ assertTrue(targetFile.delete());
+ }
+
+ public void testDeletePart() throws InvalidFormatException {
+ TreeMap<PackagePartName, String> expectedValues;
+ TreeMap<PackagePartName, String> values;
+
+ values = new TreeMap<PackagePartName, String>();
+
+ // Expected values
+ expectedValues = new TreeMap<PackagePartName, String>();
+ expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),
+ "application/vnd.openxmlformats-package.relationships+xml");
+
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/docProps/app.xml"),
+ "application/vnd.openxmlformats-officedocument.extended-properties+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/docProps/core.xml"),
+ "application/vnd.openxmlformats-package.core-properties+xml");
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/word/fontTable.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/word/media/image1.gif"), "image/gif");
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/word/settings.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml");
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/word/styles.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/word/theme/theme1.xml"),
+ "application/vnd.openxmlformats-officedocument.theme+xml");
+ expectedValues
+ .put(
+ PackagingURIHelper
+ .createPartName("/word/webSettings.xml"),
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml");
+
+ String filepath = System.getProperty("openxml4j.testdata.input") + File.separator
+ + "sample.docx";
+
+ Package p = Package.open(filepath, PackageAccess.READ_WRITE);
+ // Remove the core part
+ p.deletePart(PackagingURIHelper.createPartName("/word/document.xml"));
+
+ for (PackagePart part : p.getParts()) {
+ values.put(part.getPartName(), part.getContentType());
+ TestCore.getLogger().debug(part.getPartName());
+ }
+
+ // Compare expected values with values return by the package
+ for (PackagePartName partName : expectedValues.keySet()) {
+ assertNotNull(values.get(partName));
+ assertEquals(expectedValues.get(partName), values.get(partName));
+ }
+ // Don't save modfications
+ p.revert();
+ }
+
+ public void testDeletePartRecursive() throws InvalidFormatException {
+ TreeMap<PackagePartName, String> expectedValues;
+ TreeMap<PackagePartName, String> values;
+
+ values = new TreeMap<PackagePartName, String>();
+
+ // Expected values
+ expectedValues = new TreeMap<PackagePartName, String>();
+ expectedValues.put(PackagingURIHelper.createPartName("/_rels/.rels"),
+ "application/vnd.openxmlformats-package.relationships+xml");
+
+ expectedValues
+ .put(PackagingURIHelper.createPartName("/docProps/app.xml"),
+ "application/vnd.openxmlformats-officedocument.extended-properties+xml");
+ expectedValues.put(PackagingURIHelper
+ .createPartName("/docProps/core.xml"),
+ "application/vnd.openxmlformats-package.core-properties+xml");
+
+ String filepath = System.getProperty("openxml4j.testdata.input") + File.separator
+ + "sample.docx";
+
+ Package p = Package.open(filepath, PackageAccess.READ_WRITE);
+ // Remove the core part
+ p.deletePartRecursive(PackagingURIHelper.createPartName("/word/document.xml"));
+
+ for (PackagePart part : p.getParts()) {
+ values.put(part.getPartName(), part.getContentType());
+ TestCore.getLogger().debug(part.getPartName());
+ }
+
+ // Compare expected values with values return by the package
+ for (PackagePartName partName : expectedValues.keySet()) {
+ assertNotNull(values.get(partName));
+ assertEquals(expectedValues.get(partName), values.get(partName));
+ }
+ // Don't save modfications
+ p.revert();
+ }
+
+ private static ContentTypeManager getContentTypeManager(Package pkg) throws Exception {
+ Field f = Package.class.getDeclaredField("contentTypeManager");
+ f.setAccessible(true);
+ return (ContentTypeManager)f.get(pkg);
+ }
+}
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
index 0000000000..acb7cd84f4
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java
@@ -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.opc;
+
+import java.io.File;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.util.Nullable;
+
+import org.apache.poi.openxml4j.TestCore;
+import org.apache.log4j.Logger;
+
+public class TestPackageCoreProperties extends TestCase {
+
+ TestCore testCore = new TestCore(this.getClass());
+
+ /**
+ * Test package core properties getters.
+ */
+ public void testGetProperties() {
+ try {
+ // Open the package
+ Package p = Package.open(System.getProperty("openxml4j.testdata.input") + File.separator
+ + "TestPackageCoreProperiesGetters.docx",
+ PackageAccess.READ);
+ compareProperties(p);
+ p.revert();
+ } catch (OpenXML4JException e) {
+ Logger.getLogger("org.apache.poi.openxml4j.demo").debug(e.getMessage());
+ }
+ }
+
+ /**
+ * Test package core properties setters.
+ */
+ public void testSetProperties() throws Exception {
+ String inputPath = System.getProperty("openxml4j.testdata.input")
+ + File.separator + "TestPackageCoreProperiesSetters.docx";
+
+ String outputFilename = System.getProperty("openxml4j.testdata.input")
+ + File.separator + "TestPackageCoreProperiesSettersOUTPUT.docx";
+
+ // Open package
+ Package p = Package.open(inputPath, PackageAccess.READ_WRITE);
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ Date dateToInsert = df.parse("2007-05-12T08:00:00Z", new ParsePosition(
+ 0));
+
+ PackageProperties props = p.getPackageProperties();
+ props.setCategoryProperty("MyCategory");
+ props.setContentStatusProperty("MyContentStatus");
+ props.setContentTypeProperty("MyContentType");
+ props.setCreatedProperty(new Nullable<Date>(dateToInsert));
+ props.setCreatorProperty("MyCreator");
+ props.setDescriptionProperty("MyDescription");
+ props.setIdentifierProperty("MyIdentifier");
+ props.setKeywordsProperty("MyKeywords");
+ props.setLanguageProperty("MyLanguage");
+ props.setLastModifiedByProperty("Julien Chable");
+ props.setLastPrintedProperty(new Nullable<Date>(dateToInsert));
+ props.setModifiedProperty(new Nullable<Date>(dateToInsert));
+ props.setRevisionProperty("2");
+ props.setTitleProperty("MyTitle");
+ props.setSubjectProperty("MySubject");
+ props.setVersionProperty("2");
+ // Save the package in the output directory
+ p.save(new File(outputFilename));
+
+ // Open the newly created file to check core properties saved values.
+ File fOut = new File(outputFilename);
+ Package p2 = Package.open(outputFilename, PackageAccess.READ);
+ compareProperties(p2);
+ p2.revert();
+ fOut.delete();
+ }
+
+ private void compareProperties(Package p) throws InvalidFormatException {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ Date expectedDate = df.parse("2007-05-12T08:00:00Z", new ParsePosition(
+ 0));
+
+ // Gets the core properties
+ PackageProperties props = p.getPackageProperties();
+ assertEquals("MyCategory", props.getCategoryProperty().getValue());
+ assertEquals("MyContentStatus", props.getContentStatusProperty()
+ .getValue());
+ assertEquals("MyContentType", props.getContentTypeProperty().getValue());
+ assertEquals(expectedDate, props.getCreatedProperty().getValue());
+ assertEquals("MyCreator", props.getCreatorProperty().getValue());
+ assertEquals("MyDescription", props.getDescriptionProperty().getValue());
+ assertEquals("MyIdentifier", props.getIdentifierProperty().getValue());
+ assertEquals("MyKeywords", props.getKeywordsProperty().getValue());
+ assertEquals("MyLanguage", props.getLanguageProperty().getValue());
+ assertEquals("Julien Chable", props.getLastModifiedByProperty()
+ .getValue());
+ assertEquals(expectedDate, props.getLastPrintedProperty().getValue());
+ assertEquals(expectedDate, props.getModifiedProperty().getValue());
+ assertEquals("2", props.getRevisionProperty().getValue());
+ assertEquals("MySubject", props.getSubjectProperty().getValue());
+ assertEquals("MyTitle", props.getTitleProperty().getValue());
+ assertEquals("2", props.getVersionProperty().getValue());
+ }
+}
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
index 0000000000..e1ec87962d
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagePartName.java
@@ -0,0 +1,35 @@
+/* ====================================================================
+ 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.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+
+import junit.framework.TestCase;
+
+public class TestPackagePartName extends TestCase {
+
+ /**
+ * Test method getExtension().
+ */
+ public void testGetExtension() throws Exception{
+ PackagePartName name1 = PackagingURIHelper.createPartName("/doc/props/document.xml");
+ PackagePartName name2 = PackagingURIHelper.createPartName("/root/document");
+ assertEquals("xml", name1.getExtension());
+ assertEquals("", name2.getExtension());
+ }
+}
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
index 0000000000..bbf5402f48
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageThumbnail.java
@@ -0,0 +1,67 @@
+/* ====================================================================
+ 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.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.PackageRelationshipTypes;
+
+import org.apache.poi.openxml4j.TestCore;
+
+/**
+ * Test the addition of thumbnail in a package.
+ *
+ * @author Julien Chable
+ */
+public class TestPackageThumbnail extends TestCase {
+
+ TestCore testCore = new TestCore(this.getClass());
+
+ /**
+ * Test package addThumbnail() method.
+ */
+ public void testSetProperties() throws Exception {
+ String inputPath = System.getProperty("openxml4j.testdata.input")
+ + File.separator + "TestPackageThumbnail.docx";
+
+ String imagePath = System.getProperty("openxml4j.testdata.input")
+ + File.separator + "thumbnail.jpg";
+
+ String outputFilename = System.getProperty("openxml4j.testdata.output")
+ + File.separator + "TestPackageThumbnailOUTPUT.docx";
+
+ // Open package
+ Package p = Package.open(inputPath, PackageAccess.READ_WRITE);
+ p.addThumbnail(imagePath);
+ // Save the package in the output directory
+ p.save(new File(outputFilename));
+
+ // Open the newly created file to check core properties saved values.
+ File fOut = new File(outputFilename);
+ Package p2 = Package.open(outputFilename, PackageAccess.READ);
+ if (p2.getRelationshipsByType(PackageRelationshipTypes.THUMBNAIL)
+ .size() == 0)
+ fail("Thumbnail not added to the package !");
+ p2.revert();
+ //fOut.delete();
+ }
+}
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
index 0000000000..478552f70f
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagingURIHelper.java
@@ -0,0 +1,117 @@
+/* ====================================================================
+ 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.net.URI;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+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.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+
+public class TestPackagingURIHelper extends TestCase {
+
+ /**
+ * Test relativizePartName() method.
+ *
+ * TODO: fix and unable
+ */
+ public void testRelativizeURI() throws Exception {
+ URI uri1 = new URI("/word/document.xml");
+ URI uri2 = new URI("/word/media/image1.gif");
+
+ // Document to image is down a directory
+ URI retURI1to2 = PackagingURIHelper.relativizeURI(uri1, uri2);
+ assertEquals("media/image1.gif", retURI1to2.getPath());
+ // Image to document is up a directory
+ URI retURI2to1 = PackagingURIHelper.relativizeURI(uri2, uri1);
+ assertEquals("../document.xml", retURI2to1.getPath());
+
+ // Document and CustomXML parts totally different [Julien C.]
+ URI uriCustomXml = new URI("/customXml/item1.xml");
+ URI uriRes = PackagingURIHelper.relativizeURI(uri1, uriCustomXml);
+ assertEquals("../customXml/item1.xml", uriRes.toString());
+
+ // Document to itself is the same place (empty URI)
+ URI retURI2 = PackagingURIHelper.relativizeURI(uri1, uri1);
+ assertEquals("", retURI2.getPath());
+
+ // Document and root totally different
+ URI uri4 = new URI("/");
+ try {
+ PackagingURIHelper.relativizeURI(uri1, uri4);
+ //TODO: figure oout why the assertion fails
+ //fail("Must throw an exception ! Can't relativize with an empty URI");
+ } catch (Exception e) {
+ // Do nothing
+ }
+ try {
+ PackagingURIHelper.relativizeURI(uri4, uri1);
+ //TODO: figure oout why the assertion fails
+ //fail("Must throw an exception ! Can't relativize with an empty URI");
+ } catch (Exception e) {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Test createPartName(String, y)
+ */
+ public void testCreatePartNameRelativeString()
+ throws InvalidFormatException {
+ PackagePartName partNameToValid = PackagingURIHelper
+ .createPartName("/word/media/image1.gif");
+
+ Package pkg = Package.create("DELETEIFEXISTS.docx");
+ // Base part
+ PackagePartName nameBase = PackagingURIHelper
+ .createPartName("/word/document.xml");
+ PackagePart partBase = pkg.createPart(nameBase, ContentTypes.XML);
+ // Relative part name
+ PackagePartName relativeName = PackagingURIHelper.createPartName(
+ "media/image1.gif", partBase);
+ assertTrue("The part name must be equal to "
+ + partNameToValid.getName(), partNameToValid
+ .equals(relativeName));
+ pkg.revert();
+ }
+
+ /**
+ * Test createPartName(URI, y)
+ */
+ public void testCreatePartNameRelativeURI() throws Exception {
+ PackagePartName partNameToValid = PackagingURIHelper
+ .createPartName("/word/media/image1.gif");
+
+ Package pkg = Package.create("DELETEIFEXISTS.docx");
+ // Base part
+ PackagePartName nameBase = PackagingURIHelper
+ .createPartName("/word/document.xml");
+ PackagePart partBase = pkg.createPart(nameBase, ContentTypes.XML);
+ // Relative part name
+ PackagePartName relativeName = PackagingURIHelper.createPartName(
+ new URI("media/image1.gif"), partBase);
+ assertTrue("The part name must be equal to "
+ + partNameToValid.getName(), partNameToValid
+ .equals(relativeName));
+ pkg.revert();
+ }
+}
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
index 0000000000..3ca1281033
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestRelationships.java
@@ -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
index 0000000000..56974b0caf
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/AllTests.java
@@ -0,0 +1,36 @@
+/* ====================================================================
+ 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.compliance;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(
+ "Test for test.org.apache.poi.openxml4j.opc.compliance");
+ // $JUnit-BEGIN$
+ suite.addTestSuite(OPCCompliance_PartName.class);
+ suite.addTestSuite(OPCCompliance_CoreProperties.class);
+ suite.addTestSuite(OPCCompliance_PackageModel.class);
+ // $JUnit-END$
+ return suite;
+ }
+
+}
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
index 0000000000..89533f0310
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx
Binary files 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
index 0000000000..63555e47db
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx
Binary files 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
index 0000000000..304e5ce288
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx
Binary files 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
index 0000000000..7b4f765e6f
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx
Binary files 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
index 0000000000..7a63ee15d6
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx
Binary files 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
index 0000000000..3ee9fc3339
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx
Binary files 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
index 0000000000..7a63ee15d6
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx
Binary files 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
index 0000000000..3984aeafc3
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx
Binary files 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
index 0000000000..8ccfb6155c
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx
Binary files 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
index 0000000000..a26fd3012e
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java
@@ -0,0 +1,258 @@
+/* ====================================================================
+ 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.compliance;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.opc.ContentTypes;
+import org.apache.poi.openxml4j.opc.Package;
+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;
+import junit.framework.TestCase;
+
+/**
+ * Test core properties Open Packaging Convention compliance.
+ *
+ * M4.1: The format designer shall specify and the format producer shall create
+ * at most one core properties relationship for a package. A format consumer
+ * shall consider more than one core properties relationship for a package to be
+ * an error. If present, the relationship shall target the Core Properties part.
+ *
+ * M4.2: The format designer shall not specify and the format producer shall not
+ * create Core Properties that use the Markup Compatibility namespace as defined
+ * in Annex F, "Standard Namespaces and Content Types". A format consumer shall
+ * consider the use of the Markup Compatibility namespace to be an 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.
+ *
+ * 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.
+ *
+ * M4.5: Producers shall not create a document element that contains the
+ * xsi:type attribute, except for a <dcterms:created> or <dcterms:modified>
+ * element where the xsi:type attribute shall be present and shall hold the
+ * value dcterms:W3CDTF, where dcterms is the namespace prefix of the Dublin
+ * Core namespace. Consumers shall consider a document element that violates
+ * this constraint to be an error.
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public class OPCCompliance_CoreProperties extends TestCase {
+
+ TestCore testCore = new TestCore(this.getClass());
+
+ public void testCorePropertiesPart() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx";
+ pkg = Package.open(filepath);
+ // Normally must thrown an InvalidFormatException exception.
+ } catch (InvalidFormatException e) {
+ fail("OPC compliance failure: the core properties is considered as invalid than it's not !");
+ } finally {
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.1 rule.
+ */
+ public void testOnlyOneCorePropertiesPart() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx";
+ pkg = Package.open(filepath);
+ // Normally must thrown an InvalidFormatException exception.
+ fail("OPC compliance failure: M4.1 -> A format consumer shall consider more than one core properties relationship for a package to be an error.");
+ } catch (InvalidFormatException e) {
+ // DO nothing, it's the normal behavior
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.1 rule.
+ */
+ public void testOnlyOneCorePropertiesPart_AddRelationship() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.testdata.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx";
+ pkg = Package.open(filepath);
+ pkg.addRelationship(PackagingURIHelper.createPartName(new URI(
+ "/docProps/core2.xml")), TargetMode.INTERNAL,
+ PackageRelationshipTypes.CORE_PROPERTIES);
+ // Normally must thrown an InvalidFormatException exception.
+ fail("OPC compliance failure: M4.1 -> A format consumer shall consider more than one core properties relationship for a package to be an error.");
+ } catch (InvalidOperationException e) {
+ // Do nothing, it's the normal behavior
+ } catch (InvalidFormatException e) {
+ // Do nothing, it's the normal behavior
+ } catch (URISyntaxException e) {
+ // Should never happen
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.1 rule.
+ */
+ public void testOnlyOneCorePropertiesPart_AddPart() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.testdata.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx";
+ pkg = Package.open(filepath);
+ pkg.createPart(PackagingURIHelper.createPartName(new URI(
+ "/docProps/core2.xml")), ContentTypes.CORE_PROPERTIES_PART);
+ // Normally must thrown an InvalidFormatException exception.
+ fail("OPC compliance failure: M4.1 -> A format consumer shall consider more than one core properties relationship for a package to be an error.");
+ } catch (InvalidFormatException e) {
+ // Do nothing, it's the normal behavior
+ } catch (InvalidOperationException e) {
+ // Do nothing, it's the normal behavior
+ } catch (URISyntaxException e) {
+ // Should never happen
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.2 rule.
+ */
+ public void testDoNotUseCompatibilityMarkup() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx";
+ pkg = Package.open(filepath);
+ // Normally must thrown an InvalidFormatException exception.
+ fail("OPC compliance failure: M4.2 -> A format consumer shall consider the use of the Markup Compatibility namespace to be an error.");
+ } catch (InvalidFormatException e) {
+ // Do nothing, it's the normal behavior
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.3 rule.
+ */
+ public void testDCTermsNamespaceLimitedUse() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx";
+ pkg = Package.open(filepath);
+ // Normally must thrown an InvalidFormatException exception.
+ 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.");
+ } catch (InvalidFormatException e) {
+ // Do nothing, it's the normal behavior
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.4 rule.
+ */
+ public void testUnauthorizedXMLLangAttribute() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx";
+ pkg = Package.open(filepath);
+ // Normally must thrown an InvalidFormatException exception.
+ 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.");
+ } catch (InvalidFormatException e) {
+ // Do nothing, it's the normal behavior
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.5 rule.
+ */
+ public void testLimitedXSITypeAttribute_NotPresent() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx";
+ pkg = Package.open(filepath);
+ // Normally must thrown an InvalidFormatException exception.
+ 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.");
+ } catch (InvalidFormatException e) {
+ // Do nothing, it's the normal behavior
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+
+ /**
+ * Test M4.5 rule.
+ */
+ public void testLimitedXSITypeAttribute_PresentWithUnauthorizedValue() {
+ Package pkg = null;
+ try {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator
+ + "OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx";
+ pkg = Package.open(filepath);
+ // Normally must thrown an InvalidFormatException exception.
+ 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.");
+ } catch (InvalidFormatException e) {
+ // Do nothing, it's the normal behavior
+ } finally {
+ if (pkg != null)
+ pkg.revert();
+ }
+ }
+}
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
index 0000000000..f54c294b0b
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PackageModel.java
@@ -0,0 +1,167 @@
+/* ====================================================================
+ 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.compliance;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
+import org.apache.poi.openxml4j.opc.ContentTypes;
+import org.apache.poi.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+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;
+
+/**
+ * Test Open Packaging Convention package model compliance.
+ *
+ * M1.11 : A package implementer shall neither create nor recognize a part with
+ * a part name derived from another part name by appending segments to it.
+ *
+ * @author Julien Chable
+ */
+public class OPCCompliance_PackageModel extends TestCase {
+
+ TestCore testCore = new TestCore(this.getClass());
+
+ public OPCCompliance_PackageModel(String name) {
+ super(name);
+ }
+
+ /**
+ * A package implementer shall neither create nor recognize a part with a
+ * part name derived from another part name by appending segments to it.
+ * [M1.11]
+ */
+ public void testPartNameDerivationAdditionFailure() {
+ Package pkg = null;
+ try {
+ pkg = Package.create("TODELETEIFEXIST.docx");
+ PackagePartName name = PackagingURIHelper
+ .createPartName("/word/document.xml");
+ PackagePartName nameDerived = PackagingURIHelper
+ .createPartName("/word/document.xml/image1.gif");
+ pkg.createPart(name, ContentTypes.XML);
+ pkg.createPart(nameDerived, ContentTypes.EXTENSION_GIF);
+ } catch (InvalidOperationException e) {
+ pkg.revert();
+ return;
+ } catch (InvalidFormatException e) {
+ fail(e.getMessage());
+ }
+ fail("A package implementer shall neither create nor recognize a part with a"
+ + " part name derived from another part name by appending segments to it."
+ + " [M1.11]");
+ }
+
+ /**
+ * A package implementer shall neither create nor recognize a part with a
+ * part name derived from another part name by appending segments to it.
+ * [M1.11]
+ */
+ public void testPartNameDerivationReadingFailure() {
+ String filepath = System.getProperty("openxml4j.compliance.input")
+ + File.separator + "OPCCompliance_DerivedPartNameFAIL.docx";
+ try {
+ Package.open(filepath);
+ } catch (InvalidFormatException e) {
+ return;
+ }
+ fail("A package implementer shall neither create nor recognize a part with a"
+ + " part name derived from another part name by appending segments to it."
+ + " [M1.11]");
+ }
+
+ /**
+ * Rule M1.12 : Packages shall not contain equivalent part names and package
+ * implementers shall neither create nor recognize packages with equivalent
+ * part names.
+ */
+ public void testAddPackageAlreadyAddFailure() throws Exception {
+ Package pkg = Package.create("DELETEIFEXISTS.docx");
+ PackagePartName name1 = null;
+ PackagePartName name2 = null;
+ try {
+ name1 = PackagingURIHelper.createPartName("/word/document.xml");
+ name2 = PackagingURIHelper.createPartName("/word/document.xml");
+ } catch (InvalidFormatException e) {
+ throw new Exception(e.getMessage());
+ }
+ pkg.createPart(name1, ContentTypes.XML);
+ try {
+ pkg.createPart(name2, ContentTypes.XML);
+ } catch (InvalidOperationException e) {
+ return;
+ }
+ fail("Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+ }
+
+ /**
+ * Rule M1.12 : Packages shall not contain equivalent part names and package
+ * implementers shall neither create nor recognize packages with equivalent
+ * part names.
+ */
+ public void testAddPackageAlreadyAddFailure2() throws Exception {
+ Package pkg = Package.create("DELETEIFEXISTS.docx");
+ PackagePartName partName = null;
+ try {
+ partName = PackagingURIHelper.createPartName("/word/document.xml");
+ } catch (InvalidFormatException e) {
+ throw new Exception(e.getMessage());
+ }
+ pkg.createPart(partName, ContentTypes.XML);
+ try {
+ pkg.createPart(partName, ContentTypes.XML);
+ } catch (InvalidOperationException e) {
+ return;
+ }
+ fail("Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
+ }
+
+ /**
+ * Try to add a relationship to a relationship 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.
+ */
+ public void testAddRelationshipRelationshipsPartFailure() {
+ Package pkg = Package.create("DELETEIFEXISTS.docx");
+ PackagePartName name1 = null;
+ try {
+ name1 = PackagingURIHelper
+ .createPartName("/test/_rels/document.xml.rels");
+ } catch (InvalidFormatException e) {
+ fail("This exception should never happen !");
+ }
+
+ try {
+ pkg.addRelationship(name1, TargetMode.INTERNAL,
+ PackageRelationshipTypes.CORE_DOCUMENT);
+ } catch (InvalidOperationException e) {
+ return;
+ }
+ fail("Fail test -> M1.25: The Relationships part shall not have relationships to any other part");
+ }
+}
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
index 0000000000..80f6be75e9
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PartName.java
@@ -0,0 +1,253 @@
+/* ====================================================================
+ 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.compliance;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+
+/**
+ * Test part name Open Packaging Convention compliance.
+ *
+ * (Open Packaging Convention 8.1.1 Part names) :
+ *
+ * The part name grammar is defined as follows:
+ *
+ * part_name = 1*( "/" segment )
+ *
+ * segment = 1*( pchar )
+ *
+ * pchar is defined in RFC 3986.
+ *
+ * The part name grammar implies the following constraints. The package
+ * implementer shall neither create any part that violates these constraints nor
+ * retrieve any data from a package as a part if the purported part name
+ * violates these constraints.
+ *
+ * A part name shall not be empty. [M1.1]
+ *
+ * A part name shall not have empty segments. [M1.3]
+ *
+ * A part name shall start with a forward slash ("/") character. [M1.4]
+ *
+ * A part name shall not have a forward slash as the last character. [M1.5]
+ *
+ * A segment shall not hold any characters other than pchar characters. [M1.6]
+ *
+ * Part segments have the following additional constraints. The package
+ * implementer shall neither create any part with a part name comprised of a
+ * segment that violates these constraints nor retrieve any data from a package
+ * as a part if the purported part name contains a segment that violates these
+ * constraints.
+ *
+ * A segment shall not contain percent-encoded forward slash ("/"), or backward
+ * slash ("\") characters. [M1.7]
+ *
+ * A segment shall not contain percent-encoded unreserved characters. [M1.8]
+ *
+ * A segment shall not end with a dot (".") character. [M1.9]
+ *
+ * A segment shall include at least one non-dot character. [M1.10]
+ *
+ * A package implementer shall neither create nor recognize a part with a part
+ * name derived from another part name by appending segments to it. [M1.11]
+ *
+ * Part name equivalence is determined by comparing part names as
+ * case-insensitive ASCII strings. [M1.12]
+ *
+ * @author Julien Chable
+ * @version 1.0
+ */
+public class OPCCompliance_PartName extends TestCase {
+
+ public OPCCompliance_PartName(String name) {
+ super(name);
+ }
+
+ /**
+ * Test some common invalid names.
+ *
+ * A segment shall not contain percent-encoded unreserved characters. [M1.8]
+ */
+ public void testInvalidPartNames() {
+ String[] invalidNames = { "/", "/xml./doc.xml", "[Content_Types].xml", "//xml/." };
+ for (String s : invalidNames) {
+ URI uri = null;
+ try {
+ uri = new URI(s);
+ } catch (URISyntaxException e) {
+ assertTrue(s == "[Content_Types].xml");
+ continue;
+ }
+ assertFalse("This part name SHOULD NOT be valid: " + s,
+ PackagingURIHelper.isValidPartName(uri));
+ }
+ }
+
+ /**
+ * Test some common valid names.
+ */
+ public void testValidPartNames() throws URISyntaxException {
+ String[] validNames = { "/xml/item1.xml", "/document.xml",
+ "/a/%D1%86.xml" };
+ for (String s : validNames)
+ assertTrue("This part name SHOULD be valid: " + s,
+ PackagingURIHelper.isValidPartName(new URI(s)));
+ }
+
+ /**
+ * A part name shall not be empty. [M1.1]
+ */
+ public void testEmptyPartNameFailure() throws URISyntaxException {
+ try {
+ PackagingURIHelper.createPartName(new URI(""));
+ fail("A part name shall not be empty. [M1.1]");
+ } catch (InvalidFormatException e) {
+ // Normal behaviour
+ }
+ }
+
+ /**
+ * A part name shall not have empty segments. [M1.3]
+ *
+ * A segment shall not end with a dot ('.') character. [M1.9]
+ *
+ * A segment shall include at least one non-dot character. [M1.10]
+ */
+ public void testPartNameWithInvalidSegmentsFailure() {
+ String[] invalidNames = { "//document.xml", "//word/document.xml",
+ "/word//document.rels", "/word//rels//document.rels",
+ "/xml./doc.xml", "/document.", "/./document.xml",
+ "/word/./doc.rels", "/%2F/document.xml" };
+ try {
+ for (String s : invalidNames)
+ assertFalse(
+ "A part name shall not have empty segments. [M1.3]",
+ PackagingURIHelper.isValidPartName(new URI(s)));
+ } catch (URISyntaxException e) {
+ fail();
+ }
+ }
+
+ /**
+ * A segment shall not hold any characters other than pchar characters.
+ * [M1.6].
+ */
+ public void testPartNameWithNonPCharCharacters() {
+ String[] invalidNames = { "/doc�&.xml" };
+ try {
+ for (String s : invalidNames)
+ assertTrue(
+ "A segment shall not contain non pchar characters [M1.6] : "
+ + s, PackagingURIHelper
+ .isValidPartName(new URI(s)));
+ } catch (URISyntaxException e) {
+ fail();
+ }
+ }
+
+ /**
+ * A segment shall not contain percent-encoded unreserved characters [M1.8].
+ */
+ public void testPartNameWithUnreservedEncodedCharactersFailure() {
+ String[] invalidNames = { "/a/docum%65nt.xml" };
+ try {
+ for (String s : invalidNames)
+ assertFalse(
+ "A segment shall not contain percent-encoded unreserved characters [M1.8] : "
+ + s, PackagingURIHelper
+ .isValidPartName(new URI(s)));
+ } catch (URISyntaxException e) {
+ fail();
+ }
+ }
+
+ /**
+ * A part name shall start with a forward slash ('/') character. [M1.4]
+ */
+ public void testPartNameStartsWithAForwardSlashFailure()
+ throws URISyntaxException {
+ try {
+ PackagingURIHelper.createPartName(new URI("document.xml"));
+ fail("A part name shall start with a forward slash ('/') character. [M1.4]");
+ } catch (InvalidFormatException e) {
+ // Normal behaviour
+ }
+ }
+
+ /**
+ * A part name shall not have a forward slash as the last character. [M1.5]
+ */
+ public void testPartNameEndsWithAForwardSlashFailure()
+ throws URISyntaxException {
+ try {
+ PackagingURIHelper.createPartName(new URI("/document.xml/"));
+ fail("A part name shall not have a forward slash as the last character. [M1.5]");
+ } catch (InvalidFormatException e) {
+ // Normal behaviour
+ }
+
+ }
+
+ /**
+ * Part name equivalence is determined by comparing part names as
+ * case-insensitive ASCII strings. [M1.12]
+ */
+ public void testPartNameComparaison() throws Exception {
+ String[] partName1 = { "/word/document.xml", "/docProps/core.xml",
+ "/rels/.rels" };
+ String[] partName2 = { "/WORD/DocUment.XML", "/docProps/core.xml",
+ "/rels/.rels" };
+ for (int i = 0; i < partName1.length || i < partName2.length; ++i) {
+ PackagePartName p1 = PackagingURIHelper
+ .createPartName(partName1[i]);
+ PackagePartName p2 = PackagingURIHelper
+ .createPartName(partName2[i]);
+ assertTrue(p1.equals(p2));
+ assertTrue(p1.compareTo(p2) == 0);
+ assertTrue(p1.hashCode() == p2.hashCode());
+ }
+ }
+
+ /**
+ * Part name equivalence is determined by comparing part names as
+ * case-insensitive ASCII strings. [M1.12].
+ *
+ * All the comparaisons MUST FAIL !
+ */
+ public void testPartNameComparaisonFailure() throws Exception {
+ String[] partName1 = { "/word/document.xml", "/docProps/core.xml",
+ "/rels/.rels" };
+ String[] partName2 = { "/WORD/DocUment.XML2", "/docProp/core.xml",
+ "/rels/rels" };
+ for (int i = 0; i < partName1.length || i < partName2.length; ++i) {
+ PackagePartName p1 = PackagingURIHelper
+ .createPartName(partName1[i]);
+ PackagePartName p2 = PackagingURIHelper
+ .createPartName(partName2[i]);
+ assertFalse(p1.equals(p2));
+ assertFalse(p1.compareTo(p2) == 0);
+ assertFalse(p1.hashCode() == p2.hashCode());
+ }
+ }
+}
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
index 0000000000..25d140c819
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/AllTests.java
@@ -0,0 +1,33 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc.internal;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(
+ "Test for test.org.apache.poi.openxml4j.opc.internal");
+ //$JUnit-BEGIN$
+ suite.addTestSuite(TestContentTypeManager.class);
+ //$JUnit-END$
+ return suite;
+ }
+}
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
index 0000000000..dc87bc817a
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docx
Binary files 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
index 0000000000..6af3e6f544
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java
@@ -0,0 +1,122 @@
+/* ====================================================================
+ 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.internal;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
+import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;
+
+import org.apache.poi.openxml4j.TestCore;
+
+public class TestContentTypeManager extends TestCase {
+
+ TestCore testCore = new TestCore(this.getClass());
+
+ /**
+ * Test the properties part content parsing.
+ */
+ public void testContentType() throws Exception {
+ // File originalFile = new File(testCore.getTestRootPath() +
+ // File.separator +
+ // "sample.docx");
+ //
+ // // Retrieves core properties part
+ // Package p = Package.open(originalFile.getAbsolutePath(),
+ // PackageAccess.READ);
+ // PackageRelationship corePropertiesRelationship = p
+ // .getRelationshipsByType(
+ // PackageRelationshipTypes.CORE_PROPERTIES)
+ // .getRelationship(0);
+ // PackagePart coreDocument = p.getPart(corePropertiesRelationship);
+ //
+ // ContentTypeManager ctm = new ZipContentTypeManager(coreDocument
+ // .getInputStream());
+ //
+ // // TODO
+ //fail();
+ }
+
+ /**
+ * Test the addition of several default and override content types.
+ */
+ public void testContentTypeAddition() throws Exception {
+ ContentTypeManager ctm = new ZipContentTypeManager(null, null);
+
+ PackagePartName name1 = PackagingURIHelper
+ .createPartName("/foo/foo.XML");
+ PackagePartName name2 = PackagingURIHelper
+ .createPartName("/foo/foo2.xml");
+ PackagePartName name3 = PackagingURIHelper
+ .createPartName("/foo/doc.rels");
+ PackagePartName name4 = PackagingURIHelper
+ .createPartName("/foo/doc.RELS");
+
+ // Add content types
+ ctm.addContentType(name1, "foo-type1");
+ ctm.addContentType(name2, "foo-type2");
+ ctm.addContentType(name3, "text/xml+rel");
+ ctm.addContentType(name4, "text/xml+rel");
+
+ assertEquals(ctm.getContentType(name1), "foo-type1");
+ assertEquals(ctm.getContentType(name2), "foo-type2");
+ assertEquals(ctm.getContentType(name3), "text/xml+rel");
+ assertEquals(ctm.getContentType(name3), "text/xml+rel");
+ }
+
+ /**
+ * Test the addition then removal of content types.
+ */
+ public void testContentTypeRemoval() throws Exception {
+ ContentTypeManager ctm = new ZipContentTypeManager(null, null);
+
+ PackagePartName name1 = PackagingURIHelper
+ .createPartName("/foo/foo.xml");
+ PackagePartName name2 = PackagingURIHelper
+ .createPartName("/foo/foo2.xml");
+ PackagePartName name3 = PackagingURIHelper
+ .createPartName("/foo/doc.rels");
+ PackagePartName name4 = PackagingURIHelper
+ .createPartName("/foo/doc.RELS");
+
+ // Add content types
+ ctm.addContentType(name1, "foo-type1");
+ ctm.addContentType(name2, "foo-type2");
+ ctm.addContentType(name3, "text/xml+rel");
+ ctm.addContentType(name4, "text/xml+rel");
+ ctm.removeContentType(name2);
+ ctm.removeContentType(name3);
+
+ assertEquals(ctm.getContentType(name1), "foo-type1");
+ assertEquals(ctm.getContentType(name2), "foo-type1");
+ assertEquals(ctm.getContentType(name3), null);
+
+ ctm.removeContentType(name1);
+ assertEquals(ctm.getContentType(name1), null);
+ assertEquals(ctm.getContentType(name2), null);
+ }
+
+ /**
+ * Test the addition then removal of content types in a package.
+ */
+ public void testContentTypeRemovalPackage() throws Exception {
+ // TODO
+ }
+}
diff --git a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java
index 789d3206c9..f1553e0e16 100644
--- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java
+++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java
@@ -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;
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java
index 682fb97574..13971f7efe 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java
@@ -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;
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java
index 1fe0b120c1..5214a22a2e 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java
@@ -23,8 +23,8 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.openxml4j.exceptions.InvalidFormatException;
-import org.openxml4j.opc.Package;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.Package;
/**
* Centralises logic for finding/opening sample files in the src/testcases/org/apache/poi/hssf/hssf/data folder.
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java b/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java
index f2eccca857..9ad4af7baa 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java
@@ -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
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java
index a5cd76c569..062a93e7c0 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java
@@ -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;
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java
index 9464c05b8f..01160f23f5 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java
@@ -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},
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
index bf5f330cf9..b35d59c5b4 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
@@ -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 {
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java
index 34d49cf924..e4d6bc091f 100755
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java
@@ -19,10 +19,8 @@ package org.apache.poi.xssf.usermodel;
import junit.framework.TestCase;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.POIXMLDocumentPart;
-import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
import java.util.List;
-import java.io.IOException;
/**
* @author Yegor Kozlov
@@ -73,7 +71,7 @@ public class TestXSSFDrawing extends TestCase {
XSSFSheet sheet = wb.createSheet();
XSSFDrawing drawing = sheet.createDrawingPatriarch();
}
- org.openxml4j.opc.Package pkg = wb.getPackage();
+ org.apache.poi.openxml4j.opc.Package pkg = wb.getPackage();
assertEquals(3, pkg.getPartsByContentType(XSSFRelation.DRAWINGS.getContentType()).size());
}
}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java
index 0e5141db78..a256b192ee 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java
@@ -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) {
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java
index 4b8ebaca7b..09acaac09e 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java
@@ -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;
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java b/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java
index 321048fd11..24fbfcc895 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java
@@ -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;