From 6acad091984fe947d5f10b9f0473874a48b2618f Mon Sep 17 00:00:00 2001
From: Yegor Kozlov
Date: Fri, 25 Jan 2008 15:48:14 +0000
Subject: [PATCH] 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
---
build.xml | 16 +-
src/documentation/content/xdocs/changes.xml | 22 +-
src/documentation/content/xdocs/hssf/eval.xml | 103 +++++-
.../content/xdocs/hssf/quick-guide.xml | 31 +-
src/documentation/content/xdocs/index.xml | 8 +-
src/documentation/content/xdocs/status.xml | 21 ++
.../apache/poi/ddf/EscherContainerRecord.java | 68 +++-
.../org/apache/poi/ddf/EscherOptRecord.java | 11 +-
.../poi/hssf/model/DrawingManager2.java | 13 +-
.../apache/poi/hssf/model/FormulaParser.java | 27 +-
src/java/org/apache/poi/hssf/model/Sheet.java | 66 +++-
.../org/apache/poi/hssf/model/Workbook.java | 83 ++++-
.../record/AbstractEscherHolderRecord.java | 64 +++-
.../poi/hssf/record/BoundSheetRecord.java | 10 +
.../apache/poi/hssf/record/DVALRecord.java | 124 +++++--
.../org/apache/poi/hssf/record/DVRecord.java | 29 +-
.../poi/hssf/record/DrawingGroupRecord.java | 10 +
.../apache/poi/hssf/record/DrawingRecord.java | 16 +-
.../poi/hssf/record/EscherAggregate.java | 144 +++++++-
.../apache/poi/hssf/record/FormulaRecord.java | 22 +-
.../apache/poi/hssf/record/NameRecord.java | 127 +++++--
.../apache/poi/hssf/record/NoteRecord.java | 11 +
.../hssf/record/NoteStructureSubRecord.java | 9 +
.../org/apache/poi/hssf/record/Record.java | 28 ++
.../apache/poi/hssf/record/RecordFactory.java | 2 +-
.../poi/hssf/record/RecordInputStream.java | 23 +-
.../poi/hssf/record/SharedFormulaRecord.java | 71 ++--
.../poi/hssf/record/TextObjectRecord.java | 17 +
.../poi/hssf/record/UncalcedRecord.java | 82 +++++
.../aggregates/FormulaRecordAggregate.java | 4 +-
.../aggregates/ValueRecordsAggregate.java | 21 +-
.../poi/hssf/record/formula/Area3DPtg.java | 24 +-
.../poi/hssf/record/formula/ErrPtg.java | 2 +-
.../poi/hssf/record/formula/ExpPtg.java | 2 +-
.../apache/poi/hssf/usermodel/HSSFCell.java | 48 ++-
.../poi/hssf/usermodel/HSSFPatriarch.java | 58 ++-
.../apache/poi/hssf/usermodel/HSSFRow.java | 56 ++-
.../poi/hssf/usermodel/HSSFShapeGroup.java | 5 +-
.../apache/poi/hssf/usermodel/HSSFSheet.java | 198 ++++++++--
.../poi/hssf/usermodel/HSSFWorkbook.java | 29 ++
.../apache/poi/hssf/util/AreaReference.java | 70 +++-
.../apache/poi/hssf/util/CellReference.java | 4 +
.../poi/poifs/common/POIFSConstants.java | 3 +
.../poi/poifs/filesystem/POIFSFileSystem.java | 49 ++-
.../poi/poifs/storage/HeaderBlockReader.java | 7 +-
.../poi/poifs/storage/RawDataBlock.java | 46 ++-
src/java/org/apache/poi/util/IOUtils.java | 16 +-
.../hslf/extractor/PowerPointExtractor.java | 49 ++-
.../org/apache/poi/hslf/model/TextRun.java | 70 +++-
.../poi/hslf/record/StyleTextPropAtom.java | 52 ++-
.../poi/hslf/usermodel/RichTextRun.java | 25 +-
.../hssf/usermodel/HSSFFormulaEvaluator.java | 60 ++-
.../apache/poi/hwpf/usermodel/TableRow.java | 2 +-
.../org/apache/poi/hslf/data/SampleShow.ppt | Bin 0 -> 124416 bytes
.../org/apache/poi/hslf/data/SampleShow.pptx | Bin 0 -> 48008 bytes
.../org/apache/poi/hslf/data/SampleShow.txt | 26 ++
.../poi/hslf/extractor/TextExtractor.java | 23 ++
.../poi/hslf/usermodel/TestRichTextRun.java | 87 ++++-
.../apache/poi/hssf/data/42464-ExpPtg-bad.xls | Bin 0 -> 141824 bytes
.../apache/poi/hssf/data/42464-ExpPtg-ok.xls | Bin 0 -> 143872 bytes
.../poi/hssf/model/TestFormulaParserSP.java | 83 +++++
.../poi/hssf/usermodel/TestBug42464.java | 93 +++++
.../usermodel/TestFormulaEvaluatorDocs.java | 117 ++++++
.../org/apache/poi/hwpf/data/Bug44292.doc | Bin 0 -> 24064 bytes
.../org/apache/poi/hwpf/data/SampleDoc.doc | Bin 0 -> 27136 bytes
.../org/apache/poi/hwpf/data/SampleDoc.docx | Bin 0 -> 11611 bytes
.../org/apache/poi/hwpf/data/SampleDoc.txt | 14 +
.../poi/hwpf/usermodel/TestProblems.java | 30 ++
.../poi/ddf/TestEscherContainerRecord.java | 48 ++-
.../org/apache/poi/hssf/HSSFTests.java | 4 +-
.../org/apache/poi/hssf/data/37684-1.xls | Bin 0 -> 32768 bytes
.../org/apache/poi/hssf/data/37684-2.xls | Bin 0 -> 82944 bytes
.../apache/poi/hssf/data/42464-ExpPtg-bad.xls | Bin 0 -> 141824 bytes
.../apache/poi/hssf/data/42464-ExpPtg-ok.xls | Bin 0 -> 143872 bytes
.../org/apache/poi/hssf/data/43493.xls | Bin 0 -> 66048 bytes
.../org/apache/poi/hssf/data/43902.xls | Bin 0 -> 83456 bytes
.../poi/hssf/data/44010-SingleChart.xls | Bin 0 -> 28672 bytes
.../apache/poi/hssf/data/44010-TwoCharts.xls | Bin 0 -> 29696 bytes
.../org/apache/poi/hssf/data/44167.xls | Bin 0 -> 14336 bytes
.../org/apache/poi/hssf/data/44200.xls | Bin 0 -> 13824 bytes
.../org/apache/poi/hssf/data/44201.xls | Bin 0 -> 13824 bytes
.../org/apache/poi/hssf/data/Booleans.xlsx | Bin 0 -> 8612 bytes
.../org/apache/poi/hssf/data/ForShifting.xls | Bin 0 -> 18432 bytes
.../apache/poi/hssf/data/NoGutsRecords.xls | Bin 0 -> 35328 bytes
.../org/apache/poi/hssf/data/SampleSS.txt | 21 ++
.../org/apache/poi/hssf/data/SampleSS.xls | Bin 0 -> 17408 bytes
.../org/apache/poi/hssf/data/SampleSS.xlsx | Bin 0 -> 9112 bytes
.../poi/hssf/data/SharedFormulaTest.xls | Bin 0 -> 31232 bytes
.../apache/poi/hssf/data/SheetWithDrawing.xls | Bin 0 -> 17408 bytes
.../apache/poi/hssf/data/UncalcedRecord.xls | Bin 0 -> 18432 bytes
.../org/apache/poi/hssf/data/WithChart.xls | Bin 0 -> 20992 bytes
.../org/apache/poi/hssf/data/WithChart.xlsx | Bin 0 -> 10138 bytes
.../apache/poi/hssf/data/WithTwoCharts.xls | Bin 0 -> 25600 bytes
.../apache/poi/hssf/data/WithTwoCharts.xlsx | Bin 0 -> 12810 bytes
.../org/apache/poi/hssf/data/comments.xls | Bin 0 -> 14336 bytes
.../poi/hssf/model/TestFormulaParser.java | 6 +-
.../poi/hssf/record/TestNoteRecord.java | 23 ++
.../record/TestNoteStructureSubRecord.java | 12 +
.../poi/hssf/record/TestTextObjectRecord.java | 40 ++
.../apache/poi/hssf/usermodel/TestBugs.java | 99 ++++-
.../hssf/usermodel/TestEscherGraphics.java | 216 ++++++++++-
.../poi/hssf/usermodel/TestFormulas.java | 11 +
.../poi/hssf/usermodel/TestHSSFRow.java | 50 ++-
.../poi/hssf/usermodel/TestHSSFSheet.java | 342 +++++++++++++++++-
.../poi/hssf/usermodel/TestHSSFWorkbook.java | 114 +++++-
.../poi/hssf/usermodel/TestNamedRange.java | 20 +-
.../poi/hssf/usermodel/TestSheetHiding.java | 67 ++--
.../hssf/usermodel/TestSheetShiftRows.java | 110 ++++++
.../poi/hssf/usermodel/TestUnfixedBugs.java | 75 +---
.../poi/hssf/util/TestAreaReference.java | 226 +++++++++++-
.../TestOffice2007XMLException.java | 22 ++
.../poi/poifs/storage/TestRawDataBlock.java | 113 ++++++
112 files changed, 3797 insertions(+), 453 deletions(-)
create mode 100644 src/java/org/apache/poi/hssf/record/UncalcedRecord.java
create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/data/SampleShow.ppt
create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/data/SampleShow.pptx
create mode 100644 src/scratchpad/testcases/org/apache/poi/hslf/data/SampleShow.txt
create mode 100644 src/scratchpad/testcases/org/apache/poi/hssf/data/42464-ExpPtg-bad.xls
create mode 100644 src/scratchpad/testcases/org/apache/poi/hssf/data/42464-ExpPtg-ok.xls
create mode 100644 src/scratchpad/testcases/org/apache/poi/hssf/model/TestFormulaParserSP.java
create mode 100644 src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java
create mode 100644 src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java
create mode 100644 src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug44292.doc
create mode 100755 src/scratchpad/testcases/org/apache/poi/hwpf/data/SampleDoc.doc
create mode 100755 src/scratchpad/testcases/org/apache/poi/hwpf/data/SampleDoc.docx
create mode 100644 src/scratchpad/testcases/org/apache/poi/hwpf/data/SampleDoc.txt
create mode 100755 src/testcases/org/apache/poi/hssf/data/37684-1.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/37684-2.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/42464-ExpPtg-bad.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/42464-ExpPtg-ok.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/43493.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/43902.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/44010-SingleChart.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/44010-TwoCharts.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/44167.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/44200.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/44201.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/Booleans.xlsx
create mode 100755 src/testcases/org/apache/poi/hssf/data/ForShifting.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/NoGutsRecords.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/SampleSS.txt
create mode 100755 src/testcases/org/apache/poi/hssf/data/SampleSS.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/SampleSS.xlsx
create mode 100644 src/testcases/org/apache/poi/hssf/data/SharedFormulaTest.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/SheetWithDrawing.xls
create mode 100644 src/testcases/org/apache/poi/hssf/data/UncalcedRecord.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/WithChart.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/WithChart.xlsx
create mode 100755 src/testcases/org/apache/poi/hssf/data/WithTwoCharts.xls
create mode 100755 src/testcases/org/apache/poi/hssf/data/WithTwoCharts.xlsx
create mode 100644 src/testcases/org/apache/poi/hssf/data/comments.xls
diff --git a/build.xml b/build.xml
index 8661595b8d..9be4a793fe 100644
--- a/build.xml
+++ b/build.xml
@@ -137,7 +137,7 @@ under the License.
-
+
@@ -159,7 +159,7 @@ under the License.
-
+
@@ -368,6 +368,8 @@ under the License.
destfile="${ooxml.xsds.jar}"
javasource="1.4"
failonerror="false"
+ fork="true"
+ memoryMaximumSize="512m"
>
@@ -461,6 +463,16 @@ under the License.
destdir="${ooxml.output.dir}" debug="on" srcdir="${ooxml.src}">
+
+
+
+
+
+
+
+
+ 44292 - Correctly process the last paragraph in a word file
+ 44254 - Avoid some unread byte warnings, and properly understand DVALRecord
+ Add another formula evaluation method, evaluateFormulaCell(cell), which will re-calculate the value for a formula, without affecting the formula itself.
+ 41726 - Fix how we handle signed cell offsets in relative areas and references
+ 44233 - Support for getting and setting a flag on the sheet, which tells excel to re-calculate all formulas on it at next reload
+ 44201 - Enable cloning of sheets with data validation rules
+ 44200 - Enable cloning of sheets with notes
+ 43008 - Add a moveCell method to HSSFRow, and deprecate setCellNum(), which didn't update things properly
+ 43058 - Support setting row grouping on files from CR IX, which lack GutsRecords
+ 31795 - Support cloning of sheets with certain drawing objects on them
+ 43902 - Don't consider merged regions when auto-sizing columns
+ 42464 - Avoid "Expected ExpPtg to be converted from Shared to Non-Shared Formula" on large, formula heavy worksheets
+ 42033 - Add support for named ranges with unicode names
+ 34023 - When shifting rows, update formulas on that sheet to point to the new location of those rows
+ Support getting all the cells referenced by an AreaReference, not just the corner ones
+ 43510 - Add support for named ranges in formulas, including non-contiguous named ranges
+ 43937 - Add support for hiding and un-hiding sheets, and checking their current hidden status
+ 44167 - Fix for non-contiguous named ranges
+ 44070 - Fix for shifting comments when shifting rows
+
+ Support for tables in HSLF43781 - Fix for extracting text from TextBoxes HSLF inImprove JavaDocs relating to hssf font and fill colourings
@@ -45,7 +66,6 @@
41064 - [PATCH] Support for String continue records27511 - [PATCH] Support for data validation, via DVRecord and DVALRecord
-
43877 and 39512 - Fix for handling mixed OBJ and CONTINUE records.43807 - Throw an IllegalArgumentException if asked to create a merged region with invalid columns or rows, rather than writing out a corrupt file
diff --git a/src/documentation/content/xdocs/hssf/eval.xml b/src/documentation/content/xdocs/hssf/eval.xml
index d697eb082b..8d63512173 100644
--- a/src/documentation/content/xdocs/hssf/eval.xml
+++ b/src/documentation/content/xdocs/hssf/eval.xml
@@ -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.
- This code currently lives the scratchpad area of the POI CVS repository.
+ 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.
+
+ Status
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 Formula
evaluation development guide for details.
@@ -51,8 +55,12 @@
The following code demonstrates how to use the HSSFFormulaEvaluator
in the context of other POI excel reading code.
-
There are two ways in which you can use the HSSFFormulaEvalutator API.
+
There are several ways in which you can use the HSSFFormulaEvalutator API.
+
+ Using HSSFFormulaEvaluator.evaluate(HSSFCell cell)
+
This evaluates a given cell, and returns the new value,
+ without affecting the cell
- Using HSSFFormulaEvaluator.evaluateInCell(HSSFCell cell)
-
+
+
+ Using HSSFFormulaEvaluator.evaluateFormulaCell(HSSFCell cell)
+
evaluateFormulaCell(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
+
The return of the function is the type of the
+ formula result, such as HSSFCell.CELL_TYPE_BOOLEAN
+
+
+
+
+ Using HSSFFormulaEvaluator.evaluateInCell(HSSFCell cell)
+
evaluateInCell(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.
Generally you should have to create only one HSSFFormulaEvaluator
diff --git a/src/documentation/content/xdocs/hssf/quick-guide.xml b/src/documentation/content/xdocs/hssf/quick-guide.xml
index ddbd447fd1..b2f729fedc 100644
--- a/src/documentation/content/xdocs/hssf/quick-guide.xml
+++ b/src/documentation/content/xdocs/hssf/quick-guide.xml
@@ -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<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.
}
+
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
+ POI 3.0.2 BETA2 Release
+
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
the changelog, and
- download
+ download
the source and binaries from your
- local mirror.
+ local mirror.
The release is also available from the central Maven repository under Group ID "org.apache.poi".
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 72761cf9c0..73375a336e 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -33,6 +33,27 @@
+ 44292 - Correctly process the last paragraph in a word file
+ 44254 - Avoid some unread byte warnings, and properly understand DVALRecord
+ Add another formula evaluation method, evaluateFormulaCell(cell), which will re-calculate the value for a formula, without affecting the formula itself.
+ 41726 - Fix how we handle signed cell offsets in relative areas and references
+ 44233 - Support for getting and setting a flag on the sheet, which tells excel to re-calculate all formulas on it at next reload
+ 44201 - Enable cloning of sheets with data validation rules
+ 44200 - Enable cloning of sheets with notes
+ 43008 - Add a moveCell method to HSSFRow, and deprecate setCellNum(), which didn't update things properly
+ 43058 - Support setting row grouping on files from CR IX, which lack GutsRecords
+ 31795 - Support cloning of sheets with certain drawing objects on them
+ 43902 - Don't consider merged regions when auto-sizing columns
+ 42464 - Avoid "Expected ExpPtg to be converted from Shared to Non-Shared Formula" on large, formula heavy worksheets
+ 42033 - Add support for named ranges with unicode names
+ 34023 - When shifting rows, update formulas on that sheet to point to the new location of those rows
+ Support getting all the cells referenced by an AreaReference, not just the corner ones
+ 43510 - Add support for named ranges in formulas, including non-contiguous named ranges
+ 43937 - Add support for hiding and un-hiding sheets, and checking their current hidden status
+ 44167 - Fix for non-contiguous named ranges
+ 44070 - Fix for shifting comments when shifting rows
+
+ Support for tables in HSLF43781 - Fix for extracting text from TextBoxes HSLF inImprove JavaDocs relating to hssf font and fill colourings
diff --git a/src/java/org/apache/poi/ddf/EscherContainerRecord.java b/src/java/org/apache/poi/ddf/EscherContainerRecord.java
index 05ee6b096f..28b4a976c3 100644
--- a/src/java/org/apache/poi/ddf/EscherContainerRecord.java
+++ b/src/java/org/apache/poi/ddf/EscherContainerRecord.java
@@ -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();
}
diff --git a/src/java/org/apache/poi/ddf/EscherOptRecord.java b/src/java/org/apache/poi/ddf/EscherOptRecord.java
index f18e38f03c..d7de48edaf 100644
--- a/src/java/org/apache/poi/ddf/EscherOptRecord.java
+++ b/src/java/org/apache/poi/ddf/EscherOptRecord.java
@@ -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
diff --git a/src/java/org/apache/poi/hssf/model/DrawingManager2.java b/src/java/org/apache/poi/hssf/model/DrawingManager2.java
index f14d62ff31..dee95fb4fc 100644
--- a/src/java/org/apache/poi/hssf/model/DrawingManager2.java
+++ b/src/java/org/apache/poi/hssf/model/DrawingManager2.java
@@ -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;
diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java
index 9697dadafd..832dbdef7a 100644
--- a/src/java/org/apache/poi/hssf/model/FormulaParser.java
+++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java
@@ -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));
}
}
}
diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java
index ff18f4636b..f3f7deba07 100644
--- a/src/java/org/apache/poi/hssf/model/Sheet.java
+++ b/src/java/org/apache/poi/hssf/model/Sheet.java
@@ -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)) ) );
}
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index 972aa09015..08c236cda1 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -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() {
diff --git a/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java b/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
index efa83ca330..c5fae27ae3 100644
--- a/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
+++ b/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
@@ -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)
diff --git a/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java b/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java
index bf264bf02a..78daff2ebb 100644
--- a/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java
+++ b/src/java/org/apache/poi/hssf/record/BoundSheetRecord.java
@@ -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);
+ }
}
diff --git a/src/java/org/apache/poi/hssf/record/DVALRecord.java b/src/java/org/apache/poi/hssf/record/DVALRecord.java
index 858f525ca0..2846f5066c 100644
--- a/src/java/org/apache/poi/hssf/record/DVALRecord.java
+++ b/src/java/org/apache/poi/hssf/record/DVALRecord.java
@@ -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;
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/poi/hssf/record/DVRecord.java b/src/java/org/apache/poi/hssf/record/DVRecord.java
index 0bae009bd3..a7b68a1743 100644
--- a/src/java/org/apache/poi/hssf/record/DVRecord.java
+++ b/src/java/org/apache/poi/hssf/record/DVRecord.java
@@ -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
@@ -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 */
diff --git a/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java b/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
index 3782273a5d..ea083ee1e3 100644
--- a/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
+++ b/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
@@ -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)
diff --git a/src/java/org/apache/poi/hssf/record/DrawingRecord.java b/src/java/org/apache/poi/hssf/record/DrawingRecord.java
index d73702b6a2..009bc31bce 100644
--- a/src/java/org/apache/poi/hssf/record/DrawingRecord.java
+++ b/src/java/org/apache/poi/hssf/record/DrawingRecord.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/record/EscherAggregate.java b/src/java/org/apache/poi/hssf/record/EscherAggregate.java
index 28a717682a..d62b59adbb 100644
--- a/src/java/org/apache/poi/hssf/record/EscherAggregate.java
+++ b/src/java/org/apache/poi/hssf/record/EscherAggregate.java
@@ -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 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){
- result = ptg.toFormulaString(book);
+ return ptg.toFormulaString(book);
} else if (ptg.getClass() == DeletedArea3DPtg.class || ptg.getClass() == DeletedRef3DPtg.class) {
- result = "#REF!" ; }
-
- return result;
+ return "#REF!";
+ }
+ return "";
}
/** sets the reference , the area only (range)
@@ -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 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 )
diff --git a/src/java/org/apache/poi/hssf/record/NoteRecord.java b/src/java/org/apache/poi/hssf/record/NoteRecord.java
index 63c0b1d7dc..a9e83806f8 100644
--- a/src/java/org/apache/poi/hssf/record/NoteRecord.java
+++ b/src/java/org/apache/poi/hssf/record/NoteRecord.java
@@ -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;
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/record/NoteStructureSubRecord.java b/src/java/org/apache/poi/hssf/record/NoteStructureSubRecord.java
index 6ad3f8eb63..c99f29be35 100644
--- a/src/java/org/apache/poi/hssf/record/NoteStructureSubRecord.java
+++ b/src/java/org/apache/poi/hssf/record/NoteStructureSubRecord.java
@@ -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;
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/record/Record.java b/src/java/org/apache/poi/hssf/record/Record.java
index 3205c57740..c1b8a0cfda 100644
--- a/src/java/org/apache/poi/hssf/record/Record.java
+++ b/src/java/org/apache/poi/hssf/record/Record.java
@@ -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];
+ }
}
diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java
index cf705a316d..20e8ba788a 100644
--- a/src/java/org/apache/poi/hssf/record/RecordFactory.java
+++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java
@@ -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);
diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java
index 399e0f566d..dd853f2463 100755
--- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java
+++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java
@@ -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();
diff --git a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
index c0f77cb558..ae250246d3 100755
--- a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
+++ b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
@@ -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
diff --git a/src/java/org/apache/poi/hssf/record/TextObjectRecord.java b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
index 97685c9ca2..c8f6669b0f 100644
--- a/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
+++ b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
@@ -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;
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/record/UncalcedRecord.java b/src/java/org/apache/poi/hssf/record/UncalcedRecord.java
new file mode 100644
index 0000000000..a67b0b5af4
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/UncalcedRecord.java
@@ -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
+ *
+ * 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;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
index be0f703e80..7840d32562 100644
--- a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
+++ b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
@@ -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 );
diff --git a/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
index fe3af5aede..e48a0a902b 100644
--- a/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
+++ b/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
@@ -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);
diff --git a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
index 470b6e390b..d808a94c4d 100644
--- a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
@@ -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)
diff --git a/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java b/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java
index e382d4e759..34bad6f32c 100644
--- a/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java
@@ -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 */
diff --git a/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java b/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java
index 511ac44d7b..a7fc0274ad 100644
--- a/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java
@@ -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()
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
index 9cc4550719..3e24106ca7 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
@@ -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);
+ }
}
/**
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
index e383ed5586..583e1b4793 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
@@ -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;
+ }
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
index 423b023af1..ae5727bc68 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
@@ -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 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();
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
index 19e54e2adc..fa49528619 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
@@ -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;
}
-
-}
+}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index b52fa1e5b2..0250a4cbaa 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -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; inull 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;
}
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 487f9c6b22..e237ed75db 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -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
diff --git a/src/java/org/apache/poi/hssf/util/AreaReference.java b/src/java/org/apache/poi/hssf/util/AreaReference.java
index a881dbabb9..8169378e76 100644
--- a/src/java/org/apache/poi/hssf/util/AreaReference.java
+++ b/src/java/org/apache/poi/hssf/util/AreaReference.java
@@ -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();
diff --git a/src/java/org/apache/poi/hssf/util/CellReference.java b/src/java/org/apache/poi/hssf/util/CellReference.java
index 0bc1cee2e3..def58472a8 100644
--- a/src/java/org/apache/poi/hssf/util/CellReference.java
+++ b/src/java/org/apache/poi/hssf/util/CellReference.java
@@ -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
diff --git a/src/java/org/apache/poi/poifs/common/POIFSConstants.java b/src/java/org/apache/poi/poifs/common/POIFSConstants.java
index bc1bf6dad5..399f52be4b 100644
--- a/src/java/org/apache/poi/poifs/common/POIFSConstants.java
+++ b/src/java/org/apache/poi/poifs/common/POIFSConstants.java
@@ -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;
diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java b/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
index 981c51d39a..771db767be 100644
--- a/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
+++ b/src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
@@ -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
diff --git a/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java b/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
index 16c94e2c23..0d5bb817b4 100644
--- a/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
+++ b/src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
@@ -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");
}
diff --git a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
index 4403966343..d554298400 100644
--- a/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
+++ b/src/java/org/apache/poi/poifs/storage/RawDataBlock.java
@@ -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
{
diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java
index 42b69c850b..c3aa8695e2 100644
--- a/src/java/org/apache/poi/util/IOUtils.java
+++ b/src/java/org/apache/poi/util/IOUtils.java
@@ -58,11 +58,16 @@ public class IOUtils
}
/**
- * Same as the normal in.read(b, off, len), but tries to ensure that
- * the entire len number of bytes is read.
+ * Same as the normal in.read(b, off, len), but
+ * tries to ensure that the entire len number of bytes
+ * is read.
*
- * 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
}
}
}
-}
-
+}
\ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java
index 0fc6f5e847..f247227007 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java
@@ -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");
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
index ca6e02d69b..586932e739 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
@@ -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
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
index 32f8b2bd03..fdaa9eec2c 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
@@ -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;
}
-
-
+
/* ************************************************************************ */
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java
index d16bf7bc02..fe03755079 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java
@@ -20,25 +20,28 @@
package org.apache.poi.hslf.usermodel;
-import org.apache.poi.hslf.model.*;
+import java.awt.Color;
+
+import org.apache.poi.hslf.model.MasterSheet;
import org.apache.poi.hslf.model.Shape;
-import org.apache.poi.hslf.model.textproperties.*;
+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;
-import org.apache.poi.hslf.exceptions.HSLFException;
-
-import java.awt.*;
/**
* 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 */
diff --git a/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java b/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
index 2a9fc6c64b..f60a6adaac 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
@@ -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.
+ *
+ * int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
+ *
+ * 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:
*
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
*
+ * 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) {
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java
index ff9cf2b9c8..a88e32360e 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/TableRow.java
@@ -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]);
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/SampleShow.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/SampleShow.ppt
new file mode 100755
index 0000000000000000000000000000000000000000..81b99e100f926c1cc3fde2e90aef8c5838aa791f
GIT binary patch
literal 124416
zcmeFabzD~4_AmU<9RiAgbO_QZNJ~gJ2!eD=gLHR@G@^jg2-2;zfPhF#2}mm`qSE!w
z1$uO|J!kKGe}BB^-n%EC?^<)sS~JHObIh?G`g+ezrgg?2EY#>03Zk;1Rx9`
z0w4--9Y72~96$m<65s}a6o52<41g?v9DqE40)Qfb5`Z#*3V02Ea`KO#m%`
zTL9VsIsmr;bOH1L^Z^V23;~P)i~&plOaaUQ%mFL_ECKESSOHiA*Z|l9*a6rBH~=^T
zI04)Ra0YMza0PG!a0l=JfX4VUxI<&^1Ma>6Dxf4=kdp$m+y>koPI7>Vzv(W383T$t
zad03HZls@avSRPK;AL1o>^Z~Yuf>R@t$B7TbijFnKwtRN7$xv_7tm@4(3`fPwNM=>
zz)0AF9=QuzYjP6uYbk&9)5GB5prQ}}pi2BP_V-WdAQ*n*G!6-j9P}P*FcMJPrNR45
z!P^~9LYzR2Y(dDschLJ0LG)=4{Jr;kgY;^1r)47jsjdHO_2C8Y|MBj>+kWWH1mF7~
z8nYk$2esqu`iJHWgn!)sojqV$CbWUmGlF)$fVjcHXbD=q8tsMI>T;{aM6yw|If
z`})0Y-#gB{dlg!P;pM4L8^dWtG_cENAI*{0-r65M`=IQdGlJ!s_sBIOP3>Jl3Oje9
z7s&@1c(Mdby``iQp3(z+l%=lnTd$CJ83g^x%ZlvHQ^TfM?_Pc*B)LAUX4+~VIil1#
zRrw~8Em|6e&2f8SuRU!*%AqRHtoRvd*7Bx#9w8j(kL@ggvFg3QC>Ad%0i*-C&xk
z!N#jKeG3b^JqrcZnJaA%udfQ+N$Mp)oqd;lUmHs=Wb!r~6`gTFz1e_WHtKlyylG_i
z24T@+onm1J?tNOz5(cd7_|mF*ww?o#l-gG)Aq?$(GWefkXNn$RWRqpJ-2WQT=J%L+
zDEF{>kd0@^_sX#BU2}5bh)LPfq^m*0)q@)rNL2kvN9~uZ*2%Yh7Fd_N%{Beab&K2k
z9-