From aac81d881b18caebd45ec30294a088bb7e2d8767 Mon Sep 17 00:00:00 2001 From: Glen Stampoultzis Date: Wed, 29 May 2002 14:14:17 +0000 Subject: [PATCH] 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 --- .../poi/hssf/record/RecordProcessor.java | 60 ++ .../org/apache/poi/hssf/record/SSTRecord.java | 84 ++- .../apache/poi/hssf/record/SSTSerializer.java | 60 ++ .../apache/poi/hssf/record/UnicodeString.java | 62 +- .../org/apache/poi/hssf/data/duprich1.xls | Bin 0 -> 21504 bytes .../org/apache/poi/hssf/data/duprich2.xls | Bin 0 -> 13824 bytes .../apache/poi/hssf/record/TestSSTRecord.java | 588 ++++++++++-------- 7 files changed, 509 insertions(+), 345 deletions(-) create mode 100644 src/testcases/org/apache/poi/hssf/data/duprich1.xls create mode 100644 src/testcases/org/apache/poi/hssf/data/duprich2.xls diff --git a/src/java/org/apache/poi/hssf/record/RecordProcessor.java b/src/java/org/apache/poi/hssf/record/RecordProcessor.java index 06eb3646c..13bfc3a18 100644 --- a/src/java/org/apache/poi/hssf/record/RecordProcessor.java +++ b/src/java/org/apache/poi/hssf/record/RecordProcessor.java @@ -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 + * . + */ + 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; diff --git a/src/java/org/apache/poi/hssf/record/SSTRecord.java b/src/java/org/apache/poi/hssf/record/SSTRecord.java index 07054a577..582020a02 100644 --- a/src/java/org/apache/poi/hssf/record/SSTRecord.java +++ b/src/java/org/apache/poi/hssf/record/SSTRecord.java @@ -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() + " " ); } } } diff --git a/src/java/org/apache/poi/hssf/record/SSTSerializer.java b/src/java/org/apache/poi/hssf/record/SSTSerializer.java index 5ee3af098..580227914 100644 --- a/src/java/org/apache/poi/hssf/record/SSTSerializer.java +++ b/src/java/org/apache/poi/hssf/record/SSTSerializer.java @@ -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 + * . + */ + 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 { diff --git a/src/java/org/apache/poi/hssf/record/UnicodeString.java b/src/java/org/apache/poi/hssf/record/UnicodeString.java index c303a42e3..2d6881525 100644 --- a/src/java/org/apache/poi/hssf/record/UnicodeString.java +++ b/src/java/org/apache/poi/hssf/record/UnicodeString.java @@ -66,6 +66,7 @@ import org.apache.poi.util.StringUtil; * REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

* @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()); -// } } diff --git a/src/testcases/org/apache/poi/hssf/data/duprich1.xls b/src/testcases/org/apache/poi/hssf/data/duprich1.xls new file mode 100644 index 0000000000000000000000000000000000000000..3fddbedd27b9f4e5fdcb36af6acf4367b03aa081 GIT binary patch literal 21504 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 B5^(?k literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/data/duprich2.xls b/src/testcases/org/apache/poi/hssf/data/duprich2.xls new file mode 100644 index 0000000000000000000000000000000000000000..57af63b3d4db728347a498037ee0f02a0211a2e4 GIT binary patch literal 13824 zcmeHOdx%t382{bbncZ0*`|^?6I!v?1KHN1QrP8wQlo|$EqJ}~f)4Ds`ExTq zedjy(cka3OeCOW#^;eCf2i}`=7G+@ts&J`Li)tU=VSctLH-Pnpf)q_Ui+Qqa_zxja z8|FY(hj6L+bj_D>W;w`b5x~2_6O4hMND1sj6R<9m8cby}se$OqUD;fEdnyyXap7L= zL!TpkIL(K5oIWf}Zb_1qYWwnvuEQBx( zh|P<&bhflC>`rusV4+{1rAV*^VMIzL%34ve zyU>A!KvIl%M71Z$F1IgUu~-}Him@))6;F0Wf}Lnbt0;P63143LCGtJ!1c%jch#e@i z<#If8y}h2nJ24-3_zW8DqJ5*PX8n52IEY(3Ad>Cfi8g~o2Mt{t%Q8xG&HAgXBXXM|7 zsHSdoJSu#dtjhn(R9&4flk7_JmH5)?mEY^2RxaOYLe#|UKLv@U=nY&Yp5i~Cz0Ieb3@7Vd2+y=2dvgsUZLQV z?Eh4KnoVImfO^e0%7X+AZ1ck5KfYPJWnFJl`5nriukmxr$y3IJ+leRGvu9azA}F?I zke*lR-5RgOQZ{@apNS(z={iy;h~SUd>{PWn#MZ&DlhWY<&7B;Jfd`O6jz@}5;hPs$ z4fg4&6&C?F&+r7dbbt>vhu5b&;sLI;WVIz>wAZ8#9_CfX>oHKLEY}B-kq8I4T4iu# z-e{&?c~U6n$(3KulQ~w-lWV=47b@dj)VT=GDv_3wSghArN@7c9oLeVW>bX*NOQJD@ z>od0`X*~(}mhiJv)n@5VHOBXs65O@0#F#V826Z5Gr6FWpey$qXCa^V*;19~x>n`cfG4Ti^elz8#K{Wbq{Ragt2!H6&R*43W4`fZZfnzw9S* zy3sp43D<$<58v zT|LsD+NS-%%jagdee$UjXFWb!pZTTwb4TvPHdq8K0u}*_fJML}U=gqgSOhEr76FTZ zMd1I8z}5W!z?su$_Aj`#Vc)Ct|2xmWE&l&^q*{{r|LaJym${k5x1!w11mrc!Hj?<~ zIglD`pK%k-5($(Hdq8K0u}*_fJML}U=gqgSOhEr76FUEghoJo zZgGvp2j**e{Z6U)J){2DA-=wRVkB;}`0V1p%U4> 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(); + } }