From 3e71a7fdc3dffabb76295343cabe9ae07fce629f Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sun, 13 Apr 2008 15:25:33 +0000 Subject: [PATCH] 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-647579 via svnmerge from https://svn.apache.org:443/repos/asf/poi/trunk ........ r647278 | josh | 2008-04-11 20:36:37 +0100 (Fri, 11 Apr 2008) | 1 line fixed typo and formatting in class javadoc ........ r647567 | nick | 2008-04-13 14:16:36 +0100 (Sun, 13 Apr 2008) | 1 line Various new bits of documentation on embeded files and text extraction ........ r647574 | nick | 2008-04-13 15:58:27 +0100 (Sun, 13 Apr 2008) | 1 line Start on a eventusermodel based excel text extractor ........ r647576 | nick | 2008-04-13 16:09:42 +0100 (Sun, 13 Apr 2008) | 1 line Finish off eventusermodel based Excel Extractor, and update the xls to csv converter (moved to correct place) based on discoveries for the text extractor ........ r647577 | nick | 2008-04-13 16:13:17 +0100 (Sun, 13 Apr 2008) | 1 line Add information of EventBasedExcelExtractor to the documentation ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@647580 13f79535-47bb-0310-9956-ffa450edef68 --- KEYS | 316 ++++++++++++------ src/documentation/content/xdocs/book.xml | 1 + .../content/xdocs/poifs/book.xml | 1 + .../content/xdocs/poifs/embeded.xml | 95 ++++++ .../content/xdocs/text-extraction.xml | 113 +++++++ .../eventusermodel/examples/XLS2CSVmra.java | 40 ++- .../extractor/EventBasedExcelExtractor.java | 276 +++++++++++++++ .../record/formula/functions/Hlookup.java | 6 +- .../record/formula/functions/Vlookup.java | 4 +- .../hssf/extractor/TestExcelExtractor.java | 44 +++ 10 files changed, 782 insertions(+), 114 deletions(-) create mode 100644 src/documentation/content/xdocs/poifs/embeded.xml create mode 100644 src/documentation/content/xdocs/text-extraction.xml rename src/{scratchpad => }/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java (90%) create mode 100644 src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java diff --git a/KEYS b/KEYS index 05e7a8dbe..b55d9e1dd 100644 --- a/KEYS +++ b/KEYS @@ -48,7 +48,7 @@ uid Nick Burch sub 1024g/5E0E1748 2004-03-24 [expires: 2009-03-23] -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.4.6 (GNU/Linux) +Version: GnuPG v1.4.7 (GNU/Linux) mQGiBEBhnKkRBACOPYQoULwS4a37UsNiinwqJ+2XVMal5FQyMOc6hNneyvdau4cO oSNgdEkNtAN9fecSh06wke9mFBR9iVD4QpCqiRk8s20lr4QAFw4urxbe6iOf/Ytr @@ -58,104 +58,222 @@ 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 -vuh58AIoldgrDIeIJA== -=xCFv +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 +=j3Fb -----END PGP PUBLIC KEY BLOCK----- diff --git a/src/documentation/content/xdocs/book.xml b/src/documentation/content/xdocs/book.xml index d8cffad4e..a55b67bad 100644 --- a/src/documentation/content/xdocs/book.xml +++ b/src/documentation/content/xdocs/book.xml @@ -43,6 +43,7 @@ + diff --git a/src/documentation/content/xdocs/poifs/book.xml b/src/documentation/content/xdocs/poifs/book.xml index c96a71eee..d2fc4a3c7 100644 --- a/src/documentation/content/xdocs/poifs/book.xml +++ b/src/documentation/content/xdocs/poifs/book.xml @@ -30,6 +30,7 @@ + diff --git a/src/documentation/content/xdocs/poifs/embeded.xml b/src/documentation/content/xdocs/poifs/embeded.xml new file mode 100644 index 000000000..d888e2ed5 --- /dev/null +++ b/src/documentation/content/xdocs/poifs/embeded.xml @@ -0,0 +1,95 @@ + + + + +
+ Apache POI - POIFS - Documents embeded in other documents + Overview + + + + +
+ +
Overview +

It is possible for one OLE 2 based document to have other + OLE 2 documents embeded in it. For example, and Excel file + may have a word document and a powerpoint slideshow + embeded as part of it.

+

Normally, these other documents are stored in subdirectories + of the OLE 2 (POIFS) filesystem. The exact location of the + embeded documents will vary depending on the type of the + master document, and the exact directory names will differ + each time. To figure out exactly which directory to look + in, you will either need to process the appropriate OLE 2 + linking entry in the master document, or simple iterate + over all the directories in the filesystem.

+

As a general rule, you will find the same OLE 2 entries + in the subdirectories, as you would've found at the root + of the filesystem were a document to not be embeded.

+ +
Files embeded in Excel +

Excel normally stores embeded files in subdirectories + of the filesystem root. Typically these subdirectories + are named starting with MBD, with 8 hex characters following.

+
+ +
Files embeded in Word +

Word normally stores embeded files in subdirectories + of the ObjectPool directory, itself a subdirectory of the + filesystem root. Typically these subdirectories and named + starting with an underscore, followed by 10 numbers.

+
+ +
Files embeded in PowerPoint +

PowerPoint does not normally store embeded files + in the OLE2 layer. Instead, they are held within records + of the main PowerPoint file. To get at them, you need to + find the appropriate data within the PowerPoint stream, + and work from that.

+
+
+ +
Listing POIFS contents +

POIFS provides a simple tool for listing the contents of + OLE2 files. This can allow you to see what your POIFS file + contents, and hence if it has any embeded documents in it, + and where.

+

The tool to use is org.apache.poi.poifs.dev.POIFSLister. + This tool may be run from the command line, and takes a filename + as its parameter. It will print out all the directories and + files contained within the POIFS file.

+
+ +
Opening embeded files +

All of the POIDocument classes (HSSFWorkbook, HSLFSlideShow, + HWPFDocument and HDGFDiagram) can either be opened from + a POIFSFileSystem, or from a specific directory within a + POIFSFileSystem. So, to open embeded files, simply locate the + appropriate DirectoryNode that represents the subdirectory + of interest, and pass this + the overall POIFSFileSystem to + the constructor.

+

I you want to extract the textual contents of the embeded file, + then open the appropriate POIDocument, and then pass this to + the extractor class, instead of simply passing the POIFSFilesystem + to the extractor.

+
+ +
diff --git a/src/documentation/content/xdocs/text-extraction.xml b/src/documentation/content/xdocs/text-extraction.xml new file mode 100644 index 000000000..d71a0bf10 --- /dev/null +++ b/src/documentation/content/xdocs/text-extraction.xml @@ -0,0 +1,113 @@ + + + + + +
+ POI Text Extraction + + + +
+ + +
Overview +

POI provides text extraction for all the supported file + formats. In addition, it provides access to the metadata + associated with a given file, such as title and author.

+

In addition to providing direct text extraction classes, + POI works closely with the + Apache Tika + text extraction library. Users may wish to simply utilise + the functionality provided by Tika.

+
+ +
Common functionality +

All of the POI text extractors extend from + org.apache.poi.POITextExtractor. This provides a common + method across all extractors, getText(). For many cases, the text + returned will be all you need. However, many extractors do provide + more targetted text extraction methods, so you may wish to use + these in some cases.

+

All POIFS / OLE 2 based text extractors also extend from + org.apache.poi.POIOLE2TextExtractor. This additionally + provides common methods to get at the HPFS + document metadata.

+

All OOXML based text extractors (available in POI 3.5 and later) + also extend from + org.apache.poi.POIOOXMLTextExtractor. This additionally + provides common methods to get at the OOXML metadata.

+
+ +
Text Extractor Factory - POI 3.5 or later +

A new class in POI 3.5, + org.apache.poi.extractor.ExtractorFactory provides a + similar function to WorkbookFactory. You simply pass it an + InputStream, a file, a POIFSFileSystem or a OOXML Package. It + figures out the correct text extractor for you, and returns it.

+
+ +
Excel +

For .xls files, there is + org.apache.poi.hssf.extractor.ExcelExtractor, which will + return text, optionally with formulas instead of their contents. + Those using POI 3.5 can also use + org.apache.poi.xssf.extractor.XSSFExcelExtractor, to perform + a similar task for .xlsx files.

+

In addition, there is a second text extractor for .xls files, + org.apache.poi.hssf.extractor.EventBasedExcelExtractor. This + is based on the streaming EventUserModel code, and will generally + deliver a lower memory footprint for extraction. However, it will + have problems correctly outputting more complex formulas, as it + works with records as they pass, and so doesn't have access to all + parts of complex and shared formulas.

+
+ +
Word +

For .doc files, in scratchpad there is + org.apache.poi.hwpf.extractor.WordExtractor, which will + return text for your document. Those using POI 3.5 can also use + org.apache.poi.xwpf.extractor.XPFFWordExtractor, to perform + a similar task for .docx files.

+
+ +
PowerPoint +

For .ppt files, in scratchpad there is + org.apache.poi.hslf.extractor.PowerPointExtractor, which + will return text for your slideshow, optionally restricted to just + slides text or notes text. Those using POI 3.5 can also use + org.apache.poi.xslf.extractor.XSLFPowerPointExtractor, to + perform a similar task for .pptx files.

+
+ +
Visio +

For .vsd files, in scratchpad there is + org.apache.poi.hdgf.extractor.VisioTextExtractor, which + will return text for your file.

+
+ + +
+ + Copyright 2005 The Apache Software Foundation or its licensors, as applicable. + $Revision: 639487 $ $Date: 2008-03-20 22:31:15 +0000 (Thu, 20 Mar 2008) $ + +
+
diff --git a/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java b/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java similarity index 90% rename from src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java rename to src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java index 1c53f1ecc..9bebd3a83 100644 --- a/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java +++ b/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java @@ -23,11 +23,7 @@ import java.io.PrintStream; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; import org.apache.poi.hssf.eventusermodel.HSSFEventFactory; @@ -36,11 +32,10 @@ import org.apache.poi.hssf.eventusermodel.HSSFRequest; import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener; 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.BlankRecord; import org.apache.poi.hssf.record.BoolErrRecord; import org.apache.poi.hssf.record.CellValueRecordInterface; -import org.apache.poi.hssf.record.ExtendedFormatRecord; -import org.apache.poi.hssf.record.FormatRecord; import org.apache.poi.hssf.record.FormulaRecord; import org.apache.poi.hssf.record.LabelRecord; import org.apache.poi.hssf.record.LabelSSTRecord; @@ -49,7 +44,7 @@ import org.apache.poi.hssf.record.NumberRecord; import org.apache.poi.hssf.record.RKRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.SSTRecord; -import org.apache.poi.hssf.usermodel.HSSFDataFormat; +import org.apache.poi.hssf.record.StringRecord; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -72,6 +67,11 @@ public class XLS2CSVmra implements HSSFListener { // Records we pick up as we process private SSTRecord sstRecord; private FormatTrackingHSSFListener formatListener; + + // For handling formulas with string results + private int nextRow; + private int nextColumn; + private boolean outputNextStringRecord; /** * Creates a new XLS -> CSV converter @@ -142,6 +142,7 @@ public class XLS2CSVmra implements HSSFListener { thisColumn = berec.getColumn(); thisStr = ""; break; + case FormulaRecord.sid: FormulaRecord frec = (FormulaRecord) record; @@ -149,12 +150,31 @@ public class XLS2CSVmra implements HSSFListener { thisColumn = frec.getColumn(); if(outputFormulaValues) { - thisStr = formatNumberDateCell(frec, frec.getValue()); + if(Double.isNaN( frec.getValue() )) { + // Formula result is a string + // This is stored in the next record + outputNextStringRecord = true; + nextRow = frec.getRow(); + nextColumn = frec.getColumn(); + } else { + thisStr = formatNumberDateCell(frec, frec.getValue()); + } } else { - // TODO: Output the formula string - thisStr = '"' + frec.toString() + '"'; + thisStr = '"' + + FormulaParser.toFormulaString(null, frec.getParsedExpression()) + '"'; } break; + case StringRecord.sid: + if(outputNextStringRecord) { + // String for formula + StringRecord srec = (StringRecord)record; + thisStr = srec.getString(); + thisRow = nextRow; + thisColumn = nextColumn; + outputNextStringRecord = false; + } + break; + case LabelRecord.sid: LabelRecord lrec = (LabelRecord) record; diff --git a/src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java b/src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java new file mode 100644 index 000000000..8f3eebb2d --- /dev/null +++ b/src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java @@ -0,0 +1,276 @@ +/* ==================================================================== + 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.extractor; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.apache.poi.POIOLE2TextExtractor; +import org.apache.poi.hpsf.DocumentSummaryInformation; +import org.apache.poi.hpsf.SummaryInformation; +import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; +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.model.FormulaParser; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.CellValueRecordInterface; +import org.apache.poi.hssf.record.FormulaRecord; +import org.apache.poi.hssf.record.LabelRecord; +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.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.poifs.filesystem.POIFSFileSystem; + +/** + * A text extractor for Excel files, that is based + * on the hssf eventusermodel api. + * It will typically use less memory than + * {@link ExcelExtractor}, but may not provide + * the same richness of formatting. + * Returns the textual content of the file, suitable for + * indexing by something like Lucene, but not really + * intended for display to the user. + * To turn an excel file into a CSV or similar, then see + * the XLS2CSVmra example + * @see org.apache.poi.hssf.eventusermodel.examples.XLS2CSVmra + */ +public class EventBasedExcelExtractor extends POIOLE2TextExtractor { + private POIFSFileSystem fs; + private boolean includeSheetNames = true; + private boolean formulasNotResults = false; + + public EventBasedExcelExtractor(POIFSFileSystem fs) throws IOException { + super(null); + this.fs = fs; + } + + /** + * Would return the document information metadata for the document, + * if we supported it + */ + public DocumentSummaryInformation getDocSummaryInformation() { + throw new IllegalStateException("Metadata extraction not supported in streaming mode, please use ExcelExtractor"); + } + /** + * Would return the summary information metadata for the document, + * if we supported it + */ + public SummaryInformation getSummaryInformation() { + throw new IllegalStateException("Metadata extraction not supported in streaming mode, please use ExcelExtractor"); + } + + + /** + * Should sheet names be included? Default is true + */ + public void setIncludeSheetNames(boolean includeSheetNames) { + this.includeSheetNames = includeSheetNames; + } + /** + * Should we return the formula itself, and not + * the result it produces? Default is false + */ + public void setFormulasNotResults(boolean formulasNotResults) { + this.formulasNotResults = formulasNotResults; + } + + + /** + * Retreives the text contents of the file + */ + public String getText() { + String text = null; + try { + TextListener tl = triggerExtraction(); + + text = tl.text.toString(); + if(! text.endsWith("\n")) { + text = text + "\n"; + } + } catch(IOException e) { + throw new RuntimeException(e); + } + + return text; + } + + private TextListener triggerExtraction() throws IOException { + TextListener tl = new TextListener(); + FormatTrackingHSSFListener ft = new FormatTrackingHSSFListener(tl); + tl.ft = ft; + + // Register and process + HSSFEventFactory factory = new HSSFEventFactory(); + HSSFRequest request = new HSSFRequest(); + request.addListenerForAllRecords(ft); + + factory.processWorkbookEvents(request, fs); + + return tl; + } + + private class TextListener implements HSSFListener { + private FormatTrackingHSSFListener ft; + private SSTRecord sstRecord; + + private List sheetNames = new ArrayList(); + private StringBuffer text = new StringBuffer(); + private int sheetNum = -1; + private int rowNum; + + private boolean outputNextStringValue = false; + private int nextRow = -1; + + public void processRecord(Record record) { + String thisText = null; + int thisRow = -1; + + switch(record.getSid()) { + case BoundSheetRecord.sid: + BoundSheetRecord sr = (BoundSheetRecord)record; + sheetNames.add(sr.getSheetname()); + break; + case BOFRecord.sid: + BOFRecord bof = (BOFRecord)record; + if(bof.getType() == BOFRecord.TYPE_WORKSHEET) { + sheetNum++; + rowNum = -1; + + if(includeSheetNames) { + if(text.length() > 0) text.append("\n"); + text.append(sheetNames.get(sheetNum)); + } + } + break; + case SSTRecord.sid: + sstRecord = (SSTRecord)record; + break; + + case FormulaRecord.sid: + FormulaRecord frec = (FormulaRecord) record; + thisRow = frec.getRow(); + + if(formulasNotResults) { + thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression()); + } else { + if(Double.isNaN( frec.getValue() )) { + // Formula result is a string + // This is stored in the next record + outputNextStringValue = true; + nextRow = frec.getRow(); + } else { + thisText = formatNumberDateCell(frec, frec.getValue()); + } + } + break; + case StringRecord.sid: + if(outputNextStringValue) { + // String for formula + StringRecord srec = (StringRecord)record; + thisText = srec.getString(); + thisRow = nextRow; + outputNextStringValue = false; + } + break; + case LabelRecord.sid: + LabelRecord lrec = (LabelRecord) record; + thisRow = lrec.getRow(); + thisText = lrec.getValue(); + break; + case LabelSSTRecord.sid: + LabelSSTRecord lsrec = (LabelSSTRecord) record; + thisRow = lsrec.getRow(); + if(sstRecord == null) { + throw new IllegalStateException("No SST record found"); + } + thisText = sstRecord.getString(lsrec.getSSTIndex()).toString(); + break; + case NoteRecord.sid: + NoteRecord nrec = (NoteRecord) record; + thisRow = nrec.getRow(); + // TODO: Find object to match nrec.getShapeId() + break; + case NumberRecord.sid: + NumberRecord numrec = (NumberRecord) record; + thisRow = numrec.getRow(); + thisText = formatNumberDateCell(numrec, numrec.getValue()); + break; + default: + break; + } + + if(thisText != null) { + if(thisRow != rowNum) { + rowNum = thisRow; + if(text.length() > 0) + text.append("\n"); + } else { + text.append("\t"); + } + text.append(thisText); + } + } + + /** + * Formats a number or date cell, be that a real number, or the + * answer to a formula + */ + private String formatNumberDateCell(CellValueRecordInterface cell, double value) { + // Get the built in format, if there is one + int formatIndex = ft.getFormatIndex(cell); + String formatString = ft.getFormatString(cell); + + if(formatString == null) { + return Double.toString(value); + } else { + // Is it a date? + if(HSSFDateUtil.isADateFormat(formatIndex,formatString) && + HSSFDateUtil.isValidExcelDate(value)) { + // Java wants M not m for month + formatString = formatString.replace('m','M'); + // Change \- into -, if it's there + formatString = formatString.replaceAll("\\\\-","-"); + + // Format as a date + Date d = HSSFDateUtil.getJavaDate(value, false); + DateFormat df = new SimpleDateFormat(formatString); + return df.format(d); + } else { + if(formatString == "General") { + // Some sort of wierd default + return Double.toString(value); + } + + // Format as a number + DecimalFormat df = new DecimalFormat(formatString); + return df.format(value); + } + } + } + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java index 40ed1da49..d493cd533 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java @@ -25,15 +25,15 @@ import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector; /** - * Implementation of the VLOOKUP() function.

+ * Implementation of the HLOOKUP() function.

* - * HLOOKUP finds a column in a lookup table by the first row value and returns the value from another row. + * HLOOKUP finds a column in a lookup table by the first row value and returns the value from another row.
* * Syntax:
* HLOOKUP(lookup_value, table_array, row_index_num, range_lookup)

* * lookup_value The value to be found in the first column of the table array.
- * table_array An area reference for the lookup data.
+ * table_array An area reference for the lookup data.
* row_index_num a 1 based index specifying which row value of the lookup data will be returned.
* range_lookup If TRUE (default), HLOOKUP finds the largest value less than or equal to * the lookup_value. If FALSE, only exact matches will be considered
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java index 7d27491df..bd158b897 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java @@ -27,13 +27,13 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector; /** * Implementation of the VLOOKUP() function.

* - * VLOOKUP finds a row in a lookup table by the first column value and returns the value from another column. + * VLOOKUP finds a row in a lookup table by the first column value and returns the value from another column.
* * Syntax:
* VLOOKUP(lookup_value, table_array, col_index_num, range_lookup)

* * lookup_value The value to be found in the first column of the table array.
- * table_array An area reference for the lookup data.
+ * table_array An area reference for the lookup data.
* col_index_num a 1 based index specifying which column value of the lookup data will be returned.
* range_lookup If TRUE (default), VLOOKUP finds the largest value less than or equal to * the lookup_value. If FALSE, only exact matches will be considered
diff --git a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java index ad311eb27..63d67ee77 100644 --- a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java +++ b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java @@ -122,6 +122,50 @@ public final class TestExcelExtractor extends TestCase { assertEquals("Sheet1\nUPPER(\"xyz\")\nSheet2\nSheet3\n", extractor.getText()); } + + public void testEventExtractor() throws Exception { + EventBasedExcelExtractor extractor; + + // First up, a simple file with string + // based formulas in it + extractor = new EventBasedExcelExtractor( + new POIFSFileSystem( + HSSFTestDataSamples.openSampleFileStream("SimpleWithFormula.xls") + ) + ); + extractor.setIncludeSheetNames(true); + + String text = extractor.getText(); + assertEquals("Sheet1\nreplaceme\nreplaceme\nreplacemereplaceme\nSheet2\nSheet3\n", text); + + extractor.setIncludeSheetNames(false); + extractor.setFormulasNotResults(true); + + text = extractor.getText(); + assertEquals("replaceme\nreplaceme\nCONCATENATE(A1,A2)\n", text); + + + // Now, a slightly longer file with numeric formulas + extractor = new EventBasedExcelExtractor( + new POIFSFileSystem( + HSSFTestDataSamples.openSampleFileStream("sumifformula.xls") + ) + ); + extractor.setIncludeSheetNames(false); + extractor.setFormulasNotResults(true); + + text = extractor.getText(); + assertEquals( + "1000.0\t1.0\tSUMIF(A1:A5,\">4000\",B1:B5)\n" + + "2000.0\t2.0\n" + + "3000.0\t3.0\n" + + "4000.0\t4.0\n" + + "5000.0\t5.0\n", + text + ); + } + + /** * Embded in a non-excel file */