From fce473fada9fbf45563b76514d7bc7b204decbb0 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Thu, 29 Jan 2009 12:44:31 +0000 Subject: [PATCH] import OpenXML4j codebase git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@738842 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 25 +- legal/NOTICE | 3 - maven/openxml4j.pom | 70 - maven/poi-ooxml.pom | 23 +- src/documentation/content/xdocs/book.xml | 3 +- .../content/xdocs/oxml4j/book.xml | 34 + .../content/xdocs/oxml4j/index.xml | 42 + .../content/xdocs/spreadsheet/how-to.xml | 2 +- .../eventusermodel/examples/FromHowTo.java | 2 +- .../java/org/apache/poi/POIXMLDocument.java | 8 +- .../org/apache/poi/POIXMLDocumentPart.java | 8 +- .../java/org/apache/poi/POIXMLFactory.java | 4 +- .../java/org/apache/poi/POIXMLProperties.java | 8 +- .../poi/POIXMLPropertiesTextExtractor.java | 4 +- .../org/apache/poi/POIXMLTextExtractor.java | 2 +- .../java/org/apache/poi/dev/OOXMLLister.java | 10 +- .../poi/extractor/ExtractorFactory.java | 11 +- .../exceptions/InvalidFormatException.java | 27 + .../exceptions/InvalidOperationException.java | 32 + .../exceptions/OpenXML4JException.java | 34 + .../exceptions/OpenXML4JRuntimeException.java | 34 + .../opc/CertificateEmbeddingOption.java | 33 + .../poi/openxml4j/opc/CompressionOption.java | 47 + .../poi/openxml4j/opc/Configuration.java | 43 + .../poi/openxml4j/opc/ContentTypes.java | 129 ++ .../poi/openxml4j/opc/EncryptionOption.java | 29 + .../org/apache/poi/openxml4j/opc/Package.java | 1396 +++++++++++++++++ .../poi/openxml4j/opc/PackageAccess.java | 33 + .../poi/openxml4j/opc/PackageNamespaces.java | 52 + .../apache/poi/openxml4j/opc/PackagePart.java | 654 ++++++++ .../openxml4j/opc/PackagePartCollection.java | 81 + .../poi/openxml4j/opc/PackagePartName.java | 509 ++++++ .../poi/openxml4j/opc/PackageProperties.java | 227 +++ .../openxml4j/opc/PackageRelationship.java | 227 +++ .../opc/PackageRelationshipCollection.java | 450 ++++++ .../opc/PackageRelationshipTypes.java | 77 + .../poi/openxml4j/opc/PackagingURIHelper.java | 623 ++++++++ .../poi/openxml4j/opc/RelationshipSource.java | 166 ++ .../poi/openxml4j/opc/StreamHelper.java | 77 + .../apache/poi/openxml4j/opc/TargetMode.java | 32 + .../apache/poi/openxml4j/opc/ZipPackage.java | 464 ++++++ .../poi/openxml4j/opc/ZipPackagePart.java | 135 ++ .../openxml4j/opc/internal/ContentType.java | 224 +++ .../opc/internal/ContentTypeManager.java | 498 ++++++ .../openxml4j/opc/internal/FileHelper.java | 91 ++ .../opc/internal/MemoryPackagePart.java | 126 ++ .../MemoryPackagePartOutputStream.java | 96 ++ .../opc/internal/PackagePropertiesPart.java | 621 ++++++++ .../opc/internal/PartMarshaller.java | 49 + .../opc/internal/PartUnmarshaller.java | 50 + .../opc/internal/ZipContentTypeManager.java | 90 ++ .../poi/openxml4j/opc/internal/ZipHelper.java | 163 ++ .../marshallers/DefaultMarshaller.java | 45 + .../PackagePropertiesMarshaller.java | 434 +++++ .../ZipPackagePropertiesMarshaller.java | 64 + .../marshallers/ZipPartMarshaller.java | 193 +++ .../signature/DigitalCertificatePart.java | 79 + .../signature/DigitalSignatureOriginPart.java | 28 + .../PackagePropertiesUnmarshaller.java | 390 +++++ .../unmarshallers/UnmarshallContext.java | 96 ++ .../signature/PackageDigitalSignature.java | 70 + .../PackageDigitalSignatureManager.java | 22 + .../apache/poi/openxml4j/util/Nullable.java | 71 + .../poi/openxml4j/util/ZipEntrySource.java | 48 + .../openxml4j/util/ZipFileZipEntrySource.java | 48 + .../util/ZipInputStreamZipEntrySource.java | 125 ++ .../poi/ss/usermodel/WorkbookFactory.java | 2 +- .../org/apache/poi/util/PackageHelper.java | 10 +- .../org/apache/poi/xslf/XSLFSlideShow.java | 12 +- .../extractor/XSLFPowerPointExtractor.java | 4 +- .../poi/xssf/eventusermodel/XSSFReader.java | 16 +- .../xssf/extractor/XSSFExcelExtractor.java | 4 +- .../apache/poi/xssf/model/CommentsTable.java | 4 +- .../poi/xssf/model/SharedStringsTable.java | 5 +- .../apache/poi/xssf/model/StylesTable.java | 4 +- .../xssf/model/XSSFChildContainingModel.java | 2 +- .../poi/xssf/usermodel/XSSFActiveXData.java | 2 +- .../poi/xssf/usermodel/XSSFDialogsheet.java | 7 - .../poi/xssf/usermodel/XSSFDrawing.java | 2 +- .../poi/xssf/usermodel/XSSFFactory.java | 4 +- .../poi/xssf/usermodel/XSSFHyperlink.java | 4 +- .../poi/xssf/usermodel/XSSFPicture.java | 4 +- .../poi/xssf/usermodel/XSSFPictureData.java | 4 +- .../poi/xssf/usermodel/XSSFRelation.java | 12 +- .../poi/xssf/usermodel/XSSFShapeGroup.java | 2 +- .../apache/poi/xssf/usermodel/XSSFSheet.java | 8 +- .../poi/xssf/usermodel/XSSFWorkbook.java | 16 +- .../poi/xwpf/extractor/XWPFWordExtractor.java | 4 +- .../xwpf/model/XWPFHeaderFooterPolicy.java | 2 +- .../poi/xwpf/usermodel/XWPFDocument.java | 8 +- .../poi/xwpf/usermodel/XWPFFactory.java | 4 +- .../testcases/org/apache/poi/TestEmbeded.java | 4 +- .../poi/TestXMLPropertiesTextExtractor.java | 8 +- .../poi/extractor/TestExtractorFactory.java | 4 +- .../org/apache/poi/openxml4j/TestCore.java | 102 ++ .../apache/poi/openxml4j/opc/AllTests.java | 38 + .../opc/INPUT/ExcelWithHyperlinks.xlsx | Bin 0 -> 11409 bytes .../opc/INPUT/TestOpenPackageINPUT.docx | Bin 0 -> 10079 bytes .../opc/INPUT/TestPackageCommon.docx | Bin 0 -> 24745 bytes .../TestPackageCoreProperiesGetters.docx | Bin 0 -> 9859 bytes .../TestPackageCoreProperiesSetters.docx | Bin 0 -> 9859 bytes .../opc/INPUT/TestPackageThumbnail.docx | Bin 0 -> 13925 bytes .../poi/openxml4j/opc/INPUT/sample.docx | Bin 0 -> 14470 bytes .../poi/openxml4j/opc/INPUT/sample.xlsx | Bin 0 -> 12050 bytes .../poi/openxml4j/opc/INPUT/thumbnail.jpg | Bin 0 -> 8550 bytes .../opc/OUTPUT/TestCreatePackageOUTPUT.docx | Bin 0 -> 1014 bytes .../TestPackageRemovePartRecursiveTMP.docx | Bin 0 -> 10245 bytes .../OUTPUT/TestPackageThumbnailOUTPUT.docx | Bin 0 -> 12068 bytes .../poi/openxml4j/opc/TestContentType.java | 117 ++ .../poi/openxml4j/opc/TestFileHelper.java | 45 + .../poi/openxml4j/opc/TestListParts.java | 104 ++ .../apache/poi/openxml4j/opc/TestPackage.java | 440 ++++++ .../opc/TestPackageCoreProperties.java | 125 ++ .../openxml4j/opc/TestPackagePartName.java | 35 + .../openxml4j/opc/TestPackageThumbnail.java | 67 + .../openxml4j/opc/TestPackagingURIHelper.java | 117 ++ .../poi/openxml4j/opc/TestRelationships.java | 273 ++++ .../openxml4j/opc/compliance/AllTests.java | 36 + ...erties_DCTermsNamespaceLimitedUseFAIL.docx | Bin 0 -> 9788 bytes ...rties_DoNotUseCompatibilityMarkupFAIL.docx | Bin 0 -> 9801 bytes ...imitedXSITypeAttribute_NotPresentFAIL.docx | Bin 0 -> 9786 bytes ...bute_PresentWithUnauthorizedValueFAIL.docx | Bin 0 -> 9787 bytes ...eProperties_OnlyOneCorePropertiesPart.docx | Bin 0 -> 10058 bytes ...perties_OnlyOneCorePropertiesPartFAIL.docx | Bin 0 -> 24250 bytes .../OPCCompliance_CoreProperties_SUCCESS.docx | Bin 0 -> 10058 bytes ...ties_UnauthorizedXMLLangAttributeFAIL.docx | Bin 0 -> 9814 bytes .../OPCCompliance_DerivedPartNameFAIL.docx | Bin 0 -> 24759 bytes .../OPCCompliance_CoreProperties.java | 258 +++ .../OPCCompliance_PackageModel.java | 167 ++ .../compliance/OPCCompliance_PartName.java | 253 +++ .../poi/openxml4j/opc/internal/AllTests.java | 33 + .../openxml4j/opc/internal/INPUT/sample.docx | Bin 0 -> 14279 bytes .../opc/internal/TestContentTypeManager.java | 122 ++ .../apache/poi/ss/TestWorkbookFactory.java | 2 +- .../apache/poi/xslf/TestXSLFSlideShow.java | 4 +- .../apache/poi/xssf/XSSFTestDataSamples.java | 4 +- .../xssf/eventusermodel/TestXSSFReader.java | 2 +- .../poi/xssf/model/TestCommentsTable.java | 5 +- .../usermodel/TestFormulaEvaluatorOnXSSF.java | 2 +- .../poi/xssf/usermodel/TestXSSFBugs.java | 6 +- .../poi/xssf/usermodel/TestXSSFDrawing.java | 4 +- .../poi/xssf/usermodel/TestXSSFHyperlink.java | 1 - .../poi/xssf/usermodel/TestXSSFWorkbook.java | 8 +- .../org/apache/poi/xwpf/TestXWPFDocument.java | 4 +- 144 files changed, 11877 insertions(+), 250 deletions(-) delete mode 100755 maven/openxml4j.pom create mode 100755 src/documentation/content/xdocs/oxml4j/book.xml create mode 100755 src/documentation/content/xdocs/oxml4j/index.xml create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidOperationException.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JException.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/exceptions/OpenXML4JRuntimeException.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/CertificateEmbeddingOption.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/CompressionOption.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/Configuration.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/ContentTypes.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/EncryptionOption.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/Package.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackageAccess.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackageNamespaces.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartCollection.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePartName.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackageProperties.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationship.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipTypes.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/PackagingURIHelper.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/RelationshipSource.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/TargetMode.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentType.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ContentTypeManager.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/FileHelper.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePart.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePartOutputStream.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartMarshaller.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PartUnmarshaller.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/DefaultMarshaller.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/PackagePropertiesMarshaller.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalCertificatePart.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/signature/DigitalSignatureOriginPart.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/UnmarshallContext.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignature.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/opc/signature/PackageDigitalSignatureManager.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/util/Nullable.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java create mode 100755 src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/TestCore.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/AllTests.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestFileHelper.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestListParts.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagePartName.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageThumbnail.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackagingURIHelper.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestRelationships.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/AllTests.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PackageModel.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_PartName.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/AllTests.java create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docx create mode 100755 src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java 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. - - @@ -144,8 +142,6 @@ under the License. - - @@ -354,7 +350,6 @@ under the License. - @@ -377,10 +372,6 @@ under the License. - - - - @@ -524,7 +515,6 @@ under the License. - @@ -805,6 +795,9 @@ under the License. + + + @@ -812,6 +805,7 @@ under the License. + @@ -1129,16 +1123,7 @@ FORREST_HOME environment variable! - - - - - - - - - - + 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 @@ - - - - - - 4.0.0 - org.openxml4j - openxml4j - @VERSION@ - jar - OpenXML4J - http://openxml4j.org/ - Office Open XML File Format library for Java - - - - OpenXML4J Users List - http://sourceforge.net/mailarchive/forum.php?forum_name=openxml4j-users - - - OpenXML4J Developer List - http://sourceforge.net/mailarchive/forum.php?forum_name=openxml4j-devs - - - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - OpenXML4J - http://www.openxml4j.org/ - - - - - dom4j - dom4j - 1.6.1 - - - log4j - log4j - 1.2.8 - - - 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 @@ - commons-logging - commons-logging - 1.1 - runtime - - - log4j - log4j - 1.2.13 - runtime - - - org.apache.poi - openxml4j - 1.0-beta + org.apache.poi + poi + @VERSION@ org.apache.poi ooxml-schemas 1.0 + + dom4j + dom4j + 1.6.1 + 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 @@ - + + 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 @@ + + + + + + + + + + + + + + + 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 @@ + + + + + +
+ POI-OpenXML4J - Java API To Access Office Open XML documents + Overview +
+ + +
+ Overview +

OpenXML4J is the POI Project's pure Java implementation of the Open Packaging Conventions (OPC) defined in + ECMA-376.

+

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.

+
+
+ History +

OpenXML4J was originally developed by http://openxml4j.org/ and contributed to POI in 2008. + Thanks to the support and guidance of Julien Chable

+
+ +
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. * *

- * 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}. *

* * @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 partMarshallers; + + /** + * Default part marshaller. + */ + protected PartMarshaller defaultPartMarshaller; + + /** + * Part unmarshallers by content type. + */ + protected Hashtable 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(5); + this.partUnmarshallers = new Hashtable(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 null. + * @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 null. + * @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( + 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 null. + */ + 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 getPartsByContentType(String contentType) { + ArrayList retArr = new ArrayList(); + 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 null. + */ + public ArrayList getPartsByRelationshipType( + String relationshipType) { + if (relationshipType == null) + throw new IllegalArgumentException("relationshipType"); + ArrayList retArr = new ArrayList(); + 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 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(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 null, 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 true if the part is logically added to this package, else + * false. + */ + 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 true if the package is valid else false + */ + 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 null. + */ + 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 null 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 true if the part have at least one relationship else + * false. + * @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 true if the specified relationship exists in this part, + * else returns false + * @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 + * null. + */ + 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 MemoryPackagePart 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 true if the content has been successfully loaded, else + * false. + * @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 { + + private static final long serialVersionUID = 2515031135957635515L; + + /** + * Arraylist use to store this collection part names as string for rule + * M1.11 optimized checking. + */ + private ArrayList registerPartNameStr = new ArrayList(); + + @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 { + + /** + * 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 true 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 true 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 true if this part name respect the relationship + * part naming convention else false. + */ + 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 true if this part name respect the relationship + * part naming convention else false. + */ + 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 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 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 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 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 created); + + /** + * Get the entity primarily responsible for making the content of the + * resource. + */ + public abstract Nullable 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 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 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 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 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 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 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 lastPrinted); + + /** + * Get the date on which the resource was changed. + */ + public abstract Nullable 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 modified); + + /** + * Get the revision number. + */ + public abstract Nullable getRevisionProperty(); + + /** + * Set the revision number. + */ + public abstract void setRevisionProperty(String revision); + + /** + * Get the topic of the content of the resource. + */ + public abstract Nullable 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 getTitleProperty(); + + /** + * Set the name given to the resource. + */ + public abstract void setTitleProperty(String title); + + /** + * Get the version number. + */ + public abstract Nullable 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 { + + private static Logger logger = Logger.getLogger("org.openxml4j.opc"); + + /** + * Package relationships ordered by ID. + */ + private TreeMap relationshipsByID; + + /** + * Package relationships ordered by type. + */ + private TreeMap 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(); + relationshipsByType = new TreeMap(); + } + + /** + * 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 null + * 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 null 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 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 iterator(String typeFilter) { + ArrayList retArr = new ArrayList(); + 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 true if the URI false. + */ + 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 + * null. + */ + 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 null. + * @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 null. + * @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 null. + * @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 null. + * @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: + * + * part_name = 1*( "/" segment ) + * + * segment = 1*( pchar ) + * + * + * (pchar is defined in RFC 3986) + * + * @param partUri + * The URI to validate. + * @return true if the URI is valid to the OPC Specifications, else + * false + * + * @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 true if the part have at least one relationship else + * false. + */ + public abstract boolean hasRelationships(); + + /** + * Checks if the specified relationship is part of this package part. + * + * @param rel + * The relationship to check. + * @return true if the specified relationship exists in this part, + * else returns false + */ + @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 true if the xml is successfully written in the stream, + * else false. + */ + 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 true if the operation succeed, else return false. + */ + 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. Operation not supported. + * + * @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 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 null. + */ + @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 null. Normally + * will never be called since the MemoryPackage is use instead. + * + * @return null + */ + @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
+ * subtype = token
+ * + * 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 parameters; + + /** + * Media type compiled pattern for parameters. + */ + private final static Pattern patternMediaType; + + static { + /* + * token = 1* + * + * separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | + * <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT + * + * CTL = + * + * CHAR = + */ + 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 = + * + * TEXT = + * + * LWS = [CRLF] 1*( SP | HT ) + * + * CR = + * + * LF = + * + * SP = + * + * HT = + * + * 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(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. + */ + private TreeMap defaultContentType; + + /** + * Override content type tree. + */ + private TreeMap overrideContentType; + + /** + * Constructor. Parses the content of the specified input stream. + * + * @param archive + * If different of null 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(); + 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 false 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(); + 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 true if the specified content type is already + * register, then false. + */ + 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 null. + * + * @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 true if the operation success, else false. + */ + 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 entry : defaultContentType.entrySet()) { + appendDefaultType(typesElem, entry); + } + + // Adding specific types if any exist + if (overrideContentType != null) { + for (Entry 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 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 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 category = new Nullable(); + + /** + * The status of the content. + * + * [Example: Values might include "Draft", "Reviewed", and "Final". end + * example] + */ + protected Nullable contentStatus = new Nullable(); + + /** + * 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 contentType = new Nullable(); + + /** + * Date of creation of the resource. + */ + protected Nullable created = new Nullable(); + + /** + * An entity primarily responsible for making the content of the resource. + */ + protected Nullable creator = new Nullable(); + + /** + * 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 description = new Nullable(); + + /** + * An unambiguous reference to the resource within a given context. + */ + protected Nullable identifier = new Nullable(); + + /** + * 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 keywords = new Nullable(); + + /** + * The language of the intellectual content of the resource. + * + * [Note: IETF RFC 3066 provides guidance on encoding to represent + * languages. end note] + */ + protected Nullable language = new Nullable(); + + /** + * 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 lastModifiedBy = new Nullable(); + + /** + * The date and time of the last printing. + */ + protected Nullable lastPrinted = new Nullable(); + + /** + * Date on which the resource was changed. + */ + protected Nullable modified = new Nullable(); + + /** + * 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 revision = new Nullable(); + + /** + * The topic of the content of the resource. + */ + protected Nullable subject = new Nullable(); + + /** + * The name given to the resource. + */ + protected Nullable title = new Nullable(); + + /** + * The version number. This value is set by the user or by the application. + */ + protected Nullable version = new Nullable(); + + /* + * Getters and setters + */ + + /** + * Get the category property. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getCategoryProperty() + */ + public Nullable getCategoryProperty() { + return category; + } + + /** + * Get content status. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getContentStatusProperty() + */ + public Nullable getContentStatusProperty() { + return contentStatus; + } + + /** + * Get content type. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getContentTypeProperty() + */ + public Nullable getContentTypeProperty() { + return contentType; + } + + /** + * Get created date. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getCreatedProperty() + */ + public Nullable 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 getCreatorProperty() { + return creator; + } + + /** + * Get description. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getDescriptionProperty() + */ + public Nullable getDescriptionProperty() { + return description; + } + + /** + * Get identifier. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getIdentifierProperty() + */ + public Nullable getIdentifierProperty() { + return identifier; + } + + /** + * Get keywords. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getKeywordsProperty() + */ + public Nullable getKeywordsProperty() { + return keywords; + } + + /** + * Get the language. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getLanguageProperty() + */ + public Nullable getLanguageProperty() { + return language; + } + + /** + * Get the author of last modifications. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getLastModifiedByProperty() + */ + public Nullable getLastModifiedByProperty() { + return lastModifiedBy; + } + + /** + * Get last printed date. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getLastPrintedProperty() + */ + public Nullable 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 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(new Date())); + else + return getDateValue(modified); + } + + /** + * Get revision. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getRevisionProperty() + */ + public Nullable getRevisionProperty() { + return revision; + } + + /** + * Get subject. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getSubjectProperty() + */ + public Nullable getSubjectProperty() { + return subject; + } + + /** + * Get title. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getTitleProperty() + */ + public Nullable getTitleProperty() { + return title; + } + + /** + * Get version. + * + * @see org.apache.poi.openxml4j.opc.PackageProperties#getVersionProperty() + */ + public Nullable 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 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 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 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 + */ + private Nullable setStringValue(String s) { + if (s == null || s.equals("")) + return new Nullable(); + else + return new Nullable(s); + } + + /** + * Convert a string value represented a date into a Nullable. + * + * @throws InvalidFormatException + * Throws if the date format isnot valid. + */ + private Nullable setDateValue(String s) throws InvalidFormatException { + if (s == null || s.equals("")) + return new Nullable(); + 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(d); + } + } + + /** + * Convert a Nullable into a String. + * + * @param d + * The Date to convert. + * @return The formated date or null. + * @see java.util.SimpleDateFormat + */ + private String getDateValue(Nullable 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 false if any marshall error occurs, else true + * @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 + * true. + */ + 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 + Namespace dfNs = Namespace.get("", PackageNamespaces.RELATIONSHIPS); + Element root = xmlOutDoc.addElement(new QName( + PackageRelationship.RELATIONSHIPS_TAG_NAME, dfNs)); + + // + + 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: and 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 or + * 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: and 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 { + + 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 true if the nullable store a value (empty string is + * considered to be a value) else false. + */ + public boolean hasValue() { + return value != null; + } + + /** + * Set the stored value to null. + */ + 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 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 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 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(); + + 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 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 { + private Iterator 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 0000000000000000000000000000000000000000..ba5ed27d23c193b684ae4f44bbde906b12d3bd9a GIT binary patch literal 11409 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..771cd181459ae101e6beab90ed0638c6bdf8eb54 GIT binary patch literal 24745 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 Vf1z6`2F6wNR|DP9Z<~KV`WNvzoT~r; literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2d8bb359b5d4a31869762e5ecbbb91e8034c001b GIT binary patch literal 9859 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2d8bb359b5d4a31869762e5ecbbb91e8034c001b GIT binary patch literal 9859 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ccc990df33a5dbe8b98f87e0ab155c0a5d9d6ff8 GIT binary patch literal 13925 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a275cf417e01a78f9562b1b13725a404f234d5d1 GIT binary patch literal 12050 zcmeHt2T)YowrxWzQF4@=vjoY|G#R1EAV?INjN}ZGBxfaqM9E1c2T9UoBnpxR$vFrJ zO8zaL`#gv1tvdf#{i;{*on6(vtGo9cz4usS%{kXxQ$-$l3mum7Q!`{?MpWWTYnkst|f$0?h5q|wY$Nw+}`Zao$+BvbC zY4@>W$_gEraU}|wkrQdDYP_CR6hvus*Mih7t}^5xtYXwCBfumn?Q=fSnWYk!l}aL6 zivW%eOECUjck;-i2no=@E)o%qq}m)45eVN1$n=Jx`@zx_1ELvd+L#)W?aoZUiOUk? z5jua;2g-%uh~@`HLO8|{mn{T9NkZ<3%D`f-4ihh+Q;lQ%?34+co^<8mZWZzmt2Z|p;1}Uar?kjrE#(2LfqMs z@Vm>?az@DI%^9oPSxKQt+qp6)8Q7Lf08e%AlA>|C{iH`1R16T3cvK#I+ z7u|cm%C}j%;KY0UUH@+Ny1*Hu&}rHGcPm*5qZ9OQYpsIY4!+4)Y#NB%^mDz0$e-E4 zMs6FEWfI`;pWnEnX0V;o)o;4brv`6q?6yUQD9WaPs;Am>? z#KHdKohtkNKhyGm%yf*59fD89J6(ISO_IxJo3^qjS$T~LvW?UaDO%}vDQlu~=)}Gq z2Lk;e#xoy|*M>xO=XO^uA5xMTaVWJnFo{=i8+lGVaWBo(#Cl~!V{VDxP4b)!`e=5} z+Q2xHhEw%D)Z#?;5i@~R$lgc_CMLTznRd1strAV<1JmEgq6-hJnqM@0>6p22$0D{A`QJmg08e?Dv z!?d3A*Q;6q2O9aXVcGdZ(C2U`v8^pX_DqYS@6Cgt#e%>EMU(GPSSQv)gWqcX>k+ar zDLfQu-vR(g04NCV)*L_O?q=s`Wn^b(^&=SlA^lrFqz}LLfA`T8KLqLE#O~Vj-@{(D z9d&gfm~zGnFMp&@NtHXAZ=D*ia6Veck1uzR?MUqyH~H>?_dA9AQ#@ZRuPCjEFn`m0fs=adF)1m^I6WmEAHVd~QWorrY(BZxLL{PUp|x)s_~^EL3Swl9Tv$ z`TJ_S(L}8W6gu-hMc|V5dTkvmcIO<_f04oosoj+8>r&w4s<{n(`TGR0&ibXQ%<;3 zdL2{ai~d$xKEMhG3%FbY;cgTQeg#}we->4wn!a)yC%!lTz9^4(k~b#`E+1}=%;P&@ zXhSZ|#u)mI)#L%bdp4+eIauH$pR{z(!Ct~XW7=MXNCKS|p1Ym#&scbgo+ORF__o$~ zmwue9^W{f0$ES$$Nu4_b*%M}0MMoJ_ewBiy=~ip1CVeC%W)B)}2c<$%g9WVz*egpB zx@<7!N$%!3fTBnk@I|=$2YdybHNtQyT&8$meR|pP%mi~~MyEQFFajbwnMRH?UR(BB zeD;A{n8~<^!@{#Qo_5V|UyKbKj=^e0WjYNVZBQzOIIUfOOJ$%Y1%(p!^caxQ$>W`IPG!C z6CvX5+(~)AQ+R=%h1lm%Fy}p|ZWiO~0X;*gCTRc8yWtH2k}ZVv%PJt)o=rZ%P=*Y8|EX8X3b zvfT_H{yVHmH!5d48^&5vWS8*Ciquj?(hl1Y3d@&?TI$(iBXZuP(@W0~+8dxr2#q_O z)~v&bjn0`2x*?^&Q-WhzNF{Nf{0I{1_*jS(jjz|$=OtQ~qv?RqEJ+%=6^EG;{WniT zJHV8q0v4Wb!58wR6tQUC#{KRJ({pLEd(-1*d-|QJEOa>H<>18B`kf#MeQHAZS155` zABdW(U$H*4-2V~I!!AThgxOGy@!~Nv`Zk_pl}K7DPtW^VU~x&sk(u|Q--w&KAe#u8 zVSWsXX#_~Z8RI+>gQiKS&Y{{fK(Mh74;@x~2yr4B!}Q~^^QQOXV`y^FbBmVsdS8CkKEO*-Z1Ql>QS1KYFh zrTaU!HL9hJ-w`j4=wIrlD;0czm{v$(^nk~czL1WYd3z4PNTWOX6Tch{y4KA&e?cpE66L}&!uIICX8^kyyf#UY2(ftSL-S_A{PPH(WfC~4YIN*{g6OP z+|}fLp$ulSgc!2LPy!4eypnQ-9+=*DX+33@FV14b51P4RDy;`>5Kalxe1MY8EG}mh zsSnuIn1t@-c;GobmfHPd$={)>7EC@Yz-A;Gt&XA^dHj|oV9$YH@~iN7gl^VZ?btbD8cJU$25iHf#Vd$!BL$))RU?uifu(rkk z^L?@VN`cV5%xQ(mdpr$2EohHOH6jA1pGmW_AZD?QN&~0*yYMB#5EtMfF@C zxcDxkX@wCxkjfR}l{@57pjFyhn%CQcP*){T)bpmrUw^fke_xFdhR(6kBogzSjh%rp z;)Cjp74J9+#eu`fF@ycP{P*P=(0eJGxfMBlasd8QUxx4)F<}p(4Cgt za&=@}>M_j3EDQ|pgkV4fFC*)f4zT~JD%zy1F9!2CKNayMFLUejH&(qS5?&h4u289& z!Iu2I_#u3p0DG@M8EXbi&U%9omUZrYTw6v3>0u80g#1#Q{7;K%4EpG_W^$pU zjwR|meJ1Gq9%+4sb7+fxwt1DLD}!a>awq6&dBj~;+!|kf5GQVL8Sfxa+B)8r5i*#e zAYxfo35tzA^+t#aC?dzcXHSkkiv7?&)A|zKvG8gpt%^rMr@cs-d>DX=b{2znoBB*8 zQQ>R4#OuUe{X{x>)R&?tsi@v#H1z~UIr{tQp&MS3go@JN~GYu;$ zaeYoBiQQ}FL%!S?>}KF*S{a9q4x1bgNwZC9>^!Xk&3HzHuZKyeY7A8(CKJ^9Yo;8R zF}DfU>F8D@@;tUOY4amW;ths1QA6fOkT*ojuT&Sf&m1J|3MG92H3YCaB*i;zNZF^c zHlS$sXjl7NQE}weuv|Iq(frc5TAf;L1N(y1LlLO;y(#T;ZzbckftP)Qy`2#}u1}!d zWA(z)0!4 z-{ui-!u@I3;d#U={6+jLk8rXuHFb94xIW$g5w{{^6qVuGeDI#^c}S023MP-3`Y5Vp z$|M5nzRQqKUDSA2%o5l4Z@J`>vU>GX`&$z}X+|epUpL7Gg$|I+v@y}VrE3mL{U(x* zI`0)SjZf4vzCZ$VwN-*dSur~rC#l0Bm|&81sWyo%Ef;~wWL2?D7L0vE`n*q@#Kblt zyco?b{fAZ}T5AjIvij~?zIe|6-6ZkOg>7_UhLRu<|PM~Q`3aM|+%^$ABO z`9ng!vue_ICR*?l^40ix#czsEA4%=^6nAy)dU|BHi!{{6Iai*%IC~$ixC5*GZur&( zTuhOg+gnAd;`LFvLM8d}+N)YRiFW3#5kF3HnxQvhyi$&k7Vk{eV{x3uTcTW~C>?F% zaw+BZsDAuMw_f}D6rRJgv43Ck3ftV5hrzj(fOAayZyf(z5nMBUogrMW2LfNf!r}Rw z^f%z8q`PO_c+ee5b$*(9jWwXr=`_+zQuH_c3(qPt)7F(0`=J%S)H6^KsTpPr+s6jP zpk-qsi}Ej?)A!zqR|sr=!biOaiiVD|x1<1-lptZ<(OLxP6+x+;5b+F;J{T&9bwe~Ltp1|ukQhPz?0h69| z!XYZ*^ac>>35E-R^y8WAV~9l^{mI$-WT3?JcH=oteP3;&qlh458uU_IG3p>5sT4M^ zbZ~9iD=iR)k6a+(o1Wpu0r_uX5lK3Wf@Ydy64p?{Ub`;)WQU{I>N3S93Z_SD#xE6) z7iN?#TotSMSIU5&($&e4&{sICUnx!KC1o~_3Uoc&V?KZVgMwcASVO^VS&<%%oxVUg z1rLss-A>6+Be(izo!o%Gqktz1HTG{&!2Mr~LbwQiQs8$@0b`Jw|B9Xj)db%KK;v;A z%?}FlPR=&9QYBvepkQaiGvgx>;qx%IZ5JR~Zvj}X{EJWH_G2-)3TER`;3`=7Xv4q5 zs-*NJtUFm-<5~rjQBcvvZLNaHhrQA7w{yF26P~cfP%{ zeW90hXFWJr z+V6WV1*s}B4M=`P^G-ab@pLKVfML006TvyKEth4)N@R~&LlKKsot3LSkTyM* z(8B>-EvtwwzkG+Z(U|=(0Vu~+H@1E^sXO+7X`r02R9h?08-pV+&0Kcf=f-A|F@bMr zzV%*4cLa{-rJpi}q@J$1V9j(aj!k z$!=@lm+2YR(=~M5%2OiGY>Q)y<-p7Bq}2=V4B}&G>c+R~Z!2@r8?n+KS>2nYuCR=y z0~c6$aANo;KecDBjwA1{o)A(T<*diN{=#{F+JEav=#H52FiDRoRetZ5yq?6A*6U zcJ4THaFN9`%oFz@b5{Mesn0t7e4X+RxH=L0J0;y~$mJ`G1as?3C>O_mb^#7AUU4|l z(RCMewlERA4Eh-B5XHUip^J0!ks<$W5qu@{`%=TJfQ~mg@X%!f_iGqG!@eT(}<2?Q;{wSrH+mls~s+}?tKiiFv;Do+?_Bt5uTe>$86S8 zR_3S-xg1^R$w<|uKPyH%*F>3*oK^}m0z9ehf9$}1wX5e&rv+6XgE`&BZ0M$ zm(s&P%~xoOMdJAA3gD>ahZ1U6!jY*Zj_ zqgH6K>{W*xkXyH?I2#o^n<~+q7B66WMsP6+>CuCvcAXvaNbcsoj%ACmLrls3-1qcJ z^bWB=EsX6-1cu`52`RqC41Ob6NX6Pg&up@3e!X&bV`2Bs?u>?i3Yw-gdG^Atfq{2^ zF`~7+HBZBC@*^}6-z_nG5OB$vNChZ)6jK3cQ8GrDI|#h2<)2>DoS*-ek)gosG5!{n zGMb~ZRoiaP<PlOCs?EQR zL&_qMTf;v_5k8{$e~hLD)X~&L-PzI7*8F-r1Qw5Z& zOQdl|H(&FY(ij{EuZdz#P40x;4qp?|Xx;ZH&}@pV(#M!U0v{k!(Wi!33Z*RKJs~K} ziJViN;qrENzXT3CI%EAaRiFDU|by5 zo@|nS%Omx2n(MX8r9tN1zPIo)%x}`M+2jcgS-6Xgho>rxKN+xxKf1H2qq?cH^A8R< zZhYouH?p+dju*DW6UXj0x5KEU$r50~I*er_Q6o{I$;)zJLd4W^!_kXb?YOtvA7Nqj-%|`o$s% zgK~tT+SslSk9hT6IJcn7`zAfV@HS~>OGuH_nw*pGfJ%@zj+<_OgB0(0$`J|{x0Cyt z)JF>jr9!-v@MfzAMXO^8c>S!!#dNA%tF^_*LNu-(KGzrS4JHt}L(>j(Cid*KP=!l;LYF*Pe+(vxApuwzAJVK^7aJ57Z@{rS5UUf(v=&54>Px}zsJy;11E0$qZVlZB zi7r_7Ug&k$2@agoWOBWGPucHwz-hp~mp627v9l;d1oQ3z(e@8G-M)MLtgh9^9>C?b zQ6Q-H(3X>&^t;R8nbNAv9^Ls4B*?w#nU^8j*(tx>$KRff{^ zJ5w+hUrg;fn*(m^EgN6>g!}ik!T-G&x&9I3KVtmvBF6jwi18mW{{Io9p3FpGEId5t z!e7`w!?ZEf+E~&8YH9l;^8^iwqqT8j-yUwBCvV%f(IKLV&3!eG`V}B4#OG(Cc3=GK zvDwPO;lhR8sNyi<$8^X`^=%7(L`rJX-a-hH5625&j=|S~Q%TfaT!rLL^D?5sVcc9a45o1WNiRQD#)Sh#yzY6#k zY`8s%K<6T7QHZ@;o$xUvZ`N3Hif9&?k+*>>|0P~s*#S9nKvs&8&YX6{Yo-C&J~#^r z^}TQ!{oTv0r{yF1BEyVv`S>lo3yO@h%&Aq>WHS#KyNpJ85 z%aq(qpKd<6vbd;%NR1UXkkr|n7}eXJ zLtRu)e-#Oju?)n{Y-3(jH^rEaRR-6>~_NvY;_}7M=KO08CxAp&X z`_4_Ao81||kXGSMsyBKyZW`b0BKT$OiFR##v!~!Dz|A`AF93F&|N8fT+u^y1a&y<> z7fLMdO_bm6VcZ0~xe4$KFdp74@dqh?+}57}e{Bie1iV>N{{@%?uWbBBz+VOSn}GkB zxDfLLUUp2U!rZ-DGzf39N6%jZu zPB#laHxX{GN`E0xlU*a+D3JWNVDk4t-%$J=(CdZo-vZv^rrP+6Te@!06-`myo!1=4ZLao*GT?n^J9iTng0>jRpf8OJ=2eNBRl{IUh{8d I{_*Mm0DX*4GXMYp literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..511996002359764296933dddebb344871e06da79 GIT binary patch literal 8550 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 K|1i|Zw*LUu5Er=s literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3da1daeb9883a1108494157d94577652ec55f1ed GIT binary patch literal 1014 zcmWIWW@Zs#-~d8B&V6PKNI($CPRUOWD9SG=)=$naO4X~#%~?C~wBKO^0oU(8xlaF1 znbYRJaZ(ARmJm~;i(tpx!Zs77x)Wwg{#M`F!qOPAN#<^G|GGCt8v|{BIy|>ttUW8e zN9f8Wleu}?zcxfIE8ewY@{%dX9?mrui+yon@{LRT7QGhGVgFFn>-9tDn3dZ4W151G zUPf(j+Se(TceIz)P#3_yz9bycmnUy6GvSwZSKb0?|*#R@3+Dhmnij?DeTtM`CJdQxv=v`cZ>e@ zlI#B5eRa~cz~Wh(9&bs|o>p9TEdBFyp}Md9=l*8K)OJeU)u_8U>GqGT!s8dOt}|2g z3y z^3KS%H@IVHss zyfOOiSNYoFR=zvKJ$2T}%X2oZTRMT)XXz5#q;100r+j^6UOg2%8oY9G&x{lIGE?qs zc`Nqxc0*j|-@~sK8!NbPXREDL{nzh)zEwulNxC)MGb{SknVpYRl*O6#ZUx6a_g}Qq zC}F+TpPyU8^Fcmb!Koy&66k&7pnMH;K;iP>^*`es?;^6kXV>9Tx>)Q>IVTVj|eAmVntNc&0b zmJ{WfyPjXlaPl=gYrddE;`F&29q#^bj952qll>-lBeV2*4Bw$!ah{Hip98PQFZ?0v zdFa`rAAXVhIhj7hPn7(3b;7a4oY)0@B6V7l@p8c~`XI0S9H@A_3+ORVu-6%xL>Lem z1v%nD83h$!WSanQRPD&23Q8Ra;03e{DXpMuM0OJ>&=Eifm;|vzL4Y?a8%Pl|5bgxh I>dYV>0QK*4;s5{u literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c8c43dce69cd355caa706993c306e5025fab6fa7 GIT binary patch literal 10245 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a41daa5ec5c70c705476ee4a616e2209dde2ef66 GIT binary patch literal 12068 zcma)i1yr2NvhLvSF2UX1-6g;vL4v!xySuvv_aMPZ2=4B|-CYwL9_(|@-8p&h?)7RG z|7zC!Ro&B7-DMw877QF60R8&O@Z{$K{6=uEV_gShD@R6#w`XO-uzWWYviQv%?C^uh z6ee*{f>R)kb13l6uQQvB;X?Em`p}vonGn5&IhMA^?%2gefzI(U)hZRmmNsdLF*yYH zPJ_KSY0lj6;3utmaYez&uq;+{CgO0uZ6#8uT|1<>86iyN1F)~l2VV69?l_*%r8+f~ zX&+|kd%Q!e;D`~w7!={*sCm%z-jhy{yRA7(Oa`iH%ErVVUsA>oVtZ~%bhX6dUc0uA zXu(iMo224>5@1p{m>GcPvpR_sWj#GGO6=cvJZ#di5qXK7e*7jsB$*LpB@~ZPWN4xr z&x+;jI9gKhE^)eW+)N)@P?a6uYr63k94HF~UGsER^6Pao)<6LO;QzW^Xs>T*irU&Z z8QVDNs<_)3J8CnySz9$E{d?P2;DUTF&fc!+l9r`(C?YYIl_^dn$9I<&8STRjjI7t zNfVpU^>v;-xq9NyuE?qNVOSB~FXxdWR6@ALath5JwB7E&rww4-`ck4U| zeut|D%MZSZ?p3HaUD?OMky?TP0Fw~^q$}iCT^ZRL0v&Aa92xcP?A{dhNmi228~8OFNQphwN0U)G9_lDfkGfR5QhR>HO-WW5MR|Gcox|vwb2j#PSZ1Ao z##0@oQ|A?51Kz+^H1#usADxhKwWbGZf}BlaE5mtzA=&LN!1hzket<|05cXv57&$<@ zs4NxAs*`bFeOlUB3(mjvv$FCBnazgyeoSYnR1KH(O=Tf%19$zBAgh69p5Pa`kGMEb zcm4TwNebtnnoB}L7v#?ibTVikv*l?R-}@B<=_5GF6oPE?Bp4mC5&h1hX~Wx#NYSz z-Llo0|LE}wZm9L;!oA_c$hP`vHK=Q@`Ix+ygF4E>>Te~y`=%jz1p05 z4D&eBCawUYg4Xu1xP)%IYk@=yYb*Z++?&M^q%K)szgi3`_&-?;%I_9qXzO77W-^tD zy0)22=)G%q=wYq~QZo#qJ+o6)i^VD#=*4oLj-zs$7%%bO)T2!~_F;1>)7cake&cs5 z-K}i7g+KxGs z7f;*9@u`u;TB^OyAJ7FYFN=MK_}(=X>E6rx$=C{dtJ@01I5|;#f#|Pf+P?{|;{AnP z1!!}TxP^%cY^*Gp=B$ubk!UeQBz(+20k1zY^g8d{6P8)d_0?6+v)wF^WApH zon2Qa#s?9)3^Tj8`SH4(;=(6m_GfInAL$B?&WKb=ecBM&CXr>PZdjt zMnRS)4taYI3!@K-CyEkTB|IflW^FnA4yjLe=i|QLHKH8512z}YzD(F?`G0iNjajnp zhpj5LOi!>xHtnN1WNx@>%>TuZv@}wh;K7Wcbld++zJzrIm%~I5PBj(}b4iBDGAsSg z&B)4xwbSiNBm2u53FkX*iz+QyD@_J_r~++bnWpVxogsuCgTcakesUV})CbI+CaGb1 z?G6GN3Sl_It0~;RPu+)#Bvo84ylVhGqZ*zIdNnr&M|U*wV4a#t-9=yW7KjA|GxpC7 zBpsW8Ha7Mc$?H3F02B2s|@;d?2ZYnPU%CfOk)!!i?=nu{x1 zu-1GBK|?0gCXI)6IZtu9m!cXDG|LgX$;<{wC-~u-2_-XmhmA+ifuY06rP+Ni_K3N$ zL(<32{s}##2MycirL)tn&IM8utC^*fs9&4fn^g-@tiD;lHuG0n#{d;xqsTt3(`zTD zMz|#h00SF?@B!hWuwvLW1@Nm*hpAqSX2T=1bJvxWtG+32Tj~-^JU*x4&>S1An>^pA zqrvm?rX!!PJdWb_RyPdJH!CZ5g%1~7l+Vxk<&aOS3(7Y>%!ltzc1oQ9sUYwR+CIu0Av6}ifMcXDQ3A_3UyH{qBVaWXIJRH7qe9O1^6Hj5so!FcM|SC}u-b7X)!pxQxsu zfhl>W+>pZYxTI(cE8%($^V4qHg&2-{cm_`RjB$+NqvEDBFuX^En_QPWF+*bgyon{9 z$~QydWmO9u{nb1@O`>~cm4^A`r$+%Dj1~c76H-Cp=gEEa&21xdo2le?fkBV6D;BSS}StFfLesG+|#T<K5CDA0h*aW&`~V|qIZJ|-RB*2-DP!1*6*lRnN5CZ< zU{^0LA7F^D61wFD_t{Y{e2bepup+29MM#%6ZQTaWl7owc7?Dt%+|_>L&@FT)nsbtKF&thD@Ue~WVc`AFm+*jmcUE=JLHH^VA#7m;|3p*) zD5X**aMWN5W8wI1zj6*9s-IFa*;o4=rHSm6hUvFH*zHI)z3L>hj1Ll3O}15aFC=j_ zs-)41Au8G*Pn#=&o=E<9vwl%1IAE>f+2(het%rom><(PhN6ohJq{@-GaVYdN9q++v zJJ+D*Qp-Kw0c+SZ>_R2@!;;G560lS85z(I#Ni3THlr=~{-Ag5EjS$s{+oRWwLXN3sEqufIY4^nGTWGgVb*HV* z6FAMP!^)>mK9r|)OaxK)2b|Pa&AEy(oa8?q%p)=s~H7*w*+1nnu zZ0cH+6-@hXqYwCQJr=_ZC3q@0KG2+&r8;)YR}PTce!rstt=_rqIAMd7F`MbWRyOo} zdidGt`1oBg9HE|u>Q%V;{V4P{wtsS-6j4jx11if31mWsnBdpdA8o^);wYzi z&!JdorFjp62;WE37|-v$eP)>~5Oz-lm*QB%D4C*dr6V_P>FgD zJYlfWQ>Ku%4{a@Mrf~9rrn9LP7LsIJb|5%JjD|16o9KJJLL`*%TQ>tM2#ZA8hHBYw zrV**o`s6JZ+ECVSrDUIwHrZtWy~j70mMz zXYH@inG&`?>p2&Q(SIJKx*dF=P2_wFD6XGamhWG8sQDkm64d|fp~-8GsiJRS^;Zbm zkf^B8#f03yCijRayXL%-=GUVSzEXe$DcHemQ!%*6(Au@Vv@}VKO3s7UQmFCb+S>B$ z;OhE9@mVi2BuJGyvWgoHxZ^=S!(&_>d2BHPTAQm1G!Ld;BCdHj*y+_~rLM02r2YBGZ1SZH1+Y{jr#QE9mIWX779 zWxd9%#Gcf4L~?R0r7#u8NjY3bIOsunwkUau#4!Rpl^izr5@vbQMVjej-^oepxo}I} z7v~XKrl4Ql@t$7+)a5I}MH0yFqV!fe1lRP~Aj7b&;XR8u9QE7Op6fQf3OH9XNwtL- zw`M=6?K6j~<#DoT?gU~4*GxX4dSBXeiz=+cg$Kt;b+U4FMyBMBA&1{h#5ddXePjvL z5gQS(AtbSXcn-bTmooCYe+G%S6VUs1 z2qnq%h@Ylg4;y>$pC0JM>KOBW2{(KA%gw~KZTwki$kn!QNby770q>hq-LmkLlV4*^ zqJK<8G5(`eYhxpGeMWO@eN$s*22*nrkVRMk=u&{7(aj{xnDkdW0;^dT>kpYyK z*VfbwE0@Y9@o|8)a4wJo%qWqc zcqHp3mKaGc&MZf;d=P6HoUk??F9H))Xf3?u$yLOEqhUY-VhDpMJ)lb( z89$JMs}wJur<~c}0c9p2&jvIoZ7byZgFgu5962|r&(N%r0Yr5!b`CTeEN1{XLg9j6 zG$miI2^emKpD+lQ1W*VR?K`zdHiD2;7r?yMTqGN6h+7!MRvx(o+MFj&2!!tx(;plY zgvl>1L8%-vx+c2`YNq7%f;qhpA~sJmm1Jp!2PPR5&3Pp8DP!guO9 zsEku4k^2tQJIW2IZWe~XxMH}**7nTsBV-A3SO$_<@TdFzt=Cb_t1~D zLlLZSryTU-Ik-Ch=I*{k_Le>@4&^ufvA!K9R!~6nDH_@1jC#=biScmqFo+kfzA~oT za>Wj2&%6Czw|e zjnm+y8x?dR43qOGv=0%oBN|EzE+v^qqp0aZg=7Ww)IsxJpM!x9DK|2}hJ7xpWS$lS zYI`mKLrO;^U;^=LfH*sJHcDpx76n82c`DpbC~u#j6nLYMh8hGIlkP-)IWAQxwD$wT zH3d7+RSRt&4-+$aIz%}6N@*~VUmX2egw)X${Ki(;^Z;rZLJ)PmH2^AyV6@BWa1uMA z2m@e(NK^o*%oZ3*090%#==dj-b;*okp~5vK!>||%dIfnYYZ&Obne%jstPH&z-iaMk zM4+%jLT+JsKrehuxIhoN^C!ysq8FE|slmws9knhYByRTE2!~iF>;_>-1H%x+u=)z~ zcS>yFehkqNl7ch?slcM30Kfu<=Pa29gvP$;#0q+~>Pjcr?V>W35kS!*L&!|azgS34 zkEu8j>}3s1MM+bb@hrFN3*p(>DTONgI{#Ux0j?|{S7HKOc( zX(&?kDlS855V5_mOWx-ctR z>~=!FofjPdy}S*l9hh!P`dT2@NE+A|Y$QPwA$tKQDUSbMFeEx!K!f7H<&jh&p#ZA=~is?{fJ*luyWM!j&?yx1O9YN6HY z+Qu8C3pVk`koK5x*UB1XZw2F^%f}CgEKFjtoVrBTd_GhT$x!ycr@ah z!Gxf;pd^Xh82vpD&rZCpmlS=d@n{qfWUDw)HwX87IRZawKrAOm-ZKR;)x7^M6FF~0 zZf8)OW%3bMW8R#?oh~xKWa7oGHB|ol)JKd5pR;RW8sq`kGVYq&vRI$J+Ek5RVRb>+ zfTC)Dlt`G&1e&el!-a@yuq84Svb*6C zu;}8RlsETwppxo6Mw3QTrD5Yi8L`FCa&G4Pj4I~&!FT=0-iv^qCp%dzaU?*1Dq+Kh zm%1WlWMSv>b3N~1a-WA2K}goRaCf-KlMt(>`95eKN{zKd^tdqcPclKv3||ypzOWY= zA2YIZ17Dz~fC@<+*uBkq2zCo|QdlMA<)bVy8)b{{=KQ9Z*Q zk-C*>own|B2qM|Mq}oy@GYeez3LQRaj?)X`L2$==}>y;e$$Uoj3PnKb-djQ%HH{xtEvya$a+qW*{T z23MlWdAuYsRaL>+s{+5Q`>*qGknI$b@++l1;L&0y4b8iY=jeTQ!~~lN510=%W@<|f zPoM@f67!tfvFiuSv0PY7Mj@?F>+f>uvl_AMi>6beLlPm_5KB$7LTncHgN@$*hNwDm5D$l-;E7zN@uAtHj|R9%`$xRyV9z zl^6&ock4a*&}wf(c(>+YzH1?IAxtpF_pWb)QF}8A*Aoz&I4yHBU+vw}{IY%2{#P0= zlD>@~_nLmb# z{fU8|mGiKw#IhQ)=^cDeL$!`h_)WlWj2gtJhQc3=nHJuU*I5_qoTC_t2G}kF_#q$| zHSg2h@I#Pp=AbC`bJVa|jJiyjhdwiq9H5ia6xODe`TDx8W3|)-)k`#_3(5nM+xNSqG{5ZXO>oV$Ay5 zYRU4%feg42N#xgSNfA)ui68E&E%DHkd)3E+?p98atkCU}z1(9C>Y8!6+M<>)?8UOB=Z?*FwoVSp0lp{qy&efQ-2V!3HEw*J zBxbaUm`tAEX~ut(xD*lH*4SCkUA6*jSi$n0&{)2wq%UiO8?W<6ovj7>!9hd$V)M_2 ztsyvmZJLi6(kssfi$5Q|+EFrK#lf&E_wE*(?N_-gYSudU0GGTV?}U6L^T76hnv&pe z`f~And6buiid(Zk0CaJ4f}H6>?@{;#*x7(mnY|8zU2NL!I3vIY&J24&f=E{Z$T4p1 zcdk)j1G?Xrh}||3^ftmc0ssC2Ouz=E2Tw>C*%kmH_Z@_e?E>$}o&#vb|?gbAbbUmEYFZf>q!GyRry{{QI5ps#bIQ;@BRson9ZeZ$Mn+Sm4rN6#~ z0Jw{Ee_p56E?#Odu9m!thLaA~@bn*Ln_*No zwEdQSl9+UpR$i#TbWWk6gHOYQB@aG}QCU8J|55SeOe?)owSk+DukH9}Rr#m7tLOBE z!k{|)Hv2QHf{9hmC93cb@q#&u7>~JE>+&XINkI*fCX$f}ug?z}&XZOUw6>Yzc&>xT z=SOImB_@3aa##Flsw+^MCQ2Avm2$eXX34e~^Cmw!XKNWgs&&@G4%Mzm03;qBe~1e_ z5ad|5J**}pS|w6IDJbX@*z`pZT(MA$#NsIijecL0D4H8&g~39orrKMRUic`Vnd!hY zX>7fBZY24nZb4tS`~210-kJ^Bng@*@YLf59ASK#d$9kbp&O}-3NLjbI&11}?&K3{! z%k^%89YfIxSc3e(K~7v7%?V3a{?IWUPAk2dfeaPjSu3%-GtP@vkTI zTljdQp%9D5j^0js@vh?Q^x%t}>Zod_)s#rfi3KP9;e~Nuy8Rq0!NQo0qv#0=42&W& z6pj6spPx)+PmxzfFbS1XJE*P6Wq+OhB6?$e=_qjxfBTKk_}Lx*QsNbsjbs+SVp&?) zlKh?4ATisopz+Pz6D`7PK{FmP^Dysql45I9kz_~xqo%( zZ$sVsz&$Y$LoW_g7SL#IoaVM5!x=pLCM6p7WoY%|IBXIXPir;<0`fqWqB?fv)va3$ z%Vb11Edn>30_u4Er*4P)0*dnC*Ye7$OI$xzKbVImZ&2XWZSF}@ZS0oKC)Yg@t?D@T z?fV5&8cvLJB-->W_z=fVhxQ+f^XW&`+xMyVtvc09wBtXG0q1;K=I$_>N1Vf-(&fRD zI8&WnWq&GFhH+l2rJh&Hmnhp}oXWoeN4bu6?1cJ1YlClscV1rs!*$JfVP2IkQBnynxLcb zNSHuz3p)~FOM+JdoiEhm8w}-ZU`uj9*^y4!Z@f|jO+$ejsOuNx^L&iUT6TAG*xO5N zaaA~dG>ZQQZm%c9|8#oQe(Edo>~(uG?WwzXG1T+&YpbOF`7-!jsoZ*EHNoTaerIB} z;KSB~C+2NqP#z57moOTKWP(I*l#L!ba3?BVc>`onR`SLzAz}EkD2SD5eGqSFP<*;( zM0$_&kRDV$Fdh6g8f-7*p&Ap+^&KV}fNSvjJK{>mZ&?oMI@taGpR{7+mIXTEqgy;_ z+FI=cEYlZMTtq)IImQ|e5a%+_l6g}gQa6OH7*(v#Y41ws^++XVMmhOuXtU^vT4R0d zA&DC9F(K+}SkcZ$75yTYFyZHrABuU-c#NNz6P$jfk z%>Nk1nq35)mF&b2pGXJi6ff#9b@=3n4*CR+%$p@;`kQuX2TEj)S`2r$hu4oCY`FDA z+tZg=*b9zR>Bje5+CFL<@>nZXaRQ!KmZykOTl^8jkJ^LYY7T3C2`w}^bM*C*i-#GZ zo9}h&FN63DvIzHKO4L+TlQD2lZtR(kj6|-$+))i2Wz;$#P-{8jMSMq`+>k{A*mk0b zfy%S~B;i0e1UuEkvC{;2%ADv~Q!F8|z*P(%0WEgM5Jz?mMj(YE`-d+K8qQm5@yU+s z8CE;rK2<(zam<}ln1K0TFY4N;I9O;gca-Ja^Q$qz4X1}n1+PyZL{AbFIJCB9FX1xA z#CQIvZ?U#+bPh0@Xn00h$xx)B7a!D?mn6XQ+{V@p`P@THKxQr3C(xJ=K3i7*3_#j! z+&VaTuah=xIo4F~&;}79M$-yLGg4G5SvN;<(e)_Ps>(zAK;K}-Pg>rfTJ-!a%1)s_ zIYwe;npOgH<$#s>hYCT^Gc(R*%`%i=Wzy(O$=4m+n{9&>1+~WmSLW&Wfb5{SSPlz` zTt~Mb*~%vT-MFs7WtN&c*2dBstVMgW-KMA|Dy$AadsEboY@>G$SbD(BX2)&BoAYFe z4gxHBMGk>_{L&)3KZ~TQiAn9*A_c2p0#d8$W!>(dv3<$;KdR(wsp&KZ0MF|Dq(T~G zLldU>`9EdrC@j41tv4G%P?eq?A6bFOB9!Gv_WyBbZX^{;MHq+tE+XfCgjSlygGaB5 z-Zn(|C7O2v8^WbG+~nlRG8!$qv+;uPI3GH>m>RYRx~S0`FR^%=E)M5DwYE$0pNcpg*}gdef7b~0uGj9$I_^I?0rdamEUv}|%D6PaqXNtu(1fLSX&hJ&JStx9Vs*dDvufHe!Xq+BtCp1fu zFpb3cAjQNk$$;tj*n>8#*h~}gAn=gXEU>)Ru>K66-u73{2HTiR-uC+IP+q%CKv@t_ zbim&@-rpgsZ#drnrr%J#e=`1#pL|2K{!NUp)BHd1t$zym9ZvTKv;3QuUO`iTf-e8$ z{vDz9hM@YJ5MDL?KivNT;Qh(|_WFOIS#L0|zv=chTKFIK{{nUWDdYE!_J1!y;59k^ zzaU+Ivj5)9eQSIFO|pppX8&KB;D1W`y@UAH{Qa9^UlZ}a7V=-M;6M3)udls@LVuId z>+kxD|Ie=DpCW!Q3;g$9guEWQzeN0Amiv?a_b~0PcJMc$68wk#e<}%oGXH+O|9knJ vWdCOVGp_qn#P28M?H>4>`e^qqLf9@0nq_TT>joPk0+ literal 0 HcmV?d00001 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", "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 expectedValue = new TreeMap(); + 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 expectedValues; + + TreeMap values; + + @Override + protected void setUp() throws Exception { + values = new TreeMap(); + + // Expected values + expectedValues = new TreeMap(); + 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("".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("".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 expectedValues; + TreeMap values; + + values = new TreeMap(); + + // Expected values + expectedValues = new TreeMap(); + 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 expectedValues; + TreeMap values; + + values = new TreeMap(); + + // Expected values + expectedValues = new TreeMap(); + 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(dateToInsert)); + props.setCreatorProperty("MyCreator"); + props.setDescriptionProperty("MyDescription"); + props.setIdentifierProperty("MyIdentifier"); + props.setKeywordsProperty("MyKeywords"); + props.setLanguageProperty("MyLanguage"); + props.setLastModifiedByProperty("Julien Chable"); + props.setLastPrintedProperty(new Nullable(dateToInsert)); + props.setModifiedProperty(new Nullable(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 0000000000000000000000000000000000000000..89533f0310c3b7ef77eb5f024a0a1f1aef70ff5d GIT binary patch literal 9788 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 b`@@e@mU{pbj~|Cl&;ejr=Fx)g$FKhZB5RdF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..63555e47dbca9d4f717ecff485cf49cd4b7bae92 GIT binary patch literal 9801 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..304e5ce288e348616b54b433201dd7b1b6fe4de6 GIT binary patch literal 9786 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 X%5v~f@%VA%1QpN$%{!XW{`mDj0G^5i literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7b4f765e6fa9c113b67a69739f35e0f0dd243eba GIT binary patch literal 9787 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 aKm0fq`3ErZ_;Kb09nc0#Jet$}`1L=QsgHI5 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7a63ee15d653b6a20e45bfb2aba08080a966d00e GIT binary patch literal 10058 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 D>hYh+ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8ccfb6155c3f39a6c5fea1b299bea59631d12c17 GIT binary patch literal 24759 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 UP>mH0?FQ<5AJyKqjlUoL9|O>$y8r+H literal 0 HcmV?d00001 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: + * and 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 or + * 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: and 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: and 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: and 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: and 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 0000000000000000000000000000000000000000..dc87bc817a2327891d990b65d981eca13505aec1 GIT binary patch literal 14279 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 y