diff --git a/src/java/org/apache/poi/poifs/filesystem/NDocumentOutputStream.java b/src/java/org/apache/poi/poifs/filesystem/NDocumentOutputStream.java new file mode 100644 index 000000000..021500947 --- /dev/null +++ b/src/java/org/apache/poi/poifs/filesystem/NDocumentOutputStream.java @@ -0,0 +1,88 @@ +/* ==================================================================== + 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.poifs.filesystem; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * This class provides methods to write a DocumentEntry managed by a + * {@link NPOIFSFileSystem} instance. + */ +public final class NDocumentOutputStream extends OutputStream { + /** the Document's size */ + private int _document_size; + + /** have we been closed? */ + private boolean _closed; + + /** the actual Document */ + private NPOIFSDocument _document; + + /** + * Create an OutputStream from the specified DocumentEntry. + * The specified entry will be emptied. + * + * @param document the DocumentEntry to be written + */ + public NDocumentOutputStream(DocumentEntry document) throws IOException { + if (!(document instanceof DocumentNode)) { + throw new IOException("Cannot open internal document storage, " + document + " not a Document Node"); + } + _document_size = 0; + _closed = false; + + _document = new NPOIFSDocument((DocumentNode)document); + _document.free(); + } + + /** + * Create an OutputStream to create the specified new Entry + * + * @param parent Where to create the Entry + * @param name Name of the new entry + */ + public NDocumentOutputStream(DirectoryEntry parent, String name) throws IOException { + if (!(parent instanceof DirectoryNode)) { + throw new IOException("Cannot open internal directory storage, " + parent + " not a Directory Node"); + } + _document_size = 0; + _closed = false; + + // Have an empty one created for now + DocumentEntry doc = parent.createDocument(name, new ByteArrayInputStream(new byte[0])); + _document = new NPOIFSDocument((DocumentNode)doc); + } + + public void write(int b) throws IOException { + // TODO + } + + public void write(byte[] b) throws IOException { + // TODO + } + + public void write(byte[] b, int off, int len) throws IOException { + // TODO + } + + public void close() throws IOException { + // TODO + } +} diff --git a/src/java/org/apache/poi/poifs/filesystem/NPOIFSDocument.java b/src/java/org/apache/poi/poifs/filesystem/NPOIFSDocument.java index 0da6de87f..affaa1612 100644 --- a/src/java/org/apache/poi/poifs/filesystem/NPOIFSDocument.java +++ b/src/java/org/apache/poi/poifs/filesystem/NPOIFSDocument.java @@ -176,7 +176,9 @@ public final class NPOIFSDocument implements POIFSViewable { public void replaceContents(InputStream stream) throws IOException { free(); - store(stream); + int size = store(stream); + _property.setStartBlock(_stream.getStartBlock()); + _property.updateSize(size); } /** diff --git a/src/java/org/apache/poi/poifs/property/DocumentProperty.java b/src/java/org/apache/poi/poifs/property/DocumentProperty.java index 20ff23264..67c8b0307 100644 --- a/src/java/org/apache/poi/poifs/property/DocumentProperty.java +++ b/src/java/org/apache/poi/poifs/property/DocumentProperty.java @@ -120,6 +120,14 @@ public class DocumentProperty // do nothing } + + /** + * Update the size of the property's data + */ + public void updateSize(int size) + { + setSize(size); + } /* ********** END extension of Property ********** */ } // end public class DocumentProperty diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java index c7fafe30d..8e228f687 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestWrite.java @@ -17,7 +17,16 @@ package org.apache.poi.hpsf.basic; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Date; import java.util.HashMap; @@ -26,14 +35,37 @@ import java.util.Map; import junit.framework.TestCase; import org.apache.poi.POIDataSamples; -import org.apache.poi.hpsf.*; +import org.apache.poi.hpsf.ClassID; +import org.apache.poi.hpsf.DocumentSummaryInformation; +import org.apache.poi.hpsf.HPSFException; +import org.apache.poi.hpsf.IllegalPropertySetDataException; +import org.apache.poi.hpsf.MutableProperty; +import org.apache.poi.hpsf.MutablePropertySet; +import org.apache.poi.hpsf.MutableSection; +import org.apache.poi.hpsf.NoFormatIDException; +import org.apache.poi.hpsf.NoPropertySetStreamException; +import org.apache.poi.hpsf.PropertySet; +import org.apache.poi.hpsf.PropertySetFactory; +import org.apache.poi.hpsf.ReadingNotSupportedException; +import org.apache.poi.hpsf.Section; +import org.apache.poi.hpsf.SummaryInformation; +import org.apache.poi.hpsf.UnsupportedVariantTypeException; +import org.apache.poi.hpsf.Variant; +import org.apache.poi.hpsf.VariantSupport; +import org.apache.poi.hpsf.WritingNotSupportedException; import org.apache.poi.hpsf.wellknown.PropertyIDMap; import org.apache.poi.hpsf.wellknown.SectionIDMap; import org.apache.poi.poifs.eventfilesystem.POIFSReader; import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent; import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener; +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.DocumentNode; +import org.apache.poi.poifs.filesystem.NDocumentInputStream; +import org.apache.poi.poifs.filesystem.NDocumentOutputStream; +import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.CodePageUtil; +import org.apache.poi.util.IOUtils; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.TempFile; import org.junit.Assert; @@ -764,6 +796,106 @@ public class TestWrite extends TestCase assertEquals(ps1, ps2); } + /** + * Tests that when using NPOIFS, we can do an in-place write + * without needing to stream in + out the whole kitchen sink + * TODO Finish implementing the logic for this + */ + public void DISBALEDtestInPlaceNPOIFSWrite() throws Exception { + NPOIFSFileSystem fs = null; + DirectoryEntry root = null; + DocumentNode sinfDoc = null; + DocumentNode dinfDoc = null; + SummaryInformation sinf = null; + DocumentSummaryInformation dinf = null; + + final File copy = TempFile.createTempFile("Test-HPSF", "ole2"); + copy.deleteOnExit(); + + // Copy a test file over to a temp location + InputStream inp = _samples.openResourceAsStream("TestShiftJIS.doc"); + FileOutputStream out = new FileOutputStream(copy); + IOUtils.copy(inp, out); + inp.close(); + out.close(); + + // Open the copy in read/write mode + fs = new NPOIFSFileSystem(copy); + root = fs.getRoot(); + + // Read the properties in there + sinfDoc = (DocumentNode)root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME); + sinf = (SummaryInformation)PropertySetFactory.create(new NDocumentInputStream(sinfDoc)); + assertEquals(131077, sinf.getOSVersion()); + + dinfDoc = (DocumentNode)root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME); + dinf = (DocumentSummaryInformation)PropertySetFactory.create(new NDocumentInputStream(dinfDoc)); + assertEquals(131077, dinf.getOSVersion()); + + // Check they start as we expect + assertEquals("Reiichiro Hori", sinf.getAuthor()); + assertEquals("Microsoft Word 9.0", sinf.getApplicationName()); + assertEquals("\u7b2c1\u7ae0", sinf.getTitle()); + + assertEquals("", dinf.getCompany()); + assertEquals(null, dinf.getManager()); + + // Alter a few of them + sinf.setAuthor("Changed Author"); + sinf.setTitle("Le titre \u00e9tait chang\u00e9"); + dinf.setManager("Changed Manager"); + + + // Save this into the filesystem + sinf.write(new NDocumentOutputStream(sinfDoc)); + dinf.write(new NDocumentOutputStream(dinfDoc)); + + + // Read as-is + sinfDoc = (DocumentNode)root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME); + sinf = (SummaryInformation)PropertySetFactory.create(new NDocumentInputStream(sinfDoc)); + assertEquals(131077, sinf.getOSVersion()); + + dinfDoc = (DocumentNode)root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME); + dinf = (DocumentSummaryInformation)PropertySetFactory.create(new NDocumentInputStream(dinfDoc)); + assertEquals(131077, dinf.getOSVersion()); + + assertEquals("Changed Author", sinf.getAuthor()); + assertEquals("Microsoft Word 9.0", sinf.getApplicationName()); + assertEquals("Le titre \u00e9tait chang\u00e9", sinf.getTitle()); + + assertEquals("", dinf.getCompany()); + assertEquals("Changed Manager", dinf.getManager()); + + + // Close, re-load + fs.writeFilesystem(); + fs.close(); + + fs = new NPOIFSFileSystem(copy); + root = fs.getRoot(); + + // Re-check on load + sinfDoc = (DocumentNode)root.getEntry(SummaryInformation.DEFAULT_STREAM_NAME); + sinf = (SummaryInformation)PropertySetFactory.create(new NDocumentInputStream(sinfDoc)); + assertEquals(131077, sinf.getOSVersion()); + + dinfDoc = (DocumentNode)root.getEntry(DocumentSummaryInformation.DEFAULT_STREAM_NAME); + dinf = (DocumentSummaryInformation)PropertySetFactory.create(new NDocumentInputStream(dinfDoc)); + assertEquals(131077, dinf.getOSVersion()); + + assertEquals("Changed Author", sinf.getAuthor()); + assertEquals("Microsoft Word 9.0", sinf.getApplicationName()); + assertEquals("Le titre \u00e9tait chang\u00e9", sinf.getTitle()); + + assertEquals("", dinf.getCompany()); + assertEquals("Changed Manager", dinf.getManager()); + + + // Tidy up + fs.close(); + copy.delete(); + } /** diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java b/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java index 346787737..ea3df4cf2 100644 --- a/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java +++ b/src/testcases/org/apache/poi/poifs/filesystem/TestNPOIFSFileSystem.java @@ -862,6 +862,43 @@ public final class TestNPOIFSFileSystem { assertContentsMatches(mini3, (DocumentEntry)testDir.getEntry("Mini3")); assertContentsMatches(main4096, (DocumentEntry)testDir.getEntry("Normal4096")); + + // Change some existing streams + NPOIFSDocument mini2Doc = new NPOIFSDocument((DocumentNode)testDir.getEntry("Mini2")); + mini2Doc.replaceContents(new ByteArrayInputStream(mini)); + + byte[] main4106 = new byte[4106]; + main4106[0] = 41; + main4106[4105] = 42; + NPOIFSDocument mainDoc = new NPOIFSDocument((DocumentNode)testDir.getEntry("Normal4096")); + mainDoc.replaceContents(new ByteArrayInputStream(main4106)); + + + // Re-check + fs = writeOutAndReadBack(fs); + + root = fs.getRoot(); + testDir = (DirectoryEntry)root.getEntry("Testing 123"); + + assertEquals(5, root.getEntryCount()); + assertThat(root.getEntryNames(), hasItem("Thumbnail")); + assertThat(root.getEntryNames(), hasItem("Image")); + assertThat(root.getEntryNames(), hasItem("Testing 123")); + assertThat(root.getEntryNames(), hasItem("\u0005DocumentSummaryInformation")); + assertThat(root.getEntryNames(), hasItem("\u0005SummaryInformation")); + + assertEquals(5, testDir.getEntryCount()); + assertThat(testDir.getEntryNames(), hasItem("Mini2")); + assertThat(testDir.getEntryNames(), hasItem("Mini3")); + assertThat(testDir.getEntryNames(), hasItem("Normal4096")); + assertThat(testDir.getEntryNames(), hasItem("Testing 789")); + assertThat(testDir.getEntryNames(), hasItem("Testing ABC")); + + assertContentsMatches(mini, (DocumentEntry)testDir.getEntry("Mini2")); + assertContentsMatches(mini3, (DocumentEntry)testDir.getEntry("Mini3")); + assertContentsMatches(main4106, (DocumentEntry)testDir.getEntry("Normal4096")); + + // All done fs.close(); }