]> source.dussan.org Git - poi.git/commitdiff
Access to Saved By Information - patch from Trejkaz in bug #38647
authorNick Burch <nick@apache.org>
Mon, 14 Aug 2006 10:29:49 +0000 (10:29 +0000)
committerNick Burch <nick@apache.org>
Mon, 14 Aug 2006 10:29:49 +0000 (10:29 +0000)
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@431320 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
src/scratchpad/src/org/apache/poi/hwpf/model/SavedByEntry.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hwpf/data/saved-by-table.doc [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hwpf/model/TestSavedByTable.java [new file with mode: 0644]

index c525c572362d4d4cf55b293f818a285377798380..7657019e7fbd287e1e889e135ad7661556e61d5d 100644 (file)
@@ -86,6 +86,9 @@ public class HWPFDocument extends POIDocument
   /** Hold list tables */
   protected ListTables _lt;
 
+  /** Holds the save history for this document. */
+  protected SavedByTable _sbt;
+
   protected HWPFDocument()
   {
 
@@ -212,6 +215,13 @@ public class HWPFDocument extends POIDocument
       _lt = new ListTables(_tableStream, _fib.getFcPlcfLst(), _fib.getFcPlfLfo());
     }
 
+    int sbtOffset = _fib.getFcSttbSavedBy();
+    int sbtLength = _fib.getLcbSttbSavedBy();
+    if (sbtOffset != 0 && sbtLength != 0)
+    {
+      _sbt = new SavedByTable(_tableStream, sbtOffset, sbtLength);
+    }
+
     PlexOfCps plc = new PlexOfCps(_tableStream, _fib.getFcPlcffldMom(), _fib.getLcbPlcffldMom(), 2);
     for (int x = 0; x < plc.length(); x++)
     {
@@ -267,6 +277,17 @@ public class HWPFDocument extends POIDocument
   {
     return _lt;
   }
+
+  /**
+   * Gets a reference to the saved -by table, which holds the save history for the document.
+   *
+   * @return the saved-by table.
+   */
+  public SavedByTable getSavedByTable()
+  {
+    return _sbt;
+  }
+
   /**
    * Writes out the word file that is represented by an instance of this class.
    *
@@ -347,6 +368,16 @@ public class HWPFDocument extends POIDocument
       tableOffset = tableStream.getOffset();
     }
 
+    // write out the saved-by table.
+    if (_sbt != null)
+    {
+      _fib.setFcSttbSavedBy(tableOffset);
+      _sbt.writeTo(tableStream);
+      _fib.setLcbSttbSavedBy(tableStream.getOffset() - tableOffset);
+
+      tableOffset = tableStream.getOffset();
+    }
+
     // write out the FontTable.
     _fib.setFcSttbfffn(tableOffset);
     _ft.writeTo(docSys);
index 773ff080da41a5d70f7ffa39bf6aaab797a9af28..16bd3a39bad1ac8014d00456516a4dfdbdfbfc11 100644 (file)
@@ -61,6 +61,7 @@ public class FileInformationBlock extends FIBAbstractType
       fieldSet.add(new Integer(FIBFieldHandler.PLFLFO));
       fieldSet.add(new Integer(FIBFieldHandler.PLCFFLDMOM));
       fieldSet.add(new Integer(FIBFieldHandler.STTBFFFN));
+      fieldSet.add(new Integer(FIBFieldHandler.STTBSAVEDBY));
       fieldSet.add(new Integer(FIBFieldHandler.MODIFIED));
 
 
@@ -251,6 +252,26 @@ public class FileInformationBlock extends FIBAbstractType
       _fieldHandler.setFieldSize(FIBFieldHandler.STTBFFFN, lcbSttbFffn);
     }
 
+    public int getFcSttbSavedBy()
+    {
+        return _fieldHandler.getFieldOffset(FIBFieldHandler.STTBSAVEDBY);
+    }
+
+    public int getLcbSttbSavedBy()
+    {
+        return _fieldHandler.getFieldSize(FIBFieldHandler.STTBSAVEDBY);
+    }
+
+    public void setFcSttbSavedBy(int fcSttbSavedBy)
+    {
+      _fieldHandler.setFieldOffset(FIBFieldHandler.STTBSAVEDBY, fcSttbSavedBy);
+    }
+
+    public void setLcbSttbSavedBy(int fcSttbSavedBy)
+    {
+      _fieldHandler.setFieldSize(FIBFieldHandler.STTBSAVEDBY, fcSttbSavedBy);
+    }
+
     public int getModifiedLow()
     {
       return _fieldHandler.getFieldOffset(FIBFieldHandler.PLFLFO);
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByEntry.java b/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByEntry.java
new file mode 100644 (file)
index 0000000..82fbacd
--- /dev/null
@@ -0,0 +1,85 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+\r
+package org.apache.poi.hwpf.model;\r
+\r
+\r
+/**\r
+ * A single entry in the {@link SavedByTable}.\r
+ * \r
+ * @author Daniel Noll\r
+ */\r
+public class SavedByEntry\r
+{\r
+  private String userName;\r
+  private String saveLocation;\r
+\r
+  public SavedByEntry(String userName, String saveLocation)\r
+  {\r
+    this.userName = userName;\r
+    this.saveLocation = saveLocation;\r
+  }\r
+\r
+  public String getUserName()\r
+  {\r
+    return userName;\r
+  }\r
+\r
+  public String getSaveLocation()\r
+  {\r
+    return saveLocation;\r
+  }\r
+\r
+  /**\r
+   * Compares this object with another, for equality.\r
+   *\r
+   * @param other the object to compare to this one.\r
+   * @return <code>true</code> iff the other object is equal to this one.\r
+   */\r
+  public boolean equals(Object other)\r
+  {\r
+    if (other == this) return true;\r
+    if (!(other instanceof SavedByEntry)) return false;\r
+    SavedByEntry that = (SavedByEntry) other;\r
+    return that.userName.equals(userName) &&\r
+           that.saveLocation.equals(saveLocation);\r
+  }\r
+\r
+  /**\r
+   * Generates a hash code for consistency with {@link #equals(Object)}.\r
+   *\r
+   * @return the hash code.\r
+   */\r
+  public int hashCode()\r
+  {\r
+    int hash = 29;\r
+    hash = hash * 13 + userName.hashCode();\r
+    hash = hash * 13 + saveLocation.hashCode();\r
+    return hash;\r
+  }\r
+\r
+  /**\r
+   * Returns a string for display.\r
+   *\r
+   * @return the string.\r
+   */\r
+  public String toString()\r
+  {\r
+    return "SavedByEntry[userName=" + getUserName() +\r
+                       ",saveLocation=" + getSaveLocation() + "]";\r
+  }\r
+}\r
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java
new file mode 100644 (file)
index 0000000..d24a8ec
--- /dev/null
@@ -0,0 +1,121 @@
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+\r
+package org.apache.poi.hwpf.model;\r
+\r
+import java.io.IOException;\r
+import java.util.Arrays;\r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.StringUtil;\r
+\r
+import org.apache.poi.hwpf.model.io.HWPFOutputStream;\r
+\r
+/**\r
+ * String table containing the history of the last few revisions ("saves") of the document.\r
+ * Read-only for the time being.\r
+ * \r
+ * @author Daniel Noll\r
+ */\r
+public class SavedByTable\r
+{\r
+  /**\r
+   * A value that I don't know what it does, but is maintained for accuracy.\r
+   */\r
+  private short unknownValue = -1;\r
+\r
+  /**\r
+   * Array of entries.\r
+   */\r
+  private SavedByEntry[] entries;\r
+\r
+  /**\r
+   * Constructor to read the table from the table stream.\r
+   *\r
+   * @param tableStream the table stream.\r
+   * @param offset the offset into the byte array.\r
+   * @param size the size of the table in the byte array.\r
+   */\r
+  public SavedByTable(byte[] tableStream, int offset, int size)\r
+  {\r
+    // Read the value that I don't know what it does. :-)\r
+    unknownValue = LittleEndian.getShort(tableStream, offset);\r
+    offset += 2;\r
+\r
+    // The stored int is the number of strings, and there are two strings per entry.\r
+    int numEntries = LittleEndian.getInt(tableStream, offset) / 2;\r
+    offset += 4;\r
+\r
+    entries = new SavedByEntry[numEntries];\r
+    for (int i = 0; i < numEntries; i++)\r
+    {\r
+      int len = LittleEndian.getShort(tableStream, offset);\r
+      offset += 2;\r
+      String userName = StringUtil.getFromUnicodeLE(tableStream, offset, len);\r
+      offset += len * 2;\r
+      len = LittleEndian.getShort(tableStream, offset);\r
+      offset += 2;\r
+      String saveLocation = StringUtil.getFromUnicodeLE(tableStream, offset, len);\r
+      offset += len * 2;\r
+\r
+      entries[i] = new SavedByEntry(userName, saveLocation);\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Gets the entries.  The returned list cannot be modified.\r
+   *\r
+   * @return the list of entries.\r
+   */\r
+  public List getEntries()\r
+  {\r
+    return Collections.unmodifiableList(Arrays.asList(entries));\r
+  }\r
+\r
+  /**\r
+   * Writes this table to the table stream.\r
+   *\r
+   * @param tableStream the table stream to write to.\r
+   * @throws IOException if an error occurs while writing.\r
+   */\r
+  public void writeTo(HWPFOutputStream tableStream)\r
+    throws IOException\r
+  {\r
+    byte[] header = new byte[6];\r
+    LittleEndian.putShort(header, 0, unknownValue);\r
+    LittleEndian.putInt(header, 2, entries.length * 2);\r
+    tableStream.write(header);\r
+\r
+    for (int i = 0; i < entries.length; i++)\r
+    {\r
+      writeStringValue(tableStream, entries[i].getUserName());\r
+      writeStringValue(tableStream, entries[i].getSaveLocation());\r
+    }\r
+  }\r
+\r
+  private void writeStringValue(HWPFOutputStream tableStream, String value)\r
+    throws IOException\r
+  {\r
+    byte[] buf = new byte[value.length() * 2 + 2];\r
+    LittleEndian.putShort(buf, 0, (short) value.length());\r
+    StringUtil.putUnicodeLE(value, buf, 2);\r
+    tableStream.write(buf);\r
+  }\r
+}\r
+\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/data/saved-by-table.doc b/src/scratchpad/testcases/org/apache/poi/hwpf/data/saved-by-table.doc
new file mode 100644 (file)
index 0000000..c3ea6bc
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hwpf/data/saved-by-table.doc differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestSavedByTable.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/TestSavedByTable.java
new file mode 100644 (file)
index 0000000..bc0a6c0
--- /dev/null
@@ -0,0 +1,91 @@
+\r
+/* ====================================================================\r
+   Copyright 2002-2004   Apache Software Foundation\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+\r
+package org.apache.poi.hwpf.model;\r
+\r
+import java.io.*;\r
+import java.util.*;\r
+import junit.framework.*;\r
+\r
+import org.apache.poi.hwpf.*;\r
+import org.apache.poi.hwpf.model.*;\r
+import org.apache.poi.util.*;\r
+\r
+/**\r
+ * Unit test for {@link SavedByTable} and {@link SavedByEntry}.\r
+ *\r
+ * @author Daniel Noll\r
+ */\r
+public class TestSavedByTable\r
+  extends TestCase\r
+{\r
+  /** Data dir */\r
+  private File testFile = new File(new File(System.getProperty("HWPF.testdata.path")), "saved-by-table.doc");\r
+\r
+  /** The expected entries in the test document. */\r
+  private List expected = Arrays.asList(new Object[] {\r
+    new SavedByEntry("cic22", "C:\\DOCUME~1\\phamill\\LOCALS~1\\Temp\\AutoRecovery save of Iraq - security.asd"),\r
+    new SavedByEntry("cic22", "C:\\DOCUME~1\\phamill\\LOCALS~1\\Temp\\AutoRecovery save of Iraq - security.asd"),\r
+    new SavedByEntry("cic22", "C:\\DOCUME~1\\phamill\\LOCALS~1\\Temp\\AutoRecovery save of Iraq - security.asd"),\r
+    new SavedByEntry("JPratt", "C:\\TEMP\\Iraq - security.doc"),\r
+    new SavedByEntry("JPratt", "A:\\Iraq - security.doc"),\r
+    new SavedByEntry("ablackshaw", "C:\\ABlackshaw\\Iraq - security.doc"),\r
+    new SavedByEntry("ablackshaw", "C:\\ABlackshaw\\A;Iraq - security.doc"),\r
+    new SavedByEntry("ablackshaw", "A:\\Iraq - security.doc"),\r
+    new SavedByEntry("MKhan", "C:\\TEMP\\Iraq - security.doc"),\r
+    new SavedByEntry("MKhan", "C:\\WINNT\\Profiles\\mkhan\\Desktop\\Iraq.doc")\r
+  });\r
+\r
+  /**\r
+   * Tests reading in the entries, comparing them against the expected entries.\r
+   * Then tests writing the document out and reading the entries yet again.\r
+   *\r
+   * @throws Exception if an unexpected error occurs.\r
+   */\r
+  public void testReadWrite()\r
+    throws Exception\r
+  {\r
+    // This document is widely available on the internet as "blair.doc".\r
+    // I tried stripping the content and saving the document but my version\r
+    // of Word (from Office XP) strips this table out.\r
+    InputStream stream = new BufferedInputStream(new FileInputStream(testFile));\r
+    HWPFDocument doc;\r
+    try\r
+    {\r
+      doc = new HWPFDocument(stream);\r
+    }\r
+    finally\r
+    {\r
+      stream.close();\r
+    }\r
+\r
+    // Check what we just read.\r
+    assertEquals("List of saved-by entries was not as expected",\r
+                 expected, doc.getSavedByTable().getEntries());\r
+\r
+    // Now write the entire document out, and read it back in...\r
+    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();\r
+    doc.write(byteStream);\r
+    InputStream copyStream = new ByteArrayInputStream(byteStream.toByteArray());\r
+    HWPFDocument copy = new HWPFDocument(copyStream);\r
+\r
+    // And check again.\r
+    assertEquals("List of saved-by entries was incorrect after writing",\r
+                 expected, copy.getSavedByTable().getEntries());\r
+  }\r
+}\r