2009-03-18 14:54:01 -04:00
/ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
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 ;
2011-05-05 21:19:21 -04:00
import java.io.Closeable ;
2009-03-18 14:54:01 -04:00
import java.io.File ;
import java.io.FileInputStream ;
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 ;
2015-05-03 04:47:50 -04:00
import java.util.Collections ;
2009-03-18 14:54:01 -04:00
import java.util.Date ;
2016-06-24 17:39:44 -04:00
import java.util.HashMap ;
2011-06-01 08:07:16 -04:00
import java.util.List ;
2015-10-12 16:43:42 -04:00
import java.util.Map ;
2009-03-18 14:54:01 -04:00
import java.util.concurrent.locks.ReentrantReadWriteLock ;
2011-06-01 08:07:16 -04:00
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
2009-03-18 14:54:01 -04:00
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 ;
2011-09-22 06:26:33 -04:00
import org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException ;
2009-03-18 14:54:01 -04:00
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 ;
2016-07-16 20:14:06 -04:00
import org.apache.poi.openxml4j.util.ZipEntrySource ;
2016-04-10 17:28:41 -04:00
import org.apache.poi.util.NotImplemented ;
2009-03-18 14:54:01 -04:00
import org.apache.poi.util.POILogFactory ;
2013-11-11 18:03:00 -05:00
import org.apache.poi.util.POILogger ;
2009-03-18 14:54:01 -04:00
/ * *
* Represents a container that can store multiple data objects .
* /
2011-05-05 21:19:21 -04:00
public abstract class OPCPackage implements RelationshipSource , Closeable {
2009-03-18 14:54:01 -04:00
/ * *
* Logger .
* /
2016-03-27 01:01:02 -04:00
private static final POILogger logger = POILogFactory . getLogger ( OPCPackage . class ) ;
2009-03-18 14:54:01 -04:00
/ * *
* 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 .
* /
2015-10-12 16:43:42 -04:00
protected Map < ContentType , PartMarshaller > partMarshallers ;
2009-03-18 14:54:01 -04:00
/ * *
* Default part marshaller .
* /
protected PartMarshaller defaultPartMarshaller ;
/ * *
* Part unmarshallers by content type .
* /
2015-10-12 16:43:42 -04:00
protected Map < ContentType , PartUnmarshaller > partUnmarshallers ;
2009-03-18 14:54:01 -04:00
/ * *
* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param access
* Package access .
* /
OPCPackage ( PackageAccess access ) {
if ( getClass ( ) ! = ZipPackage . class ) {
throw new IllegalArgumentException ( " PackageBase may not be subclassed " ) ;
}
init ( ) ;
this . packageAccess = access ;
}
/ * *
* Initialize the package instance.
* /
private void init ( ) {
2016-06-24 17:39:44 -04:00
this . partMarshallers = new HashMap < ContentType , PartMarshaller > ( 5 ) ;
this . partUnmarshallers = new HashMap < ContentType , PartUnmarshaller > ( 2 ) ;
2009-03-18 14:54:01 -04:00
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 happen
throw new OpenXML4JRuntimeException (
2011-05-19 05:16:48 -04:00
" Package.init() : this exception should never happen, " +
" if you read this message please send a mail to the developers team. : " +
2015-10-12 16:43:42 -04:00
e . getMessage ( ) ,
e
2011-05-19 05:16:48 -04:00
) ;
2009-03-18 14:54:01 -04:00
}
}
/ * *
* Open a package with read / write permission .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param path
* The document path .
* @return A Package object , else < b > null < / b > .
* @throws InvalidFormatException
* If the specified file doesn ' t exist , and a parsing error
* occur .
* /
public static OPCPackage open ( String path ) throws InvalidFormatException {
return open ( path , defaultPackageAccess ) ;
}
2012-06-19 18:50:14 -04:00
/ * *
* Open a package with read / write permission .
*
* @param file
* The file to open .
* @return A Package object , else < b > null < / b > .
* @throws InvalidFormatException
* If the specified file doesn ' t exist , and a parsing error
* occur .
* /
public static OPCPackage open ( File file ) throws InvalidFormatException {
return open ( file , defaultPackageAccess ) ;
}
2016-07-16 20:14:06 -04:00
/ * *
* Open an user provided { @link ZipEntrySource } with read - only permission .
* This method can be used to stream data into POI .
* Opposed to other open variants , the data is read as - is , e . g . there aren ' t
* any zip - bomb protection put in place .
*
* @param zipEntry the custom source
* @return A Package object
* @throws InvalidFormatException if a parsing error occur .
* /
public static OPCPackage open ( ZipEntrySource zipEntry )
throws InvalidFormatException {
OPCPackage pack = new ZipPackage ( zipEntry , PackageAccess . READ ) ;
try {
if ( pack . partList = = null ) {
pack . getParts ( ) ;
}
// pack.originalPackagePath = file.getAbsolutePath();
return pack ;
} catch ( InvalidFormatException e ) {
try {
pack . close ( ) ;
} catch ( IOException e1 ) {
throw new IllegalStateException ( e ) ;
}
throw e ;
} catch ( RuntimeException e ) {
try {
pack . close ( ) ;
} catch ( IOException e1 ) {
throw new IllegalStateException ( e ) ;
}
throw e ;
}
}
2009-03-18 14:54:01 -04:00
/ * *
* Open a package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param path
* The document path .
* @param access
* PackageBase access .
* @return A PackageBase object , else < b > null < / b > .
* @throws InvalidFormatException
* If the specified file doesn ' t exist , and a parsing error
* occur .
2016-09-14 08:57:39 -04:00
* @throws InvalidOperationException
2009-03-18 14:54:01 -04:00
* /
public static OPCPackage open ( String path , PackageAccess access )
2016-09-14 08:57:39 -04:00
throws InvalidFormatException , InvalidOperationException {
2015-10-12 16:43:42 -04:00
if ( path = = null | | " " . equals ( path . trim ( ) ) ) {
2013-11-11 18:03:00 -05:00
throw new IllegalArgumentException ( " 'path' must be given " ) ;
2015-10-12 16:43:42 -04:00
}
2013-11-11 18:03:00 -05:00
File file = new File ( path ) ;
2015-10-12 16:43:42 -04:00
if ( file . exists ( ) & & file . isDirectory ( ) ) {
2013-11-11 18:03:00 -05:00
throw new IllegalArgumentException ( " path must not be a directory " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
OPCPackage pack = new ZipPackage ( path , access ) ;
2016-09-14 08:57:39 -04:00
boolean success = false ;
2009-03-18 14:54:01 -04:00
if ( pack . partList = = null & & access ! = PackageAccess . WRITE ) {
2016-09-14 08:57:39 -04:00
try {
pack . getParts ( ) ;
success = true ;
} finally {
if ( ! success ) {
try {
pack . close ( ) ;
} catch ( final IOException e ) {
throw new InvalidOperationException ( " Could not close OPCPackage while cleaning up " , e ) ;
}
}
}
2009-03-18 14:54:01 -04:00
}
pack . originalPackagePath = new File ( path ) . getAbsolutePath ( ) ;
return pack ;
}
2012-06-19 18:50:14 -04:00
/ * *
* Open a package .
*
* @param file
* The file to open .
* @param access
* PackageBase access .
* @return A PackageBase object , else < b > null < / b > .
* @throws InvalidFormatException
* If the specified file doesn ' t exist , and a parsing error
* occur .
* /
public static OPCPackage open ( File file , PackageAccess access )
throws InvalidFormatException {
2015-09-28 17:26:06 -04:00
if ( file = = null ) {
throw new IllegalArgumentException ( " 'file' must be given " ) ;
}
if ( file . exists ( ) & & file . isDirectory ( ) ) {
throw new IllegalArgumentException ( " file must not be a directory " ) ;
}
2012-06-19 18:50:14 -04:00
OPCPackage pack = new ZipPackage ( file , access ) ;
2016-04-02 07:01:43 -04:00
try {
if ( pack . partList = = null & & access ! = PackageAccess . WRITE ) {
pack . getParts ( ) ;
}
pack . originalPackagePath = file . getAbsolutePath ( ) ;
return pack ;
} catch ( InvalidFormatException e ) {
try {
pack . close ( ) ;
} catch ( IOException e1 ) {
throw new IllegalStateException ( e ) ;
}
throw e ;
} catch ( RuntimeException e ) {
try {
pack . close ( ) ;
} catch ( IOException e1 ) {
throw new IllegalStateException ( e ) ;
}
throw e ;
}
2012-06-19 18:50:14 -04:00
}
2009-03-18 14:54:01 -04:00
/ * *
* Open a package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* 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
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param in
* The InputStream to read the package from
* @return A PackageBase object
* /
public static OPCPackage open ( InputStream in ) throws InvalidFormatException ,
IOException {
2010-01-13 15:24:15 -05:00
OPCPackage pack = new ZipPackage ( in , PackageAccess . READ_WRITE ) ;
2009-03-18 14:54:01 -04:00
if ( pack . partList = = null ) {
pack . getParts ( ) ;
}
return pack ;
}
/ * *
* Opens a package if it exists , else it creates one .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 OPCPackage openOrCreate ( File file ) throws InvalidFormatException {
OPCPackage retPackage = null ;
if ( file . exists ( ) ) {
retPackage = open ( file . getAbsolutePath ( ) ) ;
} else {
retPackage = create ( file ) ;
}
return retPackage ;
}
/ * *
* Creates a new package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param path
* Path of the document .
* @return A newly created PackageBase ready to use .
* /
public static OPCPackage create ( String path ) {
return create ( new File ( path ) ) ;
}
/ * *
* Creates a new package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param file
* Path of the document .
* @return A newly created PackageBase ready to use .
* /
public static OPCPackage create ( File file ) {
2015-10-12 16:43:42 -04:00
if ( file = = null | | ( file . exists ( ) & & file . isDirectory ( ) ) ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " file " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
if ( file . exists ( ) ) {
throw new InvalidOperationException (
" This package (or file) already exists : use the open() method or delete the file. " ) ;
}
// Creates a new package
2016-07-31 13:19:17 -04:00
OPCPackage pkg = new ZipPackage ( ) ;
2009-03-18 14:54:01 -04:00
pkg . originalPackagePath = file . getAbsolutePath ( ) ;
configurePackage ( pkg ) ;
return pkg ;
}
public static OPCPackage create ( OutputStream output ) {
2016-07-31 13:19:17 -04:00
OPCPackage pkg = new ZipPackage ( ) ;
2009-03-18 14:54:01 -04:00
pkg . originalPackagePath = null ;
pkg . output = output ;
configurePackage ( pkg ) ;
return pkg ;
}
/ * *
* Configure the package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param pkg
* /
private static void configurePackage ( OPCPackage pkg ) {
try {
// Content type manager
pkg . contentTypeManager = new ZipContentTypeManager ( null , pkg ) ;
2013-11-11 18:03:00 -05:00
2009-03-18 14:54:01 -04:00
// Add default content types for .xml and .rels
2013-11-11 18:03:00 -05:00
pkg . contentTypeManager . addContentType (
PackagingURIHelper . createPartName (
PackagingURIHelper . PACKAGE_RELATIONSHIPS_ROOT_URI ) ,
ContentTypes . RELATIONSHIPS_PART ) ;
pkg . contentTypeManager . addContentType (
PackagingURIHelper . createPartName ( " /default.xml " ) ,
ContentTypes . PLAIN_OLD_XML ) ;
// Initialise some PackageBase properties
2009-03-18 14:54:01 -04:00
pkg . packageProperties = new PackagePropertiesPart ( pkg ,
PackagingURIHelper . CORE_PROPERTIES_PART_NAME ) ;
2013-11-11 18:03:00 -05:00
pkg . packageProperties . setCreatorProperty ( " Generated by Apache POI OpenXML4J " ) ;
pkg . packageProperties . setCreatedProperty ( new Nullable < Date > ( new Date ( ) ) ) ;
2009-03-18 14:54:01 -04:00
} catch ( InvalidFormatException e ) {
// Should never happen
throw new IllegalStateException ( e ) ;
}
}
/ * *
* Flush the package : save all .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @see # close ( )
* /
public void flush ( ) {
throwExceptionIfReadOnly ( ) ;
if ( this . packageProperties ! = null ) {
this . packageProperties . flush ( ) ;
}
this . flushImpl ( ) ;
}
/ * *
2010-08-04 11:58:51 -04:00
* Close the open , writable package and save its content .
*
* If your package is open read only , then you should call { @link # revert ( ) }
* when finished with the package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @throws IOException
* If an IO exception occur during the saving process .
* /
2016-06-22 18:27:53 -04:00
@Override
public void close ( ) throws IOException {
2009-03-18 14:54:01 -04:00
if ( this . packageAccess = = PackageAccess . READ ) {
2010-08-04 11:58:51 -04:00
logger . log ( POILogger . WARN ,
" The close() method is intended to SAVE a package. This package is open in READ ONLY mode, use the revert() method instead ! " ) ;
revert ( ) ;
2009-03-18 14:54:01 -04:00
return ;
}
2014-04-24 10:50:52 -04:00
if ( this . contentTypeManager = = null ) {
logger . log ( POILogger . WARN ,
" Unable to call close() on a package that hasn't been fully opened yet " ) ;
2016-04-02 07:01:56 -04:00
revert ( ) ;
2014-04-24 10:50:52 -04:00
return ;
}
2009-03-18 14:54:01 -04:00
// 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 ) ;
2010-01-24 08:26:19 -05:00
output . close ( ) ;
2009-03-18 14:54:01 -04:00
}
} finally {
l . writeLock ( ) . unlock ( ) ;
}
// Clear
this . contentTypeManager . clearAll ( ) ;
}
2016-06-21 10:34:12 -04:00
/ * *
* Close the package WITHOUT saving its content . Reinitialize this package
* and cancel all changes done to it .
* /
public void revert ( ) {
revertImpl ( ) ;
}
2009-03-18 14:54:01 -04:00
2016-06-21 10:34:12 -04:00
/ * *
* 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 {
2016-06-21 10:27:41 -04:00
// Check parameter
if ( path = = null | | path . isEmpty ( ) ) {
throw new IllegalArgumentException ( " path " ) ;
}
String name = path . substring ( path . lastIndexOf ( File . separatorChar ) + 1 ) ;
2016-06-21 10:34:12 -04:00
2016-06-21 10:27:41 -04:00
FileInputStream is = new FileInputStream ( path ) ;
2016-06-22 18:27:53 -04:00
try {
addThumbnail ( name , is ) ;
} finally {
is . close ( ) ;
}
2016-06-21 10:34:12 -04:00
}
2016-06-21 10:27:41 -04:00
/ * *
* 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 .
*
2016-06-22 18:26:08 -04:00
* @param filename The full path to the image file .
* @param data the image data
2016-06-21 10:27:41 -04:00
* /
public void addThumbnail ( String filename , InputStream data ) throws IOException {
2016-06-21 10:34:12 -04:00
// Check parameter
2016-06-21 10:27:41 -04:00
if ( filename = = null | | filename . isEmpty ( ) ) {
2016-06-21 10:34:12 -04:00
throw new IllegalArgumentException ( " filename " ) ;
}
2009-03-18 14:54:01 -04:00
2016-06-21 10:34:12 -04:00
// Create the thumbnail part name
String contentType = ContentTypes
. getContentTypeFromFileExtension ( filename ) ;
2016-07-31 13:19:17 -04:00
PackagePartName thumbnailPartName ;
2016-06-21 10:34:12 -04:00
try {
thumbnailPartName = PackagingURIHelper . createPartName ( " /docProps/ "
+ filename ) ;
} catch ( InvalidFormatException e ) {
String partName = " /docProps/thumbnail " +
filename . substring ( filename . lastIndexOf ( " . " ) + 1 ) ;
try {
thumbnailPartName = PackagingURIHelper . createPartName ( partName ) ;
} catch ( InvalidFormatException e2 ) {
throw new InvalidOperationException (
" Can't add a thumbnail file named ' " + filename + " ' " , e2 ) ;
}
}
2009-03-18 14:54:01 -04:00
2016-06-21 10:34:12 -04:00
// Check if part already exist
if ( this . getPart ( thumbnailPartName ) ! = null )
throw new InvalidOperationException (
" You already add a thumbnail named ' " + filename + " ' " ) ;
2009-03-18 14:54:01 -04:00
2016-06-21 10:34:12 -04:00
// Add the thumbnail part to this package.
PackagePart thumbnailPart = this . createPart ( thumbnailPartName ,
contentType , false ) ;
2009-03-18 14:54:01 -04:00
2016-06-21 10:34:12 -04:00
// Add the relationship between the package and the thumbnail part
this . addRelationship ( thumbnailPartName , TargetMode . INTERNAL ,
PackageRelationshipTypes . THUMBNAIL ) ;
2009-03-18 14:54:01 -04:00
2016-06-21 10:34:12 -04:00
// Copy file data to the newly created part
StreamHelper . copyStream ( data , thumbnailPart . getOutputStream ( ) ) ;
}
2009-03-18 14:54:01 -04:00
2016-06-21 10:34:12 -04:00
/ * *
* 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! " ) ;
}
}
2009-03-18 14:54:01 -04:00
/ * *
* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 {
2015-10-12 16:43:42 -04:00
if ( packageAccess = = PackageAccess . WRITE ) {
2009-03-18 14:54:01 -04:00
throw new InvalidOperationException (
" Operation not allowed, document open in write only mode! " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
}
/ * *
* Retrieves or creates if none exists , core package property part .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param partName
* Part name of the part to retrieve .
* @return The part with the specified name , else < code > null < / code > .
* /
public PackagePart getPart ( PackagePartName partName ) {
throwExceptionIfWriteOnly ( ) ;
2015-10-12 16:43:42 -04:00
if ( partName = = null ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " partName " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
// 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param contentType
* The content type criteria .
* @return All part associated to the specified content type .
* /
public ArrayList < PackagePart > getPartsByContentType ( String contentType ) {
ArrayList < PackagePart > retArr = new ArrayList < PackagePart > ( ) ;
for ( PackagePart part : partList . values ( ) ) {
2015-10-12 16:43:42 -04:00
if ( part . getContentType ( ) . equals ( contentType ) ) {
2009-03-18 14:54:01 -04:00
retArr . add ( part ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
}
2015-05-03 04:47:50 -04:00
Collections . sort ( retArr ) ;
2009-03-18 14:54:01 -04:00
return retArr ;
}
/ * *
* Retrieve parts by relationship type .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param relationshipType
* Relationship type .
* @return All parts which are the target of a relationship with the
* specified type , if the method can ' t retrieve relationships from
* the package , then return < code > null < / code > .
* /
public ArrayList < PackagePart > getPartsByRelationshipType (
String relationshipType ) {
2015-10-12 16:43:42 -04:00
if ( relationshipType = = null ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " relationshipType " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
ArrayList < PackagePart > retArr = new ArrayList < PackagePart > ( ) ;
for ( PackageRelationship rel : getRelationshipsByType ( relationshipType ) ) {
2014-05-14 17:14:16 -04:00
PackagePart part = getPart ( rel ) ;
if ( part ! = null ) {
retArr . add ( part ) ;
}
2009-03-18 14:54:01 -04:00
}
2015-05-03 04:47:50 -04:00
Collections . sort ( retArr ) ;
2009-03-18 14:54:01 -04:00
return retArr ;
}
2015-05-03 04:47:50 -04:00
/ * *
* Retrieve parts by name
*
* @param namePattern
* The pattern for matching the names
* @return All parts associated to the specified content type , sorted
* in alphanumerically by the part - name
* /
2011-06-01 08:07:16 -04:00
public List < PackagePart > getPartsByName ( final Pattern namePattern ) {
if ( namePattern = = null ) {
throw new IllegalArgumentException ( " name pattern must not be null " ) ;
}
2015-05-03 04:47:50 -04:00
Matcher matcher = namePattern . matcher ( " " ) ;
2011-06-01 08:07:16 -04:00
ArrayList < PackagePart > result = new ArrayList < PackagePart > ( ) ;
for ( PackagePart part : partList . values ( ) ) {
PackagePartName partName = part . getPartName ( ) ;
2015-05-03 04:47:50 -04:00
if ( matcher . reset ( partName . getName ( ) ) . matches ( ) ) {
2011-06-01 08:07:16 -04:00
result . add ( part ) ;
}
}
2015-05-03 04:47:50 -04:00
Collections . sort ( result ) ;
2011-06-01 08:07:16 -04:00
return result ;
}
2009-03-18 14:54:01 -04:00
/ * *
* Get the target part from the specified relationship .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 ;
}
/ * *
2012-01-30 07:59:54 -05:00
* Load the parts of the archive if it has not been done yet . The
* relationships of each part are not loaded .
*
* Note - Rule M4 . 1 states that there may only ever be one Core
* Properties Part , but Office produced files will sometimes
* have multiple ! As Office ignores all but the first , we relax
* Compliance with Rule M4 . 1 , and ignore all others silently too .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @return All this package ' s parts .
* /
public ArrayList < PackagePart > getParts ( ) throws InvalidFormatException {
throwExceptionIfWriteOnly ( ) ;
// If the part list is null, we parse the package to retrieve all parts.
if ( partList = = null ) {
/* Variables use to validate OPC Compliance */
2012-01-30 07:59:54 -05:00
// Check rule M4.1 -> A format consumer shall consider more than
2009-03-18 14:54:01 -04:00
// one core properties relationship for a package to be an error
2012-01-30 07:59:54 -05:00
// (We just log it and move on, as real files break this!)
2009-03-18 14:54:01 -04:00
boolean hasCorePropertiesPart = false ;
2012-01-30 07:59:54 -05:00
boolean needCorePropertiesPart = true ;
2009-03-18 14:54:01 -04:00
PackagePart [ ] parts = this . getPartsImpl ( ) ;
this . partList = new PackagePartCollection ( ) ;
for ( PackagePart part : parts ) {
2015-10-12 16:43:42 -04:00
if ( partList . containsKey ( part . _partName ) ) {
2009-03-18 14:54:01 -04:00
throw new InvalidFormatException (
2012-01-30 07:59:54 -05:00
" 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] " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
// Check OPC compliance rule M4.1
if ( part . getContentType ( ) . equals (
ContentTypes . CORE_PROPERTIES_PART ) ) {
2012-01-30 07:59:54 -05:00
if ( ! hasCorePropertiesPart ) {
2009-03-18 14:54:01 -04:00
hasCorePropertiesPart = true ;
2012-01-30 07:59:54 -05:00
} else {
logger . log ( POILogger . WARN , " OPC Compliance error [M4.1]: " +
" there is more than one core properties relationship in the package! " +
" POI will use only the first, but other software may reject this file. " ) ;
}
2009-03-18 14:54:01 -04:00
}
2012-01-30 07:59:54 -05:00
PartUnmarshaller partUnmarshaller = partUnmarshallers . get ( part . _contentType ) ;
2009-03-18 14:54:01 -04:00
if ( partUnmarshaller ! = null ) {
UnmarshallContext context = new UnmarshallContext ( this ,
2009-08-18 01:29:53 -04:00
part . _partName ) ;
2009-03-18 14:54:01 -04:00
try {
PackagePart unmarshallPart = partUnmarshaller
. unmarshall ( context , part . getInputStream ( ) ) ;
2009-08-18 01:29:53 -04:00
partList . put ( unmarshallPart . _partName , unmarshallPart ) ;
2009-03-18 14:54:01 -04:00
2012-01-30 07:59:54 -05:00
// Core properties case-- use first CoreProperties part we come across
// and ignore any subsequent ones
if ( unmarshallPart instanceof PackagePropertiesPart & &
hasCorePropertiesPart & &
needCorePropertiesPart ) {
2009-03-18 14:54:01 -04:00
this . packageProperties = ( PackagePropertiesPart ) unmarshallPart ;
2012-01-30 07:59:54 -05:00
needCorePropertiesPart = false ;
}
2009-03-18 14:54:01 -04:00
} catch ( IOException ioe ) {
logger . log ( POILogger . WARN , " Unmarshall operation : IOException for "
2009-08-18 01:29:53 -04:00
+ part . _partName ) ;
2009-03-18 14:54:01 -04:00
continue ;
} catch ( InvalidOperationException invoe ) {
2015-10-12 16:43:42 -04:00
throw new InvalidFormatException ( invoe . getMessage ( ) , invoe ) ;
2009-03-18 14:54:01 -04:00
}
} else {
try {
2009-08-18 01:29:53 -04:00
partList . put ( part . _partName , part ) ;
2009-03-18 14:54:01 -04:00
} catch ( InvalidOperationException e ) {
2015-10-12 16:43:42 -04:00
throw new InvalidFormatException ( e . getMessage ( ) , e ) ;
2009-03-18 14:54:01 -04:00
}
}
}
}
2015-05-03 04:47:50 -04:00
ArrayList < PackagePart > result = new ArrayList < PackagePart > ( partList . values ( ) ) ;
java . util . Collections . sort ( result ) ;
return result ;
2009-03-18 14:54:01 -04:00
}
/ * *
* Create and add a part , with the specified name and content type , to the
* package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param partName
* Part name .
* @param contentType
* Part content type .
* @return The newly created part .
2015-03-01 14:56:56 -05:00
* @throws PartAlreadyExistsException
2009-03-18 14:54:01 -04:00
* 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 .
2009-08-18 01:29:53 -04:00
* @see # createPartImpl ( PackagePartName , String , boolean )
2009-03-18 14:54:01 -04:00
* /
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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 .
2015-03-01 14:56:56 -05:00
* @throws PartAlreadyExistsException
2009-03-18 14:54:01 -04:00
* 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 .
2013-06-12 12:32:24 -04:00
* @see # createPartImpl ( PackagePartName , String , boolean )
2009-03-18 14:54:01 -04:00
* /
PackagePart createPart ( PackagePartName partName , String contentType ,
boolean loadRelationships ) {
throwExceptionIfReadOnly ( ) ;
if ( partName = = null ) {
throw new IllegalArgumentException ( " partName " ) ;
}
2009-08-21 02:08:31 -04:00
if ( contentType = = null | | contentType . equals ( " " ) ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " contentType " ) ;
}
// Check if the specified part name already exists
if ( partList . containsKey ( partName )
& & ! partList . get ( partName ) . isDeleted ( ) ) {
2011-09-22 06:26:33 -04:00
throw new PartAlreadyExistsException (
2012-01-30 07:59:54 -05:00
" 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] " ) ;
2009-03-18 14:54:01 -04:00
}
/* Check OPC compliance */
2012-01-30 07:59:54 -05:00
// Rule [M4.1]: The format designer shall specify and the format producer
2009-03-18 14:54:01 -04:00
// 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.
2012-01-30 07:59:54 -05:00
// Note - POI will read files with more than one Core Properties, which
// Office sometimes produces, but is strict on generation
2009-08-21 02:08:31 -04:00
if ( contentType . equals ( ContentTypes . CORE_PROPERTIES_PART ) ) {
2015-10-12 16:43:42 -04:00
if ( this . packageProperties ! = null ) {
2009-03-18 14:54:01 -04:00
throw new InvalidOperationException (
" OPC Compliance error [M4.1]: you try to add more than one core properties relationship in the package ! " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
}
/* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @return The new part .
* @see # 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 " ) ;
}
2009-08-18 01:29:53 -04:00
if ( partList . containsKey ( part . _partName ) ) {
if ( ! partList . get ( part . _partName ) . isDeleted ( ) ) {
2009-03-18 14:54:01 -04:00
throw new InvalidOperationException (
" A part with the name ' "
2009-08-18 01:29:53 -04:00
+ part . _partName . getName ( )
2009-03-18 14:54:01 -04:00
+ " ' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12] " ) ;
}
// If the specified partis flagged as deleted, we make it
// available
part . setDeleted ( false ) ;
// and delete the old part to replace it thereafeter
2009-08-18 01:29:53 -04:00
this . partList . remove ( part . _partName ) ;
2009-03-18 14:54:01 -04:00
}
2009-08-18 01:29:53 -04:00
this . partList . put ( part . _partName , part ) ;
2009-03-18 14:54:01 -04:00
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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param part
* The part to remove . If < code > null < / code > , skip the action .
* @see # removePart ( PackagePartName )
* /
public void removePart ( PackagePart part ) {
if ( part ! = null ) {
removePart ( part . getPartName ( ) ) ;
}
}
/ * *
* Remove a part in this package . If this part is relationship part , then
* delete all relationships in the source part .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param partName
* The part name of the part to remove .
* /
public void removePart ( PackagePartName partName ) {
throwExceptionIfReadOnly ( ) ;
2015-10-12 16:43:42 -04:00
if ( partName = = null | | ! this . containPart ( partName ) ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " partName " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
// 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
. log ( POILogger . 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 ) ;
2015-10-12 16:43:42 -04:00
if ( part ! = null ) {
2009-03-18 14:54:01 -04:00
part . clearRelationships ( ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
}
}
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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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
2009-08-18 01:29:53 -04:00
this . removePart ( relPart . _partName ) ;
2009-03-18 14:54:01 -04:00
}
// Delete the specified part
2009-08-18 01:29:53 -04:00
this . removePart ( partToRemove . _partName ) ;
2009-03-18 14:54:01 -04:00
}
/ * *
* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param partName
* Name of the part to delete
* /
public void deletePart ( PackagePartName partName ) {
2015-10-12 16:43:42 -04:00
if ( partName = = null ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " partName " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
// 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param partName
* Name of the part to delete
* /
public void deletePartRecursive ( PackagePartName partName ) {
2015-10-12 16:43:42 -04:00
if ( partName = = null | | ! this . containPart ( partName ) ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " partName " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
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 . log ( POILogger . 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 ) ;
2015-10-12 16:43:42 -04:00
if ( relationshipPartName ! = null & & containPart ( relationshipPartName ) ) {
2009-03-18 14:54:01 -04:00
this . removePart ( relationshipPartName ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
}
/ * *
* Check if a part already exists in this package from its name .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param partName
* Part name to check .
* @return < i > true < / i > if the part is logically added to this package , else
* < i > false < / i > .
* /
public boolean containPart ( PackagePartName partName ) {
return ( this . getPart ( partName ) ! = null ) ;
}
/ * *
* Add a relationship to the package ( except relationships part ) .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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
* /
2016-06-22 18:27:53 -04:00
@Override
public PackageRelationship addRelationship ( PackagePartName targetPartName ,
2009-03-18 14:54:01 -04:00
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 )
2015-10-12 16:43:42 -04:00
& & this . packageProperties ! = null ) {
2009-03-18 14:54:01 -04:00
throw new InvalidOperationException (
" OPC Compliance error [M4.1]: can't add another core properties part ! Use the built-in package method instead. " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
/ *
* 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.
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param targetPartName
* Target part name .
* @param targetMode
* Target mode , either Internal or External .
* @param relationshipType
* Relationship type .
* @see PackageRelationshipTypes
* /
2016-06-22 18:27:53 -04:00
@Override
public PackageRelationship addRelationship ( PackagePartName targetPartName ,
2009-03-18 14:54:01 -04:00
TargetMode targetMode , String relationshipType ) {
return this . addRelationship ( targetPartName , targetMode ,
relationshipType , null ) ;
}
/ * *
* Adds an external relationship to a part ( except relationships part ) .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 )
* /
2016-06-22 18:27:53 -04:00
@Override
public PackageRelationship addExternalRelationship ( String target ,
2009-03-18 14:54:01 -04:00
String relationshipType ) {
return addExternalRelationship ( target , relationshipType , null ) ;
}
/ * *
* Adds an external relationship to a part ( except relationships part ) .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* 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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 )
* /
2016-06-22 18:27:53 -04:00
@Override
public PackageRelationship addExternalRelationship ( String target ,
2009-03-18 14:54:01 -04:00
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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param id
* Id of the relationship to delete .
* /
2016-06-22 18:27:53 -04:00
@Override
public void removeRelationship ( String id ) {
2009-03-18 14:54:01 -04:00
if ( relationships ! = null ) {
relationships . removeRelationship ( id ) ;
this . isDirty = true ;
}
}
/ * *
* Retrieves all package relationships.
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @return All package relationships of this package .
* @throws OpenXML4JException
* @see # getRelationshipsHelper ( String )
* /
2016-06-22 18:27:53 -04:00
@Override
public PackageRelationshipCollection getRelationships ( ) {
2009-03-18 14:54:01 -04:00
return getRelationshipsHelper ( null ) ;
}
/ * *
* Retrieves all relationships with the specified type .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param relationshipType
* The filter specifying the relationship type .
* @return All relationships with the specified relationship type .
* /
2016-06-22 18:27:53 -04:00
@Override
public PackageRelationshipCollection getRelationshipsByType (
2009-03-18 14:54:01 -04:00
String relationshipType ) {
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 ) .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param id
* Id of the wanted relationship .
* /
private PackageRelationshipCollection getRelationshipsHelper ( String id ) {
throwExceptionIfWriteOnly ( ) ;
ensureRelationships ( ) ;
return this . relationships . getRelationships ( id ) ;
}
/ * *
* Clear package relationships.
* /
2016-06-22 18:27:53 -04:00
@Override
public void clearRelationships ( ) {
2009-03-18 14:54:01 -04:00
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 )
* /
2016-06-22 18:27:53 -04:00
@Override
public PackageRelationship getRelationship ( String id ) {
2009-03-18 14:54:01 -04:00
return this . relationships . getRelationshipByID ( id ) ;
}
/ * *
* @see org . apache . poi . openxml4j . opc . RelationshipSource # hasRelationships ( )
* /
2016-06-22 18:27:53 -04:00
@Override
public boolean hasRelationships ( ) {
2009-03-18 14:54:01 -04:00
return ( relationships . size ( ) > 0 ) ;
}
/ * *
* @see org . apache . poi . openxml4j . opc . RelationshipSource # isRelationshipExists ( org . apache . poi . openxml4j . opc . PackageRelationship )
* /
2016-06-22 18:27:53 -04:00
@Override
public boolean isRelationshipExists ( PackageRelationship rel ) {
2009-08-21 02:08:31 -04:00
for ( PackageRelationship r : this . getRelationships ( ) ) {
2015-10-12 16:43:42 -04:00
if ( r = = rel ) {
2009-08-21 02:08:31 -04:00
return true ;
2015-10-12 16:43:42 -04:00
}
2009-08-21 02:08:31 -04:00
}
return false ;
2009-03-18 14:54:01 -04:00
}
/ * *
* Add a marshaller .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 . log ( POILogger . WARN , " The specified content type is not valid: ' "
+ e . getMessage ( ) + " '. The marshaller will not be added ! " ) ;
}
}
/ * *
* Add an unmarshaller .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 . log ( POILogger . WARN , " The specified content type is not valid: ' "
+ e . getMessage ( )
+ " '. The unmarshaller will not be added ! " ) ;
}
}
/ * *
* Remove a marshaller by its content type .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param contentType
* The content type associated with the marshaller to remove .
* /
public void removeMarshaller ( String contentType ) {
2015-10-12 16:43:42 -04:00
try {
partMarshallers . remove ( new ContentType ( contentType ) ) ;
} catch ( InvalidFormatException e ) {
throw new RuntimeException ( e ) ;
}
2009-03-18 14:54:01 -04:00
}
/ * *
* Remove an unmarshaller by its content type .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param contentType
* The content type associated with the unmarshaller to remove .
* /
public void removeUnmarshaller ( String contentType ) {
2015-10-12 16:43:42 -04:00
try {
partUnmarshallers . remove ( new ContentType ( contentType ) ) ;
} catch ( InvalidFormatException e ) {
throw new RuntimeException ( e ) ;
}
2009-03-18 14:54:01 -04:00
}
/* Accesseurs */
/ * *
* Get the package access mode .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @return the packageAccess The current package access.
* /
public PackageAccess getPackageAccess ( ) {
return packageAccess ;
}
/ * *
* Validates the package compliance with the OPC specifications .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @return < b > true < / b > if the package is valid else < b > false < / b >
* /
2016-04-10 17:28:41 -04:00
@NotImplemented
2009-03-18 14:54:01 -04:00
public boolean validatePackage ( OPCPackage pkg ) throws InvalidFormatException {
throw new InvalidOperationException ( " Not implemented yet !!! " ) ;
}
/ * *
* Save the document in the specified file .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param targetFile
* Destination file .
* @throws IOException
* Throws if an IO exception occur .
* @see # save ( OutputStream )
* /
public void save ( File targetFile ) throws IOException {
2015-10-12 16:43:42 -04:00
if ( targetFile = = null ) {
2009-03-18 14:54:01 -04:00
throw new IllegalArgumentException ( " targetFile " ) ;
2015-10-12 16:43:42 -04:00
}
2009-03-18 14:54:01 -04:00
this . throwExceptionIfReadOnly ( ) ;
2010-08-04 11:58:51 -04:00
// You shouldn't save the the same file, do a close instead
if ( targetFile . exists ( ) & &
targetFile . getAbsolutePath ( ) . equals ( this . originalPackagePath ) ) {
throw new InvalidOperationException (
" You can't call save(File) to save to the currently open " +
" file. To save to the current file, please just call close() "
) ;
}
// Do the save
2009-03-18 14:54:01 -04:00
FileOutputStream fos = null ;
try {
fos = new FileOutputStream ( targetFile ) ;
2015-03-14 09:18:43 -04:00
this . save ( fos ) ;
} finally {
2015-10-12 16:43:42 -04:00
if ( fos ! = null ) fos . close ( ) ;
2015-03-14 09:18:43 -04:00
}
2009-03-18 14:54:01 -04:00
}
/ * *
* Save the document in the specified output stream .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* /
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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @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 .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @param partName
* The URI of the part to retrieve .
* @return The package part located by the specified URI , else < b > null < / b > .
* /
protected abstract PackagePart getPartImpl ( PackagePartName partName ) ;
/ * *
* Get all parts link to the package .
2009-08-18 01:29:53 -04:00
*
2009-03-18 14:54:01 -04:00
* @return A list of the part owned by the package .
* /
protected abstract PackagePart [ ] getPartsImpl ( )
throws InvalidFormatException ;
2012-02-29 02:50:10 -05:00
/ * *
* Replace a content type in this package .
*
* < p >
* A typical scneario to call this method is to rename a template file to the main format , e . g .
* " .dotx " to " .docx "
* " .dotm " to " .docm "
* " .xltx " to " .xlsx "
* " .xltm " to " .xlsm "
* " .potx " to " .pptx "
* " .potm " to " .pptm "
* < / p >
* For example , a code converting a . xlsm macro workbook to . xlsx would look as follows :
* < p >
* < pre > < code >
*
* OPCPackage pkg = OPCPackage . open ( new FileInputStream ( " macro-workbook.xlsm " ) ) ;
* pkg . replaceContentType (
* " application/vnd.ms-excel.sheet.macroEnabled.main+xml " ,
* " application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml " ) ;
*
* FileOutputStream out = new FileOutputStream ( " workbook.xlsx " ) ;
* pkg . save ( out ) ;
* out . close ( ) ;
*
* < / code > < / pre >
* < / p >
*
* @param oldContentType the content type to be replaced
* @param newContentType the replacement
* @return whether replacement was succesfull
* @since POI - 3 . 8
* /
public boolean replaceContentType ( String oldContentType , String newContentType ) {
boolean success = false ;
ArrayList < PackagePart > list = getPartsByContentType ( oldContentType ) ;
for ( PackagePart packagePart : list ) {
if ( packagePart . getContentType ( ) . equals ( oldContentType ) ) {
PackagePartName partName = packagePart . getPartName ( ) ;
contentTypeManager . addContentType ( partName , newContentType ) ;
success = true ;
}
}
return success ;
}
2015-07-13 05:05:17 -04:00
/ * *
* Add the specified part , and register its content type with the content
* type manager .
*
* @param part
* The part to add .
* /
public void registerPartAndContentType ( PackagePart part ) {
addPackagePart ( part ) ;
this . contentTypeManager . addContentType ( part . getPartName ( ) , part . getContentType ( ) ) ;
this . isDirty = true ;
}
/ * *
* Remove the specified part , and clear its content type from the content
* type manager .
*
* @param partName
* The part name of the part to remove .
* /
public void unregisterPartAndContentType ( PackagePartName partName ) {
removePart ( partName ) ;
this . contentTypeManager . removeContentType ( partName ) ;
this . isDirty = true ;
}
2009-03-18 14:54:01 -04:00
}