From 0993370781a202c67c573ee0695079df077d1d94 Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Thu, 6 Nov 2008 20:26:01 +0000 Subject: [PATCH] Merged revisions 711764 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r711764 | josh | 2008-11-05 19:43:17 -0800 (Wed, 05 Nov 2008) | 1 line Bug 45784 - More fixes to SeriesTextRecord ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@711963 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 1 + src/documentation/content/xdocs/status.xml | 1 + .../poi/hssf/record/SeriesTextRecord.java | 242 +++++++----------- .../poi/hssf/record/TestSeriesTextRecord.java | 101 +++++--- .../apache/poi/hssf/usermodel/TestBugs.java | 3 +- 5 files changed, 170 insertions(+), 178 deletions(-) diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index eae6071b3..e448fa313 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 95d926bb9..adfb211b3 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 d1ca886ff..15e454993 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 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() - { + 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 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 sb = new StringBuffer(); - public String toString() - { - StringBuffer buffer = 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(); + } - 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")); + 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); - buffer.append("[/SERIESTEXT]\n"); - return buffer.toString(); - } + 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; + } - public int serialize(int offset, byte[] data) - { - int pos = 0; + protected int getDataSize() { + return 2 + 1 + 1 + field_4_text.length() * (is16bit ? 2 : 1); + } - LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4)); + public short getSid() { + return sid; + } - 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); + public Object clone() { + SeriesTextRecord rec = new SeriesTextRecord(); - return getRecordSize(); - } + rec.field_1_id = field_1_id; + rec.is16bit = is16bit; + rec.field_4_text = field_4_text; + return rec; + } - protected int getDataSize() { - return 2 + 1 + 1 + (field_2_textLength *2); - } + /** + * Get the id field for the SeriesText record. + */ + public int getId() { + return field_1_id; + } - public short getSid() - { - return sid; - } + /** + * Set the id field for the SeriesText record. + */ + public void setId(int id) { + field_1_id = id; + } - 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 text field for the SeriesText record. + */ + public String getText() { + return field_4_text; + } - - - - /** - * 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; - } + /** + * 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 a016e60e9..8b05dc433 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, data)); + public void testLoad() { + SeriesTextRecord record = new SeriesTextRecord(TestcaseRecordInputStream.create(0x100d, SIMPLE_DATA)); - assertEquals( (short)0, record.getId()); - assertEquals( (byte)0x0C, record.getTextLength()); - assertEquals( (byte)0x01, record.getUndocumented()); - assertEquals( "Value Number", record.getText()); + assertEquals((short) 0, record.getId()); + assertEquals("Value Number", record.getText()); - assertEquals( 32, record.getRecordSize() ); - } + assertEquals(SIMPLE_DATA.length + 4, record.getRecordSize()); + } - public void testStore() - { - SeriesTextRecord record = new SeriesTextRecord(); + public void testStore() { + SeriesTextRecord record = new SeriesTextRecord(); - record.setId( (short)0 ); - record.setTextLength( (byte)0x0C ); - record.setUndocumented( (byte)0x01 ); - record.setText( "Value Number" ); + record.setId(0); + record.setText("Value Number"); + byte[] recordBytes = record.serialize(); + TestcaseRecordInputStream.confirmRecordEncoding(SeriesTextRecord.sid, SIMPLE_DATA, + recordBytes); + } - 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]); - } + 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; + } + + 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 4c8604b69..4ca87428a 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -1481,9 +1481,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); } /**