fixed RecordFormatException when reading LbsDataSubRecord, see bugzilla 47701

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@886113 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2009-12-02 10:56:01 +00:00
parent b20d0aa07d
commit 9c515122aa
8 changed files with 561 additions and 159 deletions

View File

@ -34,6 +34,7 @@
<changes> <changes>
<release version="3.6-beta1" date="2009-??-??"> <release version="3.6-beta1" date="2009-??-??">
<action dev="POI-DEVELOPERS" type="fix">47701 - fixed RecordFormatException when reading list subrecords (LbsDataSubRecord)</action>
<action dev="POI-DEVELOPERS" type="add"> memory usage optimization in XSSF - avoid creating parentless xml beans</action> <action dev="POI-DEVELOPERS" type="add"> memory usage optimization in XSSF - avoid creating parentless xml beans</action>
<action dev="POI-DEVELOPERS" type="fix">47188 - avoid corruption of workbook when adding cell comments </action> <action dev="POI-DEVELOPERS" type="fix">47188 - avoid corruption of workbook when adding cell comments </action>
<action dev="POI-DEVELOPERS" type="fix">48106 - improved work with cell comments in XSSF</action> <action dev="POI-DEVELOPERS" type="fix">48106 - improved work with cell comments in XSSF</action>

View File

@ -46,6 +46,11 @@ public final class EndSubRecord extends SubRecord {
} }
} }
@Override
public boolean isTerminating(){
return true;
}
public String toString() public String toString()
{ {
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();

View File

@ -0,0 +1,340 @@
/* ====================================================================
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.hssf.record;
import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.util.*;
/**
* This structure specifies the properties of a list or drop-down list embedded object in a sheet.
*/
public class LbsDataSubRecord extends SubRecord {
public static final int sid = 0x0013;
/**
* From [MS-XLS].pdf 2.5.147 FtLbsData:
*
* An unsigned integer that indirectly specifies whether
* some of the data in this structure appear in a subsequent Continue record.
* If _cbFContinued is 0x00, all of the fields in this structure except sid and _cbFContinued
* MUST NOT exist. If this entire structure is contained within the same record,
* then _cbFContinued MUST be greater than or equal to the size, in bytes,
* of this structure, not including the four bytes for the ft and _cbFContinued fields
*/
private int _cbFContinued;
/**
* a formula that specifies the range of cell values that are the items in this list.
*/
private int _unknownPreFormulaInt;
private Ptg _linkPtg;
private Byte _unknownPostFormulaByte;
/**
* An unsigned integer that specifies the number of items in the list.
*/
private int _cLines;
/**
* An unsigned integer that specifies the one-based index of the first selected item in this list.
* A value of 0x00 specifies there is no currently selected item.
*/
private int _iSel;
/**
* flags that tell what data follows
*/
private int _flags;
/**
* An ObjId that specifies the edit box associated with this list.
* A value of 0x00 specifies that there is no edit box associated with this list.
*/
private int _idEdit;
/**
* An optional LbsDropData that specifies properties for this dropdown control.
* This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
*/
private LbsDropData _dropData;
/**
* An optional array of strings where each string specifies an item in the list.
* The number of elements in this array, if it exists, MUST be {@link #_cLines}
*/
private String[] _rgLines;
/**
* An optional array of booleans that specifies
* which items in the list are part of a multiple selection
*/
private boolean[] _bsels;
/**
* @param in the stream to read data from
* @param cbFContinued the seconf short in the record header
* @param cmoOt the containing Obj's {@link CommonObjectDataSubRecord#field_1_objectType}
*/
public LbsDataSubRecord(LittleEndianInput in, int cbFContinued, int cmoOt) {
_cbFContinued = cbFContinued;
int encodedTokenLen = in.readUShort();
if (encodedTokenLen > 0) {
int formulaSize = in.readUShort();
_unknownPreFormulaInt = in.readInt();
Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
if (ptgs.length != 1) {
throw new RecordFormatException("Read " + ptgs.length
+ " tokens but expected exactly 1");
}
_linkPtg = ptgs[0];
switch (encodedTokenLen - formulaSize - 6) {
case 1:
_unknownPostFormulaByte = in.readByte();
break;
case 0:
_unknownPostFormulaByte = null;
break;
default:
throw new RecordFormatException("Unexpected leftover bytes");
}
}
_cLines = in.readUShort();
_iSel = in.readUShort();
_flags = in.readUShort();
_idEdit = in.readUShort();
// From [MS-XLS].pdf 2.5.147 FtLbsData:
// This field MUST exist if and only if the containing Obj?s cmo.ot is equal to 0x14.
if(cmoOt == 0x14) {
_dropData = new LbsDropData(in);
}
// From [MS-XLS].pdf 2.5.147 FtLbsData:
// This array MUST exist if and only if the fValidPlex flag (0x2) is set
if((_flags & 0x2) != 0) {
_rgLines = new String[_cLines];
for(int i=0; i < _cLines; i++) {
_rgLines[i] = StringUtil.readUnicodeString(in);
}
}
// bits 5-6 in the _flags specify the type
// of selection behavior this list control is expected to support
// From [MS-XLS].pdf 2.5.147 FtLbsData:
// This array MUST exist if and only if the wListType field is not equal to 0.
if(((_flags >> 4) & 0x2) != 0) {
_bsels = new boolean[_cLines];
for(int i=0; i < _cLines; i++) {
_bsels[i] = in.readByte() == 1;
}
}
}
/**
* @return true as LbsDataSubRecord is always the last sub-record
*/
@Override
public boolean isTerminating(){
return true;
}
@Override
protected int getDataSize() {
int result = 2; // 2 initial shorts
// optional link formula
if (_linkPtg != null) {
result += 2; // encoded Ptg size
result += 4; // unknown int
result += _linkPtg.getSize();
if (_unknownPostFormulaByte != null) {
result += 1;
}
}
result += 4 * 2; // 4 shorts
if(_dropData != null) {
result += _dropData.getDataSize();
}
if(_rgLines != null) {
for(String str : _rgLines){
result += StringUtil.getEncodedSize(str);
}
}
if(_bsels != null) {
result += _bsels.length;
}
return result;
}
@Override
public void serialize(LittleEndianOutput out) {
out.writeShort(sid);
out.writeShort(_cbFContinued);
if (_linkPtg == null) {
out.writeShort(0);
} else {
int formulaSize = _linkPtg.getSize();
int linkSize = formulaSize + 6;
if (_unknownPostFormulaByte != null) {
linkSize++;
}
out.writeShort(linkSize);
out.writeShort(formulaSize);
out.writeInt(_unknownPreFormulaInt);
_linkPtg.write(out);
if (_unknownPostFormulaByte != null) {
out.writeByte(_unknownPostFormulaByte.intValue());
}
}
out.writeShort(_cLines);
out.writeShort(_iSel);
out.writeShort(_flags);
out.writeShort(_idEdit);
if(_dropData != null) {
_dropData.serialize(out);
}
if(_rgLines != null) {
for(String str : _rgLines){
StringUtil.writeUnicodeString(out, str);
}
}
if(_bsels != null) {
for(boolean val : _bsels){
out.writeByte(val ? 1 : 0);
}
}
}
@Override
public Object clone() {
return this;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer(256);
sb.append("[ftLbsData]\n");
sb.append(" .unknownShort1 =").append(HexDump.shortToHex(_cbFContinued)).append("\n");
sb.append(" .formula = ").append('\n');
sb.append(_linkPtg.toString()).append(_linkPtg.getRVAType()).append('\n');
sb.append(" .nEntryCount =").append(HexDump.shortToHex(_cLines)).append("\n");
sb.append(" .selEntryIx =").append(HexDump.shortToHex(_iSel)).append("\n");
sb.append(" .style =").append(HexDump.shortToHex(_flags)).append("\n");
sb.append(" .unknownShort10=").append(HexDump.shortToHex(_idEdit)).append("\n");
if(_dropData != null) sb.append('\n').append(_dropData.toString());
sb.append("[/ftLbsData]\n");
return sb.toString();
}
/**
*
* @return the formula that specifies the range of cell values that are the items in this list.
*/
public Ptg getFormula(){
return _linkPtg;
}
/**
* @return the number of items in the list
*/
public int getNumberOfItems(){
return _cLines;
}
/**
* This structure specifies properties of the dropdown list control
*/
public static class LbsDropData {
/**
* An unsigned integer that specifies the style of this dropdown.
*/
private int _wStyle;
/**
* An unsigned integer that specifies the number of lines to be displayed in the dropdown.
*/
private int _cLine;
/**
* An unsigned integer that specifies the smallest width in pixels allowed for the dropdown window
*/
private int _dxMin;
/**
* a string that specifies the current string value in the dropdown
*/
private String _str;
/**
* Optional, undefined and MUST be ignored.
* This field MUST exist if and only if the size of str in bytes is an odd number
*/
private Byte _unused;
public LbsDropData(LittleEndianInput in){
_wStyle = in.readUShort();
_cLine = in.readUShort();
_dxMin = in.readUShort();
_str = StringUtil.readUnicodeString(in);
if(StringUtil.getEncodedSize(_str) % 2 != 0){
_unused = in.readByte();
}
}
public void serialize(LittleEndianOutput out) {
out.writeShort(_wStyle);
out.writeShort(_cLine);
out.writeShort(_dxMin);
StringUtil.writeUnicodeString(out, _str);
if(_unused != null) out.writeByte(_unused);
}
public int getDataSize() {
int size = 6;
size += StringUtil.getEncodedSize(_str);
size += _unused;
return size;
}
@Override
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append("[LbsDropData]\n");
sb.append(" ._wStyle: ").append(_wStyle).append('\n');
sb.append(" ._cLine: ").append(_cLine).append('\n');
sb.append(" ._dxMin: ").append(_dxMin).append('\n');
sb.append(" ._str: ").append(_str).append('\n');
if(_unused != null) sb.append(" ._unused: ").append(_unused).append('\n');
sb.append("[/LbsDropData]\n");
return sb.toString();
}
}
}

View File

@ -78,20 +78,24 @@ public final class ObjRecord extends Record {
subrecords = null; subrecords = null;
return; return;
} }
if (subRecordData.length % 2 != 0) {
//YK: files produced by OO violate the condition below
/*
if (subRecordData.length % 2 != 0) {
String msg = "Unexpected length of subRecordData : " + HexDump.toHex(subRecordData); String msg = "Unexpected length of subRecordData : " + HexDump.toHex(subRecordData);
throw new RecordFormatException(msg); throw new RecordFormatException(msg);
} }
*/
// System.out.println(HexDump.toHex(subRecordData));
subrecords = new ArrayList<SubRecord>(); subrecords = new ArrayList<SubRecord>();
ByteArrayInputStream bais = new ByteArrayInputStream(subRecordData); ByteArrayInputStream bais = new ByteArrayInputStream(subRecordData);
LittleEndianInputStream subRecStream = new LittleEndianInputStream(bais); LittleEndianInputStream subRecStream = new LittleEndianInputStream(bais);
while (true) { CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)SubRecord.createSubRecord(subRecStream, 0);
SubRecord subRecord = SubRecord.createSubRecord(subRecStream); subrecords.add(cmo);
while (true) {
SubRecord subRecord = SubRecord.createSubRecord(subRecStream, cmo.getObjectType());
subrecords.add(subRecord); subrecords.add(subRecord);
if (subRecord instanceof EndSubRecord) { if (subRecord.isTerminating()) {
break; break;
} }
} }
@ -107,7 +111,8 @@ public final class ObjRecord extends Record {
} }
_isPaddedToQuadByteMultiple = false; _isPaddedToQuadByteMultiple = false;
} }
} else {
} else {
_isPaddedToQuadByteMultiple = false; _isPaddedToQuadByteMultiple = false;
} }
_uninterpretedData = null; _uninterpretedData = null;
@ -123,11 +128,7 @@ public final class ObjRecord extends Record {
* to the its proper size. POI does the same. * to the its proper size. POI does the same.
*/ */
private static boolean canPaddingBeDiscarded(byte[] data, int nRemainingBytes) { private static boolean canPaddingBeDiscarded(byte[] data, int nRemainingBytes) {
if (data.length < 0x1FEE) { // make sure none of the padding looks important
// this looks like a different problem
return false;
}
// make sure none of the padding looks important
for(int i=data.length-nRemainingBytes; i<data.length; i++) { for(int i=data.length-nRemainingBytes; i<data.length; i++) {
if (data[i] != 0x00) { if (data[i] != 0x00) {
return false; return false;

View File

@ -17,19 +17,13 @@
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.io.ByteArrayOutputStream;
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.HexDump;
import org.apache.poi.util.LittleEndianByteArrayInputStream;
import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.LittleEndianOutputStream; import org.apache.poi.util.LittleEndianOutputStream;
import java.io.ByteArrayOutputStream;
/** /**
* Subrecords are part of the OBJ class. * Subrecords are part of the OBJ class.
*/ */
@ -38,7 +32,15 @@ public abstract class SubRecord {
// no fields to initialise // no fields to initialise
} }
public static SubRecord createSubRecord(LittleEndianInput in) { /**
* read a sub-record from the supplied stream
*
* @param in the stream to read from
* @param cmoOt the objectType field of the containing CommonObjectDataSubRecord,
* we need it to propagate to next sub-records as it defines what data follows
* @return the created sub-record
*/
public static SubRecord createSubRecord(LittleEndianInput in, int cmoOt) {
int sid = in.readUShort(); int sid = in.readUShort();
int secondUShort = in.readUShort(); // Often (but not always) the datasize for the sub-record int secondUShort = in.readUShort(); // Often (but not always) the datasize for the sub-record
@ -54,7 +56,7 @@ public abstract class SubRecord {
case NoteStructureSubRecord.sid: case NoteStructureSubRecord.sid:
return new NoteStructureSubRecord(in, secondUShort); return new NoteStructureSubRecord(in, secondUShort);
case LbsDataSubRecord.sid: case LbsDataSubRecord.sid:
return new LbsDataSubRecord(in, secondUShort); return new LbsDataSubRecord(in, secondUShort, cmoOt);
} }
return new UnknownSubRecord(in, sid, secondUShort); return new UnknownSubRecord(in, sid, secondUShort);
} }
@ -78,8 +80,19 @@ public abstract class SubRecord {
public abstract void serialize(LittleEndianOutput out); public abstract void serialize(LittleEndianOutput out);
public abstract Object clone(); public abstract Object clone();
/**
* Wether this record terminates the sub-record stream.
* There are two cases when this method must be overridden and return <code>true</code>
* - EndSubRecord (sid = 0x00)
* - LbsDataSubRecord (sid = 0x12)
*
* @return whether this record is the last in the sub-record stream
*/
public boolean isTerminating(){
return false;
}
private static final class UnknownSubRecord extends SubRecord { private static final class UnknownSubRecord extends SubRecord {
private final int _sid; private final int _sid;
private final byte[] _data; private final byte[] _data;
@ -111,140 +124,4 @@ public abstract class SubRecord {
return sb.toString(); return sb.toString();
} }
} }
// TODO make into a top level class
// perhaps all SubRecord sublcasses could go to their own package
private static final class LbsDataSubRecord extends SubRecord {
public static final int sid = 0x0013;
private int _unknownShort1;
private int _unknownInt4;
private Ptg _linkPtg;
private Byte _unknownByte6;
private int _nEntryCount;
private int _selectedEntryIndex;
private int _style;
private int _unknownShort10;
private int _comboStyle;
private int _lineCount;
private int _unknownShort13;
public LbsDataSubRecord(LittleEndianInput in, int unknownShort1) {
_unknownShort1 = unknownShort1;
int linkSize = in.readUShort();
if (linkSize > 0) {
int formulaSize = in.readUShort();
_unknownInt4 = in.readInt();
byte[] buf = new byte[formulaSize];
in.readFully(buf);
_linkPtg = readRefPtg(buf);
switch (linkSize - formulaSize - 6) {
case 1:
_unknownByte6 = Byte.valueOf(in.readByte());
break;
case 0:
_unknownByte6 = null;
break;
default:
throw new RecordFormatException("Unexpected leftover bytes");
}
} else {
_unknownInt4 = 0;
_linkPtg = null;
_unknownByte6 = null;
}
_nEntryCount = in.readUShort();
_selectedEntryIndex = in.readUShort();
_style = in.readUShort();
_unknownShort10 = in.readUShort();
_comboStyle = in.readUShort();
_lineCount = in.readUShort();
_unknownShort13 = in.readUShort();
}
protected int getDataSize() {
int result = 2; // 2 initial shorts
// optional link formula
if (_linkPtg != null) {
result += 2; // encoded Ptg size
result += 4; // unknown int
result += _linkPtg.getSize();
if (_unknownByte6 != null) {
result += 1;
}
}
result += 7 * 2; // 7 shorts
return result;
}
public void serialize(LittleEndianOutput out) {
out.writeShort(sid);
out.writeShort(_unknownShort1); // note - this is *not* the size
if (_linkPtg == null) {
out.writeShort(0);
} else {
int formulaSize = _linkPtg.getSize();
int linkSize = formulaSize + 6;
if (_unknownByte6 != null) {
linkSize++;
}
out.writeShort(linkSize);
out.writeShort(formulaSize);
out.writeInt(_unknownInt4);
_linkPtg.write(out);
if (_unknownByte6 != null) {
out.writeByte(_unknownByte6.intValue());
}
}
out.writeShort(_nEntryCount);
out.writeShort(_selectedEntryIndex);
out.writeShort(_style);
out.writeShort(_unknownShort10);
out.writeShort(_comboStyle);
out.writeShort(_lineCount);
out.writeShort(_unknownShort13);
}
private static Ptg readRefPtg(byte[] formulaRawBytes) {
LittleEndianInput in = new LittleEndianByteArrayInputStream(formulaRawBytes);
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;
}
public Object clone() {
return this;
}
public String toString() {
StringBuffer sb = new StringBuffer(256);
sb.append("[ftLbsData]\n");
sb.append(" .unknownShort1 =").append(HexDump.shortToHex(_unknownShort1)).append("\n");
if (_linkPtg == null) {
sb.append(" <no link formula>\n");
} else {
sb.append(" .unknownInt4 =").append(HexDump.intToHex(_unknownInt4)).append("\n");
sb.append(" .linkPtg =").append(_linkPtg.toFormulaString()).append(" (").append(_linkPtg.getRVAType()).append(")").append("\n");
if (_unknownByte6 != null) {
sb.append(" .unknownByte6 =").append(HexDump.byteToHex(_unknownByte6.byteValue())).append("\n");
}
}
sb.append(" .nEntryCount =").append(HexDump.shortToHex(_nEntryCount)).append("\n");
sb.append(" .selEntryIx =").append(HexDump.shortToHex(_selectedEntryIndex)).append("\n");
sb.append(" .style =").append(HexDump.shortToHex(_style)).append("\n");
sb.append(" .unknownShort10=").append(HexDump.shortToHex(_unknownShort10)).append("\n");
sb.append(" .comboStyle =").append(HexDump.shortToHex(_comboStyle)).append("\n");
sb.append(" .lineCount =").append(HexDump.shortToHex(_lineCount)).append("\n");
sb.append(" .unknownShort13=").append(HexDump.shortToHex(_unknownShort13)).append("\n");
sb.append("[/ftLbsData]\n");
return sb.toString();
}
}
} }

View File

@ -0,0 +1,170 @@
/* ====================================================================
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.hssf.record;
import junit.framework.TestCase;
import java.util.Arrays;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import org.apache.poi.util.HexRead;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.LittleEndianOutputStream;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.ss.util.CellRangeAddress;
/**
* Tests the serialization and deserialization of the LbsDataSubRecord class works correctly.
*
* @author Yegor Kozlov
*/
public final class TestLbsDataSubRecord extends TestCase {
/**
* test read-write round trip
* test data was taken from 47701.xls
*/
public void test_47701(){
byte[] data = HexRead.readFromString(
"15, 00, 12, 00, 12, 00, 02, 00, 11, 20, " +
"00, 00, 00, 00, 80, 3D, 03, 05, 00, 00, " +
"00, 00, 0C, 00, 14, 00, 00, 00, 00, 00, " +
"00, 00, 00, 00, 00, 00, 01, 00, 0A, 00, " +
"00, 00, 10, 00, 01, 00, 13, 00, EE, 1F, " +
"10, 00, 09, 00, 40, 9F, 74, 01, 25, 09, " +
"00, 0C, 00, 07, 00, 07, 00, 07, 04, 00, " +
"00, 00, 08, 00, 00, 00");
RecordInputStream in = TestcaseRecordInputStream.create(ObjRecord.sid, data);
// check read OK
ObjRecord record = new ObjRecord(in);
assertEquals(3, record.getSubRecords().size());
SubRecord sr = record.getSubRecords().get(2);
assertTrue(sr instanceof LbsDataSubRecord);
LbsDataSubRecord lbs = (LbsDataSubRecord)sr;
assertEquals(4, lbs.getNumberOfItems());
assertTrue(lbs.getFormula() instanceof AreaPtg);
AreaPtg ptg = (AreaPtg)lbs.getFormula();
CellRangeAddress range = new CellRangeAddress(
ptg.getFirstRow(), ptg.getLastRow(), ptg.getFirstColumn(), ptg.getLastColumn());
assertEquals("H10:H13", range.formatAsString());
// check that it re-serializes to the same data
byte[] ser = record.serialize();
TestcaseRecordInputStream.confirmRecordEncoding(ObjRecord.sid, data, ser);
}
/**
* test data was taken from the file attached to Bugzilla 45778
*/
public void test_45778(){
byte[] data = HexRead.readFromString(
"15, 00, 12, 00, 14, 00, 01, 00, 01, 00, " +
"01, 21, 00, 00, 3C, 13, F4, 03, 00, 00, " +
"00, 00, 0C, 00, 14, 00, 00, 00, 00, 00, " +
"00, 00, 00, 00, 00, 00, 01, 00, 08, 00, 00, " +
"00, 10, 00, 00, 00, " +
"13, 00, EE, 1F, " +
"00, 00, " +
"08, 00, " + //number of items
"08, 00, " + //selected item
"01, 03, " + //flags
"00, 00, " + //objId
//LbsDropData
"0A, 00, " + //flags
"14, 00, " + //the number of lines to be displayed in the dropdown
"6C, 00, " + //the smallest width in pixels allowed for the dropdown window
"00, 00, " + //num chars
"00, 00");
RecordInputStream in = TestcaseRecordInputStream.create(ObjRecord.sid, data);
// check read OK
ObjRecord record = new ObjRecord(in);
SubRecord sr = record.getSubRecords().get(2);
assertTrue(sr instanceof LbsDataSubRecord);
LbsDataSubRecord lbs = (LbsDataSubRecord)sr;
assertEquals(8, lbs.getNumberOfItems());
assertNull(lbs.getFormula());
// check that it re-serializes to the same data
byte[] ser = record.serialize();
TestcaseRecordInputStream.confirmRecordEncoding(ObjRecord.sid, data, ser);
}
/**
* Test data produced by OpenOffice 3.1 by opening and saving 47701.xls
* There are 5 padding bytes that are removed by POI
*/
public void test_remove_padding(){
byte[] data = HexRead.readFromString(
"5D, 00, 4C, 00, " +
"15, 00, 12, 00, 12, 00, 01, 00, 11, 00, " +
"00, 00, 00, 00, 00, 00, 00, 00, 00, 00, " +
"00, 00, 0C, 00, 14, 00, 00, 00, 00, 00, " +
"00, 00, 00, 00, 00, 00, 01, 00, 09, 00, " +
"00, 00, 0F, 00, 01, 00, " +
"13, 00, 1B, 00, " +
"10, 00, " + //next 16 bytes is a ptg aray
"09, 00, 00, 00, 00, 00, 25, 09, 00, 0C, 00, 07, 00, 07, 00, 00, " +
"01, 00, " + //num lines
"00, 00, " + //selected
"08, 00, " +
"00, 00, " +
"00, 00, 00, 00, 00"); //padding bytes
RecordInputStream in = TestcaseRecordInputStream.create(data);
// check read OK
ObjRecord record = new ObjRecord(in);
// check that it re-serializes to the same data
byte[] ser = record.serialize();
assertEquals(data.length-5, ser.length);
for(int i=0; i < ser.length; i++) assertEquals(data[i],ser[i]);
//check we can read the trimmed record
RecordInputStream in2 = TestcaseRecordInputStream.create(ser);
ObjRecord record2 = new ObjRecord(in2);
byte[] ser2 = record2.serialize();
assertTrue(Arrays.equals(ser, ser2));
}
public void test_LbsDropData(){
byte[] data = HexRead.readFromString(
//LbsDropData
"0A, 00, " + //flags
"14, 00, " + //the number of lines to be displayed in the dropdown
"6C, 00, " + //the smallest width in pixels allowed for the dropdown window
"00, 00, " + //num chars
"00, " + //compression flag
"00"); //padding byte
LittleEndianInputStream in = new LittleEndianInputStream(new ByteArrayInputStream(data));
LbsDataSubRecord.LbsDropData lbs = new LbsDataSubRecord.LbsDropData(in);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
lbs.serialize(new LittleEndianOutputStream(baos));
assertTrue(Arrays.equals(data, baos.toByteArray()));
}
}

View File

@ -1522,4 +1522,12 @@ public final class TestBugs extends BaseTestBugzillaIssues {
HSSFCell cell2 = s.getRow(0).getCell(1); HSSFCell cell2 = s.getRow(0).getCell(1);
assertEquals(1.0, cell2.getNumericCellValue()); assertEquals(1.0, cell2.getNumericCellValue());
} }
/**
* POI 3.5 beta 7 can not read excel file contain list box (Form Control)
*/
public void test47701() {
openSample("47701.xls");
}
} }

Binary file not shown.