SST Rich Text Fix.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/branches/REL_1_5_BRANCH@352656 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Glen Stampoultzis 2002-05-29 14:14:17 +00:00
parent 9a47a0c1ea
commit aac81d881b
7 changed files with 509 additions and 345 deletions

View File

@ -1,8 +1,68 @@
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache POI" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache POI", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.poi.hssf.record;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndian;
/**
* Process a single record. That is, an SST record or a continue record.
* Refactored from code originally in SSTRecord.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
class RecordProcessor
{
private byte[] data;

View File

@ -58,7 +58,6 @@ import org.apache.poi.util.BinaryTree;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -259,7 +258,8 @@ public class SSTRecord
rval = field_3_strings.size();
field_2_num_unique_strings++;
integer = new Integer( rval );
field_3_strings.put( integer, ucs );
addToStringTable( integer, ucs );
// field_3_strings.put( integer, ucs );
}
return rval;
}
@ -324,11 +324,10 @@ public class SSTRecord
public String getString( final int id )
{
return ( (UnicodeString) field_3_strings.get( new Integer( id ) ) )
.getString();
return ( (UnicodeString) field_3_strings.get( new Integer( id ) ) ).getString();
}
public boolean getString16bit( final int id )
public boolean isString16bit( final int id )
{
UnicodeString unicodeString = ( (UnicodeString) field_3_strings.get( new Integer( id ) ) );
return ( ( unicodeString.getOptionFlags() & 0x01 ) == 1 );
@ -456,7 +455,8 @@ public class SSTRecord
_unfinished_string );
Integer integer = new Integer( field_3_strings.size() );
field_3_strings.put( integer, string );
// field_3_strings.put( integer, string );
addToStringTable( integer, string );
manufactureStrings( record,
_total_length_bytes
- LittleEndianConsts
@ -725,35 +725,27 @@ public class SSTRecord
private void setupStringParameters( final byte[] data, final int index,
final int char_count )
{
byte flag = data[index + LittleEndianConsts.SHORT_SIZE];
byte optionFlag = data[index + LittleEndianConsts.SHORT_SIZE];
_wide_char = ( flag & 1 ) == 1;
boolean extended = ( flag & 4 ) == 4;
boolean formatted_run = ( flag & 8 ) == 8;
_wide_char = ( optionFlag & 1 ) == 1;
boolean extended = ( optionFlag & 4 ) == 4;
boolean rich_text = ( optionFlag & 8 ) == 8;
_total_length_bytes = STRING_MINIMAL_OVERHEAD
+ calculateByteCount( char_count );
_total_length_bytes = STRING_MINIMAL_OVERHEAD + calculateByteCount( char_count );
_string_data_offset = STRING_MINIMAL_OVERHEAD;
if ( formatted_run )
if ( rich_text )
{
short run_count = LittleEndian.getShort( data,
index
+ _string_data_offset );
short run_count = LittleEndian.getShort( data, index + _string_data_offset );
_string_data_offset += LittleEndianConsts.SHORT_SIZE;
_total_length_bytes += LittleEndianConsts.SHORT_SIZE
+ ( LittleEndianConsts.INT_SIZE
* run_count );
_total_length_bytes += LittleEndianConsts.SHORT_SIZE + ( LittleEndianConsts.INT_SIZE * run_count );
}
if ( extended )
{
int extension_length = LittleEndian.getInt( data,
index
+ _string_data_offset );
int extension_length = LittleEndian.getInt( data, index + _string_data_offset );
_string_data_offset += LittleEndianConsts.INT_SIZE;
_total_length_bytes += LittleEndianConsts.INT_SIZE
+ extension_length;
_total_length_bytes += LittleEndianConsts.INT_SIZE + extension_length;
}
}
@ -771,6 +763,7 @@ public class SSTRecord
LittleEndian.putShort( bstring, offset, char_count );
offset += LittleEndianConsts.SHORT_SIZE;
bstring[offset] = str_data[offset];
System.out.println( "_string_data_offset = " + _string_data_offset );
System.arraycopy( str_data, _string_data_offset, bstring,
STRING_MINIMAL_OVERHEAD,
bstring.length - STRING_MINIMAL_OVERHEAD );
@ -785,27 +778,32 @@ public class SSTRecord
else
{
Integer integer = new Integer( field_3_strings.size() );
addToStringTable( integer, string );
}
}
// This retry loop is a nasty hack that lets us get around the issue of duplicate
// strings in the SST record. There should never be duplicates but because we don't
// handle rich text records correctly this may occur. Also some Excel alternatives
// do not seem correctly add strings to this table.
//
// The hack bit is that we add spaces to the end of the string until don't get an
// illegal argument exception when adding. One day we will have to deal with this
// more gracefully.
boolean added = false;
while ( !added )
/**
* Okay, we are doing some major cheating here. Because we can't handle rich text strings properly
* we end up getting duplicate strings. To get around this I'm doing do things: 1. Converting rich
* text to normal text and 2. If there's a duplicate I'm adding a space onto the end. Sneaky perhaps
* but it gets the job done until we can handle this a little better.
*/
private void addToStringTable( Integer integer, UnicodeString string )
{
if (string.isRichText())
string.setOptionFlags( (byte)(string.getOptionFlags() & (~8) ) );
boolean added = false;
while (added == false)
{
try
{
try
{
field_3_strings.put( integer, string );
added = true;
}
catch ( IllegalArgumentException duplicateValue )
{
string.setString( string.getString() + " " );
}
field_3_strings.put( integer, string );
added = true;
}
catch( Exception ignore )
{
string.setString( string.getString() + " " );
}
}
}

View File

@ -1,3 +1,57 @@
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache POI" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache POI", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.poi.hssf.record;
import org.apache.poi.util.BinaryTree;
@ -6,6 +60,12 @@ import org.apache.poi.util.LittleEndianConsts;
import java.util.List;
import java.util.ArrayList;
/**
* This class handles serialization of SST records. It utilizes the record processor
* class write individual records. This has been refactored from the SSTRecord class.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
class SSTSerializer
{

View File

@ -66,6 +66,7 @@ import org.apache.poi.util.StringUtil;
* REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
* @author Andrew C. Oliver
* @author Marc Johnson (mjohnson at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
* @version 2.0-pre
*/
@ -77,12 +78,28 @@ public class UnicodeString
private short field_1_charCount; // = 0;
private byte field_2_optionflags; // = 0;
private String field_3_string; // = null;
private final int RICH_TEXT_BIT = 8;
public UnicodeString()
{
}
public int hashCode()
{
return field_1_charCount;
int stringHash = 0;
if (field_3_string != null)
stringHash = field_3_string.hashCode();
return field_1_charCount + stringHash;
}
/**
* Our handling of equals is inconsistent with compareTo. The trouble is because we don't truely understand
* rich text fields yet it's difficult to make a sound comparison.
*
* @param o The object to compare.
* @return true if the object is actually equal.
*/
public boolean equals(Object o)
{
if ((o == null) || (o.getClass() != this.getClass()))
@ -96,10 +113,6 @@ public class UnicodeString
&& field_3_string.equals(other.field_3_string));
}
public UnicodeString()
{
}
/**
* construct a unicode string record and fill its fields, ID is ignored
* @param id - ignored
@ -278,20 +291,10 @@ public class UnicodeString
public int serialize(int offset, byte [] data)
{
int charsize = 1;
// Note: I suspect this may not be right
if ((getOptionFlags() & 0x01) == 1)
{
charsize = 2;
}
// byte[] retval = new byte[ 3 + (getString().length() * charsize) ];
LittleEndian.putShort(data, 0 + offset, getCharCount());
data[ 2 + offset ] = getOptionFlags();
// System.out.println("Unicode: We've got "+retval[2]+" for our option flag");
if ((getOptionFlags() & 0x01) == 0)
if (!isUncompressedUnicode())
{
StringUtil.putCompressedUnicode(getString(), data, 0x3 + offset);
}
@ -303,14 +306,14 @@ public class UnicodeString
return getRecordSize();
}
private boolean isUncompressedUnicode()
{
return (getOptionFlags() & 0x01) == 1;
}
public int getRecordSize()
{
int charsize = 1;
if ((getOptionFlags() & 0x01) == 1)
{
charsize = 2;
}
int charsize = isUncompressedUnicode() ? 2 : 1;
return 3 + (getString().length() * charsize);
}
@ -339,11 +342,16 @@ public class UnicodeString
return this.getString().compareTo(str.getString());
}
public boolean isRichText()
{
return (getOptionFlags() & RICH_TEXT_BIT) != 0;
}
int maxBrokenLength(final int proposedBrokenLength)
{
int rval = proposedBrokenLength;
if ((field_2_optionflags & 1) == 1)
if (isUncompressedUnicode())
{
int proposedStringLength = proposedBrokenLength - 3;
@ -356,12 +364,4 @@ public class UnicodeString
return rval;
}
// public boolean equals(Object obj) {
// if (!(obj instanceof UnicodeString)) return false;
//
// UnicodeString str = (UnicodeString)obj;
//
//
// return this.getString().equals(str.getString());
// }
}

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,3 @@
/* ====================================================================
* The Apache Software License, Version 1.1
*
@ -55,24 +54,29 @@
package org.apache.poi.hssf.record;
import org.apache.poi.util.*;
import junit.framework.*;
import junit.framework.TestCase;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import java.io.*;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* @author Marc Johnson (mjohnson at apache dot org)
*/
public class TestSSTRecord
extends TestCase
extends TestCase
{
private String _test_file_path;
private String _test_file_path;
private static final String _test_file_path_property =
"HSSF.testdata.path";
"HSSF.testdata.path";
/**
* Creates new TestSSTRecord
@ -80,10 +84,10 @@ public class TestSSTRecord
* @param name
*/
public TestSSTRecord(String name)
public TestSSTRecord( String name )
{
super(name);
_test_file_path = System.getProperty(_test_file_path_property);
super( name );
_test_file_path = System.getProperty( _test_file_path_property );
}
/**
@ -93,118 +97,118 @@ public class TestSSTRecord
*/
public void testProcessContinueRecord()
throws IOException
throws IOException
{
byte[] testdata = readTestData("BigSSTRecord");
byte[] input = new byte[ testdata.length - 4 ];
byte[] testdata = readTestData( "BigSSTRecord" );
byte[] input = new byte[testdata.length - 4];
System.arraycopy(testdata, 4, input, 0, input.length);
SSTRecord record =
new SSTRecord(LittleEndian.getShort(testdata, 0),
LittleEndian.getShort(testdata, 2), input);
byte[] continueRecord = readTestData("BigSSTRecordCR");
System.arraycopy( testdata, 4, input, 0, input.length );
SSTRecord record =
new SSTRecord( LittleEndian.getShort( testdata, 0 ),
LittleEndian.getShort( testdata, 2 ), input );
byte[] continueRecord = readTestData( "BigSSTRecordCR" );
input = new byte[ continueRecord.length - 4 ];
System.arraycopy(continueRecord, 4, input, 0, input.length);
record.processContinueRecord(input);
assertEquals(1464, record.getNumStrings());
assertEquals(688, record.getNumUniqueStrings());
assertEquals(688, record.countStrings());
input = new byte[continueRecord.length - 4];
System.arraycopy( continueRecord, 4, input, 0, input.length );
record.processContinueRecord( input );
assertEquals( 1464, record.getNumStrings() );
assertEquals( 688, record.getNumUniqueStrings() );
assertEquals( 688, record.countStrings() );
byte[] ser_output = record.serialize();
int offset = 0;
short type = LittleEndian.getShort(ser_output, offset);
int offset = 0;
short type = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
short length = LittleEndian.getShort(ser_output, offset);
short length = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
byte[] recordData = new byte[ length ];
byte[] recordData = new byte[length];
System.arraycopy(ser_output, offset, recordData, 0, length);
System.arraycopy( ser_output, offset, recordData, 0, length );
offset += length;
SSTRecord testRecord = new SSTRecord(type, length, recordData);
SSTRecord testRecord = new SSTRecord( type, length, recordData );
assertEquals(ContinueRecord.sid,
LittleEndian.getShort(ser_output, offset));
assertEquals( ContinueRecord.sid,
LittleEndian.getShort( ser_output, offset ) );
offset += LittleEndianConsts.SHORT_SIZE;
length = LittleEndian.getShort(ser_output, offset);
length = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
byte[] cr = new byte[ length ];
byte[] cr = new byte[length];
System.arraycopy(ser_output, offset, cr, 0, length);
System.arraycopy( ser_output, offset, cr, 0, length );
offset += length;
assertEquals(offset, ser_output.length);
testRecord.processContinueRecord(cr);
assertEquals(record, testRecord);
assertEquals( offset, ser_output.length );
testRecord.processContinueRecord( cr );
assertEquals( record, testRecord );
// testing based on new bug report
testdata = readTestData("BigSSTRecord2");
input = new byte[ testdata.length - 4 ];
System.arraycopy(testdata, 4, input, 0, input.length);
record = new SSTRecord(LittleEndian.getShort(testdata, 0),
LittleEndian.getShort(testdata, 2), input);
byte[] continueRecord1 = readTestData("BigSSTRecord2CR1");
testdata = readTestData( "BigSSTRecord2" );
input = new byte[testdata.length - 4];
System.arraycopy( testdata, 4, input, 0, input.length );
record = new SSTRecord( LittleEndian.getShort( testdata, 0 ),
LittleEndian.getShort( testdata, 2 ), input );
byte[] continueRecord1 = readTestData( "BigSSTRecord2CR1" );
input = new byte[ continueRecord1.length - 4 ];
System.arraycopy(continueRecord1, 4, input, 0, input.length);
record.processContinueRecord(input);
byte[] continueRecord2 = readTestData("BigSSTRecord2CR2");
input = new byte[continueRecord1.length - 4];
System.arraycopy( continueRecord1, 4, input, 0, input.length );
record.processContinueRecord( input );
byte[] continueRecord2 = readTestData( "BigSSTRecord2CR2" );
input = new byte[ continueRecord2.length - 4 ];
System.arraycopy(continueRecord2, 4, input, 0, input.length);
record.processContinueRecord(input);
byte[] continueRecord3 = readTestData("BigSSTRecord2CR3");
input = new byte[continueRecord2.length - 4];
System.arraycopy( continueRecord2, 4, input, 0, input.length );
record.processContinueRecord( input );
byte[] continueRecord3 = readTestData( "BigSSTRecord2CR3" );
input = new byte[ continueRecord3.length - 4 ];
System.arraycopy(continueRecord3, 4, input, 0, input.length);
record.processContinueRecord(input);
byte[] continueRecord4 = readTestData("BigSSTRecord2CR4");
input = new byte[continueRecord3.length - 4];
System.arraycopy( continueRecord3, 4, input, 0, input.length );
record.processContinueRecord( input );
byte[] continueRecord4 = readTestData( "BigSSTRecord2CR4" );
input = new byte[ continueRecord4.length - 4 ];
System.arraycopy(continueRecord4, 4, input, 0, input.length);
record.processContinueRecord(input);
byte[] continueRecord5 = readTestData("BigSSTRecord2CR5");
input = new byte[continueRecord4.length - 4];
System.arraycopy( continueRecord4, 4, input, 0, input.length );
record.processContinueRecord( input );
byte[] continueRecord5 = readTestData( "BigSSTRecord2CR5" );
input = new byte[ continueRecord5.length - 4 ];
System.arraycopy(continueRecord5, 4, input, 0, input.length);
record.processContinueRecord(input);
byte[] continueRecord6 = readTestData("BigSSTRecord2CR6");
input = new byte[continueRecord5.length - 4];
System.arraycopy( continueRecord5, 4, input, 0, input.length );
record.processContinueRecord( input );
byte[] continueRecord6 = readTestData( "BigSSTRecord2CR6" );
input = new byte[ continueRecord6.length - 4 ];
System.arraycopy(continueRecord6, 4, input, 0, input.length);
record.processContinueRecord(input);
byte[] continueRecord7 = readTestData("BigSSTRecord2CR7");
input = new byte[continueRecord6.length - 4];
System.arraycopy( continueRecord6, 4, input, 0, input.length );
record.processContinueRecord( input );
byte[] continueRecord7 = readTestData( "BigSSTRecord2CR7" );
input = new byte[ continueRecord7.length - 4 ];
System.arraycopy(continueRecord7, 4, input, 0, input.length);
record.processContinueRecord(input);
assertEquals(158642, record.getNumStrings());
assertEquals(5249, record.getNumUniqueStrings());
assertEquals(5249, record.countStrings());
input = new byte[continueRecord7.length - 4];
System.arraycopy( continueRecord7, 4, input, 0, input.length );
record.processContinueRecord( input );
assertEquals( 158642, record.getNumStrings() );
assertEquals( 5249, record.getNumUniqueStrings() );
assertEquals( 5249, record.countStrings() );
ser_output = record.serialize();
offset = 0;
type = LittleEndian.getShort(ser_output, offset);
offset += LittleEndianConsts.SHORT_SIZE;
length = LittleEndian.getShort(ser_output, offset);
offset += LittleEndianConsts.SHORT_SIZE;
recordData = new byte[ length ];
System.arraycopy(ser_output, offset, recordData, 0, length);
offset += length;
testRecord = new SSTRecord(type, length, recordData);
for (int count = 0; count < 7; count++)
offset = 0;
type = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
length = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
recordData = new byte[length];
System.arraycopy( ser_output, offset, recordData, 0, length );
offset += length;
testRecord = new SSTRecord( type, length, recordData );
for ( int count = 0; count < 7; count++ )
{
assertEquals(ContinueRecord.sid,
LittleEndian.getShort(ser_output, offset));
assertEquals( ContinueRecord.sid,
LittleEndian.getShort( ser_output, offset ) );
offset += LittleEndianConsts.SHORT_SIZE;
length = LittleEndian.getShort(ser_output, offset);
length = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
cr = new byte[ length ];
System.arraycopy(ser_output, offset, cr, 0, length);
testRecord.processContinueRecord(cr);
cr = new byte[length];
System.arraycopy( ser_output, offset, cr, 0, length );
testRecord.processContinueRecord( cr );
offset += length;
}
assertEquals(offset, ser_output.length);
assertEquals(record, testRecord);
assertEquals( offset, ser_output.length );
assertEquals( record, testRecord );
}
/**
@ -214,23 +218,23 @@ public class TestSSTRecord
*/
public void testHugeStrings()
throws IOException
throws IOException
{
SSTRecord record = new SSTRecord();
byte[][] bstrings =
{
new byte[ 9000 ], new byte[ 7433 ], new byte[ 9002 ],
new byte[ 16998 ]
};
String[] strings = new String[ bstrings.length ];
int total_length = 0;
SSTRecord record = new SSTRecord();
byte[][] bstrings =
{
new byte[9000], new byte[7433], new byte[9002],
new byte[16998]
};
String[] strings = new String[bstrings.length];
int total_length = 0;
for (int k = 0; k < bstrings.length; k++)
for ( int k = 0; k < bstrings.length; k++ )
{
Arrays.fill(bstrings[ k ], ( byte ) ('a' + k));
strings[ k ] = new String(bstrings[ k ]);
record.addString(strings[ k ]);
total_length += 3 + bstrings[ k ].length;
Arrays.fill( bstrings[k], (byte) ( 'a' + k ) );
strings[k] = new String( bstrings[k] );
record.addString( strings[k] );
total_length += 3 + bstrings[k].length;
}
// add overhead of SST record
@ -240,88 +244,88 @@ public class TestSSTRecord
total_length += 4;
// add overhead of six records
total_length += (6 * 4);
byte[] content = new byte[ record.getRecordSize() ];
total_length += ( 6 * 4 );
byte[] content = new byte[record.getRecordSize()];
record.serialize(0, content);
assertEquals(total_length, content.length);
for (int index = 0; index != content.length; )
record.serialize( 0, content );
assertEquals( total_length, content.length );
for ( int index = 0; index != content.length; )
{
short record_type = LittleEndian.getShort(content, index);
short record_type = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
short record_length = LittleEndian.getShort(content, index);
short record_length = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
byte[] data = new byte[ record_length ];
byte[] data = new byte[record_length];
System.arraycopy(content, index, data, 0, record_length);
System.arraycopy( content, index, data, 0, record_length );
index += record_length;
if (record_type == SSTRecord.sid)
if ( record_type == SSTRecord.sid )
{
record = new SSTRecord(record_type, record_length, data);
record = new SSTRecord( record_type, record_length, data );
}
else
{
record.processContinueRecord(data);
record.processContinueRecord( data );
}
}
assertEquals(strings.length, record.getNumStrings());
assertEquals(strings.length, record.getNumUniqueStrings());
assertEquals(strings.length, record.countStrings());
for (int k = 0; k < strings.length; k++)
assertEquals( strings.length, record.getNumStrings() );
assertEquals( strings.length, record.getNumUniqueStrings() );
assertEquals( strings.length, record.countStrings() );
for ( int k = 0; k < strings.length; k++ )
{
assertEquals(strings[ k ], record.getString(k));
assertEquals( strings[k], record.getString( k ) );
}
record = new SSTRecord();
bstrings[ 1 ] = new byte[ bstrings[ 1 ].length - 1 ];
for (int k = 0; k < bstrings.length; k++)
record = new SSTRecord();
bstrings[1] = new byte[bstrings[1].length - 1];
for ( int k = 0; k < bstrings.length; k++ )
{
if ((bstrings[ k ].length % 2) == 1)
if ( ( bstrings[k].length % 2 ) == 1 )
{
Arrays.fill(bstrings[ k ], ( byte ) ('a' + k));
strings[ k ] = new String(bstrings[ k ]);
Arrays.fill( bstrings[k], (byte) ( 'a' + k ) );
strings[k] = new String( bstrings[k] );
}
else
{
char[] data = new char[ bstrings[ k ].length / 2 ];
char[] data = new char[bstrings[k].length / 2];
Arrays.fill(data, ( char ) ('\u2122' + k));
strings[ k ] = new String(data);
Arrays.fill( data, (char) ( '\u2122' + k ) );
strings[k] = new String( data );
}
record.addString(strings[ k ]);
record.addString( strings[k] );
}
content = new byte[ record.getRecordSize() ];
record.serialize(0, content);
content = new byte[record.getRecordSize()];
record.serialize( 0, content );
total_length--;
assertEquals(total_length, content.length);
for (int index = 0; index != content.length; )
assertEquals( total_length, content.length );
for ( int index = 0; index != content.length; )
{
short record_type = LittleEndian.getShort(content, index);
short record_type = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
short record_length = LittleEndian.getShort(content, index);
short record_length = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
byte[] data = new byte[ record_length ];
byte[] data = new byte[record_length];
System.arraycopy(content, index, data, 0, record_length);
System.arraycopy( content, index, data, 0, record_length );
index += record_length;
if (record_type == SSTRecord.sid)
if ( record_type == SSTRecord.sid )
{
record = new SSTRecord(record_type, record_length, data);
record = new SSTRecord( record_type, record_length, data );
}
else
{
record.processContinueRecord(data);
record.processContinueRecord( data );
}
}
assertEquals(strings.length, record.getNumStrings());
assertEquals(strings.length, record.getNumUniqueStrings());
assertEquals(strings.length, record.countStrings());
for (int k = 0; k < strings.length; k++)
assertEquals( strings.length, record.getNumStrings() );
assertEquals( strings.length, record.getNumUniqueStrings() );
assertEquals( strings.length, record.countStrings() );
for ( int k = 0; k < strings.length; k++ )
{
assertEquals(strings[ k ], record.getString(k));
assertEquals( strings[k], record.getString( k ) );
}
}
@ -332,7 +336,7 @@ public class TestSSTRecord
*/
public void testSSTRecordBug()
throws IOException
throws IOException
{
// create an SSTRecord and write a certain pattern of strings
@ -342,22 +346,22 @@ public class TestSSTRecord
// the record will start with two integers, then this string
// ... that will eat up 16 of the 8224 bytes that the record
// can hold
record.addString("Hello");
record.addString( "Hello" );
// now we have an additional 8208 bytes, which is an exact
// multiple of 16 bytes
long testvalue = 1000000000000L;
for (int k = 0; k < 2000; k++)
for ( int k = 0; k < 2000; k++ )
{
record.addString(String.valueOf(testvalue++));
record.addString( String.valueOf( testvalue++ ) );
}
byte[] content = new byte[ record.getRecordSize() ];
byte[] content = new byte[record.getRecordSize()];
record.serialize(0, content);
assertEquals(( byte ) 13, content[ 4 + 8228 ]);
assertEquals(( byte ) 13, content[ 4 + 8228 * 2 ]);
assertEquals(( byte ) 13, content[ 4 + 8228 * 3 ]);
record.serialize( 0, content );
assertEquals( (byte) 13, content[4 + 8228] );
assertEquals( (byte) 13, content[4 + 8228 * 2] );
assertEquals( (byte) 13, content[4 + 8228 * 3] );
}
/**
@ -367,43 +371,43 @@ public class TestSSTRecord
public void testSimpleAddString()
{
SSTRecord record = new SSTRecord();
String s1 = "Hello world";
String s1 = "Hello world";
// \u2122 is the encoding of the trademark symbol ...
String s2 = "Hello world\u2122";
String s2 = "Hello world\u2122";
assertEquals(0, record.addString(s1));
assertEquals(s1, record.getString(0));
assertEquals(1, record.countStrings());
assertEquals(1, record.getNumStrings());
assertEquals(1, record.getNumUniqueStrings());
assertEquals(0, record.addString(s1));
assertEquals(s1, record.getString(0));
assertEquals(1, record.countStrings());
assertEquals(2, record.getNumStrings());
assertEquals(1, record.getNumUniqueStrings());
assertEquals(1, record.addString(s2));
assertEquals(s2, record.getString(1));
assertEquals(2, record.countStrings());
assertEquals(3, record.getNumStrings());
assertEquals(2, record.getNumUniqueStrings());
assertEquals( 0, record.addString( s1 ) );
assertEquals( s1, record.getString( 0 ) );
assertEquals( 1, record.countStrings() );
assertEquals( 1, record.getNumStrings() );
assertEquals( 1, record.getNumUniqueStrings() );
assertEquals( 0, record.addString( s1 ) );
assertEquals( s1, record.getString( 0 ) );
assertEquals( 1, record.countStrings() );
assertEquals( 2, record.getNumStrings() );
assertEquals( 1, record.getNumUniqueStrings() );
assertEquals( 1, record.addString( s2 ) );
assertEquals( s2, record.getString( 1 ) );
assertEquals( 2, record.countStrings() );
assertEquals( 3, record.getNumStrings() );
assertEquals( 2, record.getNumUniqueStrings() );
Iterator iter = record.getStrings();
while (iter.hasNext())
while ( iter.hasNext() )
{
UnicodeString ucs = ( UnicodeString ) iter.next();
UnicodeString ucs = (UnicodeString) iter.next();
if (ucs.getString().equals(s1))
if ( ucs.getString().equals( s1 ) )
{
assertEquals(( byte ) 0, ucs.getOptionFlags());
assertEquals( (byte) 0, ucs.getOptionFlags() );
}
else if (ucs.getString().equals(s2))
else if ( ucs.getString().equals( s2 ) )
{
assertEquals(( byte ) 1, ucs.getOptionFlags());
assertEquals( (byte) 1, ucs.getOptionFlags() );
}
else
{
fail("cannot match string: " + ucs.getString());
fail( "cannot match string: " + ucs.getString() );
}
}
}
@ -415,25 +419,25 @@ public class TestSSTRecord
*/
public void testReaderConstructor()
throws IOException
throws IOException
{
byte[] testdata = readTestData("BigSSTRecord");
byte[] input = new byte[ testdata.length - 4 ];
byte[] testdata = readTestData( "BigSSTRecord" );
byte[] input = new byte[testdata.length - 4];
System.arraycopy(testdata, 4, input, 0, input.length);
SSTRecord record = new SSTRecord(LittleEndian.getShort(testdata, 0),
LittleEndian.getShort(testdata, 2),
input);
System.arraycopy( testdata, 4, input, 0, input.length );
SSTRecord record = new SSTRecord( LittleEndian.getShort( testdata, 0 ),
LittleEndian.getShort( testdata, 2 ),
input );
assertEquals(1464, record.getNumStrings());
assertEquals(688, record.getNumUniqueStrings());
assertEquals(492, record.countStrings());
assertEquals(1, record.getExpectedChars());
assertEquals("Consolidated B-24J Liberator The Dragon & His Tai",
record.getUnfinishedString());
assertEquals(52, record.getTotalLength());
assertEquals(3, record.getStringDataOffset());
assertTrue(!record.isWideChar());
assertEquals( 1464, record.getNumStrings() );
assertEquals( 688, record.getNumUniqueStrings() );
assertEquals( 492, record.countStrings() );
assertEquals( 1, record.getExpectedChars() );
assertEquals( "Consolidated B-24J Liberator The Dragon & His Tai",
record.getUnfinishedString() );
assertEquals( 52, record.getTotalLength() );
assertEquals( 3, record.getStringDataOffset() );
assertTrue( !record.isWideChar() );
}
/**
@ -444,26 +448,26 @@ public class TestSSTRecord
{
SSTRecord record = new SSTRecord();
assertEquals(0, record.getNumStrings());
assertEquals(0, record.getNumUniqueStrings());
assertEquals(0, record.countStrings());
assertEquals(0, record.getExpectedChars());
assertEquals("", record.getUnfinishedString());
assertEquals(0, record.getTotalLength());
assertEquals(0, record.getStringDataOffset());
assertTrue(!record.isWideChar());
byte[] output = record.serialize();
assertEquals( 0, record.getNumStrings() );
assertEquals( 0, record.getNumUniqueStrings() );
assertEquals( 0, record.countStrings() );
assertEquals( 0, record.getExpectedChars() );
assertEquals( "", record.getUnfinishedString() );
assertEquals( 0, record.getTotalLength() );
assertEquals( 0, record.getStringDataOffset() );
assertTrue( !record.isWideChar() );
byte[] output = record.serialize();
byte[] expected =
{
( byte ) record.getSid(), ( byte ) (record.getSid() >> 8),
( byte ) 8, ( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0,
( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0
};
{
(byte) record.getSid(), (byte) ( record.getSid() >> 8 ),
(byte) 8, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
(byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0
};
assertEquals(expected.length, output.length);
for (int k = 0; k < expected.length; k++)
assertEquals( expected.length, output.length );
for ( int k = 0; k < expected.length; k++ )
{
assertEquals(String.valueOf(k), expected[ k ], output[ k ]);
assertEquals( String.valueOf( k ), expected[k], output[k] );
}
}
@ -473,87 +477,87 @@ public class TestSSTRecord
* @param ignored_args
*/
public static void main(String [] ignored_args)
public static void main( String[] ignored_args )
{
System.out.println("Testing hssf.record.SSTRecord functionality");
junit.textui.TestRunner.run(TestSSTRecord.class);
System.out.println( "Testing hssf.record.SSTRecord functionality" );
junit.textui.TestRunner.run( TestSSTRecord.class );
}
private byte [] readTestData(String filename)
throws IOException
private byte[] readTestData( String filename )
throws IOException
{
File file = new File(_test_file_path
+ File.separator
+ filename);
FileInputStream stream = new FileInputStream(file);
int characterCount = 0;
byte b = ( byte ) 0;
List bytes = new ArrayList();
boolean done = false;
File file = new File( _test_file_path
+ File.separator
+ filename );
FileInputStream stream = new FileInputStream( file );
int characterCount = 0;
byte b = (byte) 0;
List bytes = new ArrayList();
boolean done = false;
while (!done)
while ( !done )
{
int count = stream.read();
switch (count)
switch ( count )
{
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
b <<= 4;
b += ( byte ) (count - '0');
b += (byte) ( count - '0' );
characterCount++;
if (characterCount == 2)
if ( characterCount == 2 )
{
bytes.add(new Byte(b));
bytes.add( new Byte( b ) );
characterCount = 0;
b = ( byte ) 0;
b = (byte) 0;
}
break;
case 'A' :
case 'B' :
case 'C' :
case 'D' :
case 'E' :
case 'F' :
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
b <<= 4;
b += ( byte ) (count + 10 - 'A');
b += (byte) ( count + 10 - 'A' );
characterCount++;
if (characterCount == 2)
if ( characterCount == 2 )
{
bytes.add(new Byte(b));
bytes.add( new Byte( b ) );
characterCount = 0;
b = ( byte ) 0;
b = (byte) 0;
}
break;
case 'a' :
case 'b' :
case 'c' :
case 'd' :
case 'e' :
case 'f' :
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
b <<= 4;
b += ( byte ) (count + 10 - 'a');
b += (byte) ( count + 10 - 'a' );
characterCount++;
if (characterCount == 2)
if ( characterCount == 2 )
{
bytes.add(new Byte(b));
bytes.add( new Byte( b ) );
characterCount = 0;
b = ( byte ) 0;
b = (byte) 0;
}
break;
case -1 :
case -1:
done = true;
break;
@ -562,13 +566,55 @@ public class TestSSTRecord
}
}
stream.close();
Byte[] polished = ( Byte [] ) bytes.toArray(new Byte[ 0 ]);
byte[] rval = new byte[ polished.length ];
Byte[] polished = (Byte[]) bytes.toArray( new Byte[0] );
byte[] rval = new byte[polished.length];
for (int j = 0; j < polished.length; j++)
for ( int j = 0; j < polished.length; j++ )
{
rval[ j ] = polished[ j ].byteValue();
rval[j] = polished[j].byteValue();
}
return rval;
}
/**
* Tests that workbooks with rich text that duplicates a non rich text cell can be read and written.
*/
public void testReadWriteDuplicatedRichText1()
throws Exception
{
File file = new File( _test_file_path + File.separator + "duprich1.xls" );
InputStream stream = new FileInputStream(file);
HSSFWorkbook wb = new HSSFWorkbook(stream);
stream.close();
HSSFSheet sheet = wb.getSheetAt(1);
assertEquals("01/05 (Wed) ", sheet.getRow(0).getCell((short)8).getStringCellValue());
assertEquals("01/05 (Wed)", sheet.getRow(1).getCell((short)8).getStringCellValue());
file = File.createTempFile("testout", "xls");
FileOutputStream outStream = new FileOutputStream(file);
wb.write(outStream);
outStream.close();
file.delete();
// test the second file.
file = new File( _test_file_path + File.separator + "duprich2.xls" );
stream = new FileInputStream(file);
wb = new HSSFWorkbook(stream);
stream.close();
sheet = wb.getSheetAt(0);
int row = 0;
assertEquals("Testing ", sheet.getRow(row++).getCell((short)0).getStringCellValue());
assertEquals("rich", sheet.getRow(row++).getCell((short)0).getStringCellValue());
assertEquals("text", sheet.getRow(row++).getCell((short)0).getStringCellValue());
assertEquals("strings", sheet.getRow(row++).getCell((short)0).getStringCellValue());
assertEquals("Testing ", sheet.getRow(row++).getCell((short)0).getStringCellValue());
assertEquals("Testing", sheet.getRow(row++).getCell((short)0).getStringCellValue());
// file = new File("/tryme.xls");
file = File.createTempFile("testout", ".xls");
outStream = new FileOutputStream(file);
wb.write(outStream);
outStream.close();
file.delete();
}
}