fixed ExternalNameRecord to properly distinguish DDE data from OLE data in the record body, see Bugzilla 48339

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@890871 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2009-12-15 16:25:14 +00:00
parent 2468b7a1e4
commit 7ae214c7c7
3 changed files with 68 additions and 95 deletions

View File

@ -34,6 +34,7 @@
<changes>
<release version="3.7-SNAPSHOT" date="2010-??-??">
<action dev="POI-DEVELOPERS" type="fix">48339 - fixed ExternalNameRecord to properly distinguish DDE data from OLE data items </action>
<action dev="POI-DEVELOPERS" type="fix">47920 - allow editing workbooks embedded into PowerPoint files</action>
<action dev="POI-DEVELOPERS" type="add">48343 - added implementation of SUBTOTAL function</action>
</release>

View File

@ -34,15 +34,14 @@ public final class ExternalNameRecord extends StandardRecord {
private static final int OPT_BUILTIN_NAME = 0x0001;
private static final int OPT_AUTOMATIC_LINK = 0x0002; // m$ doc calls this fWantAdvise
private static final int OPT_PICTURE_LINK = 0x0004;
private static final int OPT_STD_DOCUMENT_NAME = 0x0008;
private static final int OPT_OLE_LINK = 0x0010;
private static final int OPT_STD_DOCUMENT_NAME = 0x0008; //fOle
private static final int OPT_OLE_LINK = 0x0010; //fOleLink
// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0;
private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000;
private short field_1_option_flag;
private short field_2_index;
private short field_3_not_used;
private int field_2_not_used;
private String field_4_name;
private Formula field_5_name_definition;
@ -99,109 +98,67 @@ public final class ExternalNameRecord extends StandardRecord {
}
protected int getDataSize(){
int result = 3 * 2 // 3 short fields
+ 2 + field_4_name.length(); // nameLen and name
if(hasFormula()) {
result += field_5_name_definition.getEncodedSize();
} else {
if (_ddeValues != null) {
result += 3; // byte, short
result += ConstantValueParser.getEncodedSize(_ddeValues);
}
}
int result = 2 + 4; // short and int
result += StringUtil.getEncodedSize(field_4_name) - 1; //size is byte, not short
if(!isOLELink() && !isStdDocumentNameIdentifier()){
if(isAutomaticLink()){
result += 3; // byte, short
result += ConstantValueParser.getEncodedSize(_ddeValues);
} else {
result += field_5_name_definition.getEncodedSize();
}
}
return result;
}
public void serialize(LittleEndianOutput out) {
out.writeShort(field_1_option_flag);
out.writeShort(field_2_index);
out.writeShort(field_3_not_used);
int nameLen = field_4_name.length();
out.writeShort(nameLen);
StringUtil.putCompressedUnicode(field_4_name, out);
if (hasFormula()) {
field_5_name_definition.serialize(out);
} else {
if (_ddeValues != null) {
out.writeByte(_nColumns-1);
out.writeShort(_nRows-1);
ConstantValueParser.encode(out, _ddeValues);
}
}
out.writeInt(field_2_not_used);
out.writeByte(field_4_name.length());
StringUtil.writeUnicodeStringFlagAndData(out, field_4_name);
if(!isOLELink() && !isStdDocumentNameIdentifier()){
if(isAutomaticLink()){
out.writeByte(_nColumns-1);
out.writeShort(_nRows-1);
ConstantValueParser.encode(out, _ddeValues);
} else {
field_5_name_definition.serialize(out);
}
}
}
public ExternalNameRecord(RecordInputStream in) {
field_1_option_flag = in.readShort();
field_2_index = in.readShort();
field_3_not_used = in.readShort();
int nameLength = in.readUByte();
int multibyteFlag = in.readUByte();
if (multibyteFlag == 0) {
field_4_name = in.readCompressedUnicode(nameLength);
} else {
field_4_name = in.readUnicodeLEString(nameLength);
}
if(!hasFormula()) {
if (!isStdDocumentNameIdentifier() && !isOLELink() && isAutomaticLink()) {
// both need to be incremented
int nColumns = in.readUByte() + 1;
int nRows = in.readShort() + 1;
field_2_not_used = in.readInt();
int totalCount = nRows * nColumns;
_ddeValues = ConstantValueParser.parse(in, totalCount);
_nColumns = nColumns;
_nRows = nRows;
}
if(in.remaining() > 0) {
throw readFail("Some unread data (is formula present?)");
}
field_5_name_definition = null;
return;
}
int nBytesRemaining = in.available();
if(nBytesRemaining <= 0) {
throw readFail("Ran out of record data trying to read formula.");
}
int formulaLen = in.readUShort();
nBytesRemaining -=2;
field_5_name_definition = Formula.read(formulaLen, in, nBytesRemaining);
}
/*
* Makes better error messages (while hasFormula() is not reliable)
* Remove this when hasFormula() is stable.
*/
private RuntimeException readFail(String msg) {
String fullMsg = msg + " fields: (option=" + field_1_option_flag + " index=" + field_2_index
+ " not_used=" + field_3_not_used + " name='" + field_4_name + "')";
return new RecordFormatException(fullMsg);
}
int numChars = in.readUByte();
field_4_name = StringUtil.readUnicodeString(in, numChars);
private boolean hasFormula() {
// TODO - determine exact conditions when formula is present
if (false) {
// "Microsoft Office Excel 97-2007 Binary File Format (.xls) Specification"
// m$'s document suggests logic like this, but bugzilla 44774 att 21790 seems to disagree
if (isStdDocumentNameIdentifier()) {
if (isOLELink()) {
// seems to be not possible according to m$ document
throw new IllegalStateException(
"flags (std-doc-name and ole-link) cannot be true at the same time");
}
return false;
}
if (isOLELink()) {
return false;
}
return true;
}
// the record body can take different forms.
// The form is dictated by the values of 3-th and 4-th bits in field_1_option_flag
if(!isOLELink() && !isStdDocumentNameIdentifier()){
// another switch: the fWantAdvise bit specifies whether the body describes
// an external defined name or a DDE data item
if(isAutomaticLink()){
//body specifies DDE data item
int nColumns = in.readUByte() + 1;
int nRows = in.readShort() + 1;
// This was derived by trial and error, but doesn't seem quite right
if (isAutomaticLink()) {
return false;
}
return true;
}
int totalCount = nRows * nColumns;
_ddeValues = ConstantValueParser.parse(in, totalCount);
_nColumns = nColumns;
_nRows = nRows;
} else {
//body specifies an external defined name
int formulaLen = in.readUShort();
field_5_name_definition = Formula.read(formulaLen, in);
}
}
}
public short getSid() {
return sid;
@ -211,7 +168,7 @@ public final class ExternalNameRecord extends StandardRecord {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()).append(" [EXTERNALNAME ");
sb.append(" ").append(field_4_name);
sb.append(" ix=").append(field_2_index);
sb.append(" ix=").append(field_2_not_used);
sb.append("]");
return sb.toString();
}

View File

@ -18,6 +18,7 @@
package org.apache.poi.hssf.record;
import org.apache.poi.util.HexRead;
import org.apache.poi.util.HexDump;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
@ -156,5 +157,19 @@ public final class TestExternalNameRecord extends TestCase {
throw e;
}
assertEquals("\u0159azen\u00ED_Billa", enr.getText());
byte[] ser = enr.serialize();
assertEquals(HexDump.toHex(dataUN), HexDump.toHex(ser));
}
public void test48339() {
// data taken from bugzilla 48339
byte[] data = HexRead.readFromString(
"23 00 09 00" +
"F4, FF, 14, 2D, 61, 01, 01, 00, 27");
RecordInputStream in = TestcaseRecordInputStream.create(data);
ExternalNameRecord enr = new ExternalNameRecord(in);
byte[] ser = enr.serialize();
assertEquals(HexDump.toHex(data), HexDump.toHex(ser));
}
}