From 97b9695a3abf69f80ae1a7ac69446f8d4a3576ac Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Thu, 6 Nov 2008 03:43:17 +0000 Subject: [PATCH] Bug 45784 - More fixes to SeriesTextRecord git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@711764 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 1 + src/documentation/content/xdocs/status.xml | 1 + .../poi/hssf/record/SeriesTextRecord.java | 272 +++++++----------- .../poi/hssf/record/TestSeriesTextRecord.java | 101 +++++-- .../apache/poi/hssf/usermodel/TestBugs.java | 3 +- 5 files changed, 185 insertions(+), 193 deletions(-) diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index bf5932763b..dd4bd30319 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 45784 - More fixes to SeriesTextRecord 46033 - fixed TableCell to correctly set text type 46122 - fixed Picture.draw to skip rendering if picture data was not found 15716 - memory usage optimisation - converted Ptg arrays into Formula objects diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 7168b5c7b5..8c714fca38 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 45784 - More fixes to SeriesTextRecord 46033 - fixed TableCell to correctly set text type 46122 - fixed Picture.draw to skip rendering if picture data was not found 15716 - memory usage optimisation - converted Ptg arrays into Formula objects diff --git a/src/java/org/apache/poi/hssf/record/SeriesTextRecord.java b/src/java/org/apache/poi/hssf/record/SeriesTextRecord.java index d1ca886ff4..15e454993f 100644 --- a/src/java/org/apache/poi/hssf/record/SeriesTextRecord.java +++ b/src/java/org/apache/poi/hssf/record/SeriesTextRecord.java @@ -18,175 +18,121 @@ package org.apache.poi.hssf.record; import org.apache.poi.util.HexDump; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianByteArrayOutputStream; +import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.StringUtil; /** + * SERIESTEXT (0x100D)

* Defines a series name

* * @author Andrew C. Oliver (acoliver at apache.org) */ public final class SeriesTextRecord extends Record { - public final static short sid = 0x100d; - private short field_1_id; - private byte field_2_textLength; - private byte field_3_undocumented; - private String field_4_text; - - - public SeriesTextRecord() - { - - } - - public SeriesTextRecord(RecordInputStream in) - { - field_1_id = in.readShort(); - field_2_textLength = in.readByte(); - field_3_undocumented = in.readByte(); - field_4_text = in.readUnicodeLEString( - LittleEndian.ubyteToInt(field_2_textLength)); - } - - public String toString() - { - StringBuffer buffer = new StringBuffer(); - - buffer.append("[SERIESTEXT]\n"); - buffer.append(" .id = ") - .append("0x").append(HexDump.toHex( getId ())) - .append(" (").append( getId() ).append(" )"); - buffer.append(System.getProperty("line.separator")); - buffer.append(" .textLength = ") - .append("0x").append(HexDump.toHex( getTextLength ())) - .append(" (").append( getTextLength() ).append(" )"); - buffer.append(System.getProperty("line.separator")); - buffer.append(" .undocumented = ") - .append("0x").append(HexDump.toHex( getUndocumented ())) - .append(" (").append( getUndocumented() ).append(" )"); - buffer.append(System.getProperty("line.separator")); - buffer.append(" .text = ") - .append(" (").append( getText() ).append(" )"); - buffer.append(System.getProperty("line.separator")); - - buffer.append("[/SERIESTEXT]\n"); - return buffer.toString(); - } - - public int serialize(int offset, byte[] data) - { - int pos = 0; - - LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4)); - - LittleEndian.putShort(data, 4 + offset + pos, field_1_id); - data[ 6 + offset + pos ] = field_2_textLength; - data[ 7 + offset + pos ] = field_3_undocumented; - StringUtil.putUnicodeLE(field_4_text, data, 8 + offset + pos); - - return getRecordSize(); - } - - protected int getDataSize() { - return 2 + 1 + 1 + (field_2_textLength *2); - } - - public short getSid() - { - return sid; - } - - public Object clone() { - SeriesTextRecord rec = new SeriesTextRecord(); - - rec.field_1_id = field_1_id; - rec.field_2_textLength = field_2_textLength; - rec.field_3_undocumented = field_3_undocumented; - rec.field_4_text = field_4_text; - return rec; - } - - - - - /** - * Get the id field for the SeriesText record. - */ - public short getId() - { - return field_1_id; - } - - /** - * Set the id field for the SeriesText record. - */ - public void setId(short field_1_id) - { - this.field_1_id = field_1_id; - } - - /** - * Get the text length field for the SeriesText record. - */ - public int getTextLength() - { - return LittleEndian.ubyteToInt(field_2_textLength); - } - - /** - * Set the text length field for the SeriesText record. - * Needs to be wrapped. - */ - public void setTextLength(byte field_2_textLength) - { - this.field_2_textLength = field_2_textLength; - } - /** - * Set the text length field for the SeriesText record. - */ - public void setTextLength(int field_2_textLength) - { - if(field_2_textLength > 255) { - throw new IllegalArgumentException("Length must be 0-255"); - } - if(field_2_textLength > 127) { - this.field_2_textLength = (byte) - (field_2_textLength-256); - } else { - this.field_2_textLength = (byte)field_2_textLength; - } - } - - /** - * Get the undocumented field for the SeriesText record. - */ - public byte getUndocumented() - { - return field_3_undocumented; - } - - /** - * Set the undocumented field for the SeriesText record. - */ - public void setUndocumented(byte field_3_undocumented) - { - this.field_3_undocumented = field_3_undocumented; - } - - /** - * Get the text field for the SeriesText record. - */ - public String getText() - { - return field_4_text; - } - - /** - * Set the text field for the SeriesText record. - */ - public void setText(String field_4_text) - { - this.field_4_text = field_4_text; - } + public final static short sid = 0x100D; + + /** the actual text cannot be longer than 255 characters */ + private static final int MAX_LEN = 0xFF; + private int field_1_id; + private boolean is16bit; + private String field_4_text; + + public SeriesTextRecord() { + field_4_text = ""; + is16bit = false; + } + + public SeriesTextRecord(RecordInputStream in) { + field_1_id = in.readUShort(); + int field_2_textLength = in.readUByte(); + is16bit = (in.readUByte() & 0x01) != 0; + if (is16bit) { + field_4_text = in.readUnicodeLEString(field_2_textLength); + } else { + field_4_text = in.readCompressedUnicode(field_2_textLength); + } + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("[SERIESTEXT]\n"); + sb.append(" .id =").append(HexDump.shortToHex(getId())).append('\n'); + sb.append(" .textLen=").append(field_4_text.length()).append('\n'); + sb.append(" .is16bit=").append(is16bit).append('\n'); + sb.append(" .text =").append(" (").append(getText()).append(" )").append('\n'); + sb.append("[/SERIESTEXT]\n"); + return sb.toString(); + } + + public int serialize(int offset, byte[] data) { + int dataSize = getDataSize(); + int recordSize = 4 + dataSize; + LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recordSize); + out.writeShort(sid); + out.writeShort(dataSize); + + out.writeShort(field_1_id); + out.writeByte(field_4_text.length()); + if (is16bit) { + // Excel (2007) seems to choose 16bit regardless of whether it is needed + out.writeByte(0x01); + StringUtil.putUnicodeLE(field_4_text, out); + } else { + // Excel can read this OK + out.writeByte(0x00); + StringUtil.putCompressedUnicode(field_4_text, out); + } + return recordSize; + } + + protected int getDataSize() { + return 2 + 1 + 1 + field_4_text.length() * (is16bit ? 2 : 1); + } + + public short getSid() { + return sid; + } + + public Object clone() { + SeriesTextRecord rec = new SeriesTextRecord(); + + rec.field_1_id = field_1_id; + rec.is16bit = is16bit; + rec.field_4_text = field_4_text; + return rec; + } + + /** + * Get the id field for the SeriesText record. + */ + public int getId() { + return field_1_id; + } + + /** + * Set the id field for the SeriesText record. + */ + public void setId(int id) { + field_1_id = id; + } + + /** + * Get the text field for the SeriesText record. + */ + public String getText() { + return field_4_text; + } + + /** + * Set the text field for the SeriesText record. + */ + public void setText(String text) { + if (text.length() > MAX_LEN) { + throw new IllegalArgumentException("Text is too long (" + + text.length() + ">" + MAX_LEN + ")"); + } + field_4_text = text; + is16bit = StringUtil.hasMultibyte(text); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/TestSeriesTextRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSeriesTextRecord.java index a016e60e91..8b05dc4336 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestSeriesTextRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestSeriesTextRecord.java @@ -17,46 +17,89 @@ package org.apache.poi.hssf.record; - +import junit.framework.AssertionFailedError; import junit.framework.TestCase; -/** - * Tests the serialization and deserialization of the SeriesTextRecord - * class works correctly. Test data taken directly from a real - * Excel file. - * +import org.apache.poi.util.HexRead; +/** + * Tests the serialization and deserialization of the SeriesTextRecord class + * works correctly. Test data taken directly from a real Excel file. + * + * * @author Andrew C. Oliver (acoliver at apache.org) */ public final class TestSeriesTextRecord extends TestCase { - byte[] data = new byte[] { - (byte)0x00,(byte)0x00,(byte)0x0C,(byte)0x01,(byte)0x56,(byte)0x00,(byte)0x61,(byte)0x00,(byte)0x6C,(byte)0x00,(byte)0x75,(byte)0x00,(byte)0x65,(byte)0x00,(byte)0x20,(byte)0x00,(byte)0x4E,(byte)0x00,(byte)0x75,(byte)0x00,(byte)0x6D,(byte)0x00,(byte)0x62,(byte)0x00,(byte)0x65,(byte)0x00,(byte)0x72,(byte)0x00 - }; + private static final byte[] SIMPLE_DATA = HexRead + .readFromString("00 00 0C 00 56 61 6C 75 65 20 4E 75 6D 62 65 72"); + + public void testLoad() { + SeriesTextRecord record = new SeriesTextRecord(TestcaseRecordInputStream.create(0x100d, SIMPLE_DATA)); + + assertEquals((short) 0, record.getId()); + assertEquals("Value Number", record.getText()); - public void testLoad() { - SeriesTextRecord record = new SeriesTextRecord(TestcaseRecordInputStream.create(0x100d, data)); + assertEquals(SIMPLE_DATA.length + 4, record.getRecordSize()); + } - assertEquals( (short)0, record.getId()); - assertEquals( (byte)0x0C, record.getTextLength()); - assertEquals( (byte)0x01, record.getUndocumented()); - assertEquals( "Value Number", record.getText()); + public void testStore() { + SeriesTextRecord record = new SeriesTextRecord(); - assertEquals( 32, record.getRecordSize() ); - } + record.setId(0); + record.setText("Value Number"); - public void testStore() - { - SeriesTextRecord record = new SeriesTextRecord(); + byte[] recordBytes = record.serialize(); + TestcaseRecordInputStream.confirmRecordEncoding(SeriesTextRecord.sid, SIMPLE_DATA, + recordBytes); + } - record.setId( (short)0 ); - record.setTextLength( (byte)0x0C ); - record.setUndocumented( (byte)0x01 ); - record.setText( "Value Number" ); + public void testReserializeLongTitle() { + // Hex dump from bug 45784 attachment 22560 streamOffset=0x0CD1 + byte[] data = HexRead.readFromString( + "00 00, " + + "82 " + + "01 " + + "50 00 6C 00 61 00 73 00 6D 00 61 00 20 00 4C 00 " + + "65 00 76 00 65 00 6C 00 73 00 20 00 6F 00 66 00 " + + "20 00 4C 00 2D 00 30 00 30 00 30 00 31 00 31 00 " + + "31 00 32 00 32 00 32 00 2D 00 33 00 33 00 33 00 " + + "58 00 34 00 34 00 34 00 20 00 69 00 6E 00 20 00 " + + "53 00 44 00 20 00 72 00 61 00 74 00 0A 00 50 00 " + + "4F 00 20 00 33 00 2E 00 30 00 20 00 6D 00 67 00 " + + "2F 00 6B 00 67 00 20 00 28 00 35 00 2E 00 30 00 " + + "20 00 6D 00 4C 00 2F 00 6B 00 67 00 29 00 20 00 " + + "69 00 6E 00 20 00 4D 00 65 00 74 00 68 00 6F 00 " + + "63 00 65 00 6C 00 0A 00 49 00 56 00 20 00 30 00 " + + "2E 00 35 00 20 00 6D 00 67 00 2F 00 6B 00 67 00 " + + "20 00 28 00 31 00 2E 00 30 00 20 00 6D 00 4C 00 " + + "2F 00 6B 00 67 00 29 00 20 00 69 00 6E 00 20 00 " + + "36 00 30 00 25 00 20 00 50 00 45 00 47 00 20 00 " + + "32 00 30 00 30 00 0A 00 46 00 20 00 3D 00 61 00 " + + "62 00 63 00"); + RecordInputStream in = TestcaseRecordInputStream.create(SeriesTextRecord.sid, data); + SeriesTextRecord str; + try { + str = new SeriesTextRecord(in); + } catch (RecordFormatException e) { + if (e.getCause() instanceof IllegalArgumentException) { + // 'would be' error msg changed at svn r703620 + // "Illegal length - asked for -126 but only 130 left!" + // "Bad requested string length (-126)" + throw new AssertionFailedError("Identified bug 45784a"); + } + throw e; + } - byte [] recordBytes = record.serialize(); - assertEquals(recordBytes.length - 4, data.length); - for (int i = 0; i < data.length; i++) - assertEquals("At offset " + i, data[i], recordBytes[i+4]); - } + if (str.getRecordSize() < 0) { + throw new AssertionFailedError("Identified bug 45784b"); + } + byte[] ser; + try { + ser = str.serialize(); + } catch (Exception e) { + throw new RuntimeException(e); + } + TestcaseRecordInputStream.confirmRecordEncoding(SeriesTextRecord.sid, data, ser); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 08a700466c..62cdd5ea20 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -1479,9 +1479,10 @@ public final class TestBugs extends TestCase { * Charts with long titles */ public void test45784() { - // This used to break + // This used to break HSSFWorkbook wb = openSample("45784.xls"); assertEquals(1, wb.getNumberOfSheets()); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); } /** -- 2.39.5