This commit was manufactured by cvs2svn to create tag
'REL_1_5_BRANCH_MERGE4'. git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/tags/REL_1_5_BRANCH_MERGE4@352686 13f79535-47bb-0310-9956-ffa450edef68
@ -68,8 +68,8 @@
|
|||||||
|
|
||||||
<version major="1"
|
<version major="1"
|
||||||
minor="5"
|
minor="5"
|
||||||
fix ="0"
|
fix ="1"
|
||||||
tag="dev"/>
|
tag="dev"/>
|
||||||
|
|
||||||
<package>org.apache.poi</package>
|
<package>org.apache.poi</package>
|
||||||
|
|
||||||
|
BIN
src/documentation/images/logoRaPiGmbH1.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
src/documentation/images/logoRaPiGmbH2.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
src/documentation/images/logoRaPiGmbH3.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/documentation/images/logoRaPiGmbH4.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
src/documentation/images/logoRaPiGmbH5.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
src/documentation/images/logoRaPiGmbH6.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
src/documentation/images/logoRaPiGmbH7.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
<menu label="Get Involved">
|
<menu label="Get Involved">
|
||||||
<menu-item label="Contributing" href="contrib.html"/>
|
<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="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="CVS" href="http://jakarta.apache.org/site/cvsindex.html"/>
|
||||||
<menu-item label="Mail Lists" href="http://jakarta.apache.org/site/mail.html"/>
|
<menu-item label="Mail Lists" href="http://jakarta.apache.org/site/mail.html"/>
|
||||||
|
97
src/documentation/xdocs/branching.xml
Normal file
@ -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>
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<document>
|
<document>
|
||||||
<header>
|
<header>
|
||||||
<title></title>
|
<title>Project History</title>
|
||||||
<authors>
|
<authors>
|
||||||
<person id="AO" name="Andrew C. Oliver" email="acoliver@apache.org"/>
|
<person id="AO" name="Andrew C. Oliver" email="acoliver@apache.org"/>
|
||||||
</authors>
|
</authors>
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
<li><link href="#MergedCells">Merging cells</link></li>
|
<li><link href="#MergedCells">Merging cells</link></li>
|
||||||
<li><link href="#WorkingWithFonts">Working with fonts</link></li>
|
<li><link href="#WorkingWithFonts">Working with fonts</link></li>
|
||||||
<li><link href="#ReadWriteWorkbook">Reading and writing</link></li>
|
<li><link href="#ReadWriteWorkbook">Reading and writing</link></li>
|
||||||
|
<li><link href="#NewLinesInCells">Use newlines in cells.</link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section title="Features">
|
<section title="Features">
|
||||||
@ -294,6 +295,35 @@
|
|||||||
fileOut.close();
|
fileOut.close();
|
||||||
</source>
|
</source>
|
||||||
</section>
|
</section>
|
||||||
|
<anchor id="UseNewLinesInCells"/>
|
||||||
|
<section title="Using newlines in cells">
|
||||||
|
<source>
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFSheet s = wb.createSheet();
|
||||||
|
HSSFRow r = null;
|
||||||
|
HSSFCell c = null;
|
||||||
|
HSSFCellStyle cs = wb.createCellStyle();
|
||||||
|
HSSFFont f = wb.createFont();
|
||||||
|
HSSFFont f2 = wb.createFont();
|
||||||
|
|
||||||
|
cs = wb.createCellStyle();
|
||||||
|
|
||||||
|
cs.setFont( f2 );
|
||||||
|
//Word Wrap MUST be turned on
|
||||||
|
cs.setWrapText( true );
|
||||||
|
|
||||||
|
r = s.createRow( (short) 2 );
|
||||||
|
r.setHeight( (short) 0x349 );
|
||||||
|
c = r.createCell( (short) 2 );
|
||||||
|
c.setCellType( HSSFCell.CELL_TYPE_STRING );
|
||||||
|
c.setCellValue( "Use \n with word wrap on to create a new line" );
|
||||||
|
c.setCellStyle( cs );
|
||||||
|
s.setColumnWidth( (short) 2, (short) ( ( 50 * 8 ) / ( (double) 1 / 20 ) ) );
|
||||||
|
|
||||||
|
FileOutputStream fileOut = new FileOutputStream( "workbook.xls" );
|
||||||
|
wb.write( fileOut );
|
||||||
|
fileOut.close();</source>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
|
@ -2,114 +2,139 @@
|
|||||||
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "document-v11.dtd">
|
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "document-v11.dtd">
|
||||||
|
|
||||||
<document>
|
<document>
|
||||||
<header>
|
<header>
|
||||||
<title></title>
|
<title></title>
|
||||||
<authors>
|
<authors>
|
||||||
<person id="AO" name="Andrew C. Oliver" email="acoliver@apache.org"/>
|
<person id="AO" name="Andrew C. Oliver" email="acoliver@apache.org"/>
|
||||||
</authors>
|
<person id="GS" name="Glen Stampoultzis" email="glens@apache.org"/>
|
||||||
</header>
|
</authors>
|
||||||
|
</header>
|
||||||
<body>
|
|
||||||
<section title="POI logos">
|
<body>
|
||||||
<p>
|
<section title="POI logos">
|
||||||
Here are the current logo submissions. Thanks to the artists!
|
<p>
|
||||||
</p>
|
Here are the current logo submissions. Thanks to the artists!
|
||||||
<section title="Michael Mosmann">
|
</p>
|
||||||
|
<section title="Michael Mosmann">
|
||||||
<p>
|
<p>
|
||||||
<img src="images/logoMichaelMosmann.png"/>
|
<img src="images/logoMichaelMosmann.png"/>
|
||||||
</p>
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Loïc Lefèvre">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoLoicLefevre.png"/>
|
||||||
|
<img src="images/logoLoicLefevre2.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Glen Stampoultzis">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoGlenStampoutlzis.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Marcus Gustafsson">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoGustafsson1.png"/>
|
||||||
|
<img src="images/logoGustafsson2.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Adrianus Handoyo">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoAdria1.png"/>
|
||||||
|
<img src="images/logoAdria2.png"/>
|
||||||
|
<img src="images/logoAdria3.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="RussellBeattie">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoRussellBeattie1.png"/>
|
||||||
|
<img src="images/logoRussellBeattie2.png"/>
|
||||||
|
<img src="images/logoRussellBeattie3.png"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<img src="images/logoRussellBeattie4.png"/>
|
||||||
|
<img src="images/logoRussellBeattie5.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Daniel Fernandez">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoDanielFernandez.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Andrew Clements">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoAndrewClements.png"/>
|
||||||
|
<img src="images/logoAndrewClements2.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Wendy Wise">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoWendyWise.png"/>
|
||||||
|
<img src="images/logoWendyWise2.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Nikhil Karmokar">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoKarmokar1.png"/>
|
||||||
|
<img src="images/logoKarmokar1s.png"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<img src="images/logoKarmokar2.png"/>
|
||||||
|
<img src="images/logoKarmokar2s.png"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<img src="images/logoKarmokar3.png"/>
|
||||||
|
<img src="images/logoKarmokar3s.png"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<img src="images/logoKarmokar4.png"/>
|
||||||
|
<img src="images/logoKarmokar4s.png"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<img src="images/logoKarmokar5.png"/>
|
||||||
|
<img src="images/logoKarmokar5s.png"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<img src="images/logoKarmokar6.png"/>
|
||||||
|
<img src="images/logoKarmokar6s.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="Lieven Janssen">
|
||||||
|
<p>
|
||||||
|
<img src="images/logoJanssen1.png"/>
|
||||||
|
<img src="images/logoJanssen2.png"/>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section title="RaPi GmbH">
|
||||||
|
<p>
|
||||||
|
Contact Person: Fancy at: fancy at my-feiqi.com
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<img src="images/logoRaPiGmbH1.png"/>
|
||||||
|
<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>
|
||||||
<section title="Loïc Lefèvre">
|
</body>
|
||||||
<p>
|
<footer>
|
||||||
<img src="images/logoLoicLefevre.png"/>
|
<legal>
|
||||||
<img src="images/logoLoicLefevre2.png"/>
|
Copyright (c) @year@ The Apache Software Foundation All rights reserved.
|
||||||
</p>
|
$Revision$ $Date$
|
||||||
</section>
|
</legal>
|
||||||
<section title="Glen Stampoultzis">
|
</footer>
|
||||||
<p>
|
|
||||||
<img src="images/logoGlenStampoutlzis.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="Marcus Gustafsson">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoGustafsson1.png"/>
|
|
||||||
<img src="images/logoGustafsson2.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="Adrianus Handoyo">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoAdria1.png"/>
|
|
||||||
<img src="images/logoAdria2.png"/>
|
|
||||||
<img src="images/logoAdria3.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="RussellBeattie">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoRussellBeattie1.png"/>
|
|
||||||
<img src="images/logoRussellBeattie2.png"/>
|
|
||||||
<img src="images/logoRussellBeattie3.png"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<img src="images/logoRussellBeattie4.png"/>
|
|
||||||
<img src="images/logoRussellBeattie5.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="Daniel Fernandez">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoDanielFernandez.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="Andrew Clements">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoAndrewClements.png"/>
|
|
||||||
<img src="images/logoAndrewClements2.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="Wendy Wise">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoWendyWise.png"/>
|
|
||||||
<img src="images/logoWendyWise2.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="Nikhil Karmokar">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoKarmokar1.png"/>
|
|
||||||
<img src="images/logoKarmokar1s.png"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<img src="images/logoKarmokar2.png"/>
|
|
||||||
<img src="images/logoKarmokar2s.png"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<img src="images/logoKarmokar3.png"/>
|
|
||||||
<img src="images/logoKarmokar3s.png"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<img src="images/logoKarmokar4.png"/>
|
|
||||||
<img src="images/logoKarmokar4s.png"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<img src="images/logoKarmokar5.png"/>
|
|
||||||
<img src="images/logoKarmokar5s.png"/>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<img src="images/logoKarmokar6.png"/>
|
|
||||||
<img src="images/logoKarmokar6s.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section title="Lieven Janssen">
|
|
||||||
<p>
|
|
||||||
<img src="images/logoJanssen1.png"/>
|
|
||||||
<img src="images/logoJanssen2.png"/>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</body>
|
|
||||||
<footer>
|
|
||||||
<legal>
|
|
||||||
Copyright (c) @year@ The Apache Software Foundation All rights reserved.
|
|
||||||
$Revision$ $Date$
|
|
||||||
</legal>
|
|
||||||
</footer>
|
|
||||||
</document>
|
</document>
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
* 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.usermodel.examples;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.usermodel.*;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstrates how to use newlines in cells.
|
||||||
|
*
|
||||||
|
* @author Glen Stampoultzis (glens at apache.org)
|
||||||
|
* @author Fauzia Lala <fauzia.lala at wcom.com>
|
||||||
|
*/
|
||||||
|
public class NewLinesInCells
|
||||||
|
{
|
||||||
|
public static void main( String[] args ) throws IOException
|
||||||
|
{
|
||||||
|
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFSheet s = wb.createSheet();
|
||||||
|
HSSFRow r = null;
|
||||||
|
HSSFCell c = null;
|
||||||
|
HSSFCellStyle cs = wb.createCellStyle();
|
||||||
|
HSSFFont f = wb.createFont();
|
||||||
|
HSSFFont f2 = wb.createFont();
|
||||||
|
|
||||||
|
cs = wb.createCellStyle();
|
||||||
|
|
||||||
|
cs.setFont( f2 );
|
||||||
|
//Word Wrap MUST be turned on
|
||||||
|
cs.setWrapText( true );
|
||||||
|
|
||||||
|
r = s.createRow( (short) 2 );
|
||||||
|
r.setHeight( (short) 0x349 );
|
||||||
|
c = r.createCell( (short) 2 );
|
||||||
|
c.setCellType( HSSFCell.CELL_TYPE_STRING );
|
||||||
|
c.setCellValue( "Use \n with word wrap on to create a new line" );
|
||||||
|
c.setCellStyle( cs );
|
||||||
|
s.setColumnWidth( (short) 2, (short) ( ( 50 * 8 ) / ( (double) 1 / 20 ) ) );
|
||||||
|
|
||||||
|
FileOutputStream fileOut = new FileOutputStream( "workbook.xls" );
|
||||||
|
wb.write( fileOut );
|
||||||
|
fileOut.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -631,12 +631,12 @@ public class BiffViewer
|
|||||||
retval = new LinkedDataRecord(rectype, size, data);
|
retval = new LinkedDataRecord(rectype, size, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FormulaRecord.sid:
|
// case FormulaRecord.sid:
|
||||||
retval = new FormulaRecord(rectype, size, data);
|
// retval = new FormulaRecord(rectype, size, data);
|
||||||
break;
|
// break;
|
||||||
|
|
||||||
case SheetPropertiesRecord.sid:
|
case SheetPropertiesRecord.sid:
|
||||||
retval = new FormulaRecord(rectype, size, data);
|
retval = new SheetPropertiesRecord(rectype, size, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
@ -643,26 +643,11 @@ public class Workbook
|
|||||||
{
|
{
|
||||||
log.log(DEBUG, "Serializing Workbook with offsets");
|
log.log(DEBUG, "Serializing Workbook with offsets");
|
||||||
|
|
||||||
// ArrayList bytes = new ArrayList(records.size());
|
|
||||||
// int arraysize = getSize(); // 0;
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
// for (int k = 0; k < records.size(); k++)
|
|
||||||
// {
|
|
||||||
// bytes.add((( Record ) records.get(k)).serialize());
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// for (int k = 0; k < bytes.size(); k++)
|
|
||||||
// {
|
|
||||||
// arraysize += (( byte [] ) bytes.get(k)).length;
|
|
||||||
// }
|
|
||||||
for (int k = 0; k < records.size(); k++)
|
for (int k = 0; k < records.size(); k++)
|
||||||
{
|
{
|
||||||
|
pos += (( Record ) records.get(k)).serialize(pos + offset, data); // rec.length;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
log.log(DEBUG, "Exiting serialize workbook");
|
log.log(DEBUG, "Exiting serialize workbook");
|
||||||
return pos;
|
return pos;
|
||||||
|
@ -161,9 +161,7 @@ public class ContinueRecord
|
|||||||
|
|
||||||
// how many continue records do we need
|
// how many continue records do we need
|
||||||
// System.out.println("In ProcessContinue");
|
// System.out.println("In ProcessContinue");
|
||||||
int records =
|
int records = (data.length / 8214); // we've a 1 offset but we're also off by one due to rounding...so it balances out
|
||||||
(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;
|
int offset = 8214;
|
||||||
|
|
||||||
// System.out.println("we have "+records+" continue records to process");
|
// System.out.println("we have "+records+" continue records to process");
|
||||||
@ -174,8 +172,7 @@ public class ContinueRecord
|
|||||||
for (int cr = 0; cr < records; cr++)
|
for (int cr = 0; cr < records; cr++)
|
||||||
{
|
{
|
||||||
ContinueRecord contrec = new ContinueRecord();
|
ContinueRecord contrec = new ContinueRecord();
|
||||||
int arraysize = Math.min((8214 - 4),
|
int arraysize = Math.min((8214 - 4), (data.length - offset));
|
||||||
(data.length - offset));
|
|
||||||
byte[] crdata = new byte[ arraysize ];
|
byte[] crdata = new byte[ arraysize ];
|
||||||
|
|
||||||
System.arraycopy(data, offset, crdata, 0, arraysize);
|
System.arraycopy(data, offset, crdata, 0, arraysize);
|
||||||
|
202
src/java/org/apache/poi/hssf/record/RecordProcessor.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -452,8 +452,8 @@ public class RowRecord
|
|||||||
LittleEndian.putShort(data, 0 + offset, sid);
|
LittleEndian.putShort(data, 0 + offset, sid);
|
||||||
LittleEndian.putShort(data, 2 + offset, ( short ) 16);
|
LittleEndian.putShort(data, 2 + offset, ( short ) 16);
|
||||||
LittleEndian.putShort(data, 4 + offset, getRowNumber());
|
LittleEndian.putShort(data, 4 + offset, getRowNumber());
|
||||||
LittleEndian.putShort(data, 6 + offset, getFirstCol());
|
LittleEndian.putShort(data, 6 + offset, getFirstCol() == -1 ? (short)0 : getFirstCol());
|
||||||
LittleEndian.putShort(data, 8 + offset, getLastCol());
|
LittleEndian.putShort(data, 8 + offset, getLastCol() == -1 ? (short)0 : getLastCol());
|
||||||
LittleEndian.putShort(data, 10 + offset, getHeight());
|
LittleEndian.putShort(data, 10 + offset, getHeight());
|
||||||
LittleEndian.putShort(data, 12 + offset, getOptimize());
|
LittleEndian.putShort(data, 12 + offset, getOptimize());
|
||||||
LittleEndian.putShort(data, 14 + offset, field_6_reserved);
|
LittleEndian.putShort(data, 14 + offset, field_6_reserved);
|
||||||
|
562
src/java/org/apache/poi/hssf/record/SSTDeserializer.java
Normal file
@ -0,0 +1,562 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
* 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.LittleEndian;
|
||||||
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the task of deserializing a SST string. The two main entry points are
|
||||||
|
*
|
||||||
|
* @author Glen Stampoultzis (glens at apache.org)
|
||||||
|
*/
|
||||||
|
class SSTDeserializer
|
||||||
|
{
|
||||||
|
|
||||||
|
private BinaryTree strings;
|
||||||
|
/** this is the number of characters we expect in the first sub-record in a subsequent continuation record */
|
||||||
|
private int continuationExpectedChars;
|
||||||
|
/** this is the string we were working on before hitting the end of the current record. This string is NOT finished. */
|
||||||
|
private String unfinishedString;
|
||||||
|
/** this is true if the string uses wide characters */
|
||||||
|
private boolean wideChar;
|
||||||
|
/** this is true if the string is a rich text string */
|
||||||
|
private boolean richText;
|
||||||
|
/** this is true if the string is a far east string or some other wierd string */
|
||||||
|
private boolean extendedText;
|
||||||
|
/** Number of formatting runs in this rich text field */
|
||||||
|
private short runCount;
|
||||||
|
/** Number of characters in current string */
|
||||||
|
private int charCount;
|
||||||
|
private int extensionLength;
|
||||||
|
|
||||||
|
|
||||||
|
public SSTDeserializer( BinaryTree strings )
|
||||||
|
{
|
||||||
|
this.strings = strings;
|
||||||
|
initVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initVars()
|
||||||
|
{
|
||||||
|
runCount = 0;
|
||||||
|
continuationExpectedChars = 0;
|
||||||
|
unfinishedString = "";
|
||||||
|
// bytesInCurrentSegment = 0;
|
||||||
|
// stringDataOffset = 0;
|
||||||
|
wideChar = false;
|
||||||
|
richText = false;
|
||||||
|
extendedText = 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 initialOffset, short dataSize )
|
||||||
|
{
|
||||||
|
initVars();
|
||||||
|
|
||||||
|
int offset = initialOffset;
|
||||||
|
while ( ( offset - initialOffset ) < dataSize )
|
||||||
|
{
|
||||||
|
int remaining = dataSize - offset + initialOffset;
|
||||||
|
|
||||||
|
if ( ( remaining > 0 ) && ( remaining < LittleEndianConsts.SHORT_SIZE ) )
|
||||||
|
{
|
||||||
|
throw new RecordFormatException( "Cannot get length of the last string in SSTRecord" );
|
||||||
|
}
|
||||||
|
if ( remaining == LittleEndianConsts.SHORT_SIZE )
|
||||||
|
{
|
||||||
|
setContinuationExpectedChars( LittleEndian.getUShort( data, offset ) );
|
||||||
|
unfinishedString = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
charCount = LittleEndian.getUShort( data, offset );
|
||||||
|
readStringHeader( data, offset );
|
||||||
|
boolean stringContinuesOverContinuation = remaining < totalStringSize();
|
||||||
|
if ( stringContinuesOverContinuation )
|
||||||
|
{
|
||||||
|
int remainingBytes = ( initialOffset + dataSize ) - offset - stringHeaderOverhead();
|
||||||
|
setContinuationExpectedChars( charCount - calculateCharCount( remainingBytes ) );
|
||||||
|
charCount -= getContinuationExpectedChars();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setContinuationExpectedChars( 0 );
|
||||||
|
}
|
||||||
|
processString( data, offset, charCount );
|
||||||
|
offset += totalStringSize();
|
||||||
|
if ( getContinuationExpectedChars() != 0 )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private void dump( final byte[] data, int offset, int length )
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// System.out.println( "------------------- SST DUMP -------------------------" );
|
||||||
|
// HexDump.dump( (byte[]) data, offset, System.out, offset, length );
|
||||||
|
// }
|
||||||
|
// catch ( IOException e )
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// catch ( ArrayIndexOutOfBoundsException e )
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// catch ( IllegalArgumentException e )
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void readStringHeader( final byte[] data, final int index )
|
||||||
|
{
|
||||||
|
|
||||||
|
byte optionFlag = data[index + LittleEndianConsts.SHORT_SIZE];
|
||||||
|
|
||||||
|
wideChar = ( optionFlag & 1 ) == 1;
|
||||||
|
extendedText = ( optionFlag & 4 ) == 4;
|
||||||
|
richText = ( optionFlag & 8 ) == 8;
|
||||||
|
runCount = 0;
|
||||||
|
if ( richText )
|
||||||
|
{
|
||||||
|
runCount = LittleEndian.getShort( data, index + SSTRecord.STRING_MINIMAL_OVERHEAD );
|
||||||
|
}
|
||||||
|
extensionLength = 0;
|
||||||
|
if ( extendedText )
|
||||||
|
{
|
||||||
|
extensionLength = LittleEndian.getInt( data, index + SSTRecord.STRING_MINIMAL_OVERHEAD
|
||||||
|
+ (richText ? LittleEndianConsts.SHORT_SIZE : 0) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a string or the first part of a string.
|
||||||
|
*
|
||||||
|
* @param characters the number of characters to write.
|
||||||
|
*
|
||||||
|
* @return the number of bytes written.
|
||||||
|
*/
|
||||||
|
private int processString( final byte[] data, final int dataIndex, final int characters )
|
||||||
|
{
|
||||||
|
|
||||||
|
// length is the length we store it as. not the length that is read.
|
||||||
|
int length = SSTRecord.STRING_MINIMAL_OVERHEAD + calculateByteCount( characters );
|
||||||
|
byte[] unicodeStringBuffer = new byte[length];
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
// Set the length in characters
|
||||||
|
LittleEndian.putUShort( unicodeStringBuffer, offset, characters );
|
||||||
|
offset += LittleEndianConsts.SHORT_SIZE;
|
||||||
|
// Set the option flags
|
||||||
|
unicodeStringBuffer[offset] = data[dataIndex + offset];
|
||||||
|
// Copy in the string data
|
||||||
|
int bytesRead = unicodeStringBuffer.length - SSTRecord.STRING_MINIMAL_OVERHEAD;
|
||||||
|
arraycopy( data, dataIndex + stringHeaderOverhead(), unicodeStringBuffer, SSTRecord.STRING_MINIMAL_OVERHEAD, bytesRead );
|
||||||
|
// Create the unicode string
|
||||||
|
UnicodeString string = new UnicodeString( UnicodeString.sid,
|
||||||
|
(short) unicodeStringBuffer.length,
|
||||||
|
unicodeStringBuffer );
|
||||||
|
|
||||||
|
if ( isStringFinished() )
|
||||||
|
{
|
||||||
|
Integer integer = new Integer( strings.size() );
|
||||||
|
addToStringTable( strings, integer, string );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unfinishedString = string.getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStringFinished()
|
||||||
|
{
|
||||||
|
return getContinuationExpectedChars() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 two 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 ) ) );
|
||||||
|
if ( string.isExtendedText() )
|
||||||
|
string.setOptionFlags( (byte) ( string.getOptionFlags() & ( ~4 ) ) );
|
||||||
|
|
||||||
|
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 ( isStringFinished() )
|
||||||
|
{
|
||||||
|
initVars();
|
||||||
|
manufactureStrings( record, 0, (short) record.length );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reset the wide bit because that can change across a continuation. the fact that it's
|
||||||
|
// actually rich text doesn't change across continuations even though the rich text
|
||||||
|
// may on longer be set in the "new" option flag. confusing huh?
|
||||||
|
wideChar = ( record[0] & 1 ) == 1;
|
||||||
|
|
||||||
|
if ( stringSpansContinuation( record.length - LittleEndianConsts.BYTE_SIZE ) )
|
||||||
|
{
|
||||||
|
processEntireContinuation( record );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readStringRemainder( record );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the remainder string and any subsequent strings from the continuation record.
|
||||||
|
*
|
||||||
|
* @param record The entire continuation record data.
|
||||||
|
*/
|
||||||
|
private void readStringRemainder( final byte[] record )
|
||||||
|
{
|
||||||
|
int stringRemainderSizeInBytes = calculateByteCount( getContinuationExpectedChars() );
|
||||||
|
// stringDataOffset = LittleEndianConsts.BYTE_SIZE;
|
||||||
|
byte[] unicodeStringData = new byte[SSTRecord.STRING_MINIMAL_OVERHEAD
|
||||||
|
+ calculateByteCount( getContinuationExpectedChars() )];
|
||||||
|
|
||||||
|
// write the string length
|
||||||
|
LittleEndian.putShort( unicodeStringData, 0, (short) getContinuationExpectedChars() );
|
||||||
|
|
||||||
|
// write the options flag
|
||||||
|
unicodeStringData[LittleEndianConsts.SHORT_SIZE] = createOptionByte( wideChar, richText, extendedText );
|
||||||
|
|
||||||
|
// copy the bytes/words making up the string; skipping
|
||||||
|
// past all the overhead of the str_data array
|
||||||
|
arraycopy( record, LittleEndianConsts.BYTE_SIZE, unicodeStringData,
|
||||||
|
SSTRecord.STRING_MINIMAL_OVERHEAD,
|
||||||
|
unicodeStringData.length - SSTRecord.STRING_MINIMAL_OVERHEAD );
|
||||||
|
|
||||||
|
// use special constructor to create the final string
|
||||||
|
UnicodeString string = new UnicodeString( UnicodeString.sid,
|
||||||
|
(short) unicodeStringData.length, unicodeStringData,
|
||||||
|
unfinishedString );
|
||||||
|
Integer integer = new Integer( strings.size() );
|
||||||
|
|
||||||
|
addToStringTable( strings, integer, string );
|
||||||
|
|
||||||
|
int newOffset = offsetForContinuedRecord( stringRemainderSizeInBytes );
|
||||||
|
manufactureStrings( record, newOffset, (short) ( record.length - newOffset ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the size of the string in bytes based on the character width
|
||||||
|
*/
|
||||||
|
private int stringSizeInBytes()
|
||||||
|
{
|
||||||
|
return calculateByteCount( charCount );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the size of the string in byes. This figure includes all the over
|
||||||
|
* heads for the string.
|
||||||
|
*/
|
||||||
|
private int totalStringSize()
|
||||||
|
{
|
||||||
|
return stringSizeInBytes()
|
||||||
|
+ stringHeaderOverhead()
|
||||||
|
+ LittleEndianConsts.INT_SIZE * runCount
|
||||||
|
+ extensionLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int stringHeaderOverhead()
|
||||||
|
{
|
||||||
|
return SSTRecord.STRING_MINIMAL_OVERHEAD
|
||||||
|
+ ( richText ? LittleEndianConsts.SHORT_SIZE : 0 )
|
||||||
|
+ ( extendedText ? LittleEndianConsts.INT_SIZE : 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private int offsetForContinuedRecord( int stringRemainderSizeInBytes )
|
||||||
|
{
|
||||||
|
return stringRemainderSizeInBytes + LittleEndianConsts.BYTE_SIZE
|
||||||
|
+ runCount * LittleEndianConsts.INT_SIZE + extensionLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte createOptionByte( boolean wideChar, boolean richText, boolean farEast )
|
||||||
|
{
|
||||||
|
return (byte) ( ( wideChar ? 1 : 0 ) + ( farEast ? 4 : 0 ) + ( richText ? 8 : 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the continued record is so long is spans into the next continue then
|
||||||
|
* simply suck the remaining string data into the existing <code>unfinishedString</code>.
|
||||||
|
*
|
||||||
|
* @param record The data from the continuation record.
|
||||||
|
*/
|
||||||
|
private void processEntireContinuation( final byte[] record )
|
||||||
|
{
|
||||||
|
// create artificial data to create a UnicodeString
|
||||||
|
int dataLengthInBytes = record.length - LittleEndianConsts.BYTE_SIZE;
|
||||||
|
byte[] unicodeStringData = new byte[record.length + LittleEndianConsts.SHORT_SIZE];
|
||||||
|
|
||||||
|
LittleEndian.putShort( unicodeStringData, (byte) 0, (short) calculateCharCount( dataLengthInBytes ) );
|
||||||
|
arraycopy( record, 0, unicodeStringData, LittleEndianConsts.SHORT_SIZE, record.length );
|
||||||
|
UnicodeString ucs = new UnicodeString( UnicodeString.sid, (short) unicodeStringData.length, unicodeStringData );
|
||||||
|
|
||||||
|
unfinishedString = unfinishedString + ucs.getString();
|
||||||
|
setContinuationExpectedChars( getContinuationExpectedChars() - calculateCharCount( dataLengthInBytes ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean stringSpansContinuation( int continuationSizeInBytes )
|
||||||
|
{
|
||||||
|
return calculateByteCount( getContinuationExpectedChars() ) > continuationSizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the number of characters we expect in the first
|
||||||
|
* sub-record in a subsequent continuation record
|
||||||
|
*/
|
||||||
|
|
||||||
|
int getContinuationExpectedChars()
|
||||||
|
{
|
||||||
|
return continuationExpectedChars;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContinuationExpectedChars( final int count )
|
||||||
|
{
|
||||||
|
continuationExpectedChars = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateByteCount( final int character_count )
|
||||||
|
{
|
||||||
|
return character_count * ( wideChar ? LittleEndianConsts.SHORT_SIZE : LittleEndianConsts.BYTE_SIZE );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies an array from the specified source array, beginning at the
|
||||||
|
* specified position, to the specified position of the destination array.
|
||||||
|
* A subsequence of array components are copied from the source
|
||||||
|
* array referenced by <code>src</code> to the destination array
|
||||||
|
* referenced by <code>dst</code>. The number of components copied is
|
||||||
|
* equal to the <code>length</code> argument. The components at
|
||||||
|
* positions <code>srcOffset</code> through
|
||||||
|
* <code>srcOffset+length-1</code> in the source array are copied into
|
||||||
|
* positions <code>dstOffset</code> through
|
||||||
|
* <code>dstOffset+length-1</code>, respectively, of the destination
|
||||||
|
* array.
|
||||||
|
* <p>
|
||||||
|
* If the <code>src</code> and <code>dst</code> arguments refer to the
|
||||||
|
* same array object, then the copying is performed as if the
|
||||||
|
* components at positions <code>srcOffset</code> through
|
||||||
|
* <code>srcOffset+length-1</code> were first copied to a temporary
|
||||||
|
* array with <code>length</code> components and then the contents of
|
||||||
|
* the temporary array were copied into positions
|
||||||
|
* <code>dstOffset</code> through <code>dstOffset+length-1</code> of the
|
||||||
|
* destination array.
|
||||||
|
* <p>
|
||||||
|
* If <code>dst</code> is <code>null</code>, then a
|
||||||
|
* <code>NullPointerException</code> is thrown.
|
||||||
|
* <p>
|
||||||
|
* If <code>src</code> is <code>null</code>, then a
|
||||||
|
* <code>NullPointerException</code> is thrown and the destination
|
||||||
|
* array is not modified.
|
||||||
|
* <p>
|
||||||
|
* Otherwise, if any of the following is true, an
|
||||||
|
* <code>ArrayStoreException</code> is thrown and the destination is
|
||||||
|
* not modified:
|
||||||
|
* <ul>
|
||||||
|
* <li>The <code>src</code> argument refers to an object that is not an
|
||||||
|
* array.
|
||||||
|
* <li>The <code>dst</code> argument refers to an object that is not an
|
||||||
|
* array.
|
||||||
|
* <li>The <code>src</code> argument and <code>dst</code> argument refer to
|
||||||
|
* arrays whose component types are different primitive types.
|
||||||
|
* <li>The <code>src</code> argument refers to an array with a primitive
|
||||||
|
* component type and the <code>dst</code> argument refers to an array
|
||||||
|
* with a reference component type.
|
||||||
|
* <li>The <code>src</code> argument refers to an array with a reference
|
||||||
|
* component type and the <code>dst</code> argument refers to an array
|
||||||
|
* with a primitive component type.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* Otherwise, if any of the following is true, an
|
||||||
|
* <code>IndexOutOfBoundsException</code> is
|
||||||
|
* thrown and the destination is not modified:
|
||||||
|
* <ul>
|
||||||
|
* <li>The <code>srcOffset</code> argument is negative.
|
||||||
|
* <li>The <code>dstOffset</code> argument is negative.
|
||||||
|
* <li>The <code>length</code> argument is negative.
|
||||||
|
* <li><code>srcOffset+length</code> is greater than
|
||||||
|
* <code>src.length</code>, the length of the source array.
|
||||||
|
* <li><code>dstOffset+length</code> is greater than
|
||||||
|
* <code>dst.length</code>, the length of the destination array.
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* Otherwise, if any actual component of the source array from
|
||||||
|
* position <code>srcOffset</code> through
|
||||||
|
* <code>srcOffset+length-1</code> cannot be converted to the component
|
||||||
|
* type of the destination array by assignment conversion, an
|
||||||
|
* <code>ArrayStoreException</code> is thrown. In this case, let
|
||||||
|
* <b><i>k</i></b> be the smallest nonnegative integer less than
|
||||||
|
* length such that <code>src[srcOffset+</code><i>k</i><code>]</code>
|
||||||
|
* cannot be converted to the component type of the destination
|
||||||
|
* array; when the exception is thrown, source array components from
|
||||||
|
* positions <code>srcOffset</code> through
|
||||||
|
* <code>srcOffset+</code><i>k</i><code>-1</code>
|
||||||
|
* will already have been copied to destination array positions
|
||||||
|
* <code>dstOffset</code> through
|
||||||
|
* <code>dstOffset+</code><i>k</I><code>-1</code> and no other
|
||||||
|
* positions of the destination array will have been modified.
|
||||||
|
* (Because of the restrictions already itemized, this
|
||||||
|
* paragraph effectively applies only to the situation where both
|
||||||
|
* arrays have component types that are reference types.)
|
||||||
|
*
|
||||||
|
* @param src the source array.
|
||||||
|
* @param src_position start position in the source array.
|
||||||
|
* @param dst the destination array.
|
||||||
|
* @param dst_position pos start position in the destination data.
|
||||||
|
* @param length the number of array elements to be copied.
|
||||||
|
* @exception IndexOutOfBoundsException if copying would cause
|
||||||
|
* access of data outside array bounds.
|
||||||
|
* @exception ArrayStoreException if an element in the <code>src</code>
|
||||||
|
* array could not be stored into the <code>dest</code> array
|
||||||
|
* because of a type mismatch.
|
||||||
|
* @exception NullPointerException if either <code>src</code> or
|
||||||
|
* <code>dst</code> is <code>null</code>.
|
||||||
|
*/
|
||||||
|
private void arraycopy( byte[] src, int src_position,
|
||||||
|
byte[] dst, int dst_position,
|
||||||
|
int length )
|
||||||
|
{
|
||||||
|
System.arraycopy( src, src_position, dst, dst_position, length );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the unfinished string
|
||||||
|
*/
|
||||||
|
String getUnfinishedString()
|
||||||
|
{
|
||||||
|
return unfinishedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if current string uses wide characters
|
||||||
|
*/
|
||||||
|
boolean isWideChar()
|
||||||
|
{
|
||||||
|
return wideChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
356
src/java/org/apache/poi/hssf/record/SSTSerializer.java
Normal file
@ -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 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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>
|
* REFERENCE: PG 264 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
|
||||||
* @author Andrew C. Oliver
|
* @author Andrew C. Oliver
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
|
* @author Glen Stampoultzis (glens at apache.org)
|
||||||
* @version 2.0-pre
|
* @version 2.0-pre
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -77,12 +78,29 @@ public class UnicodeString
|
|||||||
private short field_1_charCount; // = 0;
|
private short field_1_charCount; // = 0;
|
||||||
private byte field_2_optionflags; // = 0;
|
private byte field_2_optionflags; // = 0;
|
||||||
private String field_3_string; // = null;
|
private String field_3_string; // = null;
|
||||||
|
private final int RICH_TEXT_BIT = 8;
|
||||||
|
private final int EXT_BIT = 4;
|
||||||
|
|
||||||
|
public UnicodeString()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public int hashCode()
|
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)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
if ((o == null) || (o.getClass() != this.getClass()))
|
if ((o == null) || (o.getClass() != this.getClass()))
|
||||||
@ -96,10 +114,6 @@ public class UnicodeString
|
|||||||
&& field_3_string.equals(other.field_3_string));
|
&& field_3_string.equals(other.field_3_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnicodeString()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* construct a unicode string record and fill its fields, ID is ignored
|
* construct a unicode string record and fill its fields, ID is ignored
|
||||||
* @param id - ignored
|
* @param id - ignored
|
||||||
@ -278,19 +292,10 @@ public class UnicodeString
|
|||||||
|
|
||||||
public int serialize(int offset, byte [] data)
|
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());
|
LittleEndian.putShort(data, 0 + offset, getCharCount());
|
||||||
data[ 2 + offset ] = getOptionFlags();
|
data[ 2 + offset ] = getOptionFlags();
|
||||||
|
|
||||||
// System.out.println("Unicode: We've got "+retval[2]+" for our option flag");
|
if (!isUncompressedUnicode())
|
||||||
if (getOptionFlags() == 0)
|
|
||||||
{
|
{
|
||||||
StringUtil.putCompressedUnicode(getString(), data, 0x3 + offset);
|
StringUtil.putCompressedUnicode(getString(), data, 0x3 + offset);
|
||||||
}
|
}
|
||||||
@ -302,14 +307,14 @@ public class UnicodeString
|
|||||||
return getRecordSize();
|
return getRecordSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isUncompressedUnicode()
|
||||||
|
{
|
||||||
|
return (getOptionFlags() & 0x01) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
public int getRecordSize()
|
public int getRecordSize()
|
||||||
{
|
{
|
||||||
int charsize = 1;
|
int charsize = isUncompressedUnicode() ? 2 : 1;
|
||||||
|
|
||||||
if (getOptionFlags() == 1)
|
|
||||||
{
|
|
||||||
charsize = 2;
|
|
||||||
}
|
|
||||||
return 3 + (getString().length() * charsize);
|
return 3 + (getString().length() * charsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,11 +343,16 @@ public class UnicodeString
|
|||||||
return this.getString().compareTo(str.getString());
|
return this.getString().compareTo(str.getString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRichText()
|
||||||
|
{
|
||||||
|
return (getOptionFlags() & RICH_TEXT_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
int maxBrokenLength(final int proposedBrokenLength)
|
int maxBrokenLength(final int proposedBrokenLength)
|
||||||
{
|
{
|
||||||
int rval = proposedBrokenLength;
|
int rval = proposedBrokenLength;
|
||||||
|
|
||||||
if ((field_2_optionflags & 1) == 1)
|
if (isUncompressedUnicode())
|
||||||
{
|
{
|
||||||
int proposedStringLength = proposedBrokenLength - 3;
|
int proposedStringLength = proposedBrokenLength - 3;
|
||||||
|
|
||||||
@ -355,12 +365,9 @@ public class UnicodeString
|
|||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public boolean equals(Object obj) {
|
public boolean isExtendedText()
|
||||||
// if (!(obj instanceof UnicodeString)) return false;
|
{
|
||||||
//
|
return (getOptionFlags() & EXT_BIT) != 0;
|
||||||
// UnicodeString str = (UnicodeString)obj;
|
}
|
||||||
//
|
|
||||||
//
|
|
||||||
// return this.getString().equals(str.getString());
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -126,8 +126,8 @@ public class HSSFRow
|
|||||||
this.sheet = sheet;
|
this.sheet = sheet;
|
||||||
row = new RowRecord();
|
row = new RowRecord();
|
||||||
row.setHeight((short) 0xff);
|
row.setHeight((short) 0xff);
|
||||||
row.setLastCol((short)-1);
|
row.setLastCol((short) -1);
|
||||||
row.setFirstCol((short)-1);
|
row.setFirstCol((short) -1);
|
||||||
|
|
||||||
// row.setRowNumber(rowNum);
|
// row.setRowNumber(rowNum);
|
||||||
setRowNum(rowNum);
|
setRowNum(rowNum);
|
||||||
@ -213,11 +213,11 @@ public class HSSFRow
|
|||||||
|
|
||||||
if (cell.getCellNum() == row.getLastCol())
|
if (cell.getCellNum() == row.getLastCol())
|
||||||
{
|
{
|
||||||
row.setLastCol( findLastCell(row.getLastCol()) );
|
row.setLastCol(findLastCell(row.getLastCol()));
|
||||||
}
|
}
|
||||||
if (cell.getCellNum() == row.getFirstCol())
|
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)
|
if (row.getFirstCol() == -1)
|
||||||
{
|
{
|
||||||
row.setFirstCol( cell.getCellNum() );
|
row.setFirstCol(cell.getCellNum());
|
||||||
}
|
}
|
||||||
if (row.getLastCol() == -1)
|
if (row.getLastCol() == -1)
|
||||||
{
|
{
|
||||||
row.setLastCol( cell.getCellNum() );
|
row.setLastCol(cell.getCellNum());
|
||||||
}
|
}
|
||||||
cells.put(new Integer(cell.getCellNum()), cell);
|
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
|
* 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.
|
* ask for a cell that is not defined....you get a null.
|
||||||
*
|
*
|
||||||
* @param cellnum - 0 based column number
|
* @param cellnum 0 based column number
|
||||||
* @returns HSSFCell representing that column or null if undefined.
|
* @return HSSFCell representing that column or null if undefined.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public HSSFCell getCell(short cellnum)
|
public HSSFCell getCell(short cellnum)
|
||||||
@ -318,7 +318,10 @@ public class HSSFRow
|
|||||||
|
|
||||||
public short getFirstCellNum()
|
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()
|
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!
|
* actually be row cell depending on how many are defined!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
* The Apache Software License, Version 1.1
|
* The Apache Software License, Version 1.1
|
||||||
*
|
*
|
||||||
@ -60,11 +59,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.poi.hssf.usermodel;
|
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.Sheet;
|
||||||
import org.apache.poi.hssf.model.Workbook;
|
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.hssf.util.Region;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -74,12 +76,12 @@ import java.util.TreeMap;
|
|||||||
* High level representation of a worksheet.
|
* High level representation of a worksheet.
|
||||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
* @author Andrew C. Oliver (acoliver at apache dot org)
|
||||||
* @author Glen Stampoultzis (glens at apache.org)
|
* @author Glen Stampoultzis (glens at apache.org)
|
||||||
* @version 1.0-pre
|
* @author Libin Roman (romal at vistaportal.com)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class HSSFSheet
|
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
|
* 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.
|
* 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
|
* reference to the low level Sheet object
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private Sheet sheet;
|
private Sheet sheet;
|
||||||
private TreeMap rows;
|
private TreeMap rows;
|
||||||
private Workbook book;
|
private Workbook book;
|
||||||
private int firstrow;
|
private int firstrow;
|
||||||
private int lastrow;
|
private int lastrow;
|
||||||
private static POILogger log = POILogFactory.getLogger(HSSFSheet.class);
|
private static POILogger log = POILogFactory.getLogger(HSSFSheet.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,8 +112,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
protected HSSFSheet(Workbook book)
|
protected HSSFSheet(Workbook book)
|
||||||
{
|
{
|
||||||
sheet = Sheet.createSheet();
|
sheet = Sheet.createSheet();
|
||||||
rows = new TreeMap(); // new ArrayList(INITIAL_CAPACITY);
|
rows = new TreeMap(); // new ArrayList(INITIAL_CAPACITY);
|
||||||
this.book = book;
|
this.book = book;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,16 +129,11 @@ public class HSSFSheet
|
|||||||
protected HSSFSheet(Workbook book, Sheet sheet)
|
protected HSSFSheet(Workbook book, Sheet sheet)
|
||||||
{
|
{
|
||||||
this.sheet = sheet;
|
this.sheet = sheet;
|
||||||
rows = new TreeMap();
|
rows = new TreeMap();
|
||||||
this.book = book;
|
this.book = book;
|
||||||
setPropertiesFromSheet(sheet);
|
setPropertiesFromSheet(sheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** private default constructor prevents bogus initializationless construction */
|
|
||||||
|
|
||||||
private HSSFSheet()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* used internally to set the properties given a Sheet object
|
* used internally to set the properties given a Sheet object
|
||||||
@ -144,8 +141,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
private void setPropertiesFromSheet(Sheet sheet)
|
private void setPropertiesFromSheet(Sheet sheet)
|
||||||
{
|
{
|
||||||
int sloc = sheet.getLoc();
|
int sloc = sheet.getLoc();
|
||||||
RowRecord row = sheet.getNextRow();
|
RowRecord row = sheet.getNextRow();
|
||||||
|
|
||||||
while (row != null)
|
while (row != null)
|
||||||
{
|
{
|
||||||
@ -154,8 +151,8 @@ public class HSSFSheet
|
|||||||
row = sheet.getNextRow();
|
row = sheet.getNextRow();
|
||||||
}
|
}
|
||||||
sheet.setLoc(sloc);
|
sheet.setLoc(sloc);
|
||||||
CellValueRecordInterface cval = sheet.getNextValueRecord();
|
CellValueRecordInterface cval = sheet.getNextValueRecord();
|
||||||
long timestart = System.currentTimeMillis();
|
long timestart = System.currentTimeMillis();
|
||||||
|
|
||||||
log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ",
|
log.log(DEBUG, "Time at start of cell creating in HSSF sheet = ",
|
||||||
new Long(timestart));
|
new Long(timestart));
|
||||||
@ -163,8 +160,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
while (cval != null)
|
while (cval != null)
|
||||||
{
|
{
|
||||||
long cellstart = System.currentTimeMillis();
|
long cellstart = System.currentTimeMillis();
|
||||||
HSSFRow hrow = lastrow;
|
HSSFRow hrow = lastrow;
|
||||||
|
|
||||||
if ((lastrow == null) || (lastrow.getRowNum() != cval.getRow()))
|
if ((lastrow == null) || (lastrow.getRowNum() != cval.getRow()))
|
||||||
{
|
{
|
||||||
@ -236,10 +233,10 @@ public class HSSFSheet
|
|||||||
|
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
HSSFCell cell = ( HSSFCell ) iter.next();
|
HSSFCell cell = (HSSFCell) iter.next();
|
||||||
|
|
||||||
sheet.removeValueRecord(row.getRowNum(),
|
sheet.removeValueRecord(row.getRowNum(),
|
||||||
cell.getCellValueRecord());
|
cell.getCellValueRecord());
|
||||||
}
|
}
|
||||||
sheet.removeRow(row.getRowRecord());
|
sheet.removeRow(row.getRowRecord());
|
||||||
}
|
}
|
||||||
@ -251,10 +248,10 @@ public class HSSFSheet
|
|||||||
|
|
||||||
private int findLastRow(int lastrow)
|
private int findLastRow(int lastrow)
|
||||||
{
|
{
|
||||||
int rownum = lastrow - 1;
|
int rownum = lastrow - 1;
|
||||||
HSSFRow r = getRow(rownum);
|
HSSFRow r = getRow(rownum);
|
||||||
|
|
||||||
while (r == null)
|
while (r == null && rownum >= 0)
|
||||||
{
|
{
|
||||||
r = getRow(--rownum);
|
r = getRow(--rownum);
|
||||||
}
|
}
|
||||||
@ -267,13 +264,17 @@ public class HSSFSheet
|
|||||||
|
|
||||||
private int findFirstRow(int firstrow)
|
private int findFirstRow(int firstrow)
|
||||||
{
|
{
|
||||||
int rownum = firstrow + 1;
|
int rownum = firstrow + 1;
|
||||||
HSSFRow r = getRow(rownum);
|
HSSFRow r = getRow(rownum);
|
||||||
|
|
||||||
while (r == null)
|
while (r == null && rownum <= getLastRowNum())
|
||||||
{
|
{
|
||||||
r = getRow(++rownum);
|
r = getRow(++rownum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rownum > getLastRowNum())
|
||||||
|
return -1;
|
||||||
|
|
||||||
return rownum;
|
return rownum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,8 +312,8 @@ public class HSSFSheet
|
|||||||
{
|
{
|
||||||
HSSFRow row = new HSSFRow();
|
HSSFRow row = new HSSFRow();
|
||||||
|
|
||||||
row.setRowNum(( short ) rownum);
|
row.setRowNum((short) rownum);
|
||||||
return ( HSSFRow ) rows.get(row);
|
return (HSSFRow) rows.get(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -344,26 +345,6 @@ public class HSSFSheet
|
|||||||
return lastrow;
|
return lastrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
* set the width (in units of 1/256th of a character width)
|
||||||
* @param column - the column to set (0-based)
|
* @param column - the column to set (0-based)
|
||||||
@ -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
|
* get the default row height for the sheet (if the rows do not define their own height) in
|
||||||
* twips (1/20 of a point)
|
* twips (1/20 of a point)
|
||||||
* @retun default row height
|
* @return default row height
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public short getDefaultRowHeight()
|
public short getDefaultRowHeight()
|
||||||
@ -449,7 +430,7 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public void setDefaultRowHeightInPoints(float height)
|
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)
|
public int addMergedRegion(Region region)
|
||||||
{
|
{
|
||||||
return sheet.addMergedRegion(( short ) region.getRowFrom(),
|
return sheet.addMergedRegion((short) region.getRowFrom(),
|
||||||
region.getColumnFrom(),
|
region.getColumnFrom(),
|
||||||
( short ) region.getRowTo(),
|
(short) region.getRowTo(),
|
||||||
region.getColumnTo());
|
region.getColumnTo());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -494,7 +475,7 @@ public class HSSFSheet
|
|||||||
public void setVerticallyCenter(boolean value)
|
public void setVerticallyCenter(boolean value)
|
||||||
{
|
{
|
||||||
VCenterRecord record =
|
VCenterRecord record =
|
||||||
( VCenterRecord ) sheet.findFirstRecordBySid(VCenterRecord.sid);
|
(VCenterRecord) sheet.findFirstRecordBySid(VCenterRecord.sid);
|
||||||
|
|
||||||
record.setVCenter(value);
|
record.setVCenter(value);
|
||||||
}
|
}
|
||||||
@ -506,7 +487,7 @@ public class HSSFSheet
|
|||||||
public boolean getVerticallyCenter(boolean value)
|
public boolean getVerticallyCenter(boolean value)
|
||||||
{
|
{
|
||||||
VCenterRecord record =
|
VCenterRecord record =
|
||||||
( VCenterRecord ) sheet.findFirstRecordBySid(VCenterRecord.sid);
|
(VCenterRecord) sheet.findFirstRecordBySid(VCenterRecord.sid);
|
||||||
|
|
||||||
return record.getVCenter();
|
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.
|
* 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)
|
public void setAlternativeExpression(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setAlternateExpression(b);
|
record.setAlternateExpression(b);
|
||||||
}
|
}
|
||||||
@ -584,7 +565,7 @@ public class HSSFSheet
|
|||||||
public void setAlternativeFormula(boolean b)
|
public void setAlternativeFormula(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setAlternateFormula(b);
|
record.setAlternateFormula(b);
|
||||||
}
|
}
|
||||||
@ -597,7 +578,7 @@ public class HSSFSheet
|
|||||||
public void setAutobreaks(boolean b)
|
public void setAutobreaks(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setAutobreaks(b);
|
record.setAutobreaks(b);
|
||||||
}
|
}
|
||||||
@ -610,7 +591,7 @@ public class HSSFSheet
|
|||||||
public void setDialog(boolean b)
|
public void setDialog(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setDialog(b);
|
record.setDialog(b);
|
||||||
}
|
}
|
||||||
@ -624,7 +605,7 @@ public class HSSFSheet
|
|||||||
public void setDisplayGuts(boolean b)
|
public void setDisplayGuts(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setDisplayGuts(b);
|
record.setDisplayGuts(b);
|
||||||
}
|
}
|
||||||
@ -637,7 +618,7 @@ public class HSSFSheet
|
|||||||
public void setFitToPage(boolean b)
|
public void setFitToPage(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setFitToPage(b);
|
record.setFitToPage(b);
|
||||||
}
|
}
|
||||||
@ -650,7 +631,7 @@ public class HSSFSheet
|
|||||||
public void setRowSumsBelow(boolean b)
|
public void setRowSumsBelow(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setRowSumsBelow(b);
|
record.setRowSumsBelow(b);
|
||||||
}
|
}
|
||||||
@ -663,7 +644,7 @@ public class HSSFSheet
|
|||||||
public void setRowSumsRight(boolean b)
|
public void setRowSumsRight(boolean b)
|
||||||
{
|
{
|
||||||
WSBoolRecord record =
|
WSBoolRecord record =
|
||||||
( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
(WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
|
||||||
|
|
||||||
record.setRowSumsRight(b);
|
record.setRowSumsRight(b);
|
||||||
}
|
}
|
||||||
@ -675,8 +656,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getAlternateExpression()
|
public boolean getAlternateExpression()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getAlternateExpression();
|
.getAlternateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -686,8 +667,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getAlternateFormula()
|
public boolean getAlternateFormula()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getAlternateFormula();
|
.getAlternateFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -697,8 +678,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getAutobreaks()
|
public boolean getAutobreaks()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getAutobreaks();
|
.getAutobreaks();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -708,8 +689,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getDialog()
|
public boolean getDialog()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getDialog();
|
.getDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -720,8 +701,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getDisplayGuts()
|
public boolean getDisplayGuts()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getDisplayGuts();
|
.getDisplayGuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -731,8 +712,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getFitToPage()
|
public boolean getFitToPage()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getFitToPage();
|
.getFitToPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -742,8 +723,8 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getRowSumsBelow()
|
public boolean getRowSumsBelow()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getRowSumsBelow();
|
.getRowSumsBelow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -753,7 +734,7 @@ public class HSSFSheet
|
|||||||
|
|
||||||
public boolean getRowSumsRight()
|
public boolean getRowSumsRight()
|
||||||
{
|
{
|
||||||
return (( WSBoolRecord ) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
return ((WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid))
|
||||||
.getRowSumsRight();
|
.getRowSumsRight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ public class HexDump
|
|||||||
* @param stream the OutputStream to which the data is to be
|
* @param stream the OutputStream to which the data is to be
|
||||||
* written
|
* written
|
||||||
* @param index initial index into the byte array
|
* @param index initial index into the byte array
|
||||||
|
* @param length number of characters to output
|
||||||
*
|
*
|
||||||
* @exception IOException is thrown if anything goes wrong writing
|
* @exception IOException is thrown if anything goes wrong writing
|
||||||
* the data to stream
|
* the data to stream
|
||||||
@ -89,11 +90,10 @@ public class HexDump
|
|||||||
* @exception IllegalArgumentException if the output stream is
|
* @exception IllegalArgumentException if the output stream is
|
||||||
* null
|
* null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public synchronized static void dump(final byte [] data, final long offset,
|
public synchronized static void dump(final byte [] data, final long offset,
|
||||||
final OutputStream stream, final int index)
|
final OutputStream stream, final int index, final int length)
|
||||||
throws IOException, ArrayIndexOutOfBoundsException,
|
throws IOException, ArrayIndexOutOfBoundsException,
|
||||||
IllegalArgumentException
|
IllegalArgumentException
|
||||||
{
|
{
|
||||||
if ((index < 0) || (index >= data.length))
|
if ((index < 0) || (index >= data.length))
|
||||||
{
|
{
|
||||||
@ -108,9 +108,11 @@ public class HexDump
|
|||||||
long display_offset = offset + index;
|
long display_offset = offset + index;
|
||||||
StringBuffer buffer = new StringBuffer(74);
|
StringBuffer buffer = new StringBuffer(74);
|
||||||
|
|
||||||
for (int j = index; j < data.length; j += 16)
|
|
||||||
|
int data_length = Math.min(data.length,index+length);
|
||||||
|
for (int j = index; j < data_length; j += 16)
|
||||||
{
|
{
|
||||||
int chars_read = data.length - j;
|
int chars_read = data_length - j;
|
||||||
|
|
||||||
if (chars_read > 16)
|
if (chars_read > 16)
|
||||||
{
|
{
|
||||||
@ -146,6 +148,32 @@ public class HexDump
|
|||||||
buffer.setLength(0);
|
buffer.setLength(0);
|
||||||
display_offset += chars_read;
|
display_offset += chars_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dump an array of bytes to an OutputStream
|
||||||
|
*
|
||||||
|
* @param data the byte array to be dumped
|
||||||
|
* @param offset its offset, whatever that might mean
|
||||||
|
* @param stream the OutputStream to which the data is to be
|
||||||
|
* written
|
||||||
|
* @param index initial index into the byte array
|
||||||
|
*
|
||||||
|
* @exception IOException is thrown if anything goes wrong writing
|
||||||
|
* the data to stream
|
||||||
|
* @exception ArrayIndexOutOfBoundsException if the index is
|
||||||
|
* outside the data array's bounds
|
||||||
|
* @exception IllegalArgumentException if the output stream is
|
||||||
|
* null
|
||||||
|
*/
|
||||||
|
|
||||||
|
public synchronized static void dump(final byte [] data, final long offset,
|
||||||
|
final OutputStream stream, final int index)
|
||||||
|
throws IOException, ArrayIndexOutOfBoundsException,
|
||||||
|
IllegalArgumentException
|
||||||
|
{
|
||||||
|
dump(data, offset, stream, index, data.length-index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String EOL =
|
public static final String EOL =
|
||||||
|
116
src/java/org/apache/poi/util/HexRead.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package org.apache.poi.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class HexRead
|
||||||
|
{
|
||||||
|
public static byte[] readTestData( String filename )
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
File file = new File( 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 )
|
||||||
|
{
|
||||||
|
|
||||||
|
case '#':
|
||||||
|
readToEOL(stream);
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
b <<= 4;
|
||||||
|
b += (byte) ( count - '0' );
|
||||||
|
characterCount++;
|
||||||
|
if ( characterCount == 2 )
|
||||||
|
{
|
||||||
|
bytes.add( new Byte( b ) );
|
||||||
|
characterCount = 0;
|
||||||
|
b = (byte) 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
case 'B':
|
||||||
|
case 'C':
|
||||||
|
case 'D':
|
||||||
|
case 'E':
|
||||||
|
case 'F':
|
||||||
|
b <<= 4;
|
||||||
|
b += (byte) ( count + 10 - 'A' );
|
||||||
|
characterCount++;
|
||||||
|
if ( characterCount == 2 )
|
||||||
|
{
|
||||||
|
bytes.add( new Byte( b ) );
|
||||||
|
characterCount = 0;
|
||||||
|
b = (byte) 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
case 'b':
|
||||||
|
case 'c':
|
||||||
|
case 'd':
|
||||||
|
case 'e':
|
||||||
|
case 'f':
|
||||||
|
b <<= 4;
|
||||||
|
b += (byte) ( count + 10 - 'a' );
|
||||||
|
characterCount++;
|
||||||
|
if ( characterCount == 2 )
|
||||||
|
{
|
||||||
|
bytes.add( new Byte( b ) );
|
||||||
|
characterCount = 0;
|
||||||
|
b = (byte) 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default :
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream.close();
|
||||||
|
Byte[] polished = (Byte[]) bytes.toArray( new Byte[0] );
|
||||||
|
byte[] rval = new byte[polished.length];
|
||||||
|
|
||||||
|
for ( int j = 0; j < polished.length; j++ )
|
||||||
|
{
|
||||||
|
rval[j] = polished[j].byteValue();
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static private void readToEOL( InputStream stream ) throws IOException
|
||||||
|
{
|
||||||
|
int c = stream.read();
|
||||||
|
while ( c != -1 && c != '\n' && c != '\r')
|
||||||
|
{
|
||||||
|
c = stream.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -236,13 +236,27 @@ public class LittleEndian
|
|||||||
*
|
*
|
||||||
* @exception ArrayIndexOutOfBoundsException may be thrown
|
* @exception ArrayIndexOutOfBoundsException may be thrown
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static void putShort(final byte[] data, final int offset,
|
public static void putShort(final byte[] data, final int offset,
|
||||||
final short value)
|
final short value)
|
||||||
{
|
{
|
||||||
putNumber(data, offset, value, SHORT_SIZE);
|
putNumber(data, offset, value, SHORT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* put an unsigned short value into a byte array
|
||||||
|
*
|
||||||
|
* @param data the byte array
|
||||||
|
* @param offset a starting offset into the byte array
|
||||||
|
* @param value the short (16-bit) value
|
||||||
|
*
|
||||||
|
* @exception ArrayIndexOutOfBoundsException may be thrown
|
||||||
|
*/
|
||||||
|
public static void putUShort(final byte[] data, final int offset,
|
||||||
|
final int value)
|
||||||
|
{
|
||||||
|
putNumber(data, offset, value, SHORT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* put a array of shorts into a byte array
|
* put a array of shorts into a byte array
|
||||||
*
|
*
|
||||||
@ -592,4 +606,17 @@ public class LittleEndian
|
|||||||
return copy;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
BIN
src/testcases/org/apache/poi/hssf/data/duprich1.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/duprich2.xls
Normal file
16
src/testcases/org/apache/poi/hssf/data/evencontinuation.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
14 00 # String length 0x14=20
|
||||||
|
01 # Option flag, 16bit
|
||||||
|
# String: At a dinner party or
|
||||||
|
41 00 74 00 20 00 61 00 20 00
|
||||||
|
64 00 69 00 6E 00 6E 00 65 00
|
||||||
|
72 00 20 00 70 00 61 00 72 00
|
||||||
|
74 00 79 00 20 00 6F 00 72 00
|
||||||
|
|
||||||
|
# Continuation record (new string on the boundry)
|
||||||
|
11 00 # String length 0x11=17
|
||||||
|
00 # Option flag, 8bit
|
||||||
|
# String: At a dinner party
|
||||||
|
41 74 20 61 20
|
||||||
|
64 69 6E 6E 65
|
||||||
|
72 20 70 61 72
|
||||||
|
74 79
|
21
src/testcases/org/apache/poi/hssf/data/richtextdata.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
1D 00 # String length 0x1b=29
|
||||||
|
09 # Option flag, rich text + 16bit
|
||||||
|
02 00 # Formatting runs
|
||||||
|
# String: At a dinner party or
|
||||||
|
41 00 74 00 20 00 61 00 20 00
|
||||||
|
64 00 69 00 6E 00 6E 00 65 00
|
||||||
|
72 00 20 00 70 00 61 00 72 00
|
||||||
|
74 00 79 00 20 00 6F 00 72 00
|
||||||
|
|
||||||
|
# Continuation record
|
||||||
|
00 # option flag
|
||||||
|
|
||||||
|
# string:at at at
|
||||||
|
41 74 20
|
||||||
|
41 74 20
|
||||||
|
41 74 20
|
||||||
|
|
||||||
|
00 00 # Formatting run 1, first formated char at 0
|
||||||
|
00 00 # Formatting run 1, Index to font record
|
||||||
|
02 00 # Formatting run 2, first formated char at 2
|
||||||
|
00 00 # Formatting run 2, Index to font record
|
@ -0,0 +1,7 @@
|
|||||||
|
14 00 # String length 0x14=20
|
||||||
|
01 # Option flag, 16bit
|
||||||
|
# String: At a dinner party or
|
||||||
|
41 00 74 00 20 00 61 00 20 00
|
||||||
|
64 00 69 00 6E 00 6E 00 65 00
|
||||||
|
72 00 20 00 70 00 61 00 72 00
|
||||||
|
74 00 79 00 20 00 6F 00 72 00
|
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
# Continuation record
|
||||||
|
22 00 # String length 0x11=17
|
||||||
|
00 # Option flag, 8bit
|
||||||
|
# String: At a dinner party
|
||||||
|
41 74 20 61 20
|
||||||
|
64 69 6E 6E 65
|
||||||
|
72 20 70 61 72
|
||||||
|
74 79
|
@ -0,0 +1,7 @@
|
|||||||
|
# Continuation record
|
||||||
|
00 # option flag
|
||||||
|
# String: At a dinner party
|
||||||
|
41 74 20 61 20
|
||||||
|
64 69 6E 6E 65
|
||||||
|
72 20 70 61 72
|
||||||
|
74 79
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
* The Apache Software License, Version 1.1
|
* The Apache Software License, Version 1.1
|
||||||
*
|
*
|
||||||
@ -55,24 +54,29 @@
|
|||||||
|
|
||||||
package org.apache.poi.hssf.record;
|
package org.apache.poi.hssf.record;
|
||||||
|
|
||||||
import org.apache.poi.util.*;
|
import junit.framework.TestCase;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import junit.framework.*;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
import org.apache.poi.util.BinaryTree;
|
||||||
|
import org.apache.poi.util.HexRead;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.*;
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Marc Johnson (mjohnson at apache dot org)
|
* @author Marc Johnson (mjohnson at apache dot org)
|
||||||
|
* @author Glen Stampoultzis (glens at apache.org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class TestSSTRecord
|
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 =
|
private static final String _test_file_path_property =
|
||||||
"HSSF.testdata.path";
|
"HSSF.testdata.path";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new TestSSTRecord
|
* Creates new TestSSTRecord
|
||||||
@ -80,10 +84,10 @@ public class TestSSTRecord
|
|||||||
* @param name
|
* @param name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public TestSSTRecord(String name)
|
public TestSSTRecord( String name )
|
||||||
{
|
{
|
||||||
super(name);
|
super( name );
|
||||||
_test_file_path = System.getProperty(_test_file_path_property);
|
_test_file_path = System.getProperty( _test_file_path_property );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,118 +97,119 @@ public class TestSSTRecord
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public void testProcessContinueRecord()
|
public void testProcessContinueRecord()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
byte[] testdata = readTestData("BigSSTRecord");
|
byte[] testdata = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord" );
|
||||||
byte[] input = new byte[ testdata.length - 4 ];
|
byte[] input = new byte[testdata.length - 4];
|
||||||
|
|
||||||
System.arraycopy(testdata, 4, input, 0, input.length);
|
System.arraycopy( testdata, 4, input, 0, input.length );
|
||||||
SSTRecord record =
|
SSTRecord record =
|
||||||
new SSTRecord(LittleEndian.getShort(testdata, 0),
|
new SSTRecord( LittleEndian.getShort( testdata, 0 ),
|
||||||
LittleEndian.getShort(testdata, 2), input);
|
LittleEndian.getShort( testdata, 2 ), input );
|
||||||
byte[] continueRecord = readTestData("BigSSTRecordCR");
|
byte[] continueRecord = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecordCR" );
|
||||||
|
|
||||||
input = new byte[ continueRecord.length - 4 ];
|
input = new byte[continueRecord.length - 4];
|
||||||
System.arraycopy(continueRecord, 4, input, 0, input.length);
|
System.arraycopy( continueRecord, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
assertEquals(1464, record.getNumStrings());
|
assertEquals( 1464, record.getNumStrings() );
|
||||||
assertEquals(688, record.getNumUniqueStrings());
|
assertEquals( 688, record.getNumUniqueStrings() );
|
||||||
assertEquals(688, record.countStrings());
|
assertEquals( 688, record.countStrings() );
|
||||||
byte[] ser_output = record.serialize();
|
byte[] ser_output = record.serialize();
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
short type = LittleEndian.getShort(ser_output, offset);
|
short type = LittleEndian.getShort( ser_output, offset );
|
||||||
|
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
offset += LittleEndianConsts.SHORT_SIZE;
|
||||||
short length = LittleEndian.getShort(ser_output, offset);
|
short length = LittleEndian.getShort( ser_output, offset );
|
||||||
|
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
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;
|
offset += length;
|
||||||
SSTRecord testRecord = new SSTRecord(type, length, recordData);
|
SSTRecord testRecord = new SSTRecord( type, length, recordData );
|
||||||
|
|
||||||
assertEquals(ContinueRecord.sid,
|
assertEquals( ContinueRecord.sid,
|
||||||
LittleEndian.getShort(ser_output, offset));
|
LittleEndian.getShort( ser_output, offset ) );
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
offset += LittleEndianConsts.SHORT_SIZE;
|
||||||
length = LittleEndian.getShort(ser_output, offset);
|
length = LittleEndian.getShort( ser_output, offset );
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
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;
|
offset += length;
|
||||||
assertEquals(offset, ser_output.length);
|
assertEquals( offset, ser_output.length );
|
||||||
testRecord.processContinueRecord(cr);
|
testRecord.processContinueRecord( cr );
|
||||||
assertEquals(record, testRecord);
|
assertEquals( record, testRecord );
|
||||||
|
|
||||||
// testing based on new bug report
|
// testing based on new bug report
|
||||||
testdata = readTestData("BigSSTRecord2");
|
testdata = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2" );
|
||||||
input = new byte[ testdata.length - 4 ];
|
input = new byte[testdata.length - 4];
|
||||||
System.arraycopy(testdata, 4, input, 0, input.length);
|
System.arraycopy( testdata, 4, input, 0, input.length );
|
||||||
record = new SSTRecord(LittleEndian.getShort(testdata, 0),
|
record = new SSTRecord( LittleEndian.getShort( testdata, 0 ),
|
||||||
LittleEndian.getShort(testdata, 2), input);
|
LittleEndian.getShort( testdata, 2 ), input );
|
||||||
byte[] continueRecord1 = readTestData("BigSSTRecord2CR1");
|
byte[] continueRecord1 = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2CR1" );
|
||||||
|
|
||||||
input = new byte[ continueRecord1.length - 4 ];
|
input = new byte[continueRecord1.length - 4];
|
||||||
System.arraycopy(continueRecord1, 4, input, 0, input.length);
|
System.arraycopy( continueRecord1, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
byte[] continueRecord2 = readTestData("BigSSTRecord2CR2");
|
byte[] continueRecord2 = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2CR2" );
|
||||||
|
|
||||||
input = new byte[ continueRecord2.length - 4 ];
|
input = new byte[continueRecord2.length - 4];
|
||||||
System.arraycopy(continueRecord2, 4, input, 0, input.length);
|
System.arraycopy( continueRecord2, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
byte[] continueRecord3 = readTestData("BigSSTRecord2CR3");
|
byte[] continueRecord3 = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2CR3" );
|
||||||
|
|
||||||
input = new byte[ continueRecord3.length - 4 ];
|
input = new byte[continueRecord3.length - 4];
|
||||||
System.arraycopy(continueRecord3, 4, input, 0, input.length);
|
System.arraycopy( continueRecord3, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
byte[] continueRecord4 = readTestData("BigSSTRecord2CR4");
|
byte[] continueRecord4 = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2CR4" );
|
||||||
|
|
||||||
input = new byte[ continueRecord4.length - 4 ];
|
input = new byte[continueRecord4.length - 4];
|
||||||
System.arraycopy(continueRecord4, 4, input, 0, input.length);
|
System.arraycopy( continueRecord4, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
byte[] continueRecord5 = readTestData("BigSSTRecord2CR5");
|
byte[] continueRecord5 = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2CR5" );
|
||||||
|
|
||||||
input = new byte[ continueRecord5.length - 4 ];
|
input = new byte[continueRecord5.length - 4];
|
||||||
System.arraycopy(continueRecord5, 4, input, 0, input.length);
|
System.arraycopy( continueRecord5, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
byte[] continueRecord6 = readTestData("BigSSTRecord2CR6");
|
byte[] continueRecord6 = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2CR6" );
|
||||||
|
|
||||||
input = new byte[ continueRecord6.length - 4 ];
|
input = new byte[continueRecord6.length - 4];
|
||||||
System.arraycopy(continueRecord6, 4, input, 0, input.length);
|
System.arraycopy( continueRecord6, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
byte[] continueRecord7 = readTestData("BigSSTRecord2CR7");
|
byte[] continueRecord7 = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord2CR7" );
|
||||||
|
|
||||||
input = new byte[ continueRecord7.length - 4 ];
|
input = new byte[continueRecord7.length - 4];
|
||||||
System.arraycopy(continueRecord7, 4, input, 0, input.length);
|
System.arraycopy( continueRecord7, 4, input, 0, input.length );
|
||||||
record.processContinueRecord(input);
|
record.processContinueRecord( input );
|
||||||
assertEquals(158642, record.getNumStrings());
|
assertEquals( 158642, record.getNumStrings() );
|
||||||
assertEquals(5249, record.getNumUniqueStrings());
|
assertEquals( 5249, record.getNumUniqueStrings() );
|
||||||
assertEquals(5249, record.countStrings());
|
assertEquals( 5249, record.countStrings() );
|
||||||
ser_output = record.serialize();
|
ser_output = record.serialize();
|
||||||
offset = 0;
|
offset = 0;
|
||||||
type = LittleEndian.getShort(ser_output, offset);
|
type = LittleEndian.getShort( ser_output, offset );
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
offset += LittleEndianConsts.SHORT_SIZE;
|
||||||
length = LittleEndian.getShort(ser_output, offset);
|
length = LittleEndian.getShort( ser_output, offset );
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
offset += LittleEndianConsts.SHORT_SIZE;
|
||||||
recordData = new byte[ length ];
|
recordData = new byte[length];
|
||||||
System.arraycopy(ser_output, offset, recordData, 0, length);
|
System.arraycopy( ser_output, offset, recordData, 0, length );
|
||||||
offset += length;
|
offset += length;
|
||||||
testRecord = new SSTRecord(type, length, recordData);
|
testRecord = new SSTRecord( type, length, recordData );
|
||||||
for (int count = 0; count < 7; count++)
|
for ( int count = 0; count < 7; count++ )
|
||||||
{
|
{
|
||||||
assertEquals(ContinueRecord.sid,
|
assertEquals( ContinueRecord.sid,
|
||||||
LittleEndian.getShort(ser_output, offset));
|
LittleEndian.getShort( ser_output, offset ) );
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
offset += LittleEndianConsts.SHORT_SIZE;
|
||||||
length = LittleEndian.getShort(ser_output, offset);
|
length = LittleEndian.getShort( ser_output, offset );
|
||||||
offset += LittleEndianConsts.SHORT_SIZE;
|
offset += LittleEndianConsts.SHORT_SIZE;
|
||||||
cr = new byte[ length ];
|
cr = new byte[length];
|
||||||
System.arraycopy(ser_output, offset, cr, 0, length);
|
System.arraycopy( ser_output, offset, cr, 0, length );
|
||||||
testRecord.processContinueRecord(cr);
|
testRecord.processContinueRecord( cr );
|
||||||
offset += length;
|
offset += length;
|
||||||
}
|
}
|
||||||
assertEquals(offset, ser_output.length);
|
assertEquals( offset, ser_output.length );
|
||||||
assertEquals(record, testRecord);
|
assertEquals( record, testRecord );
|
||||||
|
assertEquals( record.countStrings(), testRecord.countStrings() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,23 +219,23 @@ public class TestSSTRecord
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public void testHugeStrings()
|
public void testHugeStrings()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
SSTRecord record = new SSTRecord();
|
SSTRecord record = new SSTRecord();
|
||||||
byte[][] bstrings =
|
byte[][] bstrings =
|
||||||
{
|
{
|
||||||
new byte[ 9000 ], new byte[ 7433 ], new byte[ 9002 ],
|
new byte[9000], new byte[7433], new byte[9002],
|
||||||
new byte[ 16998 ]
|
new byte[16998]
|
||||||
};
|
};
|
||||||
String[] strings = new String[ bstrings.length ];
|
String[] strings = new String[bstrings.length];
|
||||||
int total_length = 0;
|
int total_length = 0;
|
||||||
|
|
||||||
for (int k = 0; k < bstrings.length; k++)
|
for ( int k = 0; k < bstrings.length; k++ )
|
||||||
{
|
{
|
||||||
Arrays.fill(bstrings[ k ], ( byte ) ('a' + k));
|
Arrays.fill( bstrings[k], (byte) ( 'a' + k ) );
|
||||||
strings[ k ] = new String(bstrings[ k ]);
|
strings[k] = new String( bstrings[k] );
|
||||||
record.addString(strings[ k ]);
|
record.addString( strings[k] );
|
||||||
total_length += 3 + bstrings[ k ].length;
|
total_length += 3 + bstrings[k].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add overhead of SST record
|
// add overhead of SST record
|
||||||
@ -240,88 +245,88 @@ public class TestSSTRecord
|
|||||||
total_length += 4;
|
total_length += 4;
|
||||||
|
|
||||||
// add overhead of six records
|
// add overhead of six records
|
||||||
total_length += (6 * 4);
|
total_length += ( 6 * 4 );
|
||||||
byte[] content = new byte[ record.getRecordSize() ];
|
byte[] content = new byte[record.getRecordSize()];
|
||||||
|
|
||||||
record.serialize(0, content);
|
record.serialize( 0, content );
|
||||||
assertEquals(total_length, content.length);
|
assertEquals( total_length, content.length );
|
||||||
for (int index = 0; index != 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;
|
index += LittleEndianConsts.SHORT_SIZE;
|
||||||
short record_length = LittleEndian.getShort(content, index);
|
short record_length = LittleEndian.getShort( content, index );
|
||||||
|
|
||||||
index += LittleEndianConsts.SHORT_SIZE;
|
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;
|
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
|
else
|
||||||
{
|
{
|
||||||
record.processContinueRecord(data);
|
record.processContinueRecord( data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertEquals(strings.length, record.getNumStrings());
|
assertEquals( strings.length, record.getNumStrings() );
|
||||||
assertEquals(strings.length, record.getNumUniqueStrings());
|
assertEquals( strings.length, record.getNumUniqueStrings() );
|
||||||
assertEquals(strings.length, record.countStrings());
|
assertEquals( strings.length, record.countStrings() );
|
||||||
for (int k = 0; k < strings.length; k++)
|
for ( int k = 0; k < strings.length; k++ )
|
||||||
{
|
{
|
||||||
assertEquals(strings[ k ], record.getString(k));
|
assertEquals( strings[k], record.getString( k ) );
|
||||||
}
|
}
|
||||||
record = new SSTRecord();
|
record = new SSTRecord();
|
||||||
bstrings[ 1 ] = new byte[ bstrings[ 1 ].length - 1 ];
|
bstrings[1] = new byte[bstrings[1].length - 1];
|
||||||
for (int k = 0; k < bstrings.length; k++)
|
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));
|
Arrays.fill( bstrings[k], (byte) ( 'a' + k ) );
|
||||||
strings[ k ] = new String(bstrings[ k ]);
|
strings[k] = new String( bstrings[k] );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char[] data = new char[ bstrings[ k ].length / 2 ];
|
char[] data = new char[bstrings[k].length / 2];
|
||||||
|
|
||||||
Arrays.fill(data, ( char ) ('\u2122' + k));
|
Arrays.fill( data, (char) ( '\u2122' + k ) );
|
||||||
strings[ k ] = new String(data);
|
strings[k] = new String( data );
|
||||||
}
|
}
|
||||||
record.addString(strings[ k ]);
|
record.addString( strings[k] );
|
||||||
}
|
}
|
||||||
content = new byte[ record.getRecordSize() ];
|
content = new byte[record.getRecordSize()];
|
||||||
record.serialize(0, content);
|
record.serialize( 0, content );
|
||||||
total_length--;
|
total_length--;
|
||||||
assertEquals(total_length, content.length);
|
assertEquals( total_length, content.length );
|
||||||
for (int index = 0; index != 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;
|
index += LittleEndianConsts.SHORT_SIZE;
|
||||||
short record_length = LittleEndian.getShort(content, index);
|
short record_length = LittleEndian.getShort( content, index );
|
||||||
|
|
||||||
index += LittleEndianConsts.SHORT_SIZE;
|
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;
|
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
|
else
|
||||||
{
|
{
|
||||||
record.processContinueRecord(data);
|
record.processContinueRecord( data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertEquals(strings.length, record.getNumStrings());
|
assertEquals( strings.length, record.getNumStrings() );
|
||||||
assertEquals(strings.length, record.getNumUniqueStrings());
|
assertEquals( strings.length, record.getNumUniqueStrings() );
|
||||||
assertEquals(strings.length, record.countStrings());
|
assertEquals( strings.length, record.countStrings() );
|
||||||
for (int k = 0; k < strings.length; k++)
|
for ( int k = 0; k < strings.length; k++ )
|
||||||
{
|
{
|
||||||
assertEquals(strings[ k ], record.getString(k));
|
assertEquals( strings[k], record.getString( k ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,9 +335,8 @@ public class TestSSTRecord
|
|||||||
*
|
*
|
||||||
* @exception IOException
|
* @exception IOException
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void testSSTRecordBug()
|
public void testSSTRecordBug()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
|
||||||
// create an SSTRecord and write a certain pattern of strings
|
// create an SSTRecord and write a certain pattern of strings
|
||||||
@ -342,68 +346,67 @@ public class TestSSTRecord
|
|||||||
// the record will start with two integers, then this string
|
// the record will start with two integers, then this string
|
||||||
// ... that will eat up 16 of the 8224 bytes that the record
|
// ... that will eat up 16 of the 8224 bytes that the record
|
||||||
// can hold
|
// can hold
|
||||||
record.addString("Hello");
|
record.addString( "Hello" );
|
||||||
|
|
||||||
// now we have an additional 8208 bytes, which is an exact
|
// now we have an additional 8208 bytes, which is an exact
|
||||||
// multiple of 16 bytes
|
// multiple of 16 bytes
|
||||||
long testvalue = 1000000000000L;
|
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);
|
record.serialize( 0, content );
|
||||||
assertEquals(( byte ) 13, content[ 4 + 8228 ]);
|
assertEquals( (byte) 13, content[4 + 8228] );
|
||||||
assertEquals(( byte ) 13, content[ 4 + 8228 * 2 ]);
|
assertEquals( (byte) 13, content[4 + 8228 * 2] );
|
||||||
assertEquals(( byte ) 13, content[ 4 + 8228 * 3 ]);
|
assertEquals( (byte) 13, content[4 + 8228 * 3] );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* test simple addString
|
* test simple addString
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void testSimpleAddString()
|
public void testSimpleAddString()
|
||||||
{
|
{
|
||||||
SSTRecord record = new SSTRecord();
|
SSTRecord record = new SSTRecord();
|
||||||
String s1 = "Hello world";
|
String s1 = "Hello world";
|
||||||
|
|
||||||
// \u2122 is the encoding of the trademark symbol ...
|
// \u2122 is the encoding of the trademark symbol ...
|
||||||
String s2 = "Hello world\u2122";
|
String s2 = "Hello world\u2122";
|
||||||
|
|
||||||
assertEquals(0, record.addString(s1));
|
assertEquals( 0, record.addString( s1 ) );
|
||||||
assertEquals(s1, record.getString(0));
|
assertEquals( s1, record.getString( 0 ) );
|
||||||
assertEquals(1, record.countStrings());
|
assertEquals( 1, record.countStrings() );
|
||||||
assertEquals(1, record.getNumStrings());
|
assertEquals( 1, record.getNumStrings() );
|
||||||
assertEquals(1, record.getNumUniqueStrings());
|
assertEquals( 1, record.getNumUniqueStrings() );
|
||||||
assertEquals(0, record.addString(s1));
|
assertEquals( 0, record.addString( s1 ) );
|
||||||
assertEquals(s1, record.getString(0));
|
assertEquals( s1, record.getString( 0 ) );
|
||||||
assertEquals(1, record.countStrings());
|
assertEquals( 1, record.countStrings() );
|
||||||
assertEquals(2, record.getNumStrings());
|
assertEquals( 2, record.getNumStrings() );
|
||||||
assertEquals(1, record.getNumUniqueStrings());
|
assertEquals( 1, record.getNumUniqueStrings() );
|
||||||
assertEquals(1, record.addString(s2));
|
assertEquals( 1, record.addString( s2 ) );
|
||||||
assertEquals(s2, record.getString(1));
|
assertEquals( s2, record.getString( 1 ) );
|
||||||
assertEquals(2, record.countStrings());
|
assertEquals( 2, record.countStrings() );
|
||||||
assertEquals(3, record.getNumStrings());
|
assertEquals( 3, record.getNumStrings() );
|
||||||
assertEquals(2, record.getNumUniqueStrings());
|
assertEquals( 2, record.getNumUniqueStrings() );
|
||||||
Iterator iter = record.getStrings();
|
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
|
else
|
||||||
{
|
{
|
||||||
fail("cannot match string: " + ucs.getString());
|
fail( "cannot match string: " + ucs.getString() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,25 +418,25 @@ public class TestSSTRecord
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public void testReaderConstructor()
|
public void testReaderConstructor()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
byte[] testdata = readTestData("BigSSTRecord");
|
byte[] testdata = HexRead.readTestData( _test_file_path + File.separator + "BigSSTRecord" );
|
||||||
byte[] input = new byte[ testdata.length - 4 ];
|
byte[] input = new byte[testdata.length - 4];
|
||||||
|
|
||||||
System.arraycopy(testdata, 4, input, 0, input.length);
|
System.arraycopy( testdata, 4, input, 0, input.length );
|
||||||
SSTRecord record = new SSTRecord(LittleEndian.getShort(testdata, 0),
|
SSTRecord record = new SSTRecord( LittleEndian.getShort( testdata, 0 ),
|
||||||
LittleEndian.getShort(testdata, 2),
|
LittleEndian.getShort( testdata, 2 ),
|
||||||
input);
|
input );
|
||||||
|
|
||||||
assertEquals(1464, record.getNumStrings());
|
assertEquals( 1464, record.getNumStrings() );
|
||||||
assertEquals(688, record.getNumUniqueStrings());
|
assertEquals( 688, record.getNumUniqueStrings() );
|
||||||
assertEquals(492, record.countStrings());
|
assertEquals( 492, record.countStrings() );
|
||||||
assertEquals(1, record.getExpectedChars());
|
assertEquals( 1, record.getDeserializer().getContinuationExpectedChars() );
|
||||||
assertEquals("Consolidated B-24J Liberator The Dragon & His Tai",
|
assertEquals( "Consolidated B-24J Liberator The Dragon & His Tai",
|
||||||
record.getUnfinishedString());
|
record.getDeserializer().getUnfinishedString() );
|
||||||
assertEquals(52, record.getTotalLength());
|
// assertEquals( 52, record.getDeserializer().getTotalLength() );
|
||||||
assertEquals(3, record.getStringDataOffset());
|
// assertEquals( 3, record.getDeserializer().getStringDataOffset() );
|
||||||
assertTrue(!record.isWideChar());
|
assertTrue( !record.getDeserializer().isWideChar() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -444,26 +447,26 @@ public class TestSSTRecord
|
|||||||
{
|
{
|
||||||
SSTRecord record = new SSTRecord();
|
SSTRecord record = new SSTRecord();
|
||||||
|
|
||||||
assertEquals(0, record.getNumStrings());
|
assertEquals( 0, record.getNumStrings() );
|
||||||
assertEquals(0, record.getNumUniqueStrings());
|
assertEquals( 0, record.getNumUniqueStrings() );
|
||||||
assertEquals(0, record.countStrings());
|
assertEquals( 0, record.countStrings() );
|
||||||
assertEquals(0, record.getExpectedChars());
|
assertEquals( 0, record.getDeserializer().getContinuationExpectedChars() );
|
||||||
assertEquals("", record.getUnfinishedString());
|
assertEquals( "", record.getDeserializer().getUnfinishedString() );
|
||||||
assertEquals(0, record.getTotalLength());
|
// assertEquals( 0, record.getDeserializer().getTotalLength() );
|
||||||
assertEquals(0, record.getStringDataOffset());
|
// assertEquals( 0, record.getDeserializer().getStringDataOffset() );
|
||||||
assertTrue(!record.isWideChar());
|
assertTrue( !record.getDeserializer().isWideChar() );
|
||||||
byte[] output = record.serialize();
|
byte[] output = record.serialize();
|
||||||
byte[] expected =
|
byte[] expected =
|
||||||
{
|
{
|
||||||
( byte ) record.getSid(), ( byte ) (record.getSid() >> 8),
|
(byte) record.getSid(), (byte) ( record.getSid() >> 8 ),
|
||||||
( byte ) 8, ( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0,
|
(byte) 8, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
|
||||||
( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0, ( byte ) 0
|
(byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0
|
||||||
};
|
};
|
||||||
|
|
||||||
assertEquals(expected.length, output.length);
|
assertEquals( expected.length, output.length );
|
||||||
for (int k = 0; k < expected.length; k++)
|
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,102 +476,99 @@ public class TestSSTRecord
|
|||||||
* @param ignored_args
|
* @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");
|
System.out.println( "Testing hssf.record.SSTRecord functionality" );
|
||||||
junit.textui.TestRunner.run(TestSSTRecord.class);
|
junit.textui.TestRunner.run( TestSSTRecord.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte [] readTestData(String filename)
|
/**
|
||||||
throws IOException
|
* 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 file = new File( _test_file_path + File.separator + "duprich1.xls" );
|
||||||
+ File.separator
|
InputStream stream = new FileInputStream( file );
|
||||||
+ filename);
|
HSSFWorkbook wb = new HSSFWorkbook( stream );
|
||||||
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)
|
|
||||||
{
|
|
||||||
|
|
||||||
case '0' :
|
|
||||||
case '1' :
|
|
||||||
case '2' :
|
|
||||||
case '3' :
|
|
||||||
case '4' :
|
|
||||||
case '5' :
|
|
||||||
case '6' :
|
|
||||||
case '7' :
|
|
||||||
case '8' :
|
|
||||||
case '9' :
|
|
||||||
b <<= 4;
|
|
||||||
b += ( byte ) (count - '0');
|
|
||||||
characterCount++;
|
|
||||||
if (characterCount == 2)
|
|
||||||
{
|
|
||||||
bytes.add(new Byte(b));
|
|
||||||
characterCount = 0;
|
|
||||||
b = ( byte ) 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'A' :
|
|
||||||
case 'B' :
|
|
||||||
case 'C' :
|
|
||||||
case 'D' :
|
|
||||||
case 'E' :
|
|
||||||
case 'F' :
|
|
||||||
b <<= 4;
|
|
||||||
b += ( byte ) (count + 10 - 'A');
|
|
||||||
characterCount++;
|
|
||||||
if (characterCount == 2)
|
|
||||||
{
|
|
||||||
bytes.add(new Byte(b));
|
|
||||||
characterCount = 0;
|
|
||||||
b = ( byte ) 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'a' :
|
|
||||||
case 'b' :
|
|
||||||
case 'c' :
|
|
||||||
case 'd' :
|
|
||||||
case 'e' :
|
|
||||||
case 'f' :
|
|
||||||
b <<= 4;
|
|
||||||
b += ( byte ) (count + 10 - 'a');
|
|
||||||
characterCount++;
|
|
||||||
if (characterCount == 2)
|
|
||||||
{
|
|
||||||
bytes.add(new Byte(b));
|
|
||||||
characterCount = 0;
|
|
||||||
b = ( byte ) 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case -1 :
|
|
||||||
done = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default :
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream.close();
|
stream.close();
|
||||||
Byte[] polished = ( Byte [] ) bytes.toArray(new Byte[ 0 ]);
|
HSSFSheet sheet = wb.getSheetAt( 1 );
|
||||||
byte[] rval = new byte[ polished.length ];
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSpanRichTextToPlainText()
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
byte[] bytes = HexRead.readTestData( _test_file_path + File.separator + "richtextdata.txt" );
|
||||||
|
BinaryTree strings = new BinaryTree();
|
||||||
|
SSTDeserializer deserializer = new SSTDeserializer( strings );
|
||||||
|
deserializer.manufactureStrings( bytes, 0, (short) 45 );
|
||||||
|
byte[] continueBytes = new byte[bytes.length - 45];
|
||||||
|
System.arraycopy( bytes, 45, continueBytes, 0, bytes.length - 45 );
|
||||||
|
deserializer.processContinueRecord( continueBytes );
|
||||||
|
// System.out.println( "strings.getKeyForValue(new Integer(0)) = " + strings.get( new Integer( 0 ) ) );
|
||||||
|
|
||||||
|
assertEquals( "At a dinner party orAt At At ", strings.get( new Integer( 0 ) ) + "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testContinuationWithNoOverlap()
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
byte[] bytes = HexRead.readTestData( _test_file_path + File.separator + "evencontinuation.txt" );
|
||||||
|
BinaryTree strings = new BinaryTree();
|
||||||
|
SSTDeserializer deserializer = new SSTDeserializer( strings );
|
||||||
|
deserializer.manufactureStrings( bytes, 0, (short) 43 );
|
||||||
|
byte[] continueBytes = new byte[bytes.length - 43];
|
||||||
|
System.arraycopy( bytes, 43, continueBytes, 0, bytes.length - 43 );
|
||||||
|
deserializer.processContinueRecord( continueBytes );
|
||||||
|
|
||||||
|
assertEquals( "At a dinner party or", strings.get( new Integer( 0 ) ) + "" );
|
||||||
|
assertEquals( "At a dinner party", strings.get( new Integer( 1 ) ) + "" );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStringAcross2Continuations()
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
byte[] bytes = HexRead.readTestData( _test_file_path + File.separator + "stringacross2continuations.txt" );
|
||||||
|
BinaryTree strings = new BinaryTree();
|
||||||
|
SSTDeserializer deserializer = new SSTDeserializer( strings );
|
||||||
|
deserializer.manufactureStrings( bytes, 0, (short) 43 );
|
||||||
|
bytes = HexRead.readTestData( _test_file_path + File.separator + "stringacross2continuationsCR1.txt" );
|
||||||
|
deserializer.processContinueRecord( bytes );
|
||||||
|
bytes = HexRead.readTestData( _test_file_path + File.separator + "stringacross2continuationsCR2.txt" );
|
||||||
|
deserializer.processContinueRecord( bytes );
|
||||||
|
|
||||||
|
assertEquals( "At a dinner party or", strings.get( new Integer( 0 ) ) + "" );
|
||||||
|
assertEquals( "At a dinner partyAt a dinner party", strings.get( new Integer( 1 ) ) + "" );
|
||||||
|
|
||||||
for (int j = 0; j < polished.length; j++)
|
|
||||||
{
|
|
||||||
rval[ j ] = polished[ j ].byteValue();
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,10 @@
|
|||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.hssf.usermodel;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
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.
|
* Test HSSFRow is okay.
|
||||||
@ -87,33 +90,47 @@ public class TestHSSFRow
|
|||||||
assertEquals(1, row.getFirstCellNum());
|
assertEquals(1, row.getFirstCellNum());
|
||||||
assertEquals(2, row.getLastCellNum());
|
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()
|
public void testRemoveCell()
|
||||||
|
throws Exception
|
||||||
{
|
{
|
||||||
HSSFWorkbook workbook = new HSSFWorkbook();
|
HSSFWorkbook workbook = new HSSFWorkbook();
|
||||||
HSSFSheet sheet = workbook.createSheet();
|
HSSFSheet sheet = workbook.createSheet();
|
||||||
HSSFRow row = sheet.createRow((short) 0);
|
HSSFRow row = sheet.createRow((short) 0);
|
||||||
assertEquals(-1, row.getLastCellNum());
|
assertEquals(-1, row.getLastCellNum());
|
||||||
assertEquals(-1, row.getFirstCellNum());
|
assertEquals(-1, row.getFirstCellNum());
|
||||||
row.createCell((short)1);
|
row.createCell((short) 1);
|
||||||
assertEquals(1, row.getLastCellNum());
|
assertEquals(1, row.getLastCellNum());
|
||||||
assertEquals(1, row.getFirstCellNum());
|
assertEquals(1, row.getFirstCellNum());
|
||||||
row.createCell((short)3);
|
row.createCell((short) 3);
|
||||||
assertEquals(3, row.getLastCellNum());
|
assertEquals(3, row.getLastCellNum());
|
||||||
assertEquals(1, row.getFirstCellNum());
|
assertEquals(1, row.getFirstCellNum());
|
||||||
row.removeCell(row.getCell((short)3));
|
row.removeCell(row.getCell((short) 3));
|
||||||
assertEquals(1, row.getLastCellNum());
|
assertEquals(1, row.getLastCellNum());
|
||||||
assertEquals(1, row.getFirstCellNum());
|
assertEquals(1, row.getFirstCellNum());
|
||||||
row.removeCell(row.getCell((short)1));
|
row.removeCell(row.getCell((short) 1));
|
||||||
assertEquals(-1, row.getLastCellNum());
|
assertEquals(-1, row.getLastCellNum());
|
||||||
assertEquals(-1, row.getFirstCellNum());
|
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());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,6 @@ import junit.framework.TestCase;
|
|||||||
import org.apache.poi.hssf.model.Sheet;
|
import org.apache.poi.hssf.model.Sheet;
|
||||||
import org.apache.poi.hssf.record.VCenterRecord;
|
import org.apache.poi.hssf.record.VCenterRecord;
|
||||||
import org.apache.poi.hssf.record.WSBoolRecord;
|
import org.apache.poi.hssf.record.WSBoolRecord;
|
||||||
import org.apache.poi.hssf.dev.BiffViewer;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@ -190,7 +189,14 @@ public class TestHSSFSheet
|
|||||||
tempFile.delete();
|
tempFile.delete();
|
||||||
assertNotNull(row);
|
assertNotNull(row);
|
||||||
assertEquals(2, row.getPhysicalNumberOfCells());
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -437,6 +437,12 @@ public class TestLittleEndian
|
|||||||
return result;
|
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
|
* main method to run the unit tests
|
||||||
*
|
*
|
||||||
|