From 8ea6b350e804531dce944fcdb2a9b1b95a20561c Mon Sep 17 00:00:00 2001 From: Sergey Vladimirov Date: Wed, 17 Aug 2011 14:53:28 +0000 Subject: [PATCH] fix 51671 - HWPFDocument.write based on NPOIFSFileSystem throws a NullPointerException git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1158754 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + src/java/org/apache/poi/POIDocument.java | 4 +- .../src/org/apache/poi/hwpf/HWPFDocument.java | 145 +++++++++++------- .../org/apache/poi/hwpf/HWPFDocumentCore.java | 19 +-- .../apache/poi/hwpf/usermodel/TestBugs.java | 17 ++ 5 files changed, 124 insertions(+), 62 deletions(-) diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 9e157b9d0..99ab3a10e 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 51671 - HWPFDocument.write based on NPOIFSFileSystem throws a NullPointerException support for tables and hyperlinks in XSLF 51535 - correct signed vs unsigned short reading in NDocumentInputStream 51634 - support SXSSF streaming from templates diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java index f3a187f7f..fad5b47bc 100644 --- a/src/java/org/apache/poi/POIDocument.java +++ b/src/java/org/apache/poi/POIDocument.java @@ -36,6 +36,7 @@ import org.apache.poi.poifs.filesystem.DocumentInputStream; import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -262,7 +263,8 @@ public abstract class POIDocument { /** * Copies an Entry into a target POIFS directory, recursively */ - private void copyNodeRecursively(Entry entry, DirectoryEntry target) + @Internal + protected void copyNodeRecursively(Entry entry, DirectoryEntry target) throws IOException { //System.err.println("copyNodeRecursively called with "+entry.getName()+ // ","+target.getName()); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java index b25ecc1ea..fc979f46d 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java @@ -22,6 +22,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Iterator; import org.apache.poi.hpsf.DocumentSummaryInformation; import org.apache.poi.hpsf.SummaryInformation; @@ -65,6 +66,7 @@ import org.apache.poi.hwpf.usermodel.Range; import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DocumentEntry; +import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.Internal; @@ -81,6 +83,10 @@ public final class HWPFDocument extends HWPFDocumentCore private static final String PROPERTY_PRESERVE_BIN_TABLES = "org.apache.poi.hwpf.preserveBinTables"; private static final String PROPERTY_PRESERVE_TEXT_TABLE = "org.apache.poi.hwpf.preserveTextTable"; + private static final String STREAM_DATA = "Data"; + private static final String STREAM_TABLE_0 = "0Table"; + private static final String STREAM_TABLE_1 = "1Table"; + /** And for making sense of CP lengths in the FIB */ @Deprecated protected CPSplitCalculator _cpSplit; @@ -181,7 +187,7 @@ public final class HWPFDocument extends HWPFDocumentCore */ public HWPFDocument(POIFSFileSystem pfilesystem) throws IOException { - this(pfilesystem.getRoot()); + this(pfilesystem.getRoot()); } /** @@ -213,7 +219,7 @@ public final class HWPFDocument extends HWPFDocumentCore { // Load the main stream and FIB // Also handles HPSF bits - super(directory); + super(directory); // Do the CP Split _cpSplit = new CPSplitCalculator(_fib); @@ -224,20 +230,20 @@ public final class HWPFDocument extends HWPFDocumentCore } // use the fib to determine the name of the table stream. - String name = "0Table"; + String name = STREAM_TABLE_0; if (_fib.isFWhichTblStm()) { - name = "1Table"; + name = STREAM_TABLE_1; } // Grab the table stream. DocumentEntry tableProps; - try { - tableProps = - (DocumentEntry)directory.getEntry(name); - } catch(FileNotFoundException fnfe) { - throw new IllegalStateException("Table Stream '" + name + "' wasn't found - Either the document is corrupt, or is Word95 (or earlier)"); - } + try { + tableProps = + (DocumentEntry)directory.getEntry(name); + } catch(FileNotFoundException fnfe) { + throw new IllegalStateException("Table Stream '" + name + "' wasn't found - Either the document is corrupt, or is Word95 (or earlier)"); + } // read in the table stream. _tableStream = new byte[tableProps.getSize()]; @@ -249,9 +255,9 @@ public final class HWPFDocument extends HWPFDocumentCore try { DocumentEntry dataProps = - (DocumentEntry)directory.getEntry("Data"); + (DocumentEntry)directory.getEntry(STREAM_DATA); _dataStream = new byte[dataProps.getSize()]; - directory.createDocumentInputStream("Data").read(_dataStream); + directory.createDocumentInputStream(STREAM_DATA).read(_dataStream); } catch(java.io.FileNotFoundException e) { @@ -396,7 +402,7 @@ public final class HWPFDocument extends HWPFDocumentCore @Deprecated public CPSplitCalculator getCPSplitCalculator() { - return _cpSplit; + return _cpSplit; } public DocumentProperties getDocProperties() @@ -512,7 +518,7 @@ public final class HWPFDocument extends HWPFDocumentCore * separators and footnote separators. */ public Range getHeaderStoryRange() { - return getRange( SubdocumentType.HEADER ); + return getRange( SubdocumentType.HEADER ); } /** @@ -550,7 +556,7 @@ public final class HWPFDocument extends HWPFDocumentCore * @return PicturesTable object, that is able to extract images from this document */ public PicturesTable getPicturesTable() { - return _pictures; + return _pictures; } @Internal @@ -636,8 +642,8 @@ public final class HWPFDocument extends HWPFDocumentCore { // initialize our streams for writing. HWPFFileSystem docSys = new HWPFFileSystem(); - HWPFOutputStream wordDocumentStream = docSys.getStream("WordDocument"); - HWPFOutputStream tableStream = docSys.getStream("1Table"); + HWPFOutputStream wordDocumentStream = docSys.getStream(STREAM_WORD_DOCUMENT); + HWPFOutputStream tableStream = docSys.getStream(STREAM_TABLE_1); //HWPFOutputStream dataStream = docSys.getStream("Data"); int tableOffset = 0; @@ -910,6 +916,9 @@ public final class HWPFDocument extends HWPFDocumentCore mainBuf = tempBuf; } + // Table1 stream will be used + _fib.setFWhichTblStm( true ); + // write out the FileInformationBlock. //_fib.serialize(mainBuf, 0); _fib.writeTo(mainBuf, tableStream); @@ -934,52 +943,84 @@ public final class HWPFDocument extends HWPFDocumentCore dataBuf = tempBuf; } -// // spit out the Word document. -// POIFSFileSystem pfs = new POIFSFileSystem(); -// -// pfs.createDocument(new ByteArrayInputStream(mainBuf), "WordDocument"); -// pfs.createDocument(new ByteArrayInputStream(tableBuf), "1Table"); -// pfs.createDocument(new ByteArrayInputStream(dataBuf), "Data"); -// writeProperties(pfs); + // create new document preserving order of entries + POIFSFileSystem pfs = new POIFSFileSystem(); + boolean docWritten = false; + boolean dataWritten = false; + boolean tableWritten = false; + boolean propertiesWritten = false; + for ( Iterator iter = directory.getEntries(); iter.hasNext(); ) + { + Entry entry = iter.next(); + if ( entry.getName().equals( STREAM_WORD_DOCUMENT ) ) + { + if ( !docWritten ) + { + pfs.createDocument( new ByteArrayInputStream( mainBuf ), + STREAM_WORD_DOCUMENT ); + docWritten = true; + } + } + else if ( entry.getName().equals( STREAM_TABLE_0 ) + || entry.getName().equals( STREAM_TABLE_1 ) ) + { + if ( !tableWritten ) + { + pfs.createDocument( new ByteArrayInputStream( tableBuf ), + STREAM_TABLE_1 ); + tableWritten = true; + } + } + else if ( entry.getName().equals( + SummaryInformation.DEFAULT_STREAM_NAME ) + || entry.getName().equals( + DocumentSummaryInformation.DEFAULT_STREAM_NAME ) ) + { + if ( !propertiesWritten ) + { + writeProperties( pfs ); + propertiesWritten = true; + } + } + else if ( entry.getName().equals( STREAM_DATA ) ) + { + if ( !dataWritten ) + { + pfs.createDocument( new ByteArrayInputStream( dataBuf ), + STREAM_DATA ); + dataWritten = true; + } + } + else + { + copyNodeRecursively( entry, pfs.getRoot() ); + } + } - POIFSFileSystem pfs = directory.getFileSystem(); - deleteEntrySafe( pfs, "WordDocument" ); - deleteEntrySafe( pfs, "0Table" ); - deleteEntrySafe( pfs, "1Table" ); - deleteEntrySafe( pfs, "Data" ); + if ( !docWritten ) + pfs.createDocument( new ByteArrayInputStream( mainBuf ), + STREAM_WORD_DOCUMENT ); + if ( !tableWritten ) + pfs.createDocument( new ByteArrayInputStream( tableBuf ), + STREAM_TABLE_1 ); + if ( !propertiesWritten ) + writeProperties( pfs ); + if ( !dataWritten ) + pfs.createDocument( new ByteArrayInputStream( dataBuf ), + STREAM_DATA ); - // read properties only if they were not read - getSummaryInformation(); - // update properties in case user changed them - deleteEntrySafe( pfs, SummaryInformation.DEFAULT_STREAM_NAME ); - deleteEntrySafe( pfs, DocumentSummaryInformation.DEFAULT_STREAM_NAME ); - writeProperties( pfs ); - - pfs.createDocument( new ByteArrayInputStream( mainBuf ), "WordDocument" ); - pfs.createDocument( new ByteArrayInputStream( tableBuf ), "1Table" ); - pfs.createDocument( new ByteArrayInputStream( dataBuf ), "Data" ); pfs.writeFilesystem( out ); + this.directory = pfs.getRoot(); /* * since we updated all references in FIB and etc, using new arrays to * access data */ + this.directory = pfs.getRoot(); this._tableStream = tableStream.toByteArray(); this._dataStream = dataBuf; } - private static void deleteEntrySafe( POIFSFileSystem pfs, final String name ) - { - try - { - pfs.getRoot().getEntry( name ).delete(); - } - catch ( FileNotFoundException exc ) - { - // ok - } - } - @Internal public byte[] getDataStream() { @@ -988,7 +1029,7 @@ public final class HWPFDocument extends HWPFDocumentCore @Internal public byte[] getTableStream() { - return _tableStream; + return _tableStream; } public int registerList(HWPFList list) diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java index fc2bb15e6..5301084a8 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java @@ -22,12 +22,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; -import org.apache.poi.hwpf.usermodel.ObjectsPool; - -import org.apache.poi.poifs.filesystem.DirectoryEntry; - -import org.apache.poi.hwpf.usermodel.ObjectPoolImpl; - import org.apache.poi.EncryptedDocumentException; import org.apache.poi.POIDocument; import org.apache.poi.hwpf.model.CHPBinTable; @@ -38,7 +32,10 @@ import org.apache.poi.hwpf.model.PAPBinTable; import org.apache.poi.hwpf.model.SectionTable; import org.apache.poi.hwpf.model.StyleSheet; import org.apache.poi.hwpf.model.TextPieceTable; +import org.apache.poi.hwpf.usermodel.ObjectPoolImpl; +import org.apache.poi.hwpf.usermodel.ObjectsPool; import org.apache.poi.hwpf.usermodel.Range; +import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -53,6 +50,9 @@ import org.apache.poi.util.Internal; */ public abstract class HWPFDocumentCore extends POIDocument { + protected static final String STREAM_OBJECT_POOL = "ObjectPool"; + protected static final String STREAM_WORD_DOCUMENT = "WordDocument"; + /** Holds OLE2 objects */ protected ObjectPoolImpl _objectPool; @@ -151,7 +151,7 @@ public abstract class HWPFDocumentCore extends POIDocument directory.getEntry("WordDocument"); _mainStream = new byte[documentProps.getSize()]; - directory.createDocumentInputStream("WordDocument").read(_mainStream); + directory.createDocumentInputStream(STREAM_WORD_DOCUMENT).read(_mainStream); // Create our FIB, and check for the doc being encrypted _fib = new FileInformationBlock(_mainStream); @@ -164,11 +164,12 @@ public abstract class HWPFDocumentCore extends POIDocument try { objectPoolEntry = (DirectoryEntry) directory - .getEntry( "ObjectPool" ); + .getEntry( STREAM_OBJECT_POOL ); } catch ( FileNotFoundException exc ) { - objectPoolEntry = directory.createDirectory( "ObjectPool" ); + objectPoolEntry = directory + .createDirectory( STREAM_OBJECT_POOL ); } _objectPool = new ObjectPoolImpl( objectPoolEntry ); } diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBugs.java b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBugs.java index a657a903a..85d49387f 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBugs.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestBugs.java @@ -16,6 +16,7 @@ ==================================================================== */ package org.apache.poi.hwpf.usermodel; +import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -24,6 +25,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; + import junit.framework.TestCase; import org.apache.commons.codec.digest.DigestUtils; @@ -630,4 +633,18 @@ public class TestBugs extends TestCase assertEquals( Arrays.toString( oldData ), Arrays.toString( newData ) ); } + + /** + * [RESOLVED FIXED] Bug 51671 - HWPFDocument.write based on NPOIFSFileSystem + * throws a NullPointerException + */ + public void test51671() throws Exception + { + InputStream is = POIDataSamples.getDocumentInstance() + .openResourceAsStream( "empty.doc" ); + NPOIFSFileSystem npoifsFileSystem = new NPOIFSFileSystem( is ); + HWPFDocument hwpfDocument = new HWPFDocument( + npoifsFileSystem.getRoot() ); + hwpfDocument.write( new ByteArrayOutputStream() ); + } }