Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@701569 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-10-03 23:50:22 +00:00
parent 301a0f834c
commit dfc67205b9
6 changed files with 395 additions and 191 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! -->
<release version="3.2-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
<action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.2-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
<action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -16,166 +15,312 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream;
import org.apache.poi.util.*;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/**
* ftPictFmla (0x0009)<br/>
* A sub-record within the OBJ record which stores a reference to an object
* stored in a separate entry within the OLE2 compound file.
*
* @author Daniel Noll
*/
public class EmbeddedObjectRefSubRecord
extends SubRecord
{
public static final short sid = 0x9;
public final class EmbeddedObjectRefSubRecord extends SubRecord {
public static final short sid = 0x0009;
public short field_1_stream_id_offset; // Offset to stream ID from the point after this value.
public short[] field_2_unknown; // Unknown stuff at the front. TODO: Confirm that it's a short[]
// TODO: Consider making a utility class for these. I've discovered the same field ordering
// in FormatRecord and StringRecord, it may be elsewhere too.
public short field_3_unicode_len; // Length of Unicode string.
public boolean field_4_unicode_flag; // Flags whether the string is Unicode.
public String field_5_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8)
public int field_6_stream_id; // ID of the OLE stream containing the actual data.
private static final byte[] EMPTY_BYTE_ARRAY = { };
private int field_5_ole_classname_padding; // developer laziness...
public byte[] remainingBytes;
private int field_1_unknown_int;
/** either an area or a cell ref */
private Ptg field_2_refPtg;
private byte[] field_2_unknownFormulaData;
// TODO: Consider making a utility class for these. I've discovered the same field ordering
// in FormatRecord and StringRecord, it may be elsewhere too.
private boolean field_3_unicode_flag; // Flags whether the string is Unicode.
private String field_4_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8)
/** Formulas often have a single non-zero trailing byte.
* This is in a similar position to he pre-streamId padding
* It is unknown if the value is important (it seems to mirror a value a few bytes earlier)
* */
private Byte field_4_unknownByte;
private Integer field_5_stream_id; // ID of the OLE stream containing the actual data.
private byte[] field_6_unknown;
public EmbeddedObjectRefSubRecord()
{
field_2_unknown = new short[0];
remainingBytes = new byte[0];
field_1_stream_id_offset = 6;
field_5_ole_classname = "";
}
public short getSid()
{
return sid;
}
// currently for testing only - needs review
EmbeddedObjectRefSubRecord() {
field_2_unknownFormulaData = new byte[] { 0x02, 0x6C, 0x6A, 0x16, 0x01, }; // just some sample data. These values vary a lot
field_6_unknown = EMPTY_BYTE_ARRAY;
field_4_ole_classname = null;
}
public EmbeddedObjectRefSubRecord(RecordInputStream in)
{
field_1_stream_id_offset = in.readShort();
field_2_unknown = in.readShortArray();
field_3_unicode_len = in.readShort();
field_4_unicode_flag = ( in.readByte() & 0x01 ) != 0;
public short getSid() {
return sid;
}
if ( field_4_unicode_flag )
{
field_5_ole_classname = in.readUnicodeLEString( field_3_unicode_len );
}
else
{
field_5_ole_classname = in.readCompressedUnicode( field_3_unicode_len );
}
public EmbeddedObjectRefSubRecord(RecordInputStream in) {
// Much guess-work going on here due to lack of any documentation.
// See similar source code in OOO:
// http://lxr.go-oo.org/source/sc/sc/source/filter/excel/xiescher.cxx
// 1223 void XclImpOleObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nRecSize )
// Padded with NUL bytes. The -2 is because field_1_stream_id_offset
// is relative to after the offset field, whereas in.getRecordOffset()
// is relative to the start of this record (minus the header.)
field_5_ole_classname_padding = 0;
while (in.getRecordOffset() - 2 < field_1_stream_id_offset)
{
field_5_ole_classname_padding++;
in.readByte(); // discard
}
int streamIdOffset = in.readShort(); // OOO calls this 'nFmlaLen'
// Fetch the stream ID
field_6_stream_id = in.readInt();
// Store what's left
remainingBytes = in.readRemainder();
}
int dataLenAfterFormula = in.remaining() - streamIdOffset;
int formulaSize = in.readUShort();
field_1_unknown_int = in.readInt();
byte[] formulaRawBytes = readRawData(in, formulaSize);
field_2_refPtg = readRefPtg(formulaRawBytes);
if (field_2_refPtg == null) {
// common case
// field_2_n16 seems to be 5 here
// The formula almost looks like tTbl but the row/column values seem like garbage.
field_2_unknownFormulaData = formulaRawBytes;
} else {
field_2_unknownFormulaData = null;
}
public int serialize(int offset, byte[] data)
{
int pos = offset;
int stringByteCount;
if (in.remaining() >= dataLenAfterFormula + 3) {
int tag = in.readByte();
if (tag != 0x03) {
throw new RecordFormatException("Expected byte 0x03 here");
}
int nChars = in.readUShort();
if (nChars > 0) {
// OOO: the 4th way Xcl stores a unicode string: not even a Grbit byte present if length 0
field_3_unicode_flag = ( in.readByte() & 0x01 ) != 0;
if (field_3_unicode_flag) {
field_4_ole_classname = in.readUnicodeLEString(nChars);
stringByteCount = nChars * 2;
} else {
field_4_ole_classname = in.readCompressedUnicode(nChars);
stringByteCount = nChars;
}
} else {
field_4_ole_classname = "";
stringByteCount = 0;
}
} else {
field_4_ole_classname = null;
stringByteCount = 0;
}
// Pad to next 2-byte boundary
if (((stringByteCount + formulaSize) % 2) != 0) {
int b = in.readByte();
if (field_2_refPtg != null && field_4_ole_classname == null) {
field_4_unknownByte = new Byte((byte)b);
}
}
int nUnexpectedPadding = in.remaining() - dataLenAfterFormula;
LittleEndian.putShort(data, pos, sid); pos += 2;
LittleEndian.putShort(data, pos, (short)(getRecordSize() - 4)); pos += 2;
if (nUnexpectedPadding > 0) {
System.err.println("Discarding " + nUnexpectedPadding + " unexpected padding bytes ");
readRawData(in, nUnexpectedPadding);
}
LittleEndian.putShort(data, pos, field_1_stream_id_offset); pos += 2;
LittleEndian.putShortArray(data, pos, field_2_unknown); pos += field_2_unknown.length * 2 + 2;
LittleEndian.putShort(data, pos, field_3_unicode_len); pos += 2;
data[pos] = field_4_unicode_flag ? (byte) 0x01 : (byte) 0x00; pos++;
// Fetch the stream ID
if (dataLenAfterFormula >= 4) {
field_5_stream_id = new Integer(in.readInt());
} else {
field_5_stream_id = null;
}
if ( field_4_unicode_flag )
{
StringUtil.putUnicodeLE( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length() * 2;
}
else
{
StringUtil.putCompressedUnicode( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length();
}
field_6_unknown = in.readRemainder();
}
// Padded with the same number of NUL bytes as were originally skipped.
// XXX: This is only accurate until we make the classname mutable.
pos += field_5_ole_classname_padding;
LittleEndian.putInt(data, pos, field_6_stream_id); pos += 4;
private static Ptg readRefPtg(byte[] formulaRawBytes) {
byte[] data = new byte[formulaRawBytes.length + 4];
LittleEndian.putUShort(data, 0, -5555);
LittleEndian.putUShort(data, 2, formulaRawBytes.length);
System.arraycopy(formulaRawBytes, 0, data, 4, formulaRawBytes.length);
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
in.nextRecord();
byte ptgSid = in.readByte();
switch(ptgSid) {
case AreaPtg.sid: return new AreaPtg(in);
case Area3DPtg.sid: return new Area3DPtg(in);
case RefPtg.sid: return new RefPtg(in);
case Ref3DPtg.sid: return new Ref3DPtg(in);
}
return null;
}
System.arraycopy(remainingBytes, 0, data, pos, remainingBytes.length);
private static byte[] readRawData(RecordInputStream in, int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative size (" + size + ")");
}
if (size == 0) {
return EMPTY_BYTE_ARRAY;
}
byte[] result = new byte[size];
for(int i=0; i< size; i++) {
result[i] = in.readByte();
}
return result;
}
private int getStreamIDOffset(int formulaSize) {
int result = 2 + 4; // formulaSize + f2unknown_int
result += formulaSize;
int stringLen;
if (field_4_ole_classname == null) {
// don't write 0x03, stringLen, flag, text
stringLen = 0;
} else {
result += 1 + 2 + 1; // 0x03, stringLen, flag
stringLen = field_4_ole_classname.length();
if (field_3_unicode_flag) {
result += stringLen * 2;
} else {
result += stringLen;
}
}
// pad to next 2 byte boundary
if ((result % 2) != 0) {
result ++;
}
return result;
}
private int getDataSize(int idOffset) {
return getRecordSize();
}
int result = 2 + idOffset; // 2 for idOffset short field itself
if (field_5_stream_id != null) {
result += 4;
}
return result + field_6_unknown.length;
}
private int getDataSize() {
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
int idOffset = getStreamIDOffset(formulaSize);
return getDataSize(idOffset);
}
/**
* Size of record (exluding 4 byte header)
*/
public int getRecordSize()
{
// The stream id offset is relative to after the stream ID.
// Add 2 bytes for the stream id offset and 4 bytes for the stream id itself and 4 byts for the record header.
return remainingBytes.length + field_1_stream_id_offset + 2 + 4 + 4;
}
public int serialize(int base, byte[] data) {
/**
* Gets the stream ID containing the actual data. The data itself
* can be found under a top-level directory entry in the OLE2 filesystem
* under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
* this ID converted into hex (in big endian order, funnily enough.)
*
* @return the data stream ID.
*/
public int getStreamId()
{
return field_6_stream_id;
}
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
int idOffset = getStreamIDOffset(formulaSize);
int dataSize = getDataSize(idOffset);
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append("[ftPictFmla]\n");
buffer.append(" .streamIdOffset = ")
.append("0x").append(HexDump.toHex( field_1_stream_id_offset ))
.append(" (").append( field_1_stream_id_offset ).append(" )")
.append(System.getProperty("line.separator"));
buffer.append(" .unknown = ")
.append("0x").append(HexDump.toHex( field_2_unknown ))
.append(" (").append( field_2_unknown.length ).append(" )")
.append(System.getProperty("line.separator"));
buffer.append(" .unicodeLen = ")
.append("0x").append(HexDump.toHex( field_3_unicode_len ))
.append(" (").append( field_3_unicode_len ).append(" )")
.append(System.getProperty("line.separator"));
buffer.append(" .unicodeFlag = ")
.append("0x").append( field_4_unicode_flag ? 0x01 : 0x00 )
.append(" (").append( field_4_unicode_flag ).append(" )")
.append(System.getProperty("line.separator"));
buffer.append(" .oleClassname = ")
.append(field_5_ole_classname)
.append(System.getProperty("line.separator"));
buffer.append(" .streamId = ")
.append("0x").append(HexDump.toHex( field_6_stream_id ))
.append(" (").append( field_6_stream_id ).append(" )")
.append(System.getProperty("line.separator"));
buffer.append("[/ftPictFmla]");
return buffer.toString();
}
LittleEndian.putUShort(data, base + 0, sid);
LittleEndian.putUShort(data, base + 2, dataSize);
LittleEndian.putUShort(data, base + 4, idOffset);
LittleEndian.putUShort(data, base + 6, formulaSize);
LittleEndian.putInt(data, base + 8, field_1_unknown_int);
int pos = base+12;
if (field_2_refPtg == null) {
System.arraycopy(field_2_unknownFormulaData, 0, data, pos, field_2_unknownFormulaData.length);
} else {
field_2_refPtg.writeBytes(data, pos);
}
pos += formulaSize;
int stringLen;
if (field_4_ole_classname == null) {
// don't write 0x03, stringLen, flag, text
stringLen = 0;
} else {
LittleEndian.putByte(data, pos, 0x03);
pos += 1;
stringLen = field_4_ole_classname.length();
LittleEndian.putUShort(data, pos, stringLen);
pos += 2;
LittleEndian.putByte(data, pos, field_3_unicode_flag ? 0x01 : 0x00);
pos += 1;
if (field_3_unicode_flag) {
StringUtil.putUnicodeLE(field_4_ole_classname, data, pos);
pos += stringLen * 2;
} else {
StringUtil.putCompressedUnicode(field_4_ole_classname, data, pos);
pos += stringLen;
}
}
// pad to next 2-byte boundary (requires 0 or 1 bytes)
switch(idOffset - (pos - 6 - base)) { // 6 for 3 shorts: sid, dataSize, idOffset
case 1:
LittleEndian.putByte(data, pos, field_4_unknownByte == null ? 0x00 : field_4_unknownByte.intValue());
pos ++;
case 0:
break;
default:
throw new IllegalStateException("Bad padding calculation (" + idOffset + ", " + (pos-base) + ")");
}
if (field_5_stream_id != null) {
LittleEndian.putInt(data, pos, field_5_stream_id.intValue());
pos += 4;
}
System.arraycopy(field_6_unknown, 0, data, pos, field_6_unknown.length);
return 4 + dataSize;
}
public int getRecordSize() {
return 4 + getDataSize();
}
/**
* Gets the stream ID containing the actual data. The data itself
* can be found under a top-level directory entry in the OLE2 filesystem
* under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
* this ID converted into hex (in big endian order, funnily enough.)
*
* @return the data stream ID. Possibly <code>null</code>
*/
public Integer getStreamId() {
return field_5_stream_id;
}
public String getOLEClassName() {
return field_4_ole_classname;
}
public byte[] getObjectData() {
return field_6_unknown;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[ftPictFmla]\n");
sb.append(" .f2unknown = ").append(HexDump.intToHex(field_1_unknown_int)).append("\n");
if (field_2_refPtg == null) {
sb.append(" .f3unknown = ").append(HexDump.toHex(field_2_unknownFormulaData)).append("\n");
} else {
sb.append(" .formula = ").append(field_2_refPtg.toString()).append("\n");
}
if (field_4_ole_classname != null) {
sb.append(" .unicodeFlag = ").append(field_3_unicode_flag).append("\n");
sb.append(" .oleClassname = ").append(field_4_ole_classname).append("\n");
}
if (field_4_unknownByte != null) {
sb.append(" .f4unknown = ").append(HexDump.byteToHex(field_4_unknownByte.intValue())).append("\n");
}
if (field_5_stream_id != null) {
sb.append(" .streamId = ").append(HexDump.intToHex(field_5_stream_id.intValue())).append("\n");
}
if (field_6_unknown.length > 0) {
sb.append(" .f7unknown = ").append(HexDump.toHex(field_6_unknown)).append("\n");
}
sb.append("[/ftPictFmla]");
return sb.toString();
}
}

View File

@ -32,7 +32,7 @@ import org.apache.poi.util.HexDump;
*
* @author Daniel Noll
*/
public class HSSFObjectData
public final class HSSFObjectData
{
/**
* Underlying object record ultimately containing a reference to the object.
@ -60,8 +60,7 @@ public class HSSFObjectData
* Returns the OLE2 Class Name of the object
*/
public String getOLE2ClassName() {
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
return subRecord.field_5_ole_classname;
return findObjectRecord().getOLEClassName();
}
/**
@ -72,9 +71,9 @@ public class HSSFObjectData
* @throws IOException if there was an error reading the data.
*/
public DirectoryEntry getDirectory() throws IOException {
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId();
int streamId = subRecord.getStreamId().intValue();
String streamName = "MBD" + HexDump.toHex(streamId);
Entry entry = poifs.getRoot().getEntry(streamName);
@ -91,8 +90,7 @@ public class HSSFObjectData
* Entry
*/
public byte[] getObjectData() {
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
return subRecord.remainingBytes;
return findObjectRecord().getObjectData();
}
/**
@ -101,10 +99,11 @@ public class HSSFObjectData
* (Not all do, those that don't have a data portion)
*/
public boolean hasDirectoryEntry() {
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
// Field 6 tells you
return (subRecord.field_6_stream_id != 0);
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
// 'stream id' field tells you
Integer streamId = subRecord.getStreamId();
return streamId != null && streamId.intValue() != 0;
}
/**
@ -117,7 +116,7 @@ public class HSSFObjectData
while (subRecordIter.hasNext()) {
Object subRecord = subRecordIter.next();
if (subRecord instanceof EmbeddedObjectRefSubRecord) {
return (EmbeddedObjectRefSubRecord)subRecord;
return (EmbeddedObjectRefSubRecord)subRecord;
}
}

View File

@ -16,13 +16,13 @@
==================================================================== */
package org.apache.poi.hssf.record;
import junit.framework.TestCase;
import org.apache.poi.util.HexRead;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import junit.framework.TestCase;
import org.apache.poi.util.HexRead;
/**
* Tests the serialization and deserialization of the TestEmbeddedObjectRefSubRecord
* class works correctly. Test data taken directly from a real
@ -30,53 +30,111 @@ import java.util.Arrays;
*
* @author Yegor Kozlov
*/
public class TestEmbeddedObjectRefSubRecord extends TestCase {
public final class TestEmbeddedObjectRefSubRecord extends TestCase {
String data1 = "[20, 00, 05, 00, FC, 10, 76, 01, 02, 24, 14, DF, 00, 03, 10, 00, 00, 46, 6F, 72, 6D, 73, 2E, 43, 68, 65, 63, 6B, 42, 6F, 78, 2E, 31, 00, 00, 00, 00, 00, 70, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ]";
String data1 = "[20, 00, 05, 00, FC, 10, 76, 01, 02, 24, 14, DF, 00, 03, 10, 00, 00, 46, 6F, 72, 6D, 73, 2E, 43, 68, 65, 63, 6B, 42, 6F, 78, 2E, 31, 00, 00, 00, 00, 00, 70, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ]";
public void testStore() throws IOException {
public void testStore() {
byte[] src = HexRead.readFromString(data1);
src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);
byte[] src = hr(data1);
src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src));
in.nextRecord();
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src));
in.nextRecord();
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in);
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in);
byte[] ser = record1.serialize();
byte[] ser = record1.serialize();
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
in2.nextRecord();
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
in2.nextRecord();
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
assertTrue(Arrays.equals(src, ser));
assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset);
assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown));
assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len);
assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag);
assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname);
assertEquals(record1.field_6_stream_id, record2.field_6_stream_id);
assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes));
}
assertTrue(Arrays.equals(src, ser));
assertEquals(record1.getOLEClassName(), record2.getOLEClassName());
public void testCreate() throws IOException {
byte[] ser2 = record1.serialize();
assertTrue(Arrays.equals(ser, ser2));
}
public void testCreate() {
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();
byte[] ser = record1.serialize();
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
in2.nextRecord();
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
assertEquals(record1.getOLEClassName(), record2.getOLEClassName());
assertEquals(record1.getStreamId(), record2.getStreamId());
byte[] ser2 = record1.serialize();
assertTrue(Arrays.equals(ser, ser2));
}
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();
/**
* taken from ftPictFmla sub-record in attachment 22645 (offset 0x40AB).
*/
private static final byte[] data45912 = hr(
"09 00 14 00 " +
"12 00 0B 00 F8 02 88 04 3B 00 " +
"00 00 00 01 00 00 00 01 " +
"00 00");
byte[] ser = record1.serialize();
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
in2.nextRecord();
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
public void testCameraTool_bug45912() {
byte[] data = data45912;
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
in.nextRecord();
assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset);
assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown));
assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len);
assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag);
assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname);
assertEquals(record1.field_6_stream_id, record2.field_6_stream_id);
assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes));
EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in);
byte[] ser2 = rec.serialize();
assertTrue(Arrays.equals(data, ser2));
}
}
private static byte[] hr(String string) {
return HexRead.readFromString(string);
}
/**
* tests various examples of OLE controls
*/
public void testVarious() {
String[] rawData = {
"12 00 0B 00 70 95 0B 05 3B 01 00 36 00 40 00 18 00 19 00 18",
"12 00 0B 00 B0 4D 3E 03 3B 00 00 00 00 01 00 00 80 01 C0 00",
"0C 00 05 00 60 AF 3B 03 24 FD FF FE C0 FE",
"24 00 05 00 40 42 3E 03 02 80 CD B4 04 03 15 00 00 46 6F 72 6D 73 2E 43 6F 6D 6D 61 6E 64 42 75 74 74 6F 6E 2E 31 00 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00",
"22 00 05 00 10 4E 3E 03 02 00 4C CC 04 03 12 00 00 46 6F 72 6D 73 2E 53 70 69 6E 42 75 74 74 6F 6E 2E 31 00 54 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00",
"20 00 05 00 E0 41 3E 03 02 00 FC 0B 05 03 10 00 00 46 6F 72 6D 73 2E 43 6F 6D 62 6F 42 6F 78 2E 31 00 74 00 00 00 4C 00 00 00 00 00 00 00 00 00 00 00",
"24 00 05 00 00 4C AF 03 02 80 E1 93 05 03 14 00 00 46 6F 72 6D 73 2E 4F 70 74 69 6F 6E 42 75 74 74 6F 6E 2E 31 00 C0 00 00 00 70 00 00 00 00 00 00 00 00 00 00 00",
"20 00 05 00 E0 A4 28 04 02 80 EA 93 05 03 10 00 00 46 6F 72 6D 73 2E 43 68 65 63 6B 42 6F 78 2E 31 00 30 01 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",
"1C 00 05 00 30 40 3E 03 02 00 CC B4 04 03 0D 00 00 46 6F 72 6D 73 2E 4C 61 62 65 6C 2E 31 9C 01 00 00 54 00 00 00 00 00 00 00 00 00 00 00",
"1E 00 05 00 B0 A4 28 04 02 00 D0 0A 05 03 0F 00 00 46 6F 72 6D 73 2E 4C 69 73 74 42 6F 78 2E 31 F0 01 00 00 48 00 00 00 00 00 00 00 00 00 00 00",
"24 00 05 00 C0 AF 3B 03 02 80 D1 0A 05 03 14 00 00 46 6F 72 6D 73 2E 54 6F 67 67 6C 65 42 75 74 74 6F 6E 2E 31 00 38 02 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",
"1E 00 05 00 90 AF 3B 03 02 80 D4 0A 05 03 0F 00 00 46 6F 72 6D 73 2E 54 65 78 74 42 6F 78 2E 31 A4 02 00 00 48 00 00 00 00 00 00 00 00 00 00 00",
"24 00 05 00 60 40 3E 03 02 00 D6 0A 05 03 14 00 00 46 6F 72 6D 73 2E 54 6F 67 67 6C 65 42 75 74 74 6F 6E 2E 31 00 EC 02 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",
"20 00 05 00 20 4D 3E 03 02 00 D9 0A 05 03 11 00 00 46 6F 72 6D 73 2E 53 63 72 6F 6C 6C 42 61 72 2E 31 58 03 00 00 20 00 00 00 00 00 00 00 00 00 00 00",
"20 00 05 00 00 AF 28 04 02 80 31 AC 04 03 10 00 00 53 68 65 6C 6C 2E 45 78 70 6C 6F 72 65 72 2E 32 00 78 03 00 00 AC 00 00 00 00 00 00 00 00 00 00 00",
};
for (int i = 0; i < rawData.length; i++) {
confirmRead(hr(rawData[i]), i);
}
}
private static void confirmRead(byte[] data, int i) {
RecordInputStream in = new TestcaseRecordInputStream(EmbeddedObjectRefSubRecord.sid, (short)data.length, data);
EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in);
byte[] ser2 = rec.serialize();
byte[] d2 = (byte[]) data.clone(); // remove sid+len for compare
System.arraycopy(ser2, 4, d2, 0, d2.length);
if (!Arrays.equals(data, d2)) {
fail("re-read NQR for case " + i);
}
}
}

View File

@ -969,7 +969,7 @@ public final class TestBugs extends TestCase {
public void test44840() {
HSSFWorkbook wb = openSample("WithCheckBoxes.xls");
// Take a look at the embeded objects
// Take a look at the embedded objects
List objects = wb.getAllEmbeddedObjects();
assertEquals(1, objects.size());
@ -980,10 +980,10 @@ public final class TestBugs extends TestCase {
EmbeddedObjectRefSubRecord rec = obj.findObjectRecord();
assertNotNull(rec);
assertEquals(32, rec.field_1_stream_id_offset);
assertEquals(0, rec.field_6_stream_id); // WRONG!
assertEquals("Forms.CheckBox.1", rec.field_5_ole_classname);
assertEquals(12, rec.remainingBytes.length);
// assertEquals(32, rec.field_1_stream_id_offset);
assertEquals(0, rec.getStreamId().intValue()); // WRONG!
assertEquals("Forms.CheckBox.1", rec.getOLEClassName());
assertEquals(12, rec.getObjectData().length);
// Doesn't have a directory
assertFalse(obj.hasDirectoryEntry());
@ -995,7 +995,7 @@ public final class TestBugs extends TestCase {
obj.getDirectory();
fail();
} catch(FileNotFoundException e) {
// expectd during successful test
// expected during successful test
} catch (IOException e) {
throw new RuntimeException(e);
}