summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author(no author) <(no author)@unknown>2002-06-01 02:25:53 +0000
committer(no author) <(no author)@unknown>2002-06-01 02:25:53 +0000
commitf6b247a19c3abfe183ebdf63e27a88911be4110e (patch)
treeb278ab43639e1c7b077397539e76b2524139098d
parent899521e1d61088cd280a0be0167f6c7cb5f2178a (diff)
downloadpoi-f6b247a19c3abfe183ebdf63e27a88911be4110e.tar.gz
poi-f6b247a19c3abfe183ebdf63e27a88911be4110e.zip
This commit was manufactured by cvs2svn to create tag
'REL_1_5_BRANCH_MERGE3'. git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/tags/REL_1_5_BRANCH_MERGE3@352659 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/documentation/images/logoRaPiGmbH1.pngbin0 -> 5976 bytes
-rw-r--r--src/documentation/images/logoRaPiGmbH2.pngbin0 -> 6001 bytes
-rw-r--r--src/documentation/images/logoRaPiGmbH3.pngbin0 -> 3594 bytes
-rw-r--r--src/documentation/images/logoRaPiGmbH4.pngbin0 -> 2658 bytes
-rw-r--r--src/documentation/images/logoRaPiGmbH5.pngbin0 -> 7423 bytes
-rw-r--r--src/documentation/images/logoRaPiGmbH6.pngbin0 -> 6640 bytes
-rw-r--r--src/documentation/images/logoRaPiGmbH7.pngbin0 -> 9630 bytes
-rw-r--r--src/documentation/xdocs/book.xml1
-rw-r--r--src/documentation/xdocs/branching.xml97
-rwxr-xr-xsrc/documentation/xdocs/historyandfuture.xml2
-rw-r--r--src/documentation/xdocs/news/logocontest.xml177
-rw-r--r--src/java/org/apache/poi/hssf/dev/BiffViewer.java8
-rw-r--r--src/java/org/apache/poi/hssf/model/Workbook.java17
-rw-r--r--src/java/org/apache/poi/hssf/record/ContinueRecord.java7
-rw-r--r--src/java/org/apache/poi/hssf/record/RecordProcessor.java202
-rw-r--r--src/java/org/apache/poi/hssf/record/RowRecord.java4
-rw-r--r--src/java/org/apache/poi/hssf/record/SSTDeserializer.java357
-rw-r--r--src/java/org/apache/poi/hssf/record/SSTRecord.java901
-rw-r--r--src/java/org/apache/poi/hssf/record/SSTSerializer.java356
-rw-r--r--src/java/org/apache/poi/hssf/record/UnicodeString.java61
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFRow.java28
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java159
-rw-r--r--src/java/org/apache/poi/util/LittleEndian.java13
-rw-r--r--src/testcases/org/apache/poi/hssf/data/duprich1.xlsbin0 -> 21504 bytes
-rw-r--r--src/testcases/org/apache/poi/hssf/data/duprich2.xlsbin0 -> 13824 bytes
-rw-r--r--src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java617
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java39
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java10
-rw-r--r--src/testcases/org/apache/poi/util/TestLittleEndian.java6
29 files changed, 1741 insertions, 1321 deletions
diff --git a/src/documentation/images/logoRaPiGmbH1.png b/src/documentation/images/logoRaPiGmbH1.png
new file mode 100644
index 0000000000..3bef8bb6e9
--- /dev/null
+++ b/src/documentation/images/logoRaPiGmbH1.png
Binary files differ
diff --git a/src/documentation/images/logoRaPiGmbH2.png b/src/documentation/images/logoRaPiGmbH2.png
new file mode 100644
index 0000000000..d842217557
--- /dev/null
+++ b/src/documentation/images/logoRaPiGmbH2.png
Binary files differ
diff --git a/src/documentation/images/logoRaPiGmbH3.png b/src/documentation/images/logoRaPiGmbH3.png
new file mode 100644
index 0000000000..0419155689
--- /dev/null
+++ b/src/documentation/images/logoRaPiGmbH3.png
Binary files differ
diff --git a/src/documentation/images/logoRaPiGmbH4.png b/src/documentation/images/logoRaPiGmbH4.png
new file mode 100644
index 0000000000..3b8d44d6e4
--- /dev/null
+++ b/src/documentation/images/logoRaPiGmbH4.png
Binary files differ
diff --git a/src/documentation/images/logoRaPiGmbH5.png b/src/documentation/images/logoRaPiGmbH5.png
new file mode 100644
index 0000000000..f96ef9ef91
--- /dev/null
+++ b/src/documentation/images/logoRaPiGmbH5.png
Binary files differ
diff --git a/src/documentation/images/logoRaPiGmbH6.png b/src/documentation/images/logoRaPiGmbH6.png
new file mode 100644
index 0000000000..53ee5e9eab
--- /dev/null
+++ b/src/documentation/images/logoRaPiGmbH6.png
Binary files differ
diff --git a/src/documentation/images/logoRaPiGmbH7.png b/src/documentation/images/logoRaPiGmbH7.png
new file mode 100644
index 0000000000..498499d9df
--- /dev/null
+++ b/src/documentation/images/logoRaPiGmbH7.png
Binary files differ
diff --git a/src/documentation/xdocs/book.xml b/src/documentation/xdocs/book.xml
index e5ab10098e..1d4b945e38 100644
--- a/src/documentation/xdocs/book.xml
+++ b/src/documentation/xdocs/book.xml
@@ -39,6 +39,7 @@
<menu label="Get Involved">
<menu-item label="Contributing" href="contrib.html"/>
+ <menu-item label="Branching" href="branching.html"/>
<menu-item label="Bug Database" href="http://nagoya.apache.org/bugzilla/buglist.cgi?product=POI"/>
<menu-item label="CVS" href="http://jakarta.apache.org/site/cvsindex.html"/>
<menu-item label="Mail Lists" href="http://jakarta.apache.org/site/mail.html"/>
diff --git a/src/documentation/xdocs/branching.xml b/src/documentation/xdocs/branching.xml
new file mode 100644
index 0000000000..4246d83443
--- /dev/null
+++ b/src/documentation/xdocs/branching.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "./dtd/document-v11.dtd">
+
+<document>
+
+ <header>
+ <title>Branching</title>
+ <authors>
+ <person id="GJS" name="Glen Stampoultzis" email="glens@apache.org"/>
+ </authors>
+ </header>
+
+ <body>
+ <section title="Branching Conventions">
+ <p>
+ Branches are tagged in the following way:
+ </p>
+ <ul>
+ <li>REL_1_5_BRANCH</li>
+ <li>REL_2_0_BRANCH</li>
+ </ul>
+ <p>
+ Merge points should be tagged as follows:
+ </p>
+ <ul>
+ <li>REL_1_5_BRANCH_MERGE1</li>
+ <li>REL_1_5_BRANCH_MERGE2</li>
+ <li>etc...</li>
+ </ul>
+ <p>
+ Releases should be tagged as:
+ </p>
+ <ul>
+ <li>REL_1_5</li>
+ <li>REL_1_5_1</li>
+ <li>REL_1_5_2</li>
+ <li>etc...</li>
+ </ul>
+
+ </section>
+ <section title="Branching Advise">
+ <p>
+ Don't forget which branch you are currently on. This is critically
+ important. Committing stuff to the wrong branch causes all sorts of
+ headaches. Best to name your checkout after the branch you are on.
+ </p>
+ </section>
+ <section title="Who Manages Branching?">
+ <p>
+ All branching is currently managed by Glen Stampoultzis. If you wish
+ to create your own branch please let him know. Merging is also
+ handled by Glen. Just pop him a mail if you feel it's necessary to
+ create a branch or perform a merge.
+ </p>
+ <p>
+ The reason to go through a single point for branching is that it can be
+ an easy thing to get wrong. Having a single person managing branches
+ means there is less chance of getting getting our wires crossed with this
+ difficult area of CVS.
+ </p>
+ </section>
+ <section title="Currently Active Branches">
+ <p>
+ The following branches are currently active:
+ </p>
+ <table>
+ <tr>
+ <th>
+ <b>Branch</b>
+ </th>
+ <th>
+ <b>Description</b>
+ </th>
+ </tr>
+ <tr>
+ <td>
+ HEAD
+ </td>
+ <td>
+ This is the trunk and is always active. Currently it is being used to continue development
+ of the 2.0 release.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ REL_1_5_BRANCH
+ </td>
+ <td>
+ All bug fixes not specifically relevant to the 2.0 work should be placed in this branch.
+ From here they will merged back to the trunk and the merge point marked.
+ </td>
+ </tr>
+ </table>
+ </section>
+ </body>
+
+</document> \ No newline at end of file
diff --git a/src/documentation/xdocs/historyandfuture.xml b/src/documentation/xdocs/historyandfuture.xml
index 3855480f69..741f6aad5c 100755
--- a/src/documentation/xdocs/historyandfuture.xml
+++ b/src/documentation/xdocs/historyandfuture.xml
@@ -3,7 +3,7 @@
<document>
<header>
- <title></title>
+ <title>Project History</title>
<authors>
<person id="AO" name="Andrew C. Oliver" email="acoliver@apache.org"/>
</authors>
diff --git a/src/documentation/xdocs/news/logocontest.xml b/src/documentation/xdocs/news/logocontest.xml
index 9c45d06c66..9d9179c865 100644
--- a/src/documentation/xdocs/news/logocontest.xml
+++ b/src/documentation/xdocs/news/logocontest.xml
@@ -2,114 +2,139 @@
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "document-v11.dtd">
<document>
- <header>
- <title></title>
- <authors>
- <person id="AO" name="Andrew C. Oliver" email="acoliver@apache.org"/>
- </authors>
- </header>
-
- <body>
- <section title="POI logos">
- <p>
- Here are the current logo submissions. Thanks to the artists!
- </p>
- <section title="Michael Mosmann">
- <p>
- <img src="images/logoMichaelMosmann.png"/>
+ <header>
+ <title></title>
+ <authors>
+ <person id="AO" name="Andrew C. Oliver" email="acoliver@apache.org"/>
+ <person id="GS" name="Glen Stampoultzis" email="glens@apache.org"/>
+ </authors>
+ </header>
+
+ <body>
+ <section title="POI logos">
+ <p>
+ Here are the current logo submissions. Thanks to the artists!
+ </p>
+ <section title="Michael Mosmann">
+ <p>
+ <img src="images/logoMichaelMosmann.png"/>
</p>
- </section>
- <section title="Loïc Lefèvre">
+ </section>
+ <section title="Loïc Lefèvre">
<p>
- <img src="images/logoLoicLefevre.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoLoicLefevre2.png"/>
+ <img src="images/logoLoicLefevre.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoLoicLefevre2.png"/>
</p>
- </section>
- <section title="Glen Stampoultzis">
+ </section>
+ <section title="Glen Stampoultzis">
<p>
- <img src="images/logoGlenStampoutlzis.png"/>
+ <img src="images/logoGlenStampoutlzis.png"/>
</p>
- </section>
- <section title="Marcus Gustafsson">
+ </section>
+ <section title="Marcus Gustafsson">
<p>
- <img src="images/logoGustafsson1.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoGustafsson2.png"/>
+ <img src="images/logoGustafsson1.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoGustafsson2.png"/>
</p>
- </section>
- <section title="Adrianus Handoyo">
+ </section>
+ <section title="Adrianus Handoyo">
<p>
- <img src="images/logoAdria1.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoAdria2.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoAdria3.png"/>
+ <img src="images/logoAdria1.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoAdria2.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoAdria3.png"/>
</p>
- </section>
- <section title="RussellBeattie">
+ </section>
+ <section title="RussellBeattie">
<p>
- <img src="images/logoRussellBeattie1.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoRussellBeattie2.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoRussellBeattie3.png"/>
+ <img src="images/logoRussellBeattie1.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoRussellBeattie2.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoRussellBeattie3.png"/>
</p>
<p>
- <img src="images/logoRussellBeattie4.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoRussellBeattie5.png"/>
+ <img src="images/logoRussellBeattie4.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoRussellBeattie5.png"/>
</p>
- </section>
- <section title="Daniel Fernandez">
+ </section>
+ <section title="Daniel Fernandez">
<p>
- <img src="images/logoDanielFernandez.png"/>
+ <img src="images/logoDanielFernandez.png"/>
</p>
- </section>
- <section title="Andrew Clements">
+ </section>
+ <section title="Andrew Clements">
<p>
- <img src="images/logoAndrewClements.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoAndrewClements2.png"/>
+ <img src="images/logoAndrewClements.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoAndrewClements2.png"/>
</p>
- </section>
- <section title="Wendy Wise">
+ </section>
+ <section title="Wendy Wise">
<p>
- <img src="images/logoWendyWise.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoWendyWise2.png"/>
+ <img src="images/logoWendyWise.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoWendyWise2.png"/>
</p>
- </section>
- <section title="Nikhil Karmokar">
+ </section>
+ <section title="Nikhil Karmokar">
<p>
- <img src="images/logoKarmokar1.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoKarmokar1s.png"/>
+ <img src="images/logoKarmokar1.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoKarmokar1s.png"/>
</p>
<p>
- <img src="images/logoKarmokar2.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoKarmokar2s.png"/>
+ <img src="images/logoKarmokar2.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoKarmokar2s.png"/>
</p>
<p>
- <img src="images/logoKarmokar3.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoKarmokar3s.png"/>
+ <img src="images/logoKarmokar3.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoKarmokar3s.png"/>
</p>
<p>
- <img src="images/logoKarmokar4.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoKarmokar4s.png"/>
+ <img src="images/logoKarmokar4.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoKarmokar4s.png"/>
</p>
<p>
- <img src="images/logoKarmokar5.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoKarmokar5s.png"/>
+ <img src="images/logoKarmokar5.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoKarmokar5s.png"/>
</p>
<p>
- <img src="images/logoKarmokar6.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoKarmokar6s.png"/>
+ <img src="images/logoKarmokar6.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoKarmokar6s.png"/>
</p>
- </section>
- <section title="Lieven Janssen">
+ </section>
+ <section title="Lieven Janssen">
+ <p>
+ <img src="images/logoJanssen1.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoJanssen2.png"/>
+ </p>
+ </section>
+ <section title="RaPi GmbH">
<p>
- <img src="images/logoJanssen1.png"/>&nbsp;&nbsp;&nbsp;
- <img src="images/logoJanssen2.png"/>
+ Contact Person: Fancy at: fancy at my-feiqi.com
</p>
+ <p>
+ <img src="images/logoRaPiGmbH1.png"/>&nbsp;&nbsp;&nbsp;
+ <img src="images/logoRaPiGmbH2.png"/>
+ </p>
+ <p>
+ <img src="images/logoRaPiGmbH3.png"/>
+ </p>
+ <p>
+ <img src="images/logoRaPiGmbH4.png"/>
+ </p>
+ <p>
+ <img src="images/logoRaPiGmbH5.png"/>
+ </p>
+ <p>
+ <img src="images/logoRaPiGmbH6.png"/>
+ </p>
+ <p>
+ <img src="images/logoRaPiGmbH7.png"/>
+ </p>
+ </section>
+
</section>
-
- </section>
- </body>
- <footer>
- <legal>
- Copyright (c) @year@ The Apache Software Foundation All rights reserved.
- $Revision$ $Date$
- </legal>
- </footer>
+ </body>
+ <footer>
+ <legal>
+ Copyright (c) @year@ The Apache Software Foundation All rights reserved.
+ $Revision$ $Date$
+ </legal>
+ </footer>
</document>
diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
index 12267d23d8..7d366f1455 100644
--- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java
+++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
@@ -631,12 +631,12 @@ public class BiffViewer
retval = new LinkedDataRecord(rectype, size, data);
break;
- case FormulaRecord.sid:
- retval = new FormulaRecord(rectype, size, data);
- break;
+// case FormulaRecord.sid:
+// retval = new FormulaRecord(rectype, size, data);
+// break;
case SheetPropertiesRecord.sid:
- retval = new FormulaRecord(rectype, size, data);
+ retval = new SheetPropertiesRecord(rectype, size, data);
break;
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index d45153f9c9..55fe659322 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -643,26 +643,11 @@ public class Workbook
{
log.log(DEBUG, "Serializing Workbook with offsets");
- // ArrayList bytes = new ArrayList(records.size());
-// int arraysize = getSize(); // 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++)
{
-
- // byte[] rec = (( byte [] ) bytes.get(k));
- // System.arraycopy(rec, 0, data, offset + pos, rec.length);
- pos += (( Record ) records.get(k)).serialize(pos + offset,
- data); // rec.length;
+ pos += (( Record ) records.get(k)).serialize(pos + offset, data); // rec.length;
}
log.log(DEBUG, "Exiting serialize workbook");
return pos;
diff --git a/src/java/org/apache/poi/hssf/record/ContinueRecord.java b/src/java/org/apache/poi/hssf/record/ContinueRecord.java
index 2b67a62d40..5017ade922 100644
--- a/src/java/org/apache/poi/hssf/record/ContinueRecord.java
+++ b/src/java/org/apache/poi/hssf/record/ContinueRecord.java
@@ -161,9 +161,7 @@ public class ContinueRecord
// how many continue records do we need
// System.out.println("In ProcessContinue");
- int records =
- (data.length
- / 8214); // we've a 1 offset but we're also off by one due to rounding...so it balances out
+ int records = (data.length / 8214); // we've a 1 offset but we're also off by one due to rounding...so it balances out
int offset = 8214;
// System.out.println("we have "+records+" continue records to process");
@@ -174,8 +172,7 @@ public class ContinueRecord
for (int cr = 0; cr < records; cr++)
{
ContinueRecord contrec = new ContinueRecord();
- int arraysize = Math.min((8214 - 4),
- (data.length - offset));
+ int arraysize = Math.min((8214 - 4), (data.length - offset));
byte[] crdata = new byte[ arraysize ];
System.arraycopy(data, offset, crdata, 0, arraysize);
diff --git a/src/java/org/apache/poi/hssf/record/RecordProcessor.java b/src/java/org/apache/poi/hssf/record/RecordProcessor.java
new file mode 100644
index 0000000000..13bfc3a188
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/RecordProcessor.java
@@ -0,0 +1,202 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Process a single record. That is, an SST record or a continue record.
+ * Refactored from code originally in SSTRecord.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+class RecordProcessor
+{
+ private byte[] data;
+ private int recordOffset;
+ private int available;
+ private SSTRecordHeader sstRecordHeader;
+
+ public RecordProcessor( byte[] data, int available, int numStrings, int numUniqueStrings )
+ {
+ this.data = data;
+ this.available = available;
+ this.sstRecordHeader = new SSTRecordHeader(numStrings, numUniqueStrings);
+ }
+
+ public int getAvailable()
+ {
+ return available;
+ }
+
+ public void writeRecordHeader( int offset, int totalWritten, int recordLength, boolean first_record )
+ {
+ if ( first_record )
+ {
+ available -= 8;
+ recordOffset = sstRecordHeader.writeSSTHeader( data, recordOffset + offset + totalWritten, recordLength );
+ }
+ else
+ {
+ recordOffset = writeContinueHeader( data, recordOffset + offset + totalWritten, recordLength );
+ }
+ }
+
+ public byte[] writeStringRemainder( boolean lastStringCompleted, byte[] stringreminant, int offset, int totalWritten )
+ {
+ if ( !lastStringCompleted )
+ {
+ // write reminant -- it'll all fit neatly
+ System.arraycopy( stringreminant, 0, data, recordOffset + offset + totalWritten, stringreminant.length );
+ adjustPointers( stringreminant.length );
+ }
+ else
+ {
+ // write as much of the remnant as possible
+ System.arraycopy( stringreminant, 0, data, recordOffset + offset + totalWritten, available );
+ byte[] leftover = new byte[( stringreminant.length - available ) + LittleEndianConsts.BYTE_SIZE];
+
+ System.arraycopy( stringreminant, available, leftover, LittleEndianConsts.BYTE_SIZE, stringreminant.length - available );
+ leftover[0] = stringreminant[0];
+ stringreminant = leftover;
+ adjustPointers( available ); // Consume all available remaining space
+ }
+ return stringreminant;
+ }
+
+ public void writeWholeString( UnicodeString unistr, int offset, int totalWritten )
+ {
+ unistr.serialize( recordOffset + offset + totalWritten, data );
+ int rsize = unistr.getRecordSize();
+ adjustPointers( rsize );
+ }
+
+ public byte[] writePartString( UnicodeString unistr, int offset, int totalWritten )
+ {
+ byte[] stringReminant;
+ byte[] ucs = unistr.serialize();
+
+ System.arraycopy( ucs, 0, data, recordOffset + offset + totalWritten, available );
+ stringReminant = new byte[( ucs.length - available ) + LittleEndianConsts.BYTE_SIZE];
+ System.arraycopy( ucs, available, stringReminant, LittleEndianConsts.BYTE_SIZE, ucs.length - available );
+ stringReminant[0] = ucs[LittleEndianConsts.SHORT_SIZE];
+ available = 0;
+ return stringReminant;
+ }
+
+
+ private int writeContinueHeader( final byte[] data, final int pos,
+ final int recsize )
+ {
+ int offset = pos;
+
+ LittleEndian.putShort( data, offset, ContinueRecord.sid );
+ offset += LittleEndianConsts.SHORT_SIZE;
+ LittleEndian.putShort( data, offset, (short) ( recsize ) );
+ offset += LittleEndianConsts.SHORT_SIZE;
+ return offset - pos;
+ }
+
+
+ private void adjustPointers( int amount )
+ {
+ recordOffset += amount;
+ available -= amount;
+ }
+}
+
+class SSTRecordHeader
+{
+ int numStrings;
+ int numUniqueStrings;
+
+ /**
+ *
+ */
+ public SSTRecordHeader( int numStrings, int numUniqueStrings )
+ {
+ this.numStrings = numStrings;
+ this.numUniqueStrings = numUniqueStrings;
+ }
+
+ /**
+ * Writes out the SST record. This consists of the sid, the record size, the number of
+ * strings and the number of unique strings.
+ *
+ * @param data The data buffer to write the header to.
+ * @param bufferIndex The index into the data buffer where the header should be written.
+ * @param recSize The number of records written.
+ *
+ * @return The bufer of bytes modified.
+ */
+ public int writeSSTHeader( byte[] data, int bufferIndex, int recSize )
+ {
+ int offset = bufferIndex;
+
+ LittleEndian.putShort( data, offset, SSTRecord.sid );
+ offset += LittleEndianConsts.SHORT_SIZE;
+ LittleEndian.putShort( data, offset, (short) ( recSize ) );
+ offset += LittleEndianConsts.SHORT_SIZE;
+// LittleEndian.putInt( data, offset, getNumStrings() );
+ LittleEndian.putInt( data, offset, numStrings );
+ offset += LittleEndianConsts.INT_SIZE;
+// LittleEndian.putInt( data, offset, getNumUniqueStrings() );
+ LittleEndian.putInt( data, offset, numUniqueStrings );
+ offset += LittleEndianConsts.INT_SIZE;
+ return offset - bufferIndex;
+ }
+
+} \ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/record/RowRecord.java b/src/java/org/apache/poi/hssf/record/RowRecord.java
index 65b627ea55..fc29fcbd26 100644
--- a/src/java/org/apache/poi/hssf/record/RowRecord.java
+++ b/src/java/org/apache/poi/hssf/record/RowRecord.java
@@ -452,8 +452,8 @@ public class RowRecord
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, ( short ) 16);
LittleEndian.putShort(data, 4 + offset, getRowNumber());
- LittleEndian.putShort(data, 6 + offset, getFirstCol());
- LittleEndian.putShort(data, 8 + offset, getLastCol());
+ LittleEndian.putShort(data, 6 + offset, getFirstCol() == -1 ? (short)0 : getFirstCol());
+ LittleEndian.putShort(data, 8 + offset, getLastCol() == -1 ? (short)0 : getLastCol());
LittleEndian.putShort(data, 10 + offset, getHeight());
LittleEndian.putShort(data, 12 + offset, getOptimize());
LittleEndian.putShort(data, 14 + offset, field_6_reserved);
diff --git a/src/java/org/apache/poi/hssf/record/SSTDeserializer.java b/src/java/org/apache/poi/hssf/record/SSTDeserializer.java
new file mode 100644
index 0000000000..58b62c316a
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/SSTDeserializer.java
@@ -0,0 +1,357 @@
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.BinaryTree;
+import org.apache.poi.util.HexDump;
+
+import java.io.IOException;
+
+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 string we were working on before hitting the end of the current record. This string is NOT finished. */
+ private String unfinishedString;
+ /** this is the total length of the current string being handled */
+ private int totalLengthBytes;
+ /** this is the offset into a string field of the actual string data */
+ private int stringDataOffset;
+ /** this is true if the string uses wide characters */
+ private boolean wideChar;
+
+
+ public SSTDeserializer(BinaryTree strings)
+ {
+ this.strings = strings;
+ setExpectedChars( 0 );
+ unfinishedString = "";
+ totalLengthBytes = 0;
+ stringDataOffset = 0;
+ wideChar = false;
+ }
+
+ /**
+ * This is the starting point where strings are constructed. Note that
+ * strings may span across multiple continuations. Read the SST record
+ * carefully before beginning to hack.
+ */
+ public void manufactureStrings( final byte[] data, final int index,
+ short size )
+ {
+ int offset = index;
+
+ while ( offset < size )
+ {
+ int remaining = size - offset;
+
+ if ( ( remaining > 0 ) && ( remaining < LittleEndianConsts.SHORT_SIZE ) )
+ {
+ throw new RecordFormatException( "Cannot get length of the last string in SSTRecord" );
+ }
+ if ( remaining == LittleEndianConsts.SHORT_SIZE )
+ {
+ setExpectedChars( LittleEndian.getUShort( data, offset ) );
+ unfinishedString = "";
+ break;
+ }
+ short charCount = LittleEndian.getShort( data, offset );
+
+ setupStringParameters( data, offset, charCount );
+ if ( remaining < totalLengthBytes )
+ {
+ setExpectedChars( calculateCharCount( totalLengthBytes - remaining ) );
+ charCount -= getExpectedChars();
+ totalLengthBytes = remaining;
+ }
+ else
+ {
+ setExpectedChars( 0 );
+ }
+ processString( data, offset, charCount );
+ offset += totalLengthBytes;
+ if ( getExpectedChars() != 0 )
+ {
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Detemines the option types for the string (ie, compressed or uncompressed unicode, rich text string or
+ * plain string etc) and calculates the length and offset for the string.
+ *
+ * @param data
+ * @param index
+ * @param char_count
+ */
+ private void setupStringParameters( final byte[] data, final int index,
+ final int char_count )
+ {
+ byte optionFlag = data[index + LittleEndianConsts.SHORT_SIZE];
+
+ wideChar = ( optionFlag & 1 ) == 1;
+ boolean farEast = ( optionFlag & 4 ) == 4;
+ boolean richText = ( optionFlag & 8 ) == 8;
+
+ totalLengthBytes = SSTRecord.STRING_MINIMAL_OVERHEAD + calculateByteCount( char_count );
+ stringDataOffset = SSTRecord.STRING_MINIMAL_OVERHEAD;
+ if ( richText )
+ {
+ short run_count = LittleEndian.getShort( data, index + stringDataOffset );
+
+ stringDataOffset += LittleEndianConsts.SHORT_SIZE;
+ totalLengthBytes += LittleEndianConsts.SHORT_SIZE + ( LittleEndianConsts.INT_SIZE * run_count );
+ }
+ if ( farEast )
+ {
+ int extension_length = LittleEndian.getInt( data, index + stringDataOffset );
+
+ stringDataOffset += LittleEndianConsts.INT_SIZE;
+ totalLengthBytes += LittleEndianConsts.INT_SIZE + extension_length;
+ }
+ }
+
+
+ private void processString( final byte[] data, final int index,
+ final short char_count )
+ {
+ byte[] stringDataBuffer = new byte[totalLengthBytes];
+ int length = SSTRecord.STRING_MINIMAL_OVERHEAD + calculateByteCount( char_count );
+ byte[] bstring = new byte[length];
+
+ System.arraycopy( data, index, stringDataBuffer, 0, stringDataBuffer.length );
+ int offset = 0;
+
+ LittleEndian.putShort( bstring, offset, char_count );
+ offset += LittleEndianConsts.SHORT_SIZE;
+ bstring[offset] = stringDataBuffer[offset];
+
+// System.out.println( "offset = " + stringDataOffset );
+// System.out.println( "length = " + (bstring.length - STRING_MINIMAL_OVERHEAD) );
+// System.out.println( "src.length = " + str_data.length );
+// try
+// {
+// System.out.println( "----------------------- DUMP -------------------------" );
+// HexDump.dump( stringDataBuffer, (long)stringDataOffset, System.out, 1);
+// }
+// catch ( IOException e )
+// {
+// }
+// catch ( ArrayIndexOutOfBoundsException e )
+// {
+// }
+// catch ( IllegalArgumentException e )
+// {
+// }
+ System.arraycopy( stringDataBuffer, stringDataOffset, bstring,
+ SSTRecord.STRING_MINIMAL_OVERHEAD,
+ bstring.length - SSTRecord.STRING_MINIMAL_OVERHEAD );
+ UnicodeString string = new UnicodeString( UnicodeString.sid,
+ (short) bstring.length,
+ bstring );
+
+ if ( getExpectedChars() != 0 )
+ {
+ unfinishedString = string.getString();
+ }
+ else
+ {
+ Integer integer = new Integer( strings.size() );
+ addToStringTable( strings, integer, string );
+ }
+ }
+
+ /**
+ * Okay, we are doing some major cheating here. Because we can't handle rich text strings properly
+ * we end up getting duplicate strings. To get around this I'm doing do things: 1. Converting rich
+ * text to normal text and 2. If there's a duplicate I'm adding a space onto the end. Sneaky perhaps
+ * but it gets the job done until we can handle this a little better.
+ */
+ static public void addToStringTable( BinaryTree strings, Integer integer, UnicodeString string )
+ {
+ if (string.isRichText())
+ string.setOptionFlags( (byte)(string.getOptionFlags() & (~8) ) );
+
+ boolean added = false;
+ while (added == false)
+ {
+ try
+ {
+ strings.put( integer, string );
+ added = true;
+ }
+ catch( Exception ignore )
+ {
+ string.setString( string.getString() + " " );
+ }
+ }
+ }
+
+
+
+ private int calculateCharCount( final int byte_count )
+ {
+ return byte_count / ( wideChar ? LittleEndianConsts.SHORT_SIZE
+ : LittleEndianConsts.BYTE_SIZE );
+ }
+
+ /**
+ * Process a Continue record. A Continue record for an SST record
+ * contains the same kind of data that the SST record contains,
+ * with the following exceptions:
+ * <P>
+ * <OL>
+ * <LI>The string counts at the beginning of the SST record are
+ * not in the Continue record
+ * <LI>The first string in the Continue record might NOT begin
+ * with a size. If the last string in the previous record is
+ * continued in this record, the size is determined by that
+ * last string in the previous record; the first string will
+ * begin with a flag byte, followed by the remaining bytes (or
+ * words) of the last string from the previous
+ * record. Otherwise, the first string in the record will
+ * begin with a string length
+ * </OL>
+ *
+ * @param record the Continue record's byte data
+ */
+
+ public void processContinueRecord( final byte[] record )
+ {
+ if ( getExpectedChars() == 0 )
+ {
+ unfinishedString = "";
+ totalLengthBytes = 0;
+ stringDataOffset = 0;
+ wideChar = false;
+ manufactureStrings( record, 0, (short) record.length );
+ }
+ else
+ {
+ int data_length = record.length - LittleEndianConsts.BYTE_SIZE;
+
+ if ( calculateByteCount( getExpectedChars() ) > data_length )
+ {
+
+ // create artificial data to create a UnicodeString
+ byte[] input =
+ new byte[record.length + LittleEndianConsts.SHORT_SIZE];
+ short size = (short) ( ( ( record[0] & 1 ) == 1 )
+ ? ( data_length / LittleEndianConsts.SHORT_SIZE )
+ : ( data_length / LittleEndianConsts.BYTE_SIZE ) );
+
+ LittleEndian.putShort( input, (byte) 0, size );
+ System.arraycopy( record, 0, input, LittleEndianConsts.SHORT_SIZE, record.length );
+ UnicodeString ucs = new UnicodeString( UnicodeString.sid, (short) input.length, input );
+
+ unfinishedString = unfinishedString + ucs.getString();
+ setExpectedChars( getExpectedChars() - size );
+ }
+ else
+ {
+ setupStringParameters( record, -LittleEndianConsts.SHORT_SIZE,
+ getExpectedChars() );
+ byte[] str_data = new byte[totalLengthBytes];
+ int length = SSTRecord.STRING_MINIMAL_OVERHEAD
+ + ( calculateByteCount( getExpectedChars() ) );
+ byte[] bstring = new byte[length];
+
+ // Copy data from the record into the string
+ // buffer. Copy skips the length of a short in the
+ // string buffer, to leave room for the string length.
+ System.arraycopy( record, 0, str_data,
+ LittleEndianConsts.SHORT_SIZE,
+ str_data.length
+ - LittleEndianConsts.SHORT_SIZE );
+
+ // write the string length
+ LittleEndian.putShort( bstring, 0,
+ (short) getExpectedChars() );
+
+ // write the options flag
+ bstring[LittleEndianConsts.SHORT_SIZE] =
+ str_data[LittleEndianConsts.SHORT_SIZE];
+
+ // copy the bytes/words making up the string; skipping
+ // past all the overhead of the str_data array
+ System.arraycopy( str_data, stringDataOffset, bstring,
+ SSTRecord.STRING_MINIMAL_OVERHEAD,
+ bstring.length - SSTRecord.STRING_MINIMAL_OVERHEAD );
+
+ // use special constructor to create the final string
+ UnicodeString string =
+ new UnicodeString( UnicodeString.sid,
+ (short) bstring.length, bstring,
+ unfinishedString );
+ Integer integer = new Integer( strings.size() );
+
+// field_3_strings.put( integer, string );
+ addToStringTable( strings, integer, string );
+ manufactureStrings( record, totalLengthBytes - LittleEndianConsts.SHORT_SIZE, (short) record.length );
+ }
+ }
+ }
+
+ /**
+ * @return the number of characters we expect in the first
+ * sub-record in a subsequent continuation record
+ */
+
+ int getExpectedChars()
+ {
+ return continuationExpectedChars;
+ }
+
+ private void setExpectedChars( final int count )
+ {
+ continuationExpectedChars = count;
+ }
+
+ private int calculateByteCount( final int character_count )
+ {
+ return character_count * ( wideChar ? LittleEndianConsts.SHORT_SIZE : LittleEndianConsts.BYTE_SIZE );
+ }
+
+
+ /**
+ * @return the unfinished string
+ */
+
+ String getUnfinishedString()
+ {
+ return unfinishedString;
+ }
+
+ /**
+ * @return the total length of the current string
+ */
+
+ int getTotalLength()
+ {
+ return totalLengthBytes;
+ }
+
+ /**
+ * @return offset into current string data
+ */
+
+ int getStringDataOffset()
+ {
+ return stringDataOffset;
+ }
+
+ /**
+ * @return true if current string uses wide characters
+ */
+
+ boolean isWideChar()
+ {
+ return wideChar;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/SSTRecord.java b/src/java/org/apache/poi/hssf/record/SSTRecord.java
index d8428148ab..6011c8f5d7 100644
--- a/src/java/org/apache/poi/hssf/record/SSTRecord.java
+++ b/src/java/org/apache/poi/hssf/record/SSTRecord.java
@@ -1,4 +1,3 @@
-
/* ====================================================================
* The Apache Software License, Version 1.1
*
@@ -59,7 +58,8 @@ import org.apache.poi.util.BinaryTree;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
-import java.util.*;
+import java.util.Iterator;
+import java.util.List;
/**
* Title: Static String Table Record
@@ -71,65 +71,45 @@ import java.util.*;
* <P>
* @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
*/
public class SSTRecord
- extends Record
+ extends Record
{
- // how big can an SST record be? As big as any record can be: 8228
- // bytes
- private static final int _max = 8228;
-
- // standard record overhead: two shorts (record id plus data space
- // size)
- private static final int _std_record_overhead =
- 2 * LittleEndianConsts.SHORT_SIZE;
-
- // SST overhead: the standard record overhead, plus the number of
- // strings and the number of unique strings -- two ints
- private static final int _sst_record_overhead =
- (_std_record_overhead + (2 * LittleEndianConsts.INT_SIZE));
+ /** how big can an SST record be? As big as any record can be: 8228 bytes */
+ static final int MAX_RECORD_SIZE = 8228;
- // how much data can we stuff into an SST record? That would be
- // _max minus the standard SST record overhead
- private static final int _max_data_space =
- _max - _sst_record_overhead;
+ /** standard record overhead: two shorts (record id plus data space size)*/
+ static final int STD_RECORD_OVERHEAD =
+ 2 * LittleEndianConsts.SHORT_SIZE;
- // overhead for each string includes the string's character count
- // (a short) and the flag describing its characteristics (a byte)
- private static final int _string_minimal_overhead =
- LittleEndianConsts.SHORT_SIZE + LittleEndianConsts.BYTE_SIZE;
- public static final short sid = 0xfc;
+ /** SST overhead: the standard record overhead, plus the number of strings and the number of unique strings -- two ints */
+ static final int SST_RECORD_OVERHEAD =
+ ( STD_RECORD_OVERHEAD + ( 2 * LittleEndianConsts.INT_SIZE ) );
- // union of strings in the SST and EXTSST
- private int field_1_num_strings;
+ /** how much data can we stuff into an SST record? That would be _max minus the standard SST record overhead */
+ static final int MAX_DATA_SPACE = MAX_RECORD_SIZE - SST_RECORD_OVERHEAD;
- // according to docs ONLY SST
- private int field_2_num_unique_strings;
- private BinaryTree field_3_strings;
+ /** overhead for each string includes the string's character count (a short) and the flag describing its characteristics (a byte) */
+ static final int STRING_MINIMAL_OVERHEAD = LittleEndianConsts.SHORT_SIZE + LittleEndianConsts.BYTE_SIZE;
- // this is the number of characters we expect in the first
- // sub-record in a subsequent continuation record
- private int __expected_chars;
+ public static final short sid = 0xfc;
- // this is the string we were working on before hitting the end of
- // the current record. This string is NOT finished.
- private String _unfinished_string;
+ /** union of strings in the SST and EXTSST */
+ private int field_1_num_strings;
- // this is the total length of the current string being handled
- private int _total_length_bytes;
+ /** according to docs ONLY SST */
+ private int field_2_num_unique_strings;
+ private BinaryTree field_3_strings;
- // this is the offset into a string field of the actual string
- // data
- private int _string_data_offset;
-
- // this is true if the string uses wide characters
- private boolean _wide_char;
- private List _record_lengths = null;
+ /** Record lengths for initial SST record and all continue records */
+ private List _record_lengths = null;
+ private SSTDeserializer deserializer;
/**
* default constructor
@@ -137,14 +117,10 @@ public class SSTRecord
public SSTRecord()
{
- field_1_num_strings = 0;
+ field_1_num_strings = 0;
field_2_num_unique_strings = 0;
- field_3_strings = new BinaryTree();
- setExpectedChars(0);
- _unfinished_string = "";
- _total_length_bytes = 0;
- _string_data_offset = 0;
- _wide_char = false;
+ field_3_strings = new BinaryTree();
+ deserializer = new SSTDeserializer(field_3_strings);
}
/**
@@ -156,9 +132,9 @@ public class SSTRecord
* @param data of the record (should not contain sid/len)
*/
- public SSTRecord(final short id, final short size, final byte [] data)
+ public SSTRecord( final short id, final short size, final byte[] data )
{
- super(id, size, data);
+ super( id, size, data );
}
/**
@@ -171,10 +147,10 @@ public class SSTRecord
* @param offset of the record
*/
- public SSTRecord(final short id, final short size, final byte [] data,
- int offset)
+ public SSTRecord( final short id, final short size, final byte[] data,
+ int offset )
{
- super(id, size, data, offset);
+ super( id, size, data, offset );
}
/**
@@ -192,13 +168,13 @@ public class SSTRecord
* @return the index of that string in the table
*/
- public int addString(final String string)
+ public int addString( final String string )
{
int rval;
- if (string == null)
+ if ( string == null )
{
- rval = addString("", false);
+ rval = addString( "", false );
}
else
{
@@ -207,17 +183,17 @@ public class SSTRecord
// present, we have to use 16-bit encoding. Otherwise, we
// can use 8-bit encoding
boolean useUTF16 = false;
- int strlen = string.length();
+ int strlen = string.length();
- for (int j = 0; j < strlen; j++)
+ for ( int j = 0; j < strlen; j++ )
{
- if (string.charAt(j) > 255)
+ if ( string.charAt( j ) > 255 )
{
useUTF16 = true;
break;
}
}
- rval = addString(string, useUTF16);
+ rval = addString( string, useUTF16 );
}
return rval;
}
@@ -238,21 +214,21 @@ public class SSTRecord
* @return the index of that string in the table
*/
- public int addString(final String string, final boolean useUTF16)
+ public int addString( final String string, final boolean useUTF16 )
{
field_1_num_strings++;
- String str = (string == null) ? ""
- : string;
- int rval = -1;
- UnicodeString ucs = new UnicodeString();
-
- ucs.setString(str);
- ucs.setCharCount(( short ) str.length());
- ucs.setOptionFlags(( byte ) (useUTF16 ? 1
- : 0));
- Integer integer = ( Integer ) field_3_strings.getKeyForValue(ucs);
-
- if (integer != null)
+ String str = ( string == null ) ? ""
+ : string;
+ int rval = -1;
+ UnicodeString ucs = new UnicodeString();
+
+ ucs.setString( str );
+ ucs.setCharCount( (short) str.length() );
+ ucs.setOptionFlags( (byte) ( useUTF16 ? 1
+ : 0 ) );
+ Integer integer = (Integer) field_3_strings.getKeyForValue( ucs );
+
+ if ( integer != null )
{
rval = integer.intValue();
}
@@ -263,8 +239,9 @@ public class SSTRecord
// strings we've already collected
rval = field_3_strings.size();
field_2_num_unique_strings++;
- integer = new Integer(rval);
- field_3_strings.put(integer, ucs);
+ integer = new Integer( rval );
+ SSTDeserializer.addToStringTable( field_3_strings, integer, ucs );
+// field_3_strings.put( integer, ucs );
}
return rval;
}
@@ -298,7 +275,7 @@ public class SSTRecord
*
*/
- public void setNumStrings(final int count)
+ public void setNumStrings( final int count )
{
field_1_num_strings = count;
}
@@ -314,7 +291,7 @@ public class SSTRecord
* @param count number of strings
*/
- public void getNumUniqueStrings(final int count)
+ public void getNumUniqueStrings( final int count )
{
field_2_num_unique_strings = count;
}
@@ -327,16 +304,15 @@ public class SSTRecord
* @return the desired string
*/
- public String getString(final int id)
+ public String getString( final int id )
{
- return (( UnicodeString ) field_3_strings.get(new Integer(id)))
- .getString();
+ return ( (UnicodeString) field_3_strings.get( new Integer( id ) ) ).getString();
}
- public boolean getString16bit(final int id)
+ public boolean isString16bit( final int id )
{
- return ((( UnicodeString ) field_3_strings.get(new Integer(id)))
- .getOptionFlags() == 1);
+ UnicodeString unicodeString = ( (UnicodeString) field_3_strings.get( new Integer( id ) ) );
+ return ( ( unicodeString.getOptionFlags() & 0x01 ) == 1 );
}
/**
@@ -349,326 +325,24 @@ public class SSTRecord
{
StringBuffer buffer = new StringBuffer();
- buffer.append("[SST]\n");
- buffer.append(" .numstrings = ")
- .append(Integer.toHexString(getNumStrings())).append("\n");
- buffer.append(" .uniquestrings = ")
- .append(Integer.toHexString(getNumUniqueStrings())).append("\n");
- for (int k = 0; k < field_3_strings.size(); k++)
+ buffer.append( "[SST]\n" );
+ buffer.append( " .numstrings = " )
+ .append( Integer.toHexString( getNumStrings() ) ).append( "\n" );
+ buffer.append( " .uniquestrings = " )
+ .append( Integer.toHexString( getNumUniqueStrings() ) ).append( "\n" );
+ for ( int k = 0; k < field_3_strings.size(); k++ )
{
- buffer.append(" .string_" + k + " = ")
- .append((( UnicodeString ) field_3_strings
- .get(new Integer(k))).toString()).append("\n");
+ buffer.append( " .string_" + k + " = " )
+ .append( ( (UnicodeString) field_3_strings
+ .get( new Integer( k ) ) ).toString() ).append( "\n" );
}
- buffer.append("[/SST]\n");
+ buffer.append( "[/SST]\n" );
return buffer.toString();
}
/**
- * Create a byte array consisting of an SST record and any
- * required Continue records, ready to be written out.
- * <p>
- * If an SST record and any subsequent Continue records are read
- * in to create this instance, this method should produce a byte
- * array that is identical to the byte array produced by
- * concatenating the input records' data.
- *
- * @return the byte array
- */
-
- public int serialize(int offset, byte [] data)
- {
- int rval = getRecordSize();
- int record_length_index = 0;
-
- // get the linear size of that array
- int unicodesize = calculateUnicodeSize();
-
- if (unicodesize > _max_data_space)
- {
- byte[] stringreminant = null;
- int unipos = 0;
- boolean lastneedcontinue = false;
- int stringbyteswritten = 0;
- boolean first_record = true;
- int totalWritten = 0;
- int size = 0;
-
- while (totalWritten != rval)
- {
- int pos = 0;
-
- // write the appropriate header
- int available;
-
- if (first_record)
- {
- size =
- (( Integer ) _record_lengths
- .get(record_length_index++)).intValue();
- available = size - 8;
- pos = writeSSTHeader(data,
- pos + offset
- + totalWritten, size);
- size += _std_record_overhead;
- first_record = false;
- }
- else
- {
- pos = 0;
- int to_be_written = (unicodesize - stringbyteswritten)
- + (lastneedcontinue ? 1
- : 0); // not used?
-
- size =
- (( Integer ) _record_lengths
- .get(record_length_index++)).intValue();
- available = size;
- pos = writeContinueHeader(data,
- pos + offset
- + totalWritten, size);
- size = size + _std_record_overhead;
- }
-
- // now, write the rest of the data into the current
- // record space
- if (lastneedcontinue)
- {
-
- // the last string in the previous record was not
- // written out completely
- if (stringreminant.length <= available)
- {
-
- // write reminant -- it'll all fit neatly
- System.arraycopy(stringreminant, 0, data,
- pos + offset + totalWritten,
- stringreminant.length);
- stringbyteswritten += stringreminant.length - 1;
- pos += stringreminant.length;
- lastneedcontinue = false;
- available -= stringreminant.length;
- }
- else
- {
-
- // write as much of the remnant as possible
- System.arraycopy(stringreminant, 0, data,
- pos + offset + totalWritten,
- available);
- stringbyteswritten += available - 1;
- pos += available;
- byte[] leftover =
- new byte[ (stringreminant.length - available) + LittleEndianConsts.BYTE_SIZE ];
-
- System.arraycopy(stringreminant, available, leftover,
- LittleEndianConsts.BYTE_SIZE,
- stringreminant.length - available);
- leftover[ 0 ] = stringreminant[ 0 ];
- stringreminant = leftover;
- available = 0;
- lastneedcontinue = true;
- }
- }
-
- // last string's remnant, if any, is cleaned up as
- // best as can be done ... now let's try and write
- // some more strings
- for (; unipos < field_3_strings.size(); unipos++)
- {
- Integer intunipos = new Integer(unipos);
- UnicodeString unistr =
- (( UnicodeString ) field_3_strings.get(intunipos));
-
- if (unistr.getRecordSize() <= available)
- {
- unistr.serialize(pos + offset + totalWritten, data);
- int rsize = unistr.getRecordSize();
-
- stringbyteswritten += rsize;
- pos += rsize;
- available -= rsize;
- }
- else
- {
-
- // can't write the entire string out
- if (available >= _string_minimal_overhead)
- {
-
- // we can write some of it
- byte[] ucs = unistr.serialize();
-
- System.arraycopy(ucs, 0, data,
- pos + offset + totalWritten,
- available);
- stringbyteswritten += available;
- stringreminant =
- new byte[ (ucs.length - available) + LittleEndianConsts.BYTE_SIZE ];
- System.arraycopy(ucs, available, stringreminant,
- LittleEndianConsts.BYTE_SIZE,
- ucs.length - available);
- stringreminant[ 0 ] =
- ucs[ LittleEndianConsts.SHORT_SIZE ];
- available = 0;
- lastneedcontinue = true;
- unipos++;
- }
- break;
- }
- }
- totalWritten += size;
- }
- }
- else
- {
-
- // short data: write one simple SST record
- int datasize = _sst_record_overhead + unicodesize; // not used?
-
- writeSSTHeader(
- data, 0 + offset,
- _sst_record_overhead
- + (( Integer ) _record_lengths.get(
- record_length_index++)).intValue() - _std_record_overhead);
- int pos = _sst_record_overhead;
-
- for (int k = 0; k < field_3_strings.size(); k++)
- {
- UnicodeString unistr =
- (( UnicodeString ) field_3_strings.get(new Integer(k)));
-
- System.arraycopy(unistr.serialize(), 0, data, pos + offset,
- unistr.getRecordSize());
- pos += unistr.getRecordSize();
- }
- }
- return rval;
- }
-
- // not used: remove?
- private int calculateStringsize()
- {
- int retval = 0;
-
- for (int k = 0; k < field_3_strings.size(); k++)
- {
- retval +=
- (( UnicodeString ) field_3_strings.get(new Integer(k)))
- .getRecordSize();
- }
- return retval;
- }
-
- /**
- * Process a Continue record. A Continue record for an SST record
- * contains the same kind of data that the SST record contains,
- * with the following exceptions:
- * <P>
- * <OL>
- * <LI>The string counts at the beginning of the SST record are
- * not in the Continue record
- * <LI>The first string in the Continue record might NOT begin
- * with a size. If the last string in the previous record is
- * continued in this record, the size is determined by that
- * last string in the previous record; the first string will
- * begin with a flag byte, followed by the remaining bytes (or
- * words) of the last string from the previous
- * record. Otherwise, the first string in the record will
- * begin with a string length
- * </OL>
- *
- * @param record the Continue record's byte data
- */
-
- public void processContinueRecord(final byte [] record)
- {
- if (getExpectedChars() == 0)
- {
- _unfinished_string = "";
- _total_length_bytes = 0;
- _string_data_offset = 0;
- _wide_char = false;
- manufactureStrings(record, 0, ( short ) record.length);
- }
- else
- {
- int data_length = record.length - LittleEndianConsts.BYTE_SIZE;
-
- if (calculateByteCount(getExpectedChars()) > data_length)
- {
-
- // create artificial data to create a UnicodeString
- byte[] input =
- new byte[ record.length + LittleEndianConsts.SHORT_SIZE ];
- short size = ( short ) (((record[ 0 ] & 1) == 1)
- ? (data_length
- / LittleEndianConsts.SHORT_SIZE)
- : (data_length
- / LittleEndianConsts.BYTE_SIZE));
-
- LittleEndian.putShort(input, ( byte ) 0, size);
- System.arraycopy(record, 0, input,
- LittleEndianConsts.SHORT_SIZE,
- record.length);
- UnicodeString ucs = new UnicodeString(UnicodeString.sid,
- ( short ) input.length,
- input);
-
- _unfinished_string = _unfinished_string + ucs.getString();
- setExpectedChars(getExpectedChars() - size);
- }
- else
- {
- setupStringParameters(record, -LittleEndianConsts.SHORT_SIZE,
- getExpectedChars());
- byte[] str_data = new byte[ _total_length_bytes ];
- int length = _string_minimal_overhead
- + (calculateByteCount(getExpectedChars()));
- byte[] bstring = new byte[ length ];
-
- // Copy data from the record into the string
- // buffer. Copy skips the length of a short in the
- // string buffer, to leave room for the string length.
- System.arraycopy(record, 0, str_data,
- LittleEndianConsts.SHORT_SIZE,
- str_data.length
- - LittleEndianConsts.SHORT_SIZE);
-
- // write the string length
- LittleEndian.putShort(bstring, 0,
- ( short ) getExpectedChars());
-
- // write the options flag
- bstring[ LittleEndianConsts.SHORT_SIZE ] =
- str_data[ LittleEndianConsts.SHORT_SIZE ];
-
- // copy the bytes/words making up the string; skipping
- // past all the overhead of the str_data array
- System.arraycopy(str_data, _string_data_offset, bstring,
- _string_minimal_overhead,
- bstring.length - _string_minimal_overhead);
-
- // use special constructor to create the final string
- UnicodeString string =
- new UnicodeString(UnicodeString.sid,
- ( short ) bstring.length, bstring,
- _unfinished_string);
- Integer integer = new Integer(field_3_strings.size());
-
- field_3_strings.put(integer, string);
- manufactureStrings(record,
- _total_length_bytes
- - LittleEndianConsts
- .SHORT_SIZE, ( short ) record.length);
- }
- }
- }
-
- /**
* @return sid
*/
-
public short getSid()
{
return sid;
@@ -677,30 +351,23 @@ public class SSTRecord
/**
* @return hashcode
*/
-
public int hashCode()
{
return field_2_num_unique_strings;
}
- /**
- *
- * @param o
- * @return true if equal
- */
-
- public boolean equals(Object o)
+ public boolean equals( Object o )
{
- if ((o == null) || (o.getClass() != this.getClass()))
+ if ( ( o == null ) || ( o.getClass() != this.getClass() ) )
{
return false;
}
- SSTRecord other = ( SSTRecord ) o;
+ SSTRecord other = (SSTRecord) o;
- return ((field_1_num_strings == other
- .field_1_num_strings) && (field_2_num_unique_strings == other
- .field_2_num_unique_strings) && field_3_strings
- .equals(other.field_3_strings));
+ return ( ( field_1_num_strings == other
+ .field_1_num_strings ) && ( field_2_num_unique_strings == other
+ .field_2_num_unique_strings ) && field_3_strings
+ .equals( other.field_3_strings ) );
}
/**
@@ -711,12 +378,12 @@ public class SSTRecord
* @exception RecordFormatException if validation fails
*/
- protected void validateSid(final short id)
- throws RecordFormatException
+ protected void validateSid( final short id )
+ throws RecordFormatException
{
- if (id != sid)
+ if ( id != sid )
{
- throw new RecordFormatException("NOT An SST RECORD");
+ throw new RecordFormatException( "NOT An SST RECORD" );
}
}
@@ -800,33 +467,20 @@ public class SSTRecord
* @param size size of the raw data
*/
- protected void fillFields(final byte [] data, final short size,
- int offset)
+ protected void fillFields( final byte[] data, final short size,
+ int offset )
{
// this method is ALWAYS called after construction -- using
// the nontrivial constructor, of course -- so this is where
// we initialize our fields
- field_1_num_strings = LittleEndian.getInt(data, 0 + offset);
- field_2_num_unique_strings = LittleEndian.getInt(data, 4 + offset);
- field_3_strings = new BinaryTree();
- setExpectedChars(0);
- _unfinished_string = "";
- _total_length_bytes = 0;
- _string_data_offset = 0;
- _wide_char = false;
- manufactureStrings(data, 8 + offset, size);
+ field_1_num_strings = LittleEndian.getInt( data, 0 + offset );
+ 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, size );
}
- /**
- * @return the number of characters we expect in the first
- * sub-record in a subsequent continuation record
- */
-
- int getExpectedChars()
- {
- return __expected_chars;
- }
/**
* @return an iterator of the strings we hold. All instances are
@@ -848,372 +502,43 @@ public class SSTRecord
}
/**
- * @return the unfinished string
+ * 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.
+ *
+ * @return byte array containing instance data
*/
- String getUnfinishedString()
+ public int serialize( int offset, byte[] data )
{
- return _unfinished_string;
+ SSTSerializer serializer = new SSTSerializer(
+ _record_lengths, field_3_strings, getNumStrings(), getNumUniqueStrings() );
+ return serializer.serialize( offset, data );
}
- /**
- * @return the total length of the current string
- */
- int getTotalLength()
+ // we can probably simplify this later...this calculates the size
+ // w/o serializing but still is a bit slow
+ public int getRecordSize()
{
- return _total_length_bytes;
- }
+ SSTSerializer serializer = new SSTSerializer(
+ _record_lengths, field_3_strings, getNumStrings(), getNumUniqueStrings() );
- /**
- * @return offset into current string data
- */
+ return serializer.getRecordSize();
+ }
- int getStringDataOffset()
+ SSTDeserializer getDeserializer()
{
- return _string_data_offset;
+ return deserializer;
}
/**
- * @return true if current string uses wide characters
+ * Strange to handle continue records this way. Is it a smell?
*/
-
- boolean isWideChar()
- {
- return _wide_char;
- }
-
- private int writeSSTHeader(final byte [] data, final int pos,
- final int recsize)
- {
- int offset = pos;
-
- LittleEndian.putShort(data, offset, sid);
- offset += LittleEndianConsts.SHORT_SIZE;
- LittleEndian.putShort(data, offset, ( short ) (recsize));
- offset += LittleEndianConsts.SHORT_SIZE;
- LittleEndian.putInt(data, offset, getNumStrings());
- offset += LittleEndianConsts.INT_SIZE;
- LittleEndian.putInt(data, offset, getNumUniqueStrings());
- offset += LittleEndianConsts.INT_SIZE;
- return offset - pos;
- }
-
- private int writeContinueHeader(final byte [] data, final int pos,
- final int recsize)
- {
- int offset = pos;
-
- LittleEndian.putShort(data, offset, ContinueRecord.sid);
- offset += LittleEndianConsts.SHORT_SIZE;
- LittleEndian.putShort(data, offset, ( short ) (recsize));
- offset += LittleEndianConsts.SHORT_SIZE;
- return offset - pos;
- }
-
- private int calculateUCArrayLength(final byte [][] ucarray)
- {
- int retval = 0;
-
- for (int k = 0; k < ucarray.length; k++)
- {
- retval += ucarray[ k ].length;
- }
- return retval;
- }
-
- private void manufactureStrings(final byte [] data, final int index,
- short size)
- {
- int offset = index;
-
- while (offset < size)
- {
- int remaining = size - offset;
-
- if ((remaining > 0)
- && (remaining < LittleEndianConsts.SHORT_SIZE))
- {
- throw new RecordFormatException(
- "Cannot get length of the last string in SSTRecord");
- }
- if (remaining == LittleEndianConsts.SHORT_SIZE)
- {
- setExpectedChars(LittleEndian.getShort(data, offset));
- _unfinished_string = "";
- break;
- }
- short char_count = LittleEndian.getShort(data, offset);
-
- setupStringParameters(data, offset, char_count);
- if (remaining < _total_length_bytes)
- {
- setExpectedChars(calculateCharCount(_total_length_bytes
- - remaining));
- char_count -= getExpectedChars();
- _total_length_bytes = remaining;
- }
- else
- {
- setExpectedChars(0);
- }
- processString(data, offset, char_count);
- offset += _total_length_bytes;
- if (getExpectedChars() != 0)
- {
- break;
- }
- }
- }
-
- private void setupStringParameters(final byte [] data, final int index,
- final int char_count)
- {
- byte flag = data[ index + LittleEndianConsts.SHORT_SIZE ];
-
- _wide_char = (flag & 1) == 1;
- boolean extended = (flag & 4) == 4;
- boolean formatted_run = (flag & 8) == 8;
-
- _total_length_bytes = _string_minimal_overhead
- + calculateByteCount(char_count);
- _string_data_offset = _string_minimal_overhead;
- if (formatted_run)
- {
- short run_count = LittleEndian.getShort(data,
- index
- + _string_data_offset);
-
- _string_data_offset += LittleEndianConsts.SHORT_SIZE;
- _total_length_bytes += LittleEndianConsts.SHORT_SIZE
- + (LittleEndianConsts.INT_SIZE
- * run_count);
- }
- if (extended)
- {
- int extension_length = LittleEndian.getInt(data,
- index
- + _string_data_offset);
-
- _string_data_offset += LittleEndianConsts.INT_SIZE;
- _total_length_bytes += LittleEndianConsts.INT_SIZE
- + extension_length;
- }
- }
-
- private void processString(final byte [] data, final int index,
- final short char_count)
- {
- byte[] str_data = new byte[ _total_length_bytes ];
- int length = _string_minimal_overhead
- + calculateByteCount(char_count);
- byte[] bstring = new byte[ length ];
-
- System.arraycopy(data, index, str_data, 0, str_data.length);
- int offset = 0;
-
- LittleEndian.putShort(bstring, offset, char_count);
- offset += LittleEndianConsts.SHORT_SIZE;
- bstring[ offset ] = str_data[ offset ];
- System.arraycopy(str_data, _string_data_offset, bstring,
- _string_minimal_overhead,
- bstring.length - _string_minimal_overhead);
- UnicodeString string = new UnicodeString(UnicodeString.sid,
- ( short ) bstring.length,
- bstring);
-
- if (getExpectedChars() != 0)
- {
- _unfinished_string = string.getString();
- }
- else
- {
- Integer integer = new Integer(field_3_strings.size());
-
- field_3_strings.put(integer, string);
- }
- }
-
- private void setExpectedChars(final int count)
- {
- __expected_chars = count;
- }
-
- private int calculateByteCount(final int character_count)
- {
- return character_count * (_wide_char ? LittleEndianConsts.SHORT_SIZE
- : LittleEndianConsts.BYTE_SIZE);
- }
-
- private int calculateCharCount(final int byte_count)
+ public void processContinueRecord( byte[] record )
{
- return byte_count / (_wide_char ? LittleEndianConsts.SHORT_SIZE
- : LittleEndianConsts.BYTE_SIZE);
+ deserializer.processContinueRecord( record );
}
+}
- // we can probably simplify this later...this calculates the size
- // w/o serializing but still is a bit slow
- public int getRecordSize()
- {
- _record_lengths = new ArrayList();
- int retval = 0;
- int unicodesize = calculateUnicodeSize();
-
- if (unicodesize > _max_data_space)
- {
- UnicodeString unistr = null;
- int stringreminant = 0;
- int unipos = 0;
- boolean lastneedcontinue = false;
- int stringbyteswritten = 0;
- boolean finished = false;
- boolean first_record = true;
- int totalWritten = 0;
-
- while (!finished)
- {
- int record = 0;
- int pos = 0;
-
- if (first_record)
- {
-
- // writing SST record
- record = _max;
- pos = 12;
- first_record = false;
- _record_lengths.add(new Integer(record
- - _std_record_overhead));
- }
- else
- {
-
- // writing continue record
- pos = 0;
- int to_be_written = (unicodesize - stringbyteswritten)
- + (lastneedcontinue ? 1
- : 0);
- int size = Math.min(_max - _std_record_overhead,
- to_be_written);
-
- if (size == to_be_written)
- {
- finished = true;
- }
- record = size + _std_record_overhead;
- _record_lengths.add(new Integer(size));
- pos = 4;
- }
- if (lastneedcontinue)
- {
- int available = _max - pos;
-
- if (stringreminant <= available)
- {
-
- // write reminant
- stringbyteswritten += stringreminant - 1;
- pos += stringreminant;
- lastneedcontinue = false;
- }
- else
- {
-
- // write as much of the remnant as possible
- int toBeWritten = unistr.maxBrokenLength(available);
-
- if (available != toBeWritten)
- {
- int shortrecord = record
- - (available - toBeWritten);
-
- _record_lengths.set(
- _record_lengths.size() - 1,
- new Integer(
- shortrecord - _std_record_overhead));
- record = shortrecord;
- }
- stringbyteswritten += toBeWritten - 1;
- pos += toBeWritten;
- stringreminant -= toBeWritten - 1;
- lastneedcontinue = true;
- }
- }
- for (; unipos < field_3_strings.size(); unipos++)
- {
- int available = _max - pos;
- Integer intunipos = new Integer(unipos);
-
- unistr =
- (( UnicodeString ) field_3_strings.get(intunipos));
- if (unistr.getRecordSize() <= available)
- {
- stringbyteswritten += unistr.getRecordSize();
- pos += unistr.getRecordSize();
- }
- else
- {
- if (available >= _string_minimal_overhead)
- {
- int toBeWritten =
- unistr.maxBrokenLength(available);
-
- stringbyteswritten += toBeWritten;
- stringreminant =
- (unistr.getRecordSize() - toBeWritten)
- + LittleEndianConsts.BYTE_SIZE;
- if (available != toBeWritten)
- {
- int shortrecord = record
- - (available - toBeWritten);
-
- _record_lengths.set(
- _record_lengths.size() - 1,
- new Integer(
- shortrecord - _std_record_overhead));
- record = shortrecord;
- }
- lastneedcontinue = true;
- unipos++;
- }
- else
- {
- int shortrecord = record - available;
-
- _record_lengths.set(
- _record_lengths.size() - 1,
- new Integer(
- shortrecord - _std_record_overhead));
- record = shortrecord;
- }
- break;
- }
- }
- totalWritten += record;
- }
- retval = totalWritten;
- }
- else
- {
-
- // short data: write one simple SST record
- retval = _sst_record_overhead + unicodesize;
- _record_lengths.add(new Integer(unicodesize));
- }
- return retval;
- }
-
- private int calculateUnicodeSize()
- {
- int retval = 0;
-
- for (int k = 0; k < field_3_strings.size(); k++)
- {
- UnicodeString string =
- ( UnicodeString ) field_3_strings.get(new Integer(k));
- retval += string.getRecordSize();
- }
- return retval;
- }
-}
diff --git a/src/java/org/apache/poi/hssf/record/SSTSerializer.java b/src/java/org/apache/poi/hssf/record/SSTSerializer.java
new file mode 100644
index 0000000000..5802279145
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/SSTSerializer.java
@@ -0,0 +1,356 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.util.BinaryTree;
+import org.apache.poi.util.LittleEndianConsts;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * This class handles serialization of SST records. It utilizes the record processor
+ * class write individual records. This has been refactored from the SSTRecord class.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+class SSTSerializer
+{
+
+ private List recordLengths;
+ private BinaryTree strings;
+ private int numStrings;
+ private int numUniqueStrings;
+ private SSTRecordHeader sstRecordHeader;
+
+ public SSTSerializer( List recordLengths, BinaryTree strings, int numStrings, int numUniqueStrings )
+ {
+ this.recordLengths = recordLengths;
+ this.strings = strings;
+ this.numStrings = numStrings;
+ this.numUniqueStrings = numUniqueStrings;
+ this.sstRecordHeader = new SSTRecordHeader(numStrings, numUniqueStrings);
+ }
+
+ /**
+ * Create a byte array consisting of an SST record and any
+ * required Continue records, ready to be written out.
+ * <p>
+ * If an SST record and any subsequent Continue records are read
+ * in to create this instance, this method should produce a byte
+ * array that is identical to the byte array produced by
+ * concatenating the input records' data.
+ *
+ * @return the byte array
+ */
+ public int serialize( int offset, byte[] data )
+ {
+ int record_size = getRecordSize();
+ int record_length_index = 0;
+
+ if ( calculateUnicodeSize() > SSTRecord.MAX_DATA_SPACE )
+ serializeLargeRecord( record_size, record_length_index, data, offset );
+ else
+ serializeSingleSSTRecord( data, offset, record_length_index );
+ return record_size;
+ }
+
+ private int calculateUnicodeSize()
+ {
+ int retval = 0;
+
+ for ( int k = 0; k < strings.size(); k++ )
+ {
+ retval += getUnicodeString(k).getRecordSize();
+ }
+ return retval;
+ }
+
+ // we can probably simplify this later...this calculates the size
+ // w/o serializing but still is a bit slow
+ public int getRecordSize()
+ {
+ recordLengths = new ArrayList();
+ int retval = 0;
+ int unicodesize = calculateUnicodeSize();
+
+ if ( unicodesize > SSTRecord.MAX_DATA_SPACE )
+ {
+ retval = calcRecordSizesForLongStrings( unicodesize );
+ }
+ else
+ {
+ // short data: write one simple SST record
+ retval = SSTRecord.SST_RECORD_OVERHEAD + unicodesize;
+ recordLengths.add( new Integer( unicodesize ) );
+ }
+ return retval;
+ }
+
+ private int calcRecordSizesForLongStrings( int unicodesize )
+ {
+ int retval;
+ UnicodeString unistr = null;
+ int stringreminant = 0;
+ int unipos = 0;
+ boolean lastneedcontinue = false;
+ int stringbyteswritten = 0;
+ boolean finished = false;
+ boolean first_record = true;
+ int totalWritten = 0;
+
+ while ( !finished )
+ {
+ int record = 0;
+ int pos = 0;
+
+ if ( first_record )
+ {
+
+ // writing SST record
+ record = SSTRecord.MAX_RECORD_SIZE;
+ pos = 12;
+ first_record = false;
+ recordLengths.add( new Integer( record - SSTRecord.STD_RECORD_OVERHEAD ) );
+ }
+ else
+ {
+
+ // writing continue record
+ pos = 0;
+ int to_be_written = ( unicodesize - stringbyteswritten ) + ( lastneedcontinue ? 1 : 0 );
+ int size = Math.min( SSTRecord.MAX_RECORD_SIZE - SSTRecord.STD_RECORD_OVERHEAD, to_be_written );
+
+ if ( size == to_be_written )
+ {
+ finished = true;
+ }
+ record = size + SSTRecord.STD_RECORD_OVERHEAD;
+ recordLengths.add( new Integer( size ) );
+ pos = 4;
+ }
+ if ( lastneedcontinue )
+ {
+ int available = SSTRecord.MAX_RECORD_SIZE - pos;
+
+ if ( stringreminant <= available )
+ {
+
+ // write reminant
+ stringbyteswritten += stringreminant - 1;
+ pos += stringreminant;
+ lastneedcontinue = false;
+ }
+ else
+ {
+
+ // write as much of the remnant as possible
+ int toBeWritten = unistr.maxBrokenLength( available );
+
+ if ( available != toBeWritten )
+ {
+ int shortrecord = record - ( available - toBeWritten );
+ recordLengths.set( recordLengths.size() - 1,
+ new Integer( shortrecord - SSTRecord.STD_RECORD_OVERHEAD ) );
+ record = shortrecord;
+ }
+ stringbyteswritten += toBeWritten - 1;
+ pos += toBeWritten;
+ stringreminant -= toBeWritten - 1;
+ lastneedcontinue = true;
+ }
+ }
+ for ( ; unipos < strings.size(); unipos++ )
+ {
+ int available = SSTRecord.MAX_RECORD_SIZE - pos;
+ Integer intunipos = new Integer( unipos );
+
+ unistr = ( (UnicodeString) strings.get( intunipos ) );
+ if ( unistr.getRecordSize() <= available )
+ {
+ stringbyteswritten += unistr.getRecordSize();
+ pos += unistr.getRecordSize();
+ }
+ else
+ {
+ if ( available >= SSTRecord.STRING_MINIMAL_OVERHEAD )
+ {
+ int toBeWritten =
+ unistr.maxBrokenLength( available );
+
+ stringbyteswritten += toBeWritten;
+ stringreminant =
+ ( unistr.getRecordSize() - toBeWritten )
+ + LittleEndianConsts.BYTE_SIZE;
+ if ( available != toBeWritten )
+ {
+ int shortrecord = record
+ - ( available - toBeWritten );
+
+ recordLengths.set(
+ recordLengths.size() - 1,
+ new Integer(
+ shortrecord - SSTRecord.STD_RECORD_OVERHEAD ) );
+ record = shortrecord;
+ }
+ lastneedcontinue = true;
+ unipos++;
+ }
+ else
+ {
+ int shortrecord = record - available;
+
+ recordLengths.set( recordLengths.size() - 1,
+ new Integer( shortrecord - SSTRecord.STD_RECORD_OVERHEAD ) );
+ record = shortrecord;
+ }
+ break;
+ }
+ }
+ totalWritten += record;
+ }
+ retval = totalWritten;
+
+ return retval;
+ }
+
+
+ private void serializeSingleSSTRecord( byte[] data, int offset, int record_length_index )
+ {
+ // short data: write one simple SST record
+
+ int len = ( (Integer) recordLengths.get( record_length_index++ ) ).intValue();
+ int recordSize = SSTRecord.SST_RECORD_OVERHEAD + len - SSTRecord.STD_RECORD_OVERHEAD;
+ sstRecordHeader.writeSSTHeader( data, 0 + offset, recordSize );
+ int pos = SSTRecord.SST_RECORD_OVERHEAD;
+
+ for ( int k = 0; k < strings.size(); k++ )
+ {
+// UnicodeString unistr = ( (UnicodeString) strings.get( new Integer( k ) ) );
+ System.arraycopy( getUnicodeString(k).serialize(), 0, data, pos + offset, getUnicodeString(k).getRecordSize() );
+ pos += getUnicodeString(k).getRecordSize();
+ }
+ }
+
+ /**
+ * Large records are serialized to an SST and to one or more CONTINUE records. Joy. They have the special
+ * characteristic that they can change the option field when a single string is split across to a
+ * CONTINUE record.
+ */
+ private void serializeLargeRecord( int record_size, int record_length_index, byte[] buffer, int offset )
+ {
+
+ byte[] stringReminant = null;
+ int stringIndex = 0;
+ boolean lastneedcontinue = false;
+ boolean first_record = true;
+ int totalWritten = 0;
+
+ while ( totalWritten != record_size )
+ {
+ int recordLength = ( (Integer) recordLengths.get( record_length_index++ ) ).intValue();
+ RecordProcessor recordProcessor = new RecordProcessor( buffer,
+ recordLength, numStrings, numUniqueStrings );
+
+ // write the appropriate header
+ recordProcessor.writeRecordHeader( offset, totalWritten, recordLength, first_record );
+ first_record = false;
+
+ // now, write the rest of the data into the current
+ // record space
+ if ( lastneedcontinue )
+ {
+ lastneedcontinue = stringReminant.length > recordProcessor.getAvailable();
+ // the last string in the previous record was not written out completely
+ stringReminant = recordProcessor.writeStringRemainder( lastneedcontinue,
+ stringReminant, offset, totalWritten );
+ }
+
+ // last string's remnant, if any, is cleaned up as best as can be done ... now let's try and write
+ // some more strings
+ for ( ; stringIndex < strings.size(); stringIndex++ )
+ {
+ UnicodeString unistr = getUnicodeString( stringIndex );
+
+ if ( unistr.getRecordSize() <= recordProcessor.getAvailable() )
+ {
+ recordProcessor.writeWholeString( unistr, offset, totalWritten );
+ }
+ else
+ {
+
+ // can't write the entire string out
+ if ( recordProcessor.getAvailable() >= SSTRecord.STRING_MINIMAL_OVERHEAD )
+ {
+
+ // we can write some of it
+ stringReminant = recordProcessor.writePartString( unistr, offset, totalWritten );
+ lastneedcontinue = true;
+ stringIndex++;
+ }
+ break;
+ }
+ }
+ totalWritten += recordLength + SSTRecord.STD_RECORD_OVERHEAD;
+ }
+ }
+
+ private UnicodeString getUnicodeString( int index )
+ {
+ Integer intunipos = new Integer( index );
+ return ( (UnicodeString) strings.get( intunipos ) );
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/UnicodeString.java b/src/java/org/apache/poi/hssf/record/UnicodeString.java
index 097be19b1c..2d68815255 100644
--- a/src/java/org/apache/poi/hssf/record/UnicodeString.java
+++ b/src/java/org/apache/poi/hssf/record/UnicodeString.java
@@ -66,6 +66,7 @@ import org.apache.poi.util.StringUtil;
* REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
* @author Andrew C. Oliver
* @author Marc Johnson (mjohnson at apache dot org)
+ * @author Glen Stampoultzis (glens at apache.org)
* @version 2.0-pre
*/
@@ -77,12 +78,28 @@ public class UnicodeString
private short field_1_charCount; // = 0;
private byte field_2_optionflags; // = 0;
private String field_3_string; // = null;
+ private final int RICH_TEXT_BIT = 8;
+
+ public UnicodeString()
+ {
+ }
+
public int hashCode()
{
- return field_1_charCount;
+ int stringHash = 0;
+ if (field_3_string != null)
+ stringHash = field_3_string.hashCode();
+ return field_1_charCount + stringHash;
}
+ /**
+ * Our handling of equals is inconsistent with compareTo. The trouble is because we don't truely understand
+ * rich text fields yet it's difficult to make a sound comparison.
+ *
+ * @param o The object to compare.
+ * @return true if the object is actually equal.
+ */
public boolean equals(Object o)
{
if ((o == null) || (o.getClass() != this.getClass()))
@@ -96,10 +113,6 @@ public class UnicodeString
&& field_3_string.equals(other.field_3_string));
}
- public UnicodeString()
- {
- }
-
/**
* construct a unicode string record and fill its fields, ID is ignored
* @param id - ignored
@@ -278,19 +291,10 @@ public class UnicodeString
public int serialize(int offset, byte [] data)
{
- int charsize = 1;
-
- if (getOptionFlags() == 1)
- {
- charsize = 2;
- }
-
- // byte[] retval = new byte[ 3 + (getString().length() * charsize) ];
LittleEndian.putShort(data, 0 + offset, getCharCount());
data[ 2 + offset ] = getOptionFlags();
-// System.out.println("Unicode: We've got "+retval[2]+" for our option flag");
- if (getOptionFlags() == 0)
+ if (!isUncompressedUnicode())
{
StringUtil.putCompressedUnicode(getString(), data, 0x3 + offset);
}
@@ -302,14 +306,14 @@ public class UnicodeString
return getRecordSize();
}
- public int getRecordSize()
+ private boolean isUncompressedUnicode()
{
- int charsize = 1;
+ return (getOptionFlags() & 0x01) == 1;
+ }
- if (getOptionFlags() == 1)
- {
- charsize = 2;
- }
+ public int getRecordSize()
+ {
+ int charsize = isUncompressedUnicode() ? 2 : 1;
return 3 + (getString().length() * charsize);
}
@@ -338,11 +342,16 @@ public class UnicodeString
return this.getString().compareTo(str.getString());
}
+ public boolean isRichText()
+ {
+ return (getOptionFlags() & RICH_TEXT_BIT) != 0;
+ }
+
int maxBrokenLength(final int proposedBrokenLength)
{
int rval = proposedBrokenLength;
- if ((field_2_optionflags & 1) == 1)
+ if (isUncompressedUnicode())
{
int proposedStringLength = proposedBrokenLength - 3;
@@ -355,12 +364,4 @@ public class UnicodeString
return rval;
}
-// public boolean equals(Object obj) {
-// if (!(obj instanceof UnicodeString)) return false;
-//
-// UnicodeString str = (UnicodeString)obj;
-//
-//
-// return this.getString().equals(str.getString());
-// }
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
index 39da13e42c..e9209800e3 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
@@ -126,8 +126,8 @@ public class HSSFRow
this.sheet = sheet;
row = new RowRecord();
row.setHeight((short) 0xff);
- row.setLastCol((short)-1);
- row.setFirstCol((short)-1);
+ row.setLastCol((short) -1);
+ row.setFirstCol((short) -1);
// row.setRowNumber(rowNum);
setRowNum(rowNum);
@@ -213,11 +213,11 @@ public class HSSFRow
if (cell.getCellNum() == row.getLastCol())
{
- row.setLastCol( findLastCell(row.getLastCol()) );
+ row.setLastCol(findLastCell(row.getLastCol()));
}
if (cell.getCellNum() == row.getFirstCol())
{
- row.setFirstCol( findFirstCell(row.getFirstCol()) );
+ row.setFirstCol(findFirstCell(row.getFirstCol()));
}
}
@@ -270,11 +270,11 @@ public class HSSFRow
{
if (row.getFirstCol() == -1)
{
- row.setFirstCol( cell.getCellNum() );
+ row.setFirstCol(cell.getCellNum());
}
if (row.getLastCol() == -1)
{
- row.setLastCol( cell.getCellNum() );
+ row.setLastCol(cell.getCellNum());
}
cells.put(new Integer(cell.getCellNum()), cell);
@@ -292,8 +292,8 @@ public class HSSFRow
* get the hssfcell representing a given column (logical cell) 0-based. If you
* ask for a cell that is not defined....you get a null.
*
- * @param cellnum - 0 based column number
- * @returns HSSFCell representing that column or null if undefined.
+ * @param cellnum 0 based column number
+ * @return HSSFCell representing that column or null if undefined.
*/
public HSSFCell getCell(short cellnum)
@@ -318,7 +318,10 @@ public class HSSFRow
public short getFirstCellNum()
{
- return row.getFirstCol();
+ if (getPhysicalNumberOfCells() == 0)
+ return -1;
+ else
+ return row.getFirstCol();
}
/**
@@ -328,7 +331,10 @@ public class HSSFRow
public short getLastCellNum()
{
- return row.getLastCol();
+ if (getPhysicalNumberOfCells() == 0)
+ return -1;
+ else
+ return row.getLastCol();
}
@@ -441,7 +447,7 @@ public class HSSFRow
}
/**
- * @returns cell iterator of the physically defined cells. Note element 4 may
+ * @return cell iterator of the physically defined cells. Note element 4 may
* actually be row cell depending on how many are defined!
*/
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index ddb5dc8a65..c6cb967622 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -1,4 +1,3 @@
-
/* ====================================================================
* The Apache Software License, Version 1.1
*
@@ -60,11 +59,14 @@
*/
package org.apache.poi.hssf.usermodel;
-import org.apache.poi.util.POILogFactory;
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.RowRecord;
+import org.apache.poi.hssf.record.VCenterRecord;
+import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.util.Region;
+import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import java.util.Iterator;
@@ -74,12 +76,12 @@ import java.util.TreeMap;
* High level representation of a worksheet.
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
- * @version 1.0-pre
+ * @author Libin Roman (romal at vistaportal.com)
*/
public class HSSFSheet
{
- private static final int DEBUG = POILogger.DEBUG;
+ private static final int DEBUG = POILogger.DEBUG;
/**
* Used for compile-time optimization. This is the initial size for the collection of
@@ -87,17 +89,17 @@ public class HSSFSheet
* by setting this to a higher number and recompiling a custom edition of HSSFSheet.
*/
- public final static int INITIAL_CAPACITY = 20;
+ public final static int INITIAL_CAPACITY = 20;
/**
* reference to the low level Sheet object
*/
- private Sheet sheet;
- private TreeMap rows;
- private Workbook book;
- private int firstrow;
- private int lastrow;
+ private Sheet sheet;
+ private TreeMap rows;
+ private Workbook book;
+ private int firstrow;
+ private int lastrow;
private static POILogger log = POILogFactory.getLogger(HSSFSheet.class);
/**
@@ -110,8 +112,8 @@ public class HSSFSheet
protected HSSFSheet(Workbook book)
{
- sheet = Sheet.createSheet();
- rows = new TreeMap(); // new ArrayList(INITIAL_CAPACITY);
+ sheet = Sheet.createSheet();
+ rows = new TreeMap(); // new ArrayList(INITIAL_CAPACITY);
this.book = book;
}
@@ -127,16 +129,11 @@ public class HSSFSheet
protected HSSFSheet(Workbook book, Sheet sheet)
{
this.sheet = sheet;
- rows = new TreeMap();
- this.book = book;
+ rows = new TreeMap();
+ this.book = book;
setPropertiesFromSheet(sheet);
}
- /** private default constructor prevents bogus initializationless construction */
-
- private HSSFSheet()
- {
- }
/**
* used internally to set the properties given a Sheet object
@@ -144,8 +141,8 @@ public class HSSFSheet
private void setPropertiesFromSheet(Sheet sheet)
{
- int sloc = sheet.getLoc();
- RowRecord row = sheet.getNextRow();
+ int sloc = sheet.getLoc();
+ RowRecord row = sheet.getNextRow();
while (row != null)
{
@@ -154,8 +151,8 @@ public class HSSFSheet
row = sheet.getNextRow();
}
sheet.setLoc(sloc);
- CellValueRecordInterface cval = sheet.getNextValueRecord();
- long timestart = System.currentTimeMillis();
+ CellValueRecordInterface cval = sheet.getNextValueRecord();
+ long timestart = System.currentTimeMillis();
log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ",
new Long(timestart));
@@ -163,8 +160,8 @@ public class HSSFSheet
while (cval != null)
{
- long cellstart = System.currentTimeMillis();
- HSSFRow hrow = lastrow;
+ long cellstart = System.currentTimeMillis();
+ HSSFRow hrow = lastrow;
if ((lastrow == null) || (lastrow.getRowNum() != cval.getRow()))
{
@@ -236,10 +233,10 @@ public class HSSFSheet
while (iter.hasNext())
{
- HSSFCell cell = ( HSSFCell ) iter.next();
+ HSSFCell cell = (HSSFCell) iter.next();
sheet.removeValueRecord(row.getRowNum(),
- cell.getCellValueRecord());
+ cell.getCellValueRecord());
}
sheet.removeRow(row.getRowRecord());
}
@@ -251,10 +248,10 @@ public class HSSFSheet
private int findLastRow(int lastrow)
{
- int rownum = lastrow - 1;
- HSSFRow r = getRow(rownum);
+ int rownum = lastrow - 1;
+ HSSFRow r = getRow(rownum);
- while (r == null)
+ while (r == null && rownum >= 0)
{
r = getRow(--rownum);
}
@@ -267,13 +264,17 @@ public class HSSFSheet
private int findFirstRow(int firstrow)
{
- int rownum = firstrow + 1;
- HSSFRow r = getRow(rownum);
+ int rownum = firstrow + 1;
+ HSSFRow r = getRow(rownum);
- while (r == null)
+ while (r == null && rownum <= getLastRowNum())
{
r = getRow(++rownum);
}
+
+ if (rownum > getLastRowNum())
+ return -1;
+
return rownum;
}
@@ -311,8 +312,8 @@ public class HSSFSheet
{
HSSFRow row = new HSSFRow();
- row.setRowNum(( short ) rownum);
- return ( HSSFRow ) rows.get(row);
+ row.setRowNum((short) rownum);
+ return (HSSFRow) rows.get(row);
}
/**
@@ -345,26 +346,6 @@ public class HSSFSheet
}
/**
- * Seems to be unused (gjs)
- *
- * used internally to add cells from a high level row to the low level model
- * @param row the row object to represent in low level RowRecord.
- */
- private void addCellsFromRow(HSSFRow row)
- {
- Iterator iter = row.cellIterator();
-
- // for (int k = 0; k < row.getPhysicalNumberOfCells(); k++)
- while (iter.hasNext())
- {
- HSSFCell cell =
- ( HSSFCell ) iter.next(); // row.getPhysicalCellAt(k);
-
- sheet.addValueRecord(row.getRowNum(), cell.getCellValueRecord());
- }
- }
-
- /**
* set the width (in units of 1/256th of a character width)
* @param column - the column to set (0-based)
* @param width - the width in units of 1/256th of a character width
@@ -400,7 +381,7 @@ public class HSSFSheet
/**
* get the default row height for the sheet (if the rows do not define their own height) in
* twips (1/20 of a point)
- * @retun default row height
+ * @return default row height
*/
public short getDefaultRowHeight()
@@ -449,7 +430,7 @@ public class HSSFSheet
public void setDefaultRowHeightInPoints(float height)
{
- sheet.setDefaultRowHeight(( short ) (height * 20));
+ sheet.setDefaultRowHeight((short) (height * 20));
}
/**
@@ -480,10 +461,10 @@ public class HSSFSheet
public int addMergedRegion(Region region)
{
- return sheet.addMergedRegion(( short ) region.getRowFrom(),
- region.getColumnFrom(),
- ( short ) region.getRowTo(),
- region.getColumnTo());
+ return sheet.addMergedRegion((short) region.getRowFrom(),
+ region.getColumnFrom(),
+ (short) region.getRowTo(),
+ region.getColumnTo());
}
/**
@@ -494,7 +475,7 @@ public class HSSFSheet
public void setVerticallyCenter(boolean value)
{
VCenterRecord record =
- ( VCenterRecord ) sheet.findFirstRecordBySid(VCenterRecord.sid);
+ (VCenterRecord) sheet.findFirstRecordBySid(VCenterRecord.sid);
record.setVCenter(value);
}
@@ -506,7 +487,7 @@ public class HSSFSheet
public boolean getVerticallyCenter(boolean value)
{
VCenterRecord record =
- ( VCenterRecord ) sheet.findFirstRecordBySid(VCenterRecord.sid);
+ (VCenterRecord) sheet.findFirstRecordBySid(VCenterRecord.sid);
return record.getVCenter();
}
@@ -543,7 +524,7 @@ public class HSSFSheet
}
/**
- * @returns an iterator of the PHYSICAL rows. Meaning the 3rd element may not
+ * @return an iterator of the PHYSICAL rows. Meaning the 3rd element may not
* be the third row if say for instance the second row is undefined.
*/
@@ -571,7 +552,7 @@ public class HSSFSheet
public void setAlternativeExpression(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setAlternateExpression(b);
}
@@ -584,7 +565,7 @@ public class HSSFSheet
public void setAlternativeFormula(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setAlternateFormula(b);
}
@@ -597,7 +578,7 @@ public class HSSFSheet
public void setAutobreaks(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setAutobreaks(b);
}
@@ -610,7 +591,7 @@ public class HSSFSheet
public void setDialog(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setDialog(b);
}
@@ -624,7 +605,7 @@ public class HSSFSheet
public void setDisplayGuts(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setDisplayGuts(b);
}
@@ -637,7 +618,7 @@ public class HSSFSheet
public void setFitToPage(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setFitToPage(b);
}
@@ -650,7 +631,7 @@ public class HSSFSheet
public void setRowSumsBelow(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setRowSumsBelow(b);
}
@@ -663,7 +644,7 @@ public class HSSFSheet
public void setRowSumsRight(boolean b)
{
WSBoolRecord record =
- ( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
record.setRowSumsRight(b);
}
@@ -675,8 +656,8 @@ public class HSSFSheet
public boolean getAlternateExpression()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getAlternateExpression();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getAlternateExpression();
}
/**
@@ -686,8 +667,8 @@ public class HSSFSheet
public boolean getAlternateFormula()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getAlternateFormula();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getAlternateFormula();
}
/**
@@ -697,8 +678,8 @@ public class HSSFSheet
public boolean getAutobreaks()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getAutobreaks();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getAutobreaks();
}
/**
@@ -708,8 +689,8 @@ public class HSSFSheet
public boolean getDialog()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getDialog();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getDialog();
}
/**
@@ -720,8 +701,8 @@ public class HSSFSheet
public boolean getDisplayGuts()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getDisplayGuts();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getDisplayGuts();
}
/**
@@ -731,8 +712,8 @@ public class HSSFSheet
public boolean getFitToPage()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getFitToPage();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getFitToPage();
}
/**
@@ -742,8 +723,8 @@ public class HSSFSheet
public boolean getRowSumsBelow()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getRowSumsBelow();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getRowSumsBelow();
}
/**
@@ -753,7 +734,7 @@ public class HSSFSheet
public boolean getRowSumsRight()
{
- return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
- .getRowSumsRight();
+ return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
+ .getRowSumsRight();
}
}
diff --git a/src/java/org/apache/poi/util/LittleEndian.java b/src/java/org/apache/poi/util/LittleEndian.java
index 6c7a4f4d23..2346a0a27f 100644
--- a/src/java/org/apache/poi/util/LittleEndian.java
+++ b/src/java/org/apache/poi/util/LittleEndian.java
@@ -592,4 +592,17 @@ public class LittleEndian
return copy;
}
+ /**
+ * Retrieves and unsigned short. This is converted UP to a int
+ * so it can fit.
+ *
+ * @param data The data to read
+ * @param offset The offset to read the short from
+ * @return An integer representation of the short.
+ */
+ public static int getUShort( byte[] data, int offset )
+ {
+ return (int)getNumber(data, offset, SHORT_SIZE);
+ }
+
}
diff --git a/src/testcases/org/apache/poi/hssf/data/duprich1.xls b/src/testcases/org/apache/poi/hssf/data/duprich1.xls
new file mode 100644
index 0000000000..3fddbedd27
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/duprich1.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/duprich2.xls b/src/testcases/org/apache/poi/hssf/data/duprich2.xls
new file mode 100644
index 0000000000..57af63b3d4
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/duprich2.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java
index 56e6fec764..f6963e137b 100644
--- a/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java
+++ b/src/testcases/org/apache/poi/hssf/record/TestSSTRecord.java
@@ -1,4 +1,3 @@
-
/* ====================================================================
* The Apache Software License, Version 1.1
*
@@ -55,24 +54,28 @@
package org.apache.poi.hssf.record;
-import org.apache.poi.util.*;
-
-import junit.framework.*;
+import junit.framework.TestCase;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
import java.io.*;
-
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
/**
* @author Marc Johnson (mjohnson at apache dot org)
*/
public class TestSSTRecord
- extends TestCase
+ extends TestCase
{
- private String _test_file_path;
+ private String _test_file_path;
private static final String _test_file_path_property =
- "HSSF.testdata.path";
+ "HSSF.testdata.path";
/**
* Creates new TestSSTRecord
@@ -80,10 +83,10 @@ public class TestSSTRecord
* @param name
*/
- public TestSSTRecord(String name)
+ public TestSSTRecord( String name )
{
- super(name);
- _test_file_path = System.getProperty(_test_file_path_property);
+ super( name );
+ _test_file_path = System.getProperty( _test_file_path_property );
}
/**
@@ -93,118 +96,118 @@ public class TestSSTRecord
*/
public void testProcessContinueRecord()
- throws IOException
+ throws IOException
{
- byte[] testdata = readTestData("BigSSTRecord");
- byte[] input = new byte[ testdata.length - 4 ];
-
- System.arraycopy(testdata, 4, input, 0, input.length);
- SSTRecord record =
- new SSTRecord(LittleEndian.getShort(testdata, 0),
- LittleEndian.getShort(testdata, 2), input);
- byte[] continueRecord = readTestData("BigSSTRecordCR");
-
- input = new byte[ continueRecord.length - 4 ];
- System.arraycopy(continueRecord, 4, input, 0, input.length);
- record.processContinueRecord(input);
- assertEquals(1464, record.getNumStrings());
- assertEquals(688, record.getNumUniqueStrings());
- assertEquals(688, record.countStrings());
+ byte[] testdata = readTestData( "BigSSTRecord" );
+ byte[] input = new byte[testdata.length - 4];
+
+ System.arraycopy( testdata, 4, input, 0, input.length );
+ SSTRecord record =
+ new SSTRecord( LittleEndian.getShort( testdata, 0 ),
+ LittleEndian.getShort( testdata, 2 ), input );
+ byte[] continueRecord = readTestData( "BigSSTRecordCR" );
+
+ input = new byte[continueRecord.length - 4];
+ System.arraycopy( continueRecord, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ assertEquals( 1464, record.getNumStrings() );
+ assertEquals( 688, record.getNumUniqueStrings() );
+ assertEquals( 688, record.countStrings() );
byte[] ser_output = record.serialize();
- int offset = 0;
- short type = LittleEndian.getShort(ser_output, offset);
+ int offset = 0;
+ short type = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
- short length = LittleEndian.getShort(ser_output, offset);
+ short length = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
- byte[] recordData = new byte[ length ];
+ byte[] recordData = new byte[length];
- System.arraycopy(ser_output, offset, recordData, 0, length);
+ System.arraycopy( ser_output, offset, recordData, 0, length );
offset += length;
- SSTRecord testRecord = new SSTRecord(type, length, recordData);
+ SSTRecord testRecord = new SSTRecord( type, length, recordData );
- assertEquals(ContinueRecord.sid,
- LittleEndian.getShort(ser_output, offset));
+ assertEquals( ContinueRecord.sid,
+ LittleEndian.getShort( ser_output, offset ) );
offset += LittleEndianConsts.SHORT_SIZE;
- length = LittleEndian.getShort(ser_output, offset);
+ length = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
- byte[] cr = new byte[ length ];
+ byte[] cr = new byte[length];
- System.arraycopy(ser_output, offset, cr, 0, length);
+ System.arraycopy( ser_output, offset, cr, 0, length );
offset += length;
- assertEquals(offset, ser_output.length);
- testRecord.processContinueRecord(cr);
- assertEquals(record, testRecord);
+ assertEquals( offset, ser_output.length );
+ testRecord.processContinueRecord( cr );
+ assertEquals( record, testRecord );
// testing based on new bug report
- testdata = readTestData("BigSSTRecord2");
- input = new byte[ testdata.length - 4 ];
- System.arraycopy(testdata, 4, input, 0, input.length);
- record = new SSTRecord(LittleEndian.getShort(testdata, 0),
- LittleEndian.getShort(testdata, 2), input);
- byte[] continueRecord1 = readTestData("BigSSTRecord2CR1");
-
- input = new byte[ continueRecord1.length - 4 ];
- System.arraycopy(continueRecord1, 4, input, 0, input.length);
- record.processContinueRecord(input);
- byte[] continueRecord2 = readTestData("BigSSTRecord2CR2");
-
- input = new byte[ continueRecord2.length - 4 ];
- System.arraycopy(continueRecord2, 4, input, 0, input.length);
- record.processContinueRecord(input);
- byte[] continueRecord3 = readTestData("BigSSTRecord2CR3");
-
- input = new byte[ continueRecord3.length - 4 ];
- System.arraycopy(continueRecord3, 4, input, 0, input.length);
- record.processContinueRecord(input);
- byte[] continueRecord4 = readTestData("BigSSTRecord2CR4");
-
- input = new byte[ continueRecord4.length - 4 ];
- System.arraycopy(continueRecord4, 4, input, 0, input.length);
- record.processContinueRecord(input);
- byte[] continueRecord5 = readTestData("BigSSTRecord2CR5");
-
- input = new byte[ continueRecord5.length - 4 ];
- System.arraycopy(continueRecord5, 4, input, 0, input.length);
- record.processContinueRecord(input);
- byte[] continueRecord6 = readTestData("BigSSTRecord2CR6");
-
- input = new byte[ continueRecord6.length - 4 ];
- System.arraycopy(continueRecord6, 4, input, 0, input.length);
- record.processContinueRecord(input);
- byte[] continueRecord7 = readTestData("BigSSTRecord2CR7");
-
- input = new byte[ continueRecord7.length - 4 ];
- System.arraycopy(continueRecord7, 4, input, 0, input.length);
- record.processContinueRecord(input);
- assertEquals(158642, record.getNumStrings());
- assertEquals(5249, record.getNumUniqueStrings());
- assertEquals(5249, record.countStrings());
+ testdata = readTestData( "BigSSTRecord2" );
+ input = new byte[testdata.length - 4];
+ System.arraycopy( testdata, 4, input, 0, input.length );
+ record = new SSTRecord( LittleEndian.getShort( testdata, 0 ),
+ LittleEndian.getShort( testdata, 2 ), input );
+ byte[] continueRecord1 = readTestData( "BigSSTRecord2CR1" );
+
+ input = new byte[continueRecord1.length - 4];
+ System.arraycopy( continueRecord1, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ byte[] continueRecord2 = readTestData( "BigSSTRecord2CR2" );
+
+ input = new byte[continueRecord2.length - 4];
+ System.arraycopy( continueRecord2, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ byte[] continueRecord3 = readTestData( "BigSSTRecord2CR3" );
+
+ input = new byte[continueRecord3.length - 4];
+ System.arraycopy( continueRecord3, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ byte[] continueRecord4 = readTestData( "BigSSTRecord2CR4" );
+
+ input = new byte[continueRecord4.length - 4];
+ System.arraycopy( continueRecord4, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ byte[] continueRecord5 = readTestData( "BigSSTRecord2CR5" );
+
+ input = new byte[continueRecord5.length - 4];
+ System.arraycopy( continueRecord5, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ byte[] continueRecord6 = readTestData( "BigSSTRecord2CR6" );
+
+ input = new byte[continueRecord6.length - 4];
+ System.arraycopy( continueRecord6, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ byte[] continueRecord7 = readTestData( "BigSSTRecord2CR7" );
+
+ input = new byte[continueRecord7.length - 4];
+ System.arraycopy( continueRecord7, 4, input, 0, input.length );
+ record.processContinueRecord( input );
+ assertEquals( 158642, record.getNumStrings() );
+ assertEquals( 5249, record.getNumUniqueStrings() );
+ assertEquals( 5249, record.countStrings() );
ser_output = record.serialize();
- offset = 0;
- type = LittleEndian.getShort(ser_output, offset);
- offset += LittleEndianConsts.SHORT_SIZE;
- length = LittleEndian.getShort(ser_output, offset);
- offset += LittleEndianConsts.SHORT_SIZE;
- recordData = new byte[ length ];
- System.arraycopy(ser_output, offset, recordData, 0, length);
- offset += length;
- testRecord = new SSTRecord(type, length, recordData);
- for (int count = 0; count < 7; count++)
+ offset = 0;
+ type = LittleEndian.getShort( ser_output, offset );
+ offset += LittleEndianConsts.SHORT_SIZE;
+ length = LittleEndian.getShort( ser_output, offset );
+ offset += LittleEndianConsts.SHORT_SIZE;
+ recordData = new byte[length];
+ System.arraycopy( ser_output, offset, recordData, 0, length );
+ offset += length;
+ testRecord = new SSTRecord( type, length, recordData );
+ for ( int count = 0; count < 7; count++ )
{
- assertEquals(ContinueRecord.sid,
- LittleEndian.getShort(ser_output, offset));
+ assertEquals( ContinueRecord.sid,
+ LittleEndian.getShort( ser_output, offset ) );
offset += LittleEndianConsts.SHORT_SIZE;
- length = LittleEndian.getShort(ser_output, offset);
+ length = LittleEndian.getShort( ser_output, offset );
offset += LittleEndianConsts.SHORT_SIZE;
- cr = new byte[ length ];
- System.arraycopy(ser_output, offset, cr, 0, length);
- testRecord.processContinueRecord(cr);
+ cr = new byte[length];
+ System.arraycopy( ser_output, offset, cr, 0, length );
+ testRecord.processContinueRecord( cr );
offset += length;
}
- assertEquals(offset, ser_output.length);
- assertEquals(record, testRecord);
+ assertEquals( offset, ser_output.length );
+ assertEquals( record, testRecord );
}
/**
@@ -214,23 +217,23 @@ public class TestSSTRecord
*/
public void testHugeStrings()
- throws IOException
+ throws IOException
{
- SSTRecord record = new SSTRecord();
- byte[][] bstrings =
- {
- new byte[ 9000 ], new byte[ 7433 ], new byte[ 9002 ],
- new byte[ 16998 ]
- };
- String[] strings = new String[ bstrings.length ];
- int total_length = 0;
-
- for (int k = 0; k < bstrings.length; k++)
+ SSTRecord record = new SSTRecord();
+ byte[][] bstrings =
+ {
+ new byte[9000], new byte[7433], new byte[9002],
+ new byte[16998]
+ };
+ String[] strings = new String[bstrings.length];
+ int total_length = 0;
+
+ for ( int k = 0; k < bstrings.length; k++ )
{
- Arrays.fill(bstrings[ k ], ( byte ) ('a' + k));
- strings[ k ] = new String(bstrings[ k ]);
- record.addString(strings[ k ]);
- total_length += 3 + bstrings[ k ].length;
+ Arrays.fill( bstrings[k], (byte) ( 'a' + k ) );
+ strings[k] = new String( bstrings[k] );
+ record.addString( strings[k] );
+ total_length += 3 + bstrings[k].length;
}
// add overhead of SST record
@@ -240,88 +243,88 @@ public class TestSSTRecord
total_length += 4;
// add overhead of six records
- total_length += (6 * 4);
- byte[] content = new byte[ record.getRecordSize() ];
+ total_length += ( 6 * 4 );
+ byte[] content = new byte[record.getRecordSize()];
- record.serialize(0, content);
- assertEquals(total_length, content.length);
- for (int index = 0; index != content.length; )
+ record.serialize( 0, content );
+ assertEquals( total_length, content.length );
+ for ( int index = 0; index != content.length; )
{
- short record_type = LittleEndian.getShort(content, index);
+ short record_type = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
- short record_length = LittleEndian.getShort(content, index);
+ short record_length = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
- byte[] data = new byte[ record_length ];
+ byte[] data = new byte[record_length];
- System.arraycopy(content, index, data, 0, record_length);
+ System.arraycopy( content, index, data, 0, record_length );
index += record_length;
- if (record_type == SSTRecord.sid)
+ if ( record_type == SSTRecord.sid )
{
- record = new SSTRecord(record_type, record_length, data);
+ record = new SSTRecord( record_type, record_length, data );
}
else
{
- record.processContinueRecord(data);
+ record.processContinueRecord( data );
}
}
- assertEquals(strings.length, record.getNumStrings());
- assertEquals(strings.length, record.getNumUniqueStrings());
- assertEquals(strings.length, record.countStrings());
- for (int k = 0; k < strings.length; k++)
+ assertEquals( strings.length, record.getNumStrings() );
+ assertEquals( strings.length, record.getNumUniqueStrings() );
+ assertEquals( strings.length, record.countStrings() );
+ for ( int k = 0; k < strings.length; k++ )
{
- assertEquals(strings[ k ], record.getString(k));
+ assertEquals( strings[k], record.getString( k ) );
}
- record = new SSTRecord();
- bstrings[ 1 ] = new byte[ bstrings[ 1 ].length - 1 ];
- for (int k = 0; k < bstrings.length; k++)
+ record = new SSTRecord();
+ bstrings[1] = new byte[bstrings[1].length - 1];
+ for ( int k = 0; k < bstrings.length; k++ )
{
- if ((bstrings[ k ].length % 2) == 1)
+ if ( ( bstrings[k].length % 2 ) == 1 )
{
- Arrays.fill(bstrings[ k ], ( byte ) ('a' + k));
- strings[ k ] = new String(bstrings[ k ]);
+ Arrays.fill( bstrings[k], (byte) ( 'a' + k ) );
+ strings[k] = new String( bstrings[k] );
}
else
{
- char[] data = new char[ bstrings[ k ].length / 2 ];
+ char[] data = new char[bstrings[k].length / 2];
- Arrays.fill(data, ( char ) ('\u2122' + k));
- strings[ k ] = new String(data);
+ Arrays.fill( data, (char) ( '\u2122' + k ) );
+ strings[k] = new String( data );
}
- record.addString(strings[ k ]);
+ record.addString( strings[k] );
}
- content = new byte[ record.getRecordSize() ];
- record.serialize(0, content);
+ content = new byte[record.getRecordSize()];
+ record.serialize( 0, content );
total_length--;
- assertEquals(total_length, content.length);
- for (int index = 0; index != content.length; )
+ assertEquals( total_length, content.length );
+ for ( int index = 0; index != content.length; )
{
- short record_type = LittleEndian.getShort(content, index);
+ short record_type = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
- short record_length = LittleEndian.getShort(content, index);
+ short record_length = LittleEndian.getShort( content, index );
index += LittleEndianConsts.SHORT_SIZE;
- byte[] data = new byte[ record_length ];
+ byte[] data = new byte[record_length];
- System.arraycopy(content, index, data, 0, record_length);
+ System.arraycopy( content, index, data, 0, record_length );
index += record_length;
- if (record_type == SSTRecord.sid)
+ if ( record_type == SSTRecord.sid )
{
- record = new SSTRecord(record_type, record_length, data);
+ record = new SSTRecord( record_type, record_length, data );
}
else
{
- record.processContinueRecord(data);
+ record.processContinueRecord( data );
}
}
- assertEquals(strings.length, record.getNumStrings());
- assertEquals(strings.length, record.getNumUniqueStrings());
- assertEquals(strings.length, record.countStrings());
- for (int k = 0; k < strings.length; k++)
+ assertEquals( strings.length, record.getNumStrings() );
+ assertEquals( strings.length, record.getNumUniqueStrings() );
+ assertEquals( strings.length, record.countStrings() );
+ for ( int k = 0; k < strings.length; k++ )
{
- assertEquals(strings[ k ], record.getString(k));
+ assertEquals( strings[k], record.getString( k ) );
}
}
@@ -332,7 +335,7 @@ public class TestSSTRecord
*/
public void testSSTRecordBug()
- throws IOException
+ throws IOException
{
// create an SSTRecord and write a certain pattern of strings
@@ -342,22 +345,22 @@ public class TestSSTRecord
// the record will start with two integers, then this string
// ... that will eat up 16 of the 8224 bytes that the record
// can hold
- record.addString("Hello");
+ record.addString( "Hello" );
// now we have an additional 8208 bytes, which is an exact
// multiple of 16 bytes
long testvalue = 1000000000000L;
- for (int k = 0; k < 2000; k++)
+ for ( int k = 0; k < 2000; k++ )
{
- record.addString(String.valueOf(testvalue++));
+ record.addString( String.valueOf( testvalue++ ) );
}
- byte[] content = new byte[ record.getRecordSize() ];
+ byte[] content = new byte[record.getRecordSize()];
- record.serialize(0, content);
- assertEquals(( byte ) 13, content[ 4 + 8228 ]);
- assertEquals(( byte ) 13, content[ 4 + 8228 * 2 ]);
- assertEquals(( byte ) 13, content[ 4 + 8228 * 3 ]);
+ record.serialize( 0, content );
+ assertEquals( (byte) 13, content[4 + 8228] );
+ assertEquals( (byte) 13, content[4 + 8228 * 2] );
+ assertEquals( (byte) 13, content[4 + 8228 * 3] );
}
/**
@@ -367,43 +370,43 @@ public class TestSSTRecord
public void testSimpleAddString()
{
SSTRecord record = new SSTRecord();
- String s1 = "Hello world";
+ String s1 = "Hello world";
// \u2122 is the encoding of the trademark symbol ...
- String s2 = "Hello world\u2122";
-
- assertEquals(0, record.addString(s1));
- assertEquals(s1, record.getString(0));
- assertEquals(1, record.countStrings());
- assertEquals(1, record.getNumStrings());
- assertEquals(1, record.getNumUniqueStrings());
- assertEquals(0, record.addString(s1));
- assertEquals(s1, record.getString(0));
- assertEquals(1, record.countStrings());
- assertEquals(2, record.getNumStrings());
- assertEquals(1, record.getNumUniqueStrings());
- assertEquals(1, record.addString(s2));
- assertEquals(s2, record.getString(1));
- assertEquals(2, record.countStrings());
- assertEquals(3, record.getNumStrings());
- assertEquals(2, record.getNumUniqueStrings());
+ String s2 = "Hello world\u2122";
+
+ assertEquals( 0, record.addString( s1 ) );
+ assertEquals( s1, record.getString( 0 ) );
+ assertEquals( 1, record.countStrings() );
+ assertEquals( 1, record.getNumStrings() );
+ assertEquals( 1, record.getNumUniqueStrings() );
+ assertEquals( 0, record.addString( s1 ) );
+ assertEquals( s1, record.getString( 0 ) );
+ assertEquals( 1, record.countStrings() );
+ assertEquals( 2, record.getNumStrings() );
+ assertEquals( 1, record.getNumUniqueStrings() );
+ assertEquals( 1, record.addString( s2 ) );
+ assertEquals( s2, record.getString( 1 ) );
+ assertEquals( 2, record.countStrings() );
+ assertEquals( 3, record.getNumStrings() );
+ assertEquals( 2, record.getNumUniqueStrings() );
Iterator iter = record.getStrings();
- while (iter.hasNext())
+ while ( iter.hasNext() )
{
- UnicodeString ucs = ( UnicodeString ) iter.next();
+ UnicodeString ucs = (UnicodeString) iter.next();
- if (ucs.getString().equals(s1))
+ if ( ucs.getString().equals( s1 ) )
{
- assertEquals(( byte ) 0, ucs.getOptionFlags());
+ assertEquals( (byte) 0, ucs.getOptionFlags() );
}
- else if (ucs.getString().equals(s2))
+ else if ( ucs.getString().equals( s2 ) )
{
- assertEquals(( byte ) 1, ucs.getOptionFlags());
+ assertEquals( (byte) 1, ucs.getOptionFlags() );
}
else
{
- fail("cannot match string: " + ucs.getString());
+ fail( "cannot match string: " + ucs.getString() );
}
}
}
@@ -415,25 +418,25 @@ public class TestSSTRecord
*/
public void testReaderConstructor()
- throws IOException
+ throws IOException
{
- byte[] testdata = readTestData("BigSSTRecord");
- byte[] input = new byte[ testdata.length - 4 ];
-
- System.arraycopy(testdata, 4, input, 0, input.length);
- SSTRecord record = new SSTRecord(LittleEndian.getShort(testdata, 0),
- LittleEndian.getShort(testdata, 2),
- input);
-
- assertEquals(1464, record.getNumStrings());
- assertEquals(688, record.getNumUniqueStrings());
- assertEquals(492, record.countStrings());
- assertEquals(1, record.getExpectedChars());
- assertEquals("Consolidated B-24J Liberator The Dragon & His Tai",
- record.getUnfinishedString());
- assertEquals(52, record.getTotalLength());
- assertEquals(3, record.getStringDataOffset());
- assertTrue(!record.isWideChar());
+ byte[] testdata = readTestData( "BigSSTRecord" );
+ byte[] input = new byte[testdata.length - 4];
+
+ System.arraycopy( testdata, 4, input, 0, input.length );
+ SSTRecord record = new SSTRecord( LittleEndian.getShort( testdata, 0 ),
+ LittleEndian.getShort( testdata, 2 ),
+ input );
+
+ assertEquals( 1464, record.getNumStrings() );
+ assertEquals( 688, record.getNumUniqueStrings() );
+ assertEquals( 492, record.countStrings() );
+ assertEquals( 1, record.getDeserializer().getExpectedChars() );
+ assertEquals( "Consolidated B-24J Liberator The Dragon & His Tai",
+ record.getDeserializer().getUnfinishedString() );
+ assertEquals( 52, record.getDeserializer().getTotalLength() );
+ assertEquals( 3, record.getDeserializer().getStringDataOffset() );
+ assertTrue( !record.getDeserializer().isWideChar() );
}
/**
@@ -444,26 +447,26 @@ public class TestSSTRecord
{
SSTRecord record = new SSTRecord();
- assertEquals(0, record.getNumStrings());
- assertEquals(0, record.getNumUniqueStrings());
- assertEquals(0, record.countStrings());
- assertEquals(0, record.getExpectedChars());
- assertEquals("", record.getUnfinishedString());
- assertEquals(0, record.getTotalLength());
- assertEquals(0, record.getStringDataOffset());
- assertTrue(!record.isWideChar());
- byte[] output = record.serialize();
+ assertEquals( 0, record.getNumStrings() );
+ assertEquals( 0, record.getNumUniqueStrings() );
+ assertEquals( 0, record.countStrings() );
+ assertEquals( 0, record.getDeserializer().getExpectedChars() );
+ assertEquals( "", record.getDeserializer().getUnfinishedString() );
+ assertEquals( 0, record.getDeserializer().getTotalLength() );
+ assertEquals( 0, record.getDeserializer().getStringDataOffset() );
+ assertTrue( !record.getDeserializer().isWideChar() );
+ byte[] output = record.serialize();
byte[] expected =
+ {
+ (byte) record.getSid(), (byte) ( record.getSid() >> 8 ),
+ (byte) 8, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+ (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0
+ };
+
+ assertEquals( expected.length, output.length );
+ for ( int k = 0; k < expected.length; k++ )
{
- ( byte ) record.getSid(), ( byte ) (record.getSid() >> 8),
- ( byte ) 8, ( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0,
- ( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0
- };
-
- assertEquals(expected.length, output.length);
- for (int k = 0; k < expected.length; k++)
- {
- assertEquals(String.valueOf(k), expected[ k ], output[ k ]);
+ assertEquals( String.valueOf( k ), expected[k], output[k] );
}
}
@@ -473,87 +476,87 @@ public class TestSSTRecord
* @param ignored_args
*/
- public static void main(String [] ignored_args)
+ public static void main( String[] ignored_args )
{
- System.out.println("Testing hssf.record.SSTRecord functionality");
- junit.textui.TestRunner.run(TestSSTRecord.class);
+ System.out.println( "Testing hssf.record.SSTRecord functionality" );
+ junit.textui.TestRunner.run( TestSSTRecord.class );
}
- private byte [] readTestData(String filename)
- throws IOException
+ private byte[] readTestData( String filename )
+ throws IOException
{
- File file = new File(_test_file_path
- + File.separator
- + filename);
- FileInputStream stream = new FileInputStream(file);
- int characterCount = 0;
- byte b = ( byte ) 0;
- List bytes = new ArrayList();
- boolean done = false;
-
- while (!done)
+ File file = new File( _test_file_path
+ + File.separator
+ + filename );
+ FileInputStream stream = new FileInputStream( file );
+ int characterCount = 0;
+ byte b = (byte) 0;
+ List bytes = new ArrayList();
+ boolean done = false;
+
+ while ( !done )
{
int count = stream.read();
- switch (count)
+ switch ( count )
{
- case '0' :
- case '1' :
- case '2' :
- case '3' :
- case '4' :
- case '5' :
- case '6' :
- case '7' :
- case '8' :
- case '9' :
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
b <<= 4;
- b += ( byte ) (count - '0');
+ b += (byte) ( count - '0' );
characterCount++;
- if (characterCount == 2)
+ if ( characterCount == 2 )
{
- bytes.add(new Byte(b));
+ bytes.add( new Byte( b ) );
characterCount = 0;
- b = ( byte ) 0;
+ b = (byte) 0;
}
break;
- case 'A' :
- case 'B' :
- case 'C' :
- case 'D' :
- case 'E' :
- case 'F' :
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
b <<= 4;
- b += ( byte ) (count + 10 - 'A');
+ b += (byte) ( count + 10 - 'A' );
characterCount++;
- if (characterCount == 2)
+ if ( characterCount == 2 )
{
- bytes.add(new Byte(b));
+ bytes.add( new Byte( b ) );
characterCount = 0;
- b = ( byte ) 0;
+ b = (byte) 0;
}
break;
- case 'a' :
- case 'b' :
- case 'c' :
- case 'd' :
- case 'e' :
- case 'f' :
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
b <<= 4;
- b += ( byte ) (count + 10 - 'a');
+ b += (byte) ( count + 10 - 'a' );
characterCount++;
- if (characterCount == 2)
+ if ( characterCount == 2 )
{
- bytes.add(new Byte(b));
+ bytes.add( new Byte( b ) );
characterCount = 0;
- b = ( byte ) 0;
+ b = (byte) 0;
}
break;
- case -1 :
+ case -1:
done = true;
break;
@@ -562,13 +565,55 @@ public class TestSSTRecord
}
}
stream.close();
- Byte[] polished = ( Byte [] ) bytes.toArray(new Byte[ 0 ]);
- byte[] rval = new byte[ polished.length ];
+ Byte[] polished = (Byte[]) bytes.toArray( new Byte[0] );
+ byte[] rval = new byte[polished.length];
- for (int j = 0; j < polished.length; j++)
+ for ( int j = 0; j < polished.length; j++ )
{
- rval[ j ] = polished[ j ].byteValue();
+ rval[j] = polished[j].byteValue();
}
return rval;
}
+
+ /**
+ * Tests that workbooks with rich text that duplicates a non rich text cell can be read and written.
+ */
+ public void testReadWriteDuplicatedRichText1()
+ throws Exception
+ {
+ File file = new File( _test_file_path + File.separator + "duprich1.xls" );
+ InputStream stream = new FileInputStream(file);
+ HSSFWorkbook wb = new HSSFWorkbook(stream);
+ stream.close();
+ HSSFSheet sheet = wb.getSheetAt(1);
+ assertEquals("01/05 (Wed) ", sheet.getRow(0).getCell((short)8).getStringCellValue());
+ assertEquals("01/05 (Wed)", sheet.getRow(1).getCell((short)8).getStringCellValue());
+
+ file = File.createTempFile("testout", "xls");
+ FileOutputStream outStream = new FileOutputStream(file);
+ wb.write(outStream);
+ outStream.close();
+ file.delete();
+
+ // test the second file.
+ file = new File( _test_file_path + File.separator + "duprich2.xls" );
+ stream = new FileInputStream(file);
+ wb = new HSSFWorkbook(stream);
+ stream.close();
+ sheet = wb.getSheetAt(0);
+ int row = 0;
+ assertEquals("Testing ", sheet.getRow(row++).getCell((short)0).getStringCellValue());
+ assertEquals("rich", sheet.getRow(row++).getCell((short)0).getStringCellValue());
+ assertEquals("text", sheet.getRow(row++).getCell((short)0).getStringCellValue());
+ assertEquals("strings", sheet.getRow(row++).getCell((short)0).getStringCellValue());
+ assertEquals("Testing ", sheet.getRow(row++).getCell((short)0).getStringCellValue());
+ assertEquals("Testing", sheet.getRow(row++).getCell((short)0).getStringCellValue());
+
+// file = new File("/tryme.xls");
+ file = File.createTempFile("testout", ".xls");
+ outStream = new FileOutputStream(file);
+ wb.write(outStream);
+ outStream.close();
+ file.delete();
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java
index f8c584839a..3c7fb51636 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java
@@ -55,7 +55,10 @@
package org.apache.poi.hssf.usermodel;
import junit.framework.TestCase;
-import org.apache.poi.hssf.record.RowRecord;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
/**
* Test HSSFRow is okay.
@@ -87,33 +90,47 @@ public class TestHSSFRow
assertEquals(1, row.getFirstCellNum());
assertEquals(2, row.getLastCellNum());
- RowRecord rowRecord = new RowRecord();
- rowRecord.setFirstCol((short) 2);
- rowRecord.setLastCol((short) 5);
- row = new HSSFRow(workbook.getWorkbook(), sheet.getSheet(), rowRecord);
- assertEquals(2, row.getFirstCellNum());
- assertEquals(5, row.getLastCellNum());
}
public void testRemoveCell()
+ throws Exception
{
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet();
HSSFRow row = sheet.createRow((short) 0);
assertEquals(-1, row.getLastCellNum());
assertEquals(-1, row.getFirstCellNum());
- row.createCell((short)1);
+ row.createCell((short) 1);
assertEquals(1, row.getLastCellNum());
assertEquals(1, row.getFirstCellNum());
- row.createCell((short)3);
+ row.createCell((short) 3);
assertEquals(3, row.getLastCellNum());
assertEquals(1, row.getFirstCellNum());
- row.removeCell(row.getCell((short)3));
+ row.removeCell(row.getCell((short) 3));
assertEquals(1, row.getLastCellNum());
assertEquals(1, row.getFirstCellNum());
- row.removeCell(row.getCell((short)1));
+ row.removeCell(row.getCell((short) 1));
assertEquals(-1, row.getLastCellNum());
assertEquals(-1, row.getFirstCellNum());
+ // check the row record actually writes it out as 0's
+ byte[] data = new byte[100];
+ row.getRowRecord().serialize(0, data);
+ assertEquals(0, data[6]);
+ assertEquals(0, data[8]);
+
+ File file = File.createTempFile("XXX", "XLS");
+ FileOutputStream stream = new FileOutputStream(file);
+ workbook.write(stream);
+ stream.close();
+ FileInputStream inputStream = new FileInputStream(file);
+ workbook = new HSSFWorkbook(inputStream);
+ sheet = workbook.getSheetAt(0);
+ stream.close();
+ file.delete();
+ assertEquals(-1, sheet.getRow((short) 0).getLastCellNum());
+ assertEquals(-1, sheet.getRow((short) 0).getFirstCellNum());
+
+
}
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
index 4868aee042..ddaedd922e 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
@@ -58,7 +58,6 @@ import junit.framework.TestCase;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.record.VCenterRecord;
import org.apache.poi.hssf.record.WSBoolRecord;
-import org.apache.poi.hssf.dev.BiffViewer;
import java.io.File;
import java.io.FileInputStream;
@@ -190,7 +189,14 @@ public class TestHSSFSheet
tempFile.delete();
assertNotNull(row);
assertEquals(2, row.getPhysicalNumberOfCells());
+ }
-
+ public void testRemoveRow()
+ {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Test boolean");
+ HSSFRow row = sheet.createRow((short) 2);
+ sheet.removeRow(row);
}
+
}
diff --git a/src/testcases/org/apache/poi/util/TestLittleEndian.java b/src/testcases/org/apache/poi/util/TestLittleEndian.java
index 1f2780d897..f9f2df2382 100644
--- a/src/testcases/org/apache/poi/util/TestLittleEndian.java
+++ b/src/testcases/org/apache/poi/util/TestLittleEndian.java
@@ -437,6 +437,12 @@ public class TestLittleEndian
return result;
}
+ public void testUnsignedShort()
+ throws Exception
+ {
+ assertEquals(0xffff, LittleEndian.getUShort(new byte[] { (byte)0xff, (byte)0xff }, 0));
+ }
+
/**
* main method to run the unit tests
*