]> source.dussan.org Git - poi.git/commitdiff
#57919 Provide an initial in-place write method for HSSFWorkbook
authorNick Burch <nick@apache.org>
Sun, 17 Jul 2016 19:02:58 +0000 (19:02 +0000)
committerNick Burch <nick@apache.org>
Sun, 17 Jul 2016 19:02:58 +0000 (19:02 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1753103 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/poifs/filesystem/NPOIFSFileSystem.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java

index c0f7228fb8f070f87a4bb889f4104371ee47ac99..63640be85f7ca03e84ac88f05d9e5b7de629601e 100644 (file)
@@ -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. 
+     * <p>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
index cb88f2d55635befd8e31dff975320f6cb22b0779..e1d4c5e5b54e1f558d54041c1a870b373b9f47f2 100644 (file)
@@ -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 
index 8ddd002f726561cae06f25b4c3fedc6300146bcb..c757ffbf50f17370ad73bf164f0ec69b8bdf44a1 100644 (file)
@@ -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());
+    }
 }