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)</p>
* Defines a series name</p>
*
* @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);
+ }
}
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);
+ }
}