diff --git a/build.xml b/build.xml index 458bf30a2..c04db3d0e 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 5db2ba858..4ff9390f6 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 5379a7324..000000000 --- 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 b3e1c0412..52c1583df 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 d9bc0e91d..ec3206526 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 000000000..bf5af2f95 --- /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 000000000..25ea1b06e --- /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 d21be707e..fcf88f69f 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 e4fe3fe16..88668093d 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 ab2d04c72..ef333d86d 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 d529bedf0..f01cae8ce 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 6341b2084..f10cf0ca0 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 894f2f800..3d862be88 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 320c6e143..97c9e2d69 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 c6a99436b..25310fff3 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 7084b38f3..a55bc2632 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 406a3145e..7640db845 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 000000000..689f8f33d --- /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 000000000..9e45ccf4b --- /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 000000000..d2fa0e144 --- /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 000000000..c68d85ecb --- /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 000000000..8b946e6d0 --- /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 000000000..b6c5b30f7 --- /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 000000000..72241b38d --- /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 000000000..b05d7903e --- /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 000000000..0e15332f3 --- /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 000000000..663ab266f --- /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 000000000..a40f4ac43 --- /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 000000000..5f53781eb --- /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 000000000..34f26821f --- /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 000000000..54021302b --- /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 000000000..414fd41aa --- /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 000000000..5ab80dd0f --- /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 000000000..efaa09324 --- /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 000000000..daf2e7e09 --- /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 000000000..e5ecaacc4 --- /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 000000000..2a63f47bc --- /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 000000000..ce9d1159c --- /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 000000000..1d55c7bb7 --- /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 000000000..12a5a55ff --- /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 000000000..f0db2ab28 --- /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 000000000..2c05898d1 --- /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 000000000..18a6ca4ab --- /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 000000000..5f96d180e --- /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 000000000..ad3115737 --- /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 000000000..8f29a4587 --- /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 000000000..debe3d185 --- /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 000000000..a9f60b011 --- /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 000000000..70bbcae6b --- /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 000000000..0b17cb892 --- /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 000000000..5f894f251 --- /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 000000000..10b2339ec --- /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 000000000..8138cda8b --- /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 000000000..438cc5dcb --- /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 000000000..39e8fa3f3 --- /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 000000000..a54bef574 --- /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 000000000..0cb1fba94 --- /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 000000000..718e78b76 --- /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 000000000..f11b969c0 --- /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 000000000..689a5c67d --- /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 000000000..8922641ce --- /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 000000000..0e5136d3a --- /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 000000000..45374dcf7 --- /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 000000000..1d64ffe4e --- /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 000000000..1a0d36695 --- /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 000000000..0b9822eab --- /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 d7984d3b1..d2f0f43e2 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 258e5b8d1..5b1b7d747 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 53851177b..7b66a3d5a 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 4366f82e7..ad820bd2f 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 ceeaa0f44..29f3bd083 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 33614edc0..80ba65311 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 b5fabd1a6..6caec6e73 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 edc818662..8287df55b 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 872eadb44..8355f4f9b 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 d16cf9a1b..1a0a6555f 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 300f27cb9..af7d64567 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 08e2a943c..d0c93cc19 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 f6e0a6c32..92be5e373 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 9317d3952..4736ecbc4 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 4dbb72f71..671e6f402 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 8e12e8606..3fc0e52e8 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 736eea053..05bb5983f 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 ef9bf7fbb..b985d6440 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 686a8f037..ac6b1268b 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 10391170a..aa35007ff 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 7594e6aa8..9a68eb7fb 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 91d52a72c..1e97b129e 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 a92972acf..3657c1fa6 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 b5f132aff..f1921783d 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 3761241bf..04bf13d11 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 f6049c396..6a427bbdc 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 3d670dd9d..33c11436e 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 de5c07fd9..e75ba3c9e 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 000000000..26bf0ce0f --- /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 000000000..4b2cc45a5 --- /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 000000000..ba5ed27d2 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/ExcelWithHyperlinks.xlsx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx new file mode 100755 index 000000000..dc105f004 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestOpenPackageINPUT.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx new file mode 100755 index 000000000..771cd1814 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCommon.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx new file mode 100755 index 000000000..2d8bb359b Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesGetters.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx new file mode 100755 index 000000000..2d8bb359b Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageCoreProperiesSetters.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx new file mode 100755 index 000000000..ccc990df3 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/TestPackageThumbnail.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx new file mode 100755 index 000000000..8e7752143 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx new file mode 100755 index 000000000..a275cf417 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/sample.xlsx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg new file mode 100755 index 000000000..511996002 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/INPUT/thumbnail.jpg differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx new file mode 100755 index 000000000..3da1daeb9 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestCreatePackageOUTPUT.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx new file mode 100755 index 000000000..c8c43dce6 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageRemovePartRecursiveTMP.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx new file mode 100755 index 000000000..a41daa5ec Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/OUTPUT/TestPackageThumbnailOUTPUT.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestContentType.java new file mode 100755 index 000000000..9f70f6f02 --- /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 000000000..280d6d992 --- /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 000000000..359c02faf --- /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 000000000..8d4e3313e --- /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 000000000..acb7cd84f --- /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 000000000..e1ec87962 --- /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 000000000..bbf5402f4 --- /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 000000000..478552f70 --- /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 000000000..3ca128103 --- /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 000000000..56974b0ca --- /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 000000000..89533f031 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx new file mode 100755 index 000000000..63555e47d Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx new file mode 100755 index 000000000..304e5ce28 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_NotPresentFAIL.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx new file mode 100755 index 000000000..7b4f765e6 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_LimitedXSITypeAttribute_PresentWithUnauthorizedValueFAIL.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx new file mode 100755 index 000000000..7a63ee15d Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx new file mode 100755 index 000000000..3ee9fc333 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx new file mode 100755 index 000000000..7a63ee15d Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_SUCCESS.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx new file mode 100755 index 000000000..3984aeafc Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_CoreProperties_UnauthorizedXMLLangAttributeFAIL.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx new file mode 100755 index 000000000..8ccfb6155 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/INPUT/OPCCompliance_DerivedPartNameFAIL.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/OPCCompliance_CoreProperties.java new file mode 100755 index 000000000..a26fd3012 --- /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 000000000..f54c294b0 --- /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 000000000..80f6be75e --- /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 000000000..25d140c81 --- /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 000000000..dc87bc817 Binary files /dev/null and b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/INPUT/sample.docx differ diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java new file mode 100755 index 000000000..6af3e6f54 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/TestContentTypeManager.java @@ -0,0 +1,122 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.openxml4j.opc.internal; + +import junit.framework.TestCase; + +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.internal.ContentTypeManager; +import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager; + +import org.apache.poi.openxml4j.TestCore; + +public class TestContentTypeManager extends TestCase { + + TestCore testCore = new TestCore(this.getClass()); + + /** + * Test the properties part content parsing. + */ + public void testContentType() throws Exception { + // File originalFile = new File(testCore.getTestRootPath() + + // File.separator + + // "sample.docx"); + // + // // Retrieves core properties part + // Package p = Package.open(originalFile.getAbsolutePath(), + // PackageAccess.READ); + // PackageRelationship corePropertiesRelationship = p + // .getRelationshipsByType( + // PackageRelationshipTypes.CORE_PROPERTIES) + // .getRelationship(0); + // PackagePart coreDocument = p.getPart(corePropertiesRelationship); + // + // ContentTypeManager ctm = new ZipContentTypeManager(coreDocument + // .getInputStream()); + // + // // TODO + //fail(); + } + + /** + * Test the addition of several default and override content types. + */ + public void testContentTypeAddition() throws Exception { + ContentTypeManager ctm = new ZipContentTypeManager(null, null); + + PackagePartName name1 = PackagingURIHelper + .createPartName("/foo/foo.XML"); + PackagePartName name2 = PackagingURIHelper + .createPartName("/foo/foo2.xml"); + PackagePartName name3 = PackagingURIHelper + .createPartName("/foo/doc.rels"); + PackagePartName name4 = PackagingURIHelper + .createPartName("/foo/doc.RELS"); + + // Add content types + ctm.addContentType(name1, "foo-type1"); + ctm.addContentType(name2, "foo-type2"); + ctm.addContentType(name3, "text/xml+rel"); + ctm.addContentType(name4, "text/xml+rel"); + + assertEquals(ctm.getContentType(name1), "foo-type1"); + assertEquals(ctm.getContentType(name2), "foo-type2"); + assertEquals(ctm.getContentType(name3), "text/xml+rel"); + assertEquals(ctm.getContentType(name3), "text/xml+rel"); + } + + /** + * Test the addition then removal of content types. + */ + public void testContentTypeRemoval() throws Exception { + ContentTypeManager ctm = new ZipContentTypeManager(null, null); + + PackagePartName name1 = PackagingURIHelper + .createPartName("/foo/foo.xml"); + PackagePartName name2 = PackagingURIHelper + .createPartName("/foo/foo2.xml"); + PackagePartName name3 = PackagingURIHelper + .createPartName("/foo/doc.rels"); + PackagePartName name4 = PackagingURIHelper + .createPartName("/foo/doc.RELS"); + + // Add content types + ctm.addContentType(name1, "foo-type1"); + ctm.addContentType(name2, "foo-type2"); + ctm.addContentType(name3, "text/xml+rel"); + ctm.addContentType(name4, "text/xml+rel"); + ctm.removeContentType(name2); + ctm.removeContentType(name3); + + assertEquals(ctm.getContentType(name1), "foo-type1"); + assertEquals(ctm.getContentType(name2), "foo-type1"); + assertEquals(ctm.getContentType(name3), null); + + ctm.removeContentType(name1); + assertEquals(ctm.getContentType(name1), null); + assertEquals(ctm.getContentType(name2), null); + } + + /** + * Test the addition then removal of content types in a package. + */ + public void testContentTypeRemovalPackage() throws Exception { + // TODO + } +} diff --git a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java index 789d3206c..f1553e0e1 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java +++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java @@ -24,7 +24,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.openxml4j.opc.Package; +import org.apache.poi.openxml4j.opc.Package; import junit.framework.TestCase; diff --git a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java index 682fb9757..13971f7ef 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java @@ -19,8 +19,8 @@ package org.apache.poi.xslf; import java.io.File; import org.apache.poi.POIXMLDocument; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.Package; +import org.apache.poi.openxml4j.opc.PackagePart; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdListEntry; diff --git a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java index 1fe0b120c..5214a22a2 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java @@ -23,8 +23,8 @@ import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.openxml4j.exceptions.InvalidFormatException; -import org.openxml4j.opc.Package; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.Package; /** * Centralises logic for finding/opening sample files in the src/testcases/org/apache/poi/hssf/hssf/data folder. diff --git a/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java b/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java index f2eccca85..9ad4af7ba 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/eventusermodel/TestXSSFReader.java @@ -25,7 +25,7 @@ import junit.framework.TestCase; import org.apache.poi.util.IOUtils; import org.apache.poi.xssf.usermodel.XSSFRichTextString; -import org.openxml4j.opc.Package; +import org.apache.poi.openxml4j.opc.Package; /** * Tests for XSSFReader diff --git a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java index a5cd76c56..062a93e7c 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCommentsTable.java @@ -20,7 +20,6 @@ package org.apache.poi.xssf.model; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.util.List; import org.apache.poi.ss.usermodel.Cell; @@ -33,9 +32,7 @@ import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.POIXMLDocumentPart; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.Package; import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; import junit.framework.TestCase; diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java index 9464c05b8..01160f23f 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestFormulaEvaluatorOnXSSF.java @@ -32,7 +32,7 @@ import org.apache.poi.ss.usermodel.CellValue; import org.apache.poi.ss.usermodel.FormulaEvaluator; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; -import org.openxml4j.opc.Package; +import org.apache.poi.openxml4j.opc.Package; /** * Performs much the same role as {@link TestFormulasFromSpreadsheet}, diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index bf5f330cf..b35d59c5b 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -21,9 +21,9 @@ import java.io.File; import junit.framework.TestCase; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.Package; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.xssf.XSSFTestDataSamples; public class TestXSSFBugs extends TestCase { diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java index 34d49cf92..e4d6bc091 100755 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java @@ -19,10 +19,8 @@ package org.apache.poi.xssf.usermodel; import junit.framework.TestCase; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.POIXMLDocumentPart; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing; import java.util.List; -import java.io.IOException; /** * @author Yegor Kozlov @@ -73,7 +71,7 @@ public class TestXSSFDrawing extends TestCase { XSSFSheet sheet = wb.createSheet(); XSSFDrawing drawing = sheet.createDrawingPatriarch(); } - org.openxml4j.opc.Package pkg = wb.getPackage(); + org.apache.poi.openxml4j.opc.Package pkg = wb.getPackage(); assertEquals(3, pkg.getPartsByContentType(XSSFRelation.DRAWINGS.getContentType()).size()); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java index 0e5141db7..a256b192e 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFHyperlink.java @@ -26,7 +26,6 @@ import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.xssf.XSSFTestDataSamples; -import org.openxml4j.opc.Package; public class TestXSSFHyperlink extends TestCase { public TestXSSFHyperlink(String name) { diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java index 4b8ebaca7..09acaac09 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java @@ -28,10 +28,10 @@ import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.model.StylesTable; -import org.openxml4j.opc.ContentTypes; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.ContentTypes; +import org.apache.poi.openxml4j.opc.Package; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook; diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java b/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java index 321048fd1..24fbfcc89 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFDocument.java @@ -21,8 +21,8 @@ import java.io.File; import org.apache.poi.POIXMLDocument; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFRelation; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.Package; +import org.apache.poi.openxml4j.opc.PackagePart; import junit.framework.TestCase;