fixes for ExternalNameRecord serialisation bug #44691

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@641964 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-03-27 20:03:29 +00:00
parent 47d3a87a5c
commit 44c475cd46
3 changed files with 187 additions and 126 deletions

View File

@ -17,7 +17,6 @@
package org.apache.poi.hssf.record;
import java.util.List;
import java.util.Stack;
import org.apache.poi.hssf.record.formula.Ptg;
@ -27,153 +26,160 @@ import org.apache.poi.util.StringUtil;
/**
* EXTERNALNAME<p/>
*
* @author josh micich
* @author Josh Micich
*/
public final class ExternalNameRecord extends Record {
public final static short sid = 0x23; // as per BIFF8. (some old versions used 0x223)
public final static short sid = 0x23; // as per BIFF8. (some old versions used 0x223)
private static final int OPT_BUILTIN_NAME = 0x0001;
private static final int OPT_AUTOMATIC_LINK = 0x0002;
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_CLIP_FORMAT_MASK = 0x7FE0;
private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000;
private static final int OPT_BUILTIN_NAME = 0x0001;
private static final int OPT_AUTOMATIC_LINK = 0x0002;
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_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 String field_4_name;
private Ptg[] field_5_name_definition; // TODO - junits for name definition field
private short field_1_option_flag;
private short field_2_index;
private short field_3_not_used;
private String field_4_name;
private Stack field_5_name_definition;
public ExternalNameRecord(RecordInputStream in) {
super(in);
}
public ExternalNameRecord(RecordInputStream in) {
super(in);
}
/**
* Convenience Function to determine if the name is a built-in name
*/
public boolean isBuiltInName() {
return (field_1_option_flag & OPT_BUILTIN_NAME) != 0;
return (field_1_option_flag & OPT_BUILTIN_NAME) != 0;
}
/**
* For OLE and DDE, links can be either 'automatic' or 'manual'
*/
public boolean isAutomaticLink() {
return (field_1_option_flag & OPT_AUTOMATIC_LINK) != 0;
}
/**
* only for OLE and DDE
*/
public boolean isPicureLink() {
return (field_1_option_flag & OPT_PICTURE_LINK) != 0;
}
/**
* DDE links only. If <code>true</code>, this denotes the 'StdDocumentName'
*/
public boolean isStdDocumentNameIdentifier() {
return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0;
}
public boolean isOLELink() {
return (field_1_option_flag & OPT_OLE_LINK) != 0;
}
public boolean isIconifiedPictureLink() {
return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0;
}
/**
* @return the standard String representation of this name
*/
public String getText() {
return field_4_name;
}
/**
* For OLE and DDE, links can be either 'automatic' or 'manual'
*/
public boolean isAutomaticLink() {
return (field_1_option_flag & OPT_AUTOMATIC_LINK) != 0;
}
/**
* only for OLE and DDE
*/
public boolean isPicureLink() {
return (field_1_option_flag & OPT_PICTURE_LINK) != 0;
}
/**
* DDE links only. If <code>true</code>, this denotes the 'StdDocumentName'
*/
public boolean isStdDocumentNameIdentifier() {
return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0;
}
public boolean isOLELink() {
return (field_1_option_flag & OPT_OLE_LINK) != 0;
}
public boolean isIconifiedPictureLink() {
return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0;
}
/**
* @return the standard String representation of this name
*/
public String getText() {
return field_4_name;
}
/**
* called by constructor, should throw runtime exception in the event of a
* record passed with a differing ID.
*
* @param id alleged id for this record
*/
protected void validateSid(short id) {
if (id != sid) {
throw new RecordFormatException("NOT A valid ExternalName RECORD");
}
}
/**
* called by constructor, should throw runtime exception in the event of a
* record passed with a differing ID.
*
* @param id alleged id for this record
*/
protected void validateSid(short id) {
if (id != sid) {
throw new RecordFormatException("NOT A valid ExternalName RECORD");
}
}
private int getDataSize(){
return 2 + 2 + field_4_name.length() + 2 + getNameDefinitionSize();
}
private int getDataSize(){
return 3 * 2 // 3 short fields
+ 2 + field_4_name.length() // nameLen and name
+ 2 + getNameDefinitionSize(); // nameDefLen and nameDef
}
/**
* called by the class that is responsible for writing this sucker.
* Subclasses should implement this so that their data is passed back in a
* byte array.
*
* @param offset to begin writing at
* @param data byte array containing instance data
* @return number of bytes written
*/
public int serialize( int offset, byte[] data ) {
// TODO - junit tests
int dataSize = getDataSize();
/**
* called by the class that is responsible for writing this sucker.
* Subclasses should implement this so that their data is passed back in a
* byte array.
*
* @param offset to begin writing at
* @param data byte array containing instance data
* @return number of bytes written
*/
public int serialize( int offset, byte[] data ) {
int dataSize = getDataSize();
LittleEndian.putShort( data, 0 + offset, sid );
LittleEndian.putShort( data, 0 + offset, sid );
LittleEndian.putShort( data, 2 + offset, (short) dataSize );
LittleEndian.putShort( data, 4 + offset, field_1_option_flag );
LittleEndian.putShort( data, 6 + offset, field_2_index );
LittleEndian.putShort( data, 8 + offset, field_3_not_used );
short nameLen = (short) field_4_name.length();
LittleEndian.putShort( data, 4 + offset, field_1_option_flag );
LittleEndian.putShort( data, 6 + offset, field_2_index );
LittleEndian.putShort( data, 8 + offset, field_3_not_used );
short nameLen = (short) field_4_name.length();
LittleEndian.putShort( data, 10 + offset, nameLen );
StringUtil.putCompressedUnicode( field_4_name, data, 10 + offset );
short defLen = (short) getNameDefinitionSize();
LittleEndian.putShort( data, 12 + nameLen + offset, defLen );
Ptg.serializePtgStack(field_5_name_definition, data, 12 + nameLen + offset );
StringUtil.putCompressedUnicode( field_4_name, data, 12 + offset );
short defLen = (short) getNameDefinitionSize();
LittleEndian.putShort( data, 12 + nameLen + offset, defLen );
Ptg.serializePtgStack(toStack(field_5_name_definition), data, 14 + nameLen + offset );
return dataSize + 4;
}
}
private int getNameDefinitionSize() {
int result = 0;
List list = field_5_name_definition;
for (int k = 0; k < list.size(); k++)
{
Ptg ptg = ( Ptg ) list.get(k);
result += ptg.getSize();
}
return result;
}
private int getNameDefinitionSize() {
int result = 0;
for (int i = 0; i < field_5_name_definition.length; i++) {
result += field_5_name_definition[i].getSize();
}
return result;
}
public int getRecordSize(){
return 6 + 2 + field_4_name.length() + 2 + getNameDefinitionSize();
}
public int getRecordSize(){
return 4 + getDataSize();
}
protected void fillFields(RecordInputStream in) {
field_1_option_flag = in.readShort();
field_2_index = in.readShort();
field_3_not_used = in.readShort();
short nameLength = in.readShort();
field_4_name = in.readCompressedUnicode(nameLength);
short formulaLen = in.readShort();
field_5_name_definition = Ptg.createParsedExpressionTokens(formulaLen, in);
}
protected void fillFields(RecordInputStream in) {
field_1_option_flag = in.readShort();
field_2_index = in.readShort();
field_3_not_used = in.readShort();
short nameLength = in.readShort();
field_4_name = in.readCompressedUnicode(nameLength);
short formulaLen = in.readShort();
field_5_name_definition = toPtgArray(Ptg.createParsedExpressionTokens(formulaLen, in));
}
public short getSid() {
return sid;
}
private static Ptg[] toPtgArray(Stack s) {
Ptg[] result = new Ptg[s.size()];
s.toArray(result);
return result;
}
private static Stack toStack(Ptg[] ptgs) {
Stack result = new Stack();
for (int i = 0; i < ptgs.length; i++) {
result.push(ptgs[i]);
}
return result;
}
public short getSid() {
return sid;
}
public String toString() {
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("]");
return sb.toString();
}
public String toString() {
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("]");
return sb.toString();
}
}

View File

@ -14,7 +14,6 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record;
@ -28,10 +27,10 @@ import junit.framework.TestSuite;
*
* @author Josh Micich
*/
public class AllRecordTests {
public final class AllRecordTests {
public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record");
TestSuite result = new TestSuite(AllRecordTests.class.getName());
result.addTest(AllFormulaTests.suite());
@ -56,6 +55,7 @@ public class AllRecordTests {
result.addTestSuite(TestEmbeddedObjectRefSubRecord.class);
result.addTestSuite(TestEndSubRecord.class);
result.addTestSuite(TestEscherAggregate.class);
result.addTestSuite(TestExternalNameRecord.class);
result.addTestSuite(TestFontBasisRecord.class);
result.addTestSuite(TestFontIndexRecord.class);
result.addTestSuite(TestFormulaRecord.class);

View File

@ -0,0 +1,55 @@
/* ====================================================================
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.AssertionFailedError;
import junit.framework.TestCase;
/**
*
* @author Josh Micich
*/
public final class TestExternalNameRecord extends TestCase {
private static final byte[] dataFDS = {
0, 0, 0, 0, 0, 0, 3, 0, 70, 68, 83, 0, 0,
};
private static ExternalNameRecord createSimpleENR() {
return new ExternalNameRecord(new TestcaseRecordInputStream((short)0x0023, dataFDS));
}
public void testBasicDeserializeReserialize() {
ExternalNameRecord enr = createSimpleENR();
assertEquals( "FDS", enr.getText());
try {
TestcaseRecordInputStream.confirmRecordEncoding(0x0023, dataFDS, enr.serialize());
} catch (ArrayIndexOutOfBoundsException e) {
if(e.getMessage().equals("15")) {
throw new AssertionFailedError("Identified bug 44691");
}
}
}
public void testBasicSize() {
ExternalNameRecord enr = createSimpleENR();
if(enr.getRecordSize() == 13) {
throw new AssertionFailedError("Identified bug 44691");
}
assertEquals(17, enr.getRecordSize());
}
}