merge BETA3 with trunk r615229. OOXML is excluded

git-svn-id: https://svn.apache.org/repos/asf/poi/tags/REL_3_0_2_BETA3@615249 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-01-25 15:48:14 +00:00
parent 63f92098a3
commit 7cd05f9a2a
112 changed files with 3799 additions and 455 deletions

View File

@ -137,7 +137,7 @@ under the License.
<property name="ooxml.jar4.dir" location="${ooxml.lib}/jsr173_1.0_api.jar"/>
<property name="ooxml.jar4.url" value="${repository}/xmlbeans/jars/jsr173_1.0_api.jar"/>
<!-- No official release of openxml4j yet -->
<property name="ooxml.jar5.dir" location="${ooxml.lib}/openxml4j-bin-prealpha-071224.jar"/>
<property name="ooxml.jar5.dir" location="${ooxml.lib}/openxml4j-bin-alpha-080124.jar"/>
<property name="ooxml.jar5.url" value="http://people.apache.org/~nick/openxml4j-bin-prealpha-071224.jar"/>
<!-- See http://www.ecma-international.org/publications/standards/Ecma-376.htm -->
@ -159,7 +159,7 @@ under the License.
<property name="mavendist.poi.dir" location="build/maven-dist/poi"/>
<property name="mavendist.oap.dir" location="build/maven-dist/org.apache.poi"/>
<property name="jar.name" value="poi"/>
<property name="version.id" value="3.0.2-beta2"/>
<property name="version.id" value="3.0.2-FINAL"/>
<property name="halt.on.test.failure" value="true"/>
<property name="jdk.version.source" value="1.3"
description="JDK version of source code"/>
@ -368,6 +368,8 @@ under the License.
destfile="${ooxml.xsds.jar}"
javasource="1.4"
failonerror="false"
fork="true"
memoryMaximumSize="512m"
>
<classpath refid="ooxml.classpath"/>
</xmlbean>
@ -461,6 +463,16 @@ under the License.
destdir="${ooxml.output.dir}" debug="on" srcdir="${ooxml.src}">
<classpath refid="ooxml.classpath"/>
</javac>
<javac target="1.5" source="1.5"
failonerror="true" destdir="${ooxml.output.test.dir}" debug="on"
fork="yes" srcdir="${ooxml.src.test}">
<classpath>
<path refid="ooxml.classpath"/>
<pathelement location="${ooxml.output.dir}"/>
<pathelement location="${junit.jar1.dir}"/>
</classpath>
</javac>
</target>
<target name="test" depends="test-main,test-scratchpad,test-contrib"

View File

@ -36,6 +36,27 @@
<!-- Don't forget to update status.xml too! -->
<release version="3.0.2-FINAL" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">44292 - Correctly process the last paragraph in a word file</action>
<action dev="POI-DEVELOPERS" type="fix">44254 - Avoid some unread byte warnings, and properly understand DVALRecord</action>
<action dev="POI-DEVELOPERS" type="add">Add another formula evaluation method, evaluateFormulaCell(cell), which will re-calculate the value for a formula, without affecting the formula itself.</action>
<action dev="POI-DEVELOPERS" type="fix">41726 - Fix how we handle signed cell offsets in relative areas and references</action>
<action dev="POI-DEVELOPERS" type="add">44233 - Support for getting and setting a flag on the sheet, which tells excel to re-calculate all formulas on it at next reload</action>
<action dev="POI-DEVELOPERS" type="fix">44201 - Enable cloning of sheets with data validation rules</action>
<action dev="POI-DEVELOPERS" type="fix">44200 - Enable cloning of sheets with notes</action>
<action dev="POI-DEVELOPERS" type="add">43008 - Add a moveCell method to HSSFRow, and deprecate setCellNum(), which didn't update things properly</action>
<action dev="POI-DEVELOPERS" type="fix">43058 - Support setting row grouping on files from CR IX, which lack GutsRecords</action>
<action dev="POI-DEVELOPERS" type="fix">31795 - Support cloning of sheets with certain drawing objects on them</action>
<action dev="POI-DEVELOPERS" type="fix">43902 - Don't consider merged regions when auto-sizing columns</action>
<action dev="POI-DEVELOPERS" type="fix">42464 - Avoid "Expected ExpPtg to be converted from Shared to Non-Shared Formula" on large, formula heavy worksheets</action>
<action dev="POI-DEVELOPERS" type="add">42033 - Add support for named ranges with unicode names</action>
<action dev="POI-DEVELOPERS" type="add">34023 - When shifting rows, update formulas on that sheet to point to the new location of those rows</action>
<action dev="POI-DEVELOPERS" type="add">Support getting all the cells referenced by an AreaReference, not just the corner ones</action>
<action dev="POI-DEVELOPERS" type="add">43510 - Add support for named ranges in formulas, including non-contiguous named ranges</action>
<action dev="POI-DEVELOPERS" type="add">43937 - Add support for hiding and un-hiding sheets, and checking their current hidden status</action>
<action dev="POI-DEVELOPERS" type="fix">44167 - Fix for non-contiguous named ranges</action>
<action dev="POI-DEVELOPERS" type="fix">44070 - Fix for shifting comments when shifting rows</action>
</release>
<release version="3.0.2-BETA2" date="2008-01-12">
<action dev="POI-DEVELOPERS" type="add">Support for tables in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">43781 - Fix for extracting text from TextBoxes HSLF in</action>
<action dev="POI-DEVELOPERS" type="fix">Improve JavaDocs relating to hssf font and fill colourings</action>
@ -45,7 +66,6 @@
<action dev="POI-DEVELOPERS" type="add">41064 - [PATCH] Support for String continue records</action>
<action dev="POI-DEVELOPERS" type="add">27511 - [PATCH] Support for data validation, via DVRecord and DVALRecord</action>
</release>
<release version="3.0.2-BETA1" date="2007-12-04">
<action dev="POI-DEVELOPERS" type="fix">43877 and 39512 - Fix for handling mixed OBJ and CONTINUE records.</action>
<action dev="POI-DEVELOPERS" type="fix">43807 - Throw an IllegalArgumentException if asked to create a merged region with invalid columns or rows, rather than writing out a corrupt file</action>

View File

@ -32,14 +32,18 @@
formulas in Excels sheets read-in, or created in POI. This document explains
how to use the API to evaluate your formulas.
</p>
<note> This code currently lives the scratchpad area of the POI CVS repository.
<note> This code currently lives the scratchpad area of the POI SVN repository.
Ensure that you have the scratchpad jar or the scratchpad build area in your
classpath before experimenting with this code.
classpath before experimenting with this code. You are advised
to make use of a recent SVN checkout, as new functions are
being supported fairly frequently.
</note>
</section>
<anchor id="Status"/>
<section><title>Status</title>
<p> The code currently provides implementations for all the arithmatic operators.
It also provides implementations for approx. 20 built in
It also provides implementations for approx. 100 built in
functions in Excel. The framework however makes is easy to add
implementation of new functions. See the <link href="eval-devguide.html"> Formula
evaluation development guide</link> for details. </p>
@ -51,8 +55,12 @@
<p>The following code demonstrates how to use the HSSFFormulaEvaluator
in the context of other POI excel reading code.
</p>
<p>There are two ways in which you can use the HSSFFormulaEvalutator API.</p>
<p>There are several ways in which you can use the HSSFFormulaEvalutator API.</p>
<anchor id="Evaluate"/>
<section><title>Using HSSFFormulaEvaluator.<strong>evaluate</strong>(HSSFCell cell)</title>
<p>This evaluates a given cell, and returns the new value,
without affecting the cell</p>
<source>
FileInputStream fis = new FileInputStream("c:/temp/test.xls");
HSSFWorkbook wb = new HSSFWorkbook(fis);
@ -94,10 +102,62 @@ switch (cellValue.getCellType()) {
a simple value object and does not maintain reference
to the original cell.
</p>
</section>
<section><title>Using HSSFFormulaEvaluator.<strong>evaluateInCell</strong>(HSSFCell cell)
</title>
<anchor id="EvaluateFormulaCell"/>
<section><title>Using HSSFFormulaEvaluator.<strong>evaluateFormulaCell</strong>(HSSFCell cell)</title>
<p><strong>evaluateFormulaCell</strong>(HSSFCell cell)
will check to see if the supplied cell is a formula cell.
If it isn't, then no changes will be made to it. If it is,
then the formula is evaluated. The value for the formula
is saved alongside it, to be displayed in excel. The
formula remains in the cell, just with a new value</p>
<p>The return of the function is the type of the
formula result, such as HSSFCell.CELL_TYPE_BOOLEAN</p>
<source>
FileInputStream fis = new FileInputStream("/somepath/test.xls");
HSSFWorkbook wb = new HSSFWorkbook(fis);
HSSFSheet sheet = wb.getSheetAt(0);
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
// suppose your formula is in B3
CellReference cellReference = new CellReference("B3");
HSSFRow row = sheet.getRow(cellReference.getRow());
HSSFCell cell = row.getCell(cellReference.getCol());
evaluator.setCurrentRow(row);
if (cell!=null) {
switch (<strong>evaluator.evaluateFormulaCell</strong>(cell)) {
case HSSFCell.CELL_TYPE_BOOLEAN:
System.out.println(cell.getBooleanCellValue());
break;
case HSSFCell.CELL_TYPE_NUMERIC:
System.out.println(cell.getNumberCellValue());
break;
case HSSFCell.CELL_TYPE_STRING:
System.out.println(cell.getStringCellValue());
break;
case HSSFCell.CELL_TYPE_BLANK:
break;
case HSSFCell.CELL_TYPE_ERROR:
System.out.println(cell.getErrorCellValue());
break;
// CELL_TYPE_FORMULA will never occur
case HSSFCell.CELL_TYPE_FORMULA:
break;
}
}
</source>
</section>
<anchor id="EvaluateInCell"/>
<section><title>Using HSSFFormulaEvaluator.<strong>evaluateInCell</strong>(HSSFCell cell)</title>
<p><strong>evaluateInCell</strong>(HSSFCell cell) will check to
see if the supplied cell is a formula cell. If it isn't,
then no changes will be made to it. If it is, then the
formula is evaluated, and the new value saved into the cell,
in place of the old formula.</p>
<source>
FileInputStream fis = new FileInputStream("/somepath/test.xls");
HSSFWorkbook wb = new HSSFWorkbook(fis);
@ -132,11 +192,36 @@ if (cell!=null) {
break;
}
}
</source>
</source>
</section>
</section>
<anchor id="EvaluateAll"/>
<section><title>Re-calculating all formulas in a Workbook</title>
<source>
FileInputStream fis = new FileInputStream("/somepath/test.xls");
HSSFWorkbook wb = new HSSFWorkbook(fis);
for(int sheetNum = 0; sheetNum &lt; wb.getNumberOfSheets(); sheetNum++) {
HSSFSheet sheet = wb.getSheetAt(sheetNum);
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
for(Iterator rit = sheet.rowIterator(); rit.hasNext();) {
HSSFRow r = (HSSFRow)rit.next();
evaluator.setCurrentRow(r);
for(Iterator cit = r.cellIterator(); cit.hasNext();) {
HSSFCell c = (HSSFCell)cit.next();
if(c.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
evaluator.evaluateFormulaCell(c);
}
}
}
}
wb.write(new FileOutputStream("/somepath/changed.xls"));
</source>
</section>
</section>
<anchor id="Performance"/>
<section><title>Performance Notes</title>
<ul>
<li>Generally you should have to create only one HSSFFormulaEvaluator

View File

@ -1151,7 +1151,7 @@ Examples:
// retrieve the cell at the named range and test its contents
AreaReference aref = new AreaReference(aNamedCell.getReference());
CellReference[] crefs = aref.getCells();
CellReference[] crefs = aref.getAllReferencedCells();
for (int i=0; i&lt;crefs.length; i++) {
HSSFSheet s = wb.getSheet(crefs[i].getSheetName());
HSSFRow r = sheet.getRow(crefs[i].getRow());
@ -1159,7 +1159,36 @@ Examples:
// extract the cell contents based on cell type etc.
}
</source>
<p>
Reading from non-contiguous Named Ranges
</p>
<source>
// Setup code
String cname = "TestName";
HSSFWorkbook wb = getMyWorkbook(); // retrieve workbook
// Retrieve the named range
// Will be something like "$C$10,$D$12:$D$14";
int namedCellIdx = wb.getNameIndex(cellName);
HSSFName aNamedCell = wb.getNameAt(namedCellIdx);
// Retrieve the cell at the named range and test its contents
// Will get back one AreaReference for C10, and
// another for D12 to D14
AreaReference[] arefs = AreaReference.generateContiguous(aNamedCell.getReference());
for (int i=0; i&lt;arefs.length; i++) {
// Only get the corners of the Area
// (use arefs[i].getAllReferencedCells() to get all cells)
CellReference[] crefs = arefs[i].getCells();
for (int j=0; j&lt;crefs.length; j++) {
// Check it turns into real stuff
HSSFSheet s = wb.getSheet(crefs[j].getSheetName());
HSSFRow r = s.getRow(crefs[j].getRow());
HSSFCell c = r.getCell(crefs[j].getCol());
// Do something with this corner cell
}
}
</source>
</section>
<anchor id="CellComments"/>
<section><title>Cell Comments</title>

View File

@ -31,14 +31,14 @@
</header>
<body>
<section><title>POI 3.0.2 BETA1 Release</title>
<p>The latest release of Apache POI is 3.0.2 BETA1 which was promoted to "Beta" on 04 December 2007. It contains a mixture of
<section><title>POI 3.0.2 BETA2 Release</title>
<p>The latest release of Apache POI is 3.0.2 BETA2 which was promoted to "Beta" on 12 January 2008. It contains a mixture of
new features and bug fixes, compared to 3.0.1. A full list of changes
is available in
<link href="./changes.html">the changelog</link>, and
<link href="http://www.apache.org/dyn/closer.cgi/poi/release/">download</link>
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">download</link>
the source and binaries from your
<link href="http://www.apache.org/dyn/closer.cgi/poi/release/">local mirror</link>.
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">local mirror</link>.
The release is also available from the central Maven repository under Group ID "org.apache.poi".
</p>
</section>

View File

@ -33,6 +33,27 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.0.2-FINAL" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">44292 - Correctly process the last paragraph in a word file</action>
<action dev="POI-DEVELOPERS" type="fix">44254 - Avoid some unread byte warnings, and properly understand DVALRecord</action>
<action dev="POI-DEVELOPERS" type="add">Add another formula evaluation method, evaluateFormulaCell(cell), which will re-calculate the value for a formula, without affecting the formula itself.</action>
<action dev="POI-DEVELOPERS" type="fix">41726 - Fix how we handle signed cell offsets in relative areas and references</action>
<action dev="POI-DEVELOPERS" type="add">44233 - Support for getting and setting a flag on the sheet, which tells excel to re-calculate all formulas on it at next reload</action>
<action dev="POI-DEVELOPERS" type="fix">44201 - Enable cloning of sheets with data validation rules</action>
<action dev="POI-DEVELOPERS" type="fix">44200 - Enable cloning of sheets with notes</action>
<action dev="POI-DEVELOPERS" type="add">43008 - Add a moveCell method to HSSFRow, and deprecate setCellNum(), which didn't update things properly</action>
<action dev="POI-DEVELOPERS" type="fix">43058 - Support setting row grouping on files from CR IX, which lack GutsRecords</action>
<action dev="POI-DEVELOPERS" type="fix">31795 - Support cloning of sheets with certain drawing objects on them</action>
<action dev="POI-DEVELOPERS" type="fix">43902 - Don't consider merged regions when auto-sizing columns</action>
<action dev="POI-DEVELOPERS" type="fix">42464 - Avoid "Expected ExpPtg to be converted from Shared to Non-Shared Formula" on large, formula heavy worksheets</action>
<action dev="POI-DEVELOPERS" type="add">42033 - Add support for named ranges with unicode names</action>
<action dev="POI-DEVELOPERS" type="add">34023 - When shifting rows, update formulas on that sheet to point to the new location of those rows</action>
<action dev="POI-DEVELOPERS" type="add">Support getting all the cells referenced by an AreaReference, not just the corner ones</action>
<action dev="POI-DEVELOPERS" type="add">43510 - Add support for named ranges in formulas, including non-contiguous named ranges</action>
<action dev="POI-DEVELOPERS" type="add">43937 - Add support for hiding and un-hiding sheets, and checking their current hidden status</action>
<action dev="POI-DEVELOPERS" type="fix">44167 - Fix for non-contiguous named ranges</action>
<action dev="POI-DEVELOPERS" type="fix">44070 - Fix for shifting comments when shifting rows</action>
</release>
<release version="3.0.2-BETA2" date="2008-01-12">
<action dev="POI-DEVELOPERS" type="add">Support for tables in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">43781 - Fix for extracting text from TextBoxes HSLF in</action>
<action dev="POI-DEVELOPERS" type="fix">Improve JavaDocs relating to hssf font and fill colourings</action>

View File

@ -100,11 +100,47 @@ public class EscherContainerRecord extends EscherRecord
}
return 8 + childRecordsSize;
}
/**
* Do any of our (top level) children have the
* given recordId?
*/
public boolean hasChildOfType(short recordId) {
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
if(r.getRecordId() == recordId) {
return true;
}
}
return false;
}
/**
* Returns a list of all the child (escher) records
* of the container.
*/
public List getChildRecords()
{
return childRecords;
}
/**
* Returns all of our children which are also
* EscherContainers (may be 0, 1, or vary rarely
* 2 or 3)
*/
public List getChildContainers() {
List containers = new ArrayList();
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
if(r instanceof EscherContainerRecord) {
containers.add(r);
}
}
return containers;
}
public void setChildRecords( List childRecords )
{
@ -148,6 +184,10 @@ public class EscherContainerRecord extends EscherRecord
}
public String toString()
{
return toString("");
}
public String toString(String indent)
{
String nl = System.getProperty( "line.separator" );
@ -155,20 +195,32 @@ public class EscherContainerRecord extends EscherRecord
if ( getChildRecords().size() > 0 )
{
children.append( " children: " + nl );
int count = 0;
for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
{
String newIndent = indent + " ";
EscherRecord record = (EscherRecord) iterator.next();
children.append( record.toString() );
// children.append( nl );
children.append(newIndent + "Child " + count + ":" + nl);
if(record instanceof EscherContainerRecord) {
EscherContainerRecord ecr = (EscherContainerRecord)record;
children.append( ecr.toString(newIndent));
} else {
children.append( record.toString() );
}
count++;
}
}
return getClass().getName() + " (" + getRecordName() + "):" + nl +
" isContainer: " + isContainerRecord() + nl +
" options: 0x" + HexDump.toHex( getOptions() ) + nl +
" recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
" numchildren: " + getChildRecords().size() + nl +
children.toString();
return
indent + getClass().getName() + " (" + getRecordName() + "):" + nl +
indent + " isContainer: " + isContainerRecord() + nl +
indent + " options: 0x" + HexDump.toHex( getOptions() ) + nl +
indent + " recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
indent + " numchildren: " + getChildRecords().size() + nl +
indent + children.toString();
}

View File

@ -18,11 +18,14 @@
package org.apache.poi.ddf;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.HexDump;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.io.IOException;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
/**
* The opt record is used to store property values for a shape. It is the key to determining

View File

@ -39,6 +39,13 @@ public class DrawingManager2
{
this.dgg = dgg;
}
/**
* Clears the cached list of drawing groups
*/
public void clearDrawingGroups() {
drawingGroups.clear();
}
public EscherDgRecord createDgRecord()
{
@ -93,9 +100,13 @@ public class DrawingManager2
}
//////////// Non-public methods /////////////
/**
* Finds the next available (1 based) drawing group id
*/
short findNewDrawingGroupId()
{
short dgId = 1;
short dgId = 1;
while ( drawingGroupExists( dgId ) )
dgId++;
return dgId;

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
//import PTG's .. since we need everything, import *
import org.apache.poi.hssf.record.formula.*;
@ -65,6 +66,12 @@ public class FormulaParser {
* Using an unsynchronized linkedlist to implement a stack since we're not multi-threaded.
*/
private List functionTokens = new LinkedList();
/**
* Used for spotting if we have a cell reference,
* or a named range
*/
private final static Pattern CELL_REFERENCE_PATTERN = Pattern.compile("(?:('?)[^:\\\\/\\?\\*\\[\\]]+\\1!)?\\$?[A-Za-z]+\\$?[\\d]+");
private static char TAB = '\t';
private static char CR = '\n';
@ -306,15 +313,27 @@ public class FormulaParser {
tokens.add(new Ref3DPtg(first,externIdx));
}
} else {
//this can be either a cell ref or a named range !!
boolean cellRef = true ; //we should probably do it with reg exp??
// This can be either a cell ref or a named range
// Try to spot which it is
boolean cellRef = CELL_REFERENCE_PATTERN.matcher(name).matches();
boolean boolLit = (name.equals("TRUE") || name.equals("FALSE"));
if (boolLit) {
tokens.add(new BoolPtg(name));
} else if (cellRef) {
tokens.add(new ReferencePtg(name));
}else {
//handle after named range is integrated!!
} else {
boolean nameRecordExists = false;
for(int i = 0; i < book.getNumNames(); i++) {
// Our formula will by now contain an upper-cased
// version of any named range names
if(book.getNameRecord(i).getNameText().toUpperCase().equals(name)) {
nameRecordExists = true;
}
}
if(!nameRecordExists)
Abort("Found reference to named range \"" + name + "\", but that named range wasn't defined!");
tokens.add(new NamePtg(name, book));
}
}
}

View File

@ -97,6 +97,8 @@ public class Sheet implements Model
protected ScenarioProtectRecord scenprotect = null;
protected PasswordRecord password = null;
/** Add an UncalcedRecord if not true indicating formulas have not been calculated */
protected boolean uncalced = false;
public static final byte PANE_LOWER_RIGHT = (byte)0;
public static final byte PANE_UPPER_RIGHT = (byte)1;
@ -161,6 +163,9 @@ public class Sheet implements Model
break;
}
}
else if (rec.getSid() == UncalcedRecord.sid) {
retval.uncalced = true;
}
else if (rec.getSid() == DimensionsRecord.sid)
{
// Make a columns aggregate if one hasn't ready been created.
@ -736,8 +741,14 @@ public class Sheet implements Model
{
Record record = (( Record ) records.get(k));
//Once the rows have been found in the list of records, start
//writing out the blocked row information. This includes the DBCell references
// Don't write out UncalcedRecord entries, as
// we handle those specially just below
if (record instanceof UncalcedRecord) {
continue;
}
// Once the rows have been found in the list of records, start
// writing out the blocked row information. This includes the DBCell references
if (record instanceof RowRecordsAggregate) {
pos += ((RowRecordsAggregate)record).serialize(pos, data, cells); // rec.length;
} else if (record instanceof ValueRecordsAggregate) {
@ -745,8 +756,14 @@ public class Sheet implements Model
} else {
pos += record.serialize(pos, data ); // rec.length;
}
//If the BOF record was just serialized then add the IndexRecord
// If the BOF record was just serialized then add the IndexRecord
if (record.getSid() == BOFRecord.sid) {
// Add an optional UncalcedRecord
if (uncalced) {
UncalcedRecord rec = new UncalcedRecord();
pos += rec.serialize(pos, data);
}
//Can there be more than one BOF for a sheet? If not then we can
//remove this guard. So be safe it is left here.
if (rows != null && !haveSerializedIndex) {
@ -2184,6 +2201,11 @@ public class Sheet implements Model
retval += 2;
}
}
// Add space for UncalcedRecord
if (uncalced) {
retval += UncalcedRecord.getStaticRecordSize();
}
return retval;
}
@ -2651,8 +2673,22 @@ public class Sheet implements Model
public boolean isDisplayRowColHeadings() {
return windowTwo.getDisplayRowColHeadings();
}
/**
* @return whether an uncalced record must be inserted or not at generation
*/
public boolean getUncalced() {
return uncalced;
}
/**
* @param uncalced whether an uncalced record must be inserted or not at generation
*/
public void setUncalced(boolean uncalced) {
this.uncalced = uncalced;
}
/**
* Returns the array of margins. If not created, will create.
*
* @return the array of marings.
@ -2663,12 +2699,26 @@ public class Sheet implements Model
return margins;
}
public int aggregateDrawingRecords(DrawingManager2 drawingManager)
/**
* Finds the DrawingRecord for our sheet, and
* attaches it to the DrawingManager (which knows about
* the overall DrawingGroup for our workbook).
* If requested, will create a new DrawRecord
* if none currently exist
* @param drawingManager The DrawingManager2 for our workbook
* @param createIfMissing Should one be created if missing?
*/
public int aggregateDrawingRecords(DrawingManager2 drawingManager, boolean createIfMissing)
{
int loc = findFirstRecordLocBySid(DrawingRecord.sid);
boolean noDrawingRecordsFound = loc == -1;
boolean noDrawingRecordsFound = (loc == -1);
if (noDrawingRecordsFound)
{
if(!createIfMissing) {
// None found, and not allowed to add in
return -1;
}
EscherAggregate aggregate = new EscherAggregate( drawingManager );
loc = findFirstRecordLocBySid(EscherAggregate.sid);
if (loc == -1)
@ -3144,7 +3194,13 @@ public class Sheet implements Model
maxLevel = Math.max(rowRecord.getOutlineLevel(), maxLevel);
}
// Grab the guts record, adding if needed
GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid );
if(guts == null) {
guts = new GutsRecord();
records.add(guts);
}
// Set the levels onto it
guts.setRowLevelMax( (short) ( maxLevel + 1 ) );
guts.setLeftRowGutter( (short) ( 29 + (12 * (maxLevel)) ) );
}

View File

@ -542,6 +542,29 @@ public class Workbook implements Model
.getSheetname();
}
/**
* gets the hidden flag for a given sheet.
*
* @param sheetnum the sheet number (0 based)
* @return True if sheet is hidden
*/
public boolean isSheetHidden(int sheetnum) {
BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum);
return bsr.isHidden();
}
/**
* Hide or unhide a sheet
*
* @param sheetnum The sheet number
* @param hidden True to mark the sheet as hidden, false otherwise
*/
public void setSheetHidden(int sheetnum, boolean hidden) {
BoundSheetRecord bsr = ( BoundSheetRecord ) boundsheets.get(sheetnum);
bsr.setHidden(hidden);
}
/**
* get the sheet's index
* @param name sheet name
@ -2142,13 +2165,68 @@ public class Workbook implements Model
}
return palette;
}
/**
* Finds the primary drawing group, if one already exists
*/
public void findDrawingGroup() {
// Need to find a DrawingGroupRecord that
// contains a EscherDggRecord
for(Iterator rit = records.iterator(); rit.hasNext();) {
Record r = (Record)rit.next();
if(r instanceof DrawingGroupRecord) {
DrawingGroupRecord dg = (DrawingGroupRecord)r;
dg.processChildRecords();
EscherContainerRecord cr =
dg.getEscherContainer();
if(cr == null) {
continue;
}
EscherDggRecord dgg = null;
for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) {
Object er = it.next();
if(er instanceof EscherDggRecord) {
dgg = (EscherDggRecord)er;
}
}
if(dgg != null) {
drawingManager = new DrawingManager2(dgg);
return;
}
}
}
// Look for the DrawingGroup record
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
// If there is one, does it have a EscherDggRecord?
if(dgLoc != -1) {
DrawingGroupRecord dg =
(DrawingGroupRecord)records.get(dgLoc);
EscherDggRecord dgg = null;
for(Iterator it = dg.getEscherRecords().iterator(); it.hasNext();) {
Object er = it.next();
if(er instanceof EscherDggRecord) {
dgg = (EscherDggRecord)er;
}
}
if(dgg != null) {
drawingManager = new DrawingManager2(dgg);
}
}
}
/**
* Creates a drawing group record. If it already exists then it's modified.
* Creates a primary drawing group record. If it already
* exists then it's modified.
*/
public void createDrawingGroup()
{
if (drawingManager == null)
{
EscherContainerRecord dggContainer = new EscherContainerRecord();
@ -2212,7 +2290,6 @@ public class Workbook implements Model
}
}
}
public WindowOneRecord getWindowOne() {

View File

@ -18,16 +18,17 @@
package org.apache.poi.hssf.record;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.ddf.DefaultEscherRecordFactory;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.ddf.EscherRecordFactory;
import org.apache.poi.ddf.NullEscherSerializationListener;
import org.apache.poi.util.LittleEndian;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* The escher container record is used to hold escher records. It is abstract and
* must be subclassed for maximum benefit.
@ -76,7 +77,7 @@ public abstract class AbstractEscherHolderRecord
{
if (id != getSid())
{
throw new RecordFormatException("Not an escher record");
throw new RecordFormatException("Not an escher record! (sid was " + id + ", expecting " + getSid() + ")");
}
}
@ -94,6 +95,9 @@ public abstract class AbstractEscherHolderRecord
}
}
protected void convertRawBytesToEscherRecords() {
convertToEscherRecords(0, rawData.length, rawData);
}
private void convertToEscherRecords( int offset, int size, byte[] data )
{
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
@ -227,7 +231,7 @@ public abstract class AbstractEscherHolderRecord
public Object clone()
{
throw new IllegalStateException("Not implemented yet.");
return cloneViaReserialise();
}
public void addEscherRecord(int index, EscherRecord element)
@ -249,6 +253,54 @@ public abstract class AbstractEscherHolderRecord
{
escherRecords.clear();
}
/**
* If we have a EscherContainerRecord as one of our
* children (and most top level escher holders do),
* then return that.
*/
public EscherContainerRecord getEscherContainer() {
for(Iterator it = escherRecords.iterator(); it.hasNext();) {
Object er = it.next();
if(er instanceof EscherContainerRecord) {
return (EscherContainerRecord)er;
}
}
return null;
}
/**
* Descends into all our children, returning the
* first EscherRecord with the given id, or null
* if none found
*/
public EscherRecord findFirstWithId(short id) {
return findFirstWithId(id, getEscherRecords());
}
private EscherRecord findFirstWithId(short id, List records) {
// Check at our level
for(Iterator it = records.iterator(); it.hasNext();) {
EscherRecord r = (EscherRecord)it.next();
if(r.getRecordId() == id) {
return r;
}
}
// Then check our children in turn
for(Iterator it = records.iterator(); it.hasNext();) {
EscherRecord r = (EscherRecord)it.next();
if(r.isContainerRecord()) {
EscherRecord found =
findFirstWithId(id, r.getChildRecords());
if(found != null) {
return found;
}
}
}
// Not found in this lot
return null;
}
public EscherRecord getEscherRecord(int index)

View File

@ -19,6 +19,7 @@
package org.apache.poi.hssf.record;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
@ -36,6 +37,7 @@ import org.apache.poi.util.StringUtil;
public class BoundSheetRecord
extends Record
{
private static final short HIDDEN_FLAG_MASK = 0x01;
public final static short sid = 0x85;
private int field_1_position_of_BOF;
private short field_2_option_flags;
@ -301,4 +303,12 @@ public class BoundSheetRecord
{
return sid;
}
public boolean isHidden() {
return BitFieldFactory.getInstance(HIDDEN_FLAG_MASK).isSet(field_2_option_flags);
}
public void setHidden(boolean hidden) {
field_2_option_flags = BitFieldFactory.getInstance(HIDDEN_FLAG_MASK).setShortBoolean(field_2_option_flags, hidden);
}
}

View File

@ -29,19 +29,22 @@ import org.apache.poi.util.LittleEndian;
public class DVALRecord extends Record
{
public final static short sid = 0x01B2;
public final static short sid = 0x01B2;
//unknown field ; it's size should be 10
private short field_unknown = 0x0000;
/** Options of the DVAL */
private short field_1_options;
/** Horizontal position of the dialog */
private int field_2_horiz_pos;
/** Vertical position of the dialog */
private int field_3_vert_pos;
//Object ID of the drop down arrow object for list boxes ;
//in our case this will be always FFFF , until
//MSODrawingGroup and MSODrawing records are implemented
private int field_cbo_id = 0xFFFFFFFF;
/** Object ID of the drop down arrow object for list boxes ;
* in our case this will be always FFFF , until
* MSODrawingGroup and MSODrawing records are implemented */
private int field_cbo_id = 0xFFFFFFFF;
//Number of following DV records
//Default value is 1
private int field_3_dv_no = 0x00000000;
/** Number of following DV Records */
private int field_5_dv_no = 0x00000000;
public DVALRecord()
{
@ -66,17 +69,38 @@ public class DVALRecord extends Record
}
}
protected void fillFields(RecordInputStream in)
{
for ( int i=0; i<5; i++)
{
this.field_unknown = in.readShort();
}
protected void fillFields(RecordInputStream in)
{
this.field_1_options = in.readShort();
this.field_2_horiz_pos = in.readInt();
this.field_3_vert_pos = in.readInt();
this.field_cbo_id = in.readInt();
this.field_3_dv_no = in.readInt();
}
this.field_5_dv_no = in.readInt();
}
/**
* @param field_1_options the options of the dialog
*/
public void setOptions(short field_1_options) {
this.field_1_options = field_1_options;
}
/**
* @param field_2_horiz_pos the Horizontal position of the dialog
*/
public void setHorizontalPos(int field_2_horiz_pos) {
this.field_2_horiz_pos = field_2_horiz_pos;
}
/**
* @param field_3_vert_pos the Vertical position of the dialog
*/
public void setVerticalPos(int field_3_vert_pos) {
this.field_3_vert_pos = field_3_vert_pos;
}
/**
* set the object ID of the drop down arrow object for list boxes
* @param cboID - Object ID
*/
@ -91,10 +115,33 @@ public class DVALRecord extends Record
*/
public void setDVRecNo(int dvNo)
{
this.field_3_dv_no = dvNo;
this.field_5_dv_no = dvNo;
}
/**
* @return the field_1_options
*/
public short getOptions() {
return field_1_options;
}
/**
* @return the Horizontal position of the dialog
*/
public int getHorizontalPos() {
return field_2_horiz_pos;
}
/**
* @return the the Vertical position of the dialog
*/
public int getVerticalPos() {
return field_3_vert_pos;
}
/**
* get Object ID of the drop down arrow object for list boxes
*/
public int getObjectID( )
@ -107,29 +154,32 @@ public class DVALRecord extends Record
*/
public int getDVRecNo( )
{
return this.field_3_dv_no;
return this.field_5_dv_no;
}
public String toString()
{
StringBuffer buffer = new StringBuffer();
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append("[DVAL]\n");
buffer.append(" .comboObjectID = ").append(Integer.toHexString(this.getObjectID())).append("\n");
buffer.append(" .DVRecordsNumber = ").append(Integer.toHexString(this.getDVRecNo())).append("\n");
buffer.append("[/DVAL]\n");
return buffer.toString();
}
buffer.append("[DVAL]\n");
buffer.append(" .options = ").append(this.getOptions()).append('\n');
buffer.append(" .horizPos = ").append(this.getHorizontalPos()).append('\n');
buffer.append(" .vertPos = ").append(this.getVerticalPos()).append('\n');
buffer.append(" .comboObjectID = ").append(Integer.toHexString(this.getObjectID())).append("\n");
buffer.append(" .DVRecordsNumber = ").append(Integer.toHexString(this.getDVRecNo())).append("\n");
buffer.append("[/DVAL]\n");
return buffer.toString();
}
public int serialize(int offset, byte [] data)
{
LittleEndian.putShort(data, 0 + offset, this.sid);
LittleEndian.putShort(data, 2 + offset, ( short)(this.getRecordSize()-4));
for ( int i=0; i<5; i++)
{
LittleEndian.putShort(data, 4 + i*2 + offset, (short)this.field_unknown);
}
LittleEndian.putShort(data, 4 + offset, this.getOptions());
LittleEndian.putInt(data, 6 + offset, this.getHorizontalPos());
LittleEndian.putInt(data, 10 + offset, this.getVerticalPos());
LittleEndian.putInt(data, 14 + offset, this.getObjectID());
LittleEndian.putInt(data, 18 + offset, this.getDVRecNo());
return getRecordSize();
@ -149,9 +199,11 @@ public class DVALRecord extends Record
public Object clone()
{
DVALRecord rec = new DVALRecord();
rec.field_unknown = this.field_unknown;
rec.field_1_options = field_1_options;
rec.field_2_horiz_pos = field_2_horiz_pos;
rec.field_3_vert_pos = field_3_vert_pos;
rec.field_cbo_id = this.field_cbo_id;
rec.field_3_dv_no = this.field_3_dv_no;
rec.field_5_dv_no = this.field_5_dv_no;
return rec;
}
}
}

View File

@ -16,16 +16,16 @@
package org.apache.poi.hssf.record;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Stack;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.util.HSSFCellRangeAddress;
import org.apache.poi.util.BitField;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
import org.apache.poi.hssf.util.HSSFCellRangeAddress;
import org.apache.poi.hssf.record.formula.Ptg;
import java.io.IOException;
import java.util.Stack;
import java.util.Hashtable;
import java.util.Enumeration;
/**
* Title: DV Record<P>
@ -187,10 +187,13 @@ public class DVRecord extends Record
this.field_not_used_2 = in.readShort();
//read sec formula data condition
// Not sure if this was needed or not...
//Not sure if this was needed or not...
try {
in.skip(this.field_size_sec_formula);
} catch(IOException e) { throw new IllegalStateException(e); }
} catch(IOException e) {
e.printStackTrace();
throw new IllegalStateException(e.getMessage());
}
token_pos = 0;
while (token_pos < this.field_size_sec_formula)
@ -502,6 +505,14 @@ public class DVRecord extends Record
{
return this.sid;
}
/**
* Clones the object. Uses serialisation, as the
* contents are somewhat complex
*/
public Object clone() {
return cloneViaReserialise();
}
/**@todo DVRecord = Serializare */

View File

@ -72,6 +72,16 @@ public class DrawingGroupRecord extends AbstractEscherHolderRecord
return writeData( offset, data, buffer );
}
}
/**
* Process the bytes into escher records.
* (Not done by default in case we break things,
* unless you set the "poi.deserialize.escher"
* system property)
*/
public void processChildRecords() {
convertRawBytesToEscherRecords();
}
/**
* Size of record (including 4 byte headers for all sections)

View File

@ -106,4 +106,18 @@ public class DrawingRecord extends Record
this.recordData = thedata;
}
}
public Object clone() {
DrawingRecord rec = new DrawingRecord();
if (recordData != null) {
rec.recordData = new byte[ recordData.length ];
System.arraycopy(recordData, 0, rec.recordData, 0, recordData.length);
}
if (contd != null) {
System.arraycopy(contd, 0, rec.contd, 0, contd.length);
rec.contd = new byte[ contd.length ];
}
return rec;
}
}

View File

@ -24,6 +24,8 @@ import org.apache.poi.hssf.model.TextboxShape;
import org.apache.poi.hssf.model.DrawingManager2;
import org.apache.poi.hssf.model.ConvertAnchor;
import org.apache.poi.hssf.model.CommentShape;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import java.util.*;
@ -47,6 +49,7 @@ import java.util.*;
public class EscherAggregate extends AbstractEscherHolderRecord
{
public static final short sid = 9876;
private static POILogger log = POILogFactory.getLogger(EscherAggregate.class);
public static final short ST_MIN = (short) 0;
public static final short ST_NOT_PRIMATIVE = ST_MIN;
@ -523,7 +526,146 @@ public class EscherAggregate extends AbstractEscherHolderRecord
{
this.patriarch = patriarch;
}
/**
* Converts the Records into UserModel
* objects on the bound HSSFPatriarch
*/
public void convertRecordsToUserModel() {
if(patriarch == null) {
throw new IllegalStateException("Must call setPatriarch() first");
}
// The top level container ought to have
// the DgRecord and the container of one container
// per shape group (patriach overall first)
EscherContainerRecord topContainer =
(EscherContainerRecord)getEscherContainer();
if(topContainer == null) {
return;
}
topContainer = (EscherContainerRecord)
topContainer.getChildContainers().get(0);
List tcc = topContainer.getChildContainers();
if(tcc.size() == 0) {
throw new IllegalStateException("No child escher containers at the point that should hold the patriach data, and one container per top level shape!");
}
// First up, get the patriach position
// This is in the first EscherSpgrRecord, in
// the first container, with a EscherSRecord too
EscherContainerRecord patriachContainer =
(EscherContainerRecord)tcc.get(0);
EscherSpgrRecord spgr = null;
for(Iterator it = patriachContainer.getChildRecords().iterator(); it.hasNext();) {
EscherRecord r = (EscherRecord)it.next();
if(r instanceof EscherSpgrRecord) {
spgr = (EscherSpgrRecord)r;
break;
}
}
if(spgr != null) {
patriarch.setCoordinates(
spgr.getRectX1(), spgr.getRectY1(),
spgr.getRectX2(), spgr.getRectY2()
);
}
// Now process the containers for each group
// and objects
for(int i=1; i<tcc.size(); i++) {
EscherContainerRecord shapeContainer =
(EscherContainerRecord)tcc.get(i);
//System.err.println("\n\n*****\n\n");
//System.err.println(shapeContainer);
// Could be a group, or a base object
if(shapeContainer.getChildRecords().size() == 1 &&
shapeContainer.getChildContainers().size() == 1) {
// Group
HSSFShapeGroup group =
new HSSFShapeGroup(null, new HSSFClientAnchor());
patriarch.getChildren().add(group);
EscherContainerRecord groupContainer =
(EscherContainerRecord)shapeContainer.getChild(0);
convertRecordsToUserModel(groupContainer, group);
} else if(shapeContainer.hasChildOfType((short)0xF00D)) {
// TextBox
HSSFTextbox box =
new HSSFTextbox(null, new HSSFClientAnchor());
patriarch.getChildren().add(box);
convertRecordsToUserModel(shapeContainer, box);
} else if(shapeContainer.hasChildOfType((short)0xF011)) {
// Not yet supporting EscherClientDataRecord stuff
} else {
// Base level
convertRecordsToUserModel(shapeContainer, patriarch);
}
}
// Now, clear any trace of what records make up
// the patriarch
// Otherwise, everything will go horribly wrong
// when we try to write out again....
// clearEscherRecords();
drawingManager.getDgg().setFileIdClusters(new EscherDggRecord.FileIdCluster[0]);
// TODO: Support converting our records
// back into shapes
log.log(POILogger.WARN, "Not processing objects into Patriarch!");
}
private void convertRecordsToUserModel(EscherContainerRecord shapeContainer, Object model) {
for(Iterator it = shapeContainer.getChildRecords().iterator(); it.hasNext();) {
EscherRecord r = (EscherRecord)it.next();
if(r instanceof EscherSpgrRecord) {
// This may be overriden by a later EscherClientAnchorRecord
EscherSpgrRecord spgr = (EscherSpgrRecord)r;
if(model instanceof HSSFShapeGroup) {
HSSFShapeGroup g = (HSSFShapeGroup)model;
g.setCoordinates(
spgr.getRectX1(), spgr.getRectY1(),
spgr.getRectX2(), spgr.getRectY2()
);
} else {
throw new IllegalStateException("Got top level anchor but not processing a group");
}
}
else if(r instanceof EscherClientAnchorRecord) {
EscherClientAnchorRecord car = (EscherClientAnchorRecord)r;
if(model instanceof HSSFShape) {
HSSFShape g = (HSSFShape)model;
g.getAnchor().setDx1(car.getDx1());
g.getAnchor().setDx2(car.getDx2());
g.getAnchor().setDy1(car.getDy1());
g.getAnchor().setDy2(car.getDy2());
} else {
throw new IllegalStateException("Got top level anchor but not processing a group or shape");
}
}
else if(r instanceof EscherTextboxRecord) {
EscherTextboxRecord tbr = (EscherTextboxRecord)r;
// Also need to find the TextObjectRecord too
// TODO
}
else if(r instanceof EscherSpRecord) {
// Use flags if needed
}
else if(r instanceof EscherOptRecord) {
// Use properties if needed
}
else {
//System.err.println(r);
}
}
}
public void clear()
{
clearEscherRecords();

View File

@ -179,7 +179,6 @@ public class FormulaRecord
*
* @return calculated value
*/
public double getValue()
{
return field_4_value;
@ -190,7 +189,6 @@ public class FormulaRecord
*
* @return bitmask
*/
public short getOptions()
{
return field_5_options;
@ -199,9 +197,25 @@ public class FormulaRecord
public boolean isSharedFormula() {
return sharedFormula.isSet(field_5_options);
}
public void setSharedFormula(boolean flag) {
sharedFormula.setBoolean(field_5_options, flag);
field_5_options =
sharedFormula.setShortBoolean(field_5_options, flag);
}
public boolean isAlwaysCalc() {
return alwaysCalc.isSet(field_5_options);
}
public void setAlwaysCalc(boolean flag) {
field_5_options =
alwaysCalc.setShortBoolean(field_5_options, flag);
}
public boolean isCalcOnLoad() {
return calcOnLoad.isSet(field_5_options);
}
public void setCalcOnLoad(boolean flag) {
field_5_options =
calcOnLoad.setShortBoolean(field_5_options, flag);
}
/**

View File

@ -20,13 +20,11 @@ package org.apache.poi.hssf.record;
import java.util.List;
import java.util.Stack;
import java.util.Iterator;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
import org.apache.poi.hssf.record.formula.DeletedRef3DPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.RangeAddress;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
@ -272,6 +270,9 @@ public class NameRecord extends Record {
*/
public void setNameText(String name){
field_12_name_text = name;
setCompressedUnicodeFlag(
StringUtil.hasMultibyte(name) ? (byte)1 : (byte)0
);
}
// public void setNameDefintion(String definition){
@ -320,12 +321,24 @@ public class NameRecord extends Record {
return field_2_keyboard_shortcut ;
}
/** gets the name length
/**
* gets the name length, in characters
* @return name length
*/
public byte getNameTextLength(){
return field_3_length_name_text;
}
/**
* gets the name length, in bytes
* @return raw name length
*/
public byte getRawNameTextLength(){
if( (field_11_compressed_unicode_flag & 0x01) == 1 ) {
return (byte)(2 * field_3_length_name_text);
}
return field_3_length_name_text;
}
/** get the definition length
* @return definition length
@ -513,27 +526,16 @@ public class NameRecord extends Record {
data[17 + offset] = getStatusBarLength();
data[18 + offset] = getCompressedUnicodeFlag();
/* temp: gjs
if (isBuiltInName())
{
LittleEndian.putShort( data, 2 + offset, (short) ( 16 + field_13_raw_name_definition.length ) );
data[19 + offset] = field_12_builtIn_name;
System.arraycopy( field_13_raw_name_definition, 0, data, 20 + offset, field_13_raw_name_definition.length );
return 20 + field_13_raw_name_definition.length;
}
else
{ */
int start_of_name_definition = 19 + field_3_length_name_text;
if (this.isBuiltInName()) {
//can send the builtin name directly in
data [19 + offset] = this.getBuiltInName();
} else if ((this.getCompressedUnicodeFlag() & 0x01) == 1) {
StringUtil.putUnicodeLE( getNameText(), data, 19 + offset );
start_of_name_definition = 19 + (2 * field_3_length_name_text);
} else {
StringUtil.putCompressedUnicode( getNameText(), data, 19 + offset );
}
@ -556,15 +558,15 @@ public class NameRecord extends Record {
/* } */
}
/** gets the length of all texts
/**
* Gets the length of all texts, in bytes
* @return total length
*/
public int getTextsLength(){
int result;
result = getNameTextLength() + getDescriptionTextLength() +
getHelpTopicLength() + getStatusBarLength();
result = getRawNameTextLength() + getDescriptionTextLength() +
getHelpTopicLength() + getStatusBarLength();
return result;
}
@ -648,17 +650,46 @@ public class NameRecord extends Record {
Ptg ptg = (Ptg) field_13_name_definition.peek();
String result = "";
if (ptg.getClass() == Area3DPtg.class){
result = ptg.toFormulaString(book);
// If it's a union, descend in and process
if (ptg.getClass() == UnionPtg.class) {
Iterator it =field_13_name_definition.iterator();
while( it.hasNext() ) {
Ptg p = (Ptg)it.next();
} else if (ptg.getClass() == Ref3DPtg.class){
result = ptg.toFormulaString(book);
} else if (ptg.getClass() == DeletedArea3DPtg.class || ptg.getClass() == DeletedRef3DPtg.class) {
result = "#REF!" ; }
String thisRes = getAreaRefString(p, book);
if(thisRes.length() > 0) {
// Add a comma to the end if needed
if(result.length() > 0 && !result.endsWith(",")) {
result += ",";
}
// And add the string it corresponds to
result += thisRes;
}
}
} else {
// Otherwise just get the string
result = getAreaRefString(ptg, book);
}
return result;
}
/**
* Turn the given ptg into a string, or
* return an empty string if nothing is possible
* for it.
*/
private String getAreaRefString(Ptg ptg,Workbook book) {
if (ptg.getClass() == Area3DPtg.class){
return ptg.toFormulaString(book);
} else if (ptg.getClass() == Ref3DPtg.class){
return ptg.toFormulaString(book);
} else if (ptg.getClass() == DeletedArea3DPtg.class || ptg.getClass() == DeletedRef3DPtg.class) {
return "#REF!";
}
return "";
}
/** sets the reference , the area only (range)
* @param ref area reference
*/
@ -686,19 +717,32 @@ public class NameRecord extends Record {
}
if (ra.hasRange()) {
ptg = new Area3DPtg();
((Area3DPtg) ptg).setExternSheetIndex(externSheetIndex);
((Area3DPtg) ptg).setArea(ref);
this.setDefinitionTextLength((short)ptg.getSize());
// Is it contiguous or not?
AreaReference[] refs =
AreaReference.generateContiguous(ref);
this.setDefinitionTextLength((short)0);
// Add the area reference(s)
for(int i=0; i<refs.length; i++) {
ptg = new Area3DPtg();
((Area3DPtg) ptg).setExternSheetIndex(externSheetIndex);
((Area3DPtg) ptg).setArea(refs[i].toString());
field_13_name_definition.push(ptg);
this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
}
// And then a union if we had more than one area
if(refs.length > 1) {
ptg = new UnionPtg();
field_13_name_definition.push(ptg);
this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
}
} else {
ptg = new Ref3DPtg();
((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex);
((Ref3DPtg) ptg).setArea(ref);
field_13_name_definition.push(ptg);
this.setDefinitionTextLength((short)ptg.getSize());
}
field_13_name_definition.push(ptg);
}
/**
@ -832,6 +876,15 @@ public class NameRecord extends Record {
.append("\n");
buffer.append(" .Name (Unicode text) = ").append( getNameText() )
.append("\n");
buffer.append(" .Parts (" + field_13_name_definition.size() +"):")
.append("\n");
Iterator it = field_13_name_definition.iterator();
while(it.hasNext()) {
Ptg ptg = (Ptg)it.next();
buffer.append(" " + ptg.toString()).append("\n");
}
buffer.append(" .Menu text (Unicode string without length field) = ").append( field_14_custom_menu_text )
.append("\n");
buffer.append(" .Description text (Unicode string without length field) = ").append( field_15_description_text )

View File

@ -243,4 +243,15 @@ public class NoteRecord extends Record {
public void setAuthor(String author){
field_5_author = author;
}
public Object clone() {
NoteRecord rec = new NoteRecord();
rec.field_1_row = field_1_row;
rec.field_2_col = field_2_col;
rec.field_3_flags = field_3_flags;
rec.field_4_shapeid = field_4_shapeid;
rec.field_5_author = field_5_author;
return rec;
}
}

View File

@ -125,6 +125,15 @@ public class NoteStructureSubRecord
{
return sid;
}
public Object clone() {
NoteStructureSubRecord rec = new NoteStructureSubRecord();
byte[] recdata = new byte[reserved.length];
System.arraycopy(reserved, 0, recdata, 0, recdata.length);
rec.reserved = recdata;
return rec;
}
}

View File

@ -19,6 +19,8 @@
package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream;
/**
* Title: Record
* Description: All HSSF Records inherit from this class. It
@ -147,4 +149,30 @@ public abstract class Record
public Object clone() {
throw new RuntimeException("The class "+getClass().getName()+" needs to define a clone method");
}
/**
* Clone the current record, via a call to serialise
* it, and another to create a new record from the
* bytes.
* May only be used for classes which don't have
* internal counts / ids in them. For those which
* do, a full record-aware serialise is needed, which
* allocates new ids / counts as needed.
*/
public Record cloneViaReserialise()
{
// Do it via a re-serialise
// It's a cheat, but it works...
byte[] b = serialize();
RecordInputStream rinp = new RecordInputStream(
new ByteArrayInputStream(b)
);
rinp.nextRecord();
Record[] r = RecordFactory.createRecord(rinp);
if(r.length != 1) {
throw new IllegalStateException("Re-serialised a record to clone it, but got " + r.length + " records back!");
}
return r[0];
}
}

View File

@ -76,7 +76,7 @@ public class RecordFactory
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class,
FileSharingRecord.class, ChartTitleFormatRecord.class,
DVRecord.class, DVALRecord.class
DVRecord.class, DVALRecord.class, UncalcedRecord.class
};
}
private static Map recordsMap = recordsToMap(records);

View File

@ -133,6 +133,9 @@ public class RecordInputStream extends InputStream
}
}
/**
* Reads an 8 bit, signed value
*/
public byte readByte() {
checkRecordPosition();
@ -141,7 +144,10 @@ public class RecordInputStream extends InputStream
pos += 1;
return result;
}
/**
* Reads a 16 bit, signed value
*/
public short readShort() {
checkRecordPosition();
@ -169,6 +175,21 @@ public class RecordInputStream extends InputStream
return result;
}
/**
* Reads an 8 bit, unsigned value
*/
public short readUByte() {
short s = readByte();
if(s < 0) {
s += 256;
}
return s;
}
/**
* Reads a 16 bit,un- signed value.
* @return
*/
public int readUShort() {
checkRecordPosition();

View File

@ -20,10 +20,8 @@
package org.apache.poi.hssf.record;
import java.util.Stack;
import java.util.List;
import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.util.LittleEndian;
/**
* Title: SharedFormulaRecord
@ -156,15 +154,12 @@ public class SharedFormulaRecord
return sid;
}
/**
* Shared formulas are to treated like unknown records, and as a result d
*/
protected void fillFields(RecordInputStream in)
{
field_1_first_row = in.readShort();
field_2_last_row = in.readShort();
field_3_first_column = in.readByte();
field_4_last_column = in.readByte();
field_1_first_row = in.readUShort();
field_2_last_row = in.readUShort();
field_3_first_column = in.readUByte();
field_4_last_column = in.readUByte();
field_5_reserved = in.readShort();
field_6_expression_len = in.readShort();
field_7_parsed_expr = getParsedExpressionTokens(in);
@ -181,6 +176,9 @@ public class SharedFormulaRecord
return stack;
}
/**
* Are we shared by the supplied formula record?
*/
public boolean isFormulaInShared(FormulaRecord formula) {
final int formulaRow = formula.getRow();
final int formulaColumn = formula.getColumn();
@ -202,48 +200,48 @@ public class SharedFormulaRecord
Ptg ptg = (Ptg) field_7_parsed_expr.get(k);
if (ptg instanceof RefNPtg) {
RefNPtg refNPtg = (RefNPtg)ptg;
ptg = new ReferencePtg( (short)(formulaRow + refNPtg.getRow()),
(byte)(formulaColumn + refNPtg.getColumn()),
ptg = new ReferencePtg(fixupRelativeRow(formulaRow,refNPtg.getRow(),refNPtg.isRowRelative()),
fixupRelativeColumn(formulaColumn,refNPtg.getColumn(),refNPtg.isColRelative()),
refNPtg.isRowRelative(),
refNPtg.isColRelative());
} else if (ptg instanceof RefNVPtg) {
RefNVPtg refNVPtg = (RefNVPtg)ptg;
ptg = new RefVPtg( (short)(formulaRow + refNVPtg.getRow()),
(byte)(formulaColumn + refNVPtg.getColumn()),
refNVPtg.isRowRelative(),
refNVPtg.isColRelative());
ptg = new RefVPtg(fixupRelativeRow(formulaRow,refNVPtg.getRow(),refNVPtg.isRowRelative()),
fixupRelativeColumn(formulaColumn,refNVPtg.getColumn(),refNVPtg.isColRelative()),
refNVPtg.isRowRelative(),
refNVPtg.isColRelative());
} else if (ptg instanceof RefNAPtg) {
RefNAPtg refNAPtg = (RefNAPtg)ptg;
ptg = new RefAPtg( (short)(formulaRow + refNAPtg.getRow()),
(byte)(formulaColumn + refNAPtg.getColumn()),
ptg = new RefAPtg( fixupRelativeRow(formulaRow,refNAPtg.getRow(),refNAPtg.isRowRelative()),
fixupRelativeColumn(formulaColumn,refNAPtg.getColumn(),refNAPtg.isColRelative()),
refNAPtg.isRowRelative(),
refNAPtg.isColRelative());
} else if (ptg instanceof AreaNPtg) {
AreaNPtg areaNPtg = (AreaNPtg)ptg;
ptg = new AreaPtg((short)(formulaRow + areaNPtg.getFirstRow()),
(short)(formulaRow + areaNPtg.getLastRow()),
(short)(formulaColumn + areaNPtg.getFirstColumn()),
(short)(formulaColumn + areaNPtg.getLastColumn()),
ptg = new AreaPtg(fixupRelativeRow(formulaRow,areaNPtg.getFirstRow(),areaNPtg.isFirstRowRelative()),
fixupRelativeRow(formulaRow,areaNPtg.getLastRow(),areaNPtg.isLastRowRelative()),
fixupRelativeColumn(formulaColumn,areaNPtg.getFirstColumn(),areaNPtg.isFirstColRelative()),
fixupRelativeColumn(formulaColumn,areaNPtg.getLastColumn(),areaNPtg.isLastColRelative()),
areaNPtg.isFirstRowRelative(),
areaNPtg.isLastRowRelative(),
areaNPtg.isFirstColRelative(),
areaNPtg.isLastColRelative());
} else if (ptg instanceof AreaNVPtg) {
AreaNVPtg areaNVPtg = (AreaNVPtg)ptg;
ptg = new AreaVPtg((short)(formulaRow + areaNVPtg.getFirstRow()),
(short)(formulaRow + areaNVPtg.getLastRow()),
(short)(formulaColumn + areaNVPtg.getFirstColumn()),
(short)(formulaColumn + areaNVPtg.getLastColumn()),
ptg = new AreaVPtg(fixupRelativeRow(formulaRow,areaNVPtg.getFirstRow(),areaNVPtg.isFirstRowRelative()),
fixupRelativeRow(formulaRow,areaNVPtg.getLastRow(),areaNVPtg.isLastRowRelative()),
fixupRelativeColumn(formulaColumn,areaNVPtg.getFirstColumn(),areaNVPtg.isFirstColRelative()),
fixupRelativeColumn(formulaColumn,areaNVPtg.getLastColumn(),areaNVPtg.isLastColRelative()),
areaNVPtg.isFirstRowRelative(),
areaNVPtg.isLastRowRelative(),
areaNVPtg.isFirstColRelative(),
areaNVPtg.isLastColRelative());
} else if (ptg instanceof AreaNAPtg) {
AreaNAPtg areaNAPtg = (AreaNAPtg)ptg;
ptg = new AreaAPtg((short)(formulaRow + areaNAPtg.getFirstRow()),
(short)(formulaRow + areaNAPtg.getLastRow()),
(short)(formulaColumn + areaNAPtg.getFirstColumn()),
(short)(formulaColumn + areaNAPtg.getLastColumn()),
ptg = new AreaAPtg(fixupRelativeRow(formulaRow,areaNAPtg.getFirstRow(),areaNAPtg.isFirstRowRelative()),
fixupRelativeRow(formulaRow,areaNAPtg.getLastRow(),areaNAPtg.isLastRowRelative()),
fixupRelativeColumn(formulaColumn,areaNAPtg.getFirstColumn(),areaNAPtg.isFirstColRelative()),
fixupRelativeColumn(formulaColumn,areaNAPtg.getLastColumn(),areaNAPtg.isLastColRelative()),
areaNAPtg.isFirstRowRelative(),
areaNAPtg.isLastRowRelative(),
areaNAPtg.isFirstColRelative(),
@ -258,6 +256,21 @@ public class SharedFormulaRecord
throw new RuntimeException("Shared Formula Conversion: Coding Error");
}
}
private short fixupRelativeColumn(int currentcolumn, short column, boolean relative) {
if(relative) {
if((column&128)!=0) column=(short)(column-256);
column+=currentcolumn;
}
return column;
}
private short fixupRelativeRow(int currentrow, short row, boolean relative) {
if(relative) {
row+=currentrow;
}
return row;
}
/**
* Mirroring formula records so it is registered in the ValueRecordsAggregate

View File

@ -251,4 +251,21 @@ public class TextObjectRecord
buffer.append( "[/TXO]\n" );
return buffer.toString();
}
public Object clone() {
TextObjectRecord rec = new TextObjectRecord();
rec.str = str;
rec.setOptions(getOptions());
rec.setTextOrientation(getTextOrientation());
rec.setReserved4(getReserved4());
rec.setReserved5(getReserved5());
rec.setReserved6(getReserved6());
rec.setTextLength(getTextLength());
rec.setFormattingRunLength(getFormattingRunLength());
rec.setReserved7(getReserved7());
return rec;
}
}

View File

@ -0,0 +1,82 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record;
import org.apache.poi.util.LittleEndian;
/**
* Title: Uncalced Record
* <P>
* If this record occurs in the Worksheet Substream, it indicates that the formulas have not
* been recalculated before the document was saved.
*
* @author Olivier Leprince
*/
public class UncalcedRecord extends Record
{
public final static short sid = 0x5E;
/**
* Default constructor
*/
public UncalcedRecord() {
}
/**
* read constructor
*/
public UncalcedRecord(RecordInputStream in) {
super(in);
}
public short getSid() {
return sid;
}
protected void validateSid(short id) {
if (id != sid) {
throw new RecordFormatException("NOT AN UNCALCED RECORD");
}
}
protected void fillFields(RecordInputStream in) {
short unused = in.readShort();
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("[UNCALCED]\n");
buffer.append("[/UNCALCED]\n");
return buffer.toString();
}
public int serialize(int offset, byte[] data) {
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, (short) 2);
LittleEndian.putShort(data, 4 + offset, (short) 0); // unused
return getRecordSize();
}
public int getRecordSize() {
return UncalcedRecord.getStaticRecordSize();
}
public static int getStaticRecordSize() {
return 6;
}
}

View File

@ -99,7 +99,7 @@ public class FormulaRecordAggregate
{
this.formulaRecord = formulaRecord;
}
public FormulaRecord getFormulaRecord()
{
return formulaRecord;
@ -109,7 +109,7 @@ public class FormulaRecordAggregate
{
return stringRecord;
}
public boolean isEqual(CellValueRecordInterface i)
{
return formulaRecord.isEqual( i );

View File

@ -127,8 +127,17 @@ public class ValueRecordsAggregate
FormulaRecordAggregate lastFormulaAggregate = null;
// First up, locate all the shared formulas
List sharedFormulas = new java.util.ArrayList();
for (k = offset; k < records.size(); k++)
{
Record rec = ( Record ) records.get(k);
if (rec instanceof SharedFormulaRecord) {
sharedFormulas.add(rec);
}
}
// Now do the main processing sweep
for (k = offset; k < records.size(); k++)
{
Record rec = ( Record ) records.get(k);
@ -137,18 +146,14 @@ public class ValueRecordsAggregate
{
break;
} else if (rec instanceof SharedFormulaRecord) {
sharedFormulas.add(rec);
// Already handled, not to worry
} else if (rec instanceof FormulaRecord)
{
FormulaRecord formula = (FormulaRecord)rec;
if (formula.isSharedFormula()) {
Record nextRecord = (Record) records.get(k + 1);
if (nextRecord instanceof SharedFormulaRecord) {
sharedFormulas.add(nextRecord);
k++;
}
//traverse the list of shared formulas in reverse order, and try to find the correct one
//for us
// Traverse the list of shared formulas in
// reverse order, and try to find the correct one
// for us
boolean found = false;
for (int i=sharedFormulas.size()-1;i>=0;i--) {
SharedFormulaRecord shrd = (SharedFormulaRecord)sharedFormulas.get(i);

View File

@ -243,16 +243,22 @@ public class Area3DPtg extends Ptg
public void setArea( String ref )
{
AreaReference ar = new AreaReference( ref );
CellReference[] crs = ar.getCells();
CellReference firstCell = crs[0];
CellReference lastCell = firstCell;
if(crs.length > 1) {
lastCell = crs[1];
}
setFirstRow( (short) ar.getCells()[0].getRow() );
setFirstColumn( (short) ar.getCells()[0].getCol() );
setLastRow( (short) ar.getCells()[1].getRow() );
setLastColumn( (short) ar.getCells()[1].getCol() );
setFirstColRelative( !ar.getCells()[0].isColAbsolute() );
setLastColRelative( !ar.getCells()[1].isColAbsolute() );
setFirstRowRelative( !ar.getCells()[0].isRowAbsolute() );
setLastRowRelative( !ar.getCells()[1].isRowAbsolute() );
setFirstRow( (short) firstCell.getRow() );
setFirstColumn( (short) firstCell.getCol() );
setLastRow( (short) lastCell.getRow() );
setLastColumn( (short) lastCell.getCol() );
setFirstColRelative( !firstCell.isColAbsolute() );
setLastColRelative( !lastCell.isColAbsolute() );
setFirstRowRelative( !firstCell.isRowAbsolute() );
setLastRowRelative( !lastCell.isRowAbsolute() );
}
public String toFormulaString(Workbook book)

View File

@ -29,7 +29,7 @@ import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
public class ErrPtg extends Ptg
{
public static final short sid = 0x1c;
private static final int SIZE = 7;
private static final int SIZE = 2;
private byte field_1_error_code;
/** Creates new ErrPtg */

View File

@ -75,7 +75,7 @@ public class ExpPtg
public String toFormulaString(Workbook book)
{
throw new RecordFormatException("Coding Error: Expected ExpPtg to be converted from Shared to Non-Shared Formula");
throw new RecordFormatException("Coding Error: Expected ExpPtg to be converted from Shared to Non-Shared Formula by ValueRecordsAggregate, but it wasn't");
}
public String toString()

View File

@ -24,17 +24,33 @@
*/
package org.apache.poi.hssf.usermodel;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
import org.apache.poi.hssf.record.ExtendedFormatRecord;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NoteRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.ObjRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SubRecord;
import org.apache.poi.hssf.record.TextObjectRecord;
import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.Ptg;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* High level representation of a cell in a row of a spreadsheet.
* Cells can be numeric, formula-based or string-based (text). The cell type
@ -266,14 +282,24 @@ public class HSSFCell
}
/**
* set the cell's number within the row (0 based)
* Set the cell's number within the row (0 based).
* @param num short the cell number
* @deprecated Doesn't update the row's idea of what cell this is, use {@link HSSFRow#moveCell(HSSFCell, short)} instead
*/
public void setCellNum(short num)
{
record.setColumn(num);
}
/**
* Updates the cell record's idea of what
* column it belongs in (0 based)
* @param num the new cell number
*/
protected void updateCellNum(short num)
{
record.setColumn(num);
}
/**
* get the cell's number within the row
@ -508,7 +534,13 @@ public class HSSFCell
{
setCellType(CELL_TYPE_NUMERIC, false, row, col, styleIndex);
}
(( NumberRecord ) record).setValue(value);
// Save into the apropriate record
if(record instanceof FormulaRecordAggregate) {
(( FormulaRecordAggregate ) record).getFormulaRecord().setValue(value);
} else {
(( NumberRecord ) record).setValue(value);
}
}
/**

View File

@ -21,6 +21,13 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.ddf.EscherComplexProperty;
import org.apache.poi.ddf.EscherOptRecord;
import org.apache.poi.ddf.EscherProperty;
import org.apache.poi.hssf.record.EscherAggregate;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/**
* The patriarch is the toplevel container for shapes in a sheet. It does
* little other than act as a container for other shapes and groups.
@ -37,13 +44,21 @@ public class HSSFPatriarch
int x2 = 1023;
int y2 = 255;
/**
* The EscherAggregate we have been bound to.
* (This will handle writing us out into records,
* and building up our shapes from the records)
*/
private EscherAggregate boundAggregate;
/**
* Creates the patriarch.
*
* @param sheet the sheet this patriarch is stored in.
* @param sheet the sheet this patriarch is stored in.
*/
HSSFPatriarch(HSSFSheet sheet)
HSSFPatriarch(HSSFSheet sheet, EscherAggregate boundAggregate)
{
this.boundAggregate = boundAggregate;
this.sheet = sheet;
}
@ -173,6 +188,39 @@ public class HSSFPatriarch
this.x2 = x2;
this.y2 = y2;
}
/**
* Does this HSSFPatriarch contain a chart?
* (Technically a reference to a chart, since they
* get stored in a different block of records)
* FIXME - detect chart in all cases (only seems
* to work on some charts so far)
*/
public boolean containsChart() {
// TODO - support charts properly in usermodel
// We're looking for a EscherOptRecord
EscherOptRecord optRecord = (EscherOptRecord)
boundAggregate.findFirstWithId(EscherOptRecord.RECORD_ID);
if(optRecord == null) {
// No opt record, can't have chart
return false;
}
for(Iterator it = optRecord.getEscherProperties().iterator(); it.hasNext();) {
EscherProperty prop = (EscherProperty)it.next();
if(prop.getPropertyNumber() == 896 && prop.isComplex()) {
EscherComplexProperty cp = (EscherComplexProperty)prop;
String str = StringUtil.getFromUnicodeLE(cp.getComplexData());
System.err.println(str);
if(str.equals("Chart 1\0")) {
return true;
}
}
}
return false;
}
/**
* The top left x coordinate of this group.
@ -206,4 +254,10 @@ public class HSSFPatriarch
return y2;
}
/**
* Returns the aggregate escher record we're bound to
*/
protected EscherAggregate _getBoundAggregate() {
return boundAggregate;
}
}

View File

@ -22,15 +22,14 @@
*/
package org.apache.poi.hssf.usermodel;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.RowRecord;
import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* High level representation of a row of a spreadsheet.
*
@ -157,11 +156,15 @@ public class HSSFRow
* remove the HSSFCell from this row.
* @param cell to remove
*/
public void removeCell(HSSFCell cell)
{
CellValueRecordInterface cval = cell.getCellValueRecord();
sheet.removeValueRecord(getRowNum(), cval);
public void removeCell(HSSFCell cell) {
removeCell(cell, true);
}
private void removeCell(HSSFCell cell, boolean alsoRemoveRecords) {
if(alsoRemoveRecords) {
CellValueRecordInterface cval = cell.getCellValueRecord();
sheet.removeValueRecord(getRowNum(), cval);
}
short column=cell.getCellNum();
if(cell!=null && column<cells.length)
{
@ -223,11 +226,44 @@ public class HSSFRow
{
return rowNum;
}
/**
* Returns the rows outline level. Increased as you
* put it into more groups (outlines), reduced as
* you take it out of them.
* TODO - Should this really be public?
*/
protected int getOutlineLevel() {
return row.getOutlineLevel();
}
/**
* Moves the supplied cell to a new column, which
* must not already have a cell there!
* @param cell The cell to move
* @param newColumn The new column number (0 based)
*/
public void moveCell(HSSFCell cell, short newColumn) {
// Ensure the destination is free
if(cells.length > newColumn && cells[newColumn] != null) {
throw new IllegalArgumentException("Asked to move cell to column " + newColumn + " but there's already a cell there");
}
// Check it's one of ours
if(! cells[cell.getCellNum()].equals(cell)) {
throw new IllegalArgumentException("Asked to move a cell, but it didn't belong to our row");
}
// Move the cell to the new position
// (Don't remove the records though)
removeCell(cell, false);
cell.updateCellNum(newColumn);
addCell(cell);
}
/**
* used internally to add a cell.
*/
private void addCell(HSSFCell cell)
{
short column=cell.getCellNum();

View File

@ -121,7 +121,7 @@ public class HSSFShapeGroup
}
/**
* Sets the coordinate space of this group. All children are contrained
* Sets the coordinate space of this group. All children are constrained
* to these coordinates.
*/
public void setCoordinates( int x1, int y1, int x2, int y2 )
@ -177,5 +177,4 @@ public class HSSFShapeGroup
}
return count;
}
}
}

View File

@ -28,6 +28,7 @@ import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.ReferencePtg;
import org.apache.poi.hssf.util.HSSFCellRangeAddress;
import org.apache.poi.hssf.util.HSSFDataValidation;
import org.apache.poi.hssf.util.Region;
@ -593,6 +594,26 @@ public class HSSFSheet
region.getColumnTo());
}
/**
* Whether a record must be inserted or not at generation to indicate that
* formula must be recalculated when workbook is opened.
* @param value true if an uncalced record must be inserted or not at generation
*/
public void setForceFormulaRecalculation(boolean value)
{
sheet.setUncalced(value);
}
/**
* Whether a record must be inserted or not at generation to indicate that
* formula must be recalculated when workbook is opened.
* @return true if an uncalced record must be inserted or not at generation
*/
public boolean getForceFormulaRecalculation()
{
return sheet.getUncalced();
}
/**
* determines whether the output is vertically centered on the page.
* @param value true to vertically center, false otherwise.
@ -1202,10 +1223,66 @@ public class HSSFSheet
row2Replace.createCellFromRecord( cellRecord );
sheet.addValueRecord( rowNum + n, cellRecord );
}
// move comments if exist (can exist even if cell is null)
HSSFComment comment = getCellComment(rowNum, col);
if (comment != null) {
comment.setRow(rowNum + n);
}
}
}
if ( endRow == lastrow || endRow + n > lastrow ) lastrow = Math.min( endRow + n, 65535 );
if ( startRow == firstrow || startRow + n < firstrow ) firstrow = Math.max( startRow + n, 0 );
// Update any formulas on this sheet that point to
// rows which have been moved
updateFormulasAfterShift(startRow, endRow, n);
}
/**
* Called by shiftRows to update formulas on this sheet
* to point to the new location of moved rows
*/
private void updateFormulasAfterShift(int startRow, int endRow, int n) {
// Need to look at every cell on the sheet
// Not just those that were moved
Iterator ri = rowIterator();
while(ri.hasNext()) {
HSSFRow r = (HSSFRow)ri.next();
Iterator ci = r.cellIterator();
while(ci.hasNext()) {
HSSFCell c = (HSSFCell)ci.next();
if(c.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
// Since it's a formula cell, process the
// formula string, and look to see if
// it contains any references
FormulaParser fp = new FormulaParser(c.getCellFormula(), workbook.getWorkbook());
fp.parse();
// Look for references, and update if needed
Ptg[] ptgs = fp.getRPNPtg();
boolean changed = false;
for(int i=0; i<ptgs.length; i++) {
if(ptgs[i] instanceof ReferencePtg) {
ReferencePtg rptg = (ReferencePtg)ptgs[i];
if(startRow <= rptg.getRowAsInt() &&
rptg.getRowAsInt() <= endRow) {
// References a row that moved
rptg.setRow(rptg.getRowAsInt() + n);
changed = true;
}
}
}
// If any references were changed, then
// re-create the formula string
if(changed) {
c.setCellFormula(
fp.toFormulaString(ptgs)
);
}
}
}
}
}
protected void insertChartRecords( List records )
@ -1431,7 +1508,7 @@ public class HSSFSheet
*/
public void dumpDrawingRecords(boolean fat)
{
sheet.aggregateDrawingRecords(book.getDrawingManager());
sheet.aggregateDrawingRecords(book.getDrawingManager(), false);
EscherAggregate r = (EscherAggregate) getSheet().findFirstRecordBySid(EscherAggregate.sid);
List escherRecords = r.getEscherRecords();
@ -1448,9 +1525,10 @@ public class HSSFSheet
}
/**
* Creates the toplevel drawing patriarch. This will have the effect of
* removing any existing drawings on this sheet.
*
* Creates the top-level drawing patriarch. This will have
* the effect of removing any existing drawings on this
* sheet.
* This may then be used to add graphics or charts
* @return The new patriarch.
*/
public HSSFPatriarch createDrawingPatriarch()
@ -1458,14 +1536,57 @@ public class HSSFSheet
// Create the drawing group if it doesn't already exist.
book.createDrawingGroup();
sheet.aggregateDrawingRecords(book.getDrawingManager());
sheet.aggregateDrawingRecords(book.getDrawingManager(), true);
EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
HSSFPatriarch patriarch = new HSSFPatriarch(this);
HSSFPatriarch patriarch = new HSSFPatriarch(this, agg);
agg.clear(); // Initially the behaviour will be to clear out any existing shapes in the sheet when
// creating a new patriarch.
agg.setPatriarch(patriarch);
return patriarch;
}
/**
* Returns the top-level drawing patriach, if there is
* one.
* This will hold any graphics or charts for the sheet.
* WARNING - calling this will trigger a parsing of the
* associated escher records. Any that aren't supported
* (such as charts and complex drawing types) will almost
* certainly be lost or corrupted when written out. Only
* use this with simple drawings, otherwise call
* {@link HSSFSheet#createDrawingPatriarch()} and
* start from scratch!
*/
public HSSFPatriarch getDrawingPatriarch() {
book.findDrawingGroup();
// If there's now no drawing manager, then there's
// no drawing escher records on the workbook
if(book.getDrawingManager() == null) {
return null;
}
int found = sheet.aggregateDrawingRecords(
book.getDrawingManager(), false
);
if(found == -1) {
// Workbook has drawing stuff, but this sheet doesn't
return null;
}
// Grab our aggregate record, and wire it up
EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
HSSFPatriarch patriarch = new HSSFPatriarch(this, agg);
agg.setPatriarch(patriarch);
// Have it process the records into high level objects
// as best it can do (this step may eat anything
// that isn't supported, you were warned...)
agg.convertRecordsToUserModel();
// Return what we could cope with
return patriarch;
}
/**
* Expands or collapses a column group.
@ -1558,7 +1679,13 @@ public class HSSFSheet
for (Iterator it = rowIterator(); it.hasNext();) {
HSSFRow row = (HSSFRow) it.next();
HSSFCell cell = row.getCell(column);
if (cell == null) continue;
boolean isCellInMergedRegion = false;
for (int i = 0 ; i < getNumMergedRegions() && ! isCellInMergedRegion; i++) {
isCellInMergedRegion = getMergedRegionAt(i).contains(row.getRowNum(), column);
}
if (cell == null | isCellInMergedRegion) continue;
HSSFCellStyle style = cell.getCellStyle();
HSSFFont font = wb.getFontAt(style.getFontIndex());
@ -1621,27 +1748,28 @@ public class HSSFSheet
} else if (cell.getCellType() == HSSFCell.CELL_TYPE_BOOLEAN) {
sval = String.valueOf(cell.getBooleanCellValue());
}
if(sval != null) {
String txt = sval + defaultChar;
str = new AttributedString(txt);
copyAttributes(font, str, 0, txt.length());
String txt = sval + defaultChar;
str = new AttributedString(txt);
copyAttributes(font, str, 0, txt.length());
layout = new TextLayout(str.getIterator(), frc);
if(style.getRotation() != 0){
/*
* Transform the text using a scale so that it's height is increased by a multiple of the leading,
* and then rotate the text before computing the bounds. The scale results in some whitespace around
* the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but
* is added by the standard Excel autosize.
*/
AffineTransform trans = new AffineTransform();
trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0));
trans.concatenate(
AffineTransform.getScaleInstance(1, fontHeightMultiple)
);
width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth);
} else {
width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth);
layout = new TextLayout(str.getIterator(), frc);
if(style.getRotation() != 0){
/*
* Transform the text using a scale so that it's height is increased by a multiple of the leading,
* and then rotate the text before computing the bounds. The scale results in some whitespace around
* the unrotated top and bottom of the text that normally wouldn't be present if unscaled, but
* is added by the standard Excel autosize.
*/
AffineTransform trans = new AffineTransform();
trans.concatenate(AffineTransform.getRotateInstance(style.getRotation()*2.0*Math.PI/360.0));
trans.concatenate(
AffineTransform.getScaleInstance(1, fontHeightMultiple)
);
width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth);
} else {
width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth);
}
}
}
@ -1671,7 +1799,21 @@ public class HSSFSheet
* @return cell comment or <code>null</code> if not found
*/
public HSSFComment getCellComment(int row, int column){
return HSSFCell.findCellComment(sheet, row, column);
// Don't call findCellComment directly, otherwise
// two calls to this method will result in two
// new HSSFComment instances, which is bad
HSSFRow r = getRow(row);
if(r != null) {
HSSFCell c = r.getCell((short)column);
if(c != null) {
return c.getCellComment();
} else {
// No cell, so you will get new
// objects every time, sorry...
return HSSFCell.findCellComment(sheet, row, column);
}
}
return null;
}
}

View File

@ -445,6 +445,35 @@ public class HSSFWorkbook extends POIDocument
return workbook.getSheetName(sheet);
}
/**
* check whether a sheet is hidden
* @param sheet Number
* @return True if sheet is hidden
*/
public boolean isSheetHidden(int sheet) {
if (sheet > (sheets.size() - 1))
{
throw new RuntimeException("Sheet out of bounds");
}
return workbook.isSheetHidden(sheet);
}
/**
* Hide or unhide a sheet
*
* @param sheetnum The sheet number
* @param hidden True to mark the sheet as hidden, false otherwise
*/
public void setSheetHidden(int sheet, boolean hidden) {
if (sheet > (sheets.size() - 1))
{
throw new RuntimeException("Sheet out of bounds");
}
workbook.setSheetHidden(sheet,hidden);
}
/*
* get the sheet's index
* @param name sheet name

View File

@ -18,15 +18,24 @@
package org.apache.poi.hssf.util;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class AreaReference {
private CellReference [] cells;
private int dim;
/** Create an area ref from a string representation
/**
* Create an area ref from a string representation.
* The area reference must be contiguous
*/
public AreaReference(String reference) {
if(! isContiguous(reference)) {
throw new IllegalArgumentException("References passed to the AreaReference must be contiguous, use generateContiguous(ref) if you have non-contiguous references");
}
String[] refs = seperateAreaRefs(reference);
dim = refs.length;
cells = new CellReference[dim];
@ -34,16 +43,73 @@ private int dim;
cells[i]=new CellReference(refs[i]);
}
}
/**
* Is the reference for a contiguous (i.e.
* unbroken) area, or is it made up of
* several different parts?
* (If it is, you will need to call
* ....
*/
public static boolean isContiguous(String reference) {
if(reference.indexOf(',') == -1) {
return true;
}
return false;
}
/**
* Takes a non-contiguous area reference, and
* returns an array of contiguous area references.
*/
public static AreaReference[] generateContiguous(String reference) {
ArrayList refs = new ArrayList();
StringTokenizer st = new StringTokenizer(reference, ",");
while(st.hasMoreTokens()) {
refs.add(
new AreaReference(st.nextToken())
);
}
return (AreaReference[])refs.toArray(new AreaReference[refs.size()]);
}
//not sure if we need to be flexible here!
/** return the dimensions of this area
**/
public int getDim() {
return dim;
}
/** return the cell references that define this area */
/**
* Return the cell references that define this area
* (i.e. the two corners)
*/
public CellReference[] getCells() {
return cells;
}
/**
* Returns a reference to every cell covered by this area
*/
public CellReference[] getAllReferencedCells() {
// Special case for single cell reference
if(cells.length == 1) {
return cells;
}
// Interpolate between the two
int minRow = Math.min(cells[0].getRow(), cells[1].getRow());
int maxRow = Math.max(cells[0].getRow(), cells[1].getRow());
int minCol = Math.min(cells[0].getCol(), cells[1].getCol());
int maxCol = Math.max(cells[0].getCol(), cells[1].getCol());
ArrayList refs = new ArrayList();
for(int row=minRow; row<=maxRow; row++) {
for(int col=minCol; col<=maxCol; col++) {
CellReference ref = new CellReference(row, col, cells[0].isRowAbsolute(), cells[0].isColAbsolute());
ref.setSheetName(cells[0].getSheetName());
refs.add(ref);
}
}
return (CellReference[])refs.toArray(new CellReference[refs.size()]);
}
public String toString() {
StringBuffer retval = new StringBuffer();

View File

@ -69,6 +69,10 @@ public class CellReference {
public boolean isRowAbsolute(){return rowAbs;}
public boolean isColAbsolute(){return colAbs;}
public String getSheetName(){return sheetName;}
protected void setSheetName(String sheetName) {
this.sheetName = sheetName;
}
/**
* takes in a column reference portion of a CellRef and converts it from

View File

@ -31,4 +31,7 @@ public interface POIFSConstants
public static final int END_OF_CHAIN = -2;
public static final int PROPERTY_SIZE = 0x0080;
public static final int UNUSED_BLOCK = -1;
public static final byte[] OOXML_FILE_HEADER =
new byte[] { 0x50, 0x4b, 0x03, 0x04 };
} // end public interface POIFSConstants;

View File

@ -19,14 +19,19 @@
package org.apache.poi.poifs.filesystem;
import java.io.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.dev.POIFSViewable;
import org.apache.poi.poifs.property.DirectoryProperty;
import org.apache.poi.poifs.property.DocumentProperty;
import org.apache.poi.poifs.property.Property;
import org.apache.poi.poifs.property.PropertyTable;
import org.apache.poi.poifs.storage.BATBlock;
@ -34,13 +39,14 @@ import org.apache.poi.poifs.storage.BlockAllocationTableReader;
import org.apache.poi.poifs.storage.BlockAllocationTableWriter;
import org.apache.poi.poifs.storage.BlockList;
import org.apache.poi.poifs.storage.BlockWritable;
import org.apache.poi.poifs.storage.HeaderBlockConstants;
import org.apache.poi.poifs.storage.HeaderBlockReader;
import org.apache.poi.poifs.storage.HeaderBlockWriter;
import org.apache.poi.poifs.storage.RawDataBlock;
import org.apache.poi.poifs.storage.RawDataBlockList;
import org.apache.poi.poifs.storage.SmallBlockTableReader;
import org.apache.poi.poifs.storage.SmallBlockTableWriter;
import org.apache.poi.poifs.storage.SmallDocumentBlock;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LongField;
/**
* This is the main class of the POIFS system; it manages the entire
@ -106,6 +112,35 @@ public class POIFSFileSystem
.getSBATStart()), data_blocks, properties.getRoot()
.getChildren(), null);
}
/**
* Checks that the supplied InputStream (which MUST
* support mark and reset, or be a PushbackInputStream)
* has a POIFS (OLE2) header at the start of it.
* If your InputStream does not support mark / reset,
* then wrap it in a PushBackInputStream, then be
* sure to always use that, and not the original!
* @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream
*/
public static boolean hasPOIFSHeader(InputStream inp) throws IOException {
// We want to peek at the first 8 bytes
inp.mark(8);
byte[] header = new byte[8];
IOUtils.readFully(inp, header);
LongField signature = new LongField(HeaderBlockConstants._signature_offset, header);
// Wind back those 8 bytes
if(inp instanceof PushbackInputStream) {
PushbackInputStream pin = (PushbackInputStream)inp;
pin.unread(header);
} else {
inp.reset();
}
// Did it match the signature?
return (signature.get() == HeaderBlockConstants._signature);
}
/**
* Create a new document to be added to the root directory

View File

@ -91,8 +91,11 @@ public class HeaderBlockReader
if (signature.get() != _signature)
{
// Is it one of the usual suspects?
if(_data[0] == 0x50 && _data[1] == 0x4b && _data[2] == 0x03 &&
_data[3] == 0x04) {
byte[] OOXML_FILE_HEADER = POIFSConstants.OOXML_FILE_HEADER;
if(_data[0] == OOXML_FILE_HEADER[0] &&
_data[1] == OOXML_FILE_HEADER[1] &&
_data[2] == OOXML_FILE_HEADER[2] &&
_data[3] == OOXML_FILE_HEADER[3]) {
throw new OfficeXmlFileException("The supplied data appears to be in the Office 2007+ XML. POI only supports OLE2 Office documents");
}

View File

@ -42,34 +42,44 @@ public class RawDataBlock
* @param stream the InputStream from which the data will be read
*
* @exception IOException on I/O errors, and if an insufficient
* amount of data is read
* amount of data is read (the InputStream must
* be an exact multiple of the block size)
*/
public RawDataBlock(final InputStream stream)
throws IOException
{
_data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
throws IOException {
this(stream, POIFSConstants.BIG_BLOCK_SIZE);
}
/**
* Constructor RawDataBlock
*
* @param stream the InputStream from which the data will be read
* @param blockSize the size of the POIFS blocks, normally 512 bytes {@link POIFSConstants#BIG_BLOCK_SIZE}
*
* @exception IOException on I/O errors, and if an insufficient
* amount of data is read (the InputStream must
* be an exact multiple of the block size)
*/
public RawDataBlock(final InputStream stream, int blockSize)
throws IOException {
_data = new byte[ blockSize ];
int count = IOUtils.readFully(stream, _data);
if (count == -1)
{
if (count == -1) {
_eof = true;
}
else if (count != POIFSConstants.BIG_BLOCK_SIZE)
{
if (count == -1)
//Cant have -1 bytes read in the error message!
count = 0;
else if (count != blockSize) {
// IOUtils.readFully will always read the
// requested number of bytes, unless it hits
// an EOF
_eof = true;
String type = " byte" + ((count == 1) ? ("")
: ("s"));
throw new IOException("Unable to read entire block; " + count
+ type + " read; expected "
+ POIFSConstants.BIG_BLOCK_SIZE + " bytes");
+ type + " read before EOF; expected "
+ blockSize + " bytes");
}
else
{
else {
_eof = false;
}
}
@ -82,7 +92,6 @@ public class RawDataBlock
*
* @exception IOException
*/
public boolean eof()
throws IOException
{
@ -98,7 +107,6 @@ public class RawDataBlock
*
* @exception IOException if there is no data
*/
public byte [] getData()
throws IOException
{

View File

@ -58,11 +58,16 @@ public class IOUtils
}
/**
* Same as the normal <tt>in.read(b, off, len)</tt>, but tries to ensure that
* the entire len number of bytes is read.
* Same as the normal <tt>in.read(b, off, len)</tt>, but
* tries to ensure that the entire len number of bytes
* is read.
* <p>
* If the end of file is reached before any bytes are read, returns -1.
* Otherwise, returns the number of bytes read.
* If the end of file is reached before any bytes
* are read, returns -1.
* If the end of the file is reached after some bytes are
* read, returns the number of bytes read.
* If the end of the file isn't reached before len
* bytes have been read, will return len bytes.
*/
public static int readFully(InputStream in, byte[] b, int off, int len)
throws IOException
@ -79,5 +84,4 @@ public class IOUtils
}
}
}
}
}

View File

@ -41,7 +41,9 @@ public class PowerPointExtractor extends POITextExtractor
private HSLFSlideShow _hslfshow;
private SlideShow _show;
private Slide[] _slides;
private Notes[] _notes;
private boolean slidesByDefault = true;
private boolean notesByDefault = false;
/**
* Basic extractor. Returns all the text, and optionally all the notes
@ -99,7 +101,6 @@ public class PowerPointExtractor extends POITextExtractor
_hslfshow = ss;
_show = new SlideShow(_hslfshow);
_slides = _show.getSlides();
_notes = _show.getNotes();
}
/**
@ -110,23 +111,39 @@ public class PowerPointExtractor extends POITextExtractor
_hslfshow = null;
_show = null;
_slides = null;
_notes = null;
}
/**
* Should a call to getText() return slide text?
* Default is yes
*/
public void setSlidesByDefault(boolean slidesByDefault) {
this.slidesByDefault = slidesByDefault;
}
/**
* Should a call to getText() return notes text?
* Default is no
*/
public void setNotesByDefault(boolean notesByDefault) {
this.notesByDefault = notesByDefault;
}
/**
* Fetches all the slide text from the slideshow, but not the notes
*/
public String getText() {
return getText(true,false);
}
/**
* Fetches all the slide text from the slideshow,
* but not the notes, unless you've called
* setSlidesByDefault() and setNotesByDefault()
* to change this
*/
public String getText() {
return getText(slidesByDefault,notesByDefault);
}
/**
* Fetches all the notes text from the slideshow, but not the slide text
*/
public String getNotes() {
return getText(false,true);
}
/**
* Fetches all the notes text from the slideshow, but not the slide text
*/
public String getNotes() {
return getText(false,true);
}
/**
* Fetches text from the slideshow, be it slide text or note text.
@ -154,7 +171,7 @@ public class PowerPointExtractor extends POITextExtractor
}
}
if(getNoteText) {
ret.append(" ");
ret.append("\n");
}
}

View File

@ -24,7 +24,12 @@ import java.util.LinkedList;
import java.util.Vector;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.RecordContainer;
import org.apache.poi.hslf.record.StyleTextPropAtom;
import org.apache.poi.hslf.record.TextBytesAtom;
import org.apache.poi.hslf.record.TextCharsAtom;
import org.apache.poi.hslf.record.TextHeaderAtom;
import org.apache.poi.hslf.usermodel.RichTextRun;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.util.StringUtil;
@ -252,9 +257,70 @@ public class TextRun
// Update methods follow
/**
* Adds the supplied text onto the end of the TextRun,
* creating a new RichTextRun (returned) for it to
* sit in.
* In many cases, before calling this, you'll want to add
* a newline onto the end of your last RichTextRun
*/
public RichTextRun appendText(String s) {
// We will need a StyleTextProp atom
ensureStyleAtomPresent();
// First up, append the text to the
// underlying text atom
int oldSize = getRawText().length();
storeText(
getRawText() + s
);
// If either of the previous styles overran
// the text by one, we need to shuffle that
// extra character onto the new ones
int pOverRun = _styleAtom.getParagraphTextLengthCovered() - oldSize;
int cOverRun = _styleAtom.getCharacterTextLengthCovered() - oldSize;
if(pOverRun > 0) {
TextPropCollection tpc = (TextPropCollection)
_styleAtom.getParagraphStyles().getLast();
tpc.updateTextSize(
tpc.getCharactersCovered() - pOverRun
);
}
if(cOverRun > 0) {
TextPropCollection tpc = (TextPropCollection)
_styleAtom.getCharacterStyles().getLast();
tpc.updateTextSize(
tpc.getCharactersCovered() - cOverRun
);
}
// Next, add the styles for its paragraph and characters
TextPropCollection newPTP =
_styleAtom.addParagraphTextPropCollection(s.length()+pOverRun);
TextPropCollection newCTP =
_styleAtom.addCharacterTextPropCollection(s.length()+cOverRun);
// Now, create the new RichTextRun
RichTextRun nr = new RichTextRun(
this, oldSize, s.length(),
newPTP, newCTP, false, false
);
// Add the new RichTextRun onto our list
RichTextRun[] newRuns = new RichTextRun[_rtRuns.length+1];
System.arraycopy(_rtRuns, 0, newRuns, 0, _rtRuns.length);
newRuns[newRuns.length-1] = nr;
_rtRuns = newRuns;
// And return the new run to the caller
return nr;
}
/**
* Saves the given string to the records. Doesn't touch the stylings.
* Saves the given string to the records. Doesn't
* touch the stylings.
*/
private void storeText(String s) {
// Remove a single trailing \n, as there is an implicit one at the

View File

@ -19,17 +19,19 @@
package org.apache.poi.hslf.record;
import org.apache.poi.hslf.model.textproperties.*;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.util.LinkedList;
import java.util.Vector;
import java.util.List;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.poi.hslf.model.textproperties.AlignmentTextProp;
import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
import org.apache.poi.hslf.model.textproperties.ParagraphFlagsTextProp;
import org.apache.poi.hslf.model.textproperties.TextProp;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogger;
/**
* A StyleTextPropAtom (type 4001). Holds basic character properties
@ -88,6 +90,37 @@ public class StyleTextPropAtom extends RecordAtom
* character stylings
*/
public void setCharacterStyles(LinkedList cs) { charStyles = cs; }
/**
* Returns how many characters the paragraph's
* TextPropCollections cover.
* (May be one or two more than the underlying text does,
* due to having extra characters meaning something
* special to powerpoint)
*/
public int getParagraphTextLengthCovered() {
return getCharactersCovered(paragraphStyles);
}
/**
* Returns how many characters the character's
* TextPropCollections cover.
* (May be one or two more than the underlying text does,
* due to having extra characters meaning something
* special to powerpoint)
*/
public int getCharacterTextLengthCovered() {
return getCharactersCovered(charStyles);
}
private int getCharactersCovered(LinkedList styles) {
int length = 0;
Iterator it = styles.iterator();
while(it.hasNext()) {
TextPropCollection tpc =
(TextPropCollection)it.next();
length += tpc.getCharactersCovered();
}
return length;
}
/** All the different kinds of paragraph properties we might handle */
public static TextProp[] paragraphTextPropTypes = new TextProp[] {
@ -355,8 +388,7 @@ public class StyleTextPropAtom extends RecordAtom
charStyles.add(tpc);
return tpc;
}
/* ************************************************************************ */

View File

@ -20,25 +20,28 @@
package org.apache.poi.hslf.usermodel;
import org.apache.poi.hslf.model.*;
import org.apache.poi.hslf.model.Shape;
import org.apache.poi.hslf.model.textproperties.*;
import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.exceptions.HSLFException;
import java.awt.Color;
import java.awt.*;
import org.apache.poi.hslf.model.MasterSheet;
import org.apache.poi.hslf.model.Shape;
import org.apache.poi.hslf.model.Sheet;
import org.apache.poi.hslf.model.TextRun;
import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
import org.apache.poi.hslf.model.textproperties.ParagraphFlagsTextProp;
import org.apache.poi.hslf.model.textproperties.TextProp;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.record.ColorSchemeAtom;
/**
* Represents a run of text, all with the same style
*
* TODO: get access to the font/character properties
*
* @author Nick Burch
* TODO: finish all the getters and setters to the
* font/character/paragraph properties (currently only
* has some of them)
*/
public class RichTextRun
{
public class RichTextRun {
/** The TextRun we belong to */
private TextRun parentRun;
/** The SlideShow we belong to */

View File

@ -217,14 +217,66 @@ public class HSSFFormulaEvaluator {
/**
* If cell contains formula, it evaluates the formula, and puts the
* formula result back into the cell.
* Else if cell does not contain formula, this method leaves the cell
* unchanged. Note that the same instance of HSSFCell is returned to
* If cell contains formula, it evaluates the formula,
* and saves the result of the formula. The cell
* remains as a formula cell.
* Else if cell does not contain formula, this method leaves
* the cell unchanged.
* Note that the type of the formula result is returned,
* so you know what kind of value is also stored with
* the formula.
* <pre>
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre>
* Be aware that your cell will hold both the formula,
* and the result. If you want the cell replaced with
* the result of the formula, use {@link #evaluateInCell(HSSFCell)}
* @param cell The cell to evaluate
* @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however)
*/
public int evaluateFormulaCell(HSSFCell cell) {
if (cell != null) {
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_FORMULA:
CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook));
switch (cv.getCellType()) {
case HSSFCell.CELL_TYPE_BOOLEAN:
cell.setCellValue(cv.getBooleanValue());
break;
case HSSFCell.CELL_TYPE_ERROR:
cell.setCellValue(cv.getErrorValue());
break;
case HSSFCell.CELL_TYPE_NUMERIC:
cell.setCellValue(cv.getNumberValue());
break;
case HSSFCell.CELL_TYPE_STRING:
cell.setCellValue(cv.getRichTextStringValue());
break;
case HSSFCell.CELL_TYPE_BLANK:
break;
case HSSFCell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
break;
}
return cv.getCellType();
}
}
return -1;
}
/**
* If cell contains formula, it evaluates the formula, and
* puts the formula result back into the cell, in place
* of the old formula.
* Else if cell does not contain formula, this method leaves
* the cell unchanged.
* Note that the same instance of HSSFCell is returned to
* allow chained calls like:
* <pre>
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
* </pre>
* Be aware that your cell value will be changed to hold the
* result of the formula. If you simply want the formula
* value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}
* @param cell
*/
public HSSFCell evaluateInCell(HSSFCell cell) {

View File

@ -58,7 +58,7 @@ public class TableRow
p = getParagraph(end);
s = p.text();
}
_cells[cellIndex] = new TableCell(start, end, this, levelNum,
_cells[cellIndex] = new TableCell(start, end+1, this, levelNum,
_tprops.getRgtc()[cellIndex],
_tprops.getRgdxaCenter()[cellIndex],
_tprops.getRgdxaCenter()[cellIndex+1]-_tprops.getRgdxaCenter()[cellIndex]);

View File

@ -0,0 +1,26 @@
Author = Nick Burch
Title = SlideShow Sample
Subject = A sample slideshow
Keywords = Sample Testing
Comments = This is a sample slideshow, for use with testing etc
(Slide 1)
Title of the first slide
Subtitle of the first slide
This bit is in italic green
(Notes 1)
I am the notes of the first slide
(Slide 2)
This is the second slide
* It has bullet points on it
* Theyre fun, arent they?
* Especially in a different font like Arial Black at 16 point!
(Notes 2)
These are the notes of the 2nd slide
THIS LINE IS BOLD

View File

@ -72,6 +72,29 @@ public class TextExtractor extends TestCase {
ensureTwoStringsTheSame(expectText, notesText);
}
public void testReadBoth() throws Exception {
String[] slText = new String[] {
"This is a test title\nThis is a test subtitle\nThis is on page 1\n",
"This is the title on page 2\nThis is page two\nIt has several blocks of text\nNone of them have formatting\n"
};
String[] ntText = new String[] {
"These are the notes for page 1\n",
"These are the notes on page two, again lacking formatting\n"
};
ppe.setSlidesByDefault(true);
ppe.setNotesByDefault(false);
assertEquals(slText[0]+slText[1], ppe.getText());
ppe.setSlidesByDefault(false);
ppe.setNotesByDefault(true);
assertEquals(ntText[0]+ntText[1], ppe.getText());
ppe.setSlidesByDefault(true);
ppe.setNotesByDefault(true);
assertEquals(slText[0]+slText[1]+"\n"+ntText[0]+ntText[1], ppe.getText());
}
/**
* Test that when presented with a PPT file missing the odd

View File

@ -16,16 +16,22 @@
*/
package org.apache.poi.hslf.usermodel;
import java.io.*;
import java.awt.*;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.model.*;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.SlideListWithText;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import junit.framework.TestCase;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.model.Slide;
import org.apache.poi.hslf.model.SlideMaster;
import org.apache.poi.hslf.model.TextBox;
import org.apache.poi.hslf.model.TextRun;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.SlideListWithText;
/**
* Test that the friendly getters and setters on RichTextRun
* behave as expected.
@ -549,4 +555,75 @@ if(false) {
assertEquals(0, rt.getBulletOffset());
assertEquals('\u263A', rt.getBulletChar());
}
public void testAddText() throws Exception {
FileInputStream is = new FileInputStream(new File(System.getProperty("HSLF.testdata.path"), "bullets.ppt"));
SlideShow ppt = new SlideShow(is);
is.close();
assertTrue("No Exceptions while reading file", true);
RichTextRun rt;
TextRun[] txt;
Slide[] slides = ppt.getSlides();
assertEquals(2, slides.length);
txt = slides[0].getTextRuns();
assertEquals(2, txt.length);
assertEquals("Title text", txt[0].getRawText());
assertEquals(1, txt[0].getRichTextRuns().length);
rt = txt[0].getRichTextRuns()[0];
assertFalse(rt.isBullet());
// Add some new text
txt[0].appendText("Foo! I'm new!");
assertEquals(2, txt[0].getRichTextRuns().length);
rt = txt[0].getRichTextRuns()[0];
assertFalse(rt.isBold());
assertEquals("Title text", rt.getText());
rt = txt[0].getRichTextRuns()[1];
assertFalse(rt.isBold());
assertEquals("Foo! I'm new!", rt.getText());
rt.setBold(true);
// And some more
txt[0].appendText("Me too!");
assertEquals(3, txt[0].getRichTextRuns().length);
rt = txt[0].getRichTextRuns()[0];
assertFalse(rt.isBold());
assertEquals("Title text", rt.getText());
rt = txt[0].getRichTextRuns()[1];
assertTrue(rt.isBold());
assertEquals("Foo! I'm new!", rt.getText());
rt = txt[0].getRichTextRuns()[2];
assertFalse(rt.isBold());
assertEquals("Me too!", rt.getText());
// Save and re-open
ByteArrayOutputStream out = new ByteArrayOutputStream();
ppt.write(out);
out.close();
ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));
slides = ppt.getSlides();
assertEquals(2, slides.length);
txt = slides[0].getTextRuns();
assertEquals(2, txt.length);
assertEquals(3, txt[0].getRichTextRuns().length);
rt = txt[0].getRichTextRuns()[0];
assertFalse(rt.isBold());
assertEquals("Title text", rt.getText());
rt = txt[0].getRichTextRuns()[1];
assertTrue(rt.isBold());
assertEquals("Foo! I'm new!", rt.getText());
rt = txt[0].getRichTextRuns()[2];
assertFalse(rt.isBold());
assertEquals("Me too!", rt.getText());
// FileOutputStream fout = new FileOutputStream("/tmp/foo.ppt");
// ppt.write(fout);
}
}

View File

@ -0,0 +1,83 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.model;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Test the low level formula parser functionality,
* but using parts which need to use the
* HSSFFormulaEvaluator, which is in scratchpad
*/
public class TestFormulaParserSP extends TestCase {
public TestFormulaParserSP(String name) {
super(name);
}
public void testWithNamedRange() throws Exception {
HSSFWorkbook workbook = new HSSFWorkbook();
FormulaParser fp;
Ptg[] ptgs;
HSSFSheet s = workbook.createSheet("Foo");
s.createRow(0).createCell((short)0).setCellValue(1.1);
s.createRow(1).createCell((short)0).setCellValue(2.3);
s.createRow(2).createCell((short)2).setCellValue(3.1);
HSSFName name = workbook.createName();
name.setNameName("testName");
name.setReference("A1:A2");
fp = HSSFFormulaEvaluator.getUnderlyingParser(workbook, "SUM(testName)");
fp.parse();
ptgs = fp.getRPNPtg();
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
assertEquals(NamePtg.class, ptgs[0].getClass());
assertEquals(FuncVarPtg.class, ptgs[1].getClass());
// Now make it a single cell
name.setReference("C3");
fp = HSSFFormulaEvaluator.getUnderlyingParser(workbook, "SUM(testName)");
fp.parse();
ptgs = fp.getRPNPtg();
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
assertEquals(NamePtg.class, ptgs[0].getClass());
assertEquals(FuncVarPtg.class, ptgs[1].getClass());
// And make it non-contiguous
name.setReference("A1:A2,C3");
fp = HSSFFormulaEvaluator.getUnderlyingParser(workbook, "SUM(testName)");
fp.parse();
ptgs = fp.getRPNPtg();
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
assertEquals(NamePtg.class, ptgs[0].getClass());
assertEquals(FuncVarPtg.class, ptgs[1].getClass());
}
}

View File

@ -0,0 +1,93 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.usermodel;
import java.io.File;
import java.io.FileInputStream;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.ExpPtg;
import org.apache.poi.hssf.util.CellReference;
import junit.framework.TestCase;
public class TestBug42464 extends TestCase {
String dirname;
protected void setUp() throws Exception {
super.setUp();
dirname = System.getProperty("HSSF.testdata.path");
}
public void testOKFile() throws Exception {
HSSFWorkbook wb = new HSSFWorkbook(
new FileInputStream(new File(dirname,"42464-ExpPtg-ok.xls"))
);
process(wb);
}
public void testExpSharedBadFile() throws Exception {
HSSFWorkbook wb = new HSSFWorkbook(
new FileInputStream(new File(dirname,"42464-ExpPtg-bad.xls"))
);
process(wb);
}
protected void process(HSSFWorkbook wb) {
for(int i=0; i<wb.getNumberOfSheets(); i++) {
HSSFSheet s = wb.getSheetAt(i);
HSSFFormulaEvaluator eval =
new HSSFFormulaEvaluator(s, wb);
Iterator it = s.rowIterator();
while(it.hasNext()) {
HSSFRow r = (HSSFRow)it.next();
eval.setCurrentRow(r);
process(r, eval);
}
}
}
protected void process(HSSFRow row, HSSFFormulaEvaluator eval) {
Iterator it = row.cellIterator();
while(it.hasNext()) {
HSSFCell cell = (HSSFCell)it.next();
if(cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
FormulaRecordAggregate record = (FormulaRecordAggregate)
cell.getCellValueRecord();
FormulaRecord r = record.getFormulaRecord();
List ptgs = r.getParsedExpression();
String cellRef = (new CellReference(row.getRowNum(), cell.getCellNum())).toString();
if(cellRef.equals("BP24")) {
System.out.print(cellRef);
System.out.println(" - has " + r.getNumberOfExpressionTokens() + " ptgs over " + r.getExpressionLength() + " tokens:");
for(int i=0; i<ptgs.size(); i++) {
String c = ptgs.get(i).getClass().toString();
System.out.println("\t" + c.substring(c.lastIndexOf('.')+1) );
}
System.out.println("-> " + cell.getCellFormula());
}
eval.evaluate(cell);
}
}
}
}

View File

@ -0,0 +1,117 @@
package org.apache.poi.hssf.usermodel;
import java.util.Iterator;
import junit.framework.TestCase;
/**
* Tests to show that our documentation at
* http://poi.apache.org/hssf/eval.html
* all actually works as we'd expect them to
*/
public class TestFormulaEvaluatorDocs extends TestCase {
protected void setUp() throws Exception {
super.setUp();
}
/**
* http://poi.apache.org/hssf/eval.html#EvaluateAll
*/
public void testEvaluateAll() throws Exception {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet s1 = wb.createSheet();
HSSFSheet s2 = wb.createSheet();
wb.setSheetName(0, "S1");
wb.setSheetName(1, "S2");
HSSFRow s1r1 = s1.createRow(0);
HSSFRow s1r2 = s1.createRow(1);
HSSFRow s2r1 = s2.createRow(0);
HSSFCell s1r1c1 = s1r1.createCell((short)0);
HSSFCell s1r1c2 = s1r1.createCell((short)1);
HSSFCell s1r1c3 = s1r1.createCell((short)2);
s1r1c1.setCellValue(22.3);
s1r1c2.setCellValue(33.4);
s1r1c3.setCellFormula("SUM(A1:B1)");
HSSFCell s1r2c1 = s1r2.createCell((short)0);
HSSFCell s1r2c2 = s1r2.createCell((short)1);
HSSFCell s1r2c3 = s1r2.createCell((short)2);
s1r2c1.setCellValue(-1.2);
s1r2c2.setCellValue(-3.4);
s1r2c3.setCellFormula("SUM(A2:B2)");
HSSFCell s2r1c1 = s2r1.createCell((short)0);
s2r1c1.setCellFormula("S1!A1");
// Not evaluated yet
assertEquals(0.0, s1r1c3.getNumericCellValue(), 0);
assertEquals(0.0, s1r2c3.getNumericCellValue(), 0);
assertEquals(0.0, s2r1c1.getNumericCellValue(), 0);
// Do a full evaluate, as per our docs
// uses evaluateFormulaCell()
for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) {
HSSFSheet sheet = wb.getSheetAt(sheetNum);
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
for(Iterator rit = sheet.rowIterator(); rit.hasNext();) {
HSSFRow r = (HSSFRow)rit.next();
evaluator.setCurrentRow(r);
for(Iterator cit = r.cellIterator(); cit.hasNext();) {
HSSFCell c = (HSSFCell)cit.next();
if(c.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
evaluator.evaluateFormulaCell(c);
// For testing - all should be numeric
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, evaluator.evaluateFormulaCell(c));
}
}
}
}
// Check now as expected
assertEquals(55.7, wb.getSheetAt(0).getRow(0).getCell((short)2).getNumericCellValue(), 0);
assertEquals("SUM(A1:B1)", wb.getSheetAt(0).getRow(0).getCell((short)2).getCellFormula());
assertEquals(HSSFCell.CELL_TYPE_FORMULA, wb.getSheetAt(0).getRow(0).getCell((short)2).getCellType());
assertEquals(-4.6, wb.getSheetAt(0).getRow(1).getCell((short)2).getNumericCellValue(), 0);
assertEquals("SUM(A2:B2)", wb.getSheetAt(0).getRow(1).getCell((short)2).getCellFormula());
assertEquals(HSSFCell.CELL_TYPE_FORMULA, wb.getSheetAt(0).getRow(1).getCell((short)2).getCellType());
assertEquals(22.3, wb.getSheetAt(1).getRow(0).getCell((short)0).getNumericCellValue(), 0);
assertEquals("S1!A1", wb.getSheetAt(1).getRow(0).getCell((short)0).getCellFormula());
assertEquals(HSSFCell.CELL_TYPE_FORMULA, wb.getSheetAt(1).getRow(0).getCell((short)0).getCellType());
// Now do the alternate call, which zaps the formulas
// uses evaluateInCell()
for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) {
HSSFSheet sheet = wb.getSheetAt(sheetNum);
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
for(Iterator rit = sheet.rowIterator(); rit.hasNext();) {
HSSFRow r = (HSSFRow)rit.next();
evaluator.setCurrentRow(r);
for(Iterator cit = r.cellIterator(); cit.hasNext();) {
HSSFCell c = (HSSFCell)cit.next();
if(c.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
evaluator.evaluateInCell(c);
}
}
}
}
assertEquals(55.7, wb.getSheetAt(0).getRow(0).getCell((short)2).getNumericCellValue(), 0);
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, wb.getSheetAt(0).getRow(0).getCell((short)2).getCellType());
assertEquals(-4.6, wb.getSheetAt(0).getRow(1).getCell((short)2).getNumericCellValue(), 0);
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, wb.getSheetAt(0).getRow(1).getCell((short)2).getCellType());
assertEquals(22.3, wb.getSheetAt(1).getRow(0).getCell((short)0).getNumericCellValue(), 0);
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, wb.getSheetAt(1).getRow(0).getCell((short)0).getCellType());
}
}

View File

@ -0,0 +1,14 @@
Author = Nick Burch
Title = Test Document
Subject = This is a sample document
Keywords = Testing Sample
Comments = This document is used for testing text and metadata extraction
I am a test document
This is page 1
I am Calibri (Body) in font size 11
This is page two
Its Arial Black in 16 point
Its also in blue

View File

@ -74,4 +74,34 @@ public class TestProblems extends TestCase {
}
}
}
/**
* Test for TableCell not skipping the last paragraph
*/
public void testTableCellLastParagraph() throws Exception {
HWPFDocument doc = new HWPFDocument(new FileInputStream(dirname + "/Bug44292.doc"));
Range r = doc.getRange();
//get the table
Paragraph p = r.getParagraph(0);
Table t = r.getTable(p);
//get the only row
TableRow row = t.getRow(0);
//get the first cell
TableCell cell = row.getCell(0);
// First cell should have one paragraph
assertEquals(1, cell.numParagraphs());
//get the second
cell = row.getCell(1);
// Second cell should be detected as having two paragraphs
assertEquals(2, cell.numParagraphs());
//get the last cell
cell = row.getCell(2);
// Last cell should have one paragraph
assertEquals(1, cell.numParagraphs());
}
}

View File

@ -82,21 +82,45 @@ public class TestEscherContainerRecord extends TestCase
r2.setOptions( (short) 0x9876 );
r2.setRecordId( EscherOptRecord.RECORD_ID );
String expected;
r.addChildRecord( r2 );
String expected = "org.apache.poi.ddf.EscherContainerRecord (SpContainer):" + nl +
" isContainer: true" + nl +
" options: 0x000F" + nl +
" recordId: 0xF004" + nl +
" numchildren: 1" + nl +
" children: " + nl +
"org.apache.poi.ddf.EscherOptRecord:" + nl +
" isContainer: false" + nl +
" options: 0x0003" + nl +
" recordId: 0xF00B" + nl +
" numchildren: 0" + nl +
" properties:" + nl;
expected = "org.apache.poi.ddf.EscherContainerRecord (SpContainer):" + nl +
" isContainer: true" + nl +
" options: 0x000F" + nl +
" recordId: 0xF004" + nl +
" numchildren: 1" + nl +
" children: " + nl +
" Child 0:" + nl +
"org.apache.poi.ddf.EscherOptRecord:" + nl +
" isContainer: false" + nl +
" options: 0x0003" + nl +
" recordId: 0xF00B" + nl +
" numchildren: 0" + nl +
" properties:" + nl;
assertEquals( expected, r.toString() );
r.addChildRecord( r2 );
expected = "org.apache.poi.ddf.EscherContainerRecord (SpContainer):" + nl +
" isContainer: true" + nl +
" options: 0x000F" + nl +
" recordId: 0xF004" + nl +
" numchildren: 2" + nl +
" children: " + nl +
" Child 0:" + nl +
"org.apache.poi.ddf.EscherOptRecord:" + nl +
" isContainer: false" + nl +
" options: 0x0003" + nl +
" recordId: 0xF00B" + nl +
" numchildren: 0" + nl +
" properties:" + nl +
" Child 1:" + nl +
"org.apache.poi.ddf.EscherOptRecord:" + nl +
" isContainer: false" + nl +
" options: 0x0003" + nl +
" recordId: 0xF00B" + nl +
" numchildren: 0" + nl +
" properties:" + nl;
assertEquals( expected, r.toString() );
}
public void testGetRecordSize() throws Exception

View File

@ -23,8 +23,8 @@ import junit.framework.TestSuite;
import org.apache.poi.hssf.eventmodel.TestEventRecordFactory;
import org.apache.poi.hssf.eventmodel.TestModelFactory;
import org.apache.poi.hssf.model.TestFormulaParser;
import org.apache.poi.hssf.model.TestDrawingManager;
import org.apache.poi.hssf.model.TestFormulaParser;
import org.apache.poi.hssf.model.TestSheet;
import org.apache.poi.hssf.record.TestAreaFormatRecord;
import org.apache.poi.hssf.record.TestAreaRecord;
@ -91,6 +91,7 @@ import org.apache.poi.hssf.usermodel.TestFontDetails;
import org.apache.poi.hssf.usermodel.TestFormulas;
import org.apache.poi.hssf.usermodel.TestHSSFCell;
import org.apache.poi.hssf.usermodel.TestHSSFClientAnchor;
import org.apache.poi.hssf.usermodel.TestHSSFComment;
import org.apache.poi.hssf.usermodel.TestHSSFDateUtil;
import org.apache.poi.hssf.usermodel.TestHSSFHeaderFooter;
import org.apache.poi.hssf.usermodel.TestHSSFPalette;
@ -110,7 +111,6 @@ import org.apache.poi.hssf.util.TestCellReference;
import org.apache.poi.hssf.util.TestRKUtil;
import org.apache.poi.hssf.util.TestRangeAddress;
import org.apache.poi.hssf.util.TestSheetReferences;
import org.apache.poi.hssf.usermodel.TestHSSFComment;
/**
* Test Suite for running just HSSF tests. Mostly

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,21 @@
Author = Nick Burch
Title = Sample Spreadsheet
Subject = Spreadsheet for testing
Keywords = Testing Sample Formulas
Comments = This is a sample spreadsheet, for use when testing things
(First Sheet)
Test spreadsheet
2nd row 2nd row 2nd column
This one is red
(Sheet Number 2)
Start of 2nd sheet
Sheet 2 row 2
I'm in bold blue, on a yellow background
cb=1 cb=10 cb=2 cb=sum
1 10 2 =SUM(A7:C7)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -45,7 +45,9 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Test the low level formula parser functionality. High level tests are to
* be done via usermodel/HSSFCell.setFormulaValue() .
* be done via usermodel/HSSFCell.setFormulaValue() .
* Some tests are also done in scratchpad, if they need
* HSSFFormulaEvaluator, which is there
*/
public class TestFormulaParser extends TestCase {
@ -349,7 +351,7 @@ public class TestFormulaParser extends TestCase {
assertTrue("ptg0 is a StringPtg", ptg[0] instanceof StringPtg);
assertTrue("ptg0 contains exact value", ((StringPtg)ptg[0]).getValue().equals(value));
}
public void testLookupAndMatchFunctionArgs()
{
FormulaParser fp = new FormulaParser("lookup(A1, A3:A52, B3:B52)", null);

View File

@ -79,4 +79,27 @@ public class TestNoteRecord
System.arraycopy(ser, 4, recdata, 0, recdata.length);
assertTrue(Arrays.equals(data, recdata));
}
public void testClone()
{
NoteRecord record = new NoteRecord();
record.setRow((short)1);
record.setColumn((short)2);
record.setFlags(NoteRecord.NOTE_VISIBLE);
record.setShapeId((short)1026);
record.setAuthor("Apache Software Foundation");
NoteRecord cloned = (NoteRecord)record.clone();
assertEquals(record.getRow(), cloned.getRow());
assertEquals(record.getColumn(), cloned.getColumn());
assertEquals(record.getFlags(), cloned.getFlags());
assertEquals(record.getShapeId(), cloned.getShapeId());
assertEquals(record.getAuthor(), cloned.getAuthor());
//finally check that the serialized data is the same
byte[] src = record.serialize();
byte[] cln = cloned.serialize();
assertTrue(Arrays.equals(src, cln));
}
}

View File

@ -65,4 +65,16 @@ public class TestNoteStructureSubRecord
assertEquals(ser.length - 4, data.length);
}
public void testClone()
{
NoteStructureSubRecord record = new NoteStructureSubRecord();
byte[] src = record.serialize();
NoteStructureSubRecord cloned = (NoteStructureSubRecord)record.clone();
byte[] cln = cloned.serialize();
assertEquals(record.getRecordSize(), cloned.getRecordSize());
assertTrue(Arrays.equals(src, cln));
}
}

View File

@ -117,4 +117,44 @@ public class TestTextObjectRecord extends TestCase {
}
}
/**
* Test cloning
*/
public void testClone() {
String text = "Hello, World";
HSSFRichTextString str = new HSSFRichTextString(text);
TextObjectRecord obj = new TextObjectRecord();
int frLength = ( str.numFormattingRuns() + 1 ) * 8;
obj.setFormattingRunLength( (short) frLength );
obj.setTextLength( (short) str.length() );
obj.setReserved1(true);
obj.setReserved2((short)2);
obj.setReserved3((short)3);
obj.setReserved4((short)4);
obj.setReserved5((short)5);
obj.setReserved6((short)6);
obj.setReserved7((short)7);
obj.setStr( str );
TextObjectRecord cloned = (TextObjectRecord)obj.clone();
assertEquals(obj.getReserved2(), cloned.getReserved2());
assertEquals(obj.getReserved3(), cloned.getReserved3());
assertEquals(obj.getReserved4(), cloned.getReserved4());
assertEquals(obj.getReserved5(), cloned.getReserved5());
assertEquals(obj.getReserved6(), cloned.getReserved6());
assertEquals(obj.getReserved7(), cloned.getReserved7());
assertEquals(obj.getRecordSize(), cloned.getRecordSize());
assertEquals(obj.getOptions(), cloned.getOptions());
assertEquals(obj.getHorizontalTextAlignment(), cloned.getHorizontalTextAlignment());
assertEquals(obj.getFormattingRunLength(), cloned.getFormattingRunLength());
assertEquals(obj.getStr().getString(), cloned.getStr().getString());
//finally check that the serialized data is the same
byte[] src = obj.serialize();
byte[] cln = cloned.serialize();
assertTrue(Arrays.equals(src, cln));
}
}

View File

@ -913,7 +913,104 @@ extends TestCase {
}
/**
* Bug 44200: Sheet not cloneable when Note added to excel cell
*/
public void test44200() throws Exception {
FileInputStream in = new FileInputStream(new File(cwd, "44200.xls"));
HSSFWorkbook wb = new HSSFWorkbook(in);
in.close();
wb.cloneSheet(0);
assertTrue("No Exceptions while cloning sheet", true);
//serialize and read again
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
out.close();
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
assertTrue("No Exceptions while reading file", true);
}
/**
* Bug 44201: Sheet not cloneable when validation added to excel cell
*/
public void test44201() throws Exception {
FileInputStream in = new FileInputStream(new File(cwd, "44201.xls"));
HSSFWorkbook wb = new HSSFWorkbook(in);
in.close();
wb.cloneSheet(0);
assertTrue("No Exceptions while cloning sheet", true);
//serialize and read again
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
out.close();
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
assertTrue("No Exceptions while reading file", true);
}
/**
* Bug 37684 : Unhandled Continue Record Error
*/
public void test37684 () throws Exception {
FileInputStream in = new FileInputStream(new File(cwd, "37684-1.xls"));
HSSFWorkbook wb = new HSSFWorkbook(in);
in.close();
assertTrue("No exceptions while reading workbook", true);
//serialize and read again
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
out.close();
assertTrue("No exceptions while saving workbook", true);
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
assertTrue("No exceptions while reading saved stream", true);
in = new FileInputStream(new File(cwd, "37684-2.xls"));
wb = new HSSFWorkbook(in);
in.close();
assertTrue("No exceptions while reading workbook", true);
//serialize and read again
out = new ByteArrayOutputStream();
wb.write(out);
out.close();
assertTrue("No exceptions while saving workbook", true);
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
assertTrue("No exceptions while reading saved stream", true);
}
/**
* Bug 41139: Constructing HSSFWorkbook is failed,threw threw ArrayIndexOutOfBoundsException for creating UnknownRecord
*/
public void test41139() throws Exception {
FileInputStream in = new FileInputStream(new File(cwd, "41139.xls"));
HSSFWorkbook wb = new HSSFWorkbook(in);
in.close();
assertTrue("No Exceptions while reading file", true);
//serialize and read again
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
out.close();
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
assertTrue("No Exceptions while reading file", true);
}
}

Some files were not shown because too many files have changed in this diff Show More