2009-05-20 04:02:35 -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.File;
|
2016-03-28 18:49:45 -04:00
|
|
|
import java.io.FileInputStream;
|
2009-05-20 04:02:35 -04:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.util.Enumeration;
|
|
|
|
import java.util.zip.ZipEntry;
|
|
|
|
import java.util.zip.ZipFile;
|
|
|
|
import java.util.zip.ZipOutputStream;
|
|
|
|
|
|
|
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
|
|
import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
|
2016-03-15 08:30:45 -04:00
|
|
|
import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
|
2016-03-15 07:50:57 -04:00
|
|
|
import org.apache.poi.openxml4j.exceptions.ODFNotOfficeXmlFileException;
|
2009-05-20 04:02:35 -04:00
|
|
|
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
|
2011-06-30 11:39:47 -04:00
|
|
|
import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
|
2009-05-20 04:02:35 -04:00
|
|
|
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.ZipPartMarshaller;
|
|
|
|
import org.apache.poi.openxml4j.util.ZipEntrySource;
|
|
|
|
import org.apache.poi.openxml4j.util.ZipFileZipEntrySource;
|
|
|
|
import org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource;
|
2015-06-23 19:39:07 -04:00
|
|
|
import org.apache.poi.openxml4j.util.ZipSecureFile.ThresholdInputStream;
|
2009-05-20 04:02:35 -04:00
|
|
|
import org.apache.poi.util.POILogFactory;
|
2013-11-12 04:51:13 -05:00
|
|
|
import org.apache.poi.util.POILogger;
|
2014-07-24 14:58:27 -04:00
|
|
|
import org.apache.poi.util.TempFile;
|
2009-05-20 04:02:35 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Physical zip package.
|
|
|
|
*/
|
2016-06-15 00:22:49 -04:00
|
|
|
public final class ZipPackage extends OPCPackage {
|
2013-11-12 04:53:57 -05:00
|
|
|
private static POILogger logger = POILogFactory.getLogger(ZipPackage.class);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Zip archive, as either a file on disk,
|
|
|
|
* or a stream
|
|
|
|
*/
|
|
|
|
private final ZipEntrySource zipArchive;
|
|
|
|
|
|
|
|
/**
|
2013-11-12 04:55:46 -05:00
|
|
|
* Constructor. Creates a new, empty ZipPackage.
|
2013-11-12 04:53:57 -05:00
|
|
|
*/
|
|
|
|
public ZipPackage() {
|
2016-03-15 07:54:16 -04:00
|
|
|
super(defaultPackageAccess);
|
|
|
|
this.zipArchive = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.contentTypeManager = new ZipContentTypeManager(null, this);
|
|
|
|
} catch (InvalidFormatException e) {
|
|
|
|
logger.log(POILogger.WARN,"Could not parse ZipPackage", e);
|
|
|
|
}
|
2013-11-12 04:53:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor. Opens a Zip based Open XML document from
|
|
|
|
* an InputStream.
|
|
|
|
*
|
|
|
|
* @param in
|
|
|
|
* Zip input stream to load.
|
|
|
|
* @param access
|
|
|
|
* The package access mode.
|
|
|
|
* @throws IllegalArgumentException
|
|
|
|
* If the specified input stream not an instance of
|
|
|
|
* ZipInputStream.
|
|
|
|
*/
|
|
|
|
ZipPackage(InputStream in, PackageAccess access) throws IOException {
|
2016-03-15 07:54:16 -04:00
|
|
|
super(access);
|
2016-03-28 18:49:45 -04:00
|
|
|
@SuppressWarnings("resource")
|
2016-03-15 08:03:30 -04:00
|
|
|
ThresholdInputStream zis = ZipHelper.openZipStream(in);
|
|
|
|
this.zipArchive = new ZipInputStreamZipEntrySource(zis);
|
2013-11-12 04:53:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-11-12 04:55:46 -05:00
|
|
|
* Constructor. Opens a Zip based Open XML document from a file.
|
2013-11-12 04:53:57 -05:00
|
|
|
*
|
|
|
|
* @param path
|
|
|
|
* The path of the file to open or create.
|
|
|
|
* @param access
|
|
|
|
* The package access mode.
|
|
|
|
*/
|
|
|
|
ZipPackage(String path, PackageAccess access) {
|
2016-03-28 18:49:45 -04:00
|
|
|
this(new File(path), access);
|
2013-11-12 04:53:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-11-12 04:55:46 -05:00
|
|
|
* Constructor. Opens a Zip based Open XML document from a File.
|
2013-11-12 04:53:57 -05:00
|
|
|
*
|
|
|
|
* @param file
|
|
|
|
* The file to open or create.
|
|
|
|
* @param access
|
|
|
|
* The package access mode.
|
|
|
|
*/
|
2016-03-28 18:49:45 -04:00
|
|
|
@SuppressWarnings("resource")
|
2013-11-12 04:53:57 -05:00
|
|
|
ZipPackage(File file, PackageAccess access) {
|
2016-03-15 07:54:16 -04:00
|
|
|
super(access);
|
2013-11-12 04:53:57 -05:00
|
|
|
|
2016-03-28 18:49:45 -04:00
|
|
|
ZipEntrySource ze;
|
2016-03-15 07:54:16 -04:00
|
|
|
try {
|
2016-03-28 18:49:45 -04:00
|
|
|
final ZipFile zipFile = ZipHelper.openZipFile(file);
|
|
|
|
ze = new ZipFileZipEntrySource(zipFile);
|
2016-03-15 07:54:16 -04:00
|
|
|
} catch (IOException e) {
|
2016-03-28 18:49:45 -04:00
|
|
|
// probably not happening with write access - not sure how to handle the default read-write access ...
|
|
|
|
if (access == PackageAccess.WRITE) {
|
|
|
|
throw new InvalidOperationException("Can't open the specified file: '" + file + "'", e);
|
|
|
|
}
|
|
|
|
logger.log(POILogger.ERROR, "Error in zip file "+file+" - falling back to stream processing (i.e. ignoring zip central directory)");
|
|
|
|
// some zips can't be opened via ZipFile in JDK6, as the central directory
|
|
|
|
// contains either non-latin entries or the compression type can't be handled
|
|
|
|
// the workaround is to iterate over the stream and not the directory
|
2016-04-08 10:36:05 -04:00
|
|
|
FileInputStream fis = null;
|
|
|
|
ThresholdInputStream zis = null;
|
2016-03-28 18:49:45 -04:00
|
|
|
try {
|
|
|
|
fis = new FileInputStream(file);
|
2016-04-08 10:36:05 -04:00
|
|
|
zis = ZipHelper.openZipStream(fis);
|
2016-03-28 18:49:45 -04:00
|
|
|
ze = new ZipInputStreamZipEntrySource(zis);
|
|
|
|
} catch (IOException e2) {
|
2016-04-08 10:36:05 -04:00
|
|
|
if (zis != null) {
|
|
|
|
try {
|
|
|
|
zis.close();
|
|
|
|
} catch (IOException e3) {
|
|
|
|
throw new InvalidOperationException("Can't open the specified file: '" + file + "'"+
|
|
|
|
" and couldn't close the file input stream", e);
|
|
|
|
}
|
|
|
|
} else if (fis != null) {
|
|
|
|
try {
|
|
|
|
fis.close();
|
|
|
|
} catch (IOException e3) {
|
|
|
|
throw new InvalidOperationException("Can't open the specified file: '" + file + "'"+
|
|
|
|
" and couldn't close the file input stream", e);
|
|
|
|
}
|
|
|
|
}
|
2016-03-28 18:49:45 -04:00
|
|
|
throw new InvalidOperationException("Can't open the specified file: '" + file + "'", e);
|
|
|
|
}
|
2016-03-15 07:54:16 -04:00
|
|
|
}
|
2016-03-28 18:49:45 -04:00
|
|
|
this.zipArchive = ze;
|
2013-11-12 04:53:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor. Opens a Zip based Open XML document from
|
|
|
|
* a custom ZipEntrySource, typically an open archive
|
|
|
|
* from another system
|
|
|
|
*
|
|
|
|
* @param zipEntry
|
|
|
|
* Zip data to load.
|
|
|
|
* @param access
|
|
|
|
* The package access mode.
|
|
|
|
*/
|
2013-11-12 04:55:46 -05:00
|
|
|
ZipPackage(ZipEntrySource zipEntry, PackageAccess access) {
|
2016-03-15 07:54:16 -04:00
|
|
|
super(access);
|
|
|
|
this.zipArchive = zipEntry;
|
2013-11-12 04:53:57 -05:00
|
|
|
}
|
2013-11-12 04:51:13 -05:00
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* 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()]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// First we need to parse the content type part
|
|
|
|
Enumeration<? extends ZipEntry> entries = this.zipArchive.getEntries();
|
|
|
|
while (entries.hasMoreElements()) {
|
|
|
|
ZipEntry entry = entries.nextElement();
|
|
|
|
if (entry.getName().equalsIgnoreCase(
|
|
|
|
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) {
|
|
|
|
// Is it a different Zip-based format?
|
2016-03-15 08:30:45 -04:00
|
|
|
int numEntries = 0;
|
2016-03-15 07:54:16 -04:00
|
|
|
boolean hasMimetype = false;
|
|
|
|
boolean hasSettingsXML = false;
|
|
|
|
entries = this.zipArchive.getEntries();
|
|
|
|
while (entries.hasMoreElements()) {
|
|
|
|
ZipEntry entry = entries.nextElement();
|
|
|
|
if (entry.getName().equals("mimetype")) {
|
|
|
|
hasMimetype = true;
|
|
|
|
}
|
2016-03-15 07:50:57 -04:00
|
|
|
if (entry.getName().equals("settings.xml")) {
|
|
|
|
hasSettingsXML = true;
|
|
|
|
}
|
2016-03-15 08:30:45 -04:00
|
|
|
numEntries++;
|
2016-03-15 07:54:16 -04:00
|
|
|
}
|
|
|
|
if (hasMimetype && hasSettingsXML) {
|
|
|
|
throw new ODFNotOfficeXmlFileException(
|
|
|
|
"The supplied data appears to be in ODF (Open Document) Format. " +
|
|
|
|
"Formats like these (eg ODS, ODP) are not supported, try Apache ODFToolkit");
|
|
|
|
}
|
2016-03-15 08:30:45 -04:00
|
|
|
if (numEntries == 0) {
|
|
|
|
throw new NotOfficeXmlFileException(
|
|
|
|
"No valid entries or contents found, this is not a valid OOXML " +
|
|
|
|
"(Office Open XML) file");
|
|
|
|
}
|
2016-03-15 07:54:16 -04:00
|
|
|
|
|
|
|
// Fallback exception
|
|
|
|
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 = 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 = 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 partList.values().toArray(new ZipPackagePart[partList.size()]);
|
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* 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().equalsIgnoreCase(
|
|
|
|
ContentTypeManager.CONTENT_TYPES_PART_NAME)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return PackagingURIHelper.createPartName(ZipHelper
|
|
|
|
.getOPCNameFromZipItemName(entry.getName()));
|
|
|
|
} catch (Exception e) {
|
|
|
|
// We assume we can continue, even in degraded mode ...
|
|
|
|
logger.log(POILogger.WARN,"Entry "
|
|
|
|
+ entry.getName()
|
|
|
|
+ " is not valid, so this part won't be add to the package.", e);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* Create a new MemoryPackagePart from the specified URI and content type
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* aram partName The part URI.
|
|
|
|
*
|
|
|
|
* @param contentType
|
|
|
|
* The part content type.
|
|
|
|
* @return The newly created zip package part, else <b>null</b>.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected PackagePart createPartImpl(PackagePartName partName,
|
|
|
|
String contentType, boolean loadRelationships) {
|
|
|
|
if (contentType == null)
|
|
|
|
throw new IllegalArgumentException("contentType");
|
|
|
|
|
|
|
|
if (partName == null)
|
|
|
|
throw new IllegalArgumentException("partName");
|
|
|
|
|
|
|
|
try {
|
|
|
|
return new MemoryPackagePart(this, partName, contentType,
|
|
|
|
loadRelationships);
|
|
|
|
} catch (InvalidFormatException e) {
|
|
|
|
logger.log(POILogger.WARN, e);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* 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");
|
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* Flush the package. Do nothing.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected void flushImpl() {
|
|
|
|
// Do nothing
|
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* Close and save the package.
|
|
|
|
*
|
|
|
|
* @see #close()
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected void closeImpl() throws IOException {
|
|
|
|
// Flush the package
|
|
|
|
flush();
|
2009-05-20 04:02:35 -04:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2014-07-24 14:58:27 -04:00
|
|
|
File tempFile = TempFile.createTempFile(
|
2009-05-20 04:02:35 -04:00
|
|
|
generateTempFileName(FileHelper
|
|
|
|
.getDirectory(targetFile)), ".tmp");
|
|
|
|
|
|
|
|
// Save the final package to a temporary file
|
|
|
|
try {
|
|
|
|
save(tempFile);
|
2010-08-04 11:58:51 -04:00
|
|
|
|
|
|
|
// Close the current zip file, so we can
|
|
|
|
// overwrite it on all platforms
|
|
|
|
this.zipArchive.close();
|
|
|
|
// Copy the new file over the old one
|
2009-05-20 04:02:35 -04:00
|
|
|
FileHelper.copyFile(tempFile, targetFile);
|
|
|
|
} finally {
|
|
|
|
// Either the save operation succeed or not, we delete the
|
|
|
|
// temporary file
|
|
|
|
if (!tempFile.delete()) {
|
|
|
|
logger
|
|
|
|
.log(POILogger.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 !");
|
|
|
|
}
|
2010-08-04 11:58:51 -04:00
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* Implement the getPart() method to retrieve a part from its URI in the
|
|
|
|
* current package
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @see #getPart(PackageRelationship)
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected PackagePart getPartImpl(PackagePartName partName) {
|
|
|
|
if (partList.containsKey(partName)) {
|
|
|
|
return partList.get(partName);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Save this package into the specified stream
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param outputStream
|
|
|
|
* The stream use to save this package.
|
|
|
|
*
|
|
|
|
* @see #save(OutputStream)
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void saveImpl(OutputStream outputStream) {
|
|
|
|
// Check that the document was open in write mode
|
|
|
|
throwExceptionIfReadOnly();
|
|
|
|
|
2016-02-15 05:08:26 -05:00
|
|
|
final ZipOutputStream zos;
|
2009-05-20 04:02:35 -04:00
|
|
|
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
|
2011-06-30 11:39:47 -04:00
|
|
|
if (this.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).size() == 0 &&
|
|
|
|
this.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES_ECMA376).size() == 0 ) {
|
2009-05-20 04:02:35 -04:00
|
|
|
logger.log(POILogger.DEBUG,"Save core properties part");
|
2015-02-28 12:36:19 -05:00
|
|
|
|
|
|
|
// Ensure that core properties are added if missing
|
|
|
|
getPackageProperties();
|
2014-05-14 17:14:16 -04:00
|
|
|
// Add core properties to part list ...
|
|
|
|
addPackagePart(this.packageProperties);
|
2009-05-20 04:02:35 -04:00
|
|
|
// ... 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.log(POILogger.DEBUG,"Save package relationships");
|
|
|
|
ZipPartMarshaller.marshallRelationshipPart(this.getRelationships(),
|
|
|
|
PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_PART_NAME,
|
|
|
|
zos);
|
|
|
|
|
|
|
|
// Save content type part.
|
|
|
|
logger.log(POILogger.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.log(POILogger.DEBUG,"Save part '"
|
|
|
|
+ ZipHelper.getZipItemNameFromOPCName(part
|
|
|
|
.getPartName().getName()) + "'");
|
|
|
|
PartMarshaller marshaller = partMarshallers
|
2009-08-18 01:29:53 -04:00
|
|
|
.get(part._contentType);
|
2009-05-20 04:02:35 -04:00
|
|
|
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();
|
2016-02-15 05:08:26 -05:00
|
|
|
} catch (OpenXML4JRuntimeException e) {
|
|
|
|
// no need to wrap this type of Exception
|
|
|
|
throw e;
|
2009-05-20 04:02:35 -04:00
|
|
|
} catch (Exception e) {
|
2011-06-30 11:39:47 -04:00
|
|
|
throw new OpenXML4JRuntimeException(
|
|
|
|
"Fail to save: an error occurs while saving the package : "
|
|
|
|
+ e.getMessage(), e);
|
2009-05-20 04:02:35 -04:00
|
|
|
}
|
2016-03-15 07:54:16 -04:00
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
|
2016-03-15 07:54:16 -04:00
|
|
|
/**
|
|
|
|
* Get the zip archive
|
|
|
|
*
|
|
|
|
* @return The zip archive.
|
|
|
|
*/
|
|
|
|
public ZipEntrySource getZipArchive() {
|
|
|
|
return zipArchive;
|
|
|
|
}
|
2009-05-20 04:02:35 -04:00
|
|
|
}
|