Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-674287 via svnmerge from

https://svn.apache.org/repos/asf/poi/trunk

........
  r671322 | nick | 2008-06-24 20:53:53 +0100 (Tue, 24 Jun 2008) | 1 line
  
  Make a start on being able to process formulas in the eventusermodel code
........
  r672230 | nick | 2008-06-27 11:12:11 +0100 (Fri, 27 Jun 2008) | 1 line
  
  Add MethodNotFound exceptions to the faq
........
  r672550 | nick | 2008-06-28 18:04:09 +0100 (Sat, 28 Jun 2008) | 1 line
  
  Finish the EventWorkbookBuilder, now does sheet references in formulas properly
........
  r672553 | nick | 2008-06-28 18:12:38 +0100 (Sat, 28 Jun 2008) | 1 line
  
  Update changelog about EventWorkbookBuilder, and tweak XLS2CSVmra to use it if formulas required
........
  r672562 | nick | 2008-06-28 19:21:21 +0100 (Sat, 28 Jun 2008) | 1 line
  
  Avoid spurious missing lines with the MissingRecordAware event code, and odd files that contain RowRecords in the middle of the cell Records.
........
  r672567 | nick | 2008-06-28 19:48:35 +0100 (Sat, 28 Jun 2008) | 1 line
  
  Patch from dnapoletano from bug #45175 - Support for variable length operands in org.apache.poi.hwpf.sprm.SprmOperation
........
  r672569 | nick | 2008-06-28 19:54:02 +0100 (Sat, 28 Jun 2008) | 1 line
  
  Patch from N. Hira from bug #45001 - Further fix for HWPF Range.delete() and unicode characters
........
  r672570 | nick | 2008-06-28 19:58:23 +0100 (Sat, 28 Jun 2008) | 1 line
  
  Patch from N.Hira from bug #45252 - Improvement for HWPF Range.replaceText()
........
  r673050 | yegor | 2008-07-01 11:32:29 +0100 (Tue, 01 Jul 2008) | 1 line
  
  updated status of the latest release, 3.1-FINAL
........
  r673853 | josh | 2008-07-03 23:20:18 +0100 (Thu, 03 Jul 2008) | 1 line
  
  Fix for bug 45334 - formula parser needs to handle dots in identifiers
........
  r673863 | josh | 2008-07-04 00:09:08 +0100 (Fri, 04 Jul 2008) | 1 line
  
  Fix for bug 45334 - added impl for ERROR.TYPE()
........
  r673987 | nick | 2008-07-04 10:58:21 +0100 (Fri, 04 Jul 2008) | 1 line
  
  Fix bug #45336 - Fix HSSFColor.getTripletHash()
........
  r673997 | nick | 2008-07-04 11:46:59 +0100 (Fri, 04 Jul 2008) | 1 line
  
  Fix bug #45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts
........


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@674296 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-07-06 13:10:09 +00:00
parent 695b85049d
commit dcf1741253
25 changed files with 1180 additions and 144 deletions

View File

@ -36,7 +36,7 @@
</devs>
<!-- Don't forget to update status.xml too! -->
<release version="3.5.1-alpha1" date="2008-04-??">
<release version="3.5.1-beta1" date="2008-07-11">
<action dev="POI-DEVELOPERS" type="add">45018 - Support for fetching embeded documents from within an OOXML file</action>
<action dev="POI-DEVELOPERS" type="add">Port support for setting a policy on missing / blank cells when fetching, to XSSF too</action>
<action dev="POI-DEVELOPERS" type="add">Common text extraction factory, which returns the correct POITextExtractor for the supplied data</action>
@ -45,8 +45,15 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.2-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="add"><!-- to keep forrest dtd quiet--></action>
<release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
<action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
<action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
<action dev="POI-DEVELOPERS" type="fix">45252 - Improvement for HWPF Range.replaceText()</action>
<action dev="POI-DEVELOPERS" type="fix">45001 - Further fix for HWPF Range.delete() and unicode characters</action>
<action dev="POI-DEVELOPERS" type="add">45175 - Support for variable length operands in org.apache.poi.hwpf.sprm.SprmOperation</action>
<action dev="POI-DEVELOPERS" type="fix">Avoid spurious missing lines with the MissingRecordAware event code, and odd files that contain RowRecords in the middle of the cell Records.</action>
<action dev="POI-DEVELOPERS" type="add">Support for parsing formulas during EventUserModel processing, via the new EventWorkbookBuilder</action>
</release>
<release version="3.1-final" date="2008-06-29">
<action dev="POI-DEVELOPERS" type="fix">30978 - Fixed re-serialization of tRefErr3d and tAreaErr3d</action>

View File

@ -20,6 +20,36 @@
<!DOCTYPE faqs PUBLIC "-//APACHE//DTD FAQ V1.1//EN" "./dtd/faq-v11.dtd">
<faqs title="Frequently Asked Questions">
<faq>
<question>
My code uses some new HSSF feature, compiles fine but fails when live with a "MethodNotFoundException"
</question>
<answer>
<p>You almost certainly have an older version of POI earlier
on your classpath. Quite a few runtimes and other packages
will ship an older version of POI, so this is an easy problem
to hit without realising.</p>
<p>The best way to identify the offending earlier jar file is
with a few lines of java. These will load one of the core POI
classes, and report where it came from.</p>
<source>
ClassLoader classloader = org.apache.poi.poifs.filesystem.POIFSFileSystem.class.getClassLoader();
URL res = classloader.getResource("org/apache/poi/poifs/filesystem/POIFSFileSystem.class">
String path = res.getPath();
System.out.println("Core POI came from " + path);
</source>
</answer>
</faq>
<faq>
<question>
My code uses the scratchpad, compiles fine but fails to run with a "MethodNotFoundException"
</question>
<answer>
<p>You almost certainly have an older version earlier on your
classpath. See the answer to the similar question above for
how to track this down.</p>
</answer>
</faq>
<faq>
<question>
Why is reading a simple sheet taking so long?

View File

@ -31,35 +31,24 @@
</header>
<body>
<section><title>Office Open XML Support</title>
<section><title>POI 3.5.1 beta 1, and Office Open XML Support (2008-07-11)</title>
<p>We are currently working to support the new Office Open XML
file formats, such as XLSX and PPTX, which were introduced in
Office 2007.</p>
<p>Support for these is currently only available in an svn branch,
but we hope to have a full release including it by the summer.
People interested should follow the
<p>Development for this is in a svn branch, but we are please to
announce our first preview release containing this support.
Users interested in the OOXML support should download the
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">POI 3.5.1 beta 1</link>
the source and binaries from their
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">local mirror</link>.
People interested should also follow the
<link href="mailinglists.html">dev list</link> to track progress.</p>
</section>
<section><title>POI 3.1-BETA2 Released (2008-05-28)</title>
<section><title>POI 3.1-FINAL Released (2008-06-29)</title>
<p>
The POI team is pleased to announce the release of 3.1 BETA2 which is one of the final steps before 3.1 FINAL.
The status of this release is a beta, meaning that we encourage users to try it out.
If you find any bugs, please report them to the POI <link href="https://issues.apache.org/bugzilla/buglist.cgi?product=POI">bug database</link> or to
the <link href="./mailinglists.html">POI Developer List</link>.
</p><p> A full list of changes is available in
<link href="./changes.html">the changelog</link>, and
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">download</link>
the source and binaries from your
<link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">local mirror</link>.
</p>
<p>
The release is also available from the central Maven repository
under Group ID "org.apache.poi" and Version "3.1-beta2".
</p>
</section>
<section><title>POI 3.0.2 Released</title>
<p>The POI team is pleased to announce POI 3.0.2, the latest release of Apache POI.
There have been many important bug fixes since the 3.0.1 release and a lot of new features. A full list of changes is available in
The POI team is pleased to announce the release of 3.1 FINAL, the latest release of Apache POI.
There have been many important bug fixes since the 3.0.2 release and a lot of new features.
</p><p> A full list of changes is available in
<link href="./changes.html">the changelog</link>, and
<link href="http://www.apache.org/dyn/closer.cgi/poi/release/">download</link>
the source and binaries from your
@ -67,13 +56,8 @@
</p>
<p>
The release is also available from the central Maven repository
under Group ID "org.apache.poi" and Version "3.0.2-FINAL".
under Group ID "org.apache.poi" and Version "3.1-FINAL".
</p>
<p>We would also like to confirm that verions 3.0 and 3.0.1 of Apache
POI do
<em>not</em> contain any viruses. Users of broken virus checkers
which do detect a 94 byte file, sci_cec.db, as containing one are
advised to contact their vendor for a fix.</p>
</section>
<section><title>Purpose</title>

View File

@ -33,7 +33,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.5.1-alpha1" date="2008-04-??">
<release version="3.5.1-beta1" date="2008-07-11">
<action dev="POI-DEVELOPERS" type="add">45018 - Support for fetching embeded documents from within an OOXML file</action>
<action dev="POI-DEVELOPERS" type="add">Port support for setting a policy on missing / blank cells when fetching, to XSSF too</action>
<action dev="POI-DEVELOPERS" type="add">Common text extraction factory, which returns the correct POITextExtractor for the supplied data</action>
@ -42,8 +42,15 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.2-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="add"><!-- to keep forrest dtd quiet--></action>
<release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
<action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
<action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
<action dev="POI-DEVELOPERS" type="fix">45252 - Improvement for HWPF Range.replaceText()</action>
<action dev="POI-DEVELOPERS" type="fix">45001 - Further fix for HWPF Range.delete() and unicode characters</action>
<action dev="POI-DEVELOPERS" type="add">45175 - Support for variable length operands in org.apache.poi.hwpf.sprm.SprmOperation</action>
<action dev="POI-DEVELOPERS" type="fix">Avoid spurious missing lines with the MissingRecordAware event code, and odd files that contain RowRecords in the middle of the cell Records.</action>
<action dev="POI-DEVELOPERS" type="add">Support for parsing formulas during EventUserModel processing, via the new EventWorkbookBuilder</action>
</release>
<release version="3.1-final" date="2008-06-29">
<action dev="POI-DEVELOPERS" type="fix">30978 - Fixed re-serialization of tRefErr3d and tAreaErr3d</action>

View File

@ -30,9 +30,11 @@ import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.CellValueRecordInterface;
@ -46,6 +48,7 @@ import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
@ -64,6 +67,10 @@ public class XLS2CSVmra implements HSSFListener {
/** Should we output the formula, or the value it has? */
private boolean outputFormulaValues = true;
/** For parsing Formulas */
private SheetRecordCollectingListener workbookBuildingListener;
private HSSFWorkbook stubWorkbook;
// Records we pick up as we process
private SSTRecord sstRecord;
private FormatTrackingHSSFListener formatListener;
@ -108,7 +115,13 @@ public class XLS2CSVmra implements HSSFListener {
HSSFEventFactory factory = new HSSFEventFactory();
HSSFRequest request = new HSSFRequest();
request.addListenerForAllRecords(formatListener);
if(outputFormulaValues) {
request.addListenerForAllRecords(formatListener);
} else {
workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
request.addListenerForAllRecords(workbookBuildingListener);
}
factory.processWorkbookEvents(request, fs);
}
@ -124,6 +137,16 @@ public class XLS2CSVmra implements HSSFListener {
switch (record.getSid())
{
case BOFRecord.sid:
BOFRecord br = (BOFRecord)record;
if(br.getType() == BOFRecord.TYPE_WORKSHEET) {
// Create sub workbook if required
if(workbookBuildingListener != null && stubWorkbook == null) {
stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook();
}
}
break;
case SSTRecord.sid:
sstRecord = (SSTRecord) record;
break;
@ -161,7 +184,7 @@ public class XLS2CSVmra implements HSSFListener {
}
} else {
thisStr = '"' +
FormulaParser.toFormulaString(null, frec.getParsedExpression()) + '"';
FormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()) + '"';
}
break;
case StringRecord.sid:

View File

@ -0,0 +1,199 @@
/* ====================================================================
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.eventusermodel;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.EOFRecord;
import org.apache.poi.hssf.record.ExternSheetRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.SupBookRecord;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* When working with the EventUserModel, if you want to
* process formulas, you need an instance of
* {@link Workbook} to pass to a {@link HSSFWorkbook},
* to finally give to {@link FormulaParser},
* and this will build you stub ones.
* Since you're working with the EventUserModel, you
* wouldn't want to get a full {@link Workbook} and
* {@link HSSFWorkbook}, as they would eat too much memory.
* Instead, you should collect a few key records as they
* go past, then call this once you have them to build a
* stub {@link Workbook}, and from that a stub
* {@link HSSFWorkbook}, to use with the {@link FormulaParser}.
*
* The records you should collect are:
* * {@link ExternSheetRecord}
* * {@link BoundSheetRecord}
* You should probably also collect {@link SSTRecord},
* but it's not required to pass this in.
*
* To help, this class includes a HSSFListener wrapper
* that will do the collecting for you.
*/
public class EventWorkbookBuilder {
/**
* Wraps up your stub {@link Workbook} as a stub
* {@link HSSFWorkbook}, ready for passing to
* {@link FormulaParser}
* @param workbook A stub {@link Workbook}
*/
public static HSSFWorkbook createStubHSSFWorkbook(Workbook workbook) {
return new StubHSSFWorkbook(workbook);
}
/**
* Creates a stub Workbook from the supplied records,
* suitable for use with the {@link FormulaParser}
* @param externs The ExternSheetRecords in your file
* @param bounds The BoundSheetRecords in your file
* @param sst The SSTRecord in your file.
* @return A stub Workbook suitable for use with {@link FormulaParser}
*/
public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
BoundSheetRecord[] bounds, SSTRecord sst) {
List wbRecords = new ArrayList();
// Core Workbook records go first
if(bounds != null) {
for(int i=0; i<bounds.length; i++) {
wbRecords.add(bounds[i]);
}
}
if(sst != null) {
wbRecords.add(sst);
}
// Now we can have the ExternSheetRecords,
// preceded by a SupBookRecord
if(externs != null) {
wbRecords.add(SupBookRecord.createInternalReferences(
(short)externs.length));
for(int i=0; i<externs.length; i++) {
wbRecords.add(externs[i]);
}
}
// Finally we need an EoF record
wbRecords.add(new EOFRecord());
return Workbook.createWorkbook(wbRecords);
}
/**
* Creates a stub workbook from the supplied records,
* suitable for use with the {@link FormulaParser}
* @param externs The ExternSheetRecords in your file
* @param bounds The BoundSheetRecords in your file
* @return A stub Workbook suitable for use with {@link FormulaParser}
*/
public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
BoundSheetRecord[] bounds) {
return createStubWorkbook(externs, bounds, null);
}
/**
* A wrapping HSSFListener which will collect
* {@link BoundSheetRecord}s and {@link ExternSheetRecord}s as
* they go past, so you can create a Stub {@link Workbook} from
* them once required.
*/
public static class SheetRecordCollectingListener implements HSSFListener {
private HSSFListener childListener;
private List boundSheetRecords = new ArrayList();
private List externSheetRecords = new ArrayList();
private SSTRecord sstRecord = null;
public SheetRecordCollectingListener(HSSFListener childListener) {
this.childListener = childListener;
}
public BoundSheetRecord[] getBoundSheetRecords() {
return (BoundSheetRecord[])boundSheetRecords.toArray(
new BoundSheetRecord[boundSheetRecords.size()]
);
}
public ExternSheetRecord[] getExternSheetRecords() {
return (ExternSheetRecord[])externSheetRecords.toArray(
new ExternSheetRecord[externSheetRecords.size()]
);
}
public SSTRecord getSSTRecord() {
return sstRecord;
}
public HSSFWorkbook getStubHSSFWorkbook() {
return createStubHSSFWorkbook(
getStubWorkbook()
);
}
public Workbook getStubWorkbook() {
return createStubWorkbook(
getExternSheetRecords(), getBoundSheetRecords(),
getSSTRecord()
);
}
/**
* Process this record ourselves, and then
* pass it on to our child listener
*/
public void processRecord(Record record) {
// Handle it ourselves
processRecordInternally(record);
// Now pass on to our child
childListener.processRecord(record);
}
/**
* Process the record ourselves, but do not
* pass it on to the child Listener.
*/
public void processRecordInternally(Record record) {
if(record instanceof BoundSheetRecord) {
boundSheetRecords.add(record);
}
else if(record instanceof ExternSheetRecord) {
externSheetRecords.add(record);
}
else if(record instanceof SSTRecord) {
sstRecord = (SSTRecord)record;
}
}
}
/**
* Let us at the {@link Workbook} constructor on
* {@link HSSFWorkbook}
*/
private static class StubHSSFWorkbook extends HSSFWorkbook {
private StubHSSFWorkbook(Workbook wb) {
super(wb);
}
}
}

View File

@ -46,8 +46,14 @@ import org.apache.poi.hssf.record.RowRecord;
*/
public class MissingRecordAwareHSSFListener implements HSSFListener {
private HSSFListener childListener;
private int lastSeenRow = -1;
private int lastSeenColumn = -1;
// Need to have different counters for cell rows and
// row rows, as you sometimes get a RowRecord in the
// middle of some cells, and that'd break everything
private int lastRowRow = -1;
private int lastCellRow = -1;
private int lastCellColumn = -1;
/**
* Constructs a new MissingRecordAwareHSSFListener, which
@ -71,14 +77,16 @@ public class MissingRecordAwareHSSFListener implements HSSFListener {
if (bof.getType() == bof.TYPE_WORKBOOK)
{
// Reset the row and column counts - new workbook
lastSeenRow = -1;
lastSeenColumn = -1;
lastRowRow = -1;
lastCellRow = -1;
lastCellColumn = -1;
//System.out.println("Encountered workbook");
} else if (bof.getType() == bof.TYPE_WORKSHEET)
{
// Reset the row and column counts - new sheet
lastSeenRow = -1;
lastSeenColumn = -1;
lastRowRow = -1;
lastCellRow = -1;
lastCellColumn = -1;
//System.out.println("Encountered sheet reference");
}
break;
@ -92,15 +100,15 @@ public class MissingRecordAwareHSSFListener implements HSSFListener {
// + rowrec.getFirstCol() + " last column at " + rowrec.getLastCol());
// If there's a jump in rows, fire off missing row records
if(lastSeenRow+1 < rowrec.getRowNumber()) {
for(int i=(lastSeenRow+1); i<rowrec.getRowNumber(); i++) {
if(lastRowRow+1 < rowrec.getRowNumber()) {
for(int i=(lastRowRow+1); i<rowrec.getRowNumber(); i++) {
MissingRowDummyRecord dr = new MissingRowDummyRecord(i);
childListener.processRecord(dr);
}
}
// Record this as the last row we saw
lastSeenRow = rowrec.getRowNumber();
lastRowRow = rowrec.getRowNumber();
break;
@ -157,45 +165,49 @@ public class MissingRecordAwareHSSFListener implements HSSFListener {
break;
}
// Do we need to fire dummy end-of-row records?
if(thisRow != lastSeenRow) {
for(int i=lastSeenRow; i<thisRow; i++) {
// If we're on cells, and this cell isn't in the same
// row as the last one, then fire the
// dummy end-of-row records?
if(thisRow != lastCellRow && lastCellRow > -1) {
for(int i=lastCellRow; i<thisRow; i++) {
int cols = -1;
if(i == lastSeenRow) {
cols = lastSeenColumn;
if(i == lastCellRow) {
cols = lastCellColumn;
}
LastCellOfRowDummyRecord r = new LastCellOfRowDummyRecord(i, cols);
childListener.processRecord(r);
}
}
// If we've finished with the columns, then fire the final
// dummy end-of-row record
if(lastSeenRow != -1 && lastSeenColumn != -1 && thisRow == -1) {
LastCellOfRowDummyRecord r = new LastCellOfRowDummyRecord(lastSeenRow, lastSeenColumn);
// If we've just finished with the cells, then fire the
// final dummy end-of-row record
if(lastCellRow != -1 && lastCellColumn != -1 && thisRow == -1) {
LastCellOfRowDummyRecord r = new LastCellOfRowDummyRecord(lastCellRow, lastCellColumn);
childListener.processRecord(r);
lastSeenRow = -1;
lastSeenColumn = -1;
lastCellRow = -1;
lastCellColumn = -1;
}
// If we've moved onto a new row, the ensure we re-set
// the column counter
if(thisRow != lastSeenRow) {
lastSeenColumn = -1;
if(thisRow != lastCellRow) {
lastCellColumn = -1;
}
// Do we need to fire dummy cell records?
if(lastSeenColumn != (thisColumn-1)) {
for(int i=lastSeenColumn+1; i<thisColumn; i++) {
// If there's a gap in the cells, then fire
// the dummy cell records?
if(lastCellColumn != (thisColumn-1)) {
for(int i=lastCellColumn+1; i<thisColumn; i++) {
MissingCellDummyRecord r = new MissingCellDummyRecord(thisRow, i);
childListener.processRecord(r);
}
}
// Update cell and row counts if doing cells
// Update cell and row counts as needed
if(thisColumn != -1) {
lastSeenRow = thisRow;
lastSeenColumn = thisColumn;
lastCellColumn = thisColumn;
lastCellRow = thisRow;
}
childListener.processRecord(record);

View File

@ -22,11 +22,13 @@ import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;
//import PTG's .. since we need everything, import *
//import PTGs .. since we need everything, import *
import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.CellReference;
/**
* This class parses a formula string into a List of tokens in RPN order.
@ -178,8 +180,14 @@ public final class FormulaParser {
GetChar();
}
/** Get an Identifier */
private String GetName() {
/**
* Parses a sheet name, named range name, or simple cell reference.<br/>
* Note - identifiers in Excel can contain dots, so this method may return a String
* which may need to be converted to an area reference. For example, this method
* may return a value like "A1..B2", in which case the caller must convert it to
* an area reference like "A1:B2"
*/
private String parseIdentifier() {
StringBuffer Token = new StringBuffer();
if (!IsAlpha(look) && look != '\'') {
throw expected("Name");
@ -201,7 +209,9 @@ public final class FormulaParser {
}
else
{
while (IsAlNum(look)) {
// allow for any sequence of dots and identifier chars
// special case of two consecutive dots is best treated in the calling code
while (IsAlNum(look) || look == '.') {
Token.append(look);
GetChar();
}
@ -220,15 +230,22 @@ public final class FormulaParser {
return value.length() == 0 ? null : value.toString();
}
private ParseNode parseFunctionOrIdentifier() {
String name = GetName();
private ParseNode parseFunctionReferenceOrName() {
String name = parseIdentifier();
if (look == '('){
//This is a function
return function(name);
}
return new ParseNode(parseIdentifier(name));
return new ParseNode(parseNameOrReference(name));
}
private Ptg parseIdentifier(String name) {
private Ptg parseNameOrReference(String name) {
AreaReference areaRef = parseArea(name);
if (areaRef != null) {
// will happen if dots are used instead of colon
return new AreaPtg(areaRef.formatAsString());
}
if (look == ':' || look == '.') { // this is a AreaReference
GetChar();
@ -238,23 +255,28 @@ public final class FormulaParser {
}
String first = name;
String second = GetName();
String second = parseIdentifier();
return new AreaPtg(first+":"+second);
}
if (look == '!') {
Match('!');
String sheetName = name;
String first = GetName();
String first = parseIdentifier();
short externIdx = (short)book.getExternalSheetIndex(book.getSheetIndex(sheetName));
areaRef = parseArea(name);
if (areaRef != null) {
// will happen if dots are used instead of colon
return new Area3DPtg(areaRef.formatAsString(), externIdx);
}
if (look == ':') {
Match(':');
String second=GetName();
String second=parseIdentifier();
if (look == '!') {
//The sheet name was included in both of the areas. Only really
//need it once
Match('!');
String third=GetName();
String third=parseIdentifier();
if (!sheetName.equals(second))
throw new RuntimeException("Unhandled double sheet reference.");
@ -271,9 +293,7 @@ public final class FormulaParser {
// 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();
if (cellRef) {
if (isValidCellReference(name)) {
return new RefPtg(name);
}
@ -287,6 +307,41 @@ public final class FormulaParser {
+ name + "\", but that named range wasn't defined!");
}
/**
* @return <code>null</code> if name cannot be split at a dot
*/
private AreaReference parseArea(String name) {
int dotPos = name.indexOf('.');
if (dotPos < 0) {
return null;
}
int dotCount = 1;
while (dotCount<name.length() && name.charAt(dotPos+dotCount) == '.') {
dotCount++;
if (dotCount>3) {
// four or more consecutive dots does not convert to ':'
return null;
}
}
String partA = name.substring(0, dotPos);
if (!isValidCellReference(partA)) {
return null;
}
String partB = name.substring(dotPos+dotCount);
if (!isValidCellReference(partB)) {
return null;
}
CellReference topLeft = new CellReference(partA);
CellReference bottomRight = new CellReference(partB);
return new AreaReference(topLeft, bottomRight);
}
private static boolean isValidCellReference(String str) {
// TODO - exact rules for recognising cell references may be too complicated for regex
return CELL_REFERENCE_PATTERN.matcher(str).matches();
}
/**
* Note - Excel function names are 'case aware but not case sensitive'. This method may end
* up creating a defined name record in the workbook if the specified name is not an internal
@ -465,7 +520,7 @@ public final class FormulaParser {
return new ParseNode(parseStringLiteral());
}
if (IsAlpha(look) || look == '\''){
return parseFunctionOrIdentifier();
return parseFunctionReferenceOrName();
}
// else - assume number
return new ParseNode(parseNumber());
@ -510,7 +565,7 @@ public final class FormulaParser {
private ErrPtg parseErrorLiteral() {
Match('#');
String part1 = GetName().toUpperCase();
String part1 = parseIdentifier().toUpperCase();
switch(part1.charAt(0)) {
case 'V':

View File

@ -1,25 +1,78 @@
/*
* 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.
*/
/*
* Created on May 15, 2005
*
*/
/* ====================================================================
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.formula.functions;
public class Errortype extends NotImplementedFunction {
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
/**
* Implementation for the ERROR.TYPE() Excel function.<p/>
*
* <b>Syntax:</b><br/>
* <b>ERROR.TYPE</b>(<b>errorValue</b>)<p/>
*
* Returns a number corresponding to the error type of the supplied argument.<p/>
*
* <table border="1" cellpadding="1" cellspacing="1" summary="Return values for ERROR.TYPE()">
* <tr><td>errorValue</td><td>Return Value</td></tr>
* <tr><td>#NULL!</td><td>1</td></tr>
* <tr><td>#DIV/0!</td><td>2</td></tr>
* <tr><td>#VALUE!</td><td>3</td></tr>
* <tr><td>#REF!</td><td>4</td></tr>
* <tr><td>#NAME?</td><td>5</td></tr>
* <tr><td>#NUM!</td><td>6</td></tr>
* <tr><td>#N/A!</td><td>7</td></tr>
* <tr><td>everything else</td><td>#N/A!</td></tr>
* </table>
*
* Note - the results of ERROR.TYPE() are different to the constants defined in
* <tt>HSSFErrorConstants</tt>.
*
* @author Josh Micich
*/
public final class Errortype implements Function {
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
try {
OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
return ErrorEval.NA;
} catch (EvaluationException e) {
int result = translateErrorCodeToErrorTypeValue(e.getErrorEval().getErrorCode());
return new NumberEval(result);
}
}
private int translateErrorCodeToErrorTypeValue(int errorCode) {
switch (errorCode) {
case HSSFErrorConstants.ERROR_NULL: return 1;
case HSSFErrorConstants.ERROR_DIV_0: return 2;
case HSSFErrorConstants.ERROR_VALUE: return 3;
case HSSFErrorConstants.ERROR_REF: return 4;
case HSSFErrorConstants.ERROR_NAME: return 5;
case HSSFErrorConstants.ERROR_NUM: return 6;
case HSSFErrorConstants.ERROR_NA : return 7;
}
throw new IllegalArgumentException("Invalid error code (" + errorCode + ")");
}
}

View File

@ -24,6 +24,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
@ -106,6 +107,12 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
private ArrayList names;
/**
* this holds the HSSFFont objects attached to this workbook.
* We only create these from the low level records as required.
*/
private Hashtable fonts;
/**
* holds whether or not to preserve other nodes in the POIFS. Used
* for macros and embedded objects.
@ -1021,9 +1028,10 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
if(fontindex == Short.MAX_VALUE){
throw new IllegalArgumentException("Maximum number of fonts was exceeded");
}
HSSFFont retval = new HSSFFont(fontindex, font);
return retval;
// Ask getFontAt() to build it for us,
// so it gets properly cached
return getFontAt(fontindex);
}
/**
@ -1033,15 +1041,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
String name, boolean italic, boolean strikeout,
short typeOffset, byte underline)
{
// System.out.println( boldWeight + ", " + color + ", " + fontHeight + ", " + name + ", " + italic + ", " + strikeout + ", " + typeOffset + ", " + underline );
for (short i = 0; i < workbook.getNumberOfFontRecords(); i++)
{
if (i == 4)
continue;
for (short i=0; i<=getNumberOfFonts(); i++) {
// Remember - there is no 4!
if(i == 4) continue;
FontRecord font = workbook.getFontRecordAt(i);
HSSFFont hssfFont = new HSSFFont(i, font);
// System.out.println( hssfFont.getBoldweight() + ", " + hssfFont.getColor() + ", " + hssfFont.getFontHeight() + ", " + hssfFont.getFontName() + ", " + hssfFont.getItalic() + ", " + hssfFont.getStrikeout() + ", " + hssfFont.getTypeOffset() + ", " + hssfFont.getUnderline() );
HSSFFont hssfFont = getFontAt(i);
if (hssfFont.getBoldweight() == boldWeight
&& hssfFont.getColor() == color
&& hssfFont.getFontHeight() == fontHeight
@ -1051,12 +1055,10 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
&& hssfFont.getTypeOffset() == typeOffset
&& hssfFont.getUnderline() == underline)
{
// System.out.println( "Found font" );
return hssfFont;
}
}
// System.out.println( "No font found" );
return null;
}
@ -1071,15 +1073,26 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
}
/**
* get the font at the given index number
* Get the font at the given index number
* @param idx index number
* @return HSSFFont at the index
*/
public HSSFFont getFontAt(short idx)
{
if(fonts == null) fonts = new Hashtable();
// So we don't confuse users, give them back
// the same object every time, but create
// them lazily
Short sIdx = Short.valueOf(idx);
if(fonts.containsKey(sIdx)) {
return (HSSFFont)fonts.get(sIdx);
}
FontRecord font = workbook.getFontRecordAt(idx);
HSSFFont retval = new HSSFFont(idx, font);
fonts.put(sIdx, retval);
return retval;
}

View File

@ -155,8 +155,12 @@ public class HSSFColor implements Color {
String hexString = color.getHexString();
if (result.containsKey(hexString)) {
throw new RuntimeException("Dup color hexString (" + hexString
+ ") for color (" + color.getClass().getName() + ")");
HSSFColor other = (HSSFColor)result.get(hexString);
throw new RuntimeException(
"Dup color hexString (" + hexString
+ ") for color (" + color.getClass().getName() + ") - "
+ " already taken by (" + other.getClass().getName() + ")"
);
}
result.put(hexString, color);
}
@ -1511,9 +1515,9 @@ public class HSSFColor implements Color {
public final static short index = 0x19;
public final static short[] triplet =
{
153, 51, 102
127, 0, 0
};
public final static String hexString = "9999:3333:6666";
public final static String hexString = "8000:0:0";
public short getIndex()
{

View File

@ -91,15 +91,18 @@ public class TextPiece extends PropertyNode implements Comparable
public void adjustForDelete(int start, int length)
{
// length is expected to be the number of code-points,
// not the number of characters
int numChars = length;
if (usesUnicode()) {
start /= 2;
length /= 2;
numChars = (length / 2);
}
int myStart = getStart();
int myEnd = getEnd();
int end = start + length;
int end = start + numChars;
/* do we have to delete from this text piece? */
if (start <= myEnd && end >= myStart) {
@ -108,9 +111,14 @@ public class TextPiece extends PropertyNode implements Comparable
int overlapStart = Math.max(myStart, start);
int overlapEnd = Math.min(myEnd, end);
((StringBuffer)_buf).delete(overlapStart, overlapEnd);
super.adjustForDelete(start, length);
}
// We need to invoke this even if text from this piece is not being
// deleted because the adjustment must propagate to all subsequent
// text pieces i.e., if text from tp[n] is being deleted, then
// tp[n + 1], tp[n + 2], etc. will need to be adjusted.
// The superclass is expected to use a separate sentry for this.
super.adjustForDelete(start, length);
}
public int characterLength()

View File

@ -101,7 +101,14 @@ public class SprmOperation
case 3:
return LittleEndian.getInt(_grpprl, _gOffset);
case 6:
throw new UnsupportedOperationException("This SPRM contains a variable length operand");
byte operandLength = _grpprl[_gOffset + 1]; //surely shorter than an int...
byte [] codeBytes = new byte[LittleEndian.INT_SIZE]; //initialized to zeros by JVM
for(int i = 0; i < operandLength; i++)
if(_gOffset + i < _grpprl.length)
codeBytes[i] = _grpprl[_gOffset + 1 + i];
return LittleEndian.getInt(codeBytes, 0);
case 7:
byte threeByteInt[] = new byte[4];
threeByteInt[0] = _grpprl[_gOffset];

View File

@ -333,7 +333,7 @@ public class Range
_doc.getCharacterTable().adjustForInsert(_charStart, adjustedLength);
_doc.getParagraphTable().adjustForInsert(_parStart, adjustedLength);
_doc.getSectionTable().adjustForInsert(_sectionStart, adjustedLength);
adjustForInsert(text.length());
adjustForInsert(adjustedLength);
// update the FIB.CCPText field
adjustFIB(text.length());
@ -656,8 +656,15 @@ public class Range
);
}
// this Range isn't a proper parent of the subRange() so we'll have to keep
// track of an updated endOffset on our own
int previousEndOffset = subRange.getEndOffset();
subRange.insertBefore(pValue);
if (subRange.getEndOffset() != previousEndOffset)
_end += (subRange.getEndOffset() - previousEndOffset);
// re-create the sub-range so we can delete it
subRange = new Range(
(absPlaceHolderIndex + pValue.length()),
@ -671,9 +678,30 @@ public class Range
(pValue.length() * 2)), getDocument()
);
// deletes are automagically propagated
subRange.delete();
}
/**
* Replace (all instances of) a piece of text with another...
*
* @param pPlaceHolder The text to be replaced (e.g., "${organization}")
* @param pValue The replacement text (e.g., "Apache Software Foundation")
*/
public void replaceText(String pPlaceHolder, String pValue)
{
boolean keepLooking = true;
while (keepLooking) {
String text = text();
int offset = text.indexOf(pPlaceHolder);
if (offset >= 0)
replaceText(pPlaceHolder, pValue, offset);
else
keepLooking = false;
}
}
/**
* Gets the character run at index. The index is relative to this range.
*
@ -915,7 +943,7 @@ public class Range
/**
* adjust this range after an insert happens.
* @param length the length to adjust for
* @param length the length to adjust for (expected to be a count of code-points, not necessarily chars)
*/
private void adjustForInsert(int length)
{

View File

@ -0,0 +1,196 @@
/* ====================================================================
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.hwpf.usermodel;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.List;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.PicturesTable;
import org.apache.poi.hwpf.usermodel.Picture;
import junit.framework.TestCase;
/**
* Test to see if Range.delete() works even if the Range contains a
* CharacterRun that uses Unicode characters.
*/
public class TestRangeDelete extends TestCase {
// u201c and u201d are "smart-quotes"
private String originalText =
"It is used to confirm that text delete works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present. Everybody should be thankful to the ${organization} ${delete} and all the POI contributors for their assistance in this matter.\r";
private String searchText = "${delete}";
private String expectedText1 = " This is an MS-Word 97 formatted document created using NeoOffice v. 2.2.4 Patch 0 (OpenOffice.org v. 2.2.1).\r";
private String expectedText2 =
"It is used to confirm that text delete works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present. Everybody should be thankful to the ${organization} and all the POI contributors for their assistance in this matter.\r";
private String expectedText3 = "Thank you, ${organization} !\r";
private String illustrativeDocFile;
protected void setUp() throws Exception {
String dirname = System.getProperty("HWPF.testdata.path");
illustrativeDocFile = dirname + "/testRangeDelete.doc";
}
/**
* Test just opening the files
*/
public void testOpen() throws Exception {
HWPFDocument docA = new HWPFDocument(new FileInputStream(illustrativeDocFile));
}
/**
* Test (more "confirm" than test) that we have the general structure that we expect to have.
*/
public void testDocStructure() throws Exception {
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
Range range = daDoc.getRange();
assertEquals(1, range.numSections());
Section section = range.getSection(0);
assertEquals(5, section.numParagraphs());
Paragraph para = section.getParagraph(2);
assertEquals(5, para.numCharacterRuns());
assertEquals(originalText, para.text());
}
/**
* Test that we can delete text (one instance) from our Range with Unicode text.
*/
public void testRangeDeleteOne() throws Exception {
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
Range range = daDoc.getRange();
assertEquals(1, range.numSections());
Section section = range.getSection(0);
assertEquals(5, section.numParagraphs());
Paragraph para = section.getParagraph(2);
String text = para.text();
assertEquals(originalText, text);
int offset = text.indexOf(searchText);
assertEquals(192, offset);
int absOffset = para.getStartOffset() + offset;
if (para.usesUnicode())
absOffset = para.getStartOffset() + (offset * 2);
Range subRange = new Range(absOffset, (absOffset + searchText.length()), para.getDocument());
if (subRange.usesUnicode())
subRange = new Range(absOffset, (absOffset + (searchText.length() * 2)), para.getDocument());
assertEquals(searchText, subRange.text());
subRange.delete();
// we need to let the model re-calculate the Range before we evaluate it
range = daDoc.getRange();
assertEquals(1, range.numSections());
section = range.getSection(0);
assertEquals(5, section.numParagraphs());
para = section.getParagraph(2);
text = para.text();
assertEquals(expectedText2, text);
// this can lead to a StringBufferOutOfBoundsException, so we will add it
// even though we don't have an assertion for it
Range daRange = daDoc.getRange();
daRange.text();
}
/**
* Test that we can delete text (all instances of) from our Range with Unicode text.
*/
public void testRangeDeleteAll() throws Exception {
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
Range range = daDoc.getRange();
assertEquals(1, range.numSections());
Section section = range.getSection(0);
assertEquals(5, section.numParagraphs());
Paragraph para = section.getParagraph(2);
String text = para.text();
assertEquals(originalText, text);
boolean keepLooking = true;
while (keepLooking) {
int offset = range.text().indexOf(searchText);
if (offset >= 0) {
int absOffset = range.getStartOffset() + offset;
if (range.usesUnicode())
absOffset = range.getStartOffset() + (offset * 2);
Range subRange = new Range(
absOffset, (absOffset + searchText.length()), range.getDocument());
if (subRange.usesUnicode())
subRange = new Range(
absOffset, (absOffset + (searchText.length() * 2)), range.getDocument());
assertEquals(searchText, subRange.text());
subRange.delete();
} else
keepLooking = false;
}
// we need to let the model re-calculate the Range before we use it
range = daDoc.getRange();
assertEquals(1, range.numSections());
section = range.getSection(0);
assertEquals(5, section.numParagraphs());
para = section.getParagraph(1);
text = para.text();
assertEquals(expectedText1, text);
para = section.getParagraph(2);
text = para.text();
assertEquals(expectedText2, text);
para = section.getParagraph(3);
text = para.text();
assertEquals(expectedText3, text);
}
}

View File

@ -39,8 +39,9 @@ public class TestRangeReplacement extends TestCase {
"It is used to confirm that text replacement works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present. Everybody should be thankful to the ${organization} and all the POI contributors for their assistance in this matter.\r";
private String searchText = "${organization}";
private String replacementText = "Apache Software Foundation";
private String expectedText =
private String expectedText2 =
"It is used to confirm that text replacement works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present. Everybody should be thankful to the Apache Software Foundation and all the POI contributors for their assistance in this matter.\r";
private String expectedText3 = "Thank you, Apache Software Foundation!\r";
private String illustrativeDocFile;
@ -84,7 +85,7 @@ public class TestRangeReplacement extends TestCase {
/**
* Test that we can replace text in our Range with Unicode text.
*/
public void testRangeReplacement() throws Exception {
public void testRangeReplacementOne() throws Exception {
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
@ -104,16 +105,46 @@ public class TestRangeReplacement extends TestCase {
para.replaceText(searchText, replacementText, offset);
// we need to let the model re-calculate the Range before we evaluate it
range = daDoc.getRange();
assertEquals(1, range.numSections());
section = range.getSection(0);
assertEquals(5, section.numParagraphs());
assertEquals(4, section.numParagraphs());
para = section.getParagraph(2);
text = para.text();
assertEquals(expectedText, text);
assertEquals(expectedText2, text);
}
/**
* Test that we can replace text in our Range with Unicode text.
*/
public void testRangeReplacementAll() throws Exception {
HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
Range range = daDoc.getRange();
assertEquals(1, range.numSections());
Section section = range.getSection(0);
assertEquals(5, section.numParagraphs());
Paragraph para = section.getParagraph(2);
String text = para.text();
assertEquals(originalText, text);
range.replaceText(searchText, replacementText);
assertEquals(1, range.numSections());
section = range.getSection(0);
assertEquals(5, section.numParagraphs());
para = section.getParagraph(2);
text = para.text();
assertEquals(expectedText2, text);
para = section.getParagraph(3);
text = para.text();
assertEquals(expectedText3, text);
}
}

Binary file not shown.

View File

@ -0,0 +1,160 @@
/* ====================================================================
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.eventusermodel;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.SheetReferences;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
* Tests for {@link EventWorkbookBuilder}
*/
public final class TestEventWorkbookBuilder extends TestCase {
private MockHSSFListener mockListen;
private SheetRecordCollectingListener listener;
public void setUp() {
HSSFRequest req = new HSSFRequest();
mockListen = new MockHSSFListener();
listener = new SheetRecordCollectingListener(mockListen);
req.addListenerForAllRecords(listener);
HSSFEventFactory factory = new HSSFEventFactory();
try {
InputStream is = HSSFTestDataSamples.openSampleFileStream("3dFormulas.xls");
POIFSFileSystem fs = new POIFSFileSystem(is);
factory.processWorkbookEvents(req, fs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void testBasics() throws Exception {
assertNotNull(listener.getSSTRecord());
assertNotNull(listener.getBoundSheetRecords());
assertNotNull(listener.getExternSheetRecords());
}
public void testGetStubWorkbooks() throws Exception {
assertNotNull(listener.getStubWorkbook());
assertNotNull(listener.getStubHSSFWorkbook());
assertNotNull(listener.getStubWorkbook().getSheetReferences());
assertNotNull(listener.getStubHSSFWorkbook().getSheetReferences());
}
public void testContents() throws Exception {
assertEquals(2, listener.getSSTRecord().getNumStrings());
assertEquals(3, listener.getBoundSheetRecords().length);
assertEquals(1, listener.getExternSheetRecords().length);
assertEquals(3, listener.getStubWorkbook().getNumSheets());
SheetReferences ref = listener.getStubWorkbook().getSheetReferences();
assertEquals("Sh3", ref.getSheetName(0));
assertEquals("Sheet1", ref.getSheetName(1));
assertEquals("S2", ref.getSheetName(2));
}
public void testFormulas() throws Exception {
FormulaRecord fr;
// Check our formula records
assertEquals(6, mockListen._frecs.size());
Workbook stubWB = listener.getStubWorkbook();
assertNotNull(stubWB);
HSSFWorkbook stubHSSF = listener.getStubHSSFWorkbook();
assertNotNull(stubHSSF);
// Check these stubs have the right stuff on them
assertEquals("Sheet1", stubWB.getSheetName(0));
assertEquals("S2", stubWB.getSheetName(1));
assertEquals("Sh3", stubWB.getSheetName(2));
// Check we can get the formula without breaking
for(int i=0; i<mockListen._frecs.size(); i++) {
fr = (FormulaRecord)mockListen._frecs.get(i);
FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression());
}
// Peer into just one formula, and check that
// all the ptgs give back the right things
List ptgs = ((FormulaRecord)mockListen._frecs.get(0)).getParsedExpression();
assertEquals(1, ptgs.size());
assertTrue(ptgs.get(0) instanceof Ref3DPtg);
Ref3DPtg ptg = (Ref3DPtg)ptgs.get(0);
assertEquals("Sheet1!A1", ptg.toFormulaString(stubHSSF));
// Now check we get the right formula back for
// a few sample ones
// Sheet 1 A2 is on same sheet
fr = (FormulaRecord)mockListen._frecs.get(0);
assertEquals(1, fr.getRow());
assertEquals(0, fr.getColumn());
assertEquals("Sheet1!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
// Sheet 1 A5 is to another sheet
fr = (FormulaRecord)mockListen._frecs.get(3);
assertEquals(4, fr.getRow());
assertEquals(0, fr.getColumn());
assertEquals("'S2'!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
// Sheet 1 A7 is to another sheet, range
fr = (FormulaRecord)mockListen._frecs.get(5);
assertEquals(6, fr.getRow());
assertEquals(0, fr.getColumn());
assertEquals("SUM('Sh3'!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
// Now, load via Usermodel and re-check
InputStream is = HSSFTestDataSamples.openSampleFileStream("3dFormulas.xls");
POIFSFileSystem fs = new POIFSFileSystem(is);
HSSFWorkbook wb = new HSSFWorkbook(fs);
assertEquals("Sheet1!A1", wb.getSheetAt(0).getRow(1).getCell(0).getCellFormula());
assertEquals("SUM('Sh3'!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
}
private static final class MockHSSFListener implements HSSFListener {
public MockHSSFListener() {}
private final List _records = new ArrayList();
private final List _frecs = new ArrayList();
public void processRecord(Record record) {
_records.add(record);
if(record instanceof FormulaRecord) {
_frecs.add(record);
}
}
}
}

View File

@ -16,8 +16,6 @@
==================================================================== */
package org.apache.poi.hssf.eventusermodel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@ -29,6 +27,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord;
@ -40,8 +39,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
private Record[] r;
public void setUp() {
public void openNormal() {
HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
@ -55,10 +53,31 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
} catch (IOException e) {
throw new RuntimeException(e);
}
r = mockListen.getRecords();
assertTrue(r.length > 100);
}
public void openAlt() {
HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
req.addListenerForAllRecords(listener);
HSSFEventFactory factory = new HSSFEventFactory();
try {
InputStream is = HSSFTestDataSamples.openSampleFileStream("MRExtraLines.xls");
POIFSFileSystem fs = new POIFSFileSystem(is);
factory.processWorkbookEvents(req, fs);
} catch (IOException e) {
throw new RuntimeException(e);
}
r = mockListen.getRecords();
assertTrue(r.length > 100);
}
public void testMissingRowRecords() throws Exception {
openNormal();
// We have rows 0, 1, 2, 20 and 21
int row0 = -1;
@ -108,6 +127,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
}
public void testEndOfRowRecords() throws Exception {
openNormal();
// Find the cell at 0,0
int cell00 = -1;
@ -194,7 +214,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
assertTrue(r[cell00+57] instanceof LastCellOfRowDummyRecord);
// Check the numbers of the last seen columns
LastCellOfRowDummyRecord[] lrs = new LastCellOfRowDummyRecord[23];
LastCellOfRowDummyRecord[] lrs = new LastCellOfRowDummyRecord[24];
int lrscount = 0;
for(int i=0; i<r.length; i++) {
if(r[i] instanceof LastCellOfRowDummyRecord) {
@ -229,6 +249,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
public void testMissingCellRecords() throws Exception {
openNormal();
// Find the cell at 0,0
int cell00 = -1;
@ -327,9 +348,37 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
assertEquals(10, mc.getColumn());
}
// Make sure we don't put in any extra new lines
// that aren't already there
public void testNoExtraNewLines() throws Exception {
// Load a different file
openAlt();
// This file has has something in lines 1-33
List lcor = new ArrayList();
for(int i=0; i<r.length; i++) {
if(r[i] instanceof LastCellOfRowDummyRecord)
lcor.add( (LastCellOfRowDummyRecord)r[i] );
}
// Check we got the 33 rows
assertEquals(33, lcor.size());
LastCellOfRowDummyRecord[] rowEnds = (LastCellOfRowDummyRecord[])
lcor.toArray(new LastCellOfRowDummyRecord[lcor.size()]);
assertEquals(33, rowEnds.length);
// And check they have the right stuff in them,
// no repeats
for(int i=0; i<rowEnds.length; i++) {
assertEquals(i, rowEnds[i].getRow());
}
}
private static final class MockHSSFListener implements HSSFListener {
public MockHSSFListener() {}
private final List _records = new ArrayList();
private boolean logToStdOut = false;
public void processRecord(Record record) {
_records.add(record);
@ -346,9 +395,20 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
LastCellOfRowDummyRecord lc = (LastCellOfRowDummyRecord)record;
log("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
}
if(record instanceof BOFRecord) {
BOFRecord r = (BOFRecord)record;
if(r.getType() == BOFRecord.TYPE_WORKSHEET) {
log("On new sheet");
}
}
if(record instanceof RowRecord) {
RowRecord rr = (RowRecord)record;
log("Starting row #" + rr.getRowNumber());
}
}
private static void log(String msg) {
if(false) { // successful tests should be quiet
private void log(String msg) {
if(logToStdOut) {
System.out.println(msg);
}
}

View File

@ -759,7 +759,7 @@ public final class TestFormulaParser extends TestCase {
}
}
public void testParseErrorExpecteMsg() {
public void testParseErrorExpectedMsg() {
try {
parseFormula("round(3.14;2)");
@ -768,4 +768,27 @@ public final class TestFormulaParser extends TestCase {
assertEquals("Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'", e.getMessage());
}
}
/**
* this function name has a dot in it.
*/
public void testParseErrorTypeFunction() {
Ptg[] ptgs;
try {
ptgs = parseFormula("error.type(A1)");
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("Invalid Formula cell reference: 'error'")) {
throw new AssertionFailedError("Identified bug 45334");
}
throw e;
}
assertEquals(2, ptgs.length);
assertEquals(FuncPtg.class, ptgs[1].getClass());
FuncPtg funcPtg = (FuncPtg) ptgs[1];
assertEquals("ERROR.TYPE", funcPtg.getName());
}
}

View File

@ -1052,4 +1052,88 @@ public final class TestBugs extends TestCase {
assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
}
}
/**
* Test that fonts get added properly
*/
public void test45338() throws Exception {
HSSFWorkbook wb = new HSSFWorkbook();
assertEquals(4, wb.getNumberOfFonts());
HSSFSheet s = wb.createSheet();
s.createRow(0);
s.createRow(1);
HSSFCell c1 = s.getRow(0).createCell((short)0);
HSSFCell c2 = s.getRow(1).createCell((short)0);
assertEquals(4, wb.getNumberOfFonts());
HSSFFont f1 = wb.getFontAt((short)0);
assertEquals(400, f1.getBoldweight());
// Check that asking for the same font
// multiple times gives you the same thing.
// Otherwise, our tests wouldn't work!
assertEquals(
wb.getFontAt((short)0),
wb.getFontAt((short)0)
);
assertEquals(
wb.getFontAt((short)2),
wb.getFontAt((short)2)
);
assertTrue(
wb.getFontAt((short)0)
!=
wb.getFontAt((short)2)
);
// Look for a new font we have
// yet to add
assertNull(
wb.findFont(
(short)11, (short)123, (short)22,
"Thingy", false, true, (short)2, (byte)2
)
);
HSSFFont nf = wb.createFont();
assertEquals(5, wb.getNumberOfFonts());
assertEquals(5, nf.getIndex());
assertEquals(nf, wb.getFontAt((short)5));
nf.setBoldweight((short)11);
nf.setColor((short)123);
nf.setFontHeight((short)22);
nf.setFontName("Thingy");
nf.setItalic(false);
nf.setStrikeout(true);
nf.setTypeOffset((short)2);
nf.setUnderline((byte)2);
assertEquals(5, wb.getNumberOfFonts());
assertEquals(nf, wb.getFontAt((short)5));
// Find it now
assertNotNull(
wb.findFont(
(short)11, (short)123, (short)22,
"Thingy", false, true, (short)2, (byte)2
)
);
assertEquals(
5,
wb.findFont(
(short)11, (short)123, (short)22,
"Thingy", false, true, (short)2, (byte)2
).getIndex()
);
assertEquals(nf,
wb.findFont(
(short)11, (short)123, (short)22,
"Thingy", false, true, (short)2, (byte)2
)
);
}
}

View File

@ -0,0 +1,52 @@
/* ====================================================================
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.util;
import java.util.Hashtable;
import junit.framework.TestCase;
public final class TestHSSFColor extends TestCase {
public void testBasics() {
assertNotNull(HSSFColor.YELLOW.class);
assertTrue(HSSFColor.YELLOW.index > 0);
assertTrue(HSSFColor.YELLOW.index2 > 0);
}
public void testContents() {
assertEquals(3, HSSFColor.YELLOW.triplet.length);
assertEquals(255, HSSFColor.YELLOW.triplet[0]);
assertEquals(255, HSSFColor.YELLOW.triplet[1]);
assertEquals(0, HSSFColor.YELLOW.triplet[2]);
assertEquals("FFFF:FFFF:0", HSSFColor.YELLOW.hexString);
}
public void testTrippletHash() {
Hashtable tripplets = HSSFColor.getTripletHash();
assertEquals(
HSSFColor.MAROON.class,
tripplets.get(HSSFColor.MAROON.hexString).getClass()
);
assertEquals(
HSSFColor.YELLOW.class,
tripplets.get(HSSFColor.YELLOW.hexString).getClass()
);
}
}