From 03805d9e201f44d6cf85eeface28908251885089 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Tue, 26 May 2015 19:30:21 +0000 Subject: [PATCH] Add WorkbookFactory.create() with a flag to allow to open files read-only, keep the current way of opening read/write as default to not break existing code. Also adjust Javadoc somewhat. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1681823 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/usermodel/WorkbookFactory.java | 153 +++++++++++++----- .../apache/poi/ss/TestWorkbookFactory.java | 16 ++ 2 files changed, 129 insertions(+), 40 deletions(-) diff --git a/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java b/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java index 3f5d5ea835..dfdf3f92ec 100644 --- a/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java +++ b/src/ooxml/java/org/apache/poi/ss/usermodel/WorkbookFactory.java @@ -30,6 +30,7 @@ import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.poifs.crypt.Decryptor; import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.filesystem.DirectoryNode; @@ -41,22 +42,22 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * Factory for creating the appropriate kind of Workbook - * (be it {@link HSSFWorkbook} or {@link XSSFWorkbook}), + * (be it {@link HSSFWorkbook} or {@link XSSFWorkbook}), * by auto-detecting from the supplied input. */ public class WorkbookFactory { /** * Creates a HSSFWorkbook from the given POIFSFileSystem - *

Note that in order to properly release resources the + *

Note that in order to properly release resources the * Workbook should be closed after use. */ public static Workbook create(POIFSFileSystem fs) throws IOException { return new HSSFWorkbook(fs); } - + /** * Creates a HSSFWorkbook from the given NPOIFSFileSystem - *

Note that in order to properly release resources the + *

Note that in order to properly release resources the * Workbook should be closed after use. */ public static Workbook create(NPOIFSFileSystem fs) throws IOException { @@ -67,19 +68,27 @@ public class WorkbookFactory { throw new IOException(e); } } - + /** * Creates a Workbook from the given NPOIFSFileSystem, which may * be password protected + * + * @param fs The {@link NPOIFSFileSystem} to read the document from + * @param password The password that should be used or null if no password is necessary. + * + * @return The created Workbook + * + * @throws IOException if an error occurs while reading the data + * @throws InvalidFormatException if the contents of the file cannot be parsed into a {@link Workbook} */ private static Workbook create(NPOIFSFileSystem fs, String password) throws IOException, InvalidFormatException { DirectoryNode root = fs.getRoot(); - + // Encrypted OOXML files go inside OLE2 containers, is this one? if (root.hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)) { EncryptionInfo info = new EncryptionInfo(fs); Decryptor d = Decryptor.getInstance(info); - + boolean passwordCorrect = false; InputStream stream = null; try { @@ -95,18 +104,18 @@ public class WorkbookFactory { } catch (GeneralSecurityException e) { throw new IOException(e); } - + if (! passwordCorrect) { if (password != null) throw new EncryptedDocumentException("Password incorrect"); else throw new EncryptedDocumentException("The supplied spreadsheet is protected, but no password was supplied"); } - + OPCPackage pkg = OPCPackage.open(stream); return create(pkg); } - + // If we get here, it isn't an encrypted XLSX file // So, treat it as a regular HSSF XLS one if (password != null) { @@ -116,53 +125,78 @@ public class WorkbookFactory { Biff8EncryptionKey.setCurrentUserPassword(null); return wb; } - + /** * Creates a XSSFWorkbook from the given OOXML Package - *

Note that in order to properly release resources the - * Workbook should be closed after use. + * + *

Note that in order to properly release resources the + * Workbook should be closed after use.

+ * + * @param pkg The {@link OPCPackage} opened for reading data. + * + * @return The created Workbook + * + * @throws IOException if an error occurs while reading the data */ public static Workbook create(OPCPackage pkg) throws IOException { return new XSSFWorkbook(pkg); } - + /** * Creates the appropriate HSSFWorkbook / XSSFWorkbook from * the given InputStream. + * *

Your input stream MUST either support mark/reset, or - * be wrapped as a {@link PushbackInputStream}! Note that - * using an {@link InputStream} has a higher memory footprint - * than using a {@link File}.

- *

Note that in order to properly release resources the + * be wrapped as a {@link PushbackInputStream}! Note that + * using an {@link InputStream} has a higher memory footprint + * than using a {@link File}.

+ * + *

Note that in order to properly release resources the * Workbook should be closed after use. Note also that loading * from an InputStream requires more memory than loading - * from a File, so prefer {@link #create(File)} where possible. - * @throws EncryptedDocumentException If the workbook given is password protected + * from a File, so prefer {@link #create(File)} where possible. + * + * @param inp The {@link InputStream} to read data from. + * + * @return The created Workbook + * + * @throws IOException if an error occurs while reading the data + * @throws InvalidFormatException if the contents of the file cannot be parsed into a {@link Workbook} + * @throws EncryptedDocumentException If the workbook given is password protected */ public static Workbook create(InputStream inp) throws IOException, InvalidFormatException, EncryptedDocumentException { return create(inp, null); } - + /** * Creates the appropriate HSSFWorkbook / XSSFWorkbook from * the given InputStream, which may be password protected. *

Your input stream MUST either support mark/reset, or - * be wrapped as a {@link PushbackInputStream}! Note that - * using an {@link InputStream} has a higher memory footprint - * than using a {@link File}.

- *

Note that in order to properly release resources the + * be wrapped as a {@link PushbackInputStream}! Note that + * using an {@link InputStream} has a higher memory footprint + * than using a {@link File}.

+ * + *

Note that in order to properly release resources the * Workbook should be closed after use. Note also that loading * from an InputStream requires more memory than loading - * from a File, so prefer {@link #create(File)} where possible. - * @throws EncryptedDocumentException If the wrong password is given for a protected file - * @throws EmptyFileException If an empty stream is given + * from a File, so prefer {@link #create(File)} where possible.

+ * + * @param inp The {@link InputStream} to read data from. + * @param password The password that should be used or null if no password is necessary. + * + * @return The created Workbook + * + * @throws IOException if an error occurs while reading the data + * @throws InvalidFormatException if the contents of the file cannot be parsed into a {@link Workbook} + * @throws EncryptedDocumentException If the wrong password is given for a protected file + * @throws EmptyFileException If an empty stream is given */ public static Workbook create(InputStream inp, String password) throws IOException, InvalidFormatException, EncryptedDocumentException { // If clearly doesn't do mark/reset, wrap up if (! inp.markSupported()) { inp = new PushbackInputStream(inp, 8); } - + // Ensure that there is at least some data there byte[] header8 = IOUtils.peekFirst8Bytes(inp); @@ -176,51 +210,90 @@ public class WorkbookFactory { } throw new IllegalArgumentException("Your InputStream was neither an OLE2 stream, nor an OOXML stream"); } - + /** * Creates the appropriate HSSFWorkbook / XSSFWorkbook from * the given File, which must exist and be readable. - *

Note that in order to properly release resources the + *

Note that in order to properly release resources the * Workbook should be closed after use. - * @throws EncryptedDocumentException If the workbook given is password protected + * + * @param file The file to read data from. + * + * @return The created Workbook + * + * @throws IOException if an error occurs while reading the data + * @throws InvalidFormatException if the contents of the file cannot be parsed into a {@link Workbook} + * @throws EncryptedDocumentException If the workbook given is password protected */ public static Workbook create(File file) throws IOException, InvalidFormatException, EncryptedDocumentException { return create(file, null); } + /** * Creates the appropriate HSSFWorkbook / XSSFWorkbook from * the given File, which must exist and be readable, and * may be password protected - *

Note that in order to properly release resources the + *

Note that in order to properly release resources the * Workbook should be closed after use. - * @throws EncryptedDocumentException If the wrong password is given for a protected file - * @throws EmptyFileException If an empty stream is given + * + * @param file The file to read data from. + * @param password The password that should be used or null if no password is necessary. + * + * @return The created Workbook + * + * @throws IOException if an error occurs while reading the data + * @throws InvalidFormatException if the contents of the file cannot be parsed into a {@link Workbook} + * @throws EncryptedDocumentException If the wrong password is given for a protected file + * @throws EmptyFileException If an empty stream is given */ public static Workbook create(File file, String password) throws IOException, InvalidFormatException, EncryptedDocumentException { + return create(file, password, false); + } + + /** + * Creates the appropriate HSSFWorkbook / XSSFWorkbook from + * the given File, which must exist and be readable, and + * may be password protected + *

Note that in order to properly release resources the + * Workbook should be closed after use. + * + * @param file The file to read data from. + * @param password The password that should be used or null if no password is necessary. + * @param readOnly If the Workbook should be opened in read-only mode to avoid writing back + * changes when the document is closed. + * + * @return The created Workbook + * + * @throws IOException if an error occurs while reading the data + * @throws InvalidFormatException if the contents of the file cannot be parsed into a {@link Workbook} + * @throws EncryptedDocumentException If the wrong password is given for a protected file + * @throws EmptyFileException If an empty stream is given + */ + public static Workbook create(File file, String password, boolean readOnly) throws IOException, InvalidFormatException, EncryptedDocumentException { if (! file.exists()) { throw new FileNotFoundException(file.toString()); } try { - NPOIFSFileSystem fs = new NPOIFSFileSystem(file); + NPOIFSFileSystem fs = new NPOIFSFileSystem(file, readOnly); return create(fs, password); } catch(OfficeXmlFileException e) { // opening as .xls failed => try opening as .xlsx - OPCPackage pkg = OPCPackage.open(file); + OPCPackage pkg = OPCPackage.open(file, readOnly ? PackageAccess.READ : PackageAccess.READ_WRITE); try { return new XSSFWorkbook(pkg); } catch (IOException ioe) { // ensure that file handles are closed (use revert() to not re-write the file) pkg.revert(); //pkg.close(); - + // rethrow exception throw ioe; } catch (IllegalArgumentException ioe) { - // ensure that file handles are closed (use revert() to not re-write the file) + // ensure that file handles are closed (use revert() to not re-write the file) pkg.revert(); //pkg.close(); - + // rethrow exception throw ioe; } diff --git a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java index 27d6d9ec1a..f3fcbb889e 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java +++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java @@ -70,6 +70,22 @@ public final class TestWorkbookFactory extends TestCase { // TODO: this re-writes the sample-file?! wb.close(); } + public void testCreateReadOnly() throws Exception { + Workbook wb; + + // POIFS -> hssf + wb = WorkbookFactory.create(HSSFTestDataSamples.getSampleFile(xls), null, true); + assertNotNull(wb); + assertTrue(wb instanceof HSSFWorkbook); + wb.close(); + + // Package -> xssf + wb = WorkbookFactory.create(HSSFTestDataSamples.getSampleFile(xlsx), null, true); + assertNotNull(wb); + assertTrue(wb instanceof XSSFWorkbook); + wb.close(); + } + /** * Creates the appropriate kind of Workbook, but * checking the mime magic at the start of the -- 2.39.5