]> source.dussan.org Git - poi.git/commitdiff
Allowed for quad-byte padding alignment on ObjRecord
authorJosh Micich <josh@apache.org>
Wed, 29 Oct 2008 20:13:58 +0000 (20:13 +0000)
committerJosh Micich <josh@apache.org>
Wed, 29 Oct 2008 20:13:58 +0000 (20:13 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@708996 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/record/ObjRecord.java
src/testcases/org/apache/poi/hssf/record/TestObjRecord.java

index c50f65b61ec8bd9e32b0565bfe5e5050879bb622..538b1ae9f192009685cbd919faac18bff41c2780 100644 (file)
 package org.apache.poi.hssf.record;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 import org.apache.poi.util.LittleEndianInputStream;
-import org.apache.poi.util.LittleEndianOutput;
-import org.apache.poi.util.LittleEndianOutputStream;
 
 /**
  * OBJRECORD (0x005D)<p/>
@@ -37,10 +35,17 @@ import org.apache.poi.util.LittleEndianOutputStream;
  */
 public final class ObjRecord extends Record {
        public final static short sid = 0x005D;
+
+       private static final int NORMAL_PAD_ALIGNMENT = 2;
+       private static int MAX_PAD_ALIGNMENT = 4;
        
        private List subrecords;
        /** used when POI has no idea what is going on */
        private byte[] _uninterpretedData;
+       /**
+        * Excel seems to tolerate padding to quad or double byte length
+        */
+       private boolean _isPaddedToQuadByteMultiple;
 
        //00000000 15 00 12 00 01 00 01 00 11 60 00 00 00 00 00 0D .........`......
        //00000010 26 01 00 00 00 00 00 00 00 00                   &.........
@@ -71,6 +76,10 @@ public final class ObjRecord extends Record {
                        _uninterpretedData = subRecordData;
                        return;
                }
+               if (subRecordData.length % 2 != 0) {
+                       String msg = "Unexpected length of subRecordData : " + HexDump.toHex(subRecordData);
+                       throw new RecordFormatException(msg);
+               }
 
 //             System.out.println(HexDump.toHex(subRecordData));
 
@@ -84,12 +93,17 @@ public final class ObjRecord extends Record {
                                break;
                        }
                }
-               if (bais.available() > 0) {
-                       // earlier versions of the code had allowances for padding
-                       // At present (Oct-2008), no unit test samples exhibit such padding
-                       String msg = "Leftover " + bais.available() 
+               int nRemainingBytes = bais.available();
+               if (nRemainingBytes > 0) {
+                       // At present (Oct-2008), most unit test samples have (subRecordData.length % 2 == 0)
+                       _isPaddedToQuadByteMultiple = subRecordData.length % MAX_PAD_ALIGNMENT == 0;
+                       if (nRemainingBytes >= (_isPaddedToQuadByteMultiple ? MAX_PAD_ALIGNMENT : NORMAL_PAD_ALIGNMENT)) {
+                               String msg = "Leftover " + nRemainingBytes 
                                + " bytes in subrecord data " + HexDump.toHex(subRecordData);
-                       throw new RecordFormatException(msg);
+                               throw new RecordFormatException(msg);
+                       }
+               } else {
+                       _isPaddedToQuadByteMultiple = false;
                }
        }
 
@@ -114,34 +128,41 @@ public final class ObjRecord extends Record {
                        SubRecord record = (SubRecord) subrecords.get(i);
                        size += record.getDataSize()+4;
                }
+               if (_isPaddedToQuadByteMultiple) {
+                       while (size % MAX_PAD_ALIGNMENT != 0) {
+                               size++;
+                       }
+               } else {
+                       while (size % NORMAL_PAD_ALIGNMENT != 0) {
+                               size++;
+                       }
+               }
                return size;
        }
 
        public int serialize(int offset, byte[] data) {
                int dataSize = getDataSize();
+               int recSize = 4 + dataSize;
+               LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
 
-               LittleEndian.putUShort(data, 0 + offset, sid);
-               LittleEndian.putUShort(data, 2 + offset, dataSize);
+               out.writeShort(sid);
+               out.writeShort(dataSize);
 
-               byte[] subRecordBytes;
                if (_uninterpretedData == null) {
-                       ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize);
-                       LittleEndianOutput leo = new LittleEndianOutputStream(baos);
 
                        for (int i = 0; i < subrecords.size(); i++) {
                                SubRecord record = (SubRecord) subrecords.get(i);
-                               record.serialize(leo);
+                               record.serialize(out);
                        }
+                       int expectedEndIx = offset+dataSize;
                        // padding
-                       while (baos.size() < dataSize) {
-                               baos.write(0);
+                       while (out.getWriteIndex() < expectedEndIx) {
+                               out.writeByte(0);
                        }
-                       subRecordBytes = baos.toByteArray();
                } else {
-                       subRecordBytes = _uninterpretedData;
+                       out.write(_uninterpretedData);
                }
-               System.arraycopy(subRecordBytes, 0, data, offset + 4, dataSize);
-               return 4 + dataSize;
+               return recSize;
        }
 
        public int getRecordSize() {
index c143120de8499bc7d04822fc8d83a1e09eac7c31..4e928e8782303dd0ba56930d1aed7960a45033b5 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.poi.hssf.record;
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.poi.util.HexRead;
+
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
@@ -38,17 +40,17 @@ public final class TestObjRecord extends TestCase {
      *     [ftCmo]
      *     [ftEnd]
      */
-    private static final byte[] recdata = {
-        0x15, 0x00, 0x12, 0x00, 0x06, 0x00, 0x01, 0x00, 0x11, 0x60,
-        (byte)0xF4, 0x02, 0x41, 0x01, 0x14, 0x10, 0x1F, 0x02, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-        // TODO - this data seems to require two extra bytes padding. not sure where original file is.
-        // it's not bug 38607 attachment 17639
-    };
-
-    private static final byte[] recdataNeedingPadding = {
-        21, 0, 18, 0, 0, 0, 1, 0, 17, 96, 0, 0, 0, 0, 56, 111, -52, 3, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0, 0, 0, 0, 0
-    };
+    private static final byte[] recdata = HexRead.readFromString(""
+            + "15 00 12 00 06 00 01 00 11 60 "
+            + "F4 02 41 01 14 10 1F 02 00 00 "
+            +"00 00 00 00 00 00"
+            // TODO - this data seems to require two extra bytes padding. not sure where original file is.
+            // it's not bug 38607 attachment 17639
+    );
+
+    private static final byte[] recdataNeedingPadding = HexRead.readFromString(""
+            + "15 00 12 00 00 00 01 00 11 60 00 00 00 00 38 6F CC 03 00 00 00 00 06 00 02 00 00 00 00 00 00 00"
+    );
 
     public void testLoad() {
         ObjRecord record = new ObjRecord(TestcaseRecordInputStream.create(ObjRecord.sid, recdata));
@@ -113,4 +115,23 @@ public final class TestObjRecord extends TestCase {
         assertEquals(GroupMarkerSubRecord.class, subrecords.get(1).getClass());
         assertEquals(EndSubRecord.class, subrecords.get(2).getClass());
     }
+    
+    /**
+     * Check that ObjRecord tolerates and preserves padding to a 4-byte boundary
+     * (normally padding is to a 2-byte boundary).
+     */
+    public void test4BytePadding() {
+        // actual data from file saved by Excel 2007
+        byte[] data = HexRead.readFromString(""
+                + "15 00 12 00  1E 00 01 00  11 60 B4 6D  3C 01 C4 06 "
+                + "49 06 00 00  00 00 00 00  00 00 00 00");
+        // this data seems to have 2 extra bytes of padding more than usual
+        // the total may have been padded to the nearest quad-byte length
+        RecordInputStream in = TestcaseRecordInputStream.create(ObjRecord.sid, data);
+        // check read OK
+        ObjRecord record = new ObjRecord(in);
+        // check that it re-serializes to the same data
+        byte[] ser = record.serialize();
+        TestcaseRecordInputStream.confirmRecordEncoding(ObjRecord.sid, data, ser);
+    }
 }