You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by jo...@apache.org on 2008/10/25 03:47:28 UTC

svn commit: r707807 [1/3] - in /poi/branches/ooxml: ./ src/documentation/content/xdocs/ src/java/org/apache/poi/hssf/dev/ src/java/org/apache/poi/hssf/model/ src/java/org/apache/poi/hssf/record/ src/java/org/apache/poi/hssf/record/formula/ src/java/org...

Author: josh
Date: Fri Oct 24 18:47:25 2008
New Revision: 707807

URL: http://svn.apache.org/viewvc?rev=707807&view=rev
Log:
Merged revisions 703100,703197,703302,703596,703620,703645,703651,706540 via svnmerge from 
https://svn.apache.org/repos/asf/poi/trunk

........
  r703100 | josh | 2008-10-09 01:33:54 -0700 (Thu, 09 Oct 2008) | 1 line
  
  Removed last occurrences of storing Ptg arrays in Stacks.  Some related clean-up.
........
  r703197 | josh | 2008-10-09 09:10:39 -0700 (Thu, 09 Oct 2008) | 1 line
  
  Should have been submitted with r703100 (changes to Ptg)
........
  r703302 | josh | 2008-10-09 17:40:58 -0700 (Thu, 09 Oct 2008) | 1 line
  
  Fix for bug 45964 - support for link formulas in Text Objects
........
  r703596 | josh | 2008-10-10 15:59:14 -0700 (Fri, 10 Oct 2008) | 1 line
  
  Made RecordInputStream final (major clean-up in test cases and BiffViewer)
........
  r703620 | josh | 2008-10-10 18:11:05 -0700 (Fri, 10 Oct 2008) | 2 lines
  
  fix for bug 45866 - allowed for change of unicode compression across Continue records
........
  r703645 | yegor | 2008-10-11 03:31:24 -0700 (Sat, 11 Oct 2008) | 1 line
  
  fixed error in eval.xml: use &lt; instead of '<'
........
  r703651 | yegor | 2008-10-11 05:01:42 -0700 (Sat, 11 Oct 2008) | 1 line
  
  set trunk version.id=3.3-alpha1
........
  r706540 | yegor | 2008-10-20 23:47:35 -0700 (Mon, 20 Oct 2008) | 1 line
  
  updated release version on the index page, started a new section in the change log
........

Added:
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestRecordInputStream.java
      - copied unchanged from r706540, poi/trunk/src/testcases/org/apache/poi/hssf/record/TestRecordInputStream.java
Removed:
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/CustomField.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java
Modified:
    poi/branches/ooxml/   (props changed)
    poi/branches/ooxml/src/documentation/content/xdocs/changes.xml
    poi/branches/ooxml/src/documentation/content/xdocs/index.xml
    poi/branches/ooxml/src/documentation/content/xdocs/status.xml
    poi/branches/ooxml/src/java/org/apache/poi/hssf/dev/BiffViewer.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/model/TextboxShape.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ContinueRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordFactory.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/record/formula/Ptg.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java
    poi/branches/ooxml/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java
    poi/branches/ooxml/src/java/org/apache/poi/ss/formula/SheetRefEvaluator.java   (props changed)
    poi/branches/ooxml/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFChart.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/eventmodel/TestEventRecordFactory.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestAreaFormatRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestAreaRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestAxisLineFormatRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestAxisOptionsRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestAxisParentRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestAxisRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestAxisUsedRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestBarRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestBoundSheetRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestCFHeaderRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestCategorySeriesAxisRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestChartRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestDatRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestDataFormatRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestDefaultDataLabelTextPropertiesRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestEndSubRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestExtendedFormatRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestFontBasisRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestFontIndexRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestFontRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestFrameRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestHyperlinkRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestLegendRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestLineFormatRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestLinkedDataRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestNameRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestNoteRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestNoteStructureSubRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestNumberFormatIndexRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestObjRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestObjectLinkRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestPaneRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestPlotAreaRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestPlotGrowthRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestRecordFactory.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSCLRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSSTDeserializer.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSeriesChartGroupIndexRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSeriesIndexRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSeriesLabelsRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSeriesListRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSeriesRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSeriesTextRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSeriesToChartGroupRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSheetPropertiesRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestStringRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestSupBookRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestTableRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestTextRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestTickRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestUnicodeString.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestUnitsRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestValueRangeRecord.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/TestcaseRecordInputStream.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/constant/TestConstantValueParser.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java
    poi/branches/ooxml/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java

Propchange: poi/branches/ooxml/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Oct 24 18:47:25 2008
@@ -1 +1 @@
-/poi/trunk:693591-694881,695264-695420,695621,695649-703092
+/poi/trunk:693591-694881,695264-695420,695621,695649-706540

Propchange: poi/branches/ooxml/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Fri Oct 24 18:47:25 2008
@@ -1 +1 @@
-/poi/trunk:1-638784,638786-639486,639488-639601,639603-640056,640058-642562,642564-642566,642568-642574,642576-642736,642739-650914,650916-703092
+/poi/trunk:1-638784,638786-639486,639488-639601,639603-640056,640058-642562,642564-642566,642568-642574,642576-642736,642739-650914,650916-706540

Modified: poi/branches/ooxml/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/documentation/content/xdocs/changes.xml?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/documentation/content/xdocs/changes.xml (original)
+++ poi/branches/ooxml/src/documentation/content/xdocs/changes.xml Fri Oct 24 18:47:25 2008
@@ -66,7 +66,12 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.2-alpha1" date="2008-??-??">
+        <release version="3.5-beta4" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">YK: remove me. required to keep Forrest DTD compiler quiet</action>
+        </release>
+        <release version="3.2-FINAL" date="2008-10-19">
+           <action dev="POI-DEVELOPERS" type="fix">45866 - allowed for change of unicode compression across Continue records</action>
+           <action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
            <action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
            <action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
            <action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>

Modified: poi/branches/ooxml/src/documentation/content/xdocs/index.xml
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/documentation/content/xdocs/index.xml?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/documentation/content/xdocs/index.xml (original)
+++ poi/branches/ooxml/src/documentation/content/xdocs/index.xml Fri Oct 24 18:47:25 2008
@@ -44,10 +44,10 @@
        People interested should also follow the
        <link href="mailinglists.html">dev list</link> to track progress.</p>
     </section>
-    <section><title>POI 3.1-FINAL Released (2008-06-29)</title>
+    <section><title>POI 3.2-FINAL Released (2008-10-19)</title>
       <p>
-        The POI team is pleased to announce the release of 3.1 FINAL, the latest release of Apache POI.
-        There have been many important bug fixes since the 3.0.2 release and a lot of new features. 
+        The POI team is pleased to announce the release of 3.2 FINAL, the latest release of Apache POI.
+        There have been many important bug fixes since the 3.1 release and a lot of new features. 
        </p><p>  A full list of changes	is available in
       <link href="./changes.html">the changelog</link>, and 
 		<link href="http://www.apache.org/dyn/closer.cgi/poi/release/">download</link>
@@ -56,7 +56,7 @@
       </p>
       <p>
         The release is also available from the central Maven repository 
-        under Group ID "org.apache.poi" and Version "3.1-FINAL".
+        under Group ID "org.apache.poi" and Version "3.2-FINAL".
       </p>  
     </section>
 

Modified: poi/branches/ooxml/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/documentation/content/xdocs/status.xml?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/documentation/content/xdocs/status.xml (original)
+++ poi/branches/ooxml/src/documentation/content/xdocs/status.xml Fri Oct 24 18:47:25 2008
@@ -63,7 +63,12 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.2-alpha1" date="2008-??-??">
+        <release version="3.5-beta4" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">YK: remove me. required to keep Forrest DTD compiler quiet</action>
+        </release>
+        <release version="3.2-FINAL" date="2008-10-19">
+           <action dev="POI-DEVELOPERS" type="fix">45866 - allowed for change of unicode compression across Continue records</action>
+           <action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
            <action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
            <action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
            <action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/dev/BiffViewer.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/dev/BiffViewer.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/dev/BiffViewer.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/dev/BiffViewer.java Fri Oct 24 18:47:25 2008
@@ -17,17 +17,23 @@
 
 package org.apache.poi.hssf.dev;
 
+import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.PrintStream;
+import java.io.Writer;
 import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.poi.hssf.record.*;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
 
 /**
  *  Utillity for reading in BIFF8 records and displaying data from them.
@@ -37,333 +43,488 @@
  *@see        #main
  */
 public final class BiffViewer {
-    private final File _inputFile;
-    private boolean dump;
-    private final PrintStream _ps;
-
-
-    public BiffViewer(File inFile, PrintStream ps) {
-        _inputFile = inFile;
-        _ps = ps;
-    }
-
-
-    /**
-     *  Method run starts up BiffViewer...
-     */
-    public void run() {
-        try {
-            POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(_inputFile));
-            InputStream stream = fs.createDocumentInputStream("Workbook");
-            createRecords(stream, dump, _ps);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-
-    /**
-     *  Create an array of records from an input stream
-     *
-     *@param  in                         the InputStream from which the records
-     *      will be obtained
-     *@param  dump
-     *@return                            an array of Records created from the
-     *      InputStream
-     *@exception  RecordFormatException  on error processing the InputStream
-     */
-    public static Record[] createRecords(InputStream in, boolean dump, PrintStream ps)
-             throws RecordFormatException {
-        ArrayList records = new ArrayList();
-        RecordDetails activeRecord = null;
-
-        BiffviewRecordInputStream recStream = new BiffviewRecordInputStream(in);
-        while (recStream.hasNextRecord()) {
-            recStream.nextRecord();
-            if (recStream.getSid() != 0) {
-                Record record = createRecord (recStream);
-                    if (record.getSid() != ContinueRecord.sid)
-                    {
-                        records.add(record);
-                        if (activeRecord != null)
-                            activeRecord.dump(ps);
-                        int startPos = (int)(recStream.getPos()-recStream.getLength() - 4);
-                        activeRecord = new RecordDetails(recStream.getSid(), recStream.getLength(), startPos, record);
-                    }
-                    if (dump) {
-                        recStream.dumpBytes(ps);
-                    }
-              }
-        }
-        if (activeRecord != null) {
-            activeRecord.dump(ps);
-        }
-        Record[] retval = new Record[records.size()];
-        records.toArray(retval);
-        return retval;
-    }
-
-
-    /**
-     *  Essentially a duplicate of RecordFactory. Kept separate as not to screw
-     *  up non-debug operations.
-     *
-     */
-    private static Record createRecord( RecordInputStream in )
-    {
-        switch ( in.getSid() )
-        {
-            case AreaFormatRecord.sid:     return new AreaFormatRecord(in);
-            case AreaRecord.sid:           return new AreaRecord(in);
-            case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in);
-            case AxisOptionsRecord.sid:    return new AxisOptionsRecord(in);
-            case AxisParentRecord.sid:     return new AxisParentRecord(in);
-            case AxisRecord.sid:           return new AxisRecord(in);
-            case AxisUsedRecord.sid:       return new AxisUsedRecord(in);
-            case BOFRecord.sid:            return new BOFRecord(in);
-            case BackupRecord.sid:         return new BackupRecord(in);
-            case BarRecord.sid:            return new BarRecord(in);
-            case BeginRecord.sid:          return new BeginRecord(in);
-            case BlankRecord.sid:          return new BlankRecord(in);
-            case BookBoolRecord.sid:       return new BookBoolRecord(in);
-            case BoolErrRecord.sid:        return new BoolErrRecord(in);
-            case BottomMarginRecord.sid:   return new BottomMarginRecord(in);
-            case BoundSheetRecord.sid:     return new BoundSheetRecord(in);
-            case CFHeaderRecord.sid:       return new CFHeaderRecord(in);
-            case CFRuleRecord.sid:         return new CFRuleRecord(in);
-            case CalcCountRecord.sid:      return new CalcCountRecord(in);
-            case CalcModeRecord.sid:       return new CalcModeRecord(in);
-            case CategorySeriesAxisRecord.sid: return new CategorySeriesAxisRecord(in);
-            case ChartFormatRecord.sid:    return new ChartFormatRecord(in);
-            case ChartRecord.sid:          return new ChartRecord(in);
-            case CodepageRecord.sid:       return new CodepageRecord(in);
-            case ColumnInfoRecord.sid:     return new ColumnInfoRecord(in);
-            case ContinueRecord.sid:       return new ContinueRecord(in);
-            case CountryRecord.sid:        return new CountryRecord(in);
-            case DBCellRecord.sid:         return new DBCellRecord(in);
-            case DSFRecord.sid:            return new DSFRecord(in);
-            case DatRecord.sid:            return new DatRecord(in);
-            case DataFormatRecord.sid:     return new DataFormatRecord(in);
-            case DateWindow1904Record.sid: return new DateWindow1904Record(in);
-            case DefaultColWidthRecord.sid:return new DefaultColWidthRecord(in);
-            case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in);
-            case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in);
-            case DeltaRecord.sid:          return new DeltaRecord(in);
-            case DimensionsRecord.sid:     return new DimensionsRecord(in);
-            case DrawingGroupRecord.sid:   return new DrawingGroupRecord(in);
-            case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in);
-            case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in);
-            case DVRecord.sid:             return new DVRecord(in);
-            case DVALRecord.sid:           return new DVALRecord(in);
-            case EOFRecord.sid:            return new EOFRecord(in);
-            case EndRecord.sid:            return new EndRecord(in);
-            case ExtSSTRecord.sid:         return new ExtSSTRecord(in);
-            case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in);
-            case ExternSheetRecord.sid:    return new ExternSheetRecord(in);
-            case FilePassRecord.sid:       return new FilePassRecord(in);
-            case FileSharingRecord.sid:    return new FileSharingRecord(in);
-            case FnGroupCountRecord.sid:   return new FnGroupCountRecord(in);
-            case FontBasisRecord.sid:      return new FontBasisRecord(in);
-            case FontIndexRecord.sid:      return new FontIndexRecord(in);
-            case FontRecord.sid:           return new FontRecord(in);
-            case FooterRecord.sid:         return new FooterRecord(in);
-            case FormatRecord.sid:         return new FormatRecord(in);
-            case FormulaRecord.sid:        return new FormulaRecord(in);
-            case FrameRecord.sid:          return new FrameRecord(in);
-            case GridsetRecord.sid:        return new GridsetRecord(in);
-            case GutsRecord.sid:           return new GutsRecord(in);
-            case HCenterRecord.sid:        return new HCenterRecord(in);
-            case HeaderRecord.sid:         return new HeaderRecord(in);
-            case HideObjRecord.sid:        return new HideObjRecord(in);
-            case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in);
-            case HyperlinkRecord.sid:      return new HyperlinkRecord(in);
-            case IndexRecord.sid:          return new IndexRecord(in);
-            case InterfaceEndRecord.sid:   return new InterfaceEndRecord(in);
-            case InterfaceHdrRecord.sid:   return new InterfaceHdrRecord(in);
-            case IterationRecord.sid:      return new IterationRecord(in);
-            case LabelRecord.sid:          return new LabelRecord(in);
-            case LabelSSTRecord.sid:       return new LabelSSTRecord(in);
-            case LeftMarginRecord.sid:     return new LeftMarginRecord(in);
-            case LegendRecord.sid:         return new LegendRecord(in);
-            case LineFormatRecord.sid:     return new LineFormatRecord(in);
-            case LinkedDataRecord.sid:     return new LinkedDataRecord(in);
-            case MMSRecord.sid:            return new MMSRecord(in);
-            case MergeCellsRecord.sid:     return new MergeCellsRecord(in);
-            case MulBlankRecord.sid:       return new MulBlankRecord(in);
-            case MulRKRecord.sid:          return new MulRKRecord(in);
-            case NameRecord.sid:           return new NameRecord(in);
-            case NoteRecord.sid:           return new NoteRecord(in);
-            case NumberRecord.sid:         return new NumberRecord(in);
-            case ObjRecord.sid:            return new ObjRecord(in);
-            case ObjectLinkRecord.sid:     return new ObjectLinkRecord(in);
-            case PaletteRecord.sid:        return new PaletteRecord(in);
-            case PaneRecord.sid:           return new PaneRecord(in);
-            case PasswordRecord.sid:       return new PasswordRecord(in);
-            case PasswordRev4Record.sid:   return new PasswordRev4Record(in);
-            case PlotAreaRecord.sid:       return new PlotAreaRecord(in);
-            case PlotGrowthRecord.sid:     return new PlotGrowthRecord(in);
-            case PrecisionRecord.sid:      return new PrecisionRecord(in);
-            case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in);
-            case PrintHeadersRecord.sid:   return new PrintHeadersRecord(in);
-            case PrintSetupRecord.sid:     return new PrintSetupRecord(in);
-            case ProtectRecord.sid:        return new ProtectRecord(in);
-            case ProtectionRev4Record.sid: return new ProtectionRev4Record(in);
-            case RKRecord.sid:             return new RKRecord(in);
-            case RefModeRecord.sid:        return new RefModeRecord(in);
-            case RefreshAllRecord.sid:     return new RefreshAllRecord(in);
-            case RightMarginRecord.sid:    return new RightMarginRecord(in);
-            case RowRecord.sid:            return new RowRecord(in);
-            case SCLRecord.sid:            return new SCLRecord(in);
-            case SSTRecord.sid:            return new SSTRecord(in);
-            case SaveRecalcRecord.sid:     return new SaveRecalcRecord(in);
-            case SelectionRecord.sid:      return new SelectionRecord(in);
-            case SeriesIndexRecord.sid:    return new SeriesIndexRecord(in);
-            case SeriesListRecord.sid:     return new SeriesListRecord(in);
-            case SeriesRecord.sid:         return new SeriesRecord(in);
-            case SeriesTextRecord.sid:     return new SeriesTextRecord(in);
-            case SeriesToChartGroupRecord.sid: return new SeriesToChartGroupRecord(in);
-            case SharedFormulaRecord.sid:  return new SharedFormulaRecord(in);
-            case SheetPropertiesRecord.sid:return new SheetPropertiesRecord(in);
-            case StringRecord.sid:         return new StringRecord(in);
-            case StyleRecord.sid:          return new StyleRecord(in);
-            case SupBookRecord.sid:        return new SupBookRecord(in);
-            case TabIdRecord.sid:          return new TabIdRecord(in);
-            case TableRecord.sid:          return new TableRecord(in);
-            case TextObjectRecord.sid:     return new TextObjectRecord(in);
-            case TextRecord.sid:           return new TextRecord(in);
-            case TickRecord.sid:           return new TickRecord(in);
-            case TopMarginRecord.sid:      return new TopMarginRecord(in);
-            case UnitsRecord.sid:          return new UnitsRecord(in);
-            case UseSelFSRecord.sid:       return new UseSelFSRecord(in);
-            case VCenterRecord.sid:        return new VCenterRecord(in);
-            case ValueRangeRecord.sid:     return new ValueRangeRecord(in);
-            case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in);
-            case WSBoolRecord.sid:         return new WSBoolRecord(in);
-            case WindowOneRecord.sid:      return new WindowOneRecord(in);
-            case WindowProtectRecord.sid:  return new WindowProtectRecord(in);
-            case WindowTwoRecord.sid:      return new WindowTwoRecord(in);
-            case WriteAccessRecord.sid:    return new WriteAccessRecord(in);
-            case WriteProtectRecord.sid:   return new WriteProtectRecord(in);        
-        
-        }
-        return new UnknownRecord(in);
-    }
-
-
-    /**
-     *  Method setDump - hex dump out data or not.
-     */
-    public void setDump(boolean dump) {
-        this.dump = dump;
-    }
-
-
-    /**
-     * Method main with 1 argument just run straight biffview against given
-     * file<P>
-     *
-     * with 2 arguments where the second argument is "on" - run biffviewer<P>
-     *
-     * with hex dumps of records <P>
-     *
-     * with 2 arguments where the second argument is "bfd" just run a big fat
-     * hex dump of the file...don't worry about biffviewing it at all
-     * <p>
-     * Define the system property <code>poi.deserialize.escher</code> to turn on
-     * deserialization of escher records.
-     *
-     */
-    public static void main(String[] args) {
-
-        System.setProperty("poi.deserialize.escher", "true");
-
-        if (args.length == 0) {
-            System.out.println( "Biff viewer needs a filename" );
-            return;
-        }
-
-        try {
-            String inFileName = args[0];
-            File inputFile = new File(inFileName);
-            if(!inputFile.exists()) {
-                throw new RuntimeException("specified inputFile '" + inFileName + "' does not exist");
-            }
-            PrintStream ps;
-            if (false) { // set to true to output to file
-                OutputStream os = new FileOutputStream(inFileName + ".out");
-                ps = new PrintStream(os);
-            } else {
-                ps = System.out;
-            }
-            BiffViewer viewer = new BiffViewer(inputFile, ps);
-
-            if (args.length > 1 && args[1].equals("on")) {
-                viewer.setDump(true);
-            }
-            if (args.length > 1 && args[1].equals("bfd")) {
-                POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(inputFile));
-                InputStream stream = fs.createDocumentInputStream("Workbook");
-                int size = stream.available();
-                byte[] data = new byte[size];
-
-                stream.read(data);
-                HexDump.dump(data, 0, System.out, 0);
-            } else {
-                viewer.run();
-            }
-            ps.close();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * This record supports dumping of completed continue records.
-     */
-    private static final class RecordDetails
-    {
-        short rectype, recsize;
-        int startloc;
-        Record record;
-
-        public RecordDetails( short rectype, short recsize, int startloc, Record record )
-        {
-            this.rectype = rectype;
-            this.recsize = recsize;
-            this.startloc = startloc;
-            this.record = record;
-        }
-
-        public short getRectype()
-        {
-            return rectype;
-        }
-
-        public short getRecsize()
-        {
-            return recsize;
-        }
-
-        public Record getRecord()
-        {
-            return record;
-        }
-
-        public void dump(PrintStream ps) {
-            ps.println("Offset 0x" + Integer.toHexString(startloc) + " (" + startloc + ")");
-            ps.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size = " + recsize );
-            ps.println( record.toString() );
-        }
-    }
-
-    private static final class BiffviewRecordInputStream extends RecordInputStream {
-      public BiffviewRecordInputStream(InputStream in) {
-        super(in);
-      }
-      public void dumpBytes(PrintStream ps) {
-        ps.println(HexDump.dump(this.data, 0, this.currentLength));
-      }
-    }
+	static final char[] NEW_LINE_CHARS = System.getProperty("line.separator").toCharArray();
 
+	private BiffViewer() {
+		// no instances of this class
+	}
+
+	/**
+	 *  Create an array of records from an input stream
+	 *
+	 *@param  in the InputStream from which the records will be obtained
+	 *@return an array of Records created from the InputStream
+	 *@exception  RecordFormatException  on error processing the InputStream
+	 */
+	public static Record[] createRecords(InputStream is, PrintStream ps, BiffRecordListener recListener, boolean dumpInterpretedRecords)
+			throws RecordFormatException {
+		ArrayList temp = new ArrayList();
+
+		RecordInputStream recStream = new RecordInputStream(is);
+		while (recStream.hasNextRecord()) {
+			recStream.nextRecord();
+			if (recStream.getSid() == 0) {
+				continue;
+			}
+			Record record = createRecord (recStream);
+			if (record.getSid() == ContinueRecord.sid) {
+				continue;
+			}
+			temp.add(record);
+			if (dumpInterpretedRecords) {
+				String[] headers = recListener.getRecentHeaders();
+				for (int i = 0; i < headers.length; i++) {
+					ps.println(headers[i]);
+				}
+				ps.print(record.toString());
+			}
+			ps.println();
+		}
+		Record[] result = new Record[temp.size()];
+		temp.toArray(result);
+		return result;
+	}
+
+
+	/**
+	 *  Essentially a duplicate of RecordFactory. Kept separate as not to screw
+	 *  up non-debug operations.
+	 *
+	 */
+	private static Record createRecord(RecordInputStream in) {
+		switch (in.getSid()) {
+			case AreaFormatRecord.sid:     return new AreaFormatRecord(in);
+			case AreaRecord.sid:           return new AreaRecord(in);
+			case ArrayRecord.sid:          return new ArrayRecord(in);
+			case AxisLineFormatRecord.sid: return new AxisLineFormatRecord(in);
+			case AxisOptionsRecord.sid:    return new AxisOptionsRecord(in);
+			case AxisParentRecord.sid:     return new AxisParentRecord(in);
+			case AxisRecord.sid:           return new AxisRecord(in);
+			case AxisUsedRecord.sid:       return new AxisUsedRecord(in);
+			case BOFRecord.sid:            return new BOFRecord(in);
+			case BackupRecord.sid:         return new BackupRecord(in);
+			case BarRecord.sid:            return new BarRecord(in);
+			case BeginRecord.sid:          return new BeginRecord(in);
+			case BlankRecord.sid:          return new BlankRecord(in);
+			case BookBoolRecord.sid:       return new BookBoolRecord(in);
+			case BoolErrRecord.sid:        return new BoolErrRecord(in);
+			case BottomMarginRecord.sid:   return new BottomMarginRecord(in);
+			case BoundSheetRecord.sid:     return new BoundSheetRecord(in);
+			case CFHeaderRecord.sid:       return new CFHeaderRecord(in);
+			case CFRuleRecord.sid:         return new CFRuleRecord(in);
+			case CalcCountRecord.sid:      return new CalcCountRecord(in);
+			case CalcModeRecord.sid:       return new CalcModeRecord(in);
+			case CategorySeriesAxisRecord.sid: return new CategorySeriesAxisRecord(in);
+			case ChartFormatRecord.sid:    return new ChartFormatRecord(in);
+			case ChartRecord.sid:          return new ChartRecord(in);
+			case CodepageRecord.sid:       return new CodepageRecord(in);
+			case ColumnInfoRecord.sid:     return new ColumnInfoRecord(in);
+			case ContinueRecord.sid:       return new ContinueRecord(in);
+			case CountryRecord.sid:        return new CountryRecord(in);
+			case DBCellRecord.sid:         return new DBCellRecord(in);
+			case DSFRecord.sid:            return new DSFRecord(in);
+			case DatRecord.sid:            return new DatRecord(in);
+			case DataFormatRecord.sid:     return new DataFormatRecord(in);
+			case DateWindow1904Record.sid: return new DateWindow1904Record(in);
+			case DefaultColWidthRecord.sid:return new DefaultColWidthRecord(in);
+			case DefaultDataLabelTextPropertiesRecord.sid: return new DefaultDataLabelTextPropertiesRecord(in);
+			case DefaultRowHeightRecord.sid: return new DefaultRowHeightRecord(in);
+			case DeltaRecord.sid:          return new DeltaRecord(in);
+			case DimensionsRecord.sid:     return new DimensionsRecord(in);
+			case DrawingGroupRecord.sid:   return new DrawingGroupRecord(in);
+			case DrawingRecordForBiffViewer.sid: return new DrawingRecordForBiffViewer(in);
+			case DrawingSelectionRecord.sid: return new DrawingSelectionRecord(in);
+			case DVRecord.sid:             return new DVRecord(in);
+			case DVALRecord.sid:           return new DVALRecord(in);
+			case EOFRecord.sid:            return new EOFRecord(in);
+			case EndRecord.sid:            return new EndRecord(in);
+			case ExtSSTRecord.sid:         return new ExtSSTRecord(in);
+			case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in);
+			case ExternSheetRecord.sid:    return new ExternSheetRecord(in);
+			case FilePassRecord.sid:       return new FilePassRecord(in);
+			case FileSharingRecord.sid:    return new FileSharingRecord(in);
+			case FnGroupCountRecord.sid:   return new FnGroupCountRecord(in);
+			case FontBasisRecord.sid:      return new FontBasisRecord(in);
+			case FontIndexRecord.sid:      return new FontIndexRecord(in);
+			case FontRecord.sid:           return new FontRecord(in);
+			case FooterRecord.sid:         return new FooterRecord(in);
+			case FormatRecord.sid:         return new FormatRecord(in);
+			case FormulaRecord.sid:        return new FormulaRecord(in);
+			case FrameRecord.sid:          return new FrameRecord(in);
+			case GridsetRecord.sid:        return new GridsetRecord(in);
+			case GutsRecord.sid:           return new GutsRecord(in);
+			case HCenterRecord.sid:        return new HCenterRecord(in);
+			case HeaderRecord.sid:         return new HeaderRecord(in);
+			case HideObjRecord.sid:        return new HideObjRecord(in);
+			case HorizontalPageBreakRecord.sid: return new HorizontalPageBreakRecord(in);
+			case HyperlinkRecord.sid:      return new HyperlinkRecord(in);
+			case IndexRecord.sid:          return new IndexRecord(in);
+			case InterfaceEndRecord.sid:   return new InterfaceEndRecord(in);
+			case InterfaceHdrRecord.sid:   return new InterfaceHdrRecord(in);
+			case IterationRecord.sid:      return new IterationRecord(in);
+			case LabelRecord.sid:          return new LabelRecord(in);
+			case LabelSSTRecord.sid:       return new LabelSSTRecord(in);
+			case LeftMarginRecord.sid:     return new LeftMarginRecord(in);
+			case LegendRecord.sid:         return new LegendRecord(in);
+			case LineFormatRecord.sid:     return new LineFormatRecord(in);
+			case LinkedDataRecord.sid:     return new LinkedDataRecord(in);
+			case MMSRecord.sid:            return new MMSRecord(in);
+			case MergeCellsRecord.sid:     return new MergeCellsRecord(in);
+			case MulBlankRecord.sid:       return new MulBlankRecord(in);
+			case MulRKRecord.sid:          return new MulRKRecord(in);
+			case NameRecord.sid:           return new NameRecord(in);
+			case NoteRecord.sid:           return new NoteRecord(in);
+			case NumberRecord.sid:         return new NumberRecord(in);
+			case ObjRecord.sid:            return new ObjRecord(in);
+			case ObjectLinkRecord.sid:     return new ObjectLinkRecord(in);
+			case PaletteRecord.sid:        return new PaletteRecord(in);
+			case PaneRecord.sid:           return new PaneRecord(in);
+			case PasswordRecord.sid:       return new PasswordRecord(in);
+			case PasswordRev4Record.sid:   return new PasswordRev4Record(in);
+			case PlotAreaRecord.sid:       return new PlotAreaRecord(in);
+			case PlotGrowthRecord.sid:     return new PlotGrowthRecord(in);
+			case PrecisionRecord.sid:      return new PrecisionRecord(in);
+			case PrintGridlinesRecord.sid: return new PrintGridlinesRecord(in);
+			case PrintHeadersRecord.sid:   return new PrintHeadersRecord(in);
+			case PrintSetupRecord.sid:     return new PrintSetupRecord(in);
+			case ProtectRecord.sid:        return new ProtectRecord(in);
+			case ProtectionRev4Record.sid: return new ProtectionRev4Record(in);
+			case RKRecord.sid:             return new RKRecord(in);
+			case RefModeRecord.sid:        return new RefModeRecord(in);
+			case RefreshAllRecord.sid:     return new RefreshAllRecord(in);
+			case RightMarginRecord.sid:    return new RightMarginRecord(in);
+			case RowRecord.sid:            return new RowRecord(in);
+			case SCLRecord.sid:            return new SCLRecord(in);
+			case SSTRecord.sid:            return new SSTRecord(in);
+			case SaveRecalcRecord.sid:     return new SaveRecalcRecord(in);
+			case SelectionRecord.sid:      return new SelectionRecord(in);
+			case SeriesIndexRecord.sid:    return new SeriesIndexRecord(in);
+			case SeriesListRecord.sid:     return new SeriesListRecord(in);
+			case SeriesRecord.sid:         return new SeriesRecord(in);
+			case SeriesTextRecord.sid:     return new SeriesTextRecord(in);
+			case SeriesToChartGroupRecord.sid: return new SeriesToChartGroupRecord(in);
+			case SharedFormulaRecord.sid:  return new SharedFormulaRecord(in);
+			case SheetPropertiesRecord.sid:return new SheetPropertiesRecord(in);
+			case StringRecord.sid:         return new StringRecord(in);
+			case StyleRecord.sid:          return new StyleRecord(in);
+			case SupBookRecord.sid:        return new SupBookRecord(in);
+			case TabIdRecord.sid:          return new TabIdRecord(in);
+			case TableRecord.sid:          return new TableRecord(in);
+			case TextObjectRecord.sid:     return new TextObjectRecord(in);
+			case TextRecord.sid:           return new TextRecord(in);
+			case TickRecord.sid:           return new TickRecord(in);
+			case TopMarginRecord.sid:      return new TopMarginRecord(in);
+			case UnitsRecord.sid:          return new UnitsRecord(in);
+			case UseSelFSRecord.sid:       return new UseSelFSRecord(in);
+			case VCenterRecord.sid:        return new VCenterRecord(in);
+			case ValueRangeRecord.sid:     return new ValueRangeRecord(in);
+			case VerticalPageBreakRecord.sid: return new VerticalPageBreakRecord(in);
+			case WSBoolRecord.sid:         return new WSBoolRecord(in);
+			case WindowOneRecord.sid:      return new WindowOneRecord(in);
+			case WindowProtectRecord.sid:  return new WindowProtectRecord(in);
+			case WindowTwoRecord.sid:      return new WindowTwoRecord(in);
+			case WriteAccessRecord.sid:    return new WriteAccessRecord(in);
+			case WriteProtectRecord.sid:   return new WriteProtectRecord(in);        
+		
+		}
+		return new UnknownRecord(in);
+	}
+
+	/**
+	 * Method main with 1 argument just run straight biffview against given
+	 * file<P>
+	 *
+	 * with 2 arguments where the second argument is "on" - run biffviewer<P>
+	 *
+	 * with hex dumps of records <P>
+	 *
+	 * with 2 arguments where the second argument is "bfd" just run a big fat
+	 * hex dump of the file...don't worry about biffviewing it at all
+	 * <p>
+	 * Define the system property <code>poi.deserialize.escher</code> to turn on
+	 * deserialization of escher records.
+	 *
+	 */
+	public static void main(String[] args) {
+
+		System.setProperty("poi.deserialize.escher", "true");
+
+		if (args.length == 0) {
+			System.out.println( "Biff viewer needs a filename" );
+			return;
+		}
+
+		try {
+			String inFileName = args[0];
+			File inputFile = new File(inFileName);
+			if(!inputFile.exists()) {
+				throw new RuntimeException("specified inputFile '" + inFileName + "' does not exist");
+			}
+			PrintStream ps;
+			if (false) { // set to true to output to file
+				OutputStream os = new FileOutputStream(inFileName + ".out");
+				ps = new PrintStream(os);
+			} else {
+				ps = System.out;
+			}
+ 
+			if (args.length > 1 && args[1].equals("bfd")) {
+				POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(inputFile));
+				InputStream stream = fs.createDocumentInputStream("Workbook");
+				int size = stream.available();
+				byte[] data = new byte[size];
+
+				stream.read(data);
+				HexDump.dump(data, 0, System.out, 0);
+			} else {
+				boolean dumpInterpretedRecords = true;
+				boolean dumpHex = args.length > 1 && args[1].equals("on");
+
+				POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(inputFile));
+				InputStream is = fs.createDocumentInputStream("Workbook");
+				BiffRecordListener recListener = new BiffRecordListener(dumpHex ? new OutputStreamWriter(ps) : null);
+				is = new BiffDumpingStream(is, recListener);
+				createRecords(is, ps, recListener, dumpInterpretedRecords);
+			}
+			ps.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	private static final class BiffRecordListener implements IBiffRecordListener {
+		private final Writer _hexDumpWriter;
+		private final List _headers;
+		public BiffRecordListener(Writer hexDumpWriter) {
+			_hexDumpWriter = hexDumpWriter;
+			_headers = new ArrayList();
+		}
+
+		public void processRecord(int globalOffset, int recordCounter, int sid, int dataSize,
+				byte[] data) {
+			String header = formatRecordDetails(globalOffset, sid, dataSize, recordCounter);
+			_headers.add(header);
+			Writer w = _hexDumpWriter;
+			if (w != null) {
+				try {
+					w.write(header);
+					w.write(NEW_LINE_CHARS);
+					hexDumpAligned(w, data, 0, dataSize+4, globalOffset);
+					w.flush();
+				} catch (IOException e) {
+					throw new RuntimeException(e);
+				}
+			}
+		}
+		public String[] getRecentHeaders() {
+			String[] result = new String[_headers.size()];
+			_headers.toArray(result);
+			_headers.clear();
+			return result;
+		}
+		private static String formatRecordDetails(int globalOffset, int sid, int size, int recordCounter) {
+			StringBuffer sb = new StringBuffer(64);
+			sb.append("Offset=").append(HexDump.intToHex(globalOffset)).append("(").append(globalOffset).append(")");
+			sb.append(" recno=").append(recordCounter);
+			sb.append(  " sid=").append(HexDump.shortToHex(sid));
+			sb.append( " size=").append(HexDump.shortToHex(size)).append("(").append(size).append(")");
+			return sb.toString();
+		}
+	}
+	
+	private static interface IBiffRecordListener {
+
+		void processRecord(int globalOffset, int recordCounter, int sid, int dataSize, byte[] data);
+		
+	}
+
+	/**
+	 * Wraps a plain {@link InputStream} and allows BIFF record information to be tapped off
+	 *
+	 */
+	private static final class BiffDumpingStream extends InputStream {
+		private final DataInputStream _is;
+		private final IBiffRecordListener _listener;
+		private final byte[] _data;
+		private int _recordCounter;
+		private int _overallStreamPos;
+		private int _currentPos;
+		private int _currentSize;
+		private boolean _innerHasReachedEOF;
+		
+		public BiffDumpingStream(InputStream is, IBiffRecordListener listener) {
+			_is = new DataInputStream(is);
+			_listener = listener;
+			_data = new byte[RecordInputStream.MAX_RECORD_DATA_SIZE + 4];
+			_recordCounter = 0;
+			_overallStreamPos = 0;
+			_currentSize = 0;
+			_currentPos = 0;
+		}
+
+		public int read() throws IOException {
+			if (_currentPos >= _currentSize) {
+				fillNextBuffer();
+			}
+			if (_currentPos >= _currentSize) {
+				return -1;
+			}
+			int result = _data[_currentPos] & 0x00FF;
+			_currentPos ++;
+			_overallStreamPos ++;
+			formatBufferIfAtEndOfRec();
+			return result;
+		}
+		public int read(byte[] b, int off, int len) throws IOException {
+			if (_currentPos >= _currentSize) {
+				fillNextBuffer();
+			}
+			if (_currentPos >= _currentSize) {
+				return -1;
+			}
+			int availSize = _currentSize - _currentPos;
+			int result;
+			if (len > availSize) {
+				System.err.println("Unexpected request to read past end of current biff record");
+				result = availSize;
+			} else {
+				result = len;
+			}
+			System.arraycopy(_data, _currentPos, b, off, result);
+			_currentPos += result;
+			_overallStreamPos += result;
+			formatBufferIfAtEndOfRec();
+			return result;
+		}
+
+		public int available() throws IOException {
+			return _currentSize - _currentPos + _is.available();
+		}
+		private void fillNextBuffer() throws IOException {
+			if (_innerHasReachedEOF) {
+				return;
+			}
+			int b0 = _is.read();
+			if (b0 == -1) {
+				_innerHasReachedEOF = true;
+				return;
+			}
+			_data[0] = (byte) b0;
+			_is.readFully(_data, 1, 3);
+			int len = LittleEndian.getShort(_data, 2);
+			_is.readFully(_data, 4, len);
+			_currentPos = 0;
+			_currentSize = len + 4;
+			_recordCounter++;
+		}
+		private void formatBufferIfAtEndOfRec() {
+			if (_currentPos != _currentSize) {
+				return;
+			}
+			int dataSize = _currentSize-4;
+			int sid = LittleEndian.getShort(_data, 0);
+			int globalOffset = _overallStreamPos-_currentSize;
+			_listener.processRecord(globalOffset, _recordCounter, sid, dataSize, _data);
+		}
+		public void close() throws IOException {
+			_is.close();
+		}
+	}
+	
+	private static final int DUMP_LINE_LEN = 16;
+	private static final char[] COLUMN_SEPARATOR = " | ".toCharArray();
+	/**
+	 * Hex-dumps a portion of a byte array in typical format, also preserving dump-line alignment  
+	 * @param globalOffset (somewhat arbitrary) used to calculate the addresses printed at the 
+	 * start of each line 
+	 */
+	static void hexDumpAligned(Writer w, byte[] data, int baseDataOffset, int dumpLen, int globalOffset) {
+		// perhaps this code should be moved to HexDump
+		int globalStart = globalOffset + baseDataOffset;
+		int globalEnd = globalOffset + baseDataOffset + dumpLen;
+		int startDelta = globalStart % DUMP_LINE_LEN;
+		int endDelta = globalEnd % DUMP_LINE_LEN;
+		int startLineAddr = globalStart - startDelta;
+		int endLineAddr = globalEnd - endDelta;
+		
+		int lineDataOffset = baseDataOffset - startDelta;
+		int lineAddr = startLineAddr;
+		
+		// output (possibly incomplete) first line
+		if (startLineAddr == endLineAddr) {
+			hexDumpLine(w, data, lineAddr, lineDataOffset, startDelta, endDelta);
+			return;
+		}
+		hexDumpLine(w, data, lineAddr, lineDataOffset, startDelta, DUMP_LINE_LEN);
+		
+		// output all full lines in the middle
+		while (true) {
+			lineAddr += DUMP_LINE_LEN;
+			lineDataOffset += DUMP_LINE_LEN;
+			if (lineAddr >= endLineAddr) {
+				break;
+			}
+			hexDumpLine(w, data, lineAddr, lineDataOffset, 0, DUMP_LINE_LEN);
+		}
+		
+		
+		// output (possibly incomplete) last line
+		if (endDelta != 0) {
+			hexDumpLine(w, data, lineAddr, lineDataOffset, 0, endDelta);
+		}
+	}
+
+	private static void hexDumpLine(Writer w, byte[] data, int lineStartAddress, int lineDataOffset, int startDelta, int endDelta) {
+		if (startDelta >= endDelta) {
+			throw new IllegalArgumentException("Bad start/end delta");
+		}
+		try {
+			writeHex(w, lineStartAddress, 8);
+			w.write(COLUMN_SEPARATOR);
+			// raw hex data
+			for (int i=0; i< DUMP_LINE_LEN; i++) {
+				if (i>0) {
+					w.write(" ");
+				}
+				if (i >= startDelta && i < endDelta) {
+					writeHex(w, data[lineDataOffset+i], 2);
+				} else {
+					w.write("  ");
+				}
+			}
+			w.write(COLUMN_SEPARATOR);
+
+			// interpreted ascii
+			for (int i=0; i< DUMP_LINE_LEN; i++) {
+				if (i >= startDelta && i < endDelta) {
+					w.write(getPrintableChar(data[lineDataOffset+i]));
+				} else {
+					w.write(" ");
+				}
+			}
+			w.write(NEW_LINE_CHARS);
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private static char getPrintableChar(byte b) {
+		char ib = (char) (b & 0x00FF);
+		if (ib < 32 || ib > 126) {
+			return '.';
+		}
+		return ib;
+	}
+
+	private static void writeHex(Writer w, int value, int nDigits) throws IOException {
+		char[] buf = new char[nDigits];
+		int acc = value;
+		for(int i=nDigits-1; i>=0; i--) {
+			int digit = acc & 0x0F;
+			buf[i] = (char) (digit < 10 ? ('0' + digit) : ('A' + digit - 10));
+			acc >>= 4;
+		}
+		w.write(buf);
+	}
 }
 

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/model/TextboxShape.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/model/TextboxShape.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/model/TextboxShape.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/model/TextboxShape.java Fri Oct 24 18:47:25 2008
@@ -138,15 +138,11 @@
         HSSFTextbox shape = hssfShape;
 
         TextObjectRecord obj = new TextObjectRecord();
-        obj.setHorizontalTextAlignment( hssfShape.getHorizontalAlignment() );
-        obj.setVerticalTextAlignment( hssfShape.getVerticalAlignment());
-        obj.setTextLocked( true );
-        obj.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );
-        int frLength = ( shape.getString().numFormattingRuns() + 1 ) * 8;
-        obj.setFormattingRunLength( (short) frLength );
-        obj.setTextLength( (short) shape.getString().length() );
-        obj.setStr( shape.getString() );
-        obj.setReserved7( 0 );
+        obj.setHorizontalTextAlignment(hssfShape.getHorizontalAlignment());
+        obj.setVerticalTextAlignment(hssfShape.getVerticalAlignment());
+        obj.setTextLocked(true);
+        obj.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_NONE);
+        obj.setStr(shape.getString());
 
         return obj;
     }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ContinueRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ContinueRecord.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ContinueRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ContinueRecord.java Fri Oct 24 18:47:25 2008
@@ -22,76 +22,44 @@
 import org.apache.poi.util.LittleEndian;
 
 /**
- * Title:        Continue Record - Helper class used primarily for SST Records <P>
+ * Title:        Continue Record(0x003C) - Helper class used primarily for SST Records <P>
  * Description:  handles overflow for prior record in the input
  *               stream; content is tailored to that prior record<P>
  * @author Marc Johnson (mjohnson at apache dot org)
  * @author Andrew C. Oliver (acoliver at apache dot org)
  * @author Csaba Nagy (ncsaba at yahoo dot com)
- * @version 2.0-pre
  */
-
-public class ContinueRecord
-    extends Record
-{
+public final class ContinueRecord extends Record {
     public final static short sid = 0x003C;
-    private byte[]            field_1_data;
-
-    /**
-     * default constructor
-     */
+    private byte[]            _data;
 
-    public ContinueRecord()
-    {
+    public ContinueRecord(byte[] data) {
+        _data = data;
     }
 
     /**
      * USE ONLY within "processContinue"
      */
-
     public byte [] serialize()
     {
-        byte[] retval = new byte[ field_1_data.length + 4 ];
+        byte[] retval = new byte[ _data.length + 4 ];
         serialize(0, retval);
         return retval;
     }
 
-    public int serialize(int offset, byte [] data)
-    {
-
-        LittleEndian.putShort(data, offset, sid);
-        LittleEndian.putShort(data, offset + 2, ( short ) field_1_data.length);
-        System.arraycopy(field_1_data, 0, data, offset + 4, field_1_data.length);
-        return field_1_data.length + 4;
-        // throw new RecordFormatException(
-        //    "You're not supposed to serialize Continue records like this directly");
-    }
-
-	/*
-     * @param data raw data
-     */
-
-    public void setData(byte [] data)
-    {
-        field_1_data = data;
+    public int serialize(int offset, byte[] data) {
+        return write(data, offset, null, _data);
     }
 
     /**
      * get the data for continuation
      * @return byte array containing all of the continued data
      */
-
     public byte [] getData()
     {
-        return field_1_data;
+        return _data;
     }
 
-    /**
-     * Debugging toString
-     *
-     * @return string representation
-     */
-
     public String toString()
     {
         StringBuffer buffer = new StringBuffer();
@@ -113,19 +81,36 @@
      *
      * @param in the RecordInputstream to read the record from
      */
-
     public ContinueRecord(RecordInputStream in)
     {
-      field_1_data = in.readRemainder();
+      _data = in.readRemainder();
     }
 
-    /**
-     * Clone this record.
-     */
     public Object clone() {
-      ContinueRecord clone = new ContinueRecord();
-      clone.setData(field_1_data);
-      return clone;
+      return new ContinueRecord(_data);
     }
 
+    /**
+     * Writes the full encoding of a Continue record without making an instance
+     */
+    public static int write(byte[] destBuf, int destOffset, Byte initialDataByte, byte[] srcData) {
+        return write(destBuf, destOffset, initialDataByte, srcData, 0, srcData.length);
+    }
+    /**
+     * @param initialDataByte (optional - often used for unicode flag). 
+     * If supplied, this will be written before srcData
+     * @return the total number of bytes written
+     */
+    public static int write(byte[] destBuf, int destOffset, Byte initialDataByte, byte[] srcData, int srcOffset, int len) {
+        int totalLen = len + (initialDataByte == null ? 0 : 1);
+        LittleEndian.putUShort(destBuf, destOffset, sid);
+        LittleEndian.putUShort(destBuf, destOffset + 2, totalLen);
+        int pos = destOffset + 4;
+        if (initialDataByte != null) {
+            LittleEndian.putByte(destBuf, pos, initialDataByte.byteValue());
+            pos += 1;
+        }
+        System.arraycopy(srcData, srcOffset, destBuf, pos, len);
+        return 4 + totalLen;
+    }
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java Fri Oct 24 18:47:25 2008
@@ -17,8 +17,6 @@
 
 package org.apache.poi.hssf.record;
 
-import java.util.Stack;
-
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
@@ -124,11 +122,7 @@
 	}
 
 	private int getNameDefinitionSize() {
-		int result = 0;
-		for (int i = 0; i < field_5_name_definition.length; i++) {
-			result += field_5_name_definition[i].getSize();
-		}
-		return result;
+		return Ptg.getEncodedSize(field_5_name_definition);
 	}
 
 

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java Fri Oct 24 18:47:25 2008
@@ -20,47 +20,38 @@
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.util.LittleEndian;
 
-import java.util.Stack;
-import java.util.Iterator;
-
 /**
  * Not implemented yet. May commit it anyway just so people can see
  * where I'm heading.
  *
  * @author Glen Stampoultzis (glens at apache.org)
  */
-public final class LinkedDataFormulaField implements CustomField {
-    Stack formulaTokens = new Stack();
+public final class LinkedDataFormulaField {
+    private Ptg[] formulaTokens;
 
     public int getSize()
     {
-        int size = 0;
-        for ( Iterator iterator = formulaTokens.iterator(); iterator.hasNext(); )
-        {
-            Ptg token = (Ptg) iterator.next();
-            size += token.getSize();
-        }
-        return size + 2;
+        return 2 + Ptg.getEncodedSize(formulaTokens);
     }
 
     public int fillField( RecordInputStream in )
     {
-        short tokenSize = in.readShort();
-        formulaTokens = Ptg.createParsedExpressionTokens(tokenSize, in);
-
+        int tokenSize = in.readUShort();
+        formulaTokens = Ptg.readTokens(tokenSize, in);
         return tokenSize + 2;
     }
 
     public void toString( StringBuffer buffer )
     {
-        for ( int k = 0; k < formulaTokens.size(); k++ )
+        for ( int k = 0; k < formulaTokens.length; k++ )
         {
+        	Ptg ptg = formulaTokens[k];
             buffer.append( "Formula " )
                     .append( k )
                     .append( "=" )
-                    .append( formulaTokens.get( k ).toString() )
+                    .append(ptg.toString() )
                     .append( "\n" )
-                    .append( ( (Ptg) formulaTokens.get( k ) ).toDebugString() )
+                    .append(ptg.toDebugString() )
                     .append( "\n" );
         }
     }
@@ -75,34 +66,26 @@
     public int serializeField( int offset, byte[] data )
     {
         int size = getSize();
-        LittleEndian.putShort(data, offset, (short)(size - 2));
+        LittleEndian.putUShort(data, offset, size - 2);
         int pos = offset + 2;
-        pos += Ptg.serializePtgStack(formulaTokens, data, pos);
+        pos += Ptg.serializePtgs(formulaTokens, data, pos);
         return size;
     }
 
-    public Object clone()
-    {
-        try
-        {
-            // todo: clone tokens? or are they immutable?
-            return super.clone();
-        }
-        catch ( CloneNotSupportedException e )
-        {
-            // should not happen
-            return null;
-        }
-    }
-
-    public void setFormulaTokens( Stack formulaTokens )
+    public void setFormulaTokens(Ptg[] ptgs)
     {
-        this.formulaTokens = (Stack) formulaTokens.clone();
+        this.formulaTokens = (Ptg[])ptgs.clone();
     }
 
-    public Stack getFormulaTokens()
+    public Ptg[] getFormulaTokens()
     {
-        return (Stack)this.formulaTokens.clone();
+        return (Ptg[])this.formulaTokens.clone();
     }
 
+	public LinkedDataFormulaField copy() {
+		LinkedDataFormulaField result = new LinkedDataFormulaField();
+		
+		result.formulaTokens = getFormulaTokens();
+		return result;
+	}
 }

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataRecord.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataRecord.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataRecord.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/LinkedDataRecord.java Fri Oct 24 18:47:25 2008
@@ -125,7 +125,7 @@
         rec.field_2_referenceType = field_2_referenceType;
         rec.field_3_options = field_3_options;
         rec.field_4_indexNumberFmtRecord = field_4_indexNumberFmtRecord;
-        rec.field_5_formulaOfLink = ((LinkedDataFormulaField)field_5_formulaOfLink.clone());
+        rec.field_5_formulaOfLink = field_5_formulaOfLink.copy();
         return rec;
     }
 

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordFactory.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordFactory.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordFactory.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordFactory.java Fri Oct 24 18:47:25 2008
@@ -321,51 +321,54 @@
 		Record lastRecord = null;
 		while (recStream.hasNextRecord()) {
 			recStream.nextRecord();
-			if (recStream.getSid() != 0) {
-				Record[] recs = createRecord(recStream);   // handle MulRK records
+			if (recStream.getSid() == 0) {
+				// After EOF, Excel seems to pad block with zeros
+				continue;
+			}
+			Record[] recs = createRecord(recStream);   // handle MulRK records
 
-				if (recs.length > 1) {
-					for (int k = 0; k < recs.length; k++) {
-						records.add(recs[ k ]);			   // these will be number records
-					}
-				} else {
-					Record record = recs[ 0 ];
+			if (recs.length > 1) {
+				for (int k = 0; k < recs.length; k++) {
+					records.add(recs[ k ]);			   // these will be number records
+				}
+				continue;
+			}
+			Record record = recs[ 0 ];
 
-					if (record != null) {
-						if (record.getSid() == DrawingGroupRecord.sid
-							   && lastRecord instanceof DrawingGroupRecord) {
-							DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
-							lastDGRecord.join((AbstractEscherHolderRecord) record);
-						} else if (record.getSid() == ContinueRecord.sid &&
-								 ((lastRecord instanceof ObjRecord) || (lastRecord instanceof TextObjectRecord))) {
-							// Drawing records have a very strange continue behaviour.
-							//There can actually be OBJ records mixed between the continues.
-							lastDrawingRecord.processContinueRecord( ((ContinueRecord)record).getData() );
-							//we must remember the position of the continue record.
-							//in the serialization procedure the original structure of records must be preserved
-							records.add(record);
-						} else if (record.getSid() == ContinueRecord.sid &&
-								(lastRecord instanceof DrawingGroupRecord)) {
-							((DrawingGroupRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
-						} else if (record.getSid() == ContinueRecord.sid &&
-								(lastRecord instanceof StringRecord)) {
-							((StringRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
-						} else if (record.getSid() == ContinueRecord.sid) {
-							if (lastRecord instanceof UnknownRecord) {
-								//Gracefully handle records that we dont know about,
-								//that happen to be continued
-								records.add(record);
-							} else 
-								throw new RecordFormatException("Unhandled Continue Record");
-						} else {
-							lastRecord = record;
-							if (record instanceof DrawingRecord) {
-								lastDrawingRecord = (DrawingRecord) record;
-							}
-							records.add(record);
-						}
-					}
+			if (record == null) {
+				continue;
+			}
+			if (record.getSid() == DrawingGroupRecord.sid
+				   && lastRecord instanceof DrawingGroupRecord) {
+				DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
+				lastDGRecord.join((AbstractEscherHolderRecord) record);
+			} else if (record.getSid() == ContinueRecord.sid) {
+				ContinueRecord contRec = (ContinueRecord)record;
+				
+				if (lastRecord instanceof ObjRecord || lastRecord instanceof TextObjectRecord) {
+					// Drawing records have a very strange continue behaviour.
+					//There can actually be OBJ records mixed between the continues.
+					lastDrawingRecord.processContinueRecord(contRec.getData() );
+					//we must remember the position of the continue record.
+					//in the serialization procedure the original structure of records must be preserved
+					records.add(record);
+				} else if (lastRecord instanceof DrawingGroupRecord) {
+					((DrawingGroupRecord)lastRecord).processContinueRecord(contRec.getData());
+				} else if (lastRecord instanceof StringRecord) {
+					((StringRecord)lastRecord).processContinueRecord(contRec.getData());
+				} else if (lastRecord instanceof UnknownRecord) {
+					//Gracefully handle records that we don't know about,
+					//that happen to be continued
+					records.add(record);
+				} else {
+					throw new RecordFormatException("Unhandled Continue Record");
+				}
+			} else {
+				lastRecord = record;
+				if (record instanceof DrawingRecord) {
+					lastDrawingRecord = (DrawingRecord) record;
 				}
+				records.add(record);
 			}
 		}
 		return records;

Modified: poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java?rev=707807&r1=707806&r2=707807&view=diff
==============================================================================
--- poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java (original)
+++ poi/branches/ooxml/src/java/org/apache/poi/hssf/record/RecordInputStream.java Fri Oct 24 18:47:25 2008
@@ -29,19 +29,19 @@
  *
  * @author Jason Height (jheight @ apache dot org)
  */
-public class RecordInputStream extends InputStream {
-  /** Maximum size of a single record (minus the 4 byte header) without a continue*/
-  public final static short MAX_RECORD_DATA_SIZE = 8224;
-  private static final int INVALID_SID_VALUE = -1;
-
-  private InputStream in;
-  protected short currentSid;
-  protected short currentLength = -1;
-  protected short nextSid;
-
-  protected byte[] data = new byte[MAX_RECORD_DATA_SIZE];
-  protected short recordOffset;
-  protected long pos;
+public final class RecordInputStream extends InputStream {
+	/** Maximum size of a single record (minus the 4 byte header) without a continue*/
+	public final static short MAX_RECORD_DATA_SIZE = 8224;
+	private static final int INVALID_SID_VALUE = -1;
+
+	private InputStream in;
+	private short currentSid;
+	private short currentLength = -1;
+	private short nextSid;
+
+	private final byte[] data = new byte[MAX_RECORD_DATA_SIZE];
+	private short recordOffset;
+	private long pos;
 
   private boolean autoContinue = true;
 
@@ -218,54 +218,81 @@
 		return result;
 	}
 
-  /**
-   *  given a byte array of 16-bit unicode characters, compress to 8-bit and
-   *  return a string
-   *
-   * { 0x16, 0x00 } -0x16
-   *
-   * @param length the length of the final string
-   * @return                                     the converted string
-   * @exception  IllegalArgumentException        if len is too large (i.e.,
-   *      there is not enough data in string to create a String of that
-   *      length)
-   */
-  public String readUnicodeLEString(int length) {
-    if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) {
-            throw new IllegalArgumentException("Illegal length - asked for " + length + " but only " + (remaining()/2) + " left!");
-    }
-
-    StringBuffer buf = new StringBuffer(length);
-    for (int i=0;i<length;i++) {
-      if ((remaining() == 0) && (isContinueNext())){
-        nextRecord();
-        int compressByte = readByte();
-        if(compressByte != 1) throw new IllegalArgumentException("compressByte in continue records must be 1 while reading unicode LE string");
-      }
-      char ch = (char)readShort();
-      buf.append(ch);
-    }
-    return buf.toString();
-  }
+	public String readString() {
+		int requestedLength = readUShort();
+		byte compressFlag = readByte();
+		return readStringCommon(requestedLength, compressFlag == 0);
+	}
+	/**
+	 *  given a byte array of 16-bit unicode characters, compress to 8-bit and
+	 *  return a string
+	 *
+	 * { 0x16, 0x00 } -0x16
+	 *
+	 * @param requestedLength the length of the final string
+	 * @return                                     the converted string
+	 * @exception  IllegalArgumentException        if len is too large (i.e.,
+	 *      there is not enough data in string to create a String of that
+	 *      length)
+	 */
+	public String readUnicodeLEString(int requestedLength) {
+		return readStringCommon(requestedLength, false);
+	}
 
-  public String readCompressedUnicode(int length) {
-    if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
-            throw new IllegalArgumentException("Illegal length " + length);
-    }
+	public String readCompressedUnicode(int requestedLength) {
+		return readStringCommon(requestedLength, true);
+	}
 
-    StringBuffer buf = new StringBuffer(length);
-    for (int i=0;i<length;i++) {
-      if ((remaining() == 0) && (isContinueNext())) {
-          nextRecord();
-          int compressByte = readByte();
-          if(compressByte != 0) throw new IllegalArgumentException("compressByte in continue records must be 0 while reading compressed unicode");
-      }
-      byte b = readByte();
-      char ch = (char)(0x00FF & b); // avoid sex
-      buf.append(ch);
-    }
-    return buf.toString();
-  }
+	private String readStringCommon(int requestedLength, boolean pIsCompressedEncoding) {
+		// Sanity check to detect garbage string lengths
+		if (requestedLength < 0 || requestedLength > 0x100000) { // 16 million chars?
+			throw new IllegalArgumentException("Bad requested string length (" + requestedLength + ")");
+		}
+		char[] buf = new char[requestedLength];
+		boolean isCompressedEncoding = pIsCompressedEncoding;
+		int curLen = 0;
+		while(true) {
+			int availableChars =isCompressedEncoding ?  remaining() : remaining() / LittleEndian.SHORT_SIZE;
+			if (requestedLength - curLen <= availableChars) {
+				// enough space in current record, so just read it out
+				while(curLen < requestedLength) {
+					char ch;
+					if (isCompressedEncoding) {
+						ch = (char)readUByte();
+					} else {
+						ch = (char)readShort();
+					}
+					buf[curLen] = ch;
+					curLen++;
+				}
+				return new String(buf);
+			}
+			// else string has been spilled into next continue record
+			// so read what's left of the current record
+			while(availableChars > 0) {
+				char ch;
+				if (isCompressedEncoding) {
+					ch = (char)readUByte();
+				} else {
+					ch = (char)readShort();
+				}
+				buf[curLen] = ch;
+				curLen++;
+				availableChars--;
+			}
+			if (!isContinueNext()) {
+				throw new RecordFormatException("Expected to find a ContinueRecord in order to read remaining " 
+						+ (requestedLength-curLen) + " of " + requestedLength + " chars");
+			}
+			if(remaining() != 0) {
+				throw new RecordFormatException("Odd number of bytes(" + remaining() + ") left behind");
+			}
+			nextRecord();
+			// note - the compressed flag may change on the fly
+			byte compressFlag = readByte();
+			isCompressedEncoding = (compressFlag == 0); 
+		}
+	}
 
   /** Returns an excel style unicode string from the bytes reminaing in the record.
    * <i>Note:</i> Unicode strings differ from <b>normal</b> strings due to the addition of



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org