This commit was manufactured by cvs2svn to create tag 'REL_2_0_RC1'.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/tags/REL_2_0_RC1@353435 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/.classpath b/.classpath
deleted file mode 100644
index 75589e2..0000000
--- a/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-    <classpathentry kind="src" path="src/testcases"/>
-    <classpathentry kind="src" path="src/java"/>
-    <classpathentry kind="src" path="src/examples/src"/>
-    <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
-    <classpathentry kind="lib" path="tools/cents/junit.cent/lib/junit-3.7.jar"/>
-    <classpathentry kind="lib" path="lib/core/commons-logging-1.0.jar"/>
-    <classpathentry kind="output" path="build"/>
-</classpath>
diff --git a/.cvsignore b/.cvsignore
index 558342c..6a33107 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1,4 +1,5 @@
 dist
+build
 scripts
 *.el
 *.ipr
@@ -8,3 +9,5 @@
 log*.*
 *.log
 build
+.classpath
+.project
diff --git a/.project b/.project
deleted file mode 100644
index 2a90ecd..0000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>POI</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/build.xml b/build.xml
index 0ed314e..3b6ba76 100644
--- a/build.xml
+++ b/build.xml
@@ -6,7 +6,7 @@
         Glen Stampoultzis   glens at apache.org
 
 
-    This build was tested with and 1.5.3 although it will probably work with
+    This build was tested with ant 1.5.3 although it will probably work with
     other versions.  The following jar files should be installed
     into the ant lib directory:
 
@@ -15,6 +15,7 @@
     junit(3.8+)     http://www.ibiblio.org/maven/junit/jars/
     xerces          http://www.ibiblio.org/maven/xerces/jars/
     jdepend         http://www.ibiblio.org/maven/jdepend/jars/
+    xalan           http://www.ibiblio.org/maven/xalan/jars/
 
     The ant jar "optional.jar" should also be available otherwise the
     build will fail.
@@ -82,7 +83,7 @@
     <property name="changelog.file" value="${build.site}/changelog.html"/>
     <property name="dist.dir" value="build/dist"/>
     <property name="jar.name" value="poi"/>
-    <property name="version.id" value="2.0-pre1"/>
+    <property name="version.id" value="2.0-RC1"/>
     <property name="halt.on.test.failure" value="true"/>
 
     <path id="main.classpath">
@@ -141,14 +142,17 @@
 
     <target name="check-jars">
         <condition property="jars.present">
+	    <or>
             <and>
                 <available file="${main.jar1.dir}"/>
                 <available file="${main.jar2.dir}"/>
                 <available file="${contrib.jar1.dir}"/>
                 <available file="${contrib.jar2.dir}"/>
-                <available file="${contrib.jar3.dir}"/>
-                <available file="${junit.jar1.dir}"/>
+              	<available file="${contrib.jar3.dir}"/>
+               	<available file="${junit.jar1.dir}"/>
             </and>
+			<isset property="disconnected"/>
+	    </or>
         </condition>
     </target>
 
@@ -214,15 +218,16 @@
         </uptodate>
     </target>
 
-    <target name="test-main" depends="compile-main,-test-main-check" unless="main.test.notRequired">
-        <junit printsummary="yes" showoutput="true" filtertrace="no" fork="no"
-            haltonfailure="${halt.on.test.failure}" failureproperty="main.test.failed">
-            <classpath>
+	<path id="test.classpath">
                 <path refid="main.classpath"/>
                 <pathelement location="${main.output.dir}"/>
                 <pathelement location="${main.output.test.dir}"/>
                 <pathelement location="${junit.jar1.dir}"/>
-            </classpath>
+         </path>
+    <target name="test-main" depends="compile-main,-test-main-check" unless="main.test.notRequired">
+        <junit printsummary="yes" showoutput="true" filtertrace="no" fork="no"
+            haltonfailure="${halt.on.test.failure}" failureproperty="main.test.failed">
+            <classpath refid="test.classpath"/>
             <sysproperty key="HSSF.testdata.path" value="${main.src.test}/org/apache/poi/hssf/data"/>
             <sysproperty key="HPSF.testdata.path" value="${main.src.test}/org/apache/poi/hpsf/data"/>
             <formatter type="plain"/>
@@ -238,14 +243,9 @@
         <antcall target="-test-main-write-testfile"/>
     </target>
 
-    <target name="single-test" depends="compile-main">
-	<junit printsummary="no" showoutput="true" filtertrace="no" fork="no" haltonfailure="${halt.on.test.failure}" failureproperty="main.test.failed" >
-	<classpath>
-                <path refid="main.classpath"/>
-                <pathelement location="${main.output.dir}"/>
-                <pathelement location="${main.output.test.dir}"/>
-                <pathelement location="${junit.jar1.dir}"/>
-            </classpath>
+    <target name="single-test" depends="-test-property-check,compile-main">
+	<junit printsummary="no" showoutput="true" filtertrace="no" haltonfailure="${halt.on.test.failure}" failureproperty="main.test.failed" >
+            <classpath refid="test.classpath"/>
 	    <sysproperty key="HSSF.testdata.path" value="${main.src.test}/org/apache/poi/hssf/data"/>
             <sysproperty key="HPSF.testdata.path" value="${main.src.test}/org/apache/poi/hpsf/data"/>
             <formatter type="plain" usefile="no"/>
@@ -254,6 +254,24 @@
 	</junit>
     </target>
 
+    <target name="debug-test" depends="-test-property-check,compile-main">
+	<junit printsummary="no" showoutput="true" filtertrace="no" fork="yes" haltonfailure="${halt.on.test.failure}" failureproperty="main.test.failed" >
+	<jvmarg value="-Xdebug"/>
+	<jvmarg value="-Xrunjdwp:transport=dt_socket,address=5001,server=y,suspend=y"/>
+	<sysproperty key="java.compiler" value="NONE"/>
+	<classpath refid="test.classpath"/>
+	    <sysproperty key="HSSF.testdata.path" value="${main.src.test}/org/apache/poi/hssf/data"/>
+            <sysproperty key="HPSF.testdata.path" value="${main.src.test}/org/apache/poi/hpsf/data"/>
+            <formatter type="plain" usefile="no"/>
+	    <test name="${testcase}" />
+	</junit>
+    </target>
+
+    <target name="-test-property-check" unless="testcase">
+	<echo message="Please use -Dtestcase=org.your.testcase to run a single test"/>
+	<fail/>
+    </target>
+
     <target name="-test-main-write-testfile" unless="main.test.failed">
         <echo file="${main.testokfile}" append="false" message="testok"/>
     </target>
@@ -334,11 +352,10 @@
     </target>
 
     <target name="-check-forrest-installed" unless="env.FORREST_HOME">
-        <echo>
-            Please install apache forrest and set the
-            FORREST_HOME environment variable.
-        </echo>
-        <fail/>
+        <echo>Please install Apache Forrest (see
+&lt;http://xml.apache.org/forrest/index.html&gt;) and set the
+FORREST_HOME environment variable!</echo>
+        <fail message="Apache Forrest is not installed."/>
     </target>
 
     <!--    <target name="check-docs">-->
@@ -386,6 +403,18 @@
         </condition>
     </target>
 
+    <target name="-cvschangelog" unless="disconnected">
+        <cvschangelog destfile="${changelog.file}" daysinpast="30"/>
+
+	<style in="${changelog.file}"
+            out="${build.site.src}/src/documentation/content/changelog.html"
+            style="changelog.xsl">
+            <param name="title" expression="POI Change Log"/>
+            <param name="module" expression="jakarta-poi"/>
+            <param name="cvsweb" expression="http://cvs.apache.org/viewcvs/"/>
+        </style>
+    </target>
+
     <target name="reports" depends="-check-reports" unless="reports.notRequired"
         description="Creates junit,jdepend and javadoc reports">
         <antcall target="test">
@@ -425,15 +454,7 @@
             out="${jdepend.report.out.dir}/index.html"
             style="jdepend.xsl"/>
 
-        <cvschangelog destfile="${changelog.file}" daysinpast="30"/>
-
-        <style in="${changelog.file}"
-            out="${build.site.src}/src/documentation/content/changelog.html"
-            style="changelog.xsl">
-            <param name="title" expression="POI Change Log"/>
-            <param name="module" expression="jakarta-poi"/>
-            <param name="cvsweb" expression="http://cvs.apache.org/viewcvs/"/>
-        </style>
+	<antcall target="-cvschangelog"/>
 
         <javadoc
             destdir="${apidocs.report.dir}"
@@ -547,9 +568,9 @@
 
         <tar destfile="${dist.dir}/${jar.name}-bin-${version.id}-${DSTAMP}.tar.gz" compression="gzip">
             <tarfileset dir="${build.site}" prefix="docs"/>
-            <tarfileset file="${dist.dir}/${jar.name}-${version.id}.jar"/>
-            <tarfileset file="${dist.dir}/${jar.name}-contrib-${version.id}.jar"/>
-            <tarfileset file="${dist.dir}/${jar.name}-scratchpad-${version.id}.jar"/>
+            <tarfileset file="${dist.dir}/${jar.name}-${version.id}-${DSTAMP}.jar"/>
+            <tarfileset file="${dist.dir}/${jar.name}-contrib-${version.id}-${DSTAMP}.jar"/>
+            <tarfileset file="${dist.dir}/${jar.name}-scratchpad-${version.id}-${DSTAMP}.jar"/>
             <tarfileset dir="legal" prefix="legal"/>
         </tar>
 
@@ -570,6 +591,6 @@
 
     <target name="clean-dist" depends="clean,dist" description="Cleans the build directory then creates a distribution"/>
 
-    <target name="gump" depends="jar"/>
+    <target name="gump" depends="jar,test"/>
 
 </project>
diff --git a/src/documentation/content/xdocs/book.xml b/src/documentation/content/xdocs/book.xml
index 178ecad..7b05418 100644
--- a/src/documentation/content/xdocs/book.xml
+++ b/src/documentation/content/xdocs/book.xml
@@ -9,7 +9,7 @@
     <menu label="Community">
         <menu-item label="News" href="news.html"/>
         <menu-item label="Mirrors" href="mirrors.html"/>
-        <menu-item label="Changes" href="changes.html"/>
+        <menu-item label="Changes" href="site:changes"/>
         <menu-item label="To Do" href="todo.html"/>
         <menu-item label="Get Involved" href="getinvolved/index.html"/>
         <menu-item label="Mailing Lists" href="http://jakarta.apache.org/site/mail.html"/>
diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml
deleted file mode 100644
index de73c7b..0000000
--- a/src/documentation/content/xdocs/changes.xml
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE changes PUBLIC "-//APACHE//DTD Changes V1.1//EN" "./dtd/changes-v11.dtd">
-
-<changes>
-
-    <title>History of Changes</title>
-
-    <devs>
-        <!-- in strict alphabetical order -->
-        <person id="AO" name="Andrew C. Oliver" email="acoliver2@users.sourceforge.net"/>
-        <person id="GJS" name="Glen Stampoultzis" email="glens@apache.org"/>
-        <person id="MJ" name="Marc Johnson" email="mjohnson@apache.org"/>
-        <person id="NKB" name="Nicola Ken Barozzi" email="barozzi@nicolaken.com"/>
-        <person id="POI-DEVELOPERS" name="Poi Developers" email="poi-dev@jakarta.apache.org"/>
-    </devs>
-    <release version="2.0-pre1" date="unreleased">
-        <action dev="POI-DEVELOPERS" type="add">Patch applied for deep cloning of worksheets was provided</action>
-        <action dev="POI-DEVELOPERS" type="add">Patch applied to allow sheet reordering</action>
-        <action dev="POI-DEVELOPERS" type="add">Added additional print area setting methods using row/column numbers</action>
-        <action dev="POI-DEVELOPERS" type="fix">HDF:  Negative Array size fix</action>
-        <action dev="POI-DEVELOPERS" type="update">Added argument pointers to support the IF formula</action>
-        <action dev="POI-DEVELOPERS" type="update">Formulas: Added special character support for string literals, specifically for SUMIF formula support and addresses a bug as well</action>
-        <action dev="POI-DEVELOPERS" type="fix">BlockingInputStream committed to help ensure reads</action>
-        <action dev="POI-DEVELOPERS" type="fix">Fixed problem with NaN values differing from the investigated value from file reads in FormulaRecords</action>
-        <action dev="POI-DEVELOPERS" type="fix">Patch for getColumnWidth in HSSF</action>
-        <action dev="POI-DEVELOPERS" type="add">Patch for dealing with mult-level numbered lists in HDF</action>
-        <action dev="POI-DEVELOPERS" type="fix">Due to named reference work, several named-ranged bugs were closed</action>
-        <action dev="POI-DEVELOPERS" type="fix">Patch applied to prevent sheet corruption after a template modification</action>
-        <action dev="POI-DEVELOPERS" type="update">Shared Formulas now Supported</action>
-        <action dev="POI-DEVELOPERS" type="update">Added GreaterEqual, LessEqual and NotEqual to Formula Parser</action>
-        <action dev="POI-DEVELOPERS" type="update">Added GreaterThan and LessThan functionality to formulas</action>
-        <action dev="POI-DEVELOPERS" type="fix">Patches for i10n</action>
-        <action dev="POI-DEVELOPERS" type="update">POI Build System Updated</action>
-        <action dev="POI-DEVELOPERS" type="fix">font names can now be null</action>
-    </release>
-    <release version="1.10-dev" date="19 Feb 2003">
-        <action dev="POI-DEVELOPERS" type="add">Support for zoom level</action>
-        <action dev="POI-DEVELOPERS" type="add">Freeze and split pane support</action>
-        <action dev="POI-DEVELOPERS" type="add">Row and column headers on printouts</action>
-    </release>
-    <release version="1.8-dev" date="20 Sep 2002">
-        <action dev="POI-DEVELOPERS" type="add">Custom Data Format Support</action>
-        <action dev="POI-DEVELOPERS" type="add">Enhanced Unicode Support for Russian and Japanese</action>
-        <action dev="POI-DEVELOPERS" type="add">Enhanced formula support including read-only for
-            "optimized if" statements.</action>
-        <action dev="POI-DEVELOPERS" type="add">Support for cloning objects</action>
-        <action dev="POI-DEVELOPERS" type="add">Fixes for header/footer</action>
-        <action dev="POI-DEVELOPERS" type="add">Spanish Documentation translations</action>
-        <action dev="POI-DEVELOPERS" type="add">Support for preserving VBA macros</action>
-    </release>
-    <release version="1.7-dev" date="???">
-        <action dev="NKB" type="update">Removed runtime dependency on commons logging.</action>
-        <action dev="POI-DEVELOPERS" type="update">Formula support</action>
-    </release>
-    <release version="1.5.1" date="16 June 2002">
-        <action dev="GJS" type="update">Removed depedency on commons logging.  Now define poi.logging system property to enable logging to standard out.</action>
-        <action dev="GJS" type="fix">Fixed SST string handling so that spreadsheets with rich text or extended text will be read correctly.</action>
-    </release>
-    <release version="1.5" date="06 May 2002">
-        <action dev="NKB" type="update">New project build.</action>
-        <action dev="NKB" type="update">New project documentation system based on Cocoon.</action>
-        <action dev="POI-DEVELOPERS" type="update">Package rename</action>
-        <action dev="POI-DEVELOPERS" type="fix">Various bug fixes</action>
-        <action dev="POI-DEVELOPERS" type="add">Early stages of HSF development (not ready for development)</action>
-        <action dev="POI-DEVELOPERS" type="add">Initial low level record support for charting (not complete)</action>
-    </release>
-    <release version="1.1.0" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Created new event model</action>
-        <action dev="POI-DEVELOPERS">Optimizations made to HSSF including aggregate records for
-        values, rows, etc.</action>
-        <action dev="POI-DEVELOPERS">predictive sizing, offset based writing (instead of lots of
-        array copies)</action>
-        <action dev="POI-DEVELOPERS">minor re-factoring and bug fixes.</action>
-    </release>
-    <release version="1.0.0" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Minor documentation updates.</action>
-    </release>
-    <release version="0.14.0" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Added DataFormat helper class and exposed set and get format
-        on HSSFCellStyle</action>
-        <action dev="POI-DEVELOPERS">Fixed column width apis (unit wise) and various javadoc on
-        the subject</action>
-        <action dev="POI-DEVELOPERS">Fix for Dimensions record (again)... (one of these days I'll
-        write a unit test for this ;-p).</action>
-        <action dev="POI-DEVELOPERS">Some optimization on sheet creation.</action>
-    </release>
-    <release version="0.13.0" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Changes not recorded.</action>
-    </release>
-    <release version="0.12.0" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Added MulBlank, Blank, ColInfo</action>
-        <action dev="POI-DEVELOPERS">Added log4j facility and removed all sys.out type logging</action>
-        <action dev="POI-DEVELOPERS">Added support for adding font's, styles and corresponding
-        high level api for styling cells</action>
-        <action dev="POI-DEVELOPERS">added support for changing row height, cell width and default
-        row height/cell width.</action>
-        <action dev="POI-DEVELOPERS">Added fixes for internationalization (UTF-16 should work now
-        from HSSFCell.setStringValue, etc when the encoding is set)</action>
-        <action dev="POI-DEVELOPERS">added support for adding/removing and naming sheets.</action>
-    </release>
-    <release version="0.11.0" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Bugfix release. We were throwing an exception when reading
-        RKRecord objects.</action>
-    </release>
-    <release version="0.10.0" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Got continuation records to work (read/write)</action>
-        <action dev="POI-DEVELOPERS">Added various pre-support for formulas</action>
-        <action dev="POI-DEVELOPERS">Massive API reorganization, repackaging.</action>
-        <action dev="POI-DEVELOPERS">BiffViewer class added for validating HSSF &amp; POI and/or
-        HSSF Output.</action>
-        <action dev="POI-DEVELOPERS">Better API support for modification.</action>
-    </release>
-    <release version="0.7 (and interim releases)" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Added encoding flag to high and low level api to use utf-16
-        when needed (HSSFCell.setEncoding())</action>
-        <action dev="POI-DEVELOPERS">added read only support for Label records (which are
-        reinterpreted as LabelSST when written)</action>
-        <action dev="POI-DEVELOPERS">Broken continuation record implementation (oops)</action>
-        <action dev="POI-DEVELOPERS">BiffViewer class added for validating HSSF &amp; POI and/or
-        HSSF Output.</action>
-    </release>
-    <release version="0.6 (release)" date="Release date not recorded">
-        <action dev="POI-DEVELOPERS">Support for read/write and modify.</action>
-        <action dev="POI-DEVELOPERS">Read only support for MulRK records (converted to Number when
-        writing)
-        </action>
-    </release>
-
-</changes>
diff --git a/src/documentation/xdocs/dtd/book-cocoon-v10.dtd b/src/documentation/content/xdocs/dtd/book-cocoon-v10.dtd
old mode 100755
new mode 100644
similarity index 100%
rename from src/documentation/xdocs/dtd/book-cocoon-v10.dtd
rename to src/documentation/content/xdocs/dtd/book-cocoon-v10.dtd
diff --git a/src/documentation/xdocs/dtd/changes-v11.dtd b/src/documentation/content/xdocs/dtd/changes-v11.dtd
similarity index 100%
rename from src/documentation/xdocs/dtd/changes-v11.dtd
rename to src/documentation/content/xdocs/dtd/changes-v11.dtd
diff --git a/src/documentation/xdocs/dtd/document-v11.dtd b/src/documentation/content/xdocs/dtd/document-v11.dtd
similarity index 100%
rename from src/documentation/xdocs/dtd/document-v11.dtd
rename to src/documentation/content/xdocs/dtd/document-v11.dtd
diff --git a/src/documentation/xdocs/dtd/faq-v11.dtd b/src/documentation/content/xdocs/dtd/faq-v11.dtd
similarity index 100%
rename from src/documentation/xdocs/dtd/faq-v11.dtd
rename to src/documentation/content/xdocs/dtd/faq-v11.dtd
diff --git a/src/documentation/xdocs/dtd/javadoc-v04draft.dtd b/src/documentation/content/xdocs/dtd/javadoc-v04draft.dtd
old mode 100755
new mode 100644
similarity index 100%
rename from src/documentation/xdocs/dtd/javadoc-v04draft.dtd
rename to src/documentation/content/xdocs/dtd/javadoc-v04draft.dtd
diff --git a/src/documentation/xdocs/dtd/specification-v11.dtd b/src/documentation/content/xdocs/dtd/specification-v11.dtd
similarity index 100%
rename from src/documentation/xdocs/dtd/specification-v11.dtd
rename to src/documentation/content/xdocs/dtd/specification-v11.dtd
diff --git a/src/documentation/xdocs/dtd/todo-v11.dtd b/src/documentation/content/xdocs/dtd/todo-v11.dtd
similarity index 100%
rename from src/documentation/xdocs/dtd/todo-v11.dtd
rename to src/documentation/content/xdocs/dtd/todo-v11.dtd
diff --git a/src/documentation/content/xdocs/howtobuild.xml b/src/documentation/content/xdocs/howtobuild.xml
index 30cca65..9b99b26 100644
--- a/src/documentation/content/xdocs/howtobuild.xml
+++ b/src/documentation/content/xdocs/howtobuild.xml
@@ -46,6 +46,10 @@
                     <td>jdepend</td>
                     <td>http://www.ibiblio.org/maven/jdepend/jars/</td>
                 </tr>
+                <tr>
+                    <td>xalan</td>
+                    <td>http://www.ibiblio.org/maven/xalan/jars/</td>
+                </tr>
             </table>
             <p>
                 Just pick the latest versions of these jars and place
diff --git a/src/documentation/content/xdocs/hssf/chart.xml b/src/documentation/content/xdocs/hssf/chart.xml
index 55d2c83..33b174b 100644
--- a/src/documentation/content/xdocs/hssf/chart.xml
+++ b/src/documentation/content/xdocs/hssf/chart.xml
@@ -478,7 +478,7 @@
     recordid = 0x1034, size =0
     [END]
     [/END]
-
+                <!--  break -->
     ============================================
     Offset 0x1006 (4102)
     rectype = 0x1044, recsize = 0x4
@@ -801,6 +801,7 @@
              .crossesFarRight          = false
              .reversed                 = false
     [/CATSERRANGE]
+                <!--  break -->
 
     ============================================
     Offset 0x1124 (4388)
@@ -1220,7 +1221,7 @@
     [UNKNOWN RECORD]
         .id        = 104f
     [/UNKNOWN RECORD]
-
+                <!--  break -->
     ============================================
     Offset 0x12c4 (4804)
     rectype = 0x1051, recsize = 0x8
diff --git a/src/documentation/content/xdocs/hssf/quick-guide.xml b/src/documentation/content/xdocs/hssf/quick-guide.xml
index b5203d2..39a1753 100644
--- a/src/documentation/content/xdocs/hssf/quick-guide.xml
+++ b/src/documentation/content/xdocs/hssf/quick-guide.xml
@@ -31,7 +31,7 @@
                     <li><link href="#ReadWriteWorkbook">Reading and writing</link></li>
                     <li><link href="#NewLinesInCells">Use newlines in cells.</link></li>
                     <li><link href="#DataFormats">Create user defined data formats.</link></li>
-                    <li><link href="#PrintArea">Fit sheet to one page</link></li>
+                    <li><link href="#FitTo">Fit Sheet to One Page</link></li>
                     <li><link href="#PrintArea2">Set print area for a sheet.</link></li>		    
                     <li><link href="#FooterPageNumbers">Set page numbers on the footer of a sheet.</link></li>
                     <li><link href="#ShiftRows">Shift rows.</link></li>
@@ -420,14 +420,14 @@
     fileOut.close();
                     </source>
                 </section>
-                <anchor id="PrintArea"/>
-                <section><title>Set Print Area to One Page</title>
+                <anchor id="FitTo"/>
+                <section><title>Fit Sheet to One Page</title>
                     <source>
     HSSFWorkbook wb = new HSSFWorkbook();
     HSSFSheet sheet = wb.createSheet("format sheet");
-    HSSFPrintSetup ps = sheet.getPrintSetup()
+    HSSFPrintSetup ps = sheet.getPrintSetup();
     
-    sheet.setAutobreaks(true)
+    sheet.setAutobreaks(true);
     
     ps.setFitHeight((short)1);
     ps.setFitWidth((short)1);
diff --git a/src/documentation/content/xdocs/index.xml b/src/documentation/content/xdocs/index.xml
index 274ebf3..634745e 100644
--- a/src/documentation/content/xdocs/index.xml
+++ b/src/documentation/content/xdocs/index.xml
@@ -18,11 +18,15 @@
     <section><title>Purpose</title>
       <p>
       The POI project consists of APIs for manipulating various file formats
-      based upon Microsoft's OLE 2 Compound Document format using pure Java.
+      based upon Microsoft's OLE 2 Compound Document format using pure Java.  In short, you can
+      read and write MS Excel files using Java.  Soon, you'll be able to read and write 
+      Word files using Java.  POI is your Java Excel solution as well as your Word Excel solution.
+      However, we have a complete API for porting other OLE 2 Compound Document formats and welcome
+      others to participate.
       </p>
       <p>
       OLE 2 Compound Document Format based files include most Microsoft Office
-      files such as XLS and DOC.
+      files such as XLS and DOC as well as MFC serialization API based file formats.
       </p>
       <p>
          As a general policy we try to collaborate as much as possible with other projects to
@@ -46,7 +50,7 @@
 		we say that POIFS is the most complete and correct port of this file format to date!
         </p>
         <p>
-		You'd use HSSF if you needed to read or write an XLS (Excel) file using Java. You can also read and modify
+		You'd use HSSF if you needed to read or write an Excel file using Java (XLS). You can also read and modify
 		spreadsheets using this API, although right now writing is more mature.
         </p>
       </section>
diff --git a/src/documentation/content/xdocs/site.xml b/src/documentation/content/xdocs/site.xml
index 0d57b60..0f80def 100644
--- a/src/documentation/content/xdocs/site.xml
+++ b/src/documentation/content/xdocs/site.xml
@@ -16,6 +16,7 @@
 
 <site label="POI" href="" xmlns="http://apache.org/forrest/linkmap/1.0">
 
+
     <external-refs>
         <xml.apache.org href="http://xml.apache.org/">
             <forrest href="forrest/">
@@ -31,7 +32,8 @@
         <junit href="junit/index.html"/>
         <jdepend href="jdepend/index.html"/>
         <javadoc href="apidocs/index.html"/>
-	<download href="http://jakarta.apache.org/builds/jakarta-poi/"/>
+        <download href="http://jakarta.apache.org/builds/jakarta-poi/"/>
+        <changes href="changes.html"/>
     </external-refs>
 
 </site>
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 6a21002..2cf814a 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -1,18 +1,191 @@
 <?xml version="1.0"?>
 <status>
-    <devs>
+    <developers>
         <!-- in strict alphabetical order -->
         <person id="AO" name="Andrew C. Oliver" email="acoliver2@users.sourceforge.net"/>
         <person id="GS" name="Glen Stampoultzis" email="gstamp@iprimus.com.au"/>
         <person id="MJ" name="Marc Johnson" email="mjohnson at apache dot org"/>
         <person id="NKB" name="Nicola Ken Barozzi" email="barozzi@nicolaken.com"/>
         <person id="open" name="Poi Developers" email="poi-dev@jakarta.apache.org"/>
-    </devs>
+    </developers>
+
+    <changes>
+
+        <release verion="2.0-RC1" date="2 November 2003">
+            <action dev="POI-DEVELOPERS" type="fix" context="All">12561 (Min)	HSSFWorkbook throws Exceptions</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">12730 (Nor)	values dont get copied to another sheet.</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">13224 (Maj)	Exception thrown when cell has =Names call</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">13796 (Nor)	Error Reading Formula Record (optimized if, external link)</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">13921 (Nor)	Sheet name cannot exceed 31 characters and cannot contain :</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">14330 (Nor)	Error reading FormulaRecord</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">14460 (Nor)	Name in Formula - ArrayOutOfBoundsException</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">15228 (Cri)	[Urgent] ArrayIndexoutofbounds Exception. POI - Version 1.8</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">16488 (Maj)	Unable to open written spreadsheet in Excel, but can in Open</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">16559 (Nor)	testCustomPalette.xls crashes Excel 97</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">16560 (Nor)	testBoolErr.xls crashes Excel '97</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">17374 (Min)	HSSFFont - BOLDWEIGHT_NORMAL</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">18800 (Maj)	The sheet made by HSSFWorkbook#cloneSheet() doesn't work cor</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">18846 (Min)	[PATCH][RFE]Refactor the transformation between byte array a</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">19599 (Min)	java.lang.IllegalArgumentException</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">19961 (Nor)	[PATCH] Sheet.getColumnWidth() returns wrong value</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">21066 (Blo)	Can not modify a blank spreadsheet</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">21444 (Enh)	[PATCH] Macro functions</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">21447 (Nor)	[RFE]String Formula Cells</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">21674 (Enh)	[PATCH] Documentation changes for @(Greater|Less|Not)EqualPt</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">21863 (Enh)	[PATCH] build.xml fixes</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">22195 (Nor)	[RFE] [PATCH] Support for Storage Class ID</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">22742 (Cri)	Failed to create HSSFWorkbook!</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">22922 (Cri)	HSSFSheet.shiftRows() throws java.lang.IndexOutOfBoundsExcep</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">22963 (Nor)	org.apache.poi.hpsf.SummaryInformation.getEditTime() should</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">24149 (Maj)	Error passing inputstream to POIFSFileSystem</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">21722 (Nor)	[PATCH] Add a ProtectRecord to Sheets and give control over</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">9576  (Nor) 	[PATCH] DBCELL, INDEX EXTSST (was Acess 97 import)</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">13478 (Blo)	[PATCH] [RFE] POIFS, RawDataBlock: Missing workaround for lo</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">14824 (Nor)	Unable to modify empty sheets</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">12843 (Cri)	[PATCH] Make POI handle chinese better</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">15353 (Nor)	[RFE] creating a cell with a hyperlink</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">15375 (Blo)	Post 1.5.1 POI causes spreadsheet to become unopenable.</action>
+        </release>
+
+        <release version="2.0-pre3" date="29 July 2003">
+            <action dev="POI-DEVELOPERS" type="add" context="All">HPSF is now able to read properties which are given in the property set stream but which don't have a value ("variant" type VT_EMPTY). The getXXX() methods of the PropertySet class return null if their return type is a reference (like a string) or 0 if the return type is numeric. Details about the return types and about how to distinguish between a property value of zero and a property value that is not present can be found in the API documentation.</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Gridlines can now be turned on and off</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">NamePTG refactoring/fixes</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">minor fixes to ExternSheet and formula strings</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Sheet comparisons now ignore case</action>
+        </release>
+
+        <release version="2.0-pre2" date="6 July 2003">
+            <action dev="POI-DEVELOPERS" type="fix" context="All" >A nasty concurrency problem has been fixed. Any users working in a multithreaded environment should seriously consider upgrading to this release.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">The EXTSST record has been implemented. This record is used by excel for optimized reading of strings.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">When rows are shifted, the merged regions now move with them. If a row contains 2 merged cells, the resulting shifted row should have those cells merged as well.</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">There were some issues when removing merged
+		    regions (specifically, removing all of them and then adding some more) and have been resolved.</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">When a sheet contained shared formulas (when a formula is
+		    dragged across greater than 6 cells), the clone would fail. We now support cloning of 
+		    sheets that contain this Excel optimization. </action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Support added for reading formulas with UnaryPlus and UnaryMinus operators.</action>
+        </release>
+        <release version="2.0-pre1" date="17 May 2003">
+            <action dev="POI-DEVELOPERS" type="add" context="All">Patch applied for deep cloning of worksheets was provided</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Patch applied to allow sheet reordering</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Added additional print area setting methods using row/column numbers</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">HDF:  Negative Array size fix</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added argument pointers to support the IF formula</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Formulas: Added special character support for string literals, specifically for SUMIF formula support and addresses a bug as well</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">BlockingInputStream committed to help ensure reads</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Fixed problem with NaN values differing from the investigated value from file reads in FormulaRecords</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Patch for getColumnWidth in HSSF</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Patch for dealing with mult-level numbered lists in HDF</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Due to named reference work, several named-ranged bugs were closed</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Patch applied to prevent sheet corruption after a template modification</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Shared Formulas now Supported</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added GreaterEqual, LessEqual and NotEqual to Formula Parser</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added GreaterThan and LessThan functionality to formulas</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Patches for i10n</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">POI Build System Updated</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">font names can now be null</action>
+        </release>
+        <release version="1.10-dev" date="19 Feb 2003">
+            <action dev="POI-DEVELOPERS" type="add" context="All">Support for zoom level</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Freeze and split pane support</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Row and column headers on printouts</action>
+        </release>
+        <release version="1.8-dev" date="20 Sep 2002">
+            <action dev="POI-DEVELOPERS" type="add" context="All">Custom Data Format Support</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Enhanced Unicode Support for Russian and Japanese</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Enhanced formula support including read-only for
+                "optimized if" statements.</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Support for cloning objects</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Fixes for header/footer</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Spanish Documentation translations</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Support for preserving VBA macros</action>
+        </release>
+        <release version="1.7-dev" date="???">
+            <action dev="NKB" type="update" context="All">Removed runtime dependency on commons logging.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Formula support</action>
+        </release>
+        <release version="1.5.1" date="16 June 2002">
+            <action dev="GJS" type="update" context="All">Removed depedency on commons logging.  Now define poi.logging system property to enable logging to standard out.</action>
+            <action dev="GJS" type="fix" context="All">Fixed SST string handling so that spreadsheets with rich text or extended text will be read correctly.</action>
+        </release>
+        <release version="1.5" date="06 May 2002">
+            <action dev="NKB" type="update" context="All">New project build.</action>
+            <action dev="NKB" type="update" context="All">New project documentation system based on Cocoon.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Package rename</action>
+            <action dev="POI-DEVELOPERS" type="fix" context="All">Various bug fixes</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Early stages of HSF development (not ready for development)</action>
+            <action dev="POI-DEVELOPERS" type="add" context="All">Initial low level record support for charting (not complete)</action>
+        </release>
+        <release version="1.1.0" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Created new event model</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Optimizations made to HSSF including aggregate records for
+                values, rows, etc.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">predictive sizing, offset based writing (instead of lots of
+                array copies)</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">minor re-factoring and bug fixes.</action>
+        </release>
+        <release version="1.0.0" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Minor documentation updates.</action>
+        </release>
+        <release version="0.14.0" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added DataFormat helper class and exposed set and get format
+                on HSSFCellStyle</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Fixed column width apis (unit wise) and various javadoc on
+                the subject</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Fix for Dimensions record (again)... (one of these days I'll
+                write a unit test for this ;-p).</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Some optimization on sheet creation.</action>
+        </release>
+        <release version="0.13.0" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Changes not recorded.</action>
+        </release>
+        <release version="0.12.0" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added MulBlank, Blank, ColInfo</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added log4j facility and removed all sys.out type logging</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added support for adding font's, styles and corresponding
+                high level api for styling cells</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">added support for changing row height, cell width and default
+                row height/cell width.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added fixes for internationalization (UTF-16 should work now
+                from HSSFCell.setStringValue, etc when the encoding is set)</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">added support for adding/removing and naming sheets.</action>
+        </release>
+        <release version="0.11.0" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Bugfix release. We were throwing an exception when reading
+                RKRecord objects.</action>
+        </release>
+        <release version="0.10.0" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Got continuation records to work (read/write)</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added various pre-support for formulas</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Massive API reorganization, repackaging.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">BiffViewer class added for validating HSSF &amp; POI and/or
+                HSSF Output.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Better API support for modification.</action>
+        </release>
+        <release version="0.7 (and interim releases)" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Added encoding flag to high and low level api to use utf-16
+                when needed (HSSFCell.setEncoding())</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">added read only support for Label records (which are
+                reinterpreted as LabelSST when written)</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Broken continuation record implementation (oops)</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">BiffViewer class added for validating HSSF &amp; POI and/or
+                HSSF Output.</action>
+        </release>
+        <release version="0.6 (release)" date="Release date not recorded">
+            <action dev="POI-DEVELOPERS" type="update" context="All">Support for read/write and modify.</action>
+            <action dev="POI-DEVELOPERS" type="update" context="All">Read only support for MulRK records (converted to Number when
+                writing)
+            </action>
+        </release>
+
+    </changes>
+
 
     <todo>
-        <title>Things To Do for POI</title>
+<!--        <title>Things To Do for POI</title>-->
 
-        <actions priority="high">
+        <actions context="all" priority="high">
             <action context="code" dev="NKB">
                 Finish HDF
             </action>
@@ -24,7 +197,7 @@
             </action>
         </actions>
 
-        <actions priority="medium">
+        <actions context="all" priority="medium">
             <action context="code" dev="open">
                 Expose functionality in low level records in higher level API
             </action>
@@ -50,103 +223,4 @@
 
     </todo>
 
-    <changes>
-
-        <title>History of Changes</title>
-
-        <release version="1.10-dev" date="19 Feb 2003">
-            <action dev="POI-DEVELOPERS" type="add">Support for zoom level</action>
-            <action dev="POI-DEVELOPERS" type="add">Freeze and split pane support</action>
-            <action dev="POI-DEVELOPERS" type="add">Row and column headers on printouts</action>
-        </release>
-        <release version="1.8-dev" date="20 Sep 2002">
-            <action dev="POI-DEVELOPERS" type="add">Custom Data Format Support</action>
-            <action dev="POI-DEVELOPERS" type="add">Enhanced Unicode Support for Russian and Japanese</action>
-            <action dev="POI-DEVELOPERS" type="add">Enhanced formula support including read-only for
-                "optimized if" statements.</action>
-            <action dev="POI-DEVELOPERS" type="add">Support for cloning objects</action>
-            <action dev="POI-DEVELOPERS" type="add">Fixes for header/footer</action>
-            <action dev="POI-DEVELOPERS" type="add">Spanish Documentation translations</action>
-            <action dev="POI-DEVELOPERS" type="add">Support for preserving VBA macros</action>
-        </release>
-        <release version="1.7-dev" date="???">
-            <action dev="NKB" type="update">Removed runtime dependency on commons logging.</action>
-            <action dev="POI-DEVELOPERS" type="update">Formula support</action>
-        </release>
-        <release version="1.5.1" date="16 June 2002">
-            <action dev="GJS" type="update">Removed depedency on commons logging.  Now define poi.logging system property to enable logging to standard out.</action>
-            <action dev="GJS" type="fix">Fixed SST string handling so that spreadsheets with rich text or extended text will be read correctly.</action>
-        </release>
-        <release version="1.5" date="06 May 2002">
-            <action dev="NKB" type="update">New project build.</action>
-            <action dev="NKB" type="update">New project documentation system based on Cocoon.</action>
-            <action dev="POI-DEVELOPERS" type="update">Package rename</action>
-            <action dev="POI-DEVELOPERS" type="fix">Various bug fixes</action>
-            <action dev="POI-DEVELOPERS" type="add">Early stages of HSF development (not ready for development)</action>
-            <action dev="POI-DEVELOPERS" type="add">Initial low level record support for charting (not complete)</action>
-        </release>
-        <release version="1.1.0" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Created new event model</action>
-            <action dev="POI-DEVELOPERS">Optimizations made to HSSF including aggregate records for
-                values, rows, etc.</action>
-            <action dev="POI-DEVELOPERS">predictive sizing, offset based writing (instead of lots of
-                array copies)</action>
-            <action dev="POI-DEVELOPERS">minor re-factoring and bug fixes.</action>
-        </release>
-        <release version="1.0.0" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Minor documentation updates.</action>
-        </release>
-        <release version="0.14.0" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Added DataFormat helper class and exposed set and get format
-                on HSSFCellStyle</action>
-            <action dev="POI-DEVELOPERS">Fixed column width apis (unit wise) and various javadoc on
-                the subject</action>
-            <action dev="POI-DEVELOPERS">Fix for Dimensions record (again)... (one of these days I'll
-                write a unit test for this ;-p).</action>
-            <action dev="POI-DEVELOPERS">Some optimization on sheet creation.</action>
-        </release>
-        <release version="0.13.0" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Changes not recorded.</action>
-        </release>
-        <release version="0.12.0" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Added MulBlank, Blank, ColInfo</action>
-            <action dev="POI-DEVELOPERS">Added log4j facility and removed all sys.out type logging</action>
-            <action dev="POI-DEVELOPERS">Added support for adding font's, styles and corresponding
-                high level api for styling cells</action>
-            <action dev="POI-DEVELOPERS">added support for changing row height, cell width and default
-                row height/cell width.</action>
-            <action dev="POI-DEVELOPERS">Added fixes for internationalization (UTF-16 should work now
-                from HSSFCell.setStringValue, etc when the encoding is set)</action>
-            <action dev="POI-DEVELOPERS">added support for adding/removing and naming sheets.</action>
-        </release>
-        <release version="0.11.0" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Bugfix release. We were throwing an exception when reading
-                RKRecord objects.</action>
-        </release>
-        <release version="0.10.0" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Got continuation records to work (read/write)</action>
-            <action dev="POI-DEVELOPERS">Added various pre-support for formulas</action>
-            <action dev="POI-DEVELOPERS">Massive API reorganization, repackaging.</action>
-            <action dev="POI-DEVELOPERS">BiffViewer class added for validating HSSF &amp; POI and/or
-                HSSF Output.</action>
-            <action dev="POI-DEVELOPERS">Better API support for modification.</action>
-        </release>
-        <release version="0.7 (and interim releases)" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Added encoding flag to high and low level api to use utf-16
-                when needed (HSSFCell.setEncoding())</action>
-            <action dev="POI-DEVELOPERS">added read only support for Label records (which are
-                reinterpreted as LabelSST when written)</action>
-            <action dev="POI-DEVELOPERS">Broken continuation record implementation (oops)</action>
-            <action dev="POI-DEVELOPERS">BiffViewer class added for validating HSSF &amp; POI and/or
-                HSSF Output.</action>
-        </release>
-        <release version="0.6 (release)" date="Release date not recorded">
-            <action dev="POI-DEVELOPERS">Support for read/write and modify.</action>
-            <action dev="POI-DEVELOPERS">Read only support for MulRK records (converted to Number when
-                writing)
-            </action>
-        </release>
-
-    </changes>
-
-</status>
\ No newline at end of file
+</status>
diff --git a/src/documentation/content/xdocs/tabs.xml b/src/documentation/content/xdocs/tabs.xml
index 761cf49..7a4b189 100644
--- a/src/documentation/content/xdocs/tabs.xml
+++ b/src/documentation/content/xdocs/tabs.xml
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE tabs PUBLIC "-//APACHE//DTD Cocoon Documentation Tab V1.0//EN" "tab-cocoon-v10.dtd">
 
-<tabs software="MyProj"
-  title="MyProj"
-  copyright="Foo"
+<tabs software="POI"
+  title="POI"
+  copyright="Apache Foundation"
   xmlns:xlink="http://www.w3.org/1999/xlink">
 
   <!-- The rules are:
diff --git a/src/documentation/content/xdocs/trans/de/book.xml b/src/documentation/content/xdocs/trans/de/book.xml
index 5810a96..9e2fa86 100644
--- a/src/documentation/content/xdocs/trans/de/book.xml
+++ b/src/documentation/content/xdocs/trans/de/book.xml
@@ -8,7 +8,7 @@
 
   <menu label="Gemeinschaft">
     <menu-item label="Neuigkeiten" href="../../news.html"/>
-    <menu-item label="Änderungen" href="../../changes.html"/>
+    <menu-item label="Änderungen" href="/changes.html"/>
     <menu-item label="Aufgaben" href="../../todo.html"/>
     <menu-item label="Mitmachen" href="../../getinvolved/index.html"/>
     <menu-item label="Unsere Vision" href="../../plan/POI20Vision.html"/>
diff --git a/src/documentation/content/xdocs/trans/es/book.xml b/src/documentation/content/xdocs/trans/es/book.xml
index 602e75c..61346d1 100644
--- a/src/documentation/content/xdocs/trans/es/book.xml
+++ b/src/documentation/content/xdocs/trans/es/book.xml
@@ -9,7 +9,7 @@
   <menu label="Comunidad">
     <menu-item label="Noticias" href="news.html"/>    
     <menu-item label="Espejos" href="../../mirrors.html"/>    
-    <menu-item label="Cambios" href="changes.html"/> 
+    <menu-item label="Cambios" href="site:changes"/>
     <menu-item label="Tareas" href="todo.html"/> 
     <menu-item label="Contribuya" href="../../getinvolved/index.html"/>
     <menu-item label="Listas de Correo" href="http://jakarta.apache.org/site/mail.html"/>
diff --git a/src/documentation/content/xdocs/trans/es/changes.xml b/src/documentation/content/xdocs/trans/es/changes.xml
deleted file mode 100644
index 62292be..0000000
--- a/src/documentation/content/xdocs/trans/es/changes.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!DOCTYPE changes PUBLIC "-//APACHE//DTD Changes V1.1//EN" "../../dtd/changes-v11.dtd">
-
-<changes><title>Historial de Cambios</title>
-
-    <devs>
-        <!-- in strict alphabetical order -->
-        <person id="AO"  name="Andrew C. Oliver"   email="acoliver2@users.sourceforge.net"/>
-        <person id="GJS"  name="Glen Stampoultzis" email="glens@apache.org"/>
-        <person id="MJ"  name="Marc Johnson"       email="mjohnson@apache.org"/>
-        <person id="NKB" name="Nicola Ken Barozzi" email="barozzi@nicolaken.com"/>
-        <person id="POI-DEVELOPERS" name="Poi Developers"    email="poi-dev@jakarta.apache.org"/>
-    </devs>
-    <release version="1.8-dev" date="20 Septiembre 2002">
-        <action dev="POI-DEVELOPERS" type="add">Soporte para Formato de Datos Personalizado (Custom)</action>
-        <action dev="POI-DEVELOPERS" type="add">Soporte Unicode Mejorado para Ruso y Japonés</action>
-        <action dev="POI-DEVELOPERS" type="add">Soporte para Fórmulas Mejorado, incluyendo
-            sólo-lectura para sentencias tipo "optimizado si" (optimized if).</action>
-        <action dev="POI-DEVELOPERS" type="add">Soporte para la clonación de objetos</action>
-        <action dev="POI-DEVELOPERS" type="add">Arreglos en la cabecera/pie</action>
-        <action dev="POI-DEVELOPERS" type="add">Traducciones de la documentación al Español</action>
-        <action dev="POI-DEVELOPERS" type="add">Soporte para preservar macros VBA</action>
-    </release>
-    <release version="1.7-dev" date="???">
-        <action dev="NKB" type="update">Se quita la dependencia en tiempo de ejecución del registro
-	(logging) de "commons".</action>
-        <action dev="POI-DEVELOPERS" type="update">Soporte para fórmulas</action>
-    </release>
-    <release version="1.5.1" date="16 Junio 2002">
-        <action dev="GJS" type="update">Se quita la dependencia del registro de "commons". Ahora hay que definir la propiedad del sistema poi.loggin para permitir
-	que los registros (logs) vayan a la salida estándar.</action>
-        <action dev="GJS" type="fix">Se arregla la gestión de la cadena SST para que las hojas de cálculo con texto rico (rich text) o texto
-	extendido se lean correctamente.</action>
-    </release>
-    <release version="1.5" date="06 Mayo 2002">
-        <action dev="NKB" type="update">Nueva versión (build) del proyecto.</action>
-        <action dev="NKB" type="update">Nuevo sistema de documentación del proyecto basada en  Cocoon.</action>
-        <action dev="POI-DEVELOPERS" type="update">Cambio de nombre del paquete</action>
-        <action dev="POI-DEVELOPERS" type="fix">Varios errores (bugs) corregidos</action>
-        <action dev="POI-DEVELOPERS" type="add">Etapas preliminares del desarrollo de HFS (no esta listo para el desarrollo)</action>
-        <action dev="POI-DEVELOPERS" type="add">Soporte inicial de registros de bajo nivel para gráficas (no está completo)</action>
-    </release>
-    <release version="1.1.0" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Se crea un nuevo modelo de eventos</action>
-        <action dev="POI-DEVELOPERS">Se optimiza HSSF, incluyendo registros (records) para
-	valores, filas, etc.</action>
-        <action dev="POI-DEVELOPERS">predicción de tamaño, escritura basada en desplazamiento (en lugar de
-	multitud de copias de arrays)</action>
-        <action dev="POI-DEVELOPERS">un poco de re-factoring (¿re-factorización? mejor no) y corrección de errores.</action>
-    </release>
-    <release version="1.0.0" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Actualizaciones menores a la documentación.</action>
-    </release>
-    <release version="0.14.0" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Se añade la clase de ayuda DataFormat y se expone el formato set y get en
-	HSSFCellStyle</action>
-        <action dev="POI-DEVELOPERS">Correcciones a las apis de anchura de columna (en cuanto a las unidades) y
-	varios comentarios javadoc al respecto</action>
-        <action dev="POI-DEVELOPERS">Corrección para el registro de "Dimensions" (de nuevo)... (uno de estos días
-	escribiré una prueba unitaria (unit test) para esto ;-p).</action>
-        <action dev="POI-DEVELOPERS">Alguna optimización en la creación de páginas.</action>
-    </release>
-    <release version="0.13.0" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Mejoras no registradas</action>
-    </release>
-    <release version="0.12.0" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Se añaden MulBlank, Blank, ColInfo</action>
-        <action dev="POI-DEVELOPERS">Se añade facilidad log4j y se quitan las anotaciones (logs) del tipo sys.out</action>
-        <action dev="POI-DEVELOPERS">Se añade soporte para la adición de fuentes, estilos y el api de alto
-	nivel correspondiente para dar estilo a las celdas</action>
-        <action dev="POI-DEVELOPERS">Se añade soporte para cambiar el alto de una fila, el ancho de una celda, y
-	sus valores por defecto.</action>
-        <action dev="POI-DEVELOPERS">Correcciones para internacionalización (UTF-16 debería funcionar ahora
-	desde HSSFCell.setStringValue, etc cuando se define la codificación)</action>
-        <action dev="POI-DEVELOPERS">Soporte para la adición / eliminación y cambio de nombre de hojas.</action>
-    </release>
-    <release version="0.11.0" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Distribución de corrección de errores.
-	Lanzamos una excepción cuando leemos objetos de tipo RKRecord.</action>
-    </release>
-    <release version="0.10.0" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Registros de continuación ya funcionan (lectura/escritura)</action>
-        <action dev="POI-DEVELOPERS">Se añade soporte previo para fórmulas</action>
-        <action dev="POI-DEVELOPERS">Reorganización del API masiva, re-enpaquetado.</action>
-        <action dev="POI-DEVELOPERS">Se añade la clase BiffViewer para validar HSSF &amp; POI y/o la
-	salida de HSSF.</action>
-        <action dev="POI-DEVELOPERS">Se mejora el soporte a la modificación del API.</action>
-    </release>
-    <release version="0.7 (y distribuciones internas)" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Se añade una bandera de codificación para que las apis de alto
-	y bajo nivel utilicen utf-16 cuando sea necesario (HSSFCell.setEncoding())</action>
-        <action dev="POI-DEVELOPERS">Se añade soporte de sólo lectura a registros de Etiqueta
-	(que son reinterpretados como LabelSST cuando se escriben)</action>
-        <action dev="POI-DEVELOPERS">Se rompe la implementación del registro de continuación (oops)</action>
-        <action dev="POI-DEVELOPERS">Se añade la clase BiffViewer
-	para validar HSSF &amp; POI y/o la
-	salida de HSSF.</action>
-    </release>
-    <release version="0.6 (distribución/release)" date="Fecha de distribución no registrada">
-        <action dev="POI-DEVELOPERS">Soporte para lectura/escritura y modificación.</action>
-        <action dev="POI-DEVELOPERS">Soporte de sólo lectura para registros de tipo MulRK
-	(convertidos a Number cuando se escriben)
-        </action>
-    </release>
-
-</changes>
diff --git a/src/documentation/resources/images/group-logo.gif b/src/documentation/resources/images/group-logo.gif
index 543f686..472e47f 100644
--- a/src/documentation/resources/images/group-logo.gif
+++ b/src/documentation/resources/images/group-logo.gif
Binary files differ
diff --git a/src/documentation/skinconf.xml b/src/documentation/skinconf.xml
index 4606684..86dcc52 100644
--- a/src/documentation/skinconf.xml
+++ b/src/documentation/skinconf.xml
@@ -9,22 +9,29 @@
 
   <!ENTITY % links.att 'name CDATA #REQUIRED'>
   <!ENTITY % link.att 'name CDATA #REQUIRED href CDATA #REQUIRED'>
-  <!ELEMENT skinconfig (disable-search?, disable-compliance-links?, searchsite-domain?, searchsite-name?,
-  project-name, project-url, project-logo, group-name?, group-url?, group-logo?,
-  host-url?, host-logo?, year?, vendor?, trail?, credits?)*>
+  <!ELEMENT skinconfig (disable-search?, disable-print-link?, disable-pdf-link?,
+  disable-xml-link?, disable-compliance-links?, obfuscate-mail-links?, searchsite-domain?, searchsite-name?,
+  project-name, project-description?, project-url, project-logo, group-name?, group-description?, group-url?, group-logo?,
+  host-url?, host-logo?, year?, vendor?, trail?, toc?, credits?)*>
   <!ELEMENT credits (credit*)>
   <!ELEMENT credit (name, url, image?, width?, height?)>
   <!-- id uniquely identifies the tool, and role indicates its function -->
   <!ATTLIST credit id   CDATA #IMPLIED
                    role CDATA #IMPLIED>
   <!ELEMENT disable-search (#PCDATA)>
+  <!ELEMENT disable-print-link (#PCDATA)>
+  <!ELEMENT disable-pdf-link (#PCDATA)>
+  <!ELEMENT disable-xml-link (#PCDATA)>
   <!ELEMENT disable-compliance-links (#PCDATA)>
+  <!ELEMENT obfuscate-mail-links (#PCDATA)>
   <!ELEMENT searchsite-domain (#PCDATA)>
-  <!ELEMENT searchsite-name (#PCDATA)>  
+  <!ELEMENT searchsite-name (#PCDATA)>
   <!ELEMENT project-name (#PCDATA)>
+  <!ELEMENT project-description (#PCDATA)>
   <!ELEMENT project-url (#PCDATA)>
   <!ELEMENT project-logo (#PCDATA)>
   <!ELEMENT group-name (#PCDATA)>
+  <!ELEMENT group-description (#PCDATA)>
   <!ELEMENT group-url (#PCDATA)>
   <!ELEMENT group-logo (#PCDATA)>
   <!ELEMENT host-url (#PCDATA)>
@@ -44,12 +51,15 @@
   <!ELEMENT image (#PCDATA)>
   <!ELEMENT width (#PCDATA)>
   <!ELEMENT height (#PCDATA)>
+  <!ELEMENT toc EMPTY>
+  <!ATTLIST toc level CDATA #IMPLIED>
   ]>
 
 <skinconfig>
   <!-- Do we want to disable the Google search box? -->
   <disable-search>false</disable-search>
   <disable-compliance-links>false</disable-compliance-links>
+  <disable-xml-link>true</disable-xml-link>
   <searchsite-domain>jakarta.apache.org</searchsite-domain>
   <searchsite-name>jakarta</searchsite-name>
 
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/ReadWriteWorkbook.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/ReadWriteWorkbook.java
index f80afb9..4080a4d 100644
--- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/ReadWriteWorkbook.java
+++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/ReadWriteWorkbook.java
@@ -80,6 +80,8 @@
         HSSFWorkbook wb = new HSSFWorkbook(fs);
         HSSFSheet sheet = wb.getSheetAt(0);
         HSSFRow row = sheet.getRow(2);
+        if (row == null)
+            row = sheet.createRow(2);
         HSSFCell cell = row.getCell((short)3);
         if (cell == null)
             cell = row.createCell((short)3);
diff --git a/src/java/org/apache/poi/hpsf/ClassID.java b/src/java/org/apache/poi/hpsf/ClassID.java
index 6d56767..e9939af 100644
--- a/src/java/org/apache/poi/hpsf/ClassID.java
+++ b/src/java/org/apache/poi/hpsf/ClassID.java
@@ -56,6 +56,8 @@
 package org.apache.poi.hpsf;
 
 import java.io.*;
+
+import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -98,15 +100,21 @@
      */
     public ClassID()
     {
-	bytes = new byte[LENGTH];
-	for (int i = 0; i < LENGTH; i++)
-	    bytes[i] = 0x00;
+        bytes = new byte[LENGTH];
+        for (int i = 0; i < LENGTH; i++)
+            bytes[i] = 0x00;
     }
 
 
 
-    public final static int LENGTH = 16;
+    /** <p>The number of bytes occupied by this object in the byte
+     * stream.</p> */
+    public static final int LENGTH = 16;
 
+    /**
+     * @return The number of bytes occupied by this object in the byte
+     * stream.
+     */
     public int length()
     {
         return LENGTH;
@@ -117,10 +125,12 @@
     /**
      * <p>Gets the bytes making out the class ID. They are returned in
      * correct order, i.e. big-endian.</p>
+     * 
+     * @return the bytes making out the class ID.
      */
     public byte[] getBytes()
     {
-	return bytes;
+        return bytes;
     }
 
 
@@ -153,9 +163,9 @@
         bytes[6] = src[7 + offset];
         bytes[7] = src[6 + offset];
 
-	/* Read 8 bytes. */
-	for (int i = 8; i < 16; i++)
-	    bytes[i] = src[i + offset];
+        /* Read 8 bytes. */
+        for (int i = 8; i < 16; i++)
+            bytes[i] = src[i + offset];
 
         return bytes;
     }
@@ -170,30 +180,75 @@
      *
      * @param offset The offset within the <var>dst</var> byte array.
      *
-     * @throws ArrayIndexOutOfBoundsException if there is not enough
-     * room for the class ID in the byte array. There must be at least
-     * 16 bytes in the byte array after the <var>offset</var>
-     * position.
+     * @exception ArrayStoreException if there is not enough room for the class 
+     * ID 16 bytes in the byte array after the <var>offset</var> position.
      */
     public void write(final byte[] dst, final int offset)
+    throws ArrayStoreException
     {
+        /* Check array size: */
+        if (dst.length < 16)
+            throw new ArrayStoreException
+                ("Destination byte[] must have room for at least 16 bytes, " +
+                 "but has a length of only " + dst.length + ".");
         /* Write double word. */
-	dst[0 + offset] = bytes[3];
-	dst[1 + offset] = bytes[2];
-	dst[2 + offset] = bytes[1];
-	dst[3 + offset] = bytes[0];
+        dst[0 + offset] = bytes[3];
+        dst[1 + offset] = bytes[2];
+        dst[2 + offset] = bytes[1];
+        dst[3 + offset] = bytes[0];
 
         /* Write first word. */
-	dst[4 + offset] = bytes[5];
-	dst[5 + offset] = bytes[4];
+        dst[4 + offset] = bytes[5];
+        dst[5 + offset] = bytes[4];
 
         /* Write second word. */
-	dst[6 + offset] = bytes[7];
-	dst[7 + offset] = bytes[6];
+        dst[6 + offset] = bytes[7];
+        dst[7 + offset] = bytes[6];
 
-	/* Write 8 bytes. */
-	for (int i = 8; i < 16; i++)
-	    dst[i + offset] = bytes[i];
+        /* Write 8 bytes. */
+        for (int i = 8; i < 16; i++)
+            dst[i + offset] = bytes[i];
     }
 
+
+
+    /**
+     * <p>Checks whether this <code>ClassID</code> is equal to another
+     * object.</p>
+     *
+     * @param o the object to compare this <code>PropertySet</code> with
+     * @return <code>true</code> if the objects are equal, else
+     * <code>false</code>.</p>
+     */
+    public boolean equals(final Object o)
+    {
+        if (o == null || !(o instanceof ClassID))
+            return false;
+        final ClassID cid = (ClassID) o;
+        if (bytes.length != cid.bytes.length)
+            return false;
+        for (int i = 0; i < bytes.length; i++)
+            if (bytes[i] != cid.bytes[i])
+                return false;
+        return true;
+    }
+    /**
+     * Returns a human readable representation of the Class ID
+     *   in standard format <code>"{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"</code>
+     * @return String representation of the Class ID represented
+     *   by this object.
+     */
+    public String toString()
+    {
+        StringBuffer sbClassId = new StringBuffer( 38);
+        sbClassId.append( '{');
+        for( int i=0; i < 16; i++) {
+            sbClassId.append( HexDump.toHex( bytes[ i]));
+            if( i == 3 || i == 5 || i == 7 || i == 9) {
+                sbClassId.append( '-');
+            }
+        }
+        sbClassId.append( '}');
+        return sbClassId.toString();
+    }
 }
diff --git a/src/java/org/apache/poi/hpsf/TypeReader.java b/src/java/org/apache/poi/hpsf/TypeReader.java
index a6264f7..dc441b4 100644
--- a/src/java/org/apache/poi/hpsf/TypeReader.java
+++ b/src/java/org/apache/poi/hpsf/TypeReader.java
@@ -62,7 +62,6 @@
  */
 package org.apache.poi.hpsf;
 
-import java.util.*;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -85,6 +84,7 @@
      * starts
      * @param length The length of the variant including the variant
      * type field
+     * @param type The variant type to read
      * @return A Java object that corresponds best to the variant
      * field. For example, a VT_I4 is returned as a {@link Long}, a
      * VT_LPSTR as a {@link String}.
@@ -92,15 +92,25 @@
      * @see Variant
      */
     public static Object read(final byte[] src, int offset, int length,
-			      final int type)
+                              final int type)
     {
-	/*
-	 * FIXME: Support reading more types and clean up this code!
-	 */
-	Object value;
-	length = length - LittleEndian.INT_SIZE;
+        /*
+         * FIXME: Support reading more types and clean up this code!
+         */
+        Object value;
+        length = length - LittleEndian.INT_SIZE;
         switch (type)
-	{
+        {
+            case Variant.VT_EMPTY:
+            {
+                /*
+                 * FIXME: The value returned by this case relies on the
+                 * assumption that the value VT_EMPTY denotes consists of zero 
+                 * bytes. I'd be glad if some could confirm or correct this. 
+                 */
+                value = null;
+                break;
+            }
             case Variant.VT_I2:
             {
                 /*
@@ -137,11 +147,11 @@
                  * Read a byte string. In Java it is represented as a
                  * String object. The 0x00 bytes at the end must be
                  * stripped.
-		 *
-		 * FIXME: Reading an 8-bit string should pay attention
-		 * to the codepage. Currently the byte making out the
-		 * property's value are interpreted according to the
-		 * platform's default character set.
+                 *
+                 * FIXME: Reading an 8-bit string should pay attention
+                 * to the codepage. Currently the byte making out the
+                 * property's value are interpreted according to the
+                 * platform's default character set.
                  */
                 final int first = offset + LittleEndian.INT_SIZE;
                 long last = first + LittleEndian.getUInt(src, offset) - 1;
@@ -149,7 +159,7 @@
                 while (src[(int) last] == 0 && first <= last)
                     last--;
                 value = new String(src, (int) first, (int) (last - first + 1));
-		break;
+                break;
             }
             case Variant.VT_LPWSTR:
             {
@@ -160,27 +170,27 @@
                  */
                 final int first = offset + LittleEndian.INT_SIZE;
                 long last = first + LittleEndian.getUInt(src, offset) - 1;
-		long l = last - first;
+                long l = last - first;
                 offset += LittleEndian.INT_SIZE;
-		StringBuffer b = new StringBuffer((int) (last - first));
-		for (int i = 0; i <= l; i++)
-		{
-		    final int i1 = offset + (i * 2);
-		    final int i2 = i1 + 1;
-		    b.append((char) ((src[i2] << 8) + src[i1]));
-		}
-		/* Strip 0x00 characters from the end of the string: */
-		while (b.charAt(b.length() - 1) == 0x00)
-		    b.setLength(b.length() - 1);
-		value = b.toString();
-		break;
+                StringBuffer b = new StringBuffer((int) (last - first));
+                for (int i = 0; i <= l; i++)
+                {
+                    final int i1 = offset + (i * 2);
+                    final int i2 = i1 + 1;
+                    b.append((char) ((src[i2] << 8) + src[i1]));
+                }
+                /* Strip 0x00 characters from the end of the string: */
+                while (b.charAt(b.length() - 1) == 0x00)
+                    b.setLength(b.length() - 1);
+                value = b.toString();
+                break;
             }
             case Variant.VT_CF:
             {
                 final byte[] v = new byte[length];
                 for (int i = 0; i < length; i++)
                     v[i] = src[(int) (offset + i)];
-		value = v;
+                value = v;
                 break;
             }
             case Variant.VT_BOOL:
@@ -190,24 +200,24 @@
                  * src[offset + 3] contain the DWord for VT_BOOL, so
                  * skip it, we don't need it.
                  */
-                final int first = offset + LittleEndian.INT_SIZE;
+                // final int first = offset + LittleEndian.INT_SIZE;
                 long bool = LittleEndian.getUInt(src, offset);
                 if (bool != 0)
                     value = new Boolean(true);
                 else
                     value = new Boolean(false);
-		break;
+                break;
             }
             default:
             {
                 final byte[] v = new byte[length];
                 for (int i = 0; i < length; i++)
                     v[i] = src[(int) (offset + i)];
-		value = v;
+                value = v;
                 break;
             }
         }
-	return value;
+        return value;
     }
 
 }
diff --git a/src/java/org/apache/poi/hpsf/Variant.java b/src/java/org/apache/poi/hpsf/Variant.java
index f49ce4a..e40685d 100644
--- a/src/java/org/apache/poi/hpsf/Variant.java
+++ b/src/java/org/apache/poi/hpsf/Variant.java
@@ -76,178 +76,178 @@
     /**
      * <p>[V][P] Nothing.</p>
      */
-    public final static int VT_EMPTY = 0;
+    public static final int VT_EMPTY = 0;
 
     /**
      * <p>[V][P] SQL style Null.</p>
      */
-    public final static int VT_NULL = 1;
+    public static final int VT_NULL = 1;
 
     /**
      * <p>[V][T][P][S] 2 byte signed int.</p>
      */
-    public final static int VT_I2 = 2;
+    public static final int VT_I2 = 2;
 
     /**
      * <p>[V][T][P][S] 4 byte signed int.</p>
      */
-    public final static int VT_I4 = 3;
+    public static final int VT_I4 = 3;
 
     /**
      * <p>[V][T][P][S] 4 byte real.</p>
      */
-    public final static int VT_R4 = 4;
+    public static final int VT_R4 = 4;
 
     /**
      * <p>[V][T][P][S] 8 byte real.</p>
      */
-    public final static int VT_R8 = 5;
+    public static final int VT_R8 = 5;
 
     /**
      * <p>[V][T][P][S] currency. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_CY = 6;
+    public static final int VT_CY = 6;
 
     /**
      * <p>[V][T][P][S] date. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_DATE = 7;
+    public static final int VT_DATE = 7;
 
     /**
      * <p>[V][T][P][S] OLE Automation string. <span
      * style="background-color: #ffff00">How long is this? How is it
      * to be interpreted?</span></p>
      */
-    public final static int VT_BSTR = 8;
+    public static final int VT_BSTR = 8;
 
     /**
      * <p>[V][T][P][S] IDispatch *. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_DISPATCH = 9;
+    public static final int VT_DISPATCH = 9;
 
     /**
      * <p>[V][T][S] SCODE. <span style="background-color: #ffff00">How
      * long is this? How is it to be interpreted?</span></p>
      */
-    public final static int VT_ERROR = 10;
+    public static final int VT_ERROR = 10;
 
     /**
      * <p>[V][T][P][S] True=-1, False=0.</p>
      */
-    public final static int VT_BOOL = 11;
+    public static final int VT_BOOL = 11;
 
     /**
      * <p>[V][T][P][S] VARIANT *. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_VARIANT = 12;
+    public static final int VT_VARIANT = 12;
 
     /**
      * <p>[V][T][S] IUnknown *. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_UNKNOWN = 13;
+    public static final int VT_UNKNOWN = 13;
 
     /**
      * <p>[V][T][S] 16 byte fixed point.</p>
      */
-    public final static int VT_DECIMAL = 14;
+    public static final int VT_DECIMAL = 14;
 
     /**
      * <p>[T] signed char.</p>
      */
-    public final static int VT_I1 = 16;
+    public static final int VT_I1 = 16;
 
     /**
      * <p>[V][T][P][S] unsigned char.</p>
      */
-    public final static int VT_UI1 = 17;
+    public static final int VT_UI1 = 17;
 
     /**
      * <p>[T][P] unsigned short.</p>
      */
-    public final static int VT_UI2 = 18;
+    public static final int VT_UI2 = 18;
 
     /**
      * <p>[T][P] unsigned int.</p>
      */
-    public final static int VT_UI4 = 19;
+    public static final int VT_UI4 = 19;
 
     /**
      * <p>[T][P] signed 64-bit int.</p>
      */
-    public final static int VT_I8 = 20;
+    public static final int VT_I8 = 20;
 
     /**
      * <p>[T][P] unsigned 64-bit int.</p>
      */
-    public final static int VT_UI8 = 21;
+    public static final int VT_UI8 = 21;
 
     /**
      * <p>[T] signed machine int.</p>
      */
-    public final static int VT_INT = 22;
+    public static final int VT_INT = 22;
 
     /**
      * <p>[T] unsigned machine int.</p>
      */
-    public final static int VT_UINT = 23;
+    public static final int VT_UINT = 23;
 
     /**
      * <p>[T] C style void.</p>
      */
-    public final static int VT_VOID = 24;
+    public static final int VT_VOID = 24;
 
     /**
      * <p>[T] Standard return type. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_HRESULT = 25;
+    public static final int VT_HRESULT = 25;
 
     /**
      * <p>[T] pointer type. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_PTR = 26;
+    public static final int VT_PTR = 26;
 
     /**
      * <p>[T] (use VT_ARRAY in VARIANT).</p>
      */
-    public final static int VT_SAFEARRAY = 27;
+    public static final int VT_SAFEARRAY = 27;
 
     /**
      * <p>[T] C style array. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_CARRAY = 28;
+    public static final int VT_CARRAY = 28;
 
     /**
      * <p>[T] user defined type. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_USERDEFINED = 29;
+    public static final int VT_USERDEFINED = 29;
 
     /**
      * <p>[T][P] null terminated string.</p>
      */
-    public final static int VT_LPSTR = 30;
+    public static final int VT_LPSTR = 30;
 
     /**
      * <p>[T][P] wide (Unicode) null terminated string.</p>
      */
-    public final static int VT_LPWSTR = 31;
+    public static final int VT_LPWSTR = 31;
 
     /**
      * <p>[P] FILETIME. The FILETIME structure holds a date and time
@@ -256,50 +256,50 @@
      * have passed since January 1, 1601. This 64-bit value is split
      * into the two dwords stored in the structure.</p>
      */
-    public final static int VT_FILETIME = 64;
+    public static final int VT_FILETIME = 64;
 
     /**
      * <p>[P] Length prefixed bytes.</p>
      */
-    public final static int VT_BLOB = 65;
+    public static final int VT_BLOB = 65;
 
     /**
      * <p>[P] Name of the stream follows.</p>
      */
-    public final static int VT_STREAM = 66;
+    public static final int VT_STREAM = 66;
 
     /**
      * <p>[P] Name of the storage follows.</p>
      */
-    public final static int VT_STORAGE = 67;
+    public static final int VT_STORAGE = 67;
 
     /**
      * <p>[P] Stream contains an object. <span
      * style="background-color: #ffff00"> How long is this? How is it
      * to be interpreted?</span></p>
      */
-    public final static int VT_STREAMED_OBJECT = 68;
+    public static final int VT_STREAMED_OBJECT = 68;
 
     /**
      * <p>[P] Storage contains an object. <span
      * style="background-color: #ffff00"> How long is this? How is it
      * to be interpreted?</span></p>
      */
-    public final static int VT_STORED_OBJECT = 69;
+    public static final int VT_STORED_OBJECT = 69;
 
     /**
      * <p>[P] Blob contains an object. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_BLOB_OBJECT = 70;
+    public static final int VT_BLOB_OBJECT = 70;
 
     /**
      * <p>[P] Clipboard format. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_CF = 71;
+    public static final int VT_CF = 71;
 
     /**
      * <p>[P] A Class ID.</p>
@@ -331,34 +331,46 @@
      * target="_blank">
      * msdn.microsoft.com/library/en-us/com/stgrstrc_0uwk.asp</a>.</p>
      */
-    public final static int VT_CLSID = 72;
+    public static final int VT_CLSID = 72;
 
     /**
      * <p>[P] simple counted array. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_VECTOR = 0x1000;
+    public static final int VT_VECTOR = 0x1000;
 
     /**
      * <p>[V] SAFEARRAY*. <span style="background-color: #ffff00">How
      * long is this? How is it to be interpreted?</span></p>
      */
-    public final static int VT_ARRAY = 0x2000;
+    public static final int VT_ARRAY = 0x2000;
 
     /**
      * <p>[V] void* for local use. <span style="background-color:
      * #ffff00">How long is this? How is it to be
      * interpreted?</span></p>
      */
-    public final static int VT_BYREF = 0x4000;
+    public static final int VT_BYREF = 0x4000;
 
-    public final static int VT_RESERVED = 0x8000;
+    /**
+     * <p>FIXME: Document this!</p>
+     */
+    public static final int VT_RESERVED = 0x8000;
 
-    public final static int VT_ILLEGAL = 0xFFFF;
+    /**
+     * <p>FIXME: Document this!</p>
+     */
+    public static final int VT_ILLEGAL = 0xFFFF;
 
-    public final static int VT_ILLEGALMASKED = 0xFFF;
+    /**
+     * <p>FIXME: Document this!</p>
+     */
+    public static final int VT_ILLEGALMASKED = 0xFFF;
 
-    public final static int VT_TYPEMASK = 0xFFF;
+    /**
+     * <p>FIXME: Document this!</p>
+     */
+    public static final int VT_TYPEMASK = 0xFFF;
 
-}
+}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/dev/FormulaViewer.java b/src/java/org/apache/poi/hssf/dev/FormulaViewer.java
index 1e7fd95..3443fda 100644
--- a/src/java/org/apache/poi/hssf/dev/FormulaViewer.java
+++ b/src/java/org/apache/poi/hssf/dev/FormulaViewer.java
@@ -77,7 +77,6 @@
 import org.apache.poi.hssf.record.formula.*;
 import org.apache.poi.hssf.model.*;
 import org.apache.poi.hssf.usermodel.*;
-import org.apache.poi.hssf.util.SheetReferences;
 
 /**
  * FormulaViewer - finds formulas in a BIFF8 file and attempts to read them/display
@@ -144,7 +143,7 @@
             StringBuffer buf = new StringBuffer();
             
             if (token instanceof ExpPtg) return;
-            buf.append(name=((OperationPtg) token).toFormulaString((SheetReferences)null));
+            buf.append(name=((OperationPtg) token).toFormulaString((Workbook)null));
             buf.append(sep);
             switch (token.getPtgClass()) {
                 case Ptg.CLASS_REF :
@@ -213,7 +212,7 @@
         StringBuffer buf = new StringBuffer();
            for (int i=0;i<numptgs;i++) {
            token = (Ptg) tokens.get(i);
-            buf.append( token.toFormulaString((SheetReferences)null));
+            buf.append( token.toFormulaString((Workbook)null));
             switch (token.getPtgClass()) {
                 case Ptg.CLASS_REF :
                     buf.append("(R)");
@@ -233,7 +232,7 @@
     
     private String composeFormula(FormulaRecord record)
     {
-       return  org.apache.poi.hssf.model.FormulaParser.toFormulaString((SheetReferences)null,record.getParsedExpression());
+       return  org.apache.poi.hssf.model.FormulaParser.toFormulaString((Workbook)null,record.getParsedExpression());
     }
 
     /**
diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java
index b41f084..6051570 100644
--- a/src/java/org/apache/poi/hssf/model/FormulaParser.java
+++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java
@@ -64,7 +64,6 @@
 //import PTG's .. since we need everything, import *
 import org.apache.poi.hssf.record.formula.*;
 
-import org.apache.poi.hssf.util.SheetReferences;
 
 
 /**
@@ -335,10 +334,14 @@
         int numArgs = Arguments();
         Match(')');
                 
-        Ptg functionPtg = getFunction(name,(byte)numArgs);
+        AbstractFunctionPtg functionPtg = getFunction(name,(byte)numArgs);
         
 		tokens.add(functionPtg);
  
+        if (functionPtg.getName().equals("externalflag")) {
+            tokens.add(new NamePtg(name, this.book));
+        }
+
  		//remove what we just put in
 		this.functionTokens.remove(0);
     }
@@ -382,8 +385,8 @@
      * @param numArgs
      * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function
      */
-    private Ptg getFunction(String name,byte numArgs) {
-        Ptg retval = null;
+    private AbstractFunctionPtg getFunction(String name, byte numArgs) {
+        AbstractFunctionPtg retval = null;
         
         if (name.equals("IF")) {
             retval = new FuncVarPtg(AbstractFunctionPtg.ATTR_NAME, numArgs);
@@ -795,69 +798,85 @@
     /**
      * Convience method which takes in a list then passes it to the other toFormulaString
      * signature. 
-     * @param lptgs - list of ptgs, can be null
+     * @param book   workbook for 3D and named references
+     * @param lptgs  list of Ptg, can be null or empty
+     * @return a human readable String
      */
-    public static String toFormulaString(SheetReferences refs, List lptgs) {
+    public static String toFormulaString(Workbook book, List lptgs) {
         String retval = null;
         if (lptgs == null || lptgs.size() == 0) return "#NAME";
         Ptg[] ptgs = new Ptg[lptgs.size()];
         ptgs = (Ptg[])lptgs.toArray(ptgs);
-        retval = toFormulaString(refs, ptgs);
+        retval = toFormulaString(book, ptgs);
         return retval;
     }
     
-    /** Static method to convert an array of Ptgs in RPN order 
-     *  to a human readable string format in infix mode
-     *  @param ptgs - array of ptgs, can be null or empty
+    /**
+     * Static method to convert an array of Ptgs in RPN order
+     * to a human readable string format in infix mode.
+     * @param book  workbook for named and 3D references
+     * @param ptgs  array of Ptg, can be null or empty
+     * @return a human readable String
      */
-    public static String toFormulaString(SheetReferences refs, Ptg[] ptgs) {
+    public static String toFormulaString(Workbook book, Ptg[] ptgs) {
         if (ptgs == null || ptgs.length == 0) return "#NAME";
         java.util.Stack stack = new java.util.Stack();
-        int numPtgs = ptgs.length;
-        OperationPtg o;
-        int numOperands;
-        String result=null;
-        String[] operands;
         AttrPtg ifptg = null;
-        for (int i=0;i<numPtgs;i++) {
+
            // Excel allows to have AttrPtg at position 0 (such as Blanks) which
            // do not have any operands. Skip them.
-            if (ptgs[i] instanceof OperationPtg && i>0) {
-                  o = (OperationPtg) ptgs[i];
+        stack.push(ptgs[0].toFormulaString(book));
                   
-                  if (o instanceof AttrPtg && ((AttrPtg)o).isOptimizedIf()) {
-                        ifptg=(AttrPtg)o;
-                  } else {
+        for (int i = 1; i < ptgs.length; i++) {
+            if (! (ptgs[i] instanceof OperationPtg)) {
+                stack.push(ptgs[i].toFormulaString(book));
+                continue;
+            }
                       
-                      numOperands = o.getNumberOfOperands();
-                      operands = new String[numOperands];
+            if (ptgs[i] instanceof AttrPtg && ((AttrPtg) ptgs[i]).isOptimizedIf()) {
+                ifptg = (AttrPtg) ptgs[i];
+                continue;
+            }
                       
-                      for (int j=0;j<numOperands;j++) {
-                          operands[numOperands-j-1] = (String) stack.pop(); //TODO: catch stack underflow and throw parse exception. 
+            final OperationPtg o = (OperationPtg) ptgs[i];
+            final String[] operands = new String[o.getNumberOfOperands()];
+
+            for (int j = operands.length; j > 0; j--) {
+                //TODO: catch stack underflow and throw parse exception.
+                operands[j - 1] = (String) stack.pop();
                       }  
 
-                      if ( (o instanceof AbstractFunctionPtg) && 
-                            ((AbstractFunctionPtg)o).getName().equals("specialflag") &&
-                            ifptg != null
-                            ) {
+            stack.push(o.toFormulaString(operands));
+            if (!(o instanceof AbstractFunctionPtg)) continue;
+
+            final AbstractFunctionPtg f = (AbstractFunctionPtg) o;
+            final String fname = f.getName();
+            if (fname == null) continue;
+
+            if ((ifptg != null) && (fname.equals("specialflag"))) {
                              // this special case will be way different.
-                             result = ifptg.toFormulaString(
-                                  new String[] {(o.toFormulaString(operands))}
-                                                           );
-                             ifptg = null;
-                      } else {                      
-                        result = o.toFormulaString(operands);                                              
+                stack.push(ifptg.toFormulaString(new String[]{(String) stack.pop()}));
+                continue;
                       }
-                      stack.push(result);                                        
+            if (fname.equals("externalflag")) {
+                final String top = (String) stack.pop();
+                final int paren = top.indexOf('(');
+                final int comma = top.indexOf(',');
+                if (comma == -1) {
+                    final int rparen = top.indexOf(')');
+                    stack.push(top.substring(paren + 1, rparen) + "()");
                   }
-                      
-                  
-            } else {
-                stack.push(ptgs[i].toFormulaString(refs));
+                else {
+                    stack.push(top.substring(paren + 1, comma) + '(' +
+                               top.substring(comma + 1));
             }
         }
-        return (String) stack.pop(); //TODO: catch stack underflow and throw parse exception. 
     }
+        // TODO: catch stack underflow and throw parse exception.
+        return (String) stack.pop();
+    }
+
+
     /** Create a tree representation of the RPN token array
      *used to run the class(RVA) change algo
      */
@@ -890,11 +909,9 @@
      *   Useful for testing
      */
     public String toString() {
-        SheetReferences refs = null;
-        if (book!=null)  book.getSheetReferences();
         StringBuffer buf = new StringBuffer();
            for (int i=0;i<tokens.size();i++) {
-            buf.append( ( (Ptg)tokens.get(i)).toFormulaString(refs));
+            buf.append( ( (Ptg)tokens.get(i)).toFormulaString(book));
             buf.append(' ');
         } 
         return buf.toString();
diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java
index 9a7abad..c0f07b8 100644
--- a/src/java/org/apache/poi/hssf/model/Sheet.java
+++ b/src/java/org/apache/poi/hssf/model/Sheet.java
@@ -108,9 +108,10 @@
     protected HeaderRecord              header           = null;
     protected FooterRecord              footer           = null;
     protected PrintGridlinesRecord      printGridlines   = null;
+    protected WindowTwoRecord           windowTwo        = null;
     protected MergeCellsRecord          merged           = null;
-    protected ArrayList                 mergedRecords    = new ArrayList();
-    protected ArrayList                 mergedLocs       = new ArrayList();
+    protected Margin                    margins[]        = null;
+    protected List                 		mergedRecords    = new ArrayList();
     protected int                       numMergedRegions = 0;
     protected SelectionRecord           selection        = null;
     private static POILogger            log              = POILogFactory.getLogger(Sheet.class);
@@ -120,6 +121,7 @@
     private Iterator                    valueRecIterator = null;
     private Iterator                    rowRecIterator   = null;
     protected int                       eofLoc           = 0;
+	protected ProtectRecord             protect          = null;
 
     public static final byte PANE_LOWER_RIGHT = (byte)0;
     public static final byte PANE_UPPER_RIGHT = (byte)1;
@@ -195,7 +197,6 @@
             {
                 retval.mergedRecords.add(rec);
                 retval.merged = ( MergeCellsRecord ) rec;
-                retval.mergedLocs.add(new Integer(k - offset));
                 retval.numMergedRegions += retval.merged.getNumAreas();
             }
             else if (rec.getSid() == ColumnInfoRecord.sid)
@@ -234,9 +235,9 @@
             }
             else if ( rec.getSid() == RowRecord.sid )
             {
-            	 RowRecord row = (RowRecord)rec;
-            	 if (!isfirstrow) rec = null; //only add the aggregate once
-            	 
+                RowRecord row = (RowRecord)rec;
+                if (!isfirstrow) rec = null; //only add the aggregate once
+
                 if ( isfirstrow )
                 {
                     retval.rows = new RowRecordsAggregate();
@@ -249,22 +250,46 @@
             {
                 retval.printGridlines = (PrintGridlinesRecord) rec;
             }
-            else if ( rec.getSid() == HeaderRecord.sid )
+            else if ( rec.getSid() == HeaderRecord.sid && bofEofNestingLevel == 1)
             {
                 retval.header = (HeaderRecord) rec;
             }
-            else if ( rec.getSid() == FooterRecord.sid )
+            else if ( rec.getSid() == FooterRecord.sid && bofEofNestingLevel == 1)
             {
                 retval.footer = (FooterRecord) rec;
             }
-            else if ( rec.getSid() == PrintSetupRecord.sid )
+            else if ( rec.getSid() == PrintSetupRecord.sid && bofEofNestingLevel == 1)
             {
                 retval.printSetup = (PrintSetupRecord) rec;
             }
+            else if ( rec.getSid() == LeftMarginRecord.sid)
+            {
+                retval.getMargins()[LeftMargin] = (LeftMarginRecord) rec;
+            }
+            else if ( rec.getSid() == RightMarginRecord.sid)
+            {
+                retval.getMargins()[RightMargin] = (RightMarginRecord) rec;
+            }
+            else if ( rec.getSid() == TopMarginRecord.sid)
+            {
+                retval.getMargins()[TopMargin] = (TopMarginRecord) rec;
+            }
+            else if ( rec.getSid() == BottomMarginRecord.sid)
+            {
+                retval.getMargins()[BottomMargin] = (BottomMarginRecord) rec;
+            }
             else if ( rec.getSid() == SelectionRecord.sid )
             {
                 retval.selection = (SelectionRecord) rec;
             }
+            else if ( rec.getSid() == WindowTwoRecord.sid )
+            {
+                retval.windowTwo = (WindowTwoRecord) rec;
+            }
+			else if ( rec.getSid() == ProtectRecord.sid )
+			{
+				retval.protect = (ProtectRecord) rec;
+			}
 
             if (rec != null)
             {
@@ -272,14 +297,16 @@
             }
         }
         retval.records = records;
-        if (retval.rows == null)
-        {
-            retval.rows = new RowRecordsAggregate();
-        }
-        if (retval.cells == null)
-        {
-            retval.cells = new ValueRecordsAggregate();
-        }
+//        if (retval.rows == null)
+//        {
+//            retval.rows = new RowRecordsAggregate();
+//        }
+        retval.checkCells();
+        retval.checkRows();
+//        if (retval.cells == null)
+//        {
+//            retval.cells = new ValueRecordsAggregate();
+//        }
         log.log(log.DEBUG, "sheet createSheet (existing file) exited");
         return retval;
     }
@@ -391,11 +418,13 @@
         retval.dims    = ( DimensionsRecord ) retval.createDimensions();
         retval.dimsloc = 19;
         records.add(retval.dims);
-        records.add(retval.createWindowTwo());
+        records.add(retval.windowTwo = retval.createWindowTwo());
         retval.setLoc(records.size() - 1);
         retval.selection = 
                 (SelectionRecord) retval.createSelection();
         records.add(retval.selection);
+		retval.protect = (ProtectRecord) retval.createProtect();
+		records.add(retval.protect);
         records.add(retval.createEOF());
         retval.records = records;
         log.log(log.DEBUG, "Sheet createsheet from scratch exit");
@@ -427,8 +456,7 @@
         if (merged == null || merged.getNumAreas() == 1027)
         {
             merged = ( MergeCellsRecord ) createMergedCells();
-            mergedRecords.add(merged);
-            mergedLocs.add(new Integer(records.size() - 1));
+            mergedRecords.add(merged);            
             records.add(records.size() - 1, merged);
         }
         merged.addArea(rowFrom, colFrom, rowTo, colTo);
@@ -469,12 +497,18 @@
         numMergedRegions--;
         if (rec.getNumAreas() == 0)
         {
-            mergedRecords.remove(pos);
-            if (merged == rec)
-            	merged = (MergeCellsRecord) mergedRecords.get(mergedRecords.size() - 1);
-            int removePos = ((Integer) mergedLocs.get(pos)).intValue();
-            records.remove(removePos);
-            mergedLocs.remove(pos);
+			mergedRecords.remove(pos);
+			//get rid of the record from the sheet
+			records.remove(merged);            
+            if (merged == rec) {
+            	//pull up the LAST record for operations when we finally
+            	//support continue records for mergedRegions
+            	if (mergedRecords.size() > 0) {
+            		merged = (MergeCellsRecord) mergedRecords.get(mergedRecords.size() - 1);
+            	} else {
+            		merged = null;
+            	}
+            }
         }
     }
 
@@ -970,10 +1004,7 @@
     {
         checkCells();
         log.logFormatted(log.DEBUG, "remove value record row,dimsloc %,%",
-                         new int[]
-        {
-            row, dimsloc
-        });
+                         new int[]{row, dimsloc} );
         loc = dimsloc;
         cells.removeCell(col);
 
@@ -2011,7 +2042,7 @@
      * @return record containing a WindowTwoRecord
      */
 
-    protected Record createWindowTwo()
+    protected WindowTwoRecord createWindowTwo()
     {
         WindowTwoRecord retval = new WindowTwoRecord();
 
@@ -2312,7 +2343,6 @@
      * @param sel True to select the sheet, false otherwise.
      */
     public void setSelected(boolean sel) {
-        WindowTwoRecord windowTwo = (WindowTwoRecord) findFirstRecordBySid(WindowTwoRecord.sid);
         windowTwo.setSelected(sel);
     }
 
@@ -2321,82 +2351,59 @@
       * @param margin which margin to get
       * @return the size of the margin
       */
-     public double getMargin(short margin) {
-         Margin m;
-         switch ( margin )
-         {
-             case LeftMargin:
-                 m = (Margin) findFirstRecordBySid( LeftMarginRecord.sid );
-                 if ( m == null )
-                     return .75;
-                 break;
-             case RightMargin:
-                 m = (Margin) findFirstRecordBySid( RightMarginRecord.sid );
-                 if ( m == null )
-                     return .75;
-                 break;
-             case TopMargin:
-                 m = (Margin) findFirstRecordBySid( TopMarginRecord.sid );
-                 if ( m == null )
-                     return 1.0;
-                 break;
-             case BottomMargin:
-                 m = (Margin) findFirstRecordBySid( BottomMarginRecord.sid );
-                 if ( m == null )
-                     return 1.0;
-                 break;
-             default :
-                 throw new RuntimeException( "Unknown margin constant:  " + margin );
-         }
-         return m.getMargin();
-     }
+    public double getMargin(short margin) {
+	if (getMargins()[margin] != null)
+	    return margins[margin].getMargin();
+	else {
+	    switch ( margin )
+		{
+		case LeftMargin:
+		    return .75;
+		case RightMargin:
+		    return .75;
+		case TopMargin:
+		    return 1.0;
+		case BottomMargin:
+		    return 1.0;
+		default :
+		    throw new RuntimeException( "Unknown margin constant:  " + margin );
+		}
+	}
+    }
 
      /**
       * Sets the size of the margin in inches.
       * @param margin which margin to get
       * @param size the size of the margin
       */
-     public void setMargin(short margin, double size) {
-         Margin m;
-         switch ( margin )
-         {
-             case LeftMargin:
-                 m = (Margin) findFirstRecordBySid( LeftMarginRecord.sid );
-                 if ( m == null )
-                 {
-                     m = new LeftMarginRecord();
-                     records.add( getDimsLoc() + 1, m );
-                 }
-                 break;
-             case RightMargin:
-                 m = (Margin) findFirstRecordBySid( RightMarginRecord.sid );
-                 if ( m == null )
-                 {
-                     m = new RightMarginRecord();
-                     records.add( getDimsLoc() + 1, m );
-                 }
-                 break;
-             case TopMargin:
-                 m = (Margin) findFirstRecordBySid( TopMarginRecord.sid );
-                 if ( m == null )
-                 {
-                     m = new TopMarginRecord();
-                     records.add( getDimsLoc() + 1, m );
-                 }
-                 break;
-             case BottomMargin:
-                 m = (Margin) findFirstRecordBySid( BottomMarginRecord.sid );
-                 if ( m == null )
-                 {
-                     m = new BottomMarginRecord();
-                     records.add( getDimsLoc() + 1, m );
-                 }
-                 break;
-             default :
-                 throw new RuntimeException( "Unknown margin constant:  " + margin );
-         }
-         m.setMargin( size );
-     }
+    public void setMargin(short margin, double size) {
+	Margin m = getMargins()[margin];
+	if (m  == null) {
+	    switch ( margin )
+		{
+		case LeftMargin:
+		    m = new LeftMarginRecord();
+		    records.add( getDimsLoc() + 1, m );
+		    break;
+		case RightMargin:
+		    m = new RightMarginRecord();
+		    records.add( getDimsLoc() + 1, m );
+		    break;
+		case TopMargin:
+		    m = new TopMarginRecord();
+		    records.add( getDimsLoc() + 1, m );
+		    break;
+		case BottomMargin:
+		    m = new BottomMarginRecord();
+		    records.add( getDimsLoc() + 1, m );
+		    break;
+		default :
+		    throw new RuntimeException( "Unknown margin constant:  " + margin );
+		}
+	    margins[margin] = m;
+	}
+	m.setMargin( size );
+    }
 
     public int getEofLoc()
     {
@@ -2434,9 +2441,8 @@
         }
         records.add(loc+1, pane);
 
-        WindowTwoRecord windowRecord = (WindowTwoRecord) records.get(loc);
-        windowRecord.setFreezePanes(true);
-        windowRecord.setFreezePanesNoSplit(true);
+        windowTwo.setFreezePanes(true);
+        windowTwo.setFreezePanesNoSplit(true);
 
         SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
 //        SelectionRecord sel2 = (SelectionRecord) sel.clone();
@@ -2484,9 +2490,8 @@
         r.setActivePane((short) activePane);
         records.add(loc+1, r);
 
-        WindowTwoRecord windowRecord = (WindowTwoRecord) records.get(loc);
-        windowRecord.setFreezePanes(false);
-        windowRecord.setFreezePanesNoSplit(false);
+        windowTwo.setFreezePanes(false);
+        windowTwo.setFreezePanesNoSplit(false);
 
         SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
 //        SelectionRecord sel2 = (SelectionRecord) sel.clone();
@@ -2519,5 +2524,84 @@
     {
         this.selection = selection;
     }
+       /**
+        * creates a Protect record with protect set to false.
+        * @see org.apache.poi.hssf.record.ProtectRecord
+        * @see org.apache.poi.hssf.record.Record
+        * @return a ProtectRecord
+        */
 
+       protected Record createProtect()
+       {
+               log.log(log.DEBUG, "create protect record with protection disabled");
+               ProtectRecord retval = new ProtectRecord();
+
+               retval.setProtect(false);
+               // by default even when we support encryption we won't
+               return retval;
+       }
+
+       public ProtectRecord getProtect()
+       {
+               return protect;
+     }
+
+    /**
+     * Sets whether the gridlines are shown in a viewer.
+     * @param show whether to show gridlines or not
+     */
+    public void setDisplayGridlines(boolean show) {
+        windowTwo.setDisplayGridlines(show);
+    }
+
+    /**
+     * Returns if gridlines are displayed.
+     * @return whether gridlines are displayed
+     */
+    public boolean isDisplayGridlines() {
+	return windowTwo.getDisplayGridlines();
+    }
+
+    /**
+     * Sets whether the formulas are shown in a viewer.
+     * @param show whether to show formulas or not
+     */
+    public void setDisplayFormulas(boolean show) {
+        windowTwo.setDisplayFormulas(show);
+    }
+
+    /**
+     * Returns if formulas are displayed.
+     * @return whether formulas are displayed
+     */
+    public boolean isDisplayFormulas() {
+	return windowTwo.getDisplayFormulas();
+    }
+
+    /**
+     * Sets whether the RowColHeadings are shown in a viewer.
+     * @param show whether to show RowColHeadings or not
+     */
+    public void setDisplayRowColHeadings(boolean show) {
+        windowTwo.setDisplayRowColHeadings(show);
+    }
+
+    /**
+     * Returns if RowColHeadings are displayed.
+     * @return whether RowColHeadings are displayed
+     */
+    public boolean isDisplayRowColHeadings() {
+	return windowTwo.getDisplayRowColHeadings();
+    }
+
+    /**
+     * Returns the array of margins.  If not created, will create.
+     *
+     * @return the array of marings.
+     */
+    protected Margin[] getMargins() {
+        if (margins == null)
+            margins = new Margin[4];
+	return margins;
+    }
 }
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index cad9117..4696d89 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -513,7 +513,7 @@
         for (int k = 0; k < boundsheets.size(); k++) {
             String sheet = getSheetName(k);
 
-            if (sheet.equals(name)) {
+            if (sheet.equalsIgnoreCase(name)) {
                 retval = k;
                 break;
             }
@@ -686,37 +686,27 @@
      *
      * @return byte array containing the HSSF-only portions of the POIFS file.
      */
-
-    public byte [] serialize() {
-        log.log(DEBUG, "Serializing Workbook!");
-        byte[] retval    = null;
-
-        // ArrayList bytes     = new ArrayList(records.size());
-        int    arraysize = getSize();
-        int    pos       = 0;
-
-        // for (int k = 0; k < records.size(); k++)
-        // {
-        // bytes.add((( Record ) records.get(k)).serialize());
-        //        }
-        // for (int k = 0; k < bytes.size(); k++)
-        // {
-        // arraysize += (( byte [] ) bytes.get(k)).length;
-        // }
-        retval = new byte[ arraysize ];
-        for (int k = 0; k < records.size(); k++) {
-
-            // byte[] rec = (( byte [] ) bytes.get(k));
-            // System.arraycopy(rec, 0, retval, pos, rec.length);
-            Record record = records.get(k);
-            // Let's skip RECALCID records, as they are only use for optimization
-	    if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
-                pos += record.serialize(pos, retval);   // rec.length;
-	    }
-        }
-        log.log(DEBUG, "Exiting serialize workbook");
-        return retval;
-    }
+     // GJS: Not used so why keep it.
+//    public byte [] serialize() {
+//        log.log(DEBUG, "Serializing Workbook!");
+//        byte[] retval    = null;
+//
+////         ArrayList bytes     = new ArrayList(records.size());
+//        int    arraysize = getSize();
+//        int    pos       = 0;
+//
+//        retval = new byte[ arraysize ];
+//        for (int k = 0; k < records.size(); k++) {
+//
+//            Record record = records.get(k);
+////             Let's skip RECALCID records, as they are only use for optimization
+//	    if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
+//                pos += record.serialize(pos, retval);   // rec.length;
+//	    }
+//        }
+//        log.log(DEBUG, "Exiting serialize workbook");
+//        return retval;
+//    }
 
     /**
      * Serializes all records int the worksheet section into a big byte array. Use
@@ -725,44 +715,54 @@
      * @param data array of bytes to write this to
      */
 
-    public int serialize(int offset, byte [] data) {
-        log.log(DEBUG, "Serializing Workbook with offsets");
+    public int serialize( int offset, byte[] data )
+    {
+        log.log( DEBUG, "Serializing Workbook with offsets" );
 
-        // ArrayList bytes     = new ArrayList(records.size());
-        //        int arraysize = getSize();   // 0;
-        int pos       = 0;
+        int pos = 0;
 
-        //        for (int k = 0; k < records.size(); k++)
-        //        {
-        //            bytes.add((( Record ) records.get(k)).serialize());
-        //
-        //        }
-        //        for (int k = 0; k < bytes.size(); k++)
-        //       {
-        //            arraysize += (( byte [] ) bytes.get(k)).length;
-        //        }
-        for (int k = 0; k < records.size(); k++) {
+        SSTRecord sst = null;
+        int sstPos = 0;
+        for ( int k = 0; k < records.size(); k++ )
+        {
 
-            // byte[] rec = (( byte [] ) bytes.get(k));
-            // System.arraycopy(rec, 0, data, offset + pos, rec.length);
-            Record record = records.get(k);
+            Record record = records.get( k );
             // Let's skip RECALCID records, as they are only use for optimization
-            if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
-		pos += record.serialize(pos + offset, data);   // rec.length;
+            if ( record.getSid() != RecalcIdRecord.sid || ( (RecalcIdRecord) record ).isNeeded() )
+            {
+                if (record instanceof SSTRecord)
+                {
+                    sst = (SSTRecord)record;
+                    sstPos = pos;
+                }
+                if (record.getSid() == ExtSSTRecord.sid && sst != null)
+                {
+                    record = sst.createExtSSTRecord(sstPos + offset);
+                }
+                pos += record.serialize( pos + offset, data );   // rec.length;
             }
         }
-        log.log(DEBUG, "Exiting serialize workbook");
+        log.log( DEBUG, "Exiting serialize workbook" );
         return pos;
     }
 
-    public int getSize() {
+    public int getSize()
+    {
         int retval = 0;
 
-        for (int k = 0; k < records.size(); k++) {
-            Record record = records.get(k);
+        SSTRecord sst = null;
+        for ( int k = 0; k < records.size(); k++ )
+        {
+            Record record = records.get( k );
             // Let's skip RECALCID records, as they are only use for optimization
-            if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
-		retval += record.getRecordSize();
+            if ( record.getSid() != RecalcIdRecord.sid || ( (RecalcIdRecord) record ).isNeeded() )
+            {
+                if (record instanceof SSTRecord)
+                    sst = (SSTRecord)record;
+                if (record.getSid() == ExtSSTRecord.sid && sst != null)
+                    retval += sst.calcExtSSTRecordSize();
+                else
+                    retval += record.getRecordSize();
             }
         }
         return retval;
@@ -1729,15 +1729,17 @@
     }
 
     public SheetReferences getSheetReferences() {
-       SheetReferences refs = new SheetReferences();
-
-       if (externSheet != null) {
-          for (int k = 0; k < externSheet.getNumOfREFStructures(); k++) {
-              String sheetName = findSheetNameFromExternSheet((short)k);
-              refs.addSheetReference(sheetName, k);
-          }
-       }
-       return refs;
+        SheetReferences refs = new SheetReferences();
+        
+        if (externSheet != null) {
+            for (int k = 0; k < externSheet.getNumOfREFStructures(); k++) {
+                
+                String sheetName = findSheetNameFromExternSheet((short)k);
+                refs.addSheetReference(sheetName, k);
+                
+            }
+        }
+        return refs;
     }
 
     /** finds the sheet name by his extern sheet index
@@ -1745,10 +1747,12 @@
      * @return sheet name
      */
     public String findSheetNameFromExternSheet(short num){
-        String result;
+        String result="";
 
         short indexToSheet = externSheet.getREFRecordAt(num).getIndexToFirstSupBook();
-        result = getSheetName(indexToSheet);
+        if (indexToSheet>-1) { //error check, bail out gracefully!
+            result = getSheetName(indexToSheet);
+        }
 
         return result;
     }
@@ -2060,18 +2064,22 @@
      */
     public PaletteRecord getCustomPalette()
     {
-        PaletteRecord palette;
-        Record rec = records.get(records.getPalettepos());
-        if (rec instanceof PaletteRecord)
-        {
-            palette = (PaletteRecord) rec;
-        }
-        else
-        {
-            palette = createPalette();
-            records.add(records.getPalettepos(), palette);
-        }
-        return palette;
+      PaletteRecord palette;
+      int palettePos = records.getPalettepos();
+      if (palettePos != -1) {
+        Record rec = records.get(palettePos);
+        if (rec instanceof PaletteRecord) {
+          palette = (PaletteRecord) rec;
+        } else throw new RuntimeException("InternalError: Expected PaletteRecord but got a '"+rec+"'");
+      }
+      else
+      {
+          palette = createPalette();
+          //Add the palette record after the bof which is always the first record
+          records.add(1, palette);
+          records.setPalettepos(1);
+      }
+      return palette;
     }
  
     
diff --git a/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java b/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
index 0f2fa32..654ea88 100644
--- a/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
+++ b/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
@@ -74,7 +74,7 @@
     private int  namepos     = 0;   // holds the position of last name record
     private int  supbookpos  = 0;   // holds the position of sup book
     private int  externsheetPos = 0;// holds the position of the extern sheet
-    private int  palettepos  = 0;   // hold the position of the palette, if applicable
+    private int  palettepos  = -1;   // hold the position of the palette, if applicable
 
 
     public void setRecords( List records )
@@ -103,7 +103,7 @@
         if (getBackuppos() >= pos) setBackuppos( backuppos + 1 );
         if (getNamepos() >= pos) setNamepos(namepos+1);
         if (getSupbookpos() >= pos) setSupbookpos(supbookpos+1);
-        if (getPalettepos() >= pos) setPalettepos( palettepos + 1 );
+        if ((getPalettepos() != -1) && (getPalettepos() >= pos)) setPalettepos( palettepos + 1 );
         if (getExternsheetPos() >= pos) setExternsheetPos(getExternsheetPos() + 1);
     }
 
@@ -128,7 +128,7 @@
         if (getBackuppos() >= pos) setBackuppos( backuppos - 1 );
         if (getNamepos() >= pos) setNamepos(getNamepos()-1);
         if (getSupbookpos() >= pos) setSupbookpos(getSupbookpos()-1);
-        if (getPalettepos() >= pos) setPalettepos( palettepos - 1 );
+        if ((getPalettepos() != -1) && (getPalettepos() >= pos)) setPalettepos( palettepos - 1 );
         if (getExternsheetPos() >= pos) setExternsheetPos( getExternsheetPos() -1);
     }
 
diff --git a/src/java/org/apache/poi/hssf/record/BoolErrRecord.java b/src/java/org/apache/poi/hssf/record/BoolErrRecord.java
index 859748a..4391fc4 100644
--- a/src/java/org/apache/poi/hssf/record/BoolErrRecord.java
+++ b/src/java/org/apache/poi/hssf/record/BoolErrRecord.java
@@ -172,12 +172,18 @@
      * set the error value for the cell
      *
      * @param value     error representing the error value
+     *                  this value can only be 0,7,15,23,29,36 or 42
+     *                  see bugzilla bug 16560 for an explanation
      */
 
     public void setValue(byte value)
     {
-        field_4_bBoolErr = value;
-        field_5_fError   = ( byte ) 1;
+        if ( (value==0)||(value==7)||(value==15)||(value==23)||(value==29)||(value==36)||(value==42)) {
+            field_4_bBoolErr = value;
+            field_5_fError   = ( byte ) 1;
+        } else {
+            throw new RuntimeException("Error Value can only be 0,7,15,23,29,36 or 42. It cannot be "+value);
+        }
     }
 
     //public short getRow()
diff --git a/src/java/org/apache/poi/hssf/record/BottomMarginRecord.java b/src/java/org/apache/poi/hssf/record/BottomMarginRecord.java
index a74be0f..fcab41a 100644
--- a/src/java/org/apache/poi/hssf/record/BottomMarginRecord.java
+++ b/src/java/org/apache/poi/hssf/record/BottomMarginRecord.java
@@ -1 +1,187 @@
-/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation.  All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in *    the documentation and/or other materials provided with the *    distribution. * * 3. The end-user documentation included with the redistribution, *    if any, must include the following acknowledgment: *       "This product includes software developed by the *        Apache Software Foundation (http://www.apache.org/)." *    Alternately, this acknowledgment may appear in the software itself, *    if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and *    "Apache POI" must not be used to endorse or promote products *    derived from this software without prior written permission. For *    written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", *    "Apache POI", nor may "Apache" appear in their name, without *    prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation.  For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */package org.apache.poi.hssf.record;import org.apache.poi.util.*;/** * Record for the bottom margin. * NOTE: This source was automatically generated. * @author Shawn Laubach (slaubach at apache dot org) */public class BottomMarginRecord    extends Record implements Margin{    public final static short      sid                             = 0x29;    private  double     field_1_margin;    public BottomMarginRecord()    {    }    /**     * Constructs a BottomMargin record and sets its fields appropriately.     *     * @param id    id must be 0x29 or an exception     *              will be throw upon validation     * @param size  size the size of the data area of the record     * @param data  data of the record (should not contain sid/len)     */    public BottomMarginRecord(short id, short size, byte [] data)    {        super(id, size, data);    }    /**     * Constructs a BottomMargin record and sets its fields appropriately.     *     * @param id    id must be 0x29 or an exception     *              will be throw upon validation     * @param size  size the size of the data area of the record     * @param data  data of the record (should not contain sid/len)     * @param offset of the record's data     */    public BottomMarginRecord(short id, short size, byte [] data, int offset)    {        super(id, size, data, offset);    }    /**     * Checks the sid matches the expected side for this record     *     * @param id   the expected sid.     */    protected void validateSid(short id)    {        if (id != sid)        {            throw new RecordFormatException("Not a BottomMargin record");        }    }    protected void fillFields(byte [] data, short size, int offset)    {        field_1_margin                  = LittleEndian.getDouble(data, 0x0 + offset);    }    public String toString()    {        StringBuffer buffer = new StringBuffer();        buffer.append("[BottomMargin]\n");        buffer.append("    .margin               = ")            .append(" (").append(getMargin()).append(" )\n");        buffer.append("[/BottomMargin]\n");        return buffer.toString();    }    public int serialize(int offset, byte[] data)    {        LittleEndian.putShort(data, 0 + offset, sid);        LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));        LittleEndian.putDouble(data, 4 + offset, field_1_margin);        return getRecordSize();    }    /**     * Size of record (exluding 4 byte header)     */    public int getRecordSize()    {        return 4  + 8;    }    public short getSid()    {        return this.sid;    }    /**     * Get the margin field for the BottomMargin record.     */    public double getMargin()    {        return field_1_margin;    }    /**     * Set the margin field for the BottomMargin record.     */    public void setMargin(double field_1_margin)    {        this.field_1_margin = field_1_margin;    }         public Object clone() {        BottomMarginRecord rec = new BottomMarginRecord();        rec.field_1_margin = this.field_1_margin;        return rec;    }}  // END OF CLASS
\ No newline at end of file
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * Record for the bottom margin.
+ * NOTE: This source was automatically generated.
+ * @author Shawn Laubach (slaubach at apache dot org)
+ */
+public class BottomMarginRecord
+	 extends Record implements Margin
+{
+	 public final static short      sid                             = 0x29;
+	 private  double     field_1_margin;
+
+
+	 public BottomMarginRecord()
+	 {
+
+	 }
+
+	 /**
+	  * Constructs a BottomMargin record and sets its fields appropriately.
+	  *
+	  * @param id    id must be 0x29 or an exception
+	  *              will be throw upon validation
+	  * @param size  size the size of the data area of the record
+	  * @param data  data of the record (should not contain sid/len)
+	  */
+
+	 public BottomMarginRecord(short id, short size, byte [] data)
+	 {
+		  super(id, size, data);
+	 }
+
+	 /**
+	  * Constructs a BottomMargin record and sets its fields appropriately.
+	  *
+	  * @param id    id must be 0x29 or an exception
+	  *              will be throw upon validation
+	  * @param size  size the size of the data area of the record
+	  * @param data  data of the record (should not contain sid/len)
+	  * @param offset of the record's data
+	  */
+
+	 public BottomMarginRecord(short id, short size, byte [] data, int offset)
+	 {
+		  super(id, size, data, offset);
+	 }
+
+	 /**
+	  * Checks the sid matches the expected side for this record
+	  *
+	  * @param id   the expected sid.
+	  */
+	 protected void validateSid(short id)
+	 {
+		  if (id != sid)
+		  {
+				throw new RecordFormatException("Not a BottomMargin record");
+		  }
+	 }
+
+	 protected void fillFields(byte [] data, short size, int offset)
+	 {
+		  field_1_margin                  = LittleEndian.getDouble(data, 0x0 + offset);
+
+	 }
+
+	 public String toString()
+	 {
+		  StringBuffer buffer = new StringBuffer();
+
+		  buffer.append("[BottomMargin]\n");
+
+		  buffer.append("    .margin               = ")
+				.append(" (").append(getMargin()).append(" )\n");
+
+		  buffer.append("[/BottomMargin]\n");
+		  return buffer.toString();
+	 }
+
+	 public int serialize(int offset, byte[] data)
+	 {
+		  LittleEndian.putShort(data, 0 + offset, sid);
+		  LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+		  LittleEndian.putDouble(data, 4 + offset, field_1_margin);
+
+		  return getRecordSize();
+	 }
+
+	 /**
+	  * Size of record (exluding 4 byte header)
+	  */
+	 public int getRecordSize()
+	 {
+		  return 4  + 8;
+	 }
+
+	 public short getSid()
+	 {
+		  return this.sid;
+	 }
+
+
+	 /**
+	  * Get the margin field for the BottomMargin record.
+	  */
+	 public double getMargin()
+	 {
+		  return field_1_margin;
+	 }
+
+	 /**
+	  * Set the margin field for the BottomMargin record.
+	  */
+	 public void setMargin(double field_1_margin)
+	 {
+		  this.field_1_margin = field_1_margin;
+	 }
+    
+	  public Object clone() {
+		  BottomMarginRecord rec = new BottomMarginRecord();
+		  rec.field_1_margin = this.field_1_margin;
+		  return rec;
+	 }
+
+
+}  // END OF CLASS
\ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java b/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java
index 6e01246..95c5878 100644
--- a/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java
+++ b/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java
@@ -194,10 +194,22 @@
     /**
      * Set the sheetname for this sheet.  (this appears in the tabs at the bottom)
      * @param sheetname the name of the sheet
+     * @thows IllegalArgumentException if sheet name will cause excel to crash. 
      */
 
     public void setSheetname( String sheetname )
     {
+        
+        if ((sheetname == null) || (sheetname.length()==0)
+                || (sheetname.length()>31)
+                || (sheetname.indexOf("/") > -1)
+                || (sheetname.indexOf("\\") > -1)
+                || (sheetname.indexOf("?") > -1)
+                || (sheetname.indexOf("*") > -1)
+                || (sheetname.indexOf("]") > -1)
+                || (sheetname.indexOf("[") > -1) ){
+                    throw new IllegalArgumentException("Sheet name cannot be blank, greater than 31 chars, or contain any of /\\*?[]");
+        }
         field_5_sheetname = sheetname;
     }
 
diff --git a/src/java/org/apache/poi/hssf/record/ContinueRecord.java b/src/java/org/apache/poi/hssf/record/ContinueRecord.java
index dbe00e5..8d7eee7 100644
--- a/src/java/org/apache/poi/hssf/record/ContinueRecord.java
+++ b/src/java/org/apache/poi/hssf/record/ContinueRecord.java
@@ -267,4 +267,14 @@
     protected void fillFields(byte [] ignored_parm1, short ignored_parm2, int ignored_parm3)
     {
     }
+
+    /**
+     * Clone this record.
+     */
+    public Object clone() {
+      ContinueRecord clone = new ContinueRecord();
+      clone.setData(field_1_data);
+      return clone;
+    }
+
 }
diff --git a/src/java/org/apache/poi/hssf/record/ExtSSTInfoSubRecord.java b/src/java/org/apache/poi/hssf/record/ExtSSTInfoSubRecord.java
index e80c8af..d98f466 100644
--- a/src/java/org/apache/poi/hssf/record/ExtSSTInfoSubRecord.java
+++ b/src/java/org/apache/poi/hssf/record/ExtSSTInfoSubRecord.java
@@ -65,7 +65,6 @@
 /**
  * Extended SST table info subrecord<P>
  * contains the elements of "info" in the SST's array field<P>
- * WE HAVE VERY LITTLE INFORMATION ON HOW TO IMPLEMENT THIS RECORD! (EXTSSST)<P>
  * @author Andrew C. Oliver (acoliver at apache dot org)
  * @version 2.0-pre
  * @see org.apache.poi.hssf.record.ExtSSTRecord
@@ -74,6 +73,7 @@
 public class ExtSSTInfoSubRecord
     extends Record
 {
+   public static final int INFO_SIZE = 8;
     public final static short sid =
         0xFFF;                                             // only here for conformance, doesn't really have an sid
     private int               field_1_stream_pos;          // stream pointer to the SST record
@@ -114,7 +114,7 @@
         field_1_stream_pos = pos;
     }
 
-    public void setBucketSSTOffset(short offset)
+    public void setBucketRecordOffset(short offset)
     {
         field_2_bucket_sst_offset = offset;
     }
@@ -159,6 +159,6 @@
 
     public short getSid()
     {
-        return this.sid;
+        return sid;
     }
 }
diff --git a/src/java/org/apache/poi/hssf/record/ExtSSTRecord.java b/src/java/org/apache/poi/hssf/record/ExtSSTRecord.java
index 7a8e239..9a85e59 100644
--- a/src/java/org/apache/poi/hssf/record/ExtSSTRecord.java
+++ b/src/java/org/apache/poi/hssf/record/ExtSSTRecord.java
@@ -61,13 +61,13 @@
 
 /**
  * Title:        Extended Static String Table<P>
- * Description:  I really don't understand this thing... its supposed to be "a hash
- *               table for optimizing external copy operations"  --
- *<P>
- *               This sounds like a job for Marc "BitMaster" Johnson aka the
- *               "Hawaiian Master Chef".<P>
+ * Description: This record is used for a quick lookup into the SST record. This
+ *              record breaks the SST table into a set of buckets. The offsets
+ *              to these buckets within the SST record are kept as well as the
+ *              position relative to the start of the SST record.
  * REFERENCE:  PG 313 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
  * @author Andrew C. Oliver (acoliver at apache dot org)
+ * @author Jason Height (jheight at apache dot org)
  * @version 2.0-pre
  * @see org.apache.poi.hssf.record.ExtSSTInfoSubRecord
  */
@@ -75,10 +75,15 @@
 public class ExtSSTRecord
     extends Record
 {
+    public static final int DEFAULT_BUCKET_SIZE = 8;
+    //Cant seem to find this documented but from the biffviewer it is clear that
+    //Excel only records the indexes for the first 128 buckets.
+    public static final int MAX_BUCKETS = 128;
     public final static short sid = 0xff;
-    private short             field_1_strings_per_bucket;
+    private short             field_1_strings_per_bucket = DEFAULT_BUCKET_SIZE;
     private ArrayList         field_2_sst_info;
 
+
     public ExtSSTRecord()
     {
         field_2_sst_info = new ArrayList();
@@ -119,12 +124,11 @@
         }
     }
 
-    // this probably doesn't work but we don't really care at this point
     protected void fillFields(byte [] data, short size, int offset)
     {
         field_2_sst_info           = new ArrayList();
         field_1_strings_per_bucket = LittleEndian.getShort(data, 0 + offset);
-        for (int k = 2; k < ((data.length - offset) - size); k += 8)
+        for (int k = 2; k < (size-offset); k += 8)
         {
             byte[] tempdata = new byte[ 8 + offset ];
 
@@ -189,26 +193,55 @@
     public int serialize(int offset, byte [] data)
     {
         LittleEndian.putShort(data, 0 + offset, sid);
-
-//    LittleEndian.putShort(data,2,(short)(2 + (getNumInfoRecords() *8)));
-        LittleEndian.putShort(data, 2 + offset, ( short ) (2 + (0x3fa - 2)));
-        int pos = 4;
+        LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+        LittleEndian.putShort(data, 4 + offset, field_1_strings_per_bucket);
+        int pos = 6;
 
         for (int k = 0; k < getNumInfoRecords(); k++)
         {
-            System.arraycopy(getInfoRecordAt(k).serialize(), 0, data,
-                             pos + offset, 8);
+            ExtSSTInfoSubRecord rec = getInfoRecordAt(k);
+            pos += rec.serialize(pos + offset, data);
         }
-        return getRecordSize();
+        return pos;
     }
 
+    /** Returns the size of this record */
     public int getRecordSize()
     {
-        return 6 + 0x3fa - 2;
+        return 6 + 8*getNumInfoRecords();
+    }
+
+    public static final int getNumberOfInfoRecsForStrings(int numStrings) {
+      int infoRecs = (numStrings / DEFAULT_BUCKET_SIZE);
+      if ((numStrings % DEFAULT_BUCKET_SIZE) != 0)
+        infoRecs ++;
+      //Excel seems to max out after 128 info records.
+      //This isnt really documented anywhere...
+      if (infoRecs > MAX_BUCKETS)
+        infoRecs = MAX_BUCKETS;
+      return infoRecs;
+    }
+
+    /** Given a number of strings (in the sst), returns the size of the extsst record*/
+    public static final int getRecordSizeForStrings(int numStrings) {
+      return 4 + 2 + (getNumberOfInfoRecsForStrings(numStrings) * 8);
     }
 
     public short getSid()
     {
-        return this.sid;
+        return sid;
     }
+
+    public void setBucketOffsets( int[] bucketAbsoluteOffsets, int[] bucketRelativeOffsets )
+    {
+        this.field_2_sst_info = new ArrayList(bucketAbsoluteOffsets.length);
+        for ( int i = 0; i < bucketAbsoluteOffsets.length; i++ )
+        {
+            ExtSSTInfoSubRecord r = new ExtSSTInfoSubRecord();
+            r.setBucketRecordOffset((short)bucketRelativeOffsets[i]);
+            r.setStreamPos(bucketAbsoluteOffsets[i]);
+            field_2_sst_info.add(r);
+        }
+    }
+
 }
diff --git a/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java b/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java
index 761d400..048fb44 100644
--- a/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java
+++ b/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java
@@ -1 +1,230 @@
-/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation.  All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in *    the documentation and/or other materials provided with the *    distribution. * * 3. The end-user documentation included with the redistribution, *    if any, must include the following acknowledgment: *       "This product includes software developed by the *        Apache Software Foundation (http://www.apache.org/)." *    Alternately, this acknowledgment may appear in the software itself, *    if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and *    "Apache POI" must not be used to endorse or promote products *    derived from this software without prior written permission. For *    written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", *    "Apache POI", nor may "Apache" appear in their name, without *    prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation.  For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */package org.apache.poi.hssf.record;import org.apache.poi.util.LittleEndian;import java.util.ArrayList;/** * Title:        Extern Sheet <P> * Description:  A List of Inndexes to SupBook <P> * REFERENCE:  <P> * @author Libin Roman (Vista Portal LDT. Developer) * @version 1.0-pre */public class ExternSheetRecord extends Record {    public final static short sid = 0x17;    private short             field_1_number_of_REF_sturcutres;    private ArrayList         field_2_REF_structures;        public ExternSheetRecord() {        field_2_REF_structures = new ArrayList();    }        /**     * Constructs a Extern Sheet record and sets its fields appropriately.     *     * @param id     id must be 0x16 or an exception will be throw upon validation     * @param size  the size of the data area of the record     * @param data  data of the record (should not contain sid/len)     */        public ExternSheetRecord(short id, short size, byte[] data) {        super(id, size, data);    }        /**     * Constructs a Extern Sheet record and sets its fields appropriately.     *     * @param id     id must be 0x16 or an exception will be throw upon validation     * @param size  the size of the data area of the record     * @param data  data of the record (should not contain sid/len)     * @param offset of the record's data     */    public ExternSheetRecord(short id, short size, byte[] data, int offset) {        super(id, size, data, offset);    }        /**     * called by constructor, should throw runtime exception in the event of a     * record passed with a differing ID.     *     * @param id alleged id for this record     */    protected void validateSid(short id) {        if (id != sid) {            throw new RecordFormatException("NOT An ExternSheet RECORD");        }    }        /**     * called by the constructor, should set class level fields.  Should throw     * runtime exception for bad/icomplete data.     *     * @param data raw data     * @param size size of data     * @param offset of the record's data (provided a big array of the file)     */    protected void fillFields(byte [] data, short size, int offset) {        field_2_REF_structures           = new ArrayList();                field_1_number_of_REF_sturcutres = LittleEndian.getShort(data, 0 + offset);                int pos = 2 + offset;        for (int i = 0 ; i < field_1_number_of_REF_sturcutres ; ++i) {            ExternSheetSubRecord rec = new ExternSheetSubRecord((short)0, (short)6 , data , pos);                        pos += 6;                        field_2_REF_structures.add( rec);        }    }        /**      * sets the number of the REF structors , that is in Excel file     * @param numStruct number of REF structs     */    public void setNumOfREFStructures(short numStruct) {        field_1_number_of_REF_sturcutres = numStruct;    }        /**       * return the number of the REF structors , that is in Excel file     * @return number of REF structs     */    public short getNumOfREFStructures() {        return field_1_number_of_REF_sturcutres;    }        /**      * adds REF struct (ExternSheetSubRecord)     * @param rec REF struct     */    public void addREFRecord(ExternSheetSubRecord rec) {        field_2_REF_structures.add(rec);    }        /** returns the number of REF Records, which is in model     * @return number of REF records     */    public int getNumOfREFRecords() {        return field_2_REF_structures.size();    }        /** returns the REF record (ExternSheetSubRecord)     * @param elem index to place     * @return REF record     */    public ExternSheetSubRecord getREFRecordAt(int elem) {        ExternSheetSubRecord result = ( ExternSheetSubRecord ) field_2_REF_structures.get(elem);                return result;    }        public String toString() {        StringBuffer buffer = new StringBuffer();                buffer.append("[EXTERNSHEET]\n");        buffer.append("   numOfRefs     = ").append(getNumOfREFStructures()).append("\n");        for (int k=0; k < this.getNumOfREFRecords(); k++) {            buffer.append("refrec         #").append(k).append('\n');            buffer.append(getREFRecordAt(k).toString());            buffer.append("----refrec     #").append(k).append('\n');        }        buffer.append("[/EXTERNSHEET]\n");                        return buffer.toString();    }        /**     * called by the class that is responsible for writing this sucker.     * Subclasses should implement this so that their data is passed back in a     * byte array.     *     * @param offset to begin writing at     * @param data byte array containing instance data     * @return number of bytes written     */    public int serialize(int offset, byte [] data) {        LittleEndian.putShort(data, 0 + offset, sid);        LittleEndian.putShort(data, 2 + offset,(short)(2 + (getNumOfREFRecords() *6)));                LittleEndian.putShort(data, 4 + offset, getNumOfREFStructures());                int pos = 6 ;                for (int k = 0; k < getNumOfREFRecords(); k++) {            ExternSheetSubRecord record = getREFRecordAt(k);            System.arraycopy(record.serialize(), 0, data, pos + offset, 6);                        pos +=6;        }        return getRecordSize();    }        public int getRecordSize() {        return 4 + 2 + getNumOfREFRecords() * 6;    }        /**     * return the non static version of the id for this record.     */    public short getSid() {        return this.sid;    }}
\ No newline at end of file
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.util.LittleEndian;
+
+import java.util.ArrayList;
+
+/**
+ * Title:        Extern Sheet <P>
+ * Description:  A List of Inndexes to SupBook <P>
+ * REFERENCE:  <P>
+ * @author Libin Roman (Vista Portal LDT. Developer)
+ * @version 1.0-pre
+ */
+
+public class ExternSheetRecord extends Record {
+    public final static short sid = 0x17;
+    private short             field_1_number_of_REF_sturcutres;
+    private ArrayList         field_2_REF_structures;
+    
+    public ExternSheetRecord() {
+        field_2_REF_structures = new ArrayList();
+    }
+    
+    /**
+     * Constructs a Extern Sheet record and sets its fields appropriately.
+     *
+     * @param id     id must be 0x16 or an exception will be throw upon validation
+     * @param size  the size of the data area of the record
+     * @param data  data of the record (should not contain sid/len)
+     */
+    
+    public ExternSheetRecord(short id, short size, byte[] data) {
+        super(id, size, data);
+    }
+    
+    /**
+     * Constructs a Extern Sheet record and sets its fields appropriately.
+     *
+     * @param id     id must be 0x16 or an exception will be throw upon validation
+     * @param size  the size of the data area of the record
+     * @param data  data of the record (should not contain sid/len)
+     * @param offset of the record's data
+     */
+    public ExternSheetRecord(short id, short size, byte[] data, int offset) {
+        super(id, size, data, offset);
+    }
+    
+    /**
+     * called by constructor, should throw runtime exception in the event of a
+     * record passed with a differing ID.
+     *
+     * @param id alleged id for this record
+     */
+    protected void validateSid(short id) {
+        if (id != sid) {
+            throw new RecordFormatException("NOT An ExternSheet RECORD");
+        }
+    }
+    
+    /**
+     * called by the constructor, should set class level fields.  Should throw
+     * runtime exception for bad/icomplete data.
+     *
+     * @param data raw data
+     * @param size size of data
+     * @param offset of the record's data (provided a big array of the file)
+     */
+    protected void fillFields(byte [] data, short size, int offset) {
+        field_2_REF_structures           = new ArrayList();
+        
+        field_1_number_of_REF_sturcutres = LittleEndian.getShort(data, 0 + offset);
+        
+        int pos = 2 + offset;
+        for (int i = 0 ; i < field_1_number_of_REF_sturcutres ; ++i) {
+            ExternSheetSubRecord rec = new ExternSheetSubRecord((short)0, (short)6 , data , pos);
+            
+            pos += 6;
+            
+            field_2_REF_structures.add( rec);
+        }
+    }
+    
+    /** 
+     * sets the number of the REF structors , that is in Excel file
+     * @param numStruct number of REF structs
+     */
+    public void setNumOfREFStructures(short numStruct) {
+        field_1_number_of_REF_sturcutres = numStruct;
+    }
+    
+    /**  
+     * return the number of the REF structors , that is in Excel file
+     * @return number of REF structs
+     */
+    public short getNumOfREFStructures() {
+        return field_1_number_of_REF_sturcutres;
+    }
+    
+    /** 
+     * adds REF struct (ExternSheetSubRecord)
+     * @param rec REF struct
+     */
+    public void addREFRecord(ExternSheetSubRecord rec) {
+        field_2_REF_structures.add(rec);
+    }
+    
+    /** returns the number of REF Records, which is in model
+     * @return number of REF records
+     */
+    public int getNumOfREFRecords() {
+        return field_2_REF_structures.size();
+    }
+    
+    /** returns the REF record (ExternSheetSubRecord)
+     * @param elem index to place
+     * @return REF record
+     */
+    public ExternSheetSubRecord getREFRecordAt(int elem) {
+        ExternSheetSubRecord result = ( ExternSheetSubRecord ) field_2_REF_structures.get(elem);
+        
+        return result;
+    }
+    
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        
+        buffer.append("[EXTERNSHEET]\n");
+        buffer.append("   numOfRefs     = ").append(getNumOfREFStructures()).append("\n");
+        for (int k=0; k < this.getNumOfREFRecords(); k++) {
+            buffer.append("refrec         #").append(k).append('\n');
+            buffer.append(getREFRecordAt(k).toString());
+            buffer.append("----refrec     #").append(k).append('\n');
+        }
+        buffer.append("[/EXTERNSHEET]\n");
+        
+        
+        return buffer.toString();
+    }
+    
+    /**
+     * called by the class that is responsible for writing this sucker.
+     * Subclasses should implement this so that their data is passed back in a
+     * byte array.
+     *
+     * @param offset to begin writing at
+     * @param data byte array containing instance data
+     * @return number of bytes written
+     */
+    public int serialize(int offset, byte [] data) {
+        LittleEndian.putShort(data, 0 + offset, sid);
+        LittleEndian.putShort(data, 2 + offset,(short)(2 + (getNumOfREFRecords() *6)));
+        
+        LittleEndian.putShort(data, 4 + offset, getNumOfREFStructures());
+        
+        int pos = 6 ;
+        
+        for (int k = 0; k < getNumOfREFRecords(); k++) {
+            ExternSheetSubRecord record = getREFRecordAt(k);
+            System.arraycopy(record.serialize(), 0, data, pos + offset, 6);
+            
+            pos +=6;
+        }
+        return getRecordSize();
+    }
+    
+    public int getRecordSize() {
+        return 4 + 2 + getNumOfREFRecords() * 6;
+    }
+    
+    /**
+     * return the non static version of the id for this record.
+     */
+    public short getSid() {
+        return this.sid;
+    }
+}
diff --git a/src/java/org/apache/poi/hssf/record/FormulaRecord.java b/src/java/org/apache/poi/hssf/record/FormulaRecord.java
index ad926be..0505587 100644
--- a/src/java/org/apache/poi/hssf/record/FormulaRecord.java
+++ b/src/java/org/apache/poi/hssf/record/FormulaRecord.java
@@ -557,8 +557,13 @@
                 .append("\n");
             buffer.append("    .xf              = ")
                 .append(Integer.toHexString(getXFIndex())).append("\n");
-            buffer.append("    .value           = ").append(getValue())
-                .append("\n");
+            if (Double.isNaN(this.getValue()) && value_data != null)
+              buffer.append("    .value (NaN)     = ")
+                  .append(org.apache.poi.util.HexDump.dump(value_data,0,0))
+                  .append("\n");
+            else
+              buffer.append("    .value           = ").append(getValue())
+                  .append("\n");
             buffer.append("    .options         = ").append(getOptions())
                 .append("\n");
             buffer.append("    .zero            = ").append(field_6_zero)
@@ -607,9 +612,10 @@
       if (field_8_parsed_expr != null)
         size = field_8_parsed_expr.size();
       for (int i=0; i< size; i++) {
-        Ptg ptg = (Ptg)((Ptg)field_8_parsed_expr.get(i)).clone();
-        rec.field_8_parsed_expr.set(i, ptg);
+        Ptg ptg = (Ptg)((Ptg)field_8_parsed_expr.get(i)).clone();        
+        rec.field_8_parsed_expr.add(i, ptg);
       }
+      rec.value_data = value_data;
       rec.all_data = all_data;
       return rec;
     }
diff --git a/src/java/org/apache/poi/hssf/record/LabelRecord.java b/src/java/org/apache/poi/hssf/record/LabelRecord.java
index 558584b..8755a06 100644
--- a/src/java/org/apache/poi/hssf/record/LabelRecord.java
+++ b/src/java/org/apache/poi/hssf/record/LabelRecord.java
@@ -150,15 +150,16 @@
         field_3_xf_index     = LittleEndian.getShort(data, 4 + offset);
         field_4_string_len   = LittleEndian.getShort(data, 6 + offset);
         field_5_unicode_flag = data[ 8 + offset ];
-        if (isUnCompressedUnicode())
-        {
-            field_6_value = StringUtil.getFromUnicode(data, 8 + offset,
+        if (field_4_string_len > 0) {
+          if (isUnCompressedUnicode()) {
+            field_6_value = StringUtil.getFromUnicode(data, 9 + offset,
                                                       field_4_string_len);
         }
-        else
-        {
-            field_6_value = StringUtil.getFromCompressedUnicode(data, 9 + offset, getStringLength());
+          else {
+            field_6_value = StringUtil.getFromCompressedUnicode(data, 9 + offset,
+                getStringLength());
         }
+        } else field_6_value = null;
     }
 
 /* READ ONLY ACCESS... THIS IS FOR COMPATIBILITY ONLY...USE LABELSST!
@@ -237,6 +238,27 @@
         return this.sid;
     }
 
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("[LABEL]\n");
+        buffer.append("    .row            = ")
+            .append(Integer.toHexString(getRow())).append("\n");
+        buffer.append("    .column         = ")
+            .append(Integer.toHexString(getColumn())).append("\n");
+        buffer.append("    .xfindex        = ")
+            .append(Integer.toHexString(getXFIndex())).append("\n");
+        buffer.append("    .string_len       = ")
+            .append(Integer.toHexString(field_4_string_len)).append("\n");
+        buffer.append("    .unicode_flag       = ")
+            .append(Integer.toHexString(field_5_unicode_flag)).append("\n");
+        buffer.append("    .value       = ")
+            .append(getValue()).append("\n");
+        buffer.append("[/LABEL]\n");
+        return buffer.toString();
+    }
+
+
     public boolean isBefore(CellValueRecordInterface i)
     {
         if (this.getRow() > i.getRow())
diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java
index 09bf2a4..b91c829 100644
--- a/src/java/org/apache/poi/hssf/record/NameRecord.java
+++ b/src/java/org/apache/poi/hssf/record/NameRecord.java
@@ -58,11 +58,11 @@
 import java.util.List;
 import java.util.Stack;
 
+import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.formula.Area3DPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.Ref3DPtg;
 import org.apache.poi.hssf.util.RangeAddress;
-import org.apache.poi.hssf.util.SheetReferences;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
@@ -127,6 +127,14 @@
 	 */
 	public final static byte  BUILTIN_SHEET_TITLE           = (byte)12;
     
+    public static final short OPT_HIDDEN_NAME =   (short) 0x0001;
+    public static final short OPT_FUNCTION_NAME = (short) 0x0002;
+    public static final short OPT_COMMAND_NAME =  (short) 0x0004;
+    public static final short OPT_MACRO =         (short) 0x0008;
+    public static final short OPT_COMPLEX =       (short) 0x0010;
+    public static final short OPT_BUILTIN =       (short) 0x0020;
+    public static final short OPT_BINDATA =       (short) 0x1000;
+
     
     private short             field_1_option_flag;
     private byte              field_2_keyboard_shortcut;
@@ -192,7 +200,7 @@
 	{
 	    this();	    
 	    this.field_12_builtIn_name = builtin;
-	    this.setOptionFlag((short)(this.getOptionFlag() | (short)0x20));
+	    this.setOptionFlag((short)(this.getOptionFlag() | OPT_BUILTIN));
 	    this.setNameTextLength((byte)1);
 	    this.setEqualsToIndexToSheet(index); //the extern sheets are set through references
 	    
@@ -252,13 +260,22 @@
 
 	/**
 	 * Convenience method to retrieve the index the name refers to.
-	 * @see getEqualsToIndexToSheet()
+	 * @see #getEqualsToIndexToSheet()
 	 * @return short
 	 */
 	public short getIndexToSheet() {
 		return getEqualsToIndexToSheet();
 	}
 
+    /**
+     * @return function group
+     * @see FnGroupCountRecord
+     */
+    public byte getFnGroup() {
+        int masked = field_1_option_flag & 0x0fc0;
+        return (byte) (masked >> 4);
+    }
+
     public void setEqualsToIndexToSheet(short value)
     {
         field_6_equals_to_index_to_sheet = value;
@@ -409,11 +426,47 @@
         return field_11_compressed_unicode_flag;
     }
 
+    /**
+     * @return true if name is hidden
+     */
+    public boolean isHiddenName() {
+        return (field_1_option_flag & OPT_HIDDEN_NAME) != 0;
+    }
+
+    /**
+     * @return true if name is a function
+     */
+    public boolean isFunctionName() {
+        return (field_1_option_flag & OPT_FUNCTION_NAME) != 0;
+    }
+
+    /**
+     * @return true if name is a command
+     */
+    public boolean isCommandName() {
+        return (field_1_option_flag & OPT_COMMAND_NAME) != 0;
+    }
+
+    /**
+     * @return true if function macro or command macro
+     */
+    public boolean isMacro() {
+        return (field_1_option_flag & OPT_MACRO) != 0;
+    }
+
+    /**
+     * @return true if array formula or user defined
+     */
+    public boolean isComplexFunction() {
+        return (field_1_option_flag & OPT_COMPLEX) != 0;
+    }
+
+
 	/**Convenience Function to determine if the name is a built-in name
 	 */
 	public boolean isBuiltInName()
 	{
-	    return ((this.getOptionFlag() & (short)0x20) != 0);
+	    return ((this.getOptionFlag() & OPT_BUILTIN) != 0);
 	}
 
 
@@ -511,7 +564,7 @@
         data[18 + offset] = getCompressedUnicodeFlag();
 
         /* temp: gjs
-        if ( ( field_1_option_flag & (short) 0x20 ) != 0 )
+        if (isBuiltInName())
         {
             LittleEndian.putShort( data, 2 + offset, (short) ( 16 + field_13_raw_name_definition.length ) );
 
@@ -647,16 +700,16 @@
     /** gets the reference , the area only (range)
      * @return area reference
      */
-    public String getAreaReference(SheetReferences refs){
+    public String getAreaReference(Workbook book){
         if (field_13_name_definition == null) return "#REF!";
         Ptg ptg = (Ptg) field_13_name_definition.peek();
         String result = "";
 
         if (ptg.getClass() == Area3DPtg.class){
-            result = ptg.toFormulaString(refs);
+            result = ptg.toFormulaString(book);
 
         } else if (ptg.getClass() == Ref3DPtg.class){
-            result = ptg.toFormulaString(refs);
+            result = ptg.toFormulaString(book);
         }
 
         return result;
@@ -727,7 +780,7 @@
 
         /*
         temp: gjs
-        if ( ( field_1_option_flag & (short)0x20 ) != 0 ) {
+        if (isBuiltInName()) {
             // DEBUG
             // System.out.println( "Built-in name" );
 
@@ -794,6 +847,8 @@
                 pos += ptg.getSize();
                 sizeCounter += ptg.getSize();
                 stack.push(ptg);
+                field_13_raw_name_definition=new byte[size];
+                System.arraycopy(data,offset,field_13_raw_name_definition,0,size);
             }
         } catch (java.lang.UnsupportedOperationException uoe) {
             System.err.println("[WARNING] Unknown Ptg "
@@ -880,7 +935,7 @@
             .append("\n");
         buffer.append("    .unused                   = ").append( field_5_index_to_sheet )
             .append("\n");
-        buffer.append("    .( 0 = Global name, otherwise index to sheet (one-based) ) = ").append( field_6_equals_to_index_to_sheet )
+        buffer.append("    .index to sheet (1-based, 0=Global)           = ").append( field_6_equals_to_index_to_sheet )
             .append("\n");
         buffer.append("    .Length of menu text (character count)        = ").append( field_7_length_custom_menu )
             .append("\n");
@@ -906,6 +961,7 @@
             .append("\n");
         buffer.append("    .Status bar text (Unicode string without length field)  = ").append( field_17_status_bar_text )
             .append("\n");
+        buffer.append(org.apache.poi.util.HexDump.dump(this.field_13_raw_name_definition,0,0));
         buffer.append("[/NAME]\n");
         
         return buffer.toString();
diff --git a/src/java/org/apache/poi/hssf/record/PasswordRecord.java b/src/java/org/apache/poi/hssf/record/PasswordRecord.java
index 9feb6a2..ba9cbfc 100644
--- a/src/java/org/apache/poi/hssf/record/PasswordRecord.java
+++ b/src/java/org/apache/poi/hssf/record/PasswordRecord.java
@@ -166,4 +166,14 @@
     {
         return this.sid;
     }
+
+    /**
+     * Clone this record.
+     */
+    public Object clone() {
+      PasswordRecord clone = new PasswordRecord();
+      clone.setPassword(field_1_password);
+      return clone;
+    }
+
 }
diff --git a/src/java/org/apache/poi/hssf/record/ProtectRecord.java b/src/java/org/apache/poi/hssf/record/ProtectRecord.java
index 66978f9..a633756 100644
--- a/src/java/org/apache/poi/hssf/record/ProtectRecord.java
+++ b/src/java/org/apache/poi/hssf/record/ProtectRecord.java
@@ -139,9 +139,9 @@
      * @return whether to protect the sheet or not
      */
 
-    public short getProtect()
+    public boolean getProtect()
     {
-        return field_1_protect;
+        return (field_1_protect == 1);
     }
 
     public String toString()
@@ -149,8 +149,8 @@
         StringBuffer buffer = new StringBuffer();
 
         buffer.append("[PROTECT]\n");
-        buffer.append("    .protected      = ")
-            .append(Integer.toHexString(getProtect())).append("\n");
+	    buffer.append("    .protect         = ").append(getProtect())
+            .append("\n");
         buffer.append("[/PROTECT]\n");
         return buffer.toString();
     }
@@ -160,7 +160,7 @@
         LittleEndian.putShort(data, 0 + offset, sid);
         LittleEndian.putShort(data, 2 + offset,
                               (( short ) 0x02));   // 2 bytes (6 total)
-        LittleEndian.putShort(data, 4 + offset, getProtect());
+        LittleEndian.putShort(data, 4 + offset, field_1_protect);
         return getRecordSize();
     }
 
diff --git a/src/java/org/apache/poi/hssf/record/ProtectionRev4Record.java b/src/java/org/apache/poi/hssf/record/ProtectionRev4Record.java
index e385a4a..be64938 100644
--- a/src/java/org/apache/poi/hssf/record/ProtectionRev4Record.java
+++ b/src/java/org/apache/poi/hssf/record/ProtectionRev4Record.java
@@ -139,18 +139,18 @@
      * @return whether to protect the workbook or not
      */
 
-    public short getProtect()
-    {
-        return field_1_protect;
-    }
+	public boolean getProtect()
+	{
+		return (field_1_protect == 1);
+	}
 
     public String toString()
     {
         StringBuffer buffer = new StringBuffer();
 
         buffer.append("[PROT4REV]\n");
-        buffer.append("    .rowheight      = ")
-            .append(Integer.toHexString(getProtect())).append("\n");
+	    buffer.append("    .protect         = ").append(getProtect())
+            .append("\n");
         buffer.append("[/PROT4REV]\n");
         return buffer.toString();
     }
@@ -160,7 +160,7 @@
         LittleEndian.putShort(data, 0 + offset, sid);
         LittleEndian.putShort(data, 2 + offset,
                               (( short ) 0x02));   // 2 bytes (6 total)
-        LittleEndian.putShort(data, 4 + offset, getProtect());
+        LittleEndian.putShort(data, 4 + offset, field_1_protect);
         return getRecordSize();
     }
 
diff --git a/src/java/org/apache/poi/hssf/record/Record.java b/src/java/org/apache/poi/hssf/record/Record.java
index 7783026..bd4e737 100644
--- a/src/java/org/apache/poi/hssf/record/Record.java
+++ b/src/java/org/apache/poi/hssf/record/Record.java
@@ -71,16 +71,6 @@
 {
 
     /**
-     * The static ID, subclasses should override this value with the id for the
-     * record type they handle.
-     */
-
-    public short   sid  = 0;
-    private short  id   = 0;
-    private short  size = 0;
-    private byte[] data = null;
-
-    /**
      * instantiates a blank record strictly for ID matching
      */
 
@@ -98,9 +88,6 @@
 
     public Record(short id, short size, byte [] data)
     {
-        this.id   = id;
-        this.size = size;
-        this.data = data;
         validateSid(id);
         fillFields(data, size);
     }
@@ -115,9 +102,6 @@
 
     public Record(short id, short size, byte [] data, int offset)
     {
-        this.id   = id;
-        this.size = size;
-        this.data = data;
         validateSid(id);
         fillFields(data, size, offset);
     }
diff --git a/src/java/org/apache/poi/hssf/record/RecordProcessor.java b/src/java/org/apache/poi/hssf/record/RecordProcessor.java
index c8d659c..ba86f67 100644
--- a/src/java/org/apache/poi/hssf/record/RecordProcessor.java
+++ b/src/java/org/apache/poi/hssf/record/RecordProcessor.java
@@ -156,5 +156,10 @@
         recordOffset += amount;
         available -= amount;
     }
+
+    public int getRecordOffset()
+    {
+        return recordOffset;
+    }
 }
 
diff --git a/src/java/org/apache/poi/hssf/record/SSTDeserializer.java b/src/java/org/apache/poi/hssf/record/SSTDeserializer.java
index dcf4e50..40fd4b0 100644
--- a/src/java/org/apache/poi/hssf/record/SSTDeserializer.java
+++ b/src/java/org/apache/poi/hssf/record/SSTDeserializer.java
@@ -62,13 +62,14 @@
  * Handles the task of deserializing a SST string.  The two main entry points are
  *
  * @author Glen Stampoultzis (glens at apache.org)
+ * @author Jason Height (jheight at apache.org)
  */
 class SSTDeserializer
 {
 
     private BinaryTree strings;
-    /** this is the number of characters we expect in the first sub-record in a subsequent continuation record */
-    private int continuationExpectedChars;
+    /** this is the number of characters that have been read prior to the continuation */
+    private int continuationReadChars;
     /** this is the string we were working on before hitting the end of the current record. This string is NOT finished. */
     private String unfinishedString;
     /** this is true if the string uses wide characters */
@@ -82,6 +83,7 @@
     /** Number of characters in current string */
     private int charCount;
     private int extensionLength;
+    private int continueSkipBytes = 0;
 
 
     public SSTDeserializer( BinaryTree strings )
@@ -93,13 +95,14 @@
     private void initVars()
     {
         runCount = 0;
-        continuationExpectedChars = 0;
+        continuationReadChars = 0;
         unfinishedString = "";
 //        bytesInCurrentSegment = 0;
 //        stringDataOffset = 0;
         wideChar = false;
         richText = false;
         extendedText = false;
+        continueSkipBytes = 0;
     }
 
     /**
@@ -107,14 +110,15 @@
      * strings may span across multiple continuations. Read the SST record
      * carefully before beginning to hack.
      */
-    public void manufactureStrings( final byte[] data, final int initialOffset, short dataSize )
+    public void manufactureStrings( final byte[] data, final int initialOffset)
     {
         initVars();
 
         int offset = initialOffset;
-        while ( ( offset - initialOffset ) < dataSize )
+        final int dataSize = data.length;
+        while ( offset < dataSize )
         {
-            int remaining = dataSize - offset + initialOffset;
+            int remaining = dataSize - offset;
 
             if ( ( remaining > 0 ) && ( remaining < LittleEndianConsts.SHORT_SIZE ) )
             {
@@ -122,26 +126,31 @@
             }
             if ( remaining == LittleEndianConsts.SHORT_SIZE )
             {
-                setContinuationExpectedChars( LittleEndian.getUShort( data, offset ) );
+              //JMH Dont know about this
+                setContinuationCharsRead( 0 );//LittleEndian.getUShort( data, offset ) );
                 unfinishedString = "";
                 break;
             }
             charCount = LittleEndian.getUShort( data, offset );
+            int charsRead = charCount;
             readStringHeader( data, offset );
             boolean stringContinuesOverContinuation = remaining < totalStringSize();
             if ( stringContinuesOverContinuation )
             {
-                int remainingBytes = ( initialOffset + dataSize ) - offset - stringHeaderOverhead();
-                setContinuationExpectedChars( charCount - calculateCharCount( remainingBytes ) );
-                charCount -= getContinuationExpectedChars();
+                int remainingBytes = dataSize - offset - stringHeaderOverhead();
+                //Only read the size of the string or whatever is left before the
+                //continuation
+                charsRead = Math.min(charsRead, calculateCharCount( remainingBytes ));
+                setContinuationCharsRead( charsRead );                
+                if (charsRead == charCount) {
+                  //Since all of the characters will have been read, but the entire string (including formatting runs etc)
+                  //hasnt, Compute the number of bytes to skip when the continue record starts
+                  continueSkipBytes = offsetForContinuedRecord(0) - (remainingBytes - calculateByteCount(charsRead));
+                }
             }
-            else
-            {
-                setContinuationExpectedChars( 0 );
-            }
-            processString( data, offset, charCount );
+            processString( data, offset, charsRead );
             offset += totalStringSize();
-            if ( getContinuationExpectedChars() != 0 )
+            if ( stringContinuesOverContinuation )
             {
                 break;
             }
@@ -222,6 +231,7 @@
         UnicodeString string = new UnicodeString( UnicodeString.sid,
                 (short) unicodeStringBuffer.length,
                 unicodeStringBuffer );
+        setContinuationCharsRead( calculateCharCount(bytesRead));
 
         if ( isStringFinished() )
         {
@@ -238,7 +248,7 @@
 
     private boolean isStringFinished()
     {
-        return getContinuationExpectedChars() == 0;
+        return getContinuationCharsRead() == charCount;
     }
 
     /**
@@ -301,8 +311,9 @@
     {
         if ( isStringFinished() )
         {
+            final int offset = continueSkipBytes;
             initVars();
-            manufactureStrings( record, 0, (short) record.length );
+            manufactureStrings( record, offset);
         }
         else
         {
@@ -330,13 +341,12 @@
      */
     private void readStringRemainder( final byte[] record )
     {
-        int stringRemainderSizeInBytes = calculateByteCount( getContinuationExpectedChars() );
-//        stringDataOffset = LittleEndianConsts.BYTE_SIZE;
+        int stringRemainderSizeInBytes = calculateByteCount( charCount-getContinuationCharsRead() );
         byte[] unicodeStringData = new byte[SSTRecord.STRING_MINIMAL_OVERHEAD
-                + calculateByteCount( getContinuationExpectedChars() )];
+                + stringRemainderSizeInBytes];
 
         // write the string length
-        LittleEndian.putShort( unicodeStringData, 0, (short) getContinuationExpectedChars() );
+        LittleEndian.putShort( unicodeStringData, 0, (short) (charCount-getContinuationCharsRead()) );
 
         // write the options flag
         unicodeStringData[LittleEndianConsts.SHORT_SIZE] = createOptionByte( wideChar, richText, extendedText );
@@ -345,7 +355,7 @@
         // past all the overhead of the str_data array
         arraycopy( record, LittleEndianConsts.BYTE_SIZE, unicodeStringData,
                 SSTRecord.STRING_MINIMAL_OVERHEAD,
-                unicodeStringData.length - SSTRecord.STRING_MINIMAL_OVERHEAD );
+                stringRemainderSizeInBytes );
 
         // use special constructor to create the final string
         UnicodeString string = new UnicodeString( UnicodeString.sid,
@@ -356,7 +366,7 @@
         addToStringTable( strings, integer, string );
 
         int newOffset = offsetForContinuedRecord( stringRemainderSizeInBytes );
-        manufactureStrings( record, newOffset, (short) ( record.length - newOffset ) );
+        manufactureStrings( record, newOffset);
     }
 
     /**
@@ -388,8 +398,12 @@
 
     private int offsetForContinuedRecord( int stringRemainderSizeInBytes )
     {
-        return stringRemainderSizeInBytes + LittleEndianConsts.BYTE_SIZE
-                + runCount * LittleEndianConsts.INT_SIZE + extensionLength;
+        int offset = stringRemainderSizeInBytes + runCount * LittleEndianConsts.INT_SIZE + extensionLength;        
+        if (stringRemainderSizeInBytes != 0)
+          //If a portion of the string remains then the wideChar options byte is repeated,
+          //so need to skip this.
+          offset += + LittleEndianConsts.BYTE_SIZE;
+        return offset;  
     }
 
     private byte createOptionByte( boolean wideChar, boolean richText, boolean farEast )
@@ -409,17 +423,22 @@
         int dataLengthInBytes = record.length - LittleEndianConsts.BYTE_SIZE;
         byte[] unicodeStringData = new byte[record.length + LittleEndianConsts.SHORT_SIZE];
 
-        LittleEndian.putShort( unicodeStringData, (byte) 0, (short) calculateCharCount( dataLengthInBytes ) );
+        int charsRead = calculateCharCount( dataLengthInBytes );
+        LittleEndian.putShort( unicodeStringData, (byte) 0, (short) charsRead );
         arraycopy( record, 0, unicodeStringData, LittleEndianConsts.SHORT_SIZE, record.length );
-        UnicodeString ucs = new UnicodeString( UnicodeString.sid, (short) unicodeStringData.length, unicodeStringData );
+        UnicodeString ucs = new UnicodeString( UnicodeString.sid, (short) unicodeStringData.length, unicodeStringData, unfinishedString);
 
-        unfinishedString = unfinishedString + ucs.getString();
-        setContinuationExpectedChars( getContinuationExpectedChars() - calculateCharCount( dataLengthInBytes ) );
+        unfinishedString = ucs.getString();
+        setContinuationCharsRead( getContinuationCharsRead() + charsRead );
+        if (getContinuationCharsRead() == charCount) {
+          Integer integer = new Integer( strings.size() );
+          addToStringTable( strings, integer, ucs );
+        }
     }
 
     private boolean stringSpansContinuation( int continuationSizeInBytes )
     {
-        return calculateByteCount( getContinuationExpectedChars() ) > continuationSizeInBytes;
+        return calculateByteCount( charCount - getContinuationCharsRead() ) > continuationSizeInBytes;
     }
 
     /**
@@ -427,14 +446,14 @@
      *         sub-record in a subsequent continuation record
      */
 
-    int getContinuationExpectedChars()
+    int getContinuationCharsRead()
     {
-        return continuationExpectedChars;
+        return continuationReadChars;
     }
 
-    private void setContinuationExpectedChars( final int count )
+    private void setContinuationCharsRead( final int count )
     {
-        continuationExpectedChars = count;
+        continuationReadChars = count;
     }
 
     private int calculateByteCount( final int character_count )
diff --git a/src/java/org/apache/poi/hssf/record/SSTRecord.java b/src/java/org/apache/poi/hssf/record/SSTRecord.java
index a3f0807..7f703e7 100644
--- a/src/java/org/apache/poi/hssf/record/SSTRecord.java
+++ b/src/java/org/apache/poi/hssf/record/SSTRecord.java
@@ -60,7 +60,6 @@
 
 import java.util.Iterator;
 import java.util.List;
-import java.util.ArrayList;
 
 /**
  * Title:        Static String Table Record
@@ -73,7 +72,7 @@
  * @author Andrew C. Oliver (acoliver at apache dot org)
  * @author Marc Johnson (mjohnson at apache dot org)
  * @author Glen Stampoultzis (glens at apache.org)
- * @version 2.0-pre
+ *
  * @see org.apache.poi.hssf.record.LabelSSTRecord
  * @see org.apache.poi.hssf.record.ContinueRecord
  */
@@ -112,10 +111,14 @@
     private List _record_lengths = null;
     private SSTDeserializer deserializer;
 
+    /** Offsets from the beginning of the SST record (even across continuations) */
+    int[] bucketAbsoluteOffsets;
+    /** Offsets relative the start of the current SST or continue record */
+    int[] bucketRelativeOffsets;
+
     /**
      * default constructor
      */
-
     public SSTRecord()
     {
         field_1_num_strings = 0;
@@ -220,7 +223,7 @@
         field_1_num_strings++;
         String str = ( string == null ) ? ""
                 : string;
-        int rval = -1;
+        int rval;
         UnicodeString ucs = new UnicodeString();
 
         ucs.setString( str );
@@ -334,7 +337,7 @@
         for ( int k = 0; k < field_3_strings.size(); k++ )
         {
             buffer.append( "    .string_" + k + "      = " )
-                    .append( ( (UnicodeString) field_3_strings
+                    .append( ( field_3_strings
                     .get( new Integer( k ) ) ).toString() ).append( "\n" );
         }
         buffer.append( "[/SST]\n" );
@@ -394,7 +397,7 @@
      * The data consists of sets of string data. This string data is
      * arranged as follows:
      * <P>
-     * <CODE>
+     * <CODE><pre>
      * short  string_length;   // length of string data
      * byte   string_flag;     // flag specifying special string
      *                         // handling
@@ -407,7 +410,7 @@
      *                         // array is run_count)
      * byte[] extension;       // optional extension (length of array
      *                         // is extend_length)
-     * </CODE>
+     * </pre></CODE>
      * <P>
      * The string_flag is bit mapped as follows:
      * <P>
@@ -479,7 +482,7 @@
         field_2_num_unique_strings = LittleEndian.getInt( data, 4 + offset );
         field_3_strings = new BinaryTree();
         deserializer = new SSTDeserializer(field_3_strings);
-        deserializer.manufactureStrings( data, 8 + offset, (short)(size - 8) );
+        deserializer.manufactureStrings( data, 8 + offset);
     }
 
 
@@ -507,14 +510,22 @@
      * Subclasses should implement this so that their data is passed back in a
      * byte array.
      *
-     * @return byte array containing instance data
+     * @return size
      */
 
     public int serialize( int offset, byte[] data )
     {
         SSTSerializer serializer = new SSTSerializer(
                 _record_lengths, field_3_strings, getNumStrings(), getNumUniqueStrings() );
-        return serializer.serialize( getRecordSize(), offset, data );
+        int bytes = serializer.serialize( getRecordSize(), offset, data );
+        bucketAbsoluteOffsets = serializer.getBucketAbsoluteOffsets();
+        bucketRelativeOffsets = serializer.getBucketRelativeOffsets();
+//        for ( int i = 0; i < bucketAbsoluteOffsets.length; i++ )
+//        {
+//            System.out.println( "bucketAbsoluteOffset = " + bucketAbsoluteOffsets[i] );
+//            System.out.println( "bucketRelativeOffset = " + bucketRelativeOffsets[i] );
+//        }
+        return bytes;
     }
 
 
@@ -538,6 +549,45 @@
     {
         deserializer.processContinueRecord( record );
     }
+
+    /**
+     * Creates an extended string record based on the current contents of
+     * the current SST record.  The offset within the stream to the SST record
+     * is required because the extended string record points directly to the
+     * strings in the SST record.
+     * <p>
+     * NOTE: THIS FUNCTION MUST ONLY BE CALLED AFTER THE SST RECORD HAS BEEN
+     *       SERIALIZED.
+     *
+     * @param sstOffset     The offset in the stream to the start of the
+     *                      SST record.
+     * @return  The new SST record.
+     */
+    public ExtSSTRecord createExtSSTRecord(int sstOffset)
+    {
+        if (bucketAbsoluteOffsets == null || bucketAbsoluteOffsets == null)
+            throw new IllegalStateException("SST record has not yet been serialized.");
+
+        ExtSSTRecord extSST = new ExtSSTRecord();
+        extSST.setNumStringsPerBucket((short)8);
+        int[] absoluteOffsets = (int[]) bucketAbsoluteOffsets.clone();
+        int[] relativeOffsets = (int[]) bucketRelativeOffsets.clone();
+        for ( int i = 0; i < absoluteOffsets.length; i++ )
+            absoluteOffsets[i] += sstOffset;
+        extSST.setBucketOffsets(absoluteOffsets, relativeOffsets);
+        return extSST;
+    }
+
+    /**
+     * Calculates the size in bytes of the EXTSST record as it would be if the
+     * record was serialized.
+     *
+     * @return  The size of the ExtSST record in bytes.
+     */
+    public int calcExtSSTRecordSize()
+    {
+      return ExtSSTRecord.getRecordSizeForStrings(field_3_strings.size());
+    }
 }
 
 
diff --git a/src/java/org/apache/poi/hssf/record/SSTRecordSizeCalculator.java b/src/java/org/apache/poi/hssf/record/SSTRecordSizeCalculator.java
index fbdfba5..15a52f9 100644
--- a/src/java/org/apache/poi/hssf/record/SSTRecordSizeCalculator.java
+++ b/src/java/org/apache/poi/hssf/record/SSTRecordSizeCalculator.java
@@ -61,7 +61,9 @@
 import java.util.Map;
 
 /**
- * Used to calculate the record sizes for a particular record.
+ * Used to calculate the record sizes for a particular record.  This kind of
+ * sucks because it's similar to the SST serialization code.  In general
+ * the SST serialization code needs to be rewritten.
  *
  * @author Glen Stampoultzis (glens at apache.org)
  */
diff --git a/src/java/org/apache/poi/hssf/record/SSTSerializer.java b/src/java/org/apache/poi/hssf/record/SSTSerializer.java
index 8239eeb..f4538bb 100644
--- a/src/java/org/apache/poi/hssf/record/SSTSerializer.java
+++ b/src/java/org/apache/poi/hssf/record/SSTSerializer.java
@@ -77,6 +77,12 @@
     private int numUniqueStrings;
     private SSTRecordHeader sstRecordHeader;
 
+    /** Offsets from the beginning of the SST record (even across continuations) */
+    int[] bucketAbsoluteOffsets;
+    /** Offsets relative the start of the current SST or continue record */
+    int[] bucketRelativeOffsets;
+    int startOfSST, startOfRecord;
+
     public SSTSerializer( List recordLengths, BinaryTree strings, int numStrings, int numUniqueStrings )
     {
         this.recordLengths = recordLengths;
@@ -84,6 +90,10 @@
         this.numStrings = numStrings;
         this.numUniqueStrings = numUniqueStrings;
         this.sstRecordHeader = new SSTRecordHeader( numStrings, numUniqueStrings );
+
+        int infoRecs = ExtSSTRecord.getNumberOfInfoRecsForStrings(strings.size());
+        this.bucketAbsoluteOffsets = new int[infoRecs];
+        this.bucketRelativeOffsets = new int[infoRecs];
     }
 
     /**
@@ -133,7 +143,6 @@
 
     /**
      * This case is chosen when an SST record does not span over to a continue record.
-     *
      */
     private void serializeSingleSSTRecord( byte[] data, int offset, int record_length_index )
     {
@@ -144,6 +153,15 @@
 
         for ( int k = 0; k < strings.size(); k++ )
         {
+            if (k % ExtSSTRecord.DEFAULT_BUCKET_SIZE == 0)
+            {
+              int index = k/ExtSSTRecord.DEFAULT_BUCKET_SIZE;
+              if (index < ExtSSTRecord.MAX_BUCKETS) {
+                //Excel only indexes the first 128 buckets.
+                bucketAbsoluteOffsets[index] = pos;
+                bucketRelativeOffsets[index] = pos;
+              }
+            }
             System.arraycopy( getUnicodeString( k ).serialize(), 0, data, pos + offset, getUnicodeString( k ).getRecordSize() );
             pos += getUnicodeString( k ).getRecordSize();
         }
@@ -157,6 +175,8 @@
     private void serializeLargeRecord( int record_size, int record_length_index, byte[] buffer, int offset )
     {
 
+        startOfSST = offset;
+
         byte[] stringReminant = null;
         int stringIndex = 0;
         boolean lastneedcontinue = false;
@@ -170,6 +190,7 @@
                     recordLength, numStrings, numUniqueStrings );
 
             // write the appropriate header
+            startOfRecord = offset + totalWritten;
             recordProcessor.writeRecordHeader( offset, totalWritten, recordLength, first_record );
             first_record = false;
 
@@ -189,6 +210,17 @@
             {
                 UnicodeString unistr = getUnicodeString( stringIndex );
 
+                if (stringIndex % ExtSSTRecord.DEFAULT_BUCKET_SIZE == 0)
+                {
+                  int index = stringIndex / ExtSSTRecord.DEFAULT_BUCKET_SIZE;
+                  if (index < ExtSSTRecord.MAX_BUCKETS) {
+                    bucketAbsoluteOffsets[index] = offset + totalWritten +
+                        recordProcessor.getRecordOffset() - startOfSST;
+                    bucketRelativeOffsets[index] = offset + totalWritten +
+                        recordProcessor.getRecordOffset() - startOfRecord;
+                  }
+                }
+
                 if ( unistr.getRecordSize() <= recordProcessor.getAvailable() )
                 {
                     recordProcessor.writeWholeString( unistr, offset, totalWritten );
@@ -235,4 +267,14 @@
     {
         return recordLengths;
     }
+
+    public int[] getBucketAbsoluteOffsets()
+    {
+        return bucketAbsoluteOffsets;
+    }
+
+    public int[] getBucketRelativeOffsets()
+    {
+        return bucketRelativeOffsets;
+    }
 }
diff --git a/src/java/org/apache/poi/hssf/record/StringRecord.java b/src/java/org/apache/poi/hssf/record/StringRecord.java
index ed157bb..914a92d 100644
--- a/src/java/org/apache/poi/hssf/record/StringRecord.java
+++ b/src/java/org/apache/poi/hssf/record/StringRecord.java
@@ -138,6 +138,11 @@
         }
     }
 
+    public boolean isInValueSection()
+    {
+        return true;
+    }
+
     private int getStringLength()
     {
         return field_1_string_length;
diff --git a/src/java/org/apache/poi/hssf/record/StyleRecord.java b/src/java/org/apache/poi/hssf/record/StyleRecord.java
index c4f8ee3..d6bb482 100644
--- a/src/java/org/apache/poi/hssf/record/StyleRecord.java
+++ b/src/java/org/apache/poi/hssf/record/StyleRecord.java
@@ -57,12 +57,14 @@
 
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
+import org.apache.poi.util.BitField;
 
 /**
  * Title:        Style Record<P>
  * Description:  Describes a builtin to the gui or user defined style<P>
  * REFERENCE:  PG 390 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
  * @author Andrew C. Oliver (acoliver at apache dot org)
+ * @author aviks : string fixes for UserDefined Style
  * @version 2.0-pre
  */
 
@@ -81,8 +83,10 @@
     private byte              field_3_outline_style_level;
 
     // only for user defined styles
-    private byte              field_2_name_length;
-    private String            field_3_name;
+    private short              field_2_name_length; //OO doc says 16 bit length, so we believe
+    private byte               field_3_string_options;
+    private BitField fHighByte;
+    private String             field_4_name;
 
     public StyleRecord()
     {
@@ -125,17 +129,24 @@
 
     protected void fillFields(byte [] data, short size, int offset)
     {
+        fHighByte = new BitField(0x01); //have to init here, since we are being called
+                                        //from super, and class level init hasnt been done. 
         field_1_xf_index = LittleEndian.getShort(data, 0 + offset);
-        if (getType() == 1)
+        if (getType() == STYLE_BUILT_IN)
         {
             field_2_builtin_style       = data[ 2 + offset ];
             field_3_outline_style_level = data[ 3 + offset ];
         }
-        else if (getType() == 0)
+        else if (getType() == STYLE_USER_DEFINED)
         {
-            field_2_name_length = data[ 2 + offset ];
-            field_3_name        = StringUtil.getFromCompressedUnicode(data, 3 + offset,
-                                             LittleEndian.ubyteToInt(field_2_name_length));
+            field_2_name_length = LittleEndian.getShort(data, 2 + offset );
+            field_3_string_options = data[4+offset];
+            
+            if (fHighByte.isSet(field_3_string_options)) {
+                field_4_name= StringUtil.getFromUnicode(data,offset+5,field_2_name_length);
+            }else {
+                field_4_name=StringUtil.getFromCompressedUnicode(data,offset+5,field_2_name_length);
+            }
         }
 
         // todo sanity check exception to make sure we're one or the other
@@ -199,7 +210,8 @@
 
     public void setName(String name)
     {
-        field_3_name = name;
+        field_4_name = name;
+        //TODO set name length and string options
     }
 
     // end user defined
@@ -273,7 +285,7 @@
      * @see #getName()
      */
 
-    public byte getNameLength()
+    public short getNameLength()
     {
         return field_2_name_length;
     }
@@ -286,7 +298,7 @@
 
     public String getName()
     {
-        return field_3_name;
+        return field_4_name;
     }
 
     // end user defined
@@ -361,7 +373,7 @@
         else
         {
             LittleEndian.putShort(data, 2 + offset,
-                                  (( short ) (0x03 + getNameLength())));
+                                  (( short ) (getRecordSize()-4)));
         }
         LittleEndian.putShort(data, 4 + offset, getIndex());
         if (getType() == STYLE_BUILT_IN)
@@ -371,8 +383,9 @@
         }
         else
         {
-            data[ 6 + offset ] = getNameLength();
-            StringUtil.putCompressedUnicode(getName(), data, 7 + offset);
+            LittleEndian.putShort(data, 6 + offset , getNameLength());
+            data[8+offset]=this.field_3_string_options;
+            StringUtil.putCompressedUnicode(getName(), data, 9 + offset);
         }
         return getRecordSize();
     }
@@ -387,7 +400,11 @@
         }
         else
         {
-            retval = 7 + getNameLength();
+             if (fHighByte.isSet(field_3_string_options))  {
+                 retval= 9+2*getNameLength();
+             }else {
+                retval = 9 + getNameLength();
+             }
         }
         return retval;
     }
diff --git a/src/java/org/apache/poi/hssf/record/UnknownRecord.java b/src/java/org/apache/poi/hssf/record/UnknownRecord.java
index 36b0595..b88d22f 100644
--- a/src/java/org/apache/poi/hssf/record/UnknownRecord.java
+++ b/src/java/org/apache/poi/hssf/record/UnknownRecord.java
@@ -65,16 +65,14 @@
  * Company:      SuperLink Software, Inc.<P>
  * @author Andrew C. Oliver (acoliver at apache dot org)
  * @author Jason Height (jheight at chariot dot net dot au)
- * @version 2.0-pre
+ * @author Glen Stampoultzis (glens at apache.org)
  */
 
 public class UnknownRecord
     extends Record
 {
-    private short  sid     = 0;
-    private short  size    = 0;
-    private byte[] thedata = null;
-    int            offset  = 0;
+    private short   sid     = 0;
+    private byte[]  thedata = null;
 
     public UnknownRecord()
     {
@@ -91,7 +89,6 @@
     public UnknownRecord(short id, short size, byte [] data)
     {
         sid     = id;
-        size    = size;
         thedata = data;
     }
 
@@ -127,7 +124,7 @@
 
     protected void fillFields(byte [] data, short sid)
     {
-        sid     = sid;
+        this.sid     = sid;
         thedata = data;
     }
 
@@ -179,9 +176,7 @@
     /** Unlike the other Record.clone methods this is a shallow clone*/
     public Object clone() {
       UnknownRecord rec = new UnknownRecord();
-      rec.offset = offset;
       rec.sid = sid;
-      rec.size = size;
       rec.thedata = thedata;
       return rec;
     }
diff --git a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
index 7d5b0b0..761d821 100644
--- a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
+++ b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
@@ -82,6 +82,19 @@
         this.stringRecord = stringRecord;
     }
 
+	/**
+	 * Used only in the clone
+	 * @param formulaRecord
+	 * @param stringRecord
+	 * @param sharedRecord
+	 */
+	public FormulaRecordAggregate( FormulaRecord formulaRecord, StringRecord stringRecord, SharedFormulaRecord sharedRecord)
+	{
+		  this.formulaRecord = formulaRecord;
+		  this.stringRecord = stringRecord;
+		  this.sharedFormulaRecord = sharedRecord;
+	}
+
 
 
     protected void validateSid( short id )
@@ -106,14 +119,14 @@
     {
         int pos = offset;
         pos += formulaRecord.serialize(pos, data);
-        if (stringRecord != null)
-        {
-            pos += stringRecord.serialize(pos, data);
-        }
         if (this.getSharedFormulaRecord() != null) 
         {
         		pos += getSharedFormulaRecord().serialize(pos, data);
         }	
+         if (stringRecord != null)
+        {
+            pos += stringRecord.serialize(pos, data);
+        }
         return pos - offset;
         
     }
@@ -221,7 +234,10 @@
      * @see java.lang.Object#clone()
      */
     public Object clone() {
-        return new FormulaRecordAggregate((FormulaRecord) this.formulaRecord.clone(), (StringRecord) this.stringRecord.clone());
+			StringRecord clonedString = (stringRecord == null) ? null : (StringRecord)stringRecord.clone();
+    		SharedFormulaRecord clonedShared = (sharedFormulaRecord == null) ? null : (SharedFormulaRecord)sharedFormulaRecord.clone();
+    		
+        return new FormulaRecordAggregate((FormulaRecord) this.formulaRecord.clone(), clonedString, clonedShared);
     }
 
 
@@ -241,4 +257,19 @@
       this.sharedFormulaRecord = sharedFormulaRecord;
    }
 
+   /* 
+    * Setting to true so that this value does not abort the whole ValueAggregation
+    * (non-Javadoc)
+    * @see org.apache.poi.hssf.record.Record#isInValueSection()
+    */
+   public boolean isInValueSection() {
+
+      return true;
+   }
+   
+   public String getStringValue() {
+        if(stringRecord==null) return null;
+        return stringRecord.getString();
+   }
+
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java
index da08a6d..cd26602 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java
@@ -54,9 +54,8 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.BinaryTree;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
-import java.util.Stack;
 
 /**
  * This class provides the base functionality for Excel sheet functions 
@@ -69,7 +68,7 @@
 	//constant used allow a ptgAttr to be mapped properly for its functionPtg
 	public static final String ATTR_NAME = "specialflag";
 	    
-      
+    public static final short INDEX_EXTERNAL = 255;
     
     private static BinaryTree map = produceHash(); 
     protected static Object[][] functionData = produceFunctionData();
@@ -104,7 +103,7 @@
         return lookupName(field_2_fnc_index);
     }
     
-    public String toFormulaString(SheetReferences refs) {
+    public String toFormulaString(Workbook book) {
         return getName();
     }
     
@@ -140,7 +139,9 @@
     }
     
     protected short lookupIndex(String name) {
-        return (short)((Integer)map.getKeyForValue(name)).intValue();
+        Integer index = (Integer) map.getKeyForValue(name);
+        if (index != null) return index.shortValue();
+        return INDEX_EXTERNAL;
     }
     
     /**
@@ -389,6 +390,7 @@
         dmap.put(new Integer(252),"FREQUENCY");
         dmap.put(new Integer(253),"ADDTOOLBAR");
         dmap.put(new Integer(254),"DELETETOOLBAR");
+        dmap.put(new Integer(255),"externalflag");
         dmap.put(new Integer(256),"RESETTOOLBAR");
         dmap.put(new Integer(257),"EVALUATE");
         dmap.put(new Integer(258),"GETTOOLBAR");
diff --git a/src/java/org/apache/poi/hssf/record/formula/AddPtg.java b/src/java/org/apache/poi/hssf/record/formula/AddPtg.java
index ead8840..3a519dc 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AddPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AddPtg.java
@@ -62,7 +62,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Addition operator PTG the "+" binomial operator.  If you need more 
@@ -113,7 +113,7 @@
     }
     
     /** Implementation of method from Ptg */
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "+";
     }
diff --git a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
index 1e42566..59c405c 100644
--- a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
@@ -291,8 +291,9 @@
 
 	}
 
-	public String toFormulaString( SheetReferences refs )
+	public String toFormulaString(Workbook book)
 	{
+		SheetReferences refs = book == null ? null : book.getSheetReferences();
 		StringBuffer retval = new StringBuffer();
 		if ( refs != null )
 		{
@@ -318,6 +319,7 @@
 		ptg.field_3_last_row = field_3_last_row;
 		ptg.field_4_first_column = field_4_first_column;
 		ptg.field_5_last_column = field_5_last_column;
+            ptg.setClass(ptgClass);
 		return ptg;
 	}
 
@@ -351,4 +353,3 @@
 
 
 }
-
diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
index 87ebb2c..3189a2c 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
@@ -65,7 +65,7 @@
 
 import org.apache.poi.hssf.util.AreaReference;
 import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Specifies a rectangular area of cells A1:A4 for instance.
@@ -305,7 +305,7 @@
         field_4_last_column = column;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
          return (new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative())).toString() + ":" +
                 (new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative())).toString();
@@ -321,6 +321,7 @@
       ptg.field_2_last_row = field_2_last_row;
       ptg.field_3_first_column = field_3_first_column;
       ptg.field_4_last_column = field_4_last_column;
+      ptg.setClass(ptgClass);
       return ptg;
     }
 
diff --git a/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java b/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
index d389094..ebbfcba 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
@@ -60,7 +60,7 @@
  */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.BitField;
@@ -207,11 +207,11 @@
         if(space.isSet(field_1_options)) {
             return operands[ 0 ];
         } else if (optiIf.isSet(field_1_options)) {
-            return toFormulaString((SheetReferences)null) + "(" + operands[ 0 ]             +")"; 
+            return toFormulaString((Workbook)null) + "(" + operands[ 0 ]             +")";
         } else if (optGoto.isSet(field_1_options)) {
-            return toFormulaString((SheetReferences)null) + operands[0];   //goto isn't a real formula element should not show up
+            return toFormulaString((Workbook)null) + operands[0];   //goto isn't a real formula element should not show up
         } else {
-            return toFormulaString((SheetReferences)null) + "(" + operands[ 0 ] + ")";
+            return toFormulaString((Workbook)null) + "(" + operands[ 0 ] + ")";
         }
     }
   
@@ -226,7 +226,7 @@
         return -1;
     }
         
-   public String toFormulaString(SheetReferences refs) {
+   public String toFormulaString(Workbook book) {
       if(semiVolatile.isSet(field_1_options)) {
         return "ATTR(semiVolatile)";
       }
diff --git a/src/java/org/apache/poi/hssf/record/formula/BoolPtg.java b/src/java/org/apache/poi/hssf/record/formula/BoolPtg.java
index 954eb0f..84ad7cf 100644
--- a/src/java/org/apache/poi/hssf/record/formula/BoolPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/BoolPtg.java
@@ -60,7 +60,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Boolean (boolean)
@@ -114,7 +114,7 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return field_1_value ? "TRUE" : "FALSE";
     }
diff --git a/src/java/org/apache/poi/hssf/record/formula/ConcatPtg.java b/src/java/org/apache/poi/hssf/record/formula/ConcatPtg.java
index bed92e4..e7249bf 100644
--- a/src/java/org/apache/poi/hssf/record/formula/ConcatPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/ConcatPtg.java
@@ -62,7 +62,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -108,7 +108,7 @@
         return 2;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return CONCAT;
     }    
diff --git a/src/java/org/apache/poi/hssf/record/formula/DividePtg.java b/src/java/org/apache/poi/hssf/record/formula/DividePtg.java
index 124f25e..6bb0d94 100644
--- a/src/java/org/apache/poi/hssf/record/formula/DividePtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/DividePtg.java
@@ -62,7 +62,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * This PTG implements the standard binomial divide "/"
@@ -108,7 +108,7 @@
         return 2;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "/";
     }
@@ -117,7 +117,7 @@
         StringBuffer buffer = new StringBuffer();
 
         buffer.append(operands[ 0 ]);
-        buffer.append(toFormulaString((SheetReferences)null));
+        buffer.append(toFormulaString((Workbook)null));
         buffer.append(operands[ 1 ]);
         return buffer.toString();
     }      
diff --git a/src/java/org/apache/poi/hssf/record/formula/EqualPtg.java b/src/java/org/apache/poi/hssf/record/formula/EqualPtg.java
index 1ddff9a..12438a3 100644
--- a/src/java/org/apache/poi/hssf/record/formula/EqualPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/EqualPtg.java
@@ -62,7 +62,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -107,7 +107,7 @@
         return 2;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "=";
     }
@@ -117,7 +117,7 @@
 
         
         buffer.append(operands[ 0 ]);
-        buffer.append(toFormulaString((SheetReferences)null));
+        buffer.append(toFormulaString((Workbook)null));
         buffer.append(operands[ 1 ]);
         return buffer.toString();
     }       
diff --git a/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java b/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java
index 576bce3..d70e579 100644
--- a/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java
@@ -60,7 +60,7 @@
  */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -102,7 +102,7 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "NO IDEA SHARED FORMULA EXP PTG";
     }
@@ -110,7 +110,10 @@
     public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
     
     public Object clone() {
-      throw new RuntimeException("NO IDEA SHARED FORMULA EXP PTG");
+    	//can't clone one that doesnt have data can we??
+		if (this.existing == null) throw new RuntimeException("NO IDEA SHARED FORMULA EXP PTG"); 
+		
+    	return new ExpPtg(this.existing, 0);
     }
 
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java
index 1c230c9..713d200 100644
--- a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java
@@ -63,7 +63,8 @@
       FuncPtg ptg = new FuncPtg();
       //ptg.field_1_num_args = field_1_num_args;
       ptg.field_2_fnc_index = field_2_fnc_index;
-      return ptg;
+      ptg.setClass(ptgClass);
+     return ptg;
     }
     
     public int getSize() {
diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java
index f333ba6..6bf4d7f 100644
--- a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java
@@ -52,6 +52,7 @@
       FuncVarPtg ptg = new FuncVarPtg();
       ptg.field_1_num_args = field_1_num_args;
       ptg.field_2_fnc_index = field_2_fnc_index;
+      ptg.setClass(ptgClass);
       return ptg;
     }
     
diff --git a/src/java/org/apache/poi/hssf/record/formula/GreaterEqualPtg.java b/src/java/org/apache/poi/hssf/record/formula/GreaterEqualPtg.java
index de0b623..b387a5e 100755
--- a/src/java/org/apache/poi/hssf/record/formula/GreaterEqualPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/GreaterEqualPtg.java
@@ -54,7 +54,7 @@
 package org.apache.poi.hssf.record.formula;
 
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * PTG class to implement greater or equal to
@@ -66,7 +66,7 @@
     public final static int  SIZE = 1;
     public final static byte sid  = 0x0c;
 
-    /** Creates new AddPtg */
+    /** Creates new GreaterEqualPtg */
 
    public GreaterEqualPtg()
     {
@@ -98,7 +98,7 @@
         return 2;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return ">=";
     }
@@ -108,7 +108,7 @@
 
         
         buffer.append(operands[ 0 ]);
-        buffer.append(toFormulaString((SheetReferences)null));
+        buffer.append(toFormulaString((Workbook)null));
         buffer.append(operands[ 1 ]);
         return buffer.toString();
     }       
diff --git a/src/java/org/apache/poi/hssf/record/formula/GreaterThanPtg.java b/src/java/org/apache/poi/hssf/record/formula/GreaterThanPtg.java
index 5f7d645..04fbcac 100644
--- a/src/java/org/apache/poi/hssf/record/formula/GreaterThanPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/GreaterThanPtg.java
@@ -61,7 +61,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Greater than operator PTG ">"
@@ -133,7 +133,7 @@
      * Implementation of method from Ptg 
      * @param refs the Sheet References
      */
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return this.GREATERTHAN;
     }
@@ -171,5 +171,3 @@
         return new GreaterThanPtg();
     }
 }
-
-
diff --git a/src/java/org/apache/poi/hssf/record/formula/IntPtg.java b/src/java/org/apache/poi/hssf/record/formula/IntPtg.java
index 200f5dc..e06f1ec 100644
--- a/src/java/org/apache/poi/hssf/record/formula/IntPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/IntPtg.java
@@ -61,7 +61,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Integer (short intger)
@@ -116,7 +116,7 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "" + getValue();
     }
diff --git a/src/java/org/apache/poi/hssf/record/formula/LessEqualPtg.java b/src/java/org/apache/poi/hssf/record/formula/LessEqualPtg.java
index 0c8165f..c1c7570 100755
--- a/src/java/org/apache/poi/hssf/record/formula/LessEqualPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/LessEqualPtg.java
@@ -53,7 +53,7 @@
  */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 
 /**
@@ -67,7 +67,7 @@
 	 public final static int  SIZE = 1;
 	 public final static byte sid  = 0x0a;
 
-	 /** Creates new AddPtg */
+	 /** Creates new LessEqualPtg */
 
 	public LessEqualPtg()
 	 {
@@ -99,7 +99,7 @@
 		  return 2;
 	 }
 
-	 public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
 	 {
 		  return "<=";
 	 }
@@ -109,7 +109,7 @@
 
         
 		  buffer.append(operands[ 0 ]);
-		  buffer.append(toFormulaString((SheetReferences)null));
+		  buffer.append(toFormulaString((Workbook)null));
 		  buffer.append(operands[ 1 ]);
 		  return buffer.toString();
 	 }       
diff --git a/src/java/org/apache/poi/hssf/record/formula/LessThanPtg.java b/src/java/org/apache/poi/hssf/record/formula/LessThanPtg.java
index cab99be..a856b93 100644
--- a/src/java/org/apache/poi/hssf/record/formula/LessThanPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/LessThanPtg.java
@@ -63,7 +63,7 @@
 import java.util.List;
 
 //POI
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Less than operator PTG "<". The SID is taken from the 
@@ -142,7 +142,7 @@
      * Implementation of method from Ptg 
      * @param refs the Sheet References
      */
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return this.LESSTHAN;
     }
@@ -180,6 +180,3 @@
     }
 
 }
-
-
-
diff --git a/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java b/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java
index c48ad8c..31a1bca 100644
--- a/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java
@@ -61,7 +61,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -118,7 +118,7 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "ERR#";
     }
diff --git a/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java b/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java
index 2d422b1..a8b6360 100644
--- a/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java
@@ -60,7 +60,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * @author Glen Stampoultzis (glens at apache.org)
@@ -96,7 +96,7 @@
         LittleEndian.putShort( array, offset + 1, (short)field_1_len_ref_subexpression );
     }
 
-    public String toFormulaString( SheetReferences refs )
+    public String toFormulaString(Workbook book)
     {
         return "";
     }
diff --git a/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java b/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java
index 3f88ded..c65ab41 100644
--- a/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java
@@ -54,7 +54,7 @@
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Missing Function Arguments
@@ -91,7 +91,7 @@
     }
 
    
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return " ";
     }
@@ -103,5 +103,3 @@
     }
 
 }
-
-
diff --git a/src/java/org/apache/poi/hssf/record/formula/MultiplyPtg.java b/src/java/org/apache/poi/hssf/record/formula/MultiplyPtg.java
index 143702c..6b33e61 100644
--- a/src/java/org/apache/poi/hssf/record/formula/MultiplyPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/MultiplyPtg.java
@@ -61,7 +61,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import java.util.List;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Implements the standard mathmatical multiplication - *
@@ -114,7 +114,7 @@
     }
     
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "*";
     }
@@ -123,9 +123,9 @@
     {
         StringBuffer buffer = new StringBuffer();
 
-        buffer.append(operands[ 0 ].toFormulaString((SheetReferences)null));
+        buffer.append(operands[ 0 ].toFormulaString((Workbook)null));
         buffer.append("*");
-        buffer.append(operands[ 1 ].toFormulaString((SheetReferences)null));
+        buffer.append(operands[ 1 ].toFormulaString((Workbook)null));
         return buffer.toString();
     }
     
@@ -133,7 +133,7 @@
         StringBuffer buffer = new StringBuffer();
 
         buffer.append(operands[ 0 ]);
-        buffer.append(toFormulaString((SheetReferences)null));
+        buffer.append(toFormulaString((Workbook)null));
         buffer.append(operands[ 1 ]);
         return buffer.toString();
     }                  
diff --git a/src/java/org/apache/poi/hssf/record/formula/NamePtg.java b/src/java/org/apache/poi/hssf/record/formula/NamePtg.java
index c926d51..6cdee6a 100644
--- a/src/java/org/apache/poi/hssf/record/formula/NamePtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/NamePtg.java
@@ -61,7 +61,8 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.NameRecord;
 
 /**
  *
@@ -73,10 +74,10 @@
     extends Ptg
 {
     public final static short sid  = 0x23;
-    private final static int  SIZE = 7;
-    private short             field_1_ixti;   // unknown function
-    private short             field_2_label_index;
-    private short             field_3_zero;   // reserved must be 0
+    private final static int  SIZE = 5;
+    private short             field_1_label_index;
+    private short             field_2_zero;   // reserved must be 0
+    boolean xtra=false;
 
 
     private NamePtg() {
@@ -85,9 +86,22 @@
 
     /** Creates new NamePtg */
 
-    public NamePtg(String name)
+    public NamePtg(String name, Workbook book)
     {
-        //TODO
+        final short n = (short) (book.getNumNames() + 1);
+        NameRecord rec;
+        for (short i = 1; i < n; i++) {
+            rec = book.getNameRecord(i - 1);
+            if (name.equals(rec.getNameText())) {
+                field_1_label_index = i;
+                return;
+            }
+        }
+        rec = new NameRecord();
+        rec.setNameText(name);
+        rec.setNameTextLength((byte) name.length());
+        book.addName(rec);
+        field_1_label_index = n;
     }
 
     /** Creates new NamePtg */
@@ -95,13 +109,17 @@
     public NamePtg(byte [] data, int offset)
     {
         offset++;
-        field_1_ixti        = LittleEndian.getShort(data, offset);
-        field_2_label_index = LittleEndian.getShort(data, offset + 2);
-        field_3_zero        = LittleEndian.getShort(data, offset + 4);
+        //field_1_ixti        = LittleEndian.getShort(data, offset);
+        field_1_label_index = LittleEndian.getShort(data, offset );
+        field_2_zero        = LittleEndian.getShort(data, offset + 2);
+        //if (data[offset+6]==0) xtra=true;
     }
 
     public void writeBytes(byte [] array, int offset)
     {
+        array[offset+0]= (byte) (sid + ptgClass);
+        LittleEndian.putShort(array,offset+1,field_1_label_index);
+        LittleEndian.putShort(array,offset+3, field_2_zero);
     }
 
     public int getSize()
@@ -109,18 +127,18 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
-        return "NO IDEA - NAME";
+        NameRecord rec = book.getNameRecord(field_1_label_index - 1);
+        return rec.getNameText();
     }
     
-    public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
+    public byte getDefaultOperandClass() {return Ptg.CLASS_REF;}
 
     public Object clone() {
       NamePtg ptg = new NamePtg();
-      ptg.field_1_ixti = field_1_ixti;
-      ptg.field_2_label_index = field_2_label_index;
-      ptg.field_3_zero = field_3_zero;
+      ptg.field_1_label_index = field_1_label_index;
+      ptg.field_2_zero = field_2_zero;
       return ptg;
     }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java
index b913f43..9b6225d 100644
--- a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java
@@ -61,7 +61,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -102,7 +102,7 @@
 
     public void writeBytes(byte [] array, int offset)
     {
-        array[ offset + 0 ] = sid;
+        array[ offset + 0 ] = (byte)(sid + ptgClass);
         LittleEndian.putShort(array, offset + 1, field_1_ixals);
         LittleEndian.putShort(array,offset+3, field_2_ilbl);
         LittleEndian.putShort(array, offset + 5, field_3_reserved);
@@ -113,7 +113,7 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "NO IDEA - NAME";
     }
@@ -125,6 +125,7 @@
       ptg.field_1_ixals = field_1_ixals;
       ptg.field_3_reserved = field_3_reserved;
       ptg.field_2_ilbl = field_2_ilbl;
+      ptg.setClass(ptgClass);
       return ptg;
     }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/NotEqualPtg.java b/src/java/org/apache/poi/hssf/record/formula/NotEqualPtg.java
index c43c196..b12b16a 100755
--- a/src/java/org/apache/poi/hssf/record/formula/NotEqualPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/NotEqualPtg.java
@@ -56,7 +56,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * Ptg class to implement not equal
@@ -69,7 +69,7 @@
 	 public final static int  SIZE = 1;
 	 public final static byte sid  = 0x0e;
 
-	 /** Creates new AddPtg */
+	 /** Creates new NotEqualPtg */
 
 	public NotEqualPtg()
 	 {
@@ -101,7 +101,7 @@
 		  return 2;
 	 }
 
-	 public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
 	 {
 		  return "<>";
 	 }
@@ -111,7 +111,7 @@
 
         
 		  buffer.append(operands[ 0 ]);
-		  buffer.append(toFormulaString((SheetReferences)null));
+		  buffer.append(toFormulaString((Workbook)null));
 		  buffer.append(operands[ 1 ]);
 		  return buffer.toString();
 	 }       
diff --git a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
index e071420..b43c553 100644
--- a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java
@@ -55,7 +55,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 /**
  * Number
  * Stores a floating point value in a formula
@@ -113,7 +113,7 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "" + getValue();
     }
@@ -125,4 +125,3 @@
       return ptg;
     }
 }
-
diff --git a/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java b/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java
index c2118f1..2abff9e 100644
--- a/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java
@@ -57,7 +57,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * While formula tokens are stored in RPN order and thus do not need parenthesis for 
@@ -107,7 +107,7 @@
         return 1;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "()";
     }
@@ -124,4 +124,3 @@
     }
 
 }
-
diff --git a/src/java/org/apache/poi/hssf/record/formula/PowerPtg.java b/src/java/org/apache/poi/hssf/record/formula/PowerPtg.java
index ca7fa64..161918a 100644
--- a/src/java/org/apache/poi/hssf/record/formula/PowerPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/PowerPtg.java
@@ -62,7 +62,7 @@
 
 import java.util.List;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -108,7 +108,7 @@
         return 2;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "^";
     }
@@ -118,7 +118,7 @@
 
         
         buffer.append(operands[ 0 ]);
-        buffer.append(toFormulaString((SheetReferences)null));
+        buffer.append(toFormulaString((Workbook)null));
         buffer.append(operands[ 1 ]);
         return buffer.toString();
     }       
diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java
index 3787d00..cdf584e 100644
--- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java
@@ -63,7 +63,7 @@
 import java.util.List;
 import java.util.ArrayList;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -307,6 +307,12 @@
             case MissingArgPtg.sid:
                 retval = new MissingArgPtg(data,offset);
                 break;
+            case UnaryPlusPtg.sid:
+                retval=new UnaryPlusPtg(data,offset);
+                break;
+            case UnaryMinusPtg.sid:
+                retval=new UnaryMinusPtg(data,offset);
+                break;
 
             default :
 
@@ -341,7 +347,7 @@
     /**
      * return a string representation of this token alone
      */
-    public abstract String toFormulaString(SheetReferences refs);
+    public abstract String toFormulaString(Workbook book);
     /**
      * dump a debug representation (hexdump) to a string
      */
@@ -357,6 +363,14 @@
         return retval;
     }
     
+    /** Overridden toString method to ensure object hash is not printed.
+     * This helps get rid of gratuitous diffs when comparing two dumps
+     * Subclasses may output more relevant information by overriding this method
+     **/
+    public String toString(){
+        return this.getClass().toString();
+    }
+    
     public static final byte CLASS_REF = 0x00;
     public static final byte CLASS_VALUE = 0x20;
     public static final byte CLASS_ARRAY = 0x40;
diff --git a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
index 0d84295..4e175e8 100644
--- a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
@@ -61,6 +61,7 @@
 import org.apache.poi.hssf.util.RangeAddress;
 import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.util.BitField;
 import org.apache.poi.hssf.model.Workbook;
 
@@ -104,7 +105,7 @@
     public String toString() {
         StringBuffer buffer = new StringBuffer();
 
-        buffer.append("Ref3dPrg\n");
+        buffer.append("Ref3dPtg\n");
         buffer.append("Index to Extern Sheet = " + getExternSheetIndex()).append("\n");
         buffer.append("Row = " + getRow()).append("\n");
         buffer.append("Col  = " + getColumn()).append("\n");
@@ -193,8 +194,9 @@
 
     }
 
-    public String toFormulaString(SheetReferences refs) {
+    public String toFormulaString(Workbook book) {
         StringBuffer retval = new StringBuffer();
+        SheetReferences refs = book == null ? null : book.getSheetReferences();
         if (refs != null) {
             retval.append(refs.getSheetName((int)this.field_1_index_extern_sheet));
             retval.append('!');
@@ -210,6 +212,7 @@
      ptg.field_1_index_extern_sheet = field_1_index_extern_sheet;
      ptg.field_2_row = field_2_row;
      ptg.field_3_column = field_3_column;
+     ptg.setClass(ptgClass);
      return ptg;
    }
 
diff --git a/src/java/org/apache/poi/hssf/record/formula/ReferencePtg.java b/src/java/org/apache/poi/hssf/record/formula/ReferencePtg.java
index 90f7d89..4c14aaf 100644
--- a/src/java/org/apache/poi/hssf/record/formula/ReferencePtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/ReferencePtg.java
@@ -64,7 +64,7 @@
 import org.apache.poi.util.BitField;
 
 import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * ReferencePtg - handles references (such as A1, A2, IA4)
@@ -179,7 +179,7 @@
         return SIZE;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         //TODO -- should we store a cellreference instance in this ptg?? but .. memory is an issue, i believe!
         return (new CellReference(getRow(),getColumn(),!isRowRelative(),!isColRelative())).toString();
@@ -193,6 +193,7 @@
       ReferencePtg ptg = new ReferencePtg();
       ptg.field_1_row = field_1_row;
       ptg.field_2_col = field_2_col;
+      ptg.setClass(ptgClass);
       return ptg;
     }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/StringPtg.java b/src/java/org/apache/poi/hssf/record/formula/StringPtg.java
index 022fffd..1ad2789 100644
--- a/src/java/org/apache/poi/hssf/record/formula/StringPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/StringPtg.java
@@ -55,8 +55,9 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
-
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.util.BitField;
+import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.util.StringUtil;
 
 /**
  * Number
@@ -70,7 +71,12 @@
 {
     public final static int  SIZE = 9;
     public final static byte sid  = 0x17;
-    private String            field_1_value;
+    //NOTE: OO doc says 16bit lenght, but BiffViewer says 8
+    // Book says something totally different, so dont look there!
+    byte field_1_length;
+    byte field_2_options;
+    BitField fHighByte = new BitField(0x01);
+    private String            field_3_string;
 
     private StringPtg() {
       //Required for clone methods
@@ -79,7 +85,16 @@
     /** Create a StringPtg from a byte array read from disk */
     public StringPtg(byte [] data, int offset)
     {
-        setValue(new String(data, offset+3, data[offset+1] + 256*data[offset+2]));
+        offset++;
+        field_1_length = data[offset];
+        field_2_options = data[offset+1];
+        if (fHighByte.isSet(field_2_options)) {
+            field_3_string= StringUtil.getFromUnicode(data,offset+2,field_1_length);
+        }else {
+            field_3_string=StringUtil.getFromCompressedUnicode(data,offset+2,field_1_length);
+        }
+				 
+        //setValue(new String(data, offset+3, data[offset+1] + 256*data[offset+2]));
     }
 
     /** Create a StringPtg from a string representation of  the number
@@ -88,35 +103,49 @@
      *  @param value : String representation of a floating point number
      */
     public StringPtg(String value) {
-        setValue(value);
+        if (value.length() >255) {
+            throw new IllegalArgumentException("String literals in formulas cant be bigger than 255 characters ASCII");
+        }
+        this.field_2_options=0;
+        this.fHighByte.setBoolean(field_2_options, false);
+        this.field_3_string=value;
+        this.field_1_length=(byte)value.length(); //for the moment, we support only ASCII strings in formulas we create
     }
 
-
+    /*
     public void setValue(String value)
     {
         field_1_value = value;
-    }
+    }*/
 
 
     public String getValue()
     {
-        return field_1_value;
+        return field_3_string;
     }
 
     public void writeBytes(byte [] array, int offset)
     {
         array[ offset + 0 ] = sid;
-        array[ offset + 1 ] = (byte)(getValue().length() % 256);
-        array[ offset + 2 ] = (byte)(getValue().length() / 256);
-        System.arraycopy(getValue().getBytes(), 0, array, offset + 3, getValue().length());
+        array[ offset + 1 ] = field_1_length;
+        array[ offset + 2 ] = field_2_options;
+        if (fHighByte.isSet(field_2_options)) {
+            StringUtil.putUncompressedUnicode(getValue(),array,offset+3);
+        }else {
+            StringUtil.putCompressedUnicode(getValue(),array,offset+3);
+        }
     }
 
     public int getSize()
     {
-        return field_1_value.length() + 3;
+        if (fHighByte.isSet(field_2_options)) {
+            return 2*field_1_length+3;
+        }else {
+            return field_1_length+3;
+        }
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "\""+getValue()+"\"";
     }
@@ -126,9 +155,10 @@
 
    public Object clone() {
      StringPtg ptg = new StringPtg();
-     ptg.field_1_value = field_1_value;
+     ptg.field_1_length = field_1_length;
+     ptg.field_2_options=field_2_options;
+     ptg.field_3_string=field_3_string;
      return ptg;
    }
 
 }
-
diff --git a/src/java/org/apache/poi/hssf/record/formula/SubtractPtg.java b/src/java/org/apache/poi/hssf/record/formula/SubtractPtg.java
index 2c5588f..cd9fa5f 100644
--- a/src/java/org/apache/poi/hssf/record/formula/SubtractPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/SubtractPtg.java
@@ -61,7 +61,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import java.util.List;
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -105,7 +105,7 @@
         return 2;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "-";
     }
diff --git a/src/java/org/apache/poi/hssf/record/formula/UnaryMinusPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnaryMinusPtg.java
new file mode 100644
index 0000000..bb60dd0
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/formula/UnaryMinusPtg.java
@@ -0,0 +1,127 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record.formula;
+
+import java.util.List;
+
+import org.apache.poi.hssf.model.Workbook;
+
+/**
+ * Unary Plus operator
+ * does not have any effect on the operand
+ * @author Avik Sengupta
+ */
+
+public class UnaryMinusPtg extends OperationPtg
+{
+    public final static int  SIZE = 1;
+    public final static byte sid  = 0x13;
+    
+    private final static String MINUS = "-";
+
+    /** Creates new AddPtg */
+
+    public UnaryMinusPtg()
+    {
+    }
+
+    public UnaryMinusPtg(byte[] data, int offset)
+    {
+
+        // doesn't need anything
+    }
+    
+   
+    public void writeBytes(byte [] array, int offset)
+    {
+        array[ offset + 0 ] = sid;
+    }
+
+    public int getSize()
+    {
+        return SIZE;
+    }
+
+    public int getType()
+    {
+        return this.TYPE_UNARY;
+    }
+
+    public int getNumberOfOperands()
+    {
+        return 1;
+    }
+    
+    /** Implementation of method from Ptg */
+    public String toFormulaString(Workbook book)
+    {
+        return "+";
+    }
+       
+   /** implementation of method from OperationsPtg*/  
+    public String toFormulaString(String[] operands) {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(MINUS);
+        buffer.append(operands[ 0]);
+        return buffer.toString();
+    }
+    
+    public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
+           
+    public Object clone() {
+      return new UnaryPlusPtg();
+    }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/UnaryPlusPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnaryPlusPtg.java
new file mode 100644
index 0000000..c7ec1e0
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/formula/UnaryPlusPtg.java
@@ -0,0 +1,127 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record.formula;
+
+import java.util.List;
+
+import org.apache.poi.hssf.model.Workbook;
+
+/**
+ * Unary Plus operator
+ * does not have any effect on the operand
+ * @author Avik Sengupta
+ */
+
+public class UnaryPlusPtg extends OperationPtg
+{
+    public final static int  SIZE = 1;
+    public final static byte sid  = 0x12;
+    
+    private final static String ADD = "+";
+
+    /** Creates new AddPtg */
+
+    public UnaryPlusPtg()
+    {
+    }
+
+    public UnaryPlusPtg(byte[] data, int offset)
+    {
+
+        // doesn't need anything
+    }
+    
+   
+    public void writeBytes(byte [] array, int offset)
+    {
+        array[ offset + 0 ] = sid;
+    }
+
+    public int getSize()
+    {
+        return SIZE;
+    }
+
+    public int getType()
+    {
+        return this.TYPE_UNARY;
+    }
+
+    public int getNumberOfOperands()
+    {
+        return 1;
+    }
+    
+    /** Implementation of method from Ptg */
+    public String toFormulaString(Workbook book)
+    {
+        return "+";
+    }
+       
+   /** implementation of method from OperationsPtg*/  
+    public String toFormulaString(String[] operands) {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(ADD);
+        buffer.append(operands[ 0]);
+        return buffer.toString();
+    }
+    
+    public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
+           
+    public Object clone() {
+      return new UnaryPlusPtg();
+    }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java
index f842350..dea5997 100644
--- a/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java
@@ -54,7 +54,7 @@
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  * @author Glen Stampoultzis (glens at apache.org)
@@ -95,7 +95,7 @@
     }
 
     /** Implementation of method from Ptg */
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return ",";
     }
diff --git a/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java
index 2dd0649..56d03f6 100644
--- a/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java
@@ -60,7 +60,7 @@
  */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.hssf.model.Workbook;
 
 /**
  *
@@ -94,7 +94,7 @@
         return size;
     }
 
-    public String toFormulaString(SheetReferences refs)
+    public String toFormulaString(Workbook book)
     {
         return "UNKNOWN";
     }
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
index 5986b84..7ebd7e3 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
@@ -326,6 +326,7 @@
 
             case CELL_TYPE_FORMULA :
                 cellValue = (( FormulaRecordAggregate ) cval).getFormulaRecord().getValue();
+                stringValue=((FormulaRecordAggregate) cval).getStringValue();
                 break;
 
             case CELL_TYPE_BOOLEAN :
@@ -726,8 +727,7 @@
 
     public String getCellFormula() {
         //Workbook.currentBook=book;
-        SheetReferences refs = book.getSheetReferences();
-        String retval = FormulaParser.toFormulaString(refs, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
+        String retval = FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
         //Workbook.currentBook=null;
         return retval;
     }
@@ -798,6 +798,7 @@
     /**
      * get the value of the cell as a string - for numeric cells we throw an exception.
      * For blank cells we return an empty string.
+     * For formulaCells that are not string Formulas, we return empty String
      */
 
     public String getStringCellValue()
@@ -821,6 +822,10 @@
             throw new NumberFormatException(
                 "You cannot get a string value from an error cell");
         }
+        if (cellType == CELL_TYPE_FORMULA) 
+        {
+            if (stringValue==null) return "";
+        }
         return stringValue;
     }
 
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
index bbde46a..61b8c60 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
@@ -85,7 +85,7 @@
      * Normal boldness (not bold)
      */
 
-    public final static short  BOLDWEIGHT_NORMAL   = 190;
+    public final static short  BOLDWEIGHT_NORMAL   = 0x190;
 
     /**
      * Bold boldness (bold)
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java
index 0f71782..8c18634 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java
@@ -123,8 +123,7 @@
 
     public String getReference() {
         String result;
-        SheetReferences refs = book.getSheetReferences();
-        result = name.getAreaReference(refs);
+        result = name.getAreaReference(book);
 
         return result;
     }
@@ -167,4 +166,3 @@
     }
 
 }
-
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
index 6c5d0ee..863533d 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
@@ -103,7 +103,7 @@
         for (short i = (short) PaletteRecord.FIRST_COLOR_INDEX; b != null;
             b = palette.getColor(++i))
         {
-            if (b[0] == red && b[1] == blue && b[2] == green)
+            if (b[0] == red && b[1] == green && b[2] == blue)
             {
                 return new CustomColor(i, b);
             }
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index a1a6e79..9fe6917 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -59,17 +59,25 @@
  */
 package org.apache.poi.hssf.usermodel;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+
 import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.model.Workbook;
-import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
+import org.apache.poi.hssf.record.HCenterRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RowRecord;
+import org.apache.poi.hssf.record.SCLRecord;
+import org.apache.poi.hssf.record.VCenterRecord;
+import org.apache.poi.hssf.record.WSBoolRecord;
+import org.apache.poi.hssf.record.WindowTwoRecord;
 import org.apache.poi.hssf.util.Region;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
-import java.util.Iterator;
-import java.util.TreeMap;
-import java.util.List;
-
 /**
  * High level representation of a worksheet.
  * @author  Andrew C. Oliver (acoliver at apache dot org)
@@ -865,6 +873,22 @@
     {
         getSheet().setMargin( margin, size );
     }
+    
+	/**
+	 * Answer whether protection is enabled or disabled
+	 * @return true => protection enabled; false => protection disabled
+	 */
+	public boolean getProtect() {
+		return getSheet().getProtect().getProtect();		
+	}
+
+	/**
+	 * Sets the protection on enabled or disabled
+	 * @param protect true => protection enabled; false => protection disabled
+	 */
+	public void setProtect(boolean protect) {
+		getSheet().getProtect().setProtect(protect);		
+	}
 
     /**
      * Sets the zoom magnication for the sheet.  The zoom is expressed as a
@@ -887,6 +911,50 @@
         getSheet().setSCLRecord(sclRecord);
     }
 
+	/**
+	 * Shifts the merged regions left or right depending on mode
+	 * <p>
+	 * TODO: MODE , this is only row specific
+	 * @param startRow
+	 * @param endRow
+	 * @param n
+	 * @param isRow
+	 */
+	protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) {
+		List shiftedRegions = new ArrayList();
+		//move merged regions completely if they fall within the new region boundaries when they are shifted
+		for (int i = 0; i < this.getNumMergedRegions(); i++) {
+			 Region merged = this.getMergedRegionAt(i);
+        		
+			 boolean inStart = (merged.getRowFrom() >= startRow || merged.getRowTo() >= startRow);
+			 boolean inEnd =  (merged.getRowTo() <= endRow || merged.getRowFrom() <= endRow);
+        		
+			 //dont check if it's not within the shifted area
+			 if (! (inStart && inEnd)) continue;
+        		
+			 //only shift if the region outside the shifted rows is not merged too        	           		
+			 if (!merged.contains(startRow-1, (short)0) && !merged.contains(endRow+1, (short)0)){
+				 merged.setRowFrom(merged.getRowFrom()+n);					
+				 merged.setRowTo(merged.getRowTo()+n);
+				 //have to remove/add it back
+				 shiftedRegions.add(merged);
+				 this.removeMergedRegion(i);
+				 i = i -1; // we have to back up now since we removed one
+					
+			 }
+        		
+		}
+		
+		//readd so it doesn't get shifted again
+		Iterator iterator = shiftedRegions.iterator();
+		while (iterator.hasNext()) {
+			Region region = (Region)iterator.next();
+			
+			this.addMergedRegion(region);
+		}
+		
+	}
+
     /**
      * Shifts rows between startRow and endRow n number of rows.
      * If you use a negative number, it will shift rows up.
@@ -894,19 +962,25 @@
      *
      * Calls shiftRows(startRow, endRow, n, false, false);
      *
+     * <p>
+     * Additionally shifts merged regions that are completely defined in these
+     * rows (ie. merged 2 cells on a row to be shifted).
      * @param startRow the row to start shifting
      * @param endRow the row to end shifting
      * @param n the number of rows to shift
      */
     public void shiftRows( int startRow, int endRow, int n ) {
-	shiftRows(startRow, endRow, n, false, false);
+		shiftRows(startRow, endRow, n, false, false);
     }
 
     /**
      * Shifts rows between startRow and endRow n number of rows.
      * If you use a negative number, it will shift rows up.
      * Code ensures that rows don't wrap around
-     *
+     * 
+     * <p>
+     * Additionally shifts merged regions that are completely defined in these
+     * rows (ie. merged 2 cells on a row to be shifted).
      * @param startRow the row to start shifting
      * @param endRow the row to end shifting
      * @param n the number of rows to shift
@@ -928,6 +1002,9 @@
             e = startRow;
             inc = -1;
         }
+
+			shiftMerged(startRow, endRow, n, true);        
+        
         for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc )
         {
             HSSFRow row = getRow( rowNum );
@@ -937,6 +1014,9 @@
 	    
             HSSFCell cell;
 
+			
+
+
 	    // Removes the cells before over writting them.
             for ( short col = row2Replace.getFirstCellNum(); col <= row2Replace.getLastCellNum(); col++ )
             {
@@ -1021,5 +1101,51 @@
         getSheet().createSplitPane( xSplitPos, ySplitPos, topRow, leftmostColumn, activePane );
     }
 
+    /**
+     * Sets whether the gridlines are shown in a viewer.
+     * @param show whether to show gridlines or not
+     */
+    public void setDisplayGridlines(boolean show) {
+        sheet.setDisplayGridlines(show);
+    }
 
+    /**
+     * Returns if gridlines are displayed.
+     * @return whether gridlines are displayed
+     */
+    public boolean isDisplayGridlines() {
+	return sheet.isDisplayGridlines();
+    }
+
+    /**
+     * Sets whether the formulas are shown in a viewer.
+     * @param show whether to show formulas or not
+     */
+    public void setDisplayFormulas(boolean show) {
+        sheet.setDisplayFormulas(show);
+    }
+
+    /**
+     * Returns if formulas are displayed.
+     * @return whether formulas are displayed
+     */
+    public boolean isDisplayFormulas() {
+	return sheet.isDisplayFormulas();
+    }
+
+    /**
+     * Sets whether the RowColHeadings are shown in a viewer.
+     * @param show whether to show RowColHeadings or not
+     */
+    public void setDisplayRowColHeadings(boolean show) {
+        sheet.setDisplayRowColHeadings(show);
+    }
+
+    /**
+     * Returns if RowColHeadings are displayed.
+     * @return whether RowColHeadings are displayed
+     */
+    public boolean isDisplayRowColHeadings() {
+	return sheet.isDisplayRowColHeadings();
+    }
 }
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 8420aa6..c1bfbd3 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -147,6 +147,14 @@
      * memory.
      */
     private POIFSFileSystem poifs;
+
+    /**
+     * Used to keep track of the data formatter so that all
+     * createDataFormatter calls return the same one for a given
+     * book.  This ensures that updates from one places is visible
+     * someplace else.
+     */
+    private HSSFDataFormat formatter;
     
     private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class);
 
@@ -273,7 +281,9 @@
     
      
     /**
-     * set the sheet name.
+     * set the sheet name. 
+     * Will throw IllegalArgumentException if the name is greater than 31 chars
+     * or contains /\?*[]
      * @param sheet number (0 based)
      * @param sheet name
      */
@@ -374,7 +384,11 @@
         windowTwo.setPaged(sheets.size() == 1);
 
         sheets.add(clonedSheet);
-        workbook.setSheetName(sheets.size()-1, srcName+"[1]");
+        if (srcName.length()<28) {
+            workbook.setSheetName(sheets.size()-1, srcName+"(2)");
+        }else {
+            workbook.setSheetName(sheets.size()-1,srcName.substring(0,28)+"(2)");
+        }
         return clonedSheet;
       }
       return null;
@@ -890,7 +904,7 @@
 		if (name == null) return null;
 		//adding one here because 0 indicates a global named region; doesnt make sense for print areas
    
-		return name.getAreaReference(workbook.getSheetReferences());
+		return name.getAreaReference(workbook);
 	}    
     
     /**
@@ -945,13 +959,15 @@
     }
 
     /**
-     * Creates an instance of HSSFDataFormat.
+     * Returns the instance of HSSFDataFormat for this workbook.
      * @return the HSSFDataFormat object
      * @see org.apache.poi.hssf.record.FormatRecord
      * @see org.apache.poi.hssf.record.Record
      */
     public HSSFDataFormat createDataFormat() {
-        return new HSSFDataFormat(workbook);
+	if (formatter == null)
+	    formatter = new HSSFDataFormat(workbook);
+	return formatter;
     }
 	
     /** remove the named range by his name
diff --git a/src/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java b/src/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java
index 135a2ba..a85e7e9 100644
--- a/src/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java
+++ b/src/java/org/apache/poi/poifs/filesystem/DirectoryEntry.java
@@ -59,6 +59,8 @@
 
 import java.util.*;
 
+import org.apache.poi.hpsf.ClassID;
+
 /**
  * This interface defines methods specific to Directory objects
  * managed by a Filesystem instance.
@@ -160,5 +162,20 @@
 
     public DirectoryEntry createDirectory(final String name)
         throws IOException;
+
+    /**
+     * Gets the storage clsid of the directory entry
+     *
+     * @return storage Class ID
+     */
+    public ClassID getStorageClsid();
+
+    /**
+     * Sets the storage clsid for the directory entry
+     *
+     * @param clsidStorage storage Class ID
+     */
+    public void setStorageClsid(ClassID clsidStorage);
+
 }   // end public interface DirectoryEntry
 
diff --git a/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java b/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
index d7c670c..feeb747 100644
--- a/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
+++ b/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
@@ -59,6 +59,7 @@
 
 import java.util.*;
 
+import org.apache.poi.hpsf.ClassID;
 import org.apache.poi.poifs.dev.POIFSViewable;
 import org.apache.poi.poifs.property.DirectoryProperty;
 import org.apache.poi.poifs.property.DocumentProperty;
@@ -346,6 +347,26 @@
         return rval;
     }
 
+    /**
+     * Gets the storage clsid of the directory entry
+     *
+     * @return storage Class ID
+     */
+    public ClassID getStorageClsid()
+    {
+        return getProperty().getStorageClsid();
+    }
+
+    /**
+     * Sets the storage clsid for the directory entry
+     *
+     * @param clsidStorage storage Class ID
+     */
+    public void setStorageClsid(ClassID clsidStorage)
+    {
+        getProperty().setStorageClsid(clsidStorage);
+    }
+
     /* **********  END  implementation of DirectoryEntry ********** */
     /* ********** START implementation of Entry ********** */
 
diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java b/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
index 3d173aa..3a62e3e 100644
--- a/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
+++ b/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
@@ -423,6 +423,8 @@
                 DirectoryNode new_dir =
                     ( DirectoryNode ) parent.createDirectory(name);
 
+                new_dir.setStorageClsid( property.getStorageClsid() );
+
                 processProperties(
                     small_blocks, big_blocks,
                     (( DirectoryProperty ) property).getChildren(), new_dir);
diff --git a/src/java/org/apache/poi/poifs/property/Property.java b/src/java/org/apache/poi/poifs/property/Property.java
index 6811a72..c09c256 100644
--- a/src/java/org/apache/poi/poifs/property/Property.java
+++ b/src/java/org/apache/poi/poifs/property/Property.java
@@ -59,6 +59,8 @@
 
 import java.util.*;
 
+import org.apache.poi.hpsf.ClassID;
+
 import org.apache.poi.poifs.common.POIFSConstants;
 import org.apache.poi.poifs.dev.POIFSViewable;
 import org.apache.poi.util.ByteField;
@@ -87,6 +89,8 @@
     static final private int    _previous_property_offset = 0x44;
     static final private int    _next_property_offset     = 0x48;
     static final private int    _child_property_offset    = 0x4C;
+    static final private int    _storage_clsid_offset     = 0x50;
+    static final private int    _user_flags_offset        = 0x60;
     static final private int    _seconds_1_offset         = 0x64;
     static final private int    _days_1_offset            = 0x68;
     static final private int    _seconds_2_offset         = 0x6C;
@@ -107,6 +111,8 @@
     private IntegerField        _previous_property;
     private IntegerField        _next_property;
     private IntegerField        _child_property;
+    private ClassID             _storage_clsid;
+    private IntegerField        _user_flags;
     private IntegerField        _seconds_1;
     private IntegerField        _days_1;
     private IntegerField        _seconds_2;
@@ -136,6 +142,8 @@
                                               _NO_INDEX, _raw_data);
         _child_property    = new IntegerField(_child_property_offset,
                                               _NO_INDEX, _raw_data);
+        _storage_clsid     = new ClassID(_raw_data,_storage_clsid_offset);
+        _user_flags        = new IntegerField(_user_flags_offset, 0, _raw_data);
         _seconds_1         = new IntegerField(_seconds_1_offset, 0,
                                               _raw_data);
         _days_1            = new IntegerField(_days_1_offset, 0, _raw_data);
@@ -173,6 +181,8 @@
                                               _raw_data);
         _child_property    = new IntegerField(_child_property_offset,
                                               _raw_data);
+        _storage_clsid     = new ClassID(_raw_data,_storage_clsid_offset);
+        _user_flags        = new IntegerField(_user_flags_offset, 0, _raw_data);
         _seconds_1         = new IntegerField(_seconds_1_offset, _raw_data);
         _days_1            = new IntegerField(_days_1_offset, _raw_data);
         _seconds_2         = new IntegerField(_seconds_2_offset, _raw_data);
@@ -296,11 +306,20 @@
     abstract public boolean isDirectory();
 
     /**
+     * Sets the storage clsid, which is the Class ID of a COM object which
+     *   reads and writes this stream
+     * @return storage Class ID for this property stream
+     */
+    public ClassID getStorageClsid()
+    {
+        return _storage_clsid;
+    }
+
+    /**
      * Set the name; silently truncates the name if it's too long.
      *
      * @param name the new name
      */
-
     protected final void setName(final String name)
     {
         char[] char_array = name.toCharArray();
@@ -328,6 +347,20 @@
     }
 
     /**
+     * Sets the storage class ID for this property stream. This is the Class ID
+     *   of the COM object which can read and write this property stream
+     * @param clsidStorage Storage Class ID
+     */
+    public void setStorageClsid( ClassID clsidStorage)
+    {
+        _storage_clsid = clsidStorage;
+        if( clsidStorage == null) {
+            Arrays.fill( _raw_data, _storage_clsid_offset, _storage_clsid_offset + ClassID.LENGTH, (byte) 0);
+        } else {
+            clsidStorage.write( _raw_data, _storage_clsid_offset);
+        }
+    }
+    /**
      * Set the property type. Makes no attempt to validate the value.
      *
      * @param propertyType the property type (root, file, directory)
diff --git a/src/java/org/apache/poi/poifs/storage/DocumentBlock.java b/src/java/org/apache/poi/poifs/storage/DocumentBlock.java
index f6d0d92..7af3487 100644
--- a/src/java/org/apache/poi/poifs/storage/DocumentBlock.java
+++ b/src/java/org/apache/poi/poifs/storage/DocumentBlock.java
@@ -62,6 +62,7 @@
 import java.util.Arrays;
 
 import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.IntegerField;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
@@ -106,7 +107,7 @@
         throws IOException
     {
         this();
-        int count = stream.read(_data);
+        int count = IOUtils.readFully(stream, _data);
 
         _bytes_read = (count == -1) ? 0
                                     : count;
diff --git a/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java b/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
index 015d455..ad77368 100644
--- a/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
+++ b/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
@@ -60,6 +60,7 @@
 import java.util.*;
 
 import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.IntegerField;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
@@ -104,7 +105,7 @@
         throws IOException
     {
         _data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
-        int byte_count = stream.read(_data);
+        int byte_count = IOUtils.readFully(stream, _data);
 
         if (byte_count != POIFSConstants.BIG_BLOCK_SIZE)
         {
diff --git a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
index 862fd8c..863b610 100644
--- a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
+++ b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
@@ -56,6 +56,7 @@
 package org.apache.poi.poifs.storage;
 
 import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.util.IOUtils;
 
 import java.io.*;
 
@@ -84,7 +85,7 @@
         throws IOException
     {
         _data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
-        int count = stream.read(_data);
+        int count = IOUtils.readFully(stream, _data);
 
         if (count == -1)
         {
diff --git a/src/java/org/apache/poi/util/HexDump.java b/src/java/org/apache/poi/util/HexDump.java
index bc84645..74198bc 100644
--- a/src/java/org/apache/poi/util/HexDump.java
+++ b/src/java/org/apache/poi/util/HexDump.java
@@ -67,6 +67,20 @@
 
 public class HexDump
 {
+    public static final String        EOL         =
+        System.getProperty("line.separator");
+//    private static final StringBuffer _lbuffer    = new StringBuffer(8);
+//    private static final StringBuffer _cbuffer    = new StringBuffer(2);
+    private static final char         _hexcodes[] =
+    {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+        'E', 'F'
+    };
+    private static final int          _shifts[]   =
+    {
+        28, 24, 20, 16, 12, 8, 4, 0
+    };
+
 
     // all static methods, so no need for a public constructor
     private HexDump()
@@ -95,12 +109,14 @@
             throws IOException, ArrayIndexOutOfBoundsException,
                     IllegalArgumentException
     {
-        if ((index < 0) || (index >= data.length))
+        if ((index < 0) || (data.length != 0 && index >= data.length))
         {
             throw new ArrayIndexOutOfBoundsException(
                 "illegal index: " + index + " into array of length "
                 + data.length);
         }
+        if (data.length == 0)
+            return; // nothing more to do.
         if (stream == null)
         {
             throw new IllegalArgumentException("cannot write to nullstream");
@@ -241,39 +257,26 @@
     }
     
 
-    public static final String        EOL         =
-        System.getProperty("line.separator");
-    private static final StringBuffer _lbuffer    = new StringBuffer(8);
-    private static final StringBuffer _cbuffer    = new StringBuffer(2);
-    private static final char         _hexcodes[] =
-    {
-        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
-        'E', 'F'
-    };
-    private static final int          _shifts[]   =
-    {
-        28, 24, 20, 16, 12, 8, 4, 0
-    };
-
     private static String dump(final long value)
     {
-        _lbuffer.setLength(0);
+        StringBuffer buf = new StringBuffer();
+        buf.setLength(0);
         for (int j = 0; j < 8; j++)
         {
-            _lbuffer
-                .append(_hexcodes[ (( int ) (value >> _shifts[ j ])) & 15 ]);
+            buf.append( _hexcodes[ (( int ) (value >> _shifts[ j ])) & 15 ]);
         }
-        return _lbuffer.toString();
+        return buf.toString();
     }
 
     private static String dump(final byte value)
     {
-        _cbuffer.setLength(0);
+        StringBuffer buf = new StringBuffer();
+        buf.setLength(0);
         for (int j = 0; j < 2; j++)
         {
-            _cbuffer.append(_hexcodes[ (value >> _shifts[ j + 6 ]) & 15 ]);
+            buf.append(_hexcodes[ (value >> _shifts[ j + 6 ]) & 15 ]);
         }
-        return _cbuffer.toString();
+        return buf.toString();
     }
 
     /**
@@ -294,6 +297,7 @@
         retVal.append(']');
         return retVal.toString();
     }
+
     /**
      * Converts the parameter to a hex value.
      *
@@ -337,4 +341,41 @@
         }
         return result.toString();
     }
+
+    /**
+     * Dumps <code>bytesToDump</code> bytes to an output stream.
+     *
+     * @param in          The stream to read from
+     * @param out         The output stream
+     * @param start       The index to use as the starting position for the left hand side label
+     * @param bytesToDump The number of bytes to output.  Use -1 to read until the end of file.
+     */
+    public static void dump( InputStream in, PrintStream out, int start, int bytesToDump ) throws IOException
+    {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        if (bytesToDump == -1)
+        {
+            int c = in.read();
+            while (c != -1)
+            {
+                buf.write(c);
+                c = in.read();
+            }
+        }
+        else
+        {
+            int bytesRemaining = bytesToDump;
+            while (bytesRemaining-- > 0)
+            {
+                int c = in.read();
+                if (c == -1)
+                    break;
+                else
+                    buf.write(c);
+            }
+        }
+
+        byte[] data = buf.toByteArray();
+        dump(data, 0, out, start, data.length);
+    }
 }
diff --git a/src/java/org/apache/poi/util/HexRead.java b/src/java/org/apache/poi/util/HexRead.java
index 0be7930..6c5eceb 100644
--- a/src/java/org/apache/poi/util/HexRead.java
+++ b/src/java/org/apache/poi/util/HexRead.java
@@ -1 +1,225 @@
-/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation.  All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in *    the documentation and/or other materials provided with the *    distribution. * * 3. The end-user documentation included with the redistribution, *    if any, must include the following acknowledgment: *       "This product includes software developed by the *        Apache Software Foundation (http://www.apache.org/)." *    Alternately, this acknowledgment may appear in the software itself, *    if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and *    "Apache POI" must not be used to endorse or promote products *    derived from this software without prior written permission. For *    written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", *    "Apache POI", nor may "Apache" appear in their name, without *    prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation.  For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */package org.apache.poi.util;import java.io.IOException;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.util.List;import java.util.ArrayList;/** * Utilities to read hex from files. * * @author Marc Johnson * @author Glen Stampoultzis (glens at apache.org) */public class HexRead{    /**     * This method reads hex data from a filename and returns a byte array.     * The file may contain line comments that are preceeded with a # symbol.     *     * @param filename  The filename to read     * @return The bytes read from the file.     * @throws IOException If there was a problem while reading the file.     */    public static byte[] readData( String filename )            throws IOException    {        File file = new File( filename );        FileInputStream stream = new FileInputStream( file );        try        {            return readData(stream, -1);        }        finally        {            stream.close();        }    }    /**     * Same as readData(String) except that this method allows you to specify sections within     * a file.  Sections are referenced using section headers in the form:     * <pre>     *  [sectioname]     * </pre>     *     * @see #readData(String)     */    public static byte[] readData(String filename, String section)        throws IOException    {        File file = new File( filename );        FileInputStream stream = new FileInputStream( file );        try        {            StringBuffer sectionText = new StringBuffer();            boolean inSection = false;            int c = stream.read();            while (c != -1)            {                switch(c)                {                    case '[':                        inSection = true;                        break;                    case '\n': case '\r':                        inSection = false;                        sectionText = new StringBuffer();                        break;                    case ']':                        inSection = false;                        if (sectionText.toString().equals(section))                            return readData(stream, '[');                        sectionText = new StringBuffer();                        break;                    default:                        if (inSection)                            sectionText.append((char)c);                }                c = stream.read();            }        }        finally        {            stream.close();        }        throw new IOException("Section '" + section + "' not found");    }    static private byte[] readData( FileInputStream stream, int eofChar ) throws IOException    {        int characterCount = 0;        byte b = (byte) 0;        List bytes = new ArrayList();        boolean done = false;        while ( !done )        {            int count = stream.read();            char baseChar = 'a';            if ( count == eofChar)                break;            switch ( count )            {                case '#':                    readToEOL(stream);                    break;                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' );                    characterCount++;                    if ( characterCount == 2 )                    {                        bytes.add( new Byte( b ) );                        characterCount = 0;                        b = (byte) 0;                    }                    break;                case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':                    baseChar = 'A';                case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':                    b <<= 4;                    b += (byte) ( count + 10 - baseChar );                    characterCount++;                    if ( characterCount == 2 )                    {                        bytes.add( new Byte( b ) );                        characterCount = 0;                        b = (byte) 0;                    }                    break;                case -1:                    done = true;                    break;                default :                    break;            }        }        Byte[] polished = (Byte[]) bytes.toArray( new Byte[0] );        byte[] rval = new byte[polished.length];        for ( int j = 0; j < polished.length; j++ )        {            rval[j] = polished[j].byteValue();        }        return rval;    }    static private void readToEOL( InputStream stream ) throws IOException    {        int c = stream.read();        while ( c != -1 && c != '\n' && c != '\r')        {            c = stream.read();        }    }}
\ No newline at end of file
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.util;
+
+import java.io.*;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Utilities to read hex from files.
+ *
+ * @author Marc Johnson
+ * @author Glen Stampoultzis (glens at apache.org) */
+public class HexRead
+{
+    /**
+     * This method reads hex data from a filename and returns a byte array.
+     * The file may contain line comments that are preceeded with a # symbol.
+     *
+     * @param filename  The filename to read
+     * @return The bytes read from the file.
+     * @throws IOException If there was a problem while reading the file.     */
+    public static byte[] readData( String filename ) throws IOException
+    {
+        File file = new File( filename );
+        FileInputStream stream = new FileInputStream( file );
+        try
+        {
+            return readData( stream, -1 );
+        }
+        finally
+        {
+            stream.close();
+        }
+    }
+
+    /**
+     * Same as readData(String) except that this method allows you to specify sections within
+     * a file.  Sections are referenced using section headers in the form:
+     * <pre>
+     *  [sectioname]
+     * </pre>
+     *
+     * @see #readData(String)
+     */
+    public static byte[] readData( String filename, String section ) throws IOException
+    {
+        File file = new File( filename );
+        FileInputStream stream = new FileInputStream( file );
+        try
+        {
+            StringBuffer sectionText = new StringBuffer();
+            boolean inSection = false;
+            int c = stream.read();
+            while ( c != -1 )
+            {
+                switch ( c )
+                {
+                    case '[':
+                        inSection = true;
+                        break;
+                    case '\n':
+                    case '\r':
+                        inSection = false;
+                        sectionText = new StringBuffer();
+                        break;
+                    case ']':
+                        inSection = false;
+                        if ( sectionText.toString().equals( section ) ) return readData( stream, '[' );
+                        sectionText = new StringBuffer();
+                        break;
+                    default:
+                        if ( inSection ) sectionText.append( (char) c );
+                }
+                c = stream.read();
+            }
+        }
+        finally
+        {
+            stream.close();
+        }
+        throw new IOException( "Section '" + section + "' not found" );
+    }
+
+    static public byte[] readData( InputStream stream, int eofChar ) throws IOException
+    {
+        int characterCount = 0;
+        byte b = (byte) 0;
+        List bytes = new ArrayList();
+        boolean done = false;
+        while ( !done )
+        {
+            int count = stream.read();
+            char baseChar = 'a';
+            if ( count == eofChar ) break;
+            switch ( count )
+            {
+                case '#':
+                    readToEOL( stream );
+                    break;
+                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' );
+                    characterCount++;
+                    if ( characterCount == 2 )
+                    {
+                        bytes.add( new Byte( b ) );
+                        characterCount = 0;
+                        b = (byte) 0;
+                    }
+                    break;
+                case 'A':
+                case 'B':
+                case 'C':
+                case 'D':
+                case 'E':
+                case 'F':
+                    baseChar = 'A';
+                case 'a':
+                case 'b':
+                case 'c':
+                case 'd':
+                case 'e':
+                case 'f':
+                    b <<= 4;
+                    b += (byte) ( count + 10 - baseChar );
+                    characterCount++;
+                    if ( characterCount == 2 )
+                    {
+                        bytes.add( new Byte( b ) );
+                        characterCount = 0;
+                        b = (byte) 0;
+                    }
+                    break;
+                case -1:
+                    done = true;
+                    break;
+                default :
+                    break;
+            }
+        }
+        Byte[] polished = (Byte[]) bytes.toArray( new Byte[0] );
+        byte[] rval = new byte[polished.length];
+        for ( int j = 0; j < polished.length; j++ )
+        {
+            rval[j] = polished[j].byteValue();
+        }
+        return rval;
+    }
+
+    static public byte[] readFromString(String data) throws IOException
+    {
+        return readData(new ByteArrayInputStream( data.getBytes() ), -1);
+    }
+
+    static private void readToEOL( InputStream stream ) throws IOException
+    {
+        int c = stream.read();
+        while ( c != -1 && c != '\n' && c != '\r' )
+        {
+            c = stream.read();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java
new file mode 100644
index 0000000..06089b0
--- /dev/null
+++ b/src/java/org/apache/poi/util/IOUtils.java
@@ -0,0 +1,99 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class IOUtils
+{
+    private IOUtils()
+    {
+    }
+
+    /**
+     * Helper method, just calls <tt>readFully(in, b, 0, b.length)</tt>
+     */
+    public static int readFully(InputStream in, byte[] b)
+    throws IOException
+    {
+        return readFully(in, b, 0, b.length);
+    }
+
+    /**
+     * Same as the normal <tt>in.read(b, off, len)</tt>, but tries to ensure that
+     * the entire len number of bytes is read.
+     * <p>
+     * If the end of file is reached before any bytes are read, returns -1.
+     * Otherwise, returns the number of bytes read.
+     */
+    public static int readFully(InputStream in, byte[] b, int off, int len)
+    throws IOException
+    {
+        int total = 0;
+        for (;;) {
+            int got = in.read(b, off + total, len - total);
+            if (got < 0) {
+                return (total == 0) ? -1 : total;
+            } else {
+                total += got;
+                if (total == len)
+                    return total;
+            }
+        }
+    }
+}
+
diff --git a/src/java/org/apache/poi/util/LittleEndian.java b/src/java/org/apache/poi/util/LittleEndian.java
index 0f525c1..4c4b51a 100644
--- a/src/java/org/apache/poi/util/LittleEndian.java
+++ b/src/java/org/apache/poi/util/LittleEndian.java
@@ -475,23 +475,10 @@
         return getLong(readFromStream(stream, LONG_SIZE));
     }
 
-
-    private final static byte[] _short_buffer = new byte[SHORT_SIZE];
-    private final static byte[] _int_buffer = new byte[INT_SIZE];
-    private final static byte[] _long_buffer = new byte[LONG_SIZE];
-
-
     /**
      *  Read the appropriate number of bytes from the stream and return them to
      *  the caller. <p>
      *
-     *  It should be noted that, in an attempt to improve system performance and
-     *  to prevent a transient explosion of discarded byte arrays to be garbage
-     *  collected, static byte arrays are employed for the standard cases of
-     *  reading a short, an int, or a long. <p>
-     *
-     *  <b>THIS INTRODUCES A RISK FOR THREADED OPERATIONS!</b> <p>
-     *
      *  However, for the purposes of the POI project, this risk is deemed
      *  negligible. It is, however, so noted.
      *
@@ -510,23 +497,8 @@
     public static byte[] readFromStream(final InputStream stream,
             final int size)
              throws IOException, BufferUnderrunException {
-        byte[] buffer = null;
+        byte[] buffer = new byte[size];
 
-        switch (size) {
-
-            case SHORT_SIZE:
-                buffer = _short_buffer;
-                break;
-            case INT_SIZE:
-                buffer = _int_buffer;
-                break;
-            case LONG_SIZE:
-                buffer = _long_buffer;
-                break;
-            default:
-                buffer = new byte[size];
-                break;
-        }
         int count = stream.read(buffer);
 
         if (count == -1) {
diff --git a/src/java/org/apache/poi/util/StringUtil.java b/src/java/org/apache/poi/util/StringUtil.java
index 845854e..a2ef124 100644
--- a/src/java/org/apache/poi/util/StringUtil.java
+++ b/src/java/org/apache/poi/util/StringUtil.java
@@ -1 +1,363 @@
-/* *  ==================================================================== *  The Apache Software License, Version 1.1 * *  Copyright (c) 2003 The Apache Software Foundation.  All rights *  reserved. * *  Redistribution and use in source and binary forms, with or without *  modification, are permitted provided that the following conditions *  are met: * *  1. Redistributions of source code must retain the above copyright *  notice, this list of conditions and the following disclaimer. * *  2. Redistributions in binary form must reproduce the above copyright *  notice, this list of conditions and the following disclaimer in *  the documentation and/or other materials provided with the *  distribution. * *  3. The end-user documentation included with the redistribution, *  if any, must include the following acknowledgment: *  "This product includes software developed by the *  Apache Software Foundation (http://www.apache.org/)." *  Alternately, this acknowledgment may appear in the software itself, *  if and wherever such third-party acknowledgments normally appear. * *  4. The names "Apache" and "Apache Software Foundation" and *  "Apache POI" must not be used to endorse or promote products *  derived from this software without prior written permission. For *  written permission, please contact apache@apache.org. * *  5. Products derived from this software may not be called "Apache", *  "Apache POI", nor may "Apache" appear in their name, without *  prior written permission of the Apache Software Foundation. * *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *  SUCH DAMAGE. *  ==================================================================== * *  This software consists of voluntary contributions made by many *  individuals on behalf of the Apache Software Foundation.  For more *  information on the Apache Software Foundation, please see *  <http://www.apache.org/>. */package org.apache.poi.util;import java.io.UnsupportedEncodingException;import java.text.NumberFormat;import java.text.FieldPosition;/** *  Title: String Utility Description: Collection of string handling utilities *  * Now it is quite confusing: the method pairs, in which * one of them write data and other read written data are: * putUncompressedUnicodeHigh and getFromUnicode * putUncompressedUnicode     and getFromUnicodeHigh * *@author     Andrew C. Oliver *@author     Sergei Kozello (sergeikozello at mail.ru) *@created    May 10, 2002 *@version    1.0 */public class StringUtil {        private final static String ENCODING="ISO-8859-1";    /**     *  Constructor for the StringUtil object     */    private StringUtil() { }        /**     *  given a byte array of 16-bit unicode characters, compress to 8-bit and     *  return a string     *     * { 0x16, 0x00 } -> 0x16     *      *@param  string                              the byte array to be converted     *@param  offset                              the initial offset into the     *      byte array. it is assumed that string[ offset ] and string[ offset +     *      1 ] contain the first 16-bit unicode character     *@param  len     *@return                                     the converted string     *@exception  ArrayIndexOutOfBoundsException  if offset is out of bounds for     *      the byte array (i.e., is negative or is greater than or equal to     *      string.length)     *@exception  IllegalArgumentException        if len is too large (i.e.,     *      there is not enough data in string to create a String of that     *      length)     *@len                                        the length of the final string     */    public static String getFromUnicodeHigh(final byte[] string,            final int offset, final int len)             throws ArrayIndexOutOfBoundsException, IllegalArgumentException {        if ((offset < 0) || (offset >= string.length)) {            throw new ArrayIndexOutOfBoundsException("Illegal offset");        }        if ((len < 0) || (((string.length - offset) / 2) < len)) {            throw new IllegalArgumentException("Illegal length");        }                char[] chars = new char[ len ];        for ( int i = 0; i < chars.length; i++ ) {            chars[i] = (char)( string[ offset + ( 2*i ) ] & 0xFF |                              ( string[ offset + ( 2*i+1 ) ] << 8 ) );        }        return new String( chars );    }            /**     *  given a byte array of 16-bit unicode characters, compress to 8-bit and     *  return a string     *      * { 0x16, 0x00 } -> 0x16     *     *@param  string  the byte array to be converted     *@return         the converted string     */    public static String getFromUnicodeHigh( final byte[] string ) {        return getFromUnicodeHigh( string, 0, string.length / 2 );    }    /**     *  given a byte array of 16-bit unicode characters, compress to 8-bit and     *  return a string     *      * { 0x00, 0x16 } -> 0x16     *     *@param  string                              the byte array to be converted     *@param  offset                              the initial offset into the     *      byte array. it is assumed that string[ offset ] and string[ offset +     *      1 ] contain the first 16-bit unicode character     *@param  len     *@return                                     the converted string     *@exception  ArrayIndexOutOfBoundsException  if offset is out of bounds for     *      the byte array (i.e., is negative or is greater than or equal to     *      string.length)     *@exception  IllegalArgumentException        if len is too large (i.e.,     *      there is not enough data in string to create a String of that     *      length)     *@len                                        the length of the final string     */    public static String getFromUnicode(final byte[] string,            final int offset, final int len)             throws ArrayIndexOutOfBoundsException, IllegalArgumentException {        if ((offset < 0) || (offset >= string.length)) {            throw new ArrayIndexOutOfBoundsException("Illegal offset");        }        if ((len < 0) || (((string.length - offset) / 2) < len)) {            throw new IllegalArgumentException("Illegal length");        }                char[] chars = new char[ len ];        for ( int i = 0; i < chars.length; i++ ) {            chars[i] = (char)( ( string[ offset + ( 2*i ) ] << 8 ) +                              string[ offset + ( 2*i+1 ) ] );        }                return new String( chars );    }    /**     *  given a byte array of 16-bit unicode characters, compress to 8-bit and     *  return a string     *      * { 0x00, 0x16 } -> 0x16     *     *@param  string  the byte array to be converted     *@return         the converted string     */    public static String getFromUnicode(final byte[] string) {        return getFromUnicode(string, 0, string.length / 2);    }      /**      * read compressed unicode(8bit)      *       * @author Toshiaki Kamoshida(kamoshida.toshiaki at future dot co dot jp)      *       * @param string byte array to read      * @param offset offset to read byte array      * @param len    length to read byte array      * @return String generated String instance by reading byte array      */     public static String getFromCompressedUnicode(final byte[] string,            final int offset, final int len){         try{             return new String(string,offset,len,"ISO-8859-1");         }         catch(UnsupportedEncodingException e){             throw new InternalError();/* unreachable */         }     }    /**     *  write compressed unicode     *     *@param  input   the String containing the data to be written     *@param  output  the byte array to which the data is to be written     *@param  offset  an offset into the byte arrat at which the data is start     *      when written     */    public static void putCompressedUnicode(final String input,            final byte[] output,            final int offset) {        int strlen = input.length();        for (int k = 0; k < strlen; k++) {            output[offset + k] = (byte) input.charAt(k);        }    }    /**     *  Write uncompressed unicode     *     *@param  input   the String containing the unicode data to be written     *@param  output  the byte array to hold the uncompressed unicode     *@param  offset  the offset to start writing into the byte array     */    public static void putUncompressedUnicode(final String input,            final byte[] output,            final int offset) {        int strlen = input.length();        for (int k = 0; k < strlen; k++) {            char c = input.charAt(k);            output[offset + (2 * k)] = (byte) c;            output[offset + (2 * k) + 1] = (byte) (c >> 8);        }    }    /**     *  Write uncompressed unicode     *     *@param  input   the String containing the unicode data to be written     *@param  output  the byte array to hold the uncompressed unicode     *@param  offset  the offset to start writing into the byte array     */    public static void putUncompressedUnicodeHigh(final String input,            final byte[] output,            final int offset) {        int strlen = input.length();        for (int k = 0; k < strlen; k++) {            char c = input.charAt(k);            output[offset + (2 * k)] = (byte) (c >> 8);            output[offset + (2 * k)] = (byte) c;        }    }                /**     *  Description of the Method     *     *@param  message  Description of the Parameter     *@param  params   Description of the Parameter     *@return          Description of the Return Value     */    public static String format(String message, Object[] params) {        int currentParamNumber = 0;        StringBuffer formattedMessage = new StringBuffer();        for (int i = 0; i < message.length(); i++) {            if (message.charAt(i) == '%') {                if (currentParamNumber >= params.length) {                    formattedMessage.append("?missing data?");                } else if ((params[currentParamNumber] instanceof Number)                        && (i + 1 < message.length())) {                    i += matchOptionalFormatting(                            (Number) params[currentParamNumber++],                            message.substring(i + 1), formattedMessage);                } else {                    formattedMessage.append(params[currentParamNumber++].toString());                }            } else {                if ((message.charAt(i) == '\\') && (i + 1 < message.length())                        && (message.charAt(i + 1) == '%')) {                    formattedMessage.append('%');                    i++;                } else {                    formattedMessage.append(message.charAt(i));                }            }        }        return formattedMessage.toString();    }    /**     *  Description of the Method     *     *@param  number      Description of the Parameter     *@param  formatting  Description of the Parameter     *@param  outputTo    Description of the Parameter     *@return             Description of the Return Value     */    private static int matchOptionalFormatting(Number number,            String formatting,            StringBuffer outputTo) {        NumberFormat numberFormat = NumberFormat.getInstance();        if ((0 < formatting.length())                && Character.isDigit(formatting.charAt(0))) {            numberFormat.setMinimumIntegerDigits(Integer.parseInt(formatting.charAt(0) + ""));            if ((2 < formatting.length()) && (formatting.charAt(1) == '.')                    && Character.isDigit(formatting.charAt(2))) {                numberFormat.setMaximumFractionDigits(Integer.parseInt(formatting.charAt(2) + ""));                numberFormat.format(number, outputTo, new FieldPosition(0));                return 3;            }            numberFormat.format(number, outputTo, new FieldPosition(0));            return 1;        } else if ((0 < formatting.length()) && (formatting.charAt(0) == '.')) {            if ((1 < formatting.length())                    && Character.isDigit(formatting.charAt(1))) {                numberFormat.setMaximumFractionDigits(Integer.parseInt(formatting.charAt(1) + ""));                numberFormat.format(number, outputTo, new FieldPosition(0));                return 2;            }        }        numberFormat.format(number, outputTo, new FieldPosition(0));        return 1;    }        /**     * @return the encoding we want to use (ISO-8859-1)     */    public static String getPreferredEncoding() {        return ENCODING;       }}
\ No newline at end of file
+/*
+ *  ====================================================================
+ *  The Apache Software License, Version 1.1
+ *
+ *  Copyright (c) 2002 The Apache Software Foundation.  All rights
+ *  reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ *  3. The end-user documentation included with the redistribution,
+ *  if any, must include the following acknowledgment:
+ *  "This product includes software developed by the
+ *  Apache Software Foundation (http://www.apache.org/)."
+ *  Alternately, this acknowledgment may appear in the software itself,
+ *  if and wherever such third-party acknowledgments normally appear.
+ *
+ *  4. The names "Apache" and "Apache Software Foundation" and
+ *  "Apache POI" must not be used to endorse or promote products
+ *  derived from this software without prior written permission. For
+ *  written permission, please contact apache@apache.org.
+ *
+ *  5. Products derived from this software may not be called "Apache",
+ *  "Apache POI", nor may "Apache" appear in their name, without
+ *  prior written permission of the Apache Software Foundation.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ *  SUCH DAMAGE.
+ *  ====================================================================
+ *
+ *  This software consists of voluntary contributions made by many
+ *  individuals on behalf of the Apache Software Foundation.  For more
+ *  information on the Apache Software Foundation, please see
+ *  <http://www.apache.org/>.
+ */
+package org.apache.poi.util;
+
+import java.io.UnsupportedEncodingException;
+
+import java.text.NumberFormat;
+import java.text.FieldPosition;
+
+/**
+ *  Title: String Utility Description: Collection of string handling utilities
+ * 
+ * Now it is quite confusing: the method pairs, in which
+ * one of them write data and other read written data are:
+ * putUncompressedUnicodeHigh and getFromUnicode
+ * putUncompressedUnicode     and getFromUnicodeHigh
+ *
+ *@author     Andrew C. Oliver
+ *@author     Sergei Kozello (sergeikozello at mail.ru)
+ *@created    May 10, 2002
+ *@version    1.0
+ */
+
+public class StringUtil {
+    
+    private final static String ENCODING="ISO-8859-1";
+    /**
+     *  Constructor for the StringUtil object
+     */
+    private StringUtil() { }
+
+    
+    /**
+     *  given a byte array of 16-bit unicode characters, compress to 8-bit and
+     *  return a string
+     *
+     * { 0x16, 0x00 } -> 0x16
+     * 
+     *@param  string                              the byte array to be converted
+     *@param  offset                              the initial offset into the
+     *      byte array. it is assumed that string[ offset ] and string[ offset +
+     *      1 ] contain the first 16-bit unicode character
+     *@param  len
+     *@return                                     the converted string
+     *@exception  ArrayIndexOutOfBoundsException  if offset is out of bounds for
+     *      the byte array (i.e., is negative or is greater than or equal to
+     *      string.length)
+     *@exception  IllegalArgumentException        if len is too large (i.e.,
+     *      there is not enough data in string to create a String of that
+     *      length)
+     *@len                                        the length of the final string
+     */
+
+    public static String getFromUnicodeHigh(final byte[] string,
+            final int offset, final int len)
+             throws ArrayIndexOutOfBoundsException, IllegalArgumentException {
+
+        if ((offset < 0) || (offset >= string.length)) {
+            throw new ArrayIndexOutOfBoundsException("Illegal offset");
+        }
+        if ((len < 0) || (((string.length - offset) / 2) < len)) {
+            throw new IllegalArgumentException("Illegal length");
+        }
+        
+        char[] chars = new char[ len ];
+        for ( int i = 0; i < chars.length; i++ ) {
+            chars[i] = (char)( string[ offset + ( 2*i ) ] & 0xFF | 
+                             ( string[ offset + ( 2*i+1 ) ] << 8 ) );
+        }
+
+        return new String( chars );
+    }
+    
+    
+    /**
+     *  given a byte array of 16-bit unicode characters, compress to 8-bit and
+     *  return a string
+     * 
+     * { 0x16, 0x00 } -> 0x16
+     *
+     *@param  string  the byte array to be converted
+     *@return         the converted string
+     */
+
+    public static String getFromUnicodeHigh( final byte[] string ) {
+        return getFromUnicodeHigh( string, 0, string.length / 2 );
+    }
+
+
+    /**
+     *  given a byte array of 16-bit unicode characters, compress to 8-bit and
+     *  return a string
+     * 
+     * { 0x00, 0x16 } -> 0x16
+     *
+     *@param  string                              the byte array to be converted
+     *@param  offset                              the initial offset into the
+     *      byte array. it is assumed that string[ offset ] and string[ offset +
+     *      1 ] contain the first 16-bit unicode character
+     *@param  len
+     *@return                                     the converted string
+     *@exception  ArrayIndexOutOfBoundsException  if offset is out of bounds for
+     *      the byte array (i.e., is negative or is greater than or equal to
+     *      string.length)
+     *@exception  IllegalArgumentException        if len is too large (i.e.,
+     *      there is not enough data in string to create a String of that
+     *      length)
+     *@len                                        the length of the final string
+     */
+
+    public static String getFromUnicode(final byte[] string,
+            final int offset, final int len)
+             throws ArrayIndexOutOfBoundsException, IllegalArgumentException {
+        if ((offset < 0) || (offset >= string.length)) {
+            throw new ArrayIndexOutOfBoundsException("Illegal offset");
+        }
+        if ((len < 0) || (((string.length - offset) / 2) < len)) {
+            throw new IllegalArgumentException("Illegal length");
+        }
+
+        
+        char[] chars = new char[ len ];
+        for ( int i = 0; i < chars.length; i++ ) {
+            chars[i] = (char)( ( string[ offset + ( 2*i ) ] << 8 ) +
+                              string[ offset + ( 2*i+1 ) ] );
+        }
+        
+        return new String( chars );
+    }
+
+
+    /**
+     *  given a byte array of 16-bit unicode characters, compress to 8-bit and
+     *  return a string
+     * 
+     * { 0x00, 0x16 } -> 0x16
+     *
+     *@param  string  the byte array to be converted
+     *@return         the converted string
+     */
+
+    public static String getFromUnicode(final byte[] string) {
+        return getFromUnicode(string, 0, string.length / 2);
+    }
+
+
+      /**
+      * read compressed unicode(8bit)
+      * 
+      * @author Toshiaki Kamoshida(kamoshida.toshiaki at future dot co dot jp)
+      * 
+      * @param string byte array to read
+      * @param offset offset to read byte array
+      * @param len    length to read byte array
+      * @return String generated String instance by reading byte array
+      */
+     public static String getFromCompressedUnicode(final byte[] string,
+            final int offset, final int len){
+         try{
+             return new String(string,offset,len,"ISO-8859-1");
+         }
+         catch(UnsupportedEncodingException e){
+             throw new InternalError();/* unreachable */
+         }
+     }
+
+    /**
+     *  write compressed unicode
+     *
+     *@param  input   the String containing the data to be written
+     *@param  output  the byte array to which the data is to be written
+     *@param  offset  an offset into the byte arrat at which the data is start
+     *      when written
+     */
+
+    public static void putCompressedUnicode(final String input,
+            final byte[] output,
+            final int offset) {
+        int strlen = input.length();
+
+        for (int k = 0; k < strlen; k++) {
+            output[offset + k] = (byte) input.charAt(k);
+        }
+    }
+
+
+    /**
+     *  Write uncompressed unicode
+     *
+     *@param  input   the String containing the unicode data to be written
+     *@param  output  the byte array to hold the uncompressed unicode
+     *@param  offset  the offset to start writing into the byte array
+     */
+
+    public static void putUncompressedUnicode(final String input,
+            final byte[] output,
+            final int offset) {
+        int strlen = input.length();
+
+        for (int k = 0; k < strlen; k++) {
+            char c = input.charAt(k);
+
+            output[offset + (2 * k)] = (byte) c;
+            output[offset + (2 * k) + 1] = (byte) (c >> 8);
+        }
+    }
+
+    /**
+     *  Write uncompressed unicode
+     *
+     *@param  input   the String containing the unicode data to be written
+     *@param  output  the byte array to hold the uncompressed unicode
+     *@param  offset  the offset to start writing into the byte array
+     */
+
+    public static void putUncompressedUnicodeHigh(final String input,
+            final byte[] output,
+            final int offset) {
+        int strlen = input.length();
+
+        for (int k = 0; k < strlen; k++) {
+            char c = input.charAt(k);
+
+            output[offset + (2 * k)] = (byte) (c >> 8);
+            output[offset + (2 * k)] = (byte) c;
+        }
+    }
+    
+    
+    
+
+    /**
+     *  Description of the Method
+     *
+     *@param  message  Description of the Parameter
+     *@param  params   Description of the Parameter
+     *@return          Description of the Return Value
+     */
+    public static String format(String message, Object[] params) {
+        int currentParamNumber = 0;
+        StringBuffer formattedMessage = new StringBuffer();
+
+        for (int i = 0; i < message.length(); i++) {
+            if (message.charAt(i) == '%') {
+                if (currentParamNumber >= params.length) {
+                    formattedMessage.append("?missing data?");
+                } else if ((params[currentParamNumber] instanceof Number)
+                        && (i + 1 < message.length())) {
+                    i += matchOptionalFormatting(
+                            (Number) params[currentParamNumber++],
+                            message.substring(i + 1), formattedMessage);
+                } else {
+                    formattedMessage.append(params[currentParamNumber++].toString());
+                }
+            } else {
+                if ((message.charAt(i) == '\\') && (i + 1 < message.length())
+                        && (message.charAt(i + 1) == '%')) {
+                    formattedMessage.append('%');
+                    i++;
+                } else {
+                    formattedMessage.append(message.charAt(i));
+                }
+            }
+        }
+        return formattedMessage.toString();
+    }
+
+
+    /**
+     *  Description of the Method
+     *
+     *@param  number      Description of the Parameter
+     *@param  formatting  Description of the Parameter
+     *@param  outputTo    Description of the Parameter
+     *@return             Description of the Return Value
+     */
+    private static int matchOptionalFormatting(Number number,
+            String formatting,
+            StringBuffer outputTo) {
+        NumberFormat numberFormat = NumberFormat.getInstance();
+
+        if ((0 < formatting.length())
+                && Character.isDigit(formatting.charAt(0))) {
+            numberFormat.setMinimumIntegerDigits(Integer.parseInt(formatting.charAt(0) + ""));
+            if ((2 < formatting.length()) && (formatting.charAt(1) == '.')
+                    && Character.isDigit(formatting.charAt(2))) {
+                numberFormat.setMaximumFractionDigits(Integer.parseInt(formatting.charAt(2) + ""));
+                numberFormat.format(number, outputTo, new FieldPosition(0));
+                return 3;
+            }
+            numberFormat.format(number, outputTo, new FieldPosition(0));
+            return 1;
+        } else if ((0 < formatting.length()) && (formatting.charAt(0) == '.')) {
+            if ((1 < formatting.length())
+                    && Character.isDigit(formatting.charAt(1))) {
+                numberFormat.setMaximumFractionDigits(Integer.parseInt(formatting.charAt(1) + ""));
+                numberFormat.format(number, outputTo, new FieldPosition(0));
+                return 2;
+            }
+        }
+        numberFormat.format(number, outputTo, new FieldPosition(0));
+        return 1;
+    }
+    
+    /**
+     * @return the encoding we want to use (ISO-8859-1)
+     */
+    public static String getPreferredEncoding() {
+        return ENCODING;   
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/util/SystemOutLogger.java b/src/java/org/apache/poi/util/SystemOutLogger.java
index ccd9522..98458f7 100644
--- a/src/java/org/apache/poi/util/SystemOutLogger.java
+++ b/src/java/org/apache/poi/util/SystemOutLogger.java
@@ -63,13 +63,12 @@
  * A logger class that strives to make it as easy as possible for
  * developers to write log calls, while simultaneously making those
  * calls as cheap as possible by performing lazy evaluation of the log
- * message.<p>
+ * message.
  *
  * @author Marc Johnson (mjohnson at apache dot org)
  * @author Glen Stampoultzis (glens at apache.org)
  * @author Nicola Ken Barozzi (nicolaken at apache.org)
  */
-
 public class SystemOutLogger extends POILogger
 {
     private String cat;
@@ -88,19 +87,27 @@
 
     public void log(final int level, final Object obj1)
     {
-        System.out.println("["+cat+"] "+obj1);
+        if (check(level))
+            System.out.println("["+cat+"] "+obj1);
     }
 
     /**
      * Check if a logger is enabled to log at the specified level
      *
      * @param level One of DEBUG, INFO, WARN, ERROR, FATAL
-     * @param obj1 The logger to check.
+     * @see #DEBUG
+     * @see #INFO
+     * @see #WARN
+     * @see #ERROR
+     * @see #FATAL
      */
-
     public boolean check(final int level)
     {
-       return true;
+        int currentLevel = Integer.parseInt(System.getProperty("poi.log.level", WARN + ""));
+        if (level >= currentLevel)
+            return true;
+        else
+            return false;
     }
 
  
diff --git a/src/testcases/org/apache/poi/hpsf/basic/POIFile.java b/src/testcases/org/apache/poi/hpsf/basic/POIFile.java
index 9786f35..288242f 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/POIFile.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/POIFile.java
@@ -53,7 +53,8 @@
  */
 
 package org.apache.poi.hpsf.basic;
-import org.apache.poi.poifs.filesystem.*;
+
+import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
 
 
 
@@ -73,32 +74,32 @@
 
     public void setName(final String name)
     {
-	this.name = name;
+        this.name = name;
     }
 
     public String getName()
     {
-	return name;
+        return name;
     }
 
     public void setPath(final POIFSDocumentPath path)
     {
-	this.path = path;
+        this.path = path;
     }
 
     public POIFSDocumentPath getPath()
     {
-	return path;
+        return path;
     }
 
     public void setBytes(final byte[] bytes)
     {
-	this.bytes = bytes;
+        this.bytes = bytes;
     }
 
     public byte[] getBytes()
     {
-	return bytes;
+        return bytes;
     }
 
 }
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
index 474489b..0357c5a 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java
@@ -54,10 +54,23 @@
 
 package org.apache.poi.hpsf.basic;
 
-import java.io.*;
-import java.util.*;
-import junit.framework.*;
-import org.apache.poi.hpsf.*;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.HPSFException;
+import org.apache.poi.hpsf.MarkUnsupportedException;
+import org.apache.poi.hpsf.NoPropertySetStreamException;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
 
 
 
@@ -71,37 +84,37 @@
 public class TestBasic extends TestCase
 {
 
-    final static String POI_FS = "TestGermanWord90.doc";
-    final static String[] POI_FILES = new String[]
-	{
-	    "\005SummaryInformation",
-	    "\005DocumentSummaryInformation",
-	    "WordDocument",
-	    "\001CompObj",
-	    "1Table"
-	};
-    final static int BYTE_ORDER = 0xfffe;
-    final static int FORMAT     = 0x0000;
-    final static int OS_VERSION = 0x00020A04;
-    final static byte[] CLASS_ID =
-	{
-	    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-	    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-	    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-	    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
-	};
-    final static int[] SECTION_COUNT =
+    static final String POI_FS = "TestGermanWord90.doc";
+    static final String[] POI_FILES = new String[]
+        {
+            "\005SummaryInformation",
+            "\005DocumentSummaryInformation",
+            "WordDocument",
+            "\001CompObj",
+            "1Table"
+        };
+    static final int BYTE_ORDER = 0xfffe;
+    static final int FORMAT     = 0x0000;
+    static final int OS_VERSION = 0x00020A04;
+    static final byte[] CLASS_ID =
+        {
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+        };
+    static final int[] SECTION_COUNT =
         {1, 2};
-    final static boolean[] IS_SUMMARY_INFORMATION =
+    static final boolean[] IS_SUMMARY_INFORMATION =
         {true, false};
-    final static boolean[] IS_DOCUMENT_SUMMARY_INFORMATION =
-        {false, true};	    
+    static final boolean[] IS_DOCUMENT_SUMMARY_INFORMATION =
+        {false, true};            
 
     POIFile[] poiFiles;
 
 
 
-    public TestBasic(String name)
+    public TestBasic(final String name)
     {
         super(name);
     }
@@ -113,11 +126,11 @@
      */
     public void setUp() throws FileNotFoundException, IOException
     {
-	final File dataDir =
-	    new File(System.getProperty("HPSF.testdata.path"));
-	final File data = new File(dataDir, POI_FS);
+        final File dataDir =
+            new File(System.getProperty("HPSF.testdata.path"));
+        final File data = new File(dataDir, POI_FS);
 
-	poiFiles = Util.readPOIFiles(data);
+        poiFiles = Util.readPOIFiles(data);
     }
 
 
@@ -128,9 +141,9 @@
      */
     public void testReadFiles() throws IOException
     {
-	String[] expected = POI_FILES;
-	for (int i = 0; i < expected.length; i++)
-	    Assert.assertEquals(poiFiles[i].getName(), expected[i]);
+        String[] expected = POI_FILES;
+        for (int i = 0; i < expected.length; i++)
+            Assert.assertEquals(poiFiles[i].getName(), expected[i]);
     }
 
 
@@ -146,37 +159,37 @@
      */
     public void testCreatePropertySets() throws IOException
     {
-	Class[] expected = new Class[]
-	    {
-		SummaryInformation.class,
-		DocumentSummaryInformation.class,
-		NoPropertySetStreamException.class,
-		NoPropertySetStreamException.class,
-		NoPropertySetStreamException.class
-	    };
-	for (int i = 0; i < expected.length; i++)
-	{
-	    InputStream in = new ByteArrayInputStream(poiFiles[i].getBytes());
-	    Object o;
-	    try
-	    {
-		o = PropertySetFactory.create(in);
-	    }
-	    catch (NoPropertySetStreamException ex)
-	    {
-		o = ex;
-	    }
-	    catch (UnexpectedPropertySetTypeException ex)
-	    {
-		o = ex;
-	    }
-	    catch (MarkUnsupportedException ex)
-	    {
-		o = ex;
-	    }
-	    in.close();
-	    Assert.assertEquals(o.getClass(), expected[i]);
-	}
+        Class[] expected = new Class[]
+            {
+                SummaryInformation.class,
+                DocumentSummaryInformation.class,
+                NoPropertySetStreamException.class,
+                NoPropertySetStreamException.class,
+                NoPropertySetStreamException.class
+            };
+        for (int i = 0; i < expected.length; i++)
+        {
+            InputStream in = new ByteArrayInputStream(poiFiles[i].getBytes());
+            Object o;
+            try
+            {
+                o = PropertySetFactory.create(in);
+            }
+            catch (NoPropertySetStreamException ex)
+            {
+                o = ex;
+            }
+            catch (UnexpectedPropertySetTypeException ex)
+            {
+                o = ex;
+            }
+            catch (MarkUnsupportedException ex)
+            {
+                o = ex;
+            }
+            in.close();
+            Assert.assertEquals(o.getClass(), expected[i]);
+        }
     }
 
 
@@ -188,25 +201,24 @@
      */
     public void testPropertySetMethods() throws IOException, HPSFException
     {
-	String[] expected = POI_FILES;
 
-	/* Loop over the two property sets. */
-	for (int i = 0; i < 2; i++)
-	{
-	    byte[] b = poiFiles[i].getBytes();
-	    PropertySet ps =
-		PropertySetFactory.create(new ByteArrayInputStream(b));
-	    Assert.assertEquals(ps.getByteOrder(), BYTE_ORDER);
-	    Assert.assertEquals(ps.getFormat(), FORMAT);
-	    Assert.assertEquals(ps.getOSVersion(), OS_VERSION);
-	    Assert.assertEquals(new String(ps.getClassID().getBytes()),
-				new String(CLASS_ID));
-	    Assert.assertEquals(ps.getSectionCount(), SECTION_COUNT[i]);
-	    Assert.assertEquals(ps.isSummaryInformation(),
-				IS_SUMMARY_INFORMATION[i]);
-	    Assert.assertEquals(ps.isDocumentSummaryInformation(),
-				IS_DOCUMENT_SUMMARY_INFORMATION[i]);
-	}
+        /* Loop over the two property sets. */
+        for (int i = 0; i < 2; i++)
+        {
+            byte[] b = poiFiles[i].getBytes();
+            PropertySet ps =
+                PropertySetFactory.create(new ByteArrayInputStream(b));
+            Assert.assertEquals(ps.getByteOrder(), BYTE_ORDER);
+            Assert.assertEquals(ps.getFormat(), FORMAT);
+            Assert.assertEquals(ps.getOSVersion(), OS_VERSION);
+            Assert.assertEquals(new String(ps.getClassID().getBytes()),
+                                new String(CLASS_ID));
+            Assert.assertEquals(ps.getSectionCount(), SECTION_COUNT[i]);
+            Assert.assertEquals(ps.isSummaryInformation(),
+                                IS_SUMMARY_INFORMATION[i]);
+            Assert.assertEquals(ps.isDocumentSummaryInformation(),
+                                IS_DOCUMENT_SUMMARY_INFORMATION[i]);
+        }
     }
 
 
@@ -214,11 +226,11 @@
     /**
      * <p>Runs the test cases stand-alone.</p>
      */
-    public static void main(String[] args) throws Throwable
+    public static void main(final String[] args) throws Throwable
     {
-	System.setProperty("HPSF.testdata.path",
-			   "./src/testcases/org/apache/poi/hpsf/data");
-	junit.textui.TestRunner.run(TestBasic.class);
+        System.setProperty("HPSF.testdata.path",
+                           "./src/testcases/org/apache/poi/hpsf/data");
+        junit.textui.TestRunner.run(TestBasic.class);
     }
 
 }
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java b/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java
new file mode 100644
index 0000000..c36f816
--- /dev/null
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestClassID.java
@@ -0,0 +1,171 @@
+/* ====================================================================

+ * The Apache Software License, Version 1.1

+ *

+ * Copyright (c) 2003 The Apache Software Foundation.  All rights

+ * reserved.

+ *

+ * Redistribution and use in source and binary forms, with or without

+ * modification, are permitted provided that the following conditions

+ * are met:

+ *

+ * 1. Redistributions of source code must retain the above copyright

+ *    notice, this list of conditions and the following disclaimer.

+ *

+ * 2. Redistributions in binary form must reproduce the above copyright

+ *    notice, this list of conditions and the following disclaimer in

+ *    the documentation and/or other materials provided with the

+ *    distribution.

+ *

+ * 3. The end-user documentation included with the redistribution,

+ *    if any, must include the following acknowledgment:

+ *       "This product includes software developed by the

+ *        Apache Software Foundation (http://www.apache.org/)."

+ *    Alternately, this acknowledgment may appear in the software itself,

+ *    if and wherever such third-party acknowledgments normally appear.

+ *

+ * 4. The names "Apache" and "Apache Software Foundation" and

+ *    "Apache POI" must not be used to endorse or promote products

+ *    derived from this software without prior written permission. For

+ *    written permission, please contact apache@apache.org.

+ *

+ * 5. Products derived from this software may not be called "Apache",

+ *    "Apache POI", nor may "Apache" appear in their name, without

+ *    prior written permission of the Apache Software Foundation.

+ *

+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED

+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES

+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR

+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF

+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,

+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT

+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

+ * SUCH DAMAGE.

+ * ====================================================================

+ *

+ * This software consists of voluntary contributions made by many

+ * individuals on behalf of the Apache Software Foundation.  For more

+ * information on the Apache Software Foundation, please see

+ * <http://www.apache.org/>.

+ */

+

+package org.apache.poi.hpsf.basic;

+

+import junit.framework.Assert;

+import junit.framework.TestCase;

+

+import org.apache.poi.hpsf.ClassID;

+

+/**

+ * <p>Tests ClassID structure.</p>

+ *

+ * @author Michael Zalewski (zalewski@optonline.net)

+ */

+public class TestClassID extends TestCase

+{

+    /**

+     * <p>Constructor</p>

+     * 

+     * @param name the test case's name

+     */

+    public TestClassID(final String name)

+    {

+        super(name);

+    }

+

+    /**

+     * Various tests of overridden .equals()

+     */

+    public void testEquals()

+    {

+        ClassID clsidTest1 = new ClassID(

+              new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08

+                         , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }

+            , 0

+        );

+        ClassID clsidTest2 = new ClassID(

+              new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08

+                         , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }

+            , 0

+        );

+        ClassID clsidTest3 = new ClassID(

+              new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08

+                         , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11 }

+            , 0

+        );

+        Assert.assertEquals( clsidTest1, clsidTest1);

+        Assert.assertEquals( clsidTest1, clsidTest2);

+        Assert.assertFalse( clsidTest1.equals( clsidTest3));

+        Assert.assertFalse( clsidTest1.equals( null));

+    }

+    /**

+     * Try to write to a buffer that is too small. This should

+     *   throw an Exception

+     */

+    public void testWriteArrayStoreException()

+    {

+        ClassID clsidTest = new ClassID(

+              new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08

+                         , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }

+            , 0

+        );

+        boolean bExceptionOccurred = false;

+        try {

+            clsidTest.write( new byte[ 15], 0);

+        } catch( Exception e) {

+            bExceptionOccurred = true;

+        }

+        Assert.assertTrue( bExceptionOccurred);

+

+        bExceptionOccurred = false;

+        try {

+            clsidTest.write( new byte[ 16], 1);

+        } catch( Exception e) {

+            bExceptionOccurred = true;

+        }

+        Assert.assertTrue( bExceptionOccurred);

+

+        // These should work without throwing an Exception

+        bExceptionOccurred = false;

+        try {

+            clsidTest.write( new byte[ 16], 0);

+            clsidTest.write( new byte[ 17], 1);

+        } catch( Exception e) {

+            bExceptionOccurred = true;

+        }

+        Assert.assertFalse( bExceptionOccurred);

+    }

+    /**

+     * <p>Tests the {@link PropertySet} methods. The test file has two

+     * property set: the first one is a {@link SummaryInformation},

+     * the second one is a {@link DocumentSummaryInformation}.</p>

+     */

+    public void testClassID()

+    {

+        ClassID clsidTest = new ClassID(

+              new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08

+                         , 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }

+            , 0

+        );

+        Assert.assertEquals( 

+              clsidTest.toString().toUpperCase()

+            , "{04030201-0605-0807-090A-0B0C0D0E0F10}"

+        );

+    }

+

+

+

+    /**

+     * <p>Runs the test cases stand-alone.</p>

+     */

+    public static void main(final String[] args)

+    {

+        System.setProperty("HPSF.testdata.path",

+                           "./src/testcases/org/apache/poi/hpsf/data");

+        junit.textui.TestRunner.run(TestClassID.class);

+    }

+

+}

diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java
new file mode 100644
index 0000000..fbe9fbf
--- /dev/null
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestEmptyProperties.java
@@ -0,0 +1,189 @@
+package org.apache.poi.hpsf.basic;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.poi.hpsf.HPSFException;
+import org.apache.poi.hpsf.MarkUnsupportedException;
+import org.apache.poi.hpsf.NoPropertySetStreamException;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.UnexpectedPropertySetTypeException;
+
+/**
+ * <p>Test case for OLE2 files with empty properties. An empty property's type
+ * is {@link Variant.VT_EMPTY}.</p>
+ *
+ * @author Rainer Klute <a
+ * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
+ * @since 2003-07-25
+ * @version $Id$
+ */
+public class TestEmptyProperties extends TestCase
+{
+
+    /**
+     * <p>This test file's summary information stream contains some empty
+     * properties.</p>
+     */
+    static final String POI_FS = "TestCorel.shw";
+
+    static final String[] POI_FILES = new String[]
+        {
+            "PerfectOffice_MAIN",
+            "\005SummaryInformation",
+            "Main"
+        };
+
+    POIFile[] poiFiles;
+
+
+
+    /**
+     * <p>Constructor</p>
+     * 
+     * @param name The name of the test case
+     */
+    public TestEmptyProperties(final String name)
+    {
+        super(name);
+    }
+
+
+
+    /**
+     * <p>Read a the test file from the "data" directory.</p>
+     *
+     * @exception FileNotFoundException if the file containing the test data
+     * does not exist
+     * @exception IOException if an I/O exception occurs
+     */
+    public void setUp() throws FileNotFoundException, IOException
+    {
+        final File dataDir =
+            new File(System.getProperty("HPSF.testdata.path"));
+        final File data = new File(dataDir, POI_FS);
+
+        poiFiles = Util.readPOIFiles(data);
+    }
+
+
+
+    /**
+     * <p>Checks the names of the files in the POI filesystem. They
+     * are expected to be in a certain order.</p>
+     * 
+     * @exception IOException if an I/O exception occurs
+     */
+    public void testReadFiles() throws IOException
+    {
+        String[] expected = POI_FILES;
+        for (int i = 0; i < expected.length; i++)
+            Assert.assertEquals(poiFiles[i].getName(), expected[i]);
+    }
+
+
+
+    /**
+     * <p>Tests whether property sets can be created from the POI
+     * files in the POI file system. This test case expects the first
+     * file to be a {@link SummaryInformation}, the second file to be
+     * a {@link DocumentSummaryInformation} and the rest to be no
+     * property sets. In the latter cases a {@link
+     * NoPropertySetStreamException} will be thrown when trying to
+     * create a {@link PropertySet}.</p>
+     * 
+     * @exception IOException if an I/O exception occurs
+     */
+    public void testCreatePropertySets() throws IOException
+    { 
+        Class[] expected = new Class[]
+            {
+                NoPropertySetStreamException.class,
+                SummaryInformation.class,
+                NoPropertySetStreamException.class
+            };
+        for (int i = 0; i < expected.length; i++)
+        {
+            InputStream in = new ByteArrayInputStream(poiFiles[i].getBytes());
+            Object o;
+            try
+            {
+                o = PropertySetFactory.create(in);
+            }
+            catch (NoPropertySetStreamException ex)
+            {
+                o = ex;
+            }
+            catch (UnexpectedPropertySetTypeException ex)
+            {
+                o = ex;
+            }
+            catch (MarkUnsupportedException ex)
+            {
+                o = ex;
+            }
+            in.close();
+            Assert.assertEquals(o.getClass(), expected[i]);
+        }
+    }
+
+
+
+    /**
+     * <p>Tests the {@link PropertySet} methods. The test file has two
+     * property sets: the first one is a {@link SummaryInformation},
+     * the second one is a {@link DocumentSummaryInformation}.</p>
+     * 
+     * @exception IOException if an I/O exception occurs
+     * @exception HPSFException if an HPSF operation fails
+     */
+    public void testPropertySetMethods() throws IOException, HPSFException
+    {
+        byte[] b = poiFiles[1].getBytes();
+        PropertySet ps =
+            PropertySetFactory.create(new ByteArrayInputStream(b));
+        SummaryInformation s = (SummaryInformation) ps;
+        assertNull(s.getTitle());
+        assertNull(s.getSubject());
+        assertNotNull(s.getAuthor());
+        assertNull(s.getKeywords());
+        assertNull(s.getComments());
+        assertNotNull(s.getTemplate());
+        assertNotNull(s.getLastAuthor());
+        assertNotNull(s.getRevNumber());
+        assertNull(s.getEditTime());
+        assertNull(s.getLastPrinted());
+        assertNull(s.getCreateDateTime());
+        assertNull(s.getLastSaveDateTime());
+        assertEquals(s.getPageCount(), 0);
+        assertEquals(s.getWordCount(), 0);
+        assertEquals(s.getCharCount(), 0);
+        assertNull(s.getThumbnail());
+        assertNull(s.getApplicationName());
+    }
+
+
+
+    /**
+     * <p>Runs the test cases stand-alone.</p>
+     * 
+     * @param args the command-line arguments (unused)
+     * 
+     * @exception Throwable if any exception or error occurs
+     */
+    public static void main(final String[] args) throws Throwable
+    {
+        System.setProperty("HPSF.testdata.path",
+                           "./src/testcases/org/apache/poi/hpsf/data");
+        junit.textui.TestRunner.run(TestBasic.class);
+    }
+
+}
diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java b/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
index b2c7b2a..cf3b351 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/TestUnicode.java
@@ -54,10 +54,18 @@
 
 package org.apache.poi.hpsf.basic;
 
-import java.io.*;
-import java.util.*;
-import junit.framework.*;
-import org.apache.poi.hpsf.*;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.poi.hpsf.HPSFException;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.Section;
 
 
 
@@ -72,17 +80,22 @@
 public class TestUnicode extends TestCase
 {
 
-    final static String POI_FS = "TestUnicode.xls";
-    final static String[] POI_FILES = new String[]
-	{
-	    "\005DocumentSummaryInformation",
-	};
+    static final String POI_FS = "TestUnicode.xls";
+    static final String[] POI_FILES = new String[]
+        {
+            "\005DocumentSummaryInformation",
+        };
     File data;
     POIFile[] poiFiles;
 
 
 
-    public TestUnicode(String name)
+    /**
+     * <p>Constructor</p>
+     * 
+     * @param name the test case's name
+     */
+    public TestUnicode(final String name)
     {
         super(name);
     }
@@ -92,11 +105,11 @@
     /**
      * <p>Read a the test file from the "data" directory.</p>
      */
-    public void setUp() throws FileNotFoundException, IOException
+    protected void setUp() throws FileNotFoundException, IOException
     {
-	final File dataDir =
-	    new File(System.getProperty("HPSF.testdata.path"));
-	data = new File(dataDir, POI_FS);
+        final File dataDir =
+            new File(System.getProperty("HPSF.testdata.path"));
+        data = new File(dataDir, POI_FS);
     }
 
 
@@ -108,23 +121,23 @@
      */
     public void testPropertySetMethods() throws IOException, HPSFException
     {
-	POIFile poiFile = Util.readPOIFiles(data, POI_FILES)[0];
-	byte[] b = poiFile.getBytes();
-	PropertySet ps =
-	    PropertySetFactory.create(new ByteArrayInputStream(b));
-	Assert.assertTrue(ps.isDocumentSummaryInformation());
-	Assert.assertEquals(ps.getSectionCount(), 2);
-	Section s = (Section) ps.getSections().get(1);
-	Assert.assertEquals(s.getProperty(1),
-			    new Integer(1200));
-	Assert.assertEquals(s.getProperty(2),
-			    new Long(4198897018l));
-	Assert.assertEquals(s.getProperty(3),
-			    "MCon_Info zu Office bei Schreiner");
-	Assert.assertEquals(s.getProperty(4),
-			    "petrovitsch@schreiner-online.de");
-	Assert.assertEquals(s.getProperty(5),
-			    "Petrovitsch, Wilhelm");
+        POIFile poiFile = Util.readPOIFiles(data, POI_FILES)[0];
+        byte[] b = poiFile.getBytes();
+        PropertySet ps =
+            PropertySetFactory.create(new ByteArrayInputStream(b));
+        Assert.assertTrue(ps.isDocumentSummaryInformation());
+        Assert.assertEquals(ps.getSectionCount(), 2);
+        Section s = (Section) ps.getSections().get(1);
+        Assert.assertEquals(s.getProperty(1),
+                            new Integer(1200));
+        Assert.assertEquals(s.getProperty(2),
+                            new Long(4198897018L));
+        Assert.assertEquals(s.getProperty(3),
+                            "MCon_Info zu Office bei Schreiner");
+        Assert.assertEquals(s.getProperty(4),
+                            "petrovitsch@schreiner-online.de");
+        Assert.assertEquals(s.getProperty(5),
+                            "Petrovitsch, Wilhelm");
     }
 
 
@@ -132,10 +145,10 @@
     /**
      * <p>Runs the test cases stand-alone.</p>
      */
-    public static void main(String[] args)
+    public static void main(final String[] args)
     {
-	System.setProperty("HPSF.testdata.path",
-			   "./src/testcases/org/apache/poi/hpsf/data");
+        System.setProperty("HPSF.testdata.path",
+                           "./src/testcases/org/apache/poi/hpsf/data");
         junit.textui.TestRunner.run(TestUnicode.class);
     }
 
diff --git a/src/testcases/org/apache/poi/hpsf/basic/Util.java b/src/testcases/org/apache/poi/hpsf/basic/Util.java
index 87aaf21..7afd371 100644
--- a/src/testcases/org/apache/poi/hpsf/basic/Util.java
+++ b/src/testcases/org/apache/poi/hpsf/basic/Util.java
@@ -54,9 +54,24 @@
 
 package org.apache.poi.hpsf.basic;
 
-import java.io.*;
-import java.util.*;
-import org.apache.poi.poifs.eventfilesystem.*;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.poi.poifs.eventfilesystem.POIFSReader;
+import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
+import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener;
 
 
 
@@ -73,6 +88,9 @@
     /**
      * <p>Reads bytes from an input stream and writes them to an
      * output stream until end of file is encountered.</p>
+     *
+     * @param in the input stream to read from
+     * @param out the output stream to write to
      */
     public static void copy(final InputStream in, final OutputStream out)
         throws IOException
@@ -88,8 +106,8 @@
                 read = in.read(b, 0, BUF_SIZE);
                 if (read > 0)
                     out.write(b, 0, read);
-		else
-		    eof = true;
+                else
+                    eof = true;
             }
             catch (EOFException ex)
             {
@@ -106,16 +124,16 @@
      * into memory and thus does not cope well with large POI
      * filessystems.</p>
      * 
-     * @param file The name of the POI filesystem as seen by the
+     * @param poiFs The name of the POI filesystem as seen by the
      * operating system. (This is the "filename".)
      *
      * @return The POI files. The elements are ordered in the same way
      * as the files in the POI filesystem.
      */
     public static POIFile[] readPOIFiles(final File poiFs)
-	throws FileNotFoundException, IOException
+        throws FileNotFoundException, IOException
     {
-	return readPOIFiles(poiFs, null);
+        return readPOIFiles(poiFs, null);
     }
 
 
@@ -126,7 +144,7 @@
      * files into memory and thus does not cope well with large POI
      * filessystems.</p>
      * 
-     * @param file The name of the POI filesystem as seen by the
+     * @param poiFs The name of the POI filesystem as seen by the
      * operating system. (This is the "filename".)
      *
      * @param poiFiles The names of the POI files to be read.
@@ -135,50 +153,49 @@
      * as the files in the POI filesystem.
      */
     public static POIFile[] readPOIFiles(final File poiFs,
-					 final String[] poiFiles)
-	throws FileNotFoundException, IOException
+                                         final String[] poiFiles)
+        throws FileNotFoundException, IOException
     {
-	final List files = new ArrayList();
-	POIFSReader r = new POIFSReader();
-	POIFSReaderListener pfl = new POIFSReaderListener()
-	    {
-		public void processPOIFSReaderEvent(POIFSReaderEvent event)
-		{
-		    try
-		    {
-			POIFile f = new POIFile();
-			f.setName(event.getName());
-			f.setPath(event.getPath());
-			InputStream in = event.getStream();
-			ByteArrayOutputStream out =
-			    new ByteArrayOutputStream();
-			Util.copy(in, out);
-			out.close();
-			f.setBytes(out.toByteArray());
-			files.add(f);
-		    }
-		    catch (IOException ex)
-		    {
-			ex.printStackTrace();
-			throw new RuntimeException(ex.getMessage());
-		    }
-		}
-	    };
-	if (poiFiles == null)
-	    /* Register the listener for all POI files. */
-	    r.registerListener(pfl);
-	else
-	    /* Register the listener for the specified POI files
-	     * only. */
-	    for (int i = 0; i < poiFiles.length; i++)
-		r.registerListener(pfl, poiFiles[i]);
+        final List files = new ArrayList();
+        POIFSReader r = new POIFSReader();
+        POIFSReaderListener pfl = new POIFSReaderListener()
+        {
+            public void processPOIFSReaderEvent(final POIFSReaderEvent event)
+            {
+                try
+                {
+                    POIFile f = new POIFile();
+                    f.setName(event.getName());
+                    f.setPath(event.getPath());
+                    InputStream in = event.getStream();
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    Util.copy(in, out);
+                    out.close();
+                    f.setBytes(out.toByteArray());
+                    files.add(f);
+                }
+                catch (IOException ex)
+                {
+                    ex.printStackTrace();
+                    throw new RuntimeException(ex.getMessage());
+                }
+            }
+        };
+        if (poiFiles == null)
+            /* Register the listener for all POI files. */
+            r.registerListener(pfl);
+        else
+            /* Register the listener for the specified POI files
+             * only. */
+            for (int i = 0; i < poiFiles.length; i++)
+                r.registerListener(pfl, poiFiles[i]);
 
-	/* Read the POI filesystem. */
-	r.read(new FileInputStream(poiFs));
-	POIFile[] result = new POIFile[files.size()];
-	for (int i = 0; i < result.length; i++)
-	    result[i] = (POIFile) files.get(i);
-	return result;
+        /* Read the POI filesystem. */
+        r.read(new FileInputStream(poiFs));
+        POIFile[] result = new POIFile[files.size()];
+        for (int i = 0; i < result.length; i++)
+            result[i] = (POIFile) files.get(i);
+        return result;
     }
 
 
@@ -188,19 +205,19 @@
      */
     public static void printSystemProperties()
     {
-	Properties p = System.getProperties();
-	List names = new LinkedList();
-	for (Iterator i = p.keySet().iterator(); i.hasNext();)
-	    names.add(i.next());
-	Collections.sort(names);
-	for (Iterator i = names.iterator(); i.hasNext();)
+        final Properties p = System.getProperties();
+        final List names = new LinkedList();
+        for (Iterator i = p.keySet().iterator(); i.hasNext();)
+            names.add(i.next());
+        Collections.sort(names);
+        for (final Iterator i = names.iterator(); i.hasNext();)
         {
-	    String name = (String) i.next();
-	    String value = (String) p.get(name);
-	    System.out.println(name + ": " + value);
-	}
-	System.out.println("Current directory: " +
-			   System.getProperty("user.dir"));
+            String name = (String) i.next();
+            String value = (String) p.get(name);
+            System.out.println(name + ": " + value);
+        }
+        System.out.println("Current directory: " +
+                           System.getProperty("user.dir"));
     }
 
 }
diff --git a/src/testcases/org/apache/poi/hpsf/data/TestCorel.shw b/src/testcases/org/apache/poi/hpsf/data/TestCorel.shw
new file mode 100755
index 0000000..e0af194
--- /dev/null
+++ b/src/testcases/org/apache/poi/hpsf/data/TestCorel.shw
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/12561-1.xls b/src/testcases/org/apache/poi/hssf/data/12561-1.xls
new file mode 100755
index 0000000..40b7276
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/12561-1.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/12561-2.xls b/src/testcases/org/apache/poi/hssf/data/12561-2.xls
new file mode 100755
index 0000000..bb360c1
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/12561-2.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/12843-1.xls b/src/testcases/org/apache/poi/hssf/data/12843-1.xls
new file mode 100644
index 0000000..0ef5df4
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/12843-1.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/12843-2.xls b/src/testcases/org/apache/poi/hssf/data/12843-2.xls
new file mode 100644
index 0000000..beff646
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/12843-2.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/13224.xls b/src/testcases/org/apache/poi/hssf/data/13224.xls
new file mode 100755
index 0000000..81f69d3
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/13224.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/13796.xls b/src/testcases/org/apache/poi/hssf/data/13796.xls
new file mode 100644
index 0000000..efae53f
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/13796.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/14330-1.xls b/src/testcases/org/apache/poi/hssf/data/14330-1.xls
new file mode 100644
index 0000000..6fd2d95
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/14330-1.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/14330-2.xls b/src/testcases/org/apache/poi/hssf/data/14330-2.xls
new file mode 100644
index 0000000..8e56a6d
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/14330-2.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/14460.xls b/src/testcases/org/apache/poi/hssf/data/14460.xls
new file mode 100644
index 0000000..284d684
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/14460.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/15228.xls b/src/testcases/org/apache/poi/hssf/data/15228.xls
new file mode 100644
index 0000000..3b26ed1
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/15228.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/15375.xls b/src/testcases/org/apache/poi/hssf/data/15375.xls
new file mode 100644
index 0000000..99ab193
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/15375.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/15556.xls b/src/testcases/org/apache/poi/hssf/data/15556.xls
new file mode 100644
index 0000000..82b48a5
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/15556.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/19599-1.xls b/src/testcases/org/apache/poi/hssf/data/19599-1.xls
new file mode 100644
index 0000000..4ce27d1
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/19599-1.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/19599-2.xls b/src/testcases/org/apache/poi/hssf/data/19599-2.xls
new file mode 100644
index 0000000..17f4583
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/19599-2.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/22742.xls b/src/testcases/org/apache/poi/hssf/data/22742.xls
new file mode 100644
index 0000000..cd367ca
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/22742.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/24215.xls b/src/testcases/org/apache/poi/hssf/data/24215.xls
new file mode 100644
index 0000000..bcbbe63
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/24215.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/EmbeddedChartHeaderTest.xls b/src/testcases/org/apache/poi/hssf/data/EmbeddedChartHeaderTest.xls
new file mode 100644
index 0000000..af0cb22
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/EmbeddedChartHeaderTest.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/SquareMacro.xls b/src/testcases/org/apache/poi/hssf/data/SquareMacro.xls
new file mode 100644
index 0000000..54beeeb
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/SquareMacro.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/blankworkbook.xls b/src/testcases/org/apache/poi/hssf/data/blankworkbook.xls
new file mode 100644
index 0000000..fcf734d
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/blankworkbook.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/model/SheetTest.java b/src/testcases/org/apache/poi/hssf/model/SheetTest.java
index 9a78fd6..0b2723c 100644
--- a/src/testcases/org/apache/poi/hssf/model/SheetTest.java
+++ b/src/testcases/org/apache/poi/hssf/model/SheetTest.java
@@ -7,6 +7,7 @@
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.ColumnInfoRecord;
+import org.apache.poi.hssf.record.MergeCellsRecord;
 import org.apache.poi.hssf.record.RowRecord;
 import org.apache.poi.hssf.record.StringRecord;
 
@@ -71,6 +72,31 @@
 		assertTrue("Expected " + recordsRemoved + " record to be removed from the starting " + records + ".  Currently there are " + sheet.getRecords().size() + " records", records - sheet.getRecords().size() == recordsRemoved);
 	}
 
+	/**
+	 * Bug: 22922 (Reported by Xuemin Guan)
+	 * <p>
+	 * Remove mergedregion fails when a sheet loses records after an initial CreateSheet
+	 * fills up the records.
+	 *
+	 */
+	public void testMovingMergedRegion() {
+		List records = new ArrayList();
+		
+		MergeCellsRecord merged = new MergeCellsRecord();
+		merged.addArea(0, (short)0, 1, (short)2);
+		records.add(new RowRecord());
+		records.add(new RowRecord());
+		records.add(new RowRecord());
+		records.add(merged);
+		
+		Sheet sheet = Sheet.createSheet(records, 0);
+		sheet.records.remove(0);
+		
+		//stub object to throw off list INDEX operations
+		sheet.removeMergedRegion(0);
+		assertEquals("Should be no more merged regions", 0, sheet.getNumMergedRegions());
+	}
+
 	public void testGetMergedRegionAt()
 	{
 		//TODO
@@ -147,13 +173,6 @@
 		
 	}
 
-
-	public static void main(String [] args) {
-		System.out
-		.println("Testing : "+SheetTest.class.getName());
-		junit.textui.TestRunner.run(SheetTest.class);
-  }
-
 }
 
 
diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
index c4722a9..b45f48f 100644
--- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
+++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
@@ -56,7 +56,6 @@
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.formula.*;
-import org.apache.poi.hssf.util.SheetReferences;
 
 /**
  * Test the low level formula parser functionality. High level tests are to 
@@ -145,7 +144,7 @@
         assertEquals(true, flag.getValue());
         assertEquals("Y", y.getValue());
         assertEquals("N", n.getValue());
-        assertEquals("IF", funif.toFormulaString(new SheetReferences()));
+        assertEquals("IF", funif.toFormulaString((Workbook) null));
         assertTrue("Goto ptg exists", goto1.isGoto());
     }
 
@@ -285,6 +284,36 @@
 		
 	}
 	    
+    public void testMacroFunction() {
+        Workbook w = new Workbook();
+        FormulaParser fp = new FormulaParser("FOO()", w);
+        fp.parse();
+        Ptg[] ptg = fp.getRPNPtg();
+
+        AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[0];
+        assertEquals("externalflag", tfunc.getName());
+
+        NamePtg tname = (NamePtg) ptg[1];
+        assertEquals("FOO", tname.toFormulaString(w));
+    }
+
+    public void testEmbeddedSlash() {
+        FormulaParser fp = new FormulaParser("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\");",null);
+        fp.parse();
+        Ptg[] ptg = fp.getRPNPtg();
+        assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
+        assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
+        
+    }
+    
+    public void testConcatenate(){
+         FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")",null);
+         fp.parse();
+         Ptg[] ptg = fp.getRPNPtg();
+        assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
+        assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
+    }
+    
      public static void main(String [] args) {
         System.out.println("Testing org.apache.poi.hssf.record.formula.FormulaParser");
         junit.textui.TestRunner.run(TestFormulaParser.class);
diff --git a/src/testcases/org/apache/poi/hssf/record/TestBoundSheetRecord.java b/src/testcases/org/apache/poi/hssf/record/TestBoundSheetRecord.java
index 2058c2f..4ca6075 100644
--- a/src/testcases/org/apache/poi/hssf/record/TestBoundSheetRecord.java
+++ b/src/testcases/org/apache/poi/hssf/record/TestBoundSheetRecord.java
@@ -92,5 +92,25 @@
 
         assertEquals(" 2  +  2  +  4  +   2   +    1     +    1    + len(str) * 2", 24, record.getRecordSize());
     }
+    
+    public void testName() {
+        BoundSheetRecord record = new BoundSheetRecord();
+        record.setSheetname("1234567890223456789032345678904");
+        assertTrue("Success", true);
+        try {
+            record.setSheetname("12345678902234567890323456789042");
+            assertTrue("Should have thrown IllegalArgumentException, but didnt", false);
+        } catch (IllegalArgumentException e) {
+            assertTrue("succefully threw exception",true);
+        }
+        
+        try {
+            record.setSheetname("s//*s");
+            assertTrue("Should have thrown IllegalArgumentException, but didnt", false);
+        } catch (IllegalArgumentException e) {
+            assertTrue("succefully threw exception",true);
+        }
+            
+    }
 
 }
diff --git a/src/testcases/org/apache/poi/hssf/record/TestSSTDeserializer.java b/src/testcases/org/apache/poi/hssf/record/TestSSTDeserializer.java
index 6272653..a506e37 100644
--- a/src/testcases/org/apache/poi/hssf/record/TestSSTDeserializer.java
+++ b/src/testcases/org/apache/poi/hssf/record/TestSSTDeserializer.java
@@ -88,7 +88,7 @@
         byte[] bytes = HexRead.readData( _test_file_path + File.separator + "richtextdata.txt", "header" );
         BinaryTree strings = new BinaryTree();
         SSTDeserializer deserializer = new SSTDeserializer( strings );
-        deserializer.manufactureStrings( bytes, 0, (short)bytes.length );
+        deserializer.manufactureStrings( bytes, 0);
         byte[] continueBytes = HexRead.readData( _test_file_path + File.separator + "richtextdata.txt", "continue1" );
         deserializer.processContinueRecord( continueBytes );
 
@@ -101,7 +101,7 @@
         byte[] bytes = HexRead.readData( _test_file_path + File.separator + "evencontinuation.txt", "header" );
         BinaryTree strings = new BinaryTree();
         SSTDeserializer deserializer = new SSTDeserializer( strings );
-        deserializer.manufactureStrings( bytes, 0, (short)bytes.length );
+        deserializer.manufactureStrings( bytes, 0);
         byte[] continueBytes = HexRead.readData( _test_file_path + File.separator + "evencontinuation.txt", "continue1" );
         deserializer.processContinueRecord( continueBytes );
 
@@ -119,7 +119,7 @@
         byte[] bytes = HexRead.readData( _test_file_path + File.separator + "stringacross2continuations.txt", "header" );
         BinaryTree strings = new BinaryTree();
         SSTDeserializer deserializer = new SSTDeserializer( strings );
-        deserializer.manufactureStrings( bytes, 0, (short)bytes.length );
+        deserializer.manufactureStrings( bytes, 0);
         bytes = HexRead.readData( _test_file_path + File.separator + "stringacross2continuations.txt", "continue1" );
         deserializer.processContinueRecord( bytes );
         bytes = HexRead.readData( _test_file_path + File.separator + "stringacross2continuations.txt", "continue2" );
@@ -136,7 +136,7 @@
         byte[] bytes = HexRead.readData( _test_file_path + File.separator + "extendedtextstrings.txt", "rich-header" );
         BinaryTree strings = new BinaryTree();
         SSTDeserializer deserializer = new SSTDeserializer( strings );
-        deserializer.manufactureStrings( bytes, 0, (short)bytes.length );
+        deserializer.manufactureStrings( bytes, 0);
         byte[] continueBytes = HexRead.readData( _test_file_path + File.separator + "extendedtextstrings.txt", "rich-continue1" );
         deserializer.processContinueRecord( continueBytes );
 
@@ -146,7 +146,7 @@
         bytes = HexRead.readData( _test_file_path + File.separator + "extendedtextstrings.txt", "norich-header" );
         strings = new BinaryTree();
         deserializer = new SSTDeserializer( strings );
-        deserializer.manufactureStrings( bytes, 0, (short)bytes.length );
+        deserializer.manufactureStrings( bytes, 0);
         continueBytes = HexRead.readData( _test_file_path + File.separator + "extendedtextstrings.txt", "norich-continue1" );
         deserializer.processContinueRecord( continueBytes );
 
diff --git a/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java
index 2410599..747868c 100644
--- a/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java
+++ b/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java
@@ -429,7 +429,7 @@
         assertEquals( 1464, record.getNumStrings() );
         assertEquals( 688, record.getNumUniqueStrings() );
         assertEquals( 492, record.countStrings() );
-        assertEquals( 1, record.getDeserializer().getContinuationExpectedChars() );
+//jmh        assertEquals( 1, record.getDeserializer().getContinuationExpectedChars() );
         assertEquals( "Consolidated B-24J Liberator The Dragon & His Tai",
                 record.getDeserializer().getUnfinishedString() );
 //        assertEquals( 52, record.getDeserializer().getTotalLength() );
@@ -448,7 +448,7 @@
         assertEquals( 0, record.getNumStrings() );
         assertEquals( 0, record.getNumUniqueStrings() );
         assertEquals( 0, record.countStrings() );
-        assertEquals( 0, record.getDeserializer().getContinuationExpectedChars() );
+        assertEquals( 0, record.getDeserializer().getContinuationCharsRead() );
         assertEquals( "", record.getDeserializer().getUnfinishedString() );
 //        assertEquals( 0, record.getDeserializer().getTotalLength() );
 //        assertEquals( 0, record.getDeserializer().getStringDataOffset() );
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
new file mode 100644
index 0000000..b43d5c6
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
@@ -0,0 +1,413 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003, 2003 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.hssf.usermodel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+
+
+/**
+ * Testcases for bugs entered in bugzilla
+ * the Test name contains the bugzilla bug id
+ * @author Avik Sengupta
+ */
+
+public class TestBugs
+extends TestCase {
+    public TestBugs(String s) {
+        super(s);
+    }
+    
+    /** Test reading AND writing a complicated workbook
+     *Test opening resulting sheet in excel*/
+    public void test15228()
+    throws java.io.IOException {
+        String readFilename = System.getProperty("HSSF.testdata.path");
+        FileInputStream in = new FileInputStream(readFilename+File.separator+"15228.xls");
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        HSSFSheet s = wb.getSheetAt(0);
+        HSSFRow r = s.createRow(0);
+        HSSFCell c = r.createCell((short)0);
+        c.setCellValue(10);
+        File file = File.createTempFile("test15228",".xls");
+        FileOutputStream out    = new FileOutputStream(file);
+        wb.write(out);
+        assertTrue("No exception thrown", true);
+        assertTrue("File Should Exist", file.exists());
+        
+    }
+    
+    public void test13796()
+    throws java.io.IOException {
+        String readFilename = System.getProperty("HSSF.testdata.path");
+        FileInputStream in = new FileInputStream(readFilename+File.separator+"13796.xls");
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        HSSFSheet s = wb.getSheetAt(0);
+        HSSFRow r = s.createRow(0);
+        HSSFCell c = r.createCell((short)0);
+        c.setCellValue(10);
+        File file = File.createTempFile("test13796",".xls");
+        FileOutputStream out    = new FileOutputStream(file);
+        wb.write(out);
+        assertTrue("No exception thrown", true);
+        assertTrue("File Should Exist", file.exists());
+        
+    }
+    /**Test writing a hyperlink
+     * Open resulting sheet in Excel and check that A1 contains a hyperlink*/
+    public void test23094() throws Exception {
+        File file = File.createTempFile("test23094",".xls");
+        FileOutputStream out    = new FileOutputStream(file);
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet();
+        HSSFRow r = s.createRow(0);
+        r.createCell((short)0).setCellFormula("HYPERLINK( \"http://jakarta.apache.org\", \"Jakarta\" )");
+        assertTrue("No Exception expected",true);
+        wb.write(out);
+        out.close();
+    }
+    
+     /* test hyperlinks
+      * open resulting file in excel, and check that there is a link to Google
+      **/
+    public void test15353() throws Exception {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet("My sheet");
+        
+        HSSFRow row = sheet.createRow( (short) 0 );
+        HSSFCell cell = row.createCell( (short) 0 );
+        cell.setCellFormula("HYPERLINK(\"http://google.com\",\"Google\")");
+        
+        // Write out the workbook
+        File f = File.createTempFile("test15353",".xls");
+        FileOutputStream fileOut = new FileOutputStream(f);
+        wb.write(fileOut);
+        fileOut.close();
+    }
+    
+    /** test reading of a formula with a name and a cell ref in one
+     **/
+    public void test14460() throws Exception {
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/14460.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        HSSFSheet sheet = wb.getSheetAt(0);
+        assertTrue("No exception throws", true);
+    }
+    
+    public void test14330() throws Exception {
+        String filedir = System.getProperty("HSSF.testdata.path");
+        String filename=filedir+"/14330-1.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        HSSFSheet sheet = wb.getSheetAt(0);
+        
+        filename=filedir+"/14330-2.xls";
+        in = new FileInputStream(filename);
+        wb = new HSSFWorkbook(in);
+        sheet = wb.getSheetAt(0);
+        assertTrue("No exception throws", true);
+    }
+    
+    /** test rewriting a file with large number of unique strings
+     *open resulting file in Excel to check results!*/
+    public void test15375() {
+        
+        try {
+            String filename = System.getProperty("HSSF.testdata.path");
+            filename=filename+"/15375.xls";
+            FileInputStream in = new FileInputStream(filename);
+            HSSFWorkbook wb = new HSSFWorkbook(in);
+            HSSFSheet sheet = wb.getSheetAt(0);
+            
+            HSSFRow row = sheet.getRow(5);
+            HSSFCell cell = row.getCell((short)3);
+            if (cell == null)
+                cell = row.createCell((short)3);
+            
+            // Write test
+            cell.setCellType(HSSFCell.CELL_TYPE_STRING);
+            cell.setCellValue("a test");
+            
+            // change existing numeric cell value
+            
+            HSSFRow oRow = sheet.getRow(14);
+            HSSFCell oCell = oRow.getCell((short)4);
+            oCell.setCellValue(75);
+            oCell = oRow.getCell((short)5);
+            oCell.setCellValue("0.3");
+            
+            // Write the output to a file
+            File f = File.createTempFile("test15375",".xls");
+            FileOutputStream fileOut = new FileOutputStream(f);
+            wb.write(fileOut);
+            fileOut.close();
+        }
+        catch (java.io.FileNotFoundException ex) {
+            ex.printStackTrace();
+        }
+        catch (java.io.IOException ex) {
+            ex.printStackTrace();
+        }
+        
+    }
+    
+    /** test writing a file with large number of unique strings
+     *open resulting file in Excel to check results!*/
+    
+    public void test15375_2() throws Exception{
+        
+        
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+        
+        String tmp1 = null;
+        String tmp2 = null;
+        String tmp3 = null;
+        
+        for (int i = 0; i < 6000; i++) {
+            tmp1 = "Test1" + i;
+            tmp2 = "Test2" + i;
+            tmp3 = "Test3" + i;
+            
+            HSSFRow row = sheet.createRow((short)i);
+            
+            HSSFCell cell = row.createCell((short)0);
+            cell.setCellValue(tmp1);
+            cell = row.createCell((short)1);
+            cell.setCellValue(tmp2);
+            cell = row.createCell((short)2);
+            cell.setCellValue(tmp3);
+        }
+        File f = File.createTempFile("test15375-2",".xls");
+        FileOutputStream fileOut = new FileOutputStream(f);
+        wb.write(fileOut);
+        fileOut.close();
+    }
+    /** another test for the number of unique strings issue
+     *test opening the resulting file in Excel*/
+    public void test22568() {
+        int r=2000;int c=3;
+        
+        HSSFWorkbook wb = new HSSFWorkbook() ;
+        HSSFSheet sheet = wb.createSheet("ExcelTest") ;
+        
+        int col_cnt=0, rw_cnt=0 ;
+        
+        col_cnt = c;
+        rw_cnt = r;
+        
+        HSSFRow rw = null ;
+        HSSFCell cell =null;
+        rw = sheet.createRow((short)0) ;
+        //Header row
+        for(short j=0; j<col_cnt; j++){
+            cell = rw.createCell((short)j) ;
+            cell.setCellValue("Col " + (j+1)) ;
+        }
+        
+        for(int i=1; i<rw_cnt; i++){
+            rw = sheet.createRow((short)i) ;
+            for(short j=0; j<col_cnt; j++){
+                cell = rw.createCell((short)j) ;
+                cell.setCellValue("Row:" + (i+1) + ",Column:" +
+                (j+1)) ;
+            }
+        }
+        
+        sheet.setDefaultColumnWidth((short) 18) ;
+        
+        try {
+            File f = File.createTempFile("test22568",".xls");
+            FileOutputStream out = new FileOutputStream(f) ;
+            wb.write(out) ;
+            
+            out.close() ;
+        }
+        catch(java.io.IOException io_Excp) {
+            System.out.println(io_Excp.getMessage()) ;
+        }
+    }
+    
+    /**Double byte strings*/
+    public void test15556() throws java.io.IOException {
+        
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/15556.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        HSSFSheet sheet = wb.getSheetAt(0);
+        HSSFRow row = sheet.getRow(45);
+        this.assertTrue("Read row fine!" , true);
+        
+    }
+    
+    /*Double byte strings */
+    public void test22742() throws java.io.IOException {
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/22742.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);        this.assertTrue("Read workbook!" , true);
+        
+    }
+    /*Double byte strings */
+    public void test12561_1() throws java.io.IOException {
+        
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/12561-1.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        this.assertTrue("Read workbook!" , true);
+        
+    }
+    /*Double byte strings */
+    public void test12561_2() throws java.io.IOException {
+        
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/12561-2.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        this.assertTrue("Read workbook!" , true);
+        
+    }
+    /*Double byte strings
+     File supplied by jubeson*/
+    public void test12843_1() throws java.io.IOException {
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/12843-1.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        this.assertTrue("Read workbook!" , true);
+    }
+    
+    /*Double byte strings
+     File supplied by Paul Chung*/
+    public void test12843_2() throws java.io.IOException {
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/12843-2.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        this.assertTrue("Read workbook!" , true);
+    }
+    
+    /** Reference to Name*/
+    public void test13224() throws java.io.IOException {
+        String filename = System.getProperty("HSSF.testdata.path");
+        filename=filename+"/13224.xls";
+        FileInputStream in = new FileInputStream(filename);
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        this.assertTrue("Read workbook!" , true);
+        
+    }
+    
+    /** Illegal argument exception - cannot store duplicate value in Map*/
+    public void test19599() throws java.io.IOException {
+        String filename = System.getProperty("HSSF.testdata.path");
+        FileInputStream in = new FileInputStream(filename+"/19599-1.xls");
+        HSSFWorkbook wb = new HSSFWorkbook(in);
+        in = new FileInputStream(filename+"/19599-2.xls");
+        wb = new HSSFWorkbook(in);
+        this.assertTrue("Read workbook, No exceptions" , true);
+        
+    }
+    
+    public void test24215() throws Exception {
+        String filename = System.getProperty("HSSF.testdata.path");
+        HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(filename+"/24215.xls"));
+        
+        for (int sheetIndex = 0; sheetIndex < wb.getNumberOfSheets();sheetIndex++) {
+            HSSFSheet sheet = wb.getSheetAt(sheetIndex);
+            int rows = sheet.getLastRowNum();
+            
+            for (int rowIndex = 0; rowIndex < rows; rowIndex++) {
+                HSSFRow row = sheet.getRow(rowIndex);
+                int cells = row.getLastCellNum();
+                
+                for (short cellIndex = 0; cellIndex < cells; cellIndex++) {
+                    HSSFCell cell  = row.getCell(cellIndex);
+                }
+            }
+        }
+        assertTrue("No Exceptions while reading file", true);
+    }
+    
+     public void test18800() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        HSSFWorkbook book = new HSSFWorkbook();
+        book.createSheet("TEST");
+        HSSFSheet sheet = book.cloneSheet(0);
+        book.setSheetName(1,"CLONE");
+        sheet.createRow(0).createCell((short)0).setCellValue("Test");
+        book.write(out);
+        
+        book = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
+        sheet = book.getSheet("CLONE");
+        HSSFRow row = sheet.getRow(0);
+        HSSFCell cell = row.getCell((short)0);
+        System.out.println(cell.getStringCellValue());
+    }
+}
+
+
+
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java
index 0525161..6ccf29e 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java
@@ -79,7 +79,7 @@
 			s.addMergedRegion(new Region((short)0,(short)0,(short)1,(short)1));
 			b.cloneSheet(0);
 		}
-		catch(Exception e){fail(e.getMessage());}
+		catch(Exception e){e.printStackTrace();fail(e.getMessage());}
 	}
 
 }
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java
index 450c732..5b75303 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java
@@ -744,7 +744,7 @@
     }
     
     public void testSheetFunctions()
-        throws java.io.IOException
+        throws IOException
     {
         String filename = System.getProperty("HSSF.testdata.path");
 
@@ -818,7 +818,7 @@
     }
     
     public void testStringFormulas()
-        throws java.io.IOException
+        throws IOException
     {
         String readFilename = System.getProperty("HSSF.testdata.path");
 
@@ -852,7 +852,7 @@
     
     
     public void testLogicalFormulas()
-        throws java.io.IOException
+        throws IOException
     {
 
             File file = File.createTempFile("testLogicalFormula",".xls");
@@ -880,7 +880,7 @@
     }
     
     public void testDateFormulas()
-        throws java.io.IOException
+        throws IOException
     {
         String readFilename = System.getProperty("HSSF.testdata.path");
 
@@ -918,7 +918,7 @@
 
     
     public void testIfFormulas()
-        throws java.io.IOException
+        throws IOException
     {
         String readFilename = System.getProperty("HSSF.testdata.path");
 
@@ -1008,7 +1008,7 @@
     }
 
 	public void testSumIf()
-		throws java.io.IOException
+		throws IOException
 	{
 		String readFilename = System.getProperty("HSSF.testdata.path");		
 		String function ="SUMIF(A1:A5,\">4000\",B1:B5)";
@@ -1060,6 +1060,66 @@
 		assertTrue("sumif == 0 bytes", file.length() > 0);
 	}
     
+    public void testSquareMacro() throws IOException {
+        File dir = new File(System.getProperty("HSSF.testdata.path"));
+        File xls = new File(dir, "SquareMacro.xls");
+        FileInputStream in = new FileInputStream(xls);
+        HSSFWorkbook w;
+        try {
+            w = new HSSFWorkbook(in);
+        } finally {
+            in.close();
+        }
+        HSSFSheet s0 = w.getSheetAt(0);
+        HSSFRow[] r = {s0.getRow(0), s0.getRow(1)};
+
+        HSSFCell a1 = r[0].getCell((short) 0);
+        assertEquals("square(1)", a1.getCellFormula());
+        assertEquals(1d, a1.getNumericCellValue(), 1e-9);
+
+        HSSFCell a2 = r[1].getCell((short) 0);
+        assertEquals("square(2)", a2.getCellFormula());
+        assertEquals(4d, a2.getNumericCellValue(), 1e-9);
+
+        HSSFCell b1 = r[0].getCell((short) 1);
+        assertEquals("IF(TRUE,square(1))", b1.getCellFormula());
+        assertEquals(1d, b1.getNumericCellValue(), 1e-9);
+
+        HSSFCell b2 = r[1].getCell((short) 1);
+        assertEquals("IF(TRUE,square(2))", b2.getCellFormula());
+        assertEquals(4d, b2.getNumericCellValue(), 1e-9);
+
+        HSSFCell c1 = r[0].getCell((short) 2);
+        assertEquals("square(square(1))", c1.getCellFormula());
+        assertEquals(1d, c1.getNumericCellValue(), 1e-9);
+
+        HSSFCell c2 = r[1].getCell((short) 2);
+        assertEquals("square(square(2))", c2.getCellFormula());
+        assertEquals(16d, c2.getNumericCellValue(), 1e-9);
+
+        HSSFCell d1 = r[0].getCell((short) 3);
+        assertEquals("square(one())", d1.getCellFormula());
+        assertEquals(1d, d1.getNumericCellValue(), 1e-9);
+
+        HSSFCell d2 = r[1].getCell((short) 3);
+        assertEquals("square(two())", d2.getCellFormula());
+        assertEquals(4d, d2.getNumericCellValue(), 1e-9);
+    }
+
+    public void testStringFormulaRead() throws IOException {
+        File dir = new File(System.getProperty("HSSF.testdata.path"));
+        File xls = new File(dir, "StringFormulas.xls");
+        FileInputStream in = new FileInputStream(xls);
+        HSSFWorkbook w;
+        try {
+            w = new HSSFWorkbook(in);
+        } finally {
+            in.close();
+        }
+        HSSFCell c = w.getSheetAt(0).getRow(0).getCell((short)0);
+        assertEquals("String Cell value","XYZ",c.getStringCellValue());
+    }
+    
     public static void main(String [] args) {
         System.out
         .println("Testing org.apache.poi.hssf.usermodel.TestFormulas");
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
index dcb114c..1ad9759 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
@@ -116,7 +116,7 @@
 
             c=r.createCell((short)2);
             //c.setCellType(HSSFCell.CELL_TYPE_ERROR);
-            c.setCellErrorValue((byte)1);
+            c.setCellErrorValue((byte)7);
 
 
             wb.write(out);
@@ -136,7 +136,7 @@
             c = r.getCell((short)1);
             assertTrue("boolean value 0,1 = 0",c.getErrorCellValue() == 0);
             c = r.getCell((short)2);
-            assertTrue("boolean value 0,2 = 1",c.getErrorCellValue() == 1);
+            assertTrue("boolean value 0,2 = 7",c.getErrorCellValue() == 7);
 
             in.close();
     }
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHeaderFooter.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHeaderFooter.java
new file mode 100644
index 0000000..9374c17
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHeaderFooter.java
@@ -0,0 +1,126 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+import org.apache.poi.hssf.usermodel.HSSFHeader;
+import org.apache.poi.hssf.usermodel.HSSFFooter;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+/**
+ * Tests row shifting capabilities.
+ *
+ *
+ * @author Shawn Laubach (slaubach at apache dot com)
+ */
+
+public class TestHSSFHeaderFooter extends TestCase {
+
+    /**
+     * Constructor for TestHeaderFooter.
+     * @param arg0
+     */
+    public TestHSSFHeaderFooter(String arg0) {
+	super(arg0);
+    }
+
+    /**
+     * Tests that get header retreives the proper values.
+     *
+     * @author Shawn Laubach (slaubach at apache dot org)
+     */
+    public void testRetrieveCorrectHeader() throws Exception
+    {
+        // Read initial file in
+        String filename = System.getProperty( "HSSF.testdata.path" );
+        filename = filename + "/EmbeddedChartHeaderTest.xls";
+        FileInputStream fin = new FileInputStream( filename );
+        HSSFWorkbook wb = new HSSFWorkbook( fin );
+        fin.close();
+        HSSFSheet s = wb.getSheetAt( 0 );
+	HSSFHeader head = s.getHeader();
+
+	assertEquals("Top Left", head.getLeft());
+	assertEquals("Top Center", head.getCenter());
+	assertEquals("Top Right", head.getRight());
+    }
+
+    /**
+     * Tests that get header retreives the proper values.
+     *
+     * @author Shawn Laubach (slaubach at apache dot org)
+     */
+    public void testRetrieveCorrectFooter() throws Exception
+    {
+        // Read initial file in
+        String filename = System.getProperty( "HSSF.testdata.path" );
+        filename = filename + "/EmbeddedChartHeaderTest.xls";
+        FileInputStream fin = new FileInputStream( filename );
+        HSSFWorkbook wb = new HSSFWorkbook( fin );
+        fin.close();
+        HSSFSheet s = wb.getSheetAt( 0 );
+	HSSFFooter foot = s.getFooter();
+
+	assertEquals("Bottom Left", foot.getLeft());
+	assertEquals("Bottom Center", foot.getCenter());
+	assertEquals("Bottom Right", foot.getRight());
+    }
+}
+
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
index 8e58186..3779f3a 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
@@ -54,18 +54,21 @@
 
 package org.apache.poi.hssf.usermodel;
 
-import junit.framework.TestCase;
-import org.apache.poi.hssf.model.Sheet;
-import org.apache.poi.hssf.record.HCenterRecord;
-import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.WSBoolRecord;
-import org.apache.poi.hssf.record.SCLRecord;
-import org.apache.poi.hssf.record.WindowTwoRecord;
-
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.model.Sheet;
+import org.apache.poi.hssf.record.HCenterRecord;
+import org.apache.poi.hssf.record.ProtectRecord;
+import org.apache.poi.hssf.record.SCLRecord;
+import org.apache.poi.hssf.record.VCenterRecord;
+import org.apache.poi.hssf.record.WSBoolRecord;
+import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.util.Region;
+
 /**
  * Tests HSSFSheet.  This test case is very incomplete at the moment.
  *
@@ -236,6 +239,27 @@
         cell.setCellValue("Difference Check");
         assertEquals(cloned.getRow((short)0).getCell((short)0).getStringCellValue(), "clone_test");
     }
+    
+	/**
+	 * Test that the ProtectRecord is included when creating or cloning a sheet
+	 */
+	public void testProtect() {
+		HSSFWorkbook workbook = new HSSFWorkbook();
+		HSSFSheet hssfSheet = workbook.createSheet();
+		Sheet sheet = hssfSheet.getSheet();
+		ProtectRecord protect = sheet.getProtect();
+   	
+		assertFalse(protect.getProtect());
+
+		// This will tell us that cloneSheet, and by extension,
+		// the list forms of createSheet leave us with an accessible
+		// ProtectRecord.
+		hssfSheet.setProtect(true);
+		Sheet cloned = sheet.cloneSheet();
+		assertNotNull(cloned.getProtect());
+		assertTrue(hssfSheet.getProtect());
+	}
+
 
     public void testZoom()
             throws Exception
@@ -254,4 +278,111 @@
         assertTrue(sclLoc == window2Loc + 1);
 
     }
+    
+
+	/**
+	 * When removing one merged region, it would break
+	 *
+	 */    
+	public void testRemoveMerged() {
+		HSSFWorkbook wb = new HSSFWorkbook();
+		HSSFSheet sheet = wb.createSheet();
+		Region region = new Region(0, (short)0, 1, (short)1);   	
+		sheet.addMergedRegion(region);
+		region = new Region(1, (short)0, 2, (short)1);
+		sheet.addMergedRegion(region);
+		
+    	sheet.removeMergedRegion(0);
+    	
+    	region = sheet.getMergedRegionAt(0);
+    	assertEquals("Left over region should be starting at row 1", 1, region.getRowFrom());
+    	
+    	sheet.removeMergedRegion(0);
+    	
+		assertEquals("there should be no merged regions left!", 0, sheet.getNumMergedRegions());
+		
+		//an, add, remove, get(0) would null pointer
+		sheet.addMergedRegion(region);
+		assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
+		sheet.removeMergedRegion(0);
+		assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
+		//add it again!
+		region.setRowTo(4);
+    	
+		sheet.addMergedRegion(region);
+		assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
+		
+		//should exist now!
+		assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
+		region = sheet.getMergedRegionAt(0);
+		assertEquals("the merged row to doesnt match the one we put in ", 4, region.getRowTo());
+    	
+    }
+
+	public void testShiftMerged() {
+		HSSFWorkbook wb = new HSSFWorkbook();
+		HSSFSheet sheet = wb.createSheet();
+		HSSFRow row = sheet.createRow(0);
+		HSSFCell cell = row.createCell((short)0);
+		cell.setCellValue("first row, first cell");
+		
+		row = sheet.createRow(1);
+		cell = row.createCell((short)1);
+		cell.setCellValue("second row, second cell");
+		
+		Region region = new Region(1, (short)0, 1, (short)1);   	
+		sheet.addMergedRegion(region);
+		
+		sheet.shiftRows(1, 1, 1);
+		
+		region = sheet.getMergedRegionAt(0);
+		assertEquals("Merged region not moved over to row 2", 2, region.getRowFrom());
+		
+	}
+
+    /**
+     * Tests the display of gridlines, formulas, and rowcolheadings.
+     * @author Shawn Laubach (slaubach at apache dot org)
+     */
+    public void testDisplayOptions() throws Exception {
+	HSSFWorkbook wb = new HSSFWorkbook();
+	HSSFSheet sheet = wb.createSheet();
+	
+        File tempFile = File.createTempFile("display", "test.xls");
+        FileOutputStream stream = new FileOutputStream(tempFile);
+        wb.write(stream);
+        stream.close();
+
+        FileInputStream readStream = new FileInputStream(tempFile);
+        wb = new HSSFWorkbook(readStream);
+        sheet = wb.getSheetAt(0);
+	readStream.close();
+
+	assertEquals(sheet.isDisplayGridlines(), true);
+	assertEquals(sheet.isDisplayRowColHeadings(), true);
+	assertEquals(sheet.isDisplayFormulas(), false);
+
+	sheet.setDisplayGridlines(false);
+	sheet.setDisplayRowColHeadings(false);
+	sheet.setDisplayFormulas(true);
+
+        tempFile = File.createTempFile("display", "test.xls");
+        stream = new FileOutputStream(tempFile);
+        wb.write(stream);
+        stream.close();
+
+        readStream = new FileInputStream(tempFile);
+        wb = new HSSFWorkbook(readStream);
+        sheet = wb.getSheetAt(0);
+	readStream.close();
+
+
+	assertEquals(sheet.isDisplayGridlines(), false);
+	assertEquals(sheet.isDisplayRowColHeadings(), false);
+	assertEquals(sheet.isDisplayFormulas(), true);
+    }
+
+	public static void main(java.lang.String[] args) {
+		 junit.textui.TestRunner.run(TestHSSFSheet.class);
+	}    
 }
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
index a48fb68..e91003e 100755
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
@@ -175,5 +175,18 @@
 	s.createRow(3).createCell((short)0).setCellValue("TEST2");
 	s.shiftRows(0,4,1);
     }
+
+    /**
+     * Tests when shifting the first row.
+     *
+     * @author Toshiaki Kamoshida (kamoshida.toshiaki at future dot co dot jp)
+     */
+    public void testShiftRow0(){
+	HSSFWorkbook b = new HSSFWorkbook();
+	HSSFSheet s    = b.createSheet();
+	s.createRow(0).createCell((short)0).setCellValue("TEST1");
+	s.createRow(3).createCell((short)0).setCellValue("TEST2");
+	s.shiftRows(0,4,1);
+    }
 }
 
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java
index c8bc7b8..6b5fd97 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestWorkbook.java
@@ -304,6 +304,8 @@
         assertEquals(1.25,cell.getNumericCellValue(), 1e-10);
 
 	assertEquals(format.getFormat(df), "0.0");
+
+	assertEquals(format, workbook.createDataFormat());
 	
         stream.close();
     }
diff --git a/src/testcases/org/apache/poi/poifs/filesystem/SlowInputStream.java b/src/testcases/org/apache/poi/poifs/filesystem/SlowInputStream.java
new file mode 100644
index 0000000..efd8c56
--- /dev/null
+++ b/src/testcases/org/apache/poi/poifs/filesystem/SlowInputStream.java
@@ -0,0 +1,76 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache POI" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache POI", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.poifs.filesystem;
+
+import java.io.*;
+import java.util.Random;
+
+/**
+ * Returns a random amount of requested data. Used to check conformance with
+ * InputStream API contracts.
+ */
+public class SlowInputStream extends FilterInputStream
+{
+    private Random r = new Random(0);
+    
+    public SlowInputStream(InputStream in) {
+        super(in);
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        return super.read(b, off, r.nextInt(len) + 1);
+    }
+}
diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestDocument.java b/src/testcases/org/apache/poi/poifs/filesystem/TestDocument.java
index af417fa..2e9fd02 100644
--- a/src/testcases/org/apache/poi/poifs/filesystem/TestDocument.java
+++ b/src/testcases/org/apache/poi/poifs/filesystem/TestDocument.java
@@ -107,7 +107,7 @@
         {
             array[ j ] = ( byte ) j;
         }
-        document = new POIFSDocument("foo", new ByteArrayInputStream(array));
+        document = new POIFSDocument("foo", new SlowInputStream(new ByteArrayInputStream(array)));
         checkDocument(document, array);
 
         // verify correct number of blocks get created for document
diff --git a/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java b/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java
index 74a3807..e677a79 100644
--- a/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java
+++ b/src/testcases/org/apache/poi/poifs/property/TestDocumentProperty.java
@@ -150,6 +150,7 @@
             ( byte ) 0xFE, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF,
             ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
             ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+
             ( byte ) 0x57, ( byte ) 0x00, ( byte ) 0x6F, ( byte ) 0x00,
             ( byte ) 0x72, ( byte ) 0x00, ( byte ) 0x6B, ( byte ) 0x00,
             ( byte ) 0x62, ( byte ) 0x00, ( byte ) 0x6F, ( byte ) 0x00,
@@ -182,6 +183,7 @@
             ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
             ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00,
             ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+
             ( byte ) 0x05, ( byte ) 0x00, ( byte ) 0x53, ( byte ) 0x00,
             ( byte ) 0x75, ( byte ) 0x00, ( byte ) 0x6D, ( byte ) 0x00,
             ( byte ) 0x6D, ( byte ) 0x00, ( byte ) 0x61, ( byte ) 0x00,
@@ -214,6 +216,7 @@
             ( byte ) 0x08, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
             ( byte ) 0x00, ( byte ) 0x10, ( byte ) 0x00, ( byte ) 0x00,
             ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
+
             ( byte ) 0x05, ( byte ) 0x00, ( byte ) 0x44, ( byte ) 0x00,
             ( byte ) 0x6F, ( byte ) 0x00, ( byte ) 0x63, ( byte ) 0x00,
             ( byte ) 0x75, ( byte ) 0x00, ( byte ) 0x6D, ( byte ) 0x00,
diff --git a/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java b/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java
index 430c521..8357a0a 100644
--- a/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java
+++ b/src/testcases/org/apache/poi/poifs/property/TestRootProperty.java
@@ -217,11 +217,11 @@
             ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00
         };
 
-        verifyReadingProperty(0, input, 0, "Root Entry");
+        verifyReadingProperty(0, input, 0, "Root Entry", "{00020820-0000-0000-C000-000000000046}");
     }
 
     private void verifyReadingProperty(int index, byte [] input, int offset,
-                                       String name)
+                                       String name, String sClsId)
         throws IOException
     {
         RootProperty          property = new RootProperty(index, input,
@@ -242,6 +242,7 @@
         assertEquals(index, property.getIndex());
         assertEquals(name, property.getName());
         assertTrue(!property.getChildren().hasNext());
+        assertEquals(property.getStorageClsid().toString(), sClsId);
     }
 
     /**
diff --git a/src/testcases/org/apache/poi/util/TestHexDump.java b/src/testcases/org/apache/poi/util/TestHexDump.java
index 7a86cff..c8e3ca9 100644
--- a/src/testcases/org/apache/poi/util/TestHexDump.java
+++ b/src/testcases/org/apache/poi/util/TestHexDump.java
@@ -314,6 +314,9 @@
 
             // as expected
         }
+
+        // verify proper behaviour with a 0 length dump on 0 length dataset
+        HexDump.dump(new byte[0], 0, new ByteArrayOutputStream(), 0, 0);
     }
 
     public void testToHex()