2007-09-19 07:56:36 -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;
|
|
|
|
|
2018-06-02 16:29:35 -04:00
|
|
|
import static org.apache.poi.hpsf.PropertySetFactory.newDocumentSummaryInformation;
|
|
|
|
|
2007-09-19 07:56:36 -04:00
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
2016-06-19 18:12:55 -04:00
|
|
|
import java.io.Closeable;
|
2015-12-16 13:15:31 -05:00
|
|
|
import java.io.File;
|
2007-09-19 07:56:36 -04:00
|
|
|
import java.io.IOException;
|
2014-12-24 20:56:29 -05:00
|
|
|
import java.io.InputStream;
|
2008-05-20 13:12:08 -04:00
|
|
|
import java.io.OutputStream;
|
2017-06-14 13:21:50 -04:00
|
|
|
import java.security.GeneralSecurityException;
|
2007-09-19 07:56:36 -04:00
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import org.apache.poi.hpsf.DocumentSummaryInformation;
|
|
|
|
import org.apache.poi.hpsf.PropertySet;
|
|
|
|
import org.apache.poi.hpsf.PropertySetFactory;
|
|
|
|
import org.apache.poi.hpsf.SummaryInformation;
|
2018-11-01 06:30:16 -04:00
|
|
|
import org.apache.poi.hpsf.WritingNotSupportedException;
|
2014-12-24 20:56:29 -05:00
|
|
|
import org.apache.poi.poifs.crypt.EncryptionInfo;
|
2017-06-14 13:21:50 -04:00
|
|
|
import org.apache.poi.poifs.crypt.Encryptor;
|
2016-08-03 19:54:01 -04:00
|
|
|
import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
|
2017-06-14 13:21:50 -04:00
|
|
|
import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
|
2008-04-10 12:59:10 -04:00
|
|
|
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
2007-09-19 07:56:36 -04:00
|
|
|
import org.apache.poi.poifs.filesystem.DocumentInputStream;
|
|
|
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
2017-06-14 13:21:50 -04:00
|
|
|
import org.apache.poi.util.IOUtils;
|
2015-11-11 14:11:35 -05:00
|
|
|
import org.apache.poi.util.Internal;
|
2007-09-19 07:56:36 -04:00
|
|
|
import org.apache.poi.util.POILogFactory;
|
|
|
|
import org.apache.poi.util.POILogger;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This holds the common functionality for all POI
|
|
|
|
* Document classes.
|
|
|
|
* Currently, this relates to Document Information Properties
|
|
|
|
*/
|
2016-06-19 18:12:55 -04:00
|
|
|
public abstract class POIDocument implements Closeable {
|
2013-06-25 19:34:22 -04:00
|
|
|
/** Holds metadata on our document */
|
|
|
|
private SummaryInformation sInf;
|
|
|
|
/** Holds further metadata on our document */
|
|
|
|
private DocumentSummaryInformation dsInf;
|
|
|
|
/** The directory that our document lives in */
|
2016-12-01 18:46:27 -05:00
|
|
|
private DirectoryNode directory;
|
2013-06-25 19:34:22 -04:00
|
|
|
|
|
|
|
/** For our own logging use */
|
2016-06-13 06:55:49 -04:00
|
|
|
private static final POILogger logger = POILogFactory.getLogger(POIDocument.class);
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2008-03-03 10:10:46 -05:00
|
|
|
/* Have the property streams been read yet? (Only done on-demand) */
|
2016-12-26 14:51:32 -05:00
|
|
|
private boolean initialized;
|
2016-08-03 19:54:01 -04:00
|
|
|
|
2014-07-27 14:32:24 -04:00
|
|
|
/**
|
|
|
|
* Constructs a POIDocument with the given directory node.
|
|
|
|
*
|
|
|
|
* @param dir The {@link DirectoryNode} where information is read from.
|
|
|
|
*/
|
2010-12-28 22:19:46 -05:00
|
|
|
protected POIDocument(DirectoryNode dir) {
|
2008-04-10 12:59:10 -04:00
|
|
|
this.directory = dir;
|
|
|
|
}
|
2014-07-27 14:32:24 -04:00
|
|
|
|
2015-05-11 14:59:10 -04:00
|
|
|
/**
|
|
|
|
* Constructs from the default POIFS
|
2016-06-06 19:34:17 -04:00
|
|
|
*
|
|
|
|
* @param fs the filesystem the document is read from
|
2015-05-11 14:59:10 -04:00
|
|
|
*/
|
|
|
|
protected POIDocument(POIFSFileSystem fs) {
|
|
|
|
this(fs.getRoot());
|
|
|
|
}
|
2008-03-03 10:10:46 -05:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
/**
|
|
|
|
* Fetch the Document Summary Information of the document
|
2014-07-27 14:32:24 -04:00
|
|
|
*
|
|
|
|
* @return The Document Summary Information or null
|
|
|
|
* if it could not be read for this document.
|
2013-06-25 19:34:22 -04:00
|
|
|
*/
|
|
|
|
public DocumentSummaryInformation getDocumentSummaryInformation() {
|
2017-06-14 13:21:50 -04:00
|
|
|
if(!initialized) {
|
|
|
|
readProperties();
|
|
|
|
}
|
2008-03-03 10:10:46 -05:00
|
|
|
return dsInf;
|
|
|
|
}
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
/**
|
|
|
|
* Fetch the Summary Information of the document
|
2014-07-27 14:32:24 -04:00
|
|
|
*
|
|
|
|
* @return The Summary information for the document or null
|
|
|
|
* if it could not be read for this document.
|
2013-06-25 19:34:22 -04:00
|
|
|
*/
|
|
|
|
public SummaryInformation getSummaryInformation() {
|
2017-06-14 13:21:50 -04:00
|
|
|
if(!initialized) {
|
|
|
|
readProperties();
|
|
|
|
}
|
2008-03-03 10:10:46 -05:00
|
|
|
return sInf;
|
|
|
|
}
|
2009-11-25 07:37:48 -05:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
/**
|
|
|
|
* Will create whichever of SummaryInformation
|
|
|
|
* and DocumentSummaryInformation (HPSF) properties
|
|
|
|
* are not already part of your document.
|
|
|
|
* This is normally useful when creating a new
|
|
|
|
* document from scratch.
|
|
|
|
* If the information properties are already there,
|
|
|
|
* then nothing will happen.
|
|
|
|
*/
|
|
|
|
public void createInformationProperties() {
|
2017-06-14 13:21:50 -04:00
|
|
|
if (!initialized) {
|
|
|
|
readProperties();
|
|
|
|
}
|
2013-06-25 19:34:22 -04:00
|
|
|
if (sInf == null) {
|
|
|
|
sInf = PropertySetFactory.newSummaryInformation();
|
|
|
|
}
|
|
|
|
if (dsInf == null) {
|
2018-06-02 16:29:35 -04:00
|
|
|
dsInf = newDocumentSummaryInformation();
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
|
|
|
}
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
/**
|
|
|
|
* Find, and create objects for, the standard
|
2014-07-27 14:32:24 -04:00
|
|
|
* Document Information Properties (HPSF).
|
2013-06-25 19:34:22 -04:00
|
|
|
* If a given property set is missing or corrupt,
|
|
|
|
* it will remain null;
|
|
|
|
*/
|
|
|
|
protected void readProperties() {
|
2017-06-14 13:21:50 -04:00
|
|
|
if (initialized) {
|
|
|
|
return;
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
2017-06-14 13:21:50 -04:00
|
|
|
DocumentSummaryInformation dsi = readPropertySet(DocumentSummaryInformation.class, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
|
|
|
|
if (dsi != null) {
|
|
|
|
dsInf = dsi;
|
|
|
|
}
|
|
|
|
SummaryInformation si = readPropertySet(SummaryInformation.class, SummaryInformation.DEFAULT_STREAM_NAME);
|
|
|
|
if (si != null) {
|
|
|
|
sInf = si;
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
2008-03-03 10:10:46 -05:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
// Mark the fact that we've now loaded up the properties
|
2008-03-03 10:10:46 -05:00
|
|
|
initialized = true;
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2017-06-14 13:21:50 -04:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
private <T> T readPropertySet(Class<T> clazz, String name) {
|
|
|
|
String localName = clazz.getName().substring(clazz.getName().lastIndexOf('.')+1);
|
|
|
|
try {
|
|
|
|
PropertySet ps = getPropertySet(name);
|
|
|
|
if (clazz.isInstance(ps)) {
|
|
|
|
return (T)ps;
|
|
|
|
} else if (ps != null) {
|
|
|
|
logger.log(POILogger.WARN, localName+" property set came back with wrong class - "+ps.getClass().getName());
|
|
|
|
} else {
|
|
|
|
logger.log(POILogger.WARN, localName+" property set came back as null");
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
logger.log(POILogger.ERROR, "can't retrieve property set", e);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
/**
|
|
|
|
* For a given named property entry, either return it or null if
|
|
|
|
* if it wasn't found
|
2014-07-27 14:32:24 -04:00
|
|
|
*
|
|
|
|
* @param setName The property to read
|
|
|
|
* @return The value of the given property or null if it wasn't found.
|
2018-11-01 06:30:16 -04:00
|
|
|
*
|
|
|
|
* @throws IOException If retrieving properties fails
|
2013-06-25 19:34:22 -04:00
|
|
|
*/
|
2018-08-26 07:55:00 -04:00
|
|
|
@SuppressWarnings("WeakerAccess")
|
2017-06-14 13:21:50 -04:00
|
|
|
protected PropertySet getPropertySet(String setName) throws IOException {
|
|
|
|
return getPropertySet(setName, getEncryptionInfo());
|
2014-12-24 20:56:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For a given named property entry, either return it or null if
|
|
|
|
* if it wasn't found
|
|
|
|
*
|
|
|
|
* @param setName The property to read
|
|
|
|
* @param encryptionInfo the encryption descriptor in case of cryptoAPI encryption
|
|
|
|
* @return The value of the given property or null if it wasn't found.
|
2018-11-01 06:30:16 -04:00
|
|
|
*
|
|
|
|
* @throws IOException If retrieving properties fails
|
2014-12-24 20:56:29 -05:00
|
|
|
*/
|
2018-08-26 07:55:00 -04:00
|
|
|
@SuppressWarnings("WeakerAccess")
|
2017-06-14 13:21:50 -04:00
|
|
|
protected PropertySet getPropertySet(String setName, EncryptionInfo encryptionInfo) throws IOException {
|
2014-12-24 20:56:29 -05:00
|
|
|
DirectoryNode dirNode = directory;
|
|
|
|
|
2018-08-30 20:25:50 -04:00
|
|
|
POIFSFileSystem encPoifs = null;
|
2016-06-06 19:34:17 -04:00
|
|
|
String step = "getting";
|
|
|
|
try {
|
2017-06-06 18:21:11 -04:00
|
|
|
if (encryptionInfo != null && encryptionInfo.isDocPropsEncrypted()) {
|
2016-06-06 19:34:17 -04:00
|
|
|
step = "getting encrypted";
|
2017-06-14 13:21:50 -04:00
|
|
|
String encryptedStream = getEncryptedPropertyStreamName();
|
|
|
|
if (!dirNode.hasEntry(encryptedStream)) {
|
|
|
|
throw new EncryptedDocumentException("can't find encrypted property stream '"+encryptedStream+"'");
|
2016-06-06 19:34:17 -04:00
|
|
|
}
|
2016-08-03 19:54:01 -04:00
|
|
|
CryptoAPIDecryptor dec = (CryptoAPIDecryptor)encryptionInfo.getDecryptor();
|
|
|
|
encPoifs = dec.getSummaryEntries(dirNode, encryptedStream);
|
|
|
|
dirNode = encPoifs.getRoot();
|
2016-06-06 19:34:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//directory can be null when creating new documents
|
|
|
|
if (dirNode == null || !dirNode.hasEntry(setName)) {
|
2014-12-24 20:56:29 -05:00
|
|
|
return null;
|
|
|
|
}
|
2016-06-06 19:34:17 -04:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
// Find the entry, and get an input stream for it
|
2016-06-06 19:34:17 -04:00
|
|
|
step = "getting";
|
2018-08-26 07:41:09 -04:00
|
|
|
try (DocumentInputStream dis = dirNode.createDocumentInputStream(dirNode.getEntry(setName))) {
|
2016-06-06 19:34:17 -04:00
|
|
|
// Create the Property Set
|
|
|
|
step = "creating";
|
|
|
|
return PropertySetFactory.create(dis);
|
|
|
|
}
|
2017-06-14 13:21:50 -04:00
|
|
|
} catch (IOException e) {
|
|
|
|
throw e;
|
2016-06-06 19:34:17 -04:00
|
|
|
} catch (Exception e) {
|
2017-06-14 13:21:50 -04:00
|
|
|
throw new IOException("Error "+step+" property set with name " + setName, e);
|
2016-06-06 19:34:17 -04:00
|
|
|
} finally {
|
2017-06-14 13:21:50 -04:00
|
|
|
IOUtils.closeQuietly(encPoifs);
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
|
|
|
}
|
2015-05-11 14:04:30 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes out the updated standard Document Information Properties (HPSF)
|
|
|
|
* into the currently open NPOIFSFileSystem
|
|
|
|
*
|
|
|
|
* @throws IOException if an error when writing to the open
|
2018-08-30 20:25:50 -04:00
|
|
|
* {@link POIFSFileSystem} occurs
|
2015-05-11 14:04:30 -04:00
|
|
|
*/
|
|
|
|
protected void writeProperties() throws IOException {
|
2016-07-21 19:09:07 -04:00
|
|
|
validateInPlaceWritePossible();
|
|
|
|
writeProperties(directory.getFileSystem(), null);
|
2015-05-11 14:04:30 -04:00
|
|
|
}
|
2013-06-25 19:34:22 -04:00
|
|
|
|
|
|
|
/**
|
2015-05-11 14:04:30 -04:00
|
|
|
* Writes out the standard Document Information Properties (HPSF)
|
2013-06-25 19:34:22 -04:00
|
|
|
* @param outFS the POIFSFileSystem to write the properties into
|
2014-07-27 14:32:24 -04:00
|
|
|
*
|
|
|
|
* @throws IOException if an error when writing to the
|
2018-08-30 20:25:50 -04:00
|
|
|
* {@link POIFSFileSystem} occurs
|
2013-06-25 19:34:22 -04:00
|
|
|
*/
|
2018-08-30 20:25:50 -04:00
|
|
|
protected void writeProperties(POIFSFileSystem outFS) throws IOException {
|
2013-06-25 19:34:22 -04:00
|
|
|
writeProperties(outFS, null);
|
|
|
|
}
|
|
|
|
/**
|
2015-05-11 14:04:30 -04:00
|
|
|
* Writes out the standard Document Information Properties (HPSF)
|
|
|
|
* @param outFS the NPOIFSFileSystem to write the properties into
|
2013-06-25 19:34:22 -04:00
|
|
|
* @param writtenEntries a list of POIFS entries to add the property names too
|
2014-07-27 14:32:24 -04:00
|
|
|
*
|
|
|
|
* @throws IOException if an error when writing to the
|
2018-08-30 20:25:50 -04:00
|
|
|
* {@link POIFSFileSystem} occurs
|
2013-06-25 19:34:22 -04:00
|
|
|
*/
|
2018-08-30 20:25:50 -04:00
|
|
|
protected void writeProperties(POIFSFileSystem outFS, List<String> writtenEntries) throws IOException {
|
2018-06-02 16:29:35 -04:00
|
|
|
final EncryptionInfo ei = getEncryptionInfo();
|
2017-06-14 13:21:50 -04:00
|
|
|
final boolean encryptProps = (ei != null && ei.isDocPropsEncrypted());
|
2018-08-30 20:25:50 -04:00
|
|
|
try (POIFSFileSystem tmpFS = new POIFSFileSystem()) {
|
|
|
|
final POIFSFileSystem fs = (encryptProps) ? tmpFS : outFS;
|
2018-06-02 16:29:35 -04:00
|
|
|
|
|
|
|
writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME, getSummaryInformation(), fs, writtenEntries);
|
|
|
|
writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, getDocumentSummaryInformation(), fs, writtenEntries);
|
|
|
|
|
|
|
|
if (!encryptProps) {
|
|
|
|
return;
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
2018-06-02 16:29:35 -04:00
|
|
|
|
|
|
|
// create empty document summary
|
|
|
|
writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, newDocumentSummaryInformation(), outFS);
|
|
|
|
|
|
|
|
// remove summary, if previously available
|
|
|
|
if (outFS.getRoot().hasEntry(SummaryInformation.DEFAULT_STREAM_NAME)) {
|
|
|
|
outFS.getRoot().getEntry(SummaryInformation.DEFAULT_STREAM_NAME).delete();
|
|
|
|
}
|
|
|
|
Encryptor encGen = ei.getEncryptor();
|
|
|
|
if (!(encGen instanceof CryptoAPIEncryptor)) {
|
|
|
|
throw new EncryptedDocumentException(
|
|
|
|
"Using " + ei.getEncryptionMode() + " encryption. Only CryptoAPI encryption supports encrypted property sets!");
|
|
|
|
}
|
|
|
|
CryptoAPIEncryptor enc = (CryptoAPIEncryptor) encGen;
|
|
|
|
try {
|
|
|
|
enc.setSummaryEntries(outFS.getRoot(), getEncryptedPropertyStreamName(), fs);
|
|
|
|
} catch (GeneralSecurityException e) {
|
|
|
|
throw new IOException(e);
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
|
|
|
}
|
2018-06-02 16:29:35 -04:00
|
|
|
}
|
2017-06-14 13:21:50 -04:00
|
|
|
|
2018-08-30 20:25:50 -04:00
|
|
|
private void writePropertySet(String name, PropertySet ps, POIFSFileSystem outFS, List<String> writtenEntries)
|
2018-06-02 16:29:35 -04:00
|
|
|
throws IOException {
|
|
|
|
if (ps == null) {
|
2017-06-14 13:21:50 -04:00
|
|
|
return;
|
|
|
|
}
|
2018-06-02 16:29:35 -04:00
|
|
|
writePropertySet(name, ps, outFS);
|
|
|
|
if (writtenEntries != null) {
|
|
|
|
writtenEntries.add(name);
|
2017-06-14 13:21:50 -04:00
|
|
|
}
|
2013-06-25 19:34:22 -04:00
|
|
|
}
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
/**
|
2018-11-01 06:30:16 -04:00
|
|
|
* Writes out a given PropertySet
|
|
|
|
*
|
2013-06-25 19:34:22 -04:00
|
|
|
* @param name the (POIFS Level) name of the property to write
|
|
|
|
* @param set the PropertySet to write out
|
2015-05-11 14:04:30 -04:00
|
|
|
* @param outFS the NPOIFSFileSystem to write the property into
|
2014-07-27 14:32:24 -04:00
|
|
|
*
|
|
|
|
* @throws IOException if an error when writing to the
|
2018-08-30 20:25:50 -04:00
|
|
|
* {@link POIFSFileSystem} occurs
|
2013-06-25 19:34:22 -04:00
|
|
|
*/
|
2018-08-30 20:25:50 -04:00
|
|
|
private void writePropertySet(String name, PropertySet set, POIFSFileSystem outFS) throws IOException {
|
2013-06-25 19:34:22 -04:00
|
|
|
try {
|
2016-12-26 14:51:32 -05:00
|
|
|
PropertySet mSet = new PropertySet(set);
|
2013-06-25 19:34:22 -04:00
|
|
|
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
mSet.write(bOut);
|
|
|
|
byte[] data = bOut.toByteArray();
|
|
|
|
ByteArrayInputStream bIn = new ByteArrayInputStream(data);
|
2016-07-21 19:09:07 -04:00
|
|
|
|
|
|
|
// Create or Update the Property Set stream in the POIFS
|
|
|
|
outFS.createOrUpdateDocument(bIn, name);
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
logger.log(POILogger.INFO, "Wrote property set " + name + " of size " + data.length);
|
2018-11-01 06:30:16 -04:00
|
|
|
} catch(WritingNotSupportedException ignored) {
|
2013-06-25 19:34:22 -04:00
|
|
|
logger.log( POILogger.ERROR, "Couldn't write property set with name " + name + " as not supported by HPSF yet");
|
|
|
|
}
|
|
|
|
}
|
2007-09-19 07:56:36 -04:00
|
|
|
|
2016-07-20 14:41:27 -04:00
|
|
|
/**
|
|
|
|
* Called during a {@link #write()} to ensure that the Document (and
|
|
|
|
* associated {@link POIFSFileSystem}) was opened in a way compatible
|
|
|
|
* with an in-place write.
|
|
|
|
*
|
|
|
|
* @throws IllegalStateException if the document was opened suitably
|
|
|
|
*/
|
|
|
|
protected void validateInPlaceWritePossible() throws IllegalStateException {
|
|
|
|
if (directory == null) {
|
|
|
|
throw new IllegalStateException("Newly created Document, cannot save in-place");
|
|
|
|
}
|
|
|
|
if (directory.getParent() != null) {
|
|
|
|
throw new IllegalStateException("This is not the root Document, cannot save embedded resource in-place");
|
|
|
|
}
|
|
|
|
if (directory.getFileSystem() == null ||
|
|
|
|
!directory.getFileSystem().isInPlaceWriteable()) {
|
|
|
|
throw new IllegalStateException("Opened read-only or via an InputStream, a Writeable File is required");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes the document out to the currently open {@link File}, via the
|
|
|
|
* writeable {@link POIFSFileSystem} it was opened from.
|
|
|
|
*
|
|
|
|
* <p>This will fail (with an {@link IllegalStateException} if the
|
|
|
|
* document was opened read-only, opened from an {@link InputStream}
|
|
|
|
* instead of a File, or if this is not the root document. For those cases,
|
|
|
|
* you must use {@link #write(OutputStream)} or {@link #write(File)} to
|
|
|
|
* write to a brand new document.
|
2016-07-21 06:27:27 -04:00
|
|
|
*
|
|
|
|
* @since POI 3.15 beta 3
|
2016-07-20 14:41:27 -04:00
|
|
|
*
|
|
|
|
* @throws IOException thrown on errors writing to the file
|
2016-07-21 19:09:07 -04:00
|
|
|
* @throws IllegalStateException if this isn't from a writable File
|
2016-07-20 14:41:27 -04:00
|
|
|
*/
|
2016-07-20 18:35:51 -04:00
|
|
|
public abstract void write() throws IOException;
|
2016-07-20 14:41:27 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes the document out to the specified new {@link File}. If the file
|
|
|
|
* exists, it will be replaced, otherwise a new one will be created
|
|
|
|
*
|
2016-07-21 06:27:27 -04:00
|
|
|
* @since POI 3.15 beta 3
|
|
|
|
*
|
2016-07-20 14:41:27 -04:00
|
|
|
* @param newFile The new File to write to.
|
|
|
|
*
|
|
|
|
* @throws IOException thrown on errors writing to the file
|
|
|
|
*/
|
2016-07-20 18:35:51 -04:00
|
|
|
public abstract void write(File newFile) throws IOException;
|
2016-07-20 14:41:27 -04:00
|
|
|
|
2013-06-25 19:34:22 -04:00
|
|
|
/**
|
2014-07-27 14:32:24 -04:00
|
|
|
* Writes the document out to the specified output stream. The
|
|
|
|
* stream is not closed as part of this operation.
|
|
|
|
*
|
2015-12-16 13:15:31 -05:00
|
|
|
* Note - if the Document was opened from a {@link File} rather
|
2016-07-20 14:41:27 -04:00
|
|
|
* than an {@link InputStream}, you <b>must</b> write out using
|
|
|
|
* {@link #write()} or to a different File. Overwriting the currently
|
|
|
|
* open file via an OutputStream isn't possible.
|
2016-07-17 16:25:37 -04:00
|
|
|
*
|
|
|
|
* If {@code stream} is a {@link java.io.FileOutputStream} on a networked drive
|
|
|
|
* or has a high cost/latency associated with each written byte,
|
|
|
|
* consider wrapping the OutputStream in a {@link java.io.BufferedOutputStream}
|
2016-07-20 14:41:27 -04:00
|
|
|
* to improve write performance, or use {@link #write()} / {@link #write(File)}
|
|
|
|
* if possible.
|
2015-12-16 13:15:31 -05:00
|
|
|
*
|
2014-07-27 14:32:24 -04:00
|
|
|
* @param out The stream to write to.
|
|
|
|
*
|
|
|
|
* @throws IOException thrown on errors writing to the stream
|
2013-06-25 19:34:22 -04:00
|
|
|
*/
|
|
|
|
public abstract void write(OutputStream out) throws IOException;
|
2015-11-11 14:11:35 -05:00
|
|
|
|
2016-06-19 18:12:55 -04:00
|
|
|
/**
|
2018-08-30 20:25:50 -04:00
|
|
|
* Closes the underlying {@link POIFSFileSystem} from which
|
2016-06-19 18:12:55 -04:00
|
|
|
* the document was read, if any. Has no effect on documents
|
2018-08-26 07:55:00 -04:00
|
|
|
* opened from an InputStream, or newly created ones.<p>
|
|
|
|
*
|
|
|
|
* Once {@code close()} has been called, no further operations
|
2016-06-19 18:12:55 -04:00
|
|
|
* should be called on the document.
|
|
|
|
*/
|
2016-12-26 14:51:32 -05:00
|
|
|
@Override
|
2016-06-19 18:12:55 -04:00
|
|
|
public void close() throws IOException {
|
|
|
|
if (directory != null) {
|
|
|
|
if (directory.getNFileSystem() != null) {
|
|
|
|
directory.getNFileSystem().close();
|
2016-12-01 18:46:27 -05:00
|
|
|
clearDirectory();
|
2016-06-19 18:12:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 14:11:35 -05:00
|
|
|
@Internal
|
|
|
|
public DirectoryNode getDirectory() {
|
|
|
|
return directory;
|
|
|
|
}
|
2016-12-01 18:46:27 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear/unlink the attached directory entry
|
|
|
|
*/
|
|
|
|
@Internal
|
|
|
|
protected void clearDirectory() {
|
|
|
|
directory = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* check if we were created by POIFS otherwise create a new dummy POIFS
|
|
|
|
* for storing the package data
|
|
|
|
*
|
|
|
|
* @return {@code true} if dummy directory was created, {@code false} otherwise
|
|
|
|
*/
|
2016-12-12 19:36:03 -05:00
|
|
|
@SuppressWarnings("resource")
|
2016-12-01 18:46:27 -05:00
|
|
|
@Internal
|
|
|
|
protected boolean initDirectory() {
|
|
|
|
if (directory == null) {
|
2018-08-30 20:25:50 -04:00
|
|
|
directory = new POIFSFileSystem().getRoot(); // NOSONAR
|
2016-12-01 18:46:27 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces the attached directory, e.g. if this document is written
|
|
|
|
* to a new POIFSFileSystem
|
|
|
|
*
|
|
|
|
* @param newDirectory the new directory
|
|
|
|
*/
|
|
|
|
@Internal
|
2018-08-26 07:55:00 -04:00
|
|
|
protected void replaceDirectory(DirectoryNode newDirectory) {
|
2016-12-01 18:46:27 -05:00
|
|
|
directory = newDirectory;
|
|
|
|
}
|
2017-06-14 13:21:50 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the stream name of the property set collection, if the document is encrypted
|
|
|
|
*/
|
|
|
|
protected String getEncryptedPropertyStreamName() {
|
|
|
|
return "encryption";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the encryption info if the document is encrypted, otherwise {@code null}
|
2018-11-01 06:30:16 -04:00
|
|
|
*
|
|
|
|
* @throws IOException If retrieving the encryption information fails
|
2017-06-14 13:21:50 -04:00
|
|
|
*/
|
|
|
|
public EncryptionInfo getEncryptionInfo() throws IOException {
|
|
|
|
return null;
|
|
|
|
}
|
2007-09-19 07:56:36 -04:00
|
|
|
}
|