From 60c8dc56883b2099129464220bae5c6fee0e2fba Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Thu, 27 Mar 2008 20:03:29 +0000 Subject: [PATCH] fixes for ExternalNameRecord serialisation bug #44691 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@641964 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/record/ExternalNameRecord.java | 252 +++++++++--------- .../poi/hssf/record/AllRecordTests.java | 6 +- .../hssf/record/TestExternalNameRecord.java | 55 ++++ 3 files changed, 187 insertions(+), 126 deletions(-) create mode 100644 src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java diff --git a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java index 771603c85..fe2085948 100755 --- a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java +++ b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java @@ -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

* - * @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 true, 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 true, 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(); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java index b1acfeafa..9da8f45eb 100755 --- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java +++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java @@ -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); diff --git a/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java new file mode 100644 index 000000000..a90221f5e --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java @@ -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()); + } +}