Hi
Gentlemen,

We are using POI 2.5 (2.5.1 shows the same problem) to parse Excel files
and have encountered a problem with the SSTDeserializer class.

We have solved the problem but we would like to consult with you if our
corrections are acceptable. 
This is also possible that this is a known problem and an official fix
exists?

We'll appreciate any help much.

Reproduce the problem:
Unfortunately we can not provide sample excel file lots of confidential
information and we failed to generate file with the same problem with
synthetic data (detailed description of the problem demonstrating
rareness of the case can be found below)...

Following exception occurs during opening of excel file:
java.lang.NullPointerException
            at
org.apache.poi.hssf.record.SSTRecord.getString(SSTRecord.java:277)
            at
org.apache.poi.hssf.model.Workbook.getSSTString(Workbook.java:649)
            at
org.apache.poi.hssf.usermodel.HSSFCell.<init>(HSSFCell.java:283)
            at
org.apache.poi.hssf.usermodel.HSSFRow.createCellFromRecord(HSSFRow.java:
198)
            at
org.apache.poi.hssf.usermodel.HSSFSheet.setPropertiesFromSheet(HSSFSheet
.java:156)
            at
org.apache.poi.hssf.usermodel.HSSFSheet.<init>(HSSFSheet.java:110)
            at
org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:177)
            at
org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:210)
            at
org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:191)
 
Problem Description:
The problem reproduces when string in CONTINUE RECORD finishes at the
end of record and extra CONTINUE RECORD exists.
The readStringRemainder method reads remainder of current string but
doesn't update continuationCharsRead variable with new length.
As a result when next CONTINUE record is called with
processContinueRecord the function isStringFinished returns invalud
result,and  string is treated as unfinished one.


Solutuion:
 We  suggest to correct this problem by modifying readStringRemainder
code like this 

private void readStringRemainder( final byte[] record )
    {
        int stringRemainderSizeInBytes = calculateByteCount(
charCount-getContinuationCharsRead() );
        byte[] unicodeStringData = new
byte[SSTRecord.STRING_MINIMAL_OVERHEAD
                + stringRemainderSizeInBytes];

        // write the string length
        LittleEndian.putShort( unicodeStringData, 0, (short)
(charCount-getContinuationCharsRead()) );

        // write the options flag
        unicodeStringData[LittleEndianConsts.SHORT_SIZE] =
createOptionByte( wideChar, richText, extendedText );

        // copy the bytes/words making up the string; skipping
        // past all the overhead of the str_data array
        arraycopy( record, LittleEndianConsts.BYTE_SIZE,
unicodeStringData,
                SSTRecord.STRING_MINIMAL_OVERHEAD,
                stringRemainderSizeInBytes );

        // use special constructor to create the final string
        UnicodeString string = new UnicodeString( UnicodeString.sid,
                (short) unicodeStringData.length, unicodeStringData,
                unfinishedString );
        Integer integer = new Integer( strings.size() );

        addToStringTable( strings, integer, string );

        int newOffset = offsetForContinuedRecord(
stringRemainderSizeInBytes );
    

        // ----------------------- CORRECTIONS
BEGIN-------------------------
       /* 
        * This function doesn't update the continuationCharsRead
variable
        * with new string length (unfinished string length + remaining
string length )
        * Because string variable is a concatenation of unfinishedString
and stringRemainder,
        * it 's length is characketers can be used as new value for
continuationCharsRead.
      */

        setContinuationCharsRead(string.getCharCount() );
      
        /*
         * If we didn't reach end of current record we have to call 
         * manufactureStrings to process other strings in this record. 
         * Because of manufactureStrings  checks if end of record is
reached 
         * it can be called unconditionally.
         * But the problem is, manufactureStrings   
         * will call initVars first and reset the continuationCharsRead
value,
         * which is necessary for isStringFinished to work correctly
         * when next processContinueRecord will be called.
         */
        if (newOffset < record.length)
        {
            manufactureStrings( record, newOffset);
        }

        // ----------------------- CORRECTIONS END
-------------------------

    }

 
 

 Thank you.
Sergey.

 

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
Mailing List:    http://jakarta.apache.org/site/mail2.html#poi
The Apache Jakarta POI Project: http://jakarta.apache.org/poi/

Reply via email to