From cf6d3489e0d8f8ce66f3f924334500ba6b56a7b3 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sun, 17 Jul 2016 19:02:58 +0000 Subject: [PATCH] #57919 Provide an initial in-place write method for HSSFWorkbook git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1753103 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/usermodel/HSSFWorkbook.java | 37 +++++++++- .../poifs/filesystem/NPOIFSFileSystem.java | 15 +++++ .../poi/hssf/usermodel/TestHSSFWorkbook.java | 67 +++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index c0f7228fb8..63640be85f 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -22,6 +22,7 @@ import static org.apache.poi.hssf.model.InternalWorkbook.WORKBOOK_DIR_ENTRY_NAME import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -33,8 +34,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -77,8 +78,10 @@ import org.apache.poi.hssf.util.CellReference; import org.apache.poi.poifs.crypt.Decryptor; import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.DocumentNode; import org.apache.poi.poifs.filesystem.EntryUtils; import org.apache.poi.poifs.filesystem.FilteringDirectoryNode; +import org.apache.poi.poifs.filesystem.NPOIFSDocument; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.Ole10Native; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -1288,6 +1291,38 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss super.close(); } + //@Override // TODO Not yet on POIDocument + /** + * Write out this workbook to the currently open {@link File} via the + * writeable {@link POIFSFileSystem} it was opened as. + *

This will fail (with an {@link IllegalStateException} if the + * Workbook 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)} to write to a brand new stream. + */ + public void write() throws IOException { + // TODO Push much of this logic down to POIDocument, as will be common for most formats + if (directory == null) { + throw new IllegalStateException("Newly created Workbook, cannot save in-place"); + } + if (directory.getParent() != null) { + throw new IllegalStateException("This is not the root document, cannot save in-place"); + } + if (directory.getFileSystem() == null || + !directory.getFileSystem().isInPlaceWriteable()) { + throw new IllegalStateException("Opened read-only or via an InputStream, a Writeable File"); + } + + // Update the Workbook stream in the file + DocumentNode workbookNode = (DocumentNode)directory.getEntry( + getWorkbookDirEntryName(directory)); + NPOIFSDocument workbookDoc = new NPOIFSDocument(workbookNode); + workbookDoc.replaceContents(new ByteArrayInputStream(getBytes())); + + // Sync with the File on disk + directory.getFileSystem().writeFilesystem(); + } + /** * Method write - write out this workbook to an {@link OutputStream}. Constructs * a new POI POIFSFileSystem, passes in the workbook binary representation and diff --git a/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java b/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java index cb88f2d556..e1d4c5e5b5 100644 --- a/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java +++ b/src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java @@ -725,6 +725,21 @@ public class NPOIFSFileSystem extends BlockStore return getRoot().createDirectory(name); } + /** + * Does the filesystem support an in-place write via + * {@link #writeFilesystem()} ? If false, only writing out to + * a brand new file via {@link #writeFilesystem(OutputStream)} + * is supported. + */ + public boolean isInPlaceWriteable() { + if(_data instanceof FileBackedDataSource) { + if ( ((FileBackedDataSource)_data).isWriteable() ) { + return true; + } + } + return false; + } + /** * Write the filesystem out to the open file. Will thrown an * {@link IllegalArgumentException} if opened from an diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java index 8ddd002f72..c757ffbf50 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java @@ -54,6 +54,7 @@ import org.apache.poi.hssf.record.WindowOneRecord; import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; +import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.formula.ptg.Area3DPtg; import org.apache.poi.ss.usermodel.BaseTestWorkbook; @@ -66,6 +67,7 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.SheetConditionalFormatting; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.util.IOUtils; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.RecordFormatException; import org.apache.poi.util.TempFile; @@ -1210,4 +1212,69 @@ public final class TestHSSFWorkbook extends BaseTestWorkbook { throw new Exception("Moving a sheet to the end should not throw an exception, but threw ", e); } } + + @Test + public void invalidInPlaceWrite() throws Exception { + HSSFWorkbook wb; + + // Can't work for new files + wb = new HSSFWorkbook(); + try { + wb.write(); + fail("Shouldn't work for new files"); + } catch (IllegalStateException e) {} + + // Can't work for InputStream opened files + wb = new HSSFWorkbook( + POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SampleSS.xls")); + try { + wb.write(); + fail("Shouldn't work for InputStream"); + } catch (IllegalStateException e) {} + + // Can't work for OPOIFS + OPOIFSFileSystem ofs = new OPOIFSFileSystem( + POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SampleSS.xls")); + wb = new HSSFWorkbook(ofs.getRoot(), true); + try { + wb.write(); + fail("Shouldn't work for OPOIFSFileSystem"); + } catch (IllegalStateException e) {} + + // Can't work for Read-Only files + NPOIFSFileSystem fs = new NPOIFSFileSystem( + POIDataSamples.getSpreadSheetInstance().getFile("SampleSS.xls"), true); + wb = new HSSFWorkbook(fs); + try { + wb.write(); + fail("Shouldn't work for Read Only"); + } catch (IllegalStateException e) {} + } + + @Test + public void inPlaceWrite() throws Exception { + // Setup as a copy of a known-good file + final File file = TempFile.createTempFile("TestHSSFWorkbook", ".xls"); + IOUtils.copy( + POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SampleSS.xls"), + new FileOutputStream(file) + ); + + // Open from the temp file in read-write mode + HSSFWorkbook wb = new HSSFWorkbook(new NPOIFSFileSystem(file, false)); + assertEquals(3, wb.getNumberOfSheets()); + + // Change + wb.removeSheetAt(2); + wb.removeSheetAt(1); + wb.getSheetAt(0).getRow(0).getCell(0).setCellValue("Changed!"); + + // Save in-place, close, re-open and check + wb.write(); + wb.close(); + + wb = new HSSFWorkbook(new NPOIFSFileSystem(file)); + assertEquals(1, wb.getNumberOfSheets()); + assertEquals("Changed!", wb.getSheetAt(0).getRow(0).getCell(0).toString()); + } } -- 2.39.5