]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924...
authorNick Burch <nick@apache.org>
Sun, 13 Apr 2008 15:25:33 +0000 (15:25 +0000)
committerNick Burch <nick@apache.org>
Sun, 13 Apr 2008 15:25:33 +0000 (15:25 +0000)
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
src/documentation/content/xdocs/book.xml
src/documentation/content/xdocs/poifs/book.xml
src/documentation/content/xdocs/poifs/embeded.xml [new file with mode: 0644]
src/documentation/content/xdocs/text-extraction.xml [new file with mode: 0644]
src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java
src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java
src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java [deleted file]
src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java

diff --git a/KEYS b/KEYS
index 05e7a8dbe9abd0c63e09b5731bc35a611e82b0c8..b55d9e1ddf0391d30cbd0102e1f89afc5278a5a6 100644 (file)
--- a/KEYS
+++ b/KEYS
@@ -48,7 +48,7 @@ uid                  Nick Burch <nick@apache.org>
 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 @@ UtpUwP38GH81VCtbEWfEJpMD/jraHQdr4KXnv7iIvGtOLbODrmgfGfZYPsGoJlHl
 YlIvLz/S2WCZxKyNrIE86WK/DlMAXoHAJigErD+X1UYLNVb0+u5MeHN+FF0yLMS4
 9Cp4A/wJznIOc7sO+L6psNNUWSu1G9TAPo6QRu/6CTaYHMy8uMzovjrlYF9bjZFe
 9TVJUkHYMv2tsthZ7eWG0jAyHabvn1XgCDRjJcs10FZ6obIU5BDaJLjaxMU+u91k
-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
-vuh58AIoldgrDIeIJA==
-=xCFv
+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
+=j3Fb
 -----END PGP PUBLIC KEY BLOCK-----
 
 
index d8cffad4eedd50313e0ca649b1ea15c112739c9e..a55b67bad07a51f1da60e4c5aa0e4acce4505de7 100644 (file)
@@ -43,6 +43,7 @@
         <menu-item label="HDGF" href="hdgf/index.html"/>
                <menu-item label="POI-Ruby" href="poi-ruby.html"/>
         <menu-item label="POI-Utils" href="utils/index.html"/>
+        <menu-item label="Text Extraction" href="text-extraction.html"/>
         <menu-item label="Download" href="ext:download"/>
     </menu>
 
index c96a71eeef07d8073779f400239fdcdbc892ac6e..d2fc4a3c78c22936a5f1bafe52e1e301851b4f19 100644 (file)
@@ -30,6 +30,7 @@
   <menu label="POIFS">
     <menu-item label="Overview" href="index.html"/>
     <menu-item label="How To" href="how-to.html"/>
+    <menu-item label="Embeded Documents" href="embeded.html"/>
     <menu-item label="File System Documentation" href="fileformat.html"/>
     <menu-item label="Use Cases" href="usecases.html"/>
   </menu>
diff --git a/src/documentation/content/xdocs/poifs/embeded.xml b/src/documentation/content/xdocs/poifs/embeded.xml
new file mode 100644 (file)
index 0000000..d888e2e
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   ====================================================================
+   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.
+   ====================================================================
+-->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "../dtd/document-v11.dtd">
+<document>
+  <header>
+    <title>Apache POI - POIFS - Documents embeded in other documents</title>
+    <subtitle>Overview</subtitle>
+    <authors>
+      <person name="Nick Burch" email="nick@apache.org"/>     
+      <person name="Yegor Kozlov" email="yegor@apache.org"/>     
+    </authors>
+  </header>
+  <body>
+    <section><title>Overview</title>
+      <p>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.</p>
+      <p>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.</p>
+      <p>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.</p>
+
+       <section><title>Files embeded in Excel</title>
+         <p>Excel normally stores embeded files in subdirectories
+         of the filesystem root. Typically these subdirectories
+         are named starting with MBD, with 8 hex characters following.</p>
+       </section>
+
+       <section><title>Files embeded in Word</title>
+         <p>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.</p>
+       </section>
+
+       <section><title>Files embeded in PowerPoint</title>
+         <p>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.</p>
+       </section>
+    </section>
+
+    <section><title>Listing POIFS contents</title>
+      <p>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.</p>
+      <p>The tool to use is <em>org.apache.poi.poifs.dev.POIFSLister</em>.
+         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.</p>
+    </section>
+
+    <section><title>Opening embeded files</title>
+      <p>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.</p>
+      <p>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.</p>
+    </section>
+  </body>
+</document>
diff --git a/src/documentation/content/xdocs/text-extraction.xml b/src/documentation/content/xdocs/text-extraction.xml
new file mode 100644 (file)
index 0000000..d71a0bf
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   ====================================================================
+   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.
+   ====================================================================
+-->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.3//EN" "./dtd/document-v13.dtd">
+
+<document>
+  <header>
+    <title>POI Text Extraction</title>
+    <authors>
+      <person id="NB" name="Nick Burch" email="nick@apache.org"/>
+    </authors>
+  </header>
+  
+  <body>
+    <section><title>Overview</title>
+      <p>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.</p>
+      <p>In addition to providing direct text extraction classes,
+       POI works closely with the 
+       <link href="http://incubator.apache.org/tika/">Apache Tika</link>
+       text extraction library. Users may wish to simply utilise 
+       the functionality provided by Tika.</p>
+    </section>
+
+    <section><title>Common functionality</title>
+     <p>All of the POI text extractors extend from
+      <em>org.apache.poi.POITextExtractor</em>. 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.</p>
+     <p>All POIFS / OLE 2 based text extractors also extend from
+      <em>org.apache.poi.POIOLE2TextExtractor</em>. This additionally
+      provides common methods to get at the <link href="hpfs/">HPFS
+      document metadata</link>.</p>
+     <p>All OOXML based text extractors (available in POI 3.5 and later) 
+      also extend from
+      <em>org.apache.poi.POIOOXMLTextExtractor</em>. This additionally
+      provides common methods to get at the OOXML metadata.</p>
+    </section>
+
+    <section><title>Text Extractor Factory - POI 3.5 or later</title>
+     <p>A new class in POI 3.5, 
+      <em>org.apache.poi.extractor.ExtractorFactory</em> 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.</p>
+    </section>
+
+    <section><title>Excel</title>
+     <p>For .xls files, there is 
+      <em>org.apache.poi.hssf.extractor.ExcelExtractor</em>, which will 
+      return text, optionally with formulas instead of their contents. 
+      Those using POI 3.5 can also use 
+      <em>org.apache.poi.xssf.extractor.XSSFExcelExtractor</em>, to perform
+      a similar task for .xlsx files.</p>
+     <p>In addition, there is a second text extractor for .xls files,
+      <em>org.apache.poi.hssf.extractor.EventBasedExcelExtractor</em>. 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.</p>
+    </section>
+
+    <section><title>Word</title>
+     <p>For .doc files, in scratchpad there is 
+      <em>org.apache.poi.hwpf.extractor.WordExtractor</em>, which will 
+      return text for your document. Those using POI 3.5 can also use 
+      <em>org.apache.poi.xwpf.extractor.XPFFWordExtractor</em>, to perform
+      a similar task for .docx files.</p>
+    </section>
+
+    <section><title>PowerPoint</title>
+     <p>For .ppt files, in scratchpad there is 
+      <em>org.apache.poi.hslf.extractor.PowerPointExtractor</em>, which 
+      will return text for your slideshow, optionally restricted to just
+      slides text or notes text. Those using POI 3.5 can also use 
+      <em>org.apache.poi.xslf.extractor.XSLFPowerPointExtractor</em>, to 
+      perform a similar task for .pptx files.</p>
+    </section>
+
+    <section><title>Visio</title>
+     <p>For .vsd files, in scratchpad there is 
+      <em>org.apache.poi.hdgf.extractor.VisioTextExtractor</em>, which 
+      will return text for your file.</p>
+    </section>
+  </body>
+
+  <footer>
+    <legal>
+      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) $
+    </legal>
+  </footer>
+</document>
diff --git a/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java b/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java
new file mode 100644 (file)
index 0000000..9bebd3a
--- /dev/null
@@ -0,0 +1,323 @@
+/* ====================================================================
+   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.examples;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+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.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.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.RKRecord;
+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 XLS -> CSV processor, that uses the MissingRecordAware
+ *  EventModel code to ensure it outputs all columns and rows.
+ * @author Nick Burch
+ */
+public class XLS2CSVmra implements HSSFListener {
+       private int minColumns;
+       private POIFSFileSystem fs;
+       private PrintStream output;
+       
+       private int lastRowNumber;
+       private int lastColumnNumber;
+       
+       /** Should we output the formula, or the value it has? */
+       private boolean outputFormulaValues = true;
+       
+       // 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
+        * @param fs The POIFSFileSystem to process
+        * @param output The PrintStream to output the CSV to
+        * @param minColumns The minimum number of columns to output, or -1 for no minimum
+        */
+       public XLS2CSVmra(POIFSFileSystem fs, PrintStream output, int minColumns) {
+               this.fs = fs;
+               this.output = output;
+               this.minColumns = minColumns;
+       }
+       
+       /**
+        * Creates a new XLS -> CSV converter
+        * @param filename The file to process
+        * @param minColumns The minimum number of columns to output, or -1 for no minimum
+        * @throws IOException
+        * @throws FileNotFoundException
+        */
+       public XLS2CSVmra(String filename, int minColumns) throws IOException, FileNotFoundException {
+               this(
+                               new POIFSFileSystem(new FileInputStream(filename)),
+                               System.out, minColumns
+               );
+       }
+       
+       /**
+        * Initiates the processing of the XLS file to CSV
+        */
+       public void process() throws IOException {
+               MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
+               formatListener = new FormatTrackingHSSFListener(listener); 
+               
+               HSSFEventFactory factory = new HSSFEventFactory();
+               HSSFRequest request = new HSSFRequest();
+               request.addListenerForAllRecords(formatListener);
+               
+               factory.processWorkbookEvents(request, fs);
+       }
+       
+       /**
+        * Main HSSFListener method, processes events, and outputs the
+        *  CSV as the file is processed. 
+        */
+       public void processRecord(Record record) {
+               int thisRow = -1;
+               int thisColumn = -1;
+               String thisStr = null;
+               
+               switch (record.getSid())
+        {
+               case SSTRecord.sid:
+                       sstRecord = (SSTRecord) record;
+                       break;
+                       
+        case BlankRecord.sid:
+               BlankRecord brec = (BlankRecord) record;
+               
+            thisRow = brec.getRow();
+            thisColumn = brec.getColumn();
+            thisStr = "";
+            break;
+        case BoolErrRecord.sid:
+               BoolErrRecord berec = (BoolErrRecord) record;
+               
+            thisRow = berec.getRow();
+            thisColumn = berec.getColumn();
+            thisStr = "";
+            break;
+            
+        case FormulaRecord.sid:
+               FormulaRecord frec = (FormulaRecord) record;
+               
+               thisRow = frec.getRow();
+               thisColumn = frec.getColumn();
+               
+               if(outputFormulaValues) {
+                       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 {
+                       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;
+               
+            thisRow = lrec.getRow();
+            thisColumn = lrec.getColumn();
+            thisStr = '"' + lrec.getValue() + '"';
+            break;
+        case LabelSSTRecord.sid:
+               LabelSSTRecord lsrec = (LabelSSTRecord) record;
+               
+            thisRow = lsrec.getRow();
+            thisColumn = lsrec.getColumn();
+            if(sstRecord == null) {
+               thisStr = '"' + "(No SST Record, can't identify string)" + '"';
+            } else {
+               thisStr = '"' + sstRecord.getString(lsrec.getSSTIndex()).toString() + '"';
+            }
+            break;
+        case NoteRecord.sid:
+               NoteRecord nrec = (NoteRecord) record;
+               
+               thisRow = nrec.getRow();
+               thisColumn = nrec.getColumn();
+               // TODO: Find object to match nrec.getShapeId()
+               thisStr = '"' + "(TODO)" + '"';
+            break;
+        case NumberRecord.sid:
+            NumberRecord numrec = (NumberRecord) record;
+            
+            thisRow = numrec.getRow();
+            thisColumn = numrec.getColumn();
+            
+            // Format
+            thisStr = formatNumberDateCell(numrec, numrec.getValue());
+            break;
+        case RKRecord.sid:
+               RKRecord rkrec = (RKRecord) record;
+               
+               thisRow = rkrec.getRow();
+               thisColumn = rkrec.getColumn();
+               thisStr = '"' + "(TODO)" + '"';
+               break;
+        default:
+               break;
+        }
+               
+               // Handle new row
+               if(thisRow != -1 && thisRow != lastRowNumber) {
+                       lastColumnNumber = -1;
+               }
+               
+               // Handle missing column
+               if(record instanceof MissingCellDummyRecord) {
+                       MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
+                       thisRow = mc.getRow();
+                       thisColumn = mc.getColumn();
+                       thisStr = "";
+               }
+               
+               // If we got something to print out, do so
+               if(thisStr != null) {
+                       if(thisColumn > 0) {
+                               output.print(',');
+                       }
+                       output.print(thisStr);
+               }
+               
+               // Update column and row count
+               if(thisRow > -1)
+                       lastRowNumber = thisRow;
+               if(thisColumn > -1)
+                       lastColumnNumber = thisColumn;
+               
+               // Handle end of row
+               if(record instanceof LastCellOfRowDummyRecord) {
+                       // Print out any missing commas if needed
+                       if(minColumns > 0) {
+                               // Columns are 0 based
+                               if(lastColumnNumber == -1) { lastColumnNumber = 0; }
+                               for(int i=lastColumnNumber; i<(minColumns); i++) {
+                                       output.print(',');
+                               }
+                       }
+                       
+                       // We're onto a new row
+                       lastColumnNumber = -1;
+                       
+                       // End the row
+                       output.println();
+               }
+       }
+       
+       /**
+        * 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 = formatListener.getFormatIndex(cell);
+               String formatString = formatListener.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);
+               }
+        }
+       }
+
+       
+       public static void main(String[] args) throws Exception {
+               if(args.length < 1) {
+                       System.err.println("Use:");
+                       System.err.println("  XLS2CSVmra <xls file> [min columns]");
+                       System.exit(1);
+               }
+               
+               int minColumns = -1;
+               if(args.length >= 2) {
+                       minColumns = Integer.parseInt(args[1]);
+               }
+               
+               XLS2CSVmra xls2csv = new XLS2CSVmra(args[0], minColumns);
+               xls2csv.process();
+       }
+}
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 (file)
index 0000000..8f3eebb
--- /dev/null
@@ -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);
+                       }
+               }
+               }
+       }
+}
index 40ed1da4903ab12507c2a8e69ffe52522a20dedb..d493cd53321f43c0da40d5c94fc2492e61a1074c 100644 (file)
@@ -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.<p/>
+ * Implementation of the HLOOKUP() function.<p/>
  * 
- * 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.<br/>
  * 
  * <b>Syntax</b>:<br/>
  * <b>HLOOKUP</b>(<b>lookup_value</b>, <b>table_array</b>, <b>row_index_num</b>, range_lookup)<p/>
  * 
  * <b>lookup_value</b>  The value to be found in the first column of the table array.<br/>
- * <b>table_array</> An area reference for the lookup data. <br/>
+ * <b>table_array</b> An area reference for the lookup data. <br/>
  * <b>row_index_num</b> a 1 based index specifying which row value of the lookup data will be returned.<br/>
  * <b>range_lookup</b> If TRUE (default), HLOOKUP finds the largest value less than or equal to 
  * the lookup_value.  If FALSE, only exact matches will be considered<br/>   
index 7d27491df1590a1089c13ec2750d41abad654fb5..bd158b897c818e03a33c0a6227c5962a5f04c732 100644 (file)
@@ -27,13 +27,13 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
 /**
  * Implementation of the VLOOKUP() function.<p/>
  * 
- * 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.<br/>
  * 
  * <b>Syntax</b>:<br/>
  * <b>VLOOKUP</b>(<b>lookup_value</b>, <b>table_array</b>, <b>col_index_num</b>, range_lookup)<p/>
  * 
  * <b>lookup_value</b>  The value to be found in the first column of the table array.<br/>
- * <b>table_array</> An area reference for the lookup data. <br/>
+ * <b>table_array</b> An area reference for the lookup data. <br/>
  * <b>col_index_num</b> a 1 based index specifying which column value of the lookup data will be returned.<br/>
  * <b>range_lookup</b> If TRUE (default), VLOOKUP finds the largest value less than or equal to 
  * the lookup_value.  If FALSE, only exact matches will be considered<br/>   
diff --git a/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java b/src/scratchpad/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java
deleted file mode 100644 (file)
index 1c53f1e..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-/* ====================================================================
-   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.examples;
-
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-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;
-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.dummyrecord.LastCellOfRowDummyRecord;
-import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
-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;
-import org.apache.poi.hssf.record.NoteRecord;
-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.usermodel.HSSFDateUtil;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-/**
- * A XLS -> CSV processor, that uses the MissingRecordAware
- *  EventModel code to ensure it outputs all columns and rows.
- * @author Nick Burch
- */
-public class XLS2CSVmra implements HSSFListener {
-       private int minColumns;
-       private POIFSFileSystem fs;
-       private PrintStream output;
-       
-       private int lastRowNumber;
-       private int lastColumnNumber;
-       
-       /** Should we output the formula, or the value it has? */
-       private boolean outputFormulaValues = true;
-       
-       // Records we pick up as we process
-       private SSTRecord sstRecord;
-       private FormatTrackingHSSFListener formatListener;
-
-       /**
-        * Creates a new XLS -> CSV converter
-        * @param fs The POIFSFileSystem to process
-        * @param output The PrintStream to output the CSV to
-        * @param minColumns The minimum number of columns to output, or -1 for no minimum
-        */
-       public XLS2CSVmra(POIFSFileSystem fs, PrintStream output, int minColumns) {
-               this.fs = fs;
-               this.output = output;
-               this.minColumns = minColumns;
-       }
-       
-       /**
-        * Creates a new XLS -> CSV converter
-        * @param filename The file to process
-        * @param minColumns The minimum number of columns to output, or -1 for no minimum
-        * @throws IOException
-        * @throws FileNotFoundException
-        */
-       public XLS2CSVmra(String filename, int minColumns) throws IOException, FileNotFoundException {
-               this(
-                               new POIFSFileSystem(new FileInputStream(filename)),
-                               System.out, minColumns
-               );
-       }
-       
-       /**
-        * Initiates the processing of the XLS file to CSV
-        */
-       public void process() throws IOException {
-               MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
-               formatListener = new FormatTrackingHSSFListener(listener); 
-               
-               HSSFEventFactory factory = new HSSFEventFactory();
-               HSSFRequest request = new HSSFRequest();
-               request.addListenerForAllRecords(formatListener);
-               
-               factory.processWorkbookEvents(request, fs);
-       }
-       
-       /**
-        * Main HSSFListener method, processes events, and outputs the
-        *  CSV as the file is processed. 
-        */
-       public void processRecord(Record record) {
-               int thisRow = -1;
-               int thisColumn = -1;
-               String thisStr = null;
-               
-               switch (record.getSid())
-        {
-               case SSTRecord.sid:
-                       sstRecord = (SSTRecord) record;
-                       break;
-                       
-        case BlankRecord.sid:
-               BlankRecord brec = (BlankRecord) record;
-               
-            thisRow = brec.getRow();
-            thisColumn = brec.getColumn();
-            thisStr = "";
-            break;
-        case BoolErrRecord.sid:
-               BoolErrRecord berec = (BoolErrRecord) record;
-               
-            thisRow = berec.getRow();
-            thisColumn = berec.getColumn();
-            thisStr = "";
-            break;
-        case FormulaRecord.sid:
-               FormulaRecord frec = (FormulaRecord) record;
-               
-               thisRow = frec.getRow();
-               thisColumn = frec.getColumn();
-               
-               if(outputFormulaValues) {
-                thisStr = formatNumberDateCell(frec, frec.getValue());
-               } else {
-                       // TODO: Output the formula string
-                       thisStr = '"' + frec.toString() + '"';
-               }
-            break;
-        case LabelRecord.sid:
-               LabelRecord lrec = (LabelRecord) record;
-               
-            thisRow = lrec.getRow();
-            thisColumn = lrec.getColumn();
-            thisStr = '"' + lrec.getValue() + '"';
-            break;
-        case LabelSSTRecord.sid:
-               LabelSSTRecord lsrec = (LabelSSTRecord) record;
-               
-            thisRow = lsrec.getRow();
-            thisColumn = lsrec.getColumn();
-            if(sstRecord == null) {
-               thisStr = '"' + "(No SST Record, can't identify string)" + '"';
-            } else {
-               thisStr = '"' + sstRecord.getString(lsrec.getSSTIndex()).toString() + '"';
-            }
-            break;
-        case NoteRecord.sid:
-               NoteRecord nrec = (NoteRecord) record;
-               
-               thisRow = nrec.getRow();
-               thisColumn = nrec.getColumn();
-               // TODO: Find object to match nrec.getShapeId()
-               thisStr = '"' + "(TODO)" + '"';
-            break;
-        case NumberRecord.sid:
-            NumberRecord numrec = (NumberRecord) record;
-            
-            thisRow = numrec.getRow();
-            thisColumn = numrec.getColumn();
-            
-            // Format
-            thisStr = formatNumberDateCell(numrec, numrec.getValue());
-            break;
-        case RKRecord.sid:
-               RKRecord rkrec = (RKRecord) record;
-               
-               thisRow = rkrec.getRow();
-               thisColumn = rkrec.getColumn();
-               thisStr = '"' + "(TODO)" + '"';
-               break;
-        default:
-               break;
-        }
-               
-               // Handle new row
-               if(thisRow != -1 && thisRow != lastRowNumber) {
-                       lastColumnNumber = -1;
-               }
-               
-               // Handle missing column
-               if(record instanceof MissingCellDummyRecord) {
-                       MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
-                       thisRow = mc.getRow();
-                       thisColumn = mc.getColumn();
-                       thisStr = "";
-               }
-               
-               // If we got something to print out, do so
-               if(thisStr != null) {
-                       if(thisColumn > 0) {
-                               output.print(',');
-                       }
-                       output.print(thisStr);
-               }
-               
-               // Update column and row count
-               if(thisRow > -1)
-                       lastRowNumber = thisRow;
-               if(thisColumn > -1)
-                       lastColumnNumber = thisColumn;
-               
-               // Handle end of row
-               if(record instanceof LastCellOfRowDummyRecord) {
-                       // Print out any missing commas if needed
-                       if(minColumns > 0) {
-                               // Columns are 0 based
-                               if(lastColumnNumber == -1) { lastColumnNumber = 0; }
-                               for(int i=lastColumnNumber; i<(minColumns); i++) {
-                                       output.print(',');
-                               }
-                       }
-                       
-                       // We're onto a new row
-                       lastColumnNumber = -1;
-                       
-                       // End the row
-                       output.println();
-               }
-       }
-       
-       /**
-        * 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 = formatListener.getFormatIndex(cell);
-               String formatString = formatListener.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);
-               }
-        }
-       }
-
-       
-       public static void main(String[] args) throws Exception {
-               if(args.length < 1) {
-                       System.err.println("Use:");
-                       System.err.println("  XLS2CSVmra <xls file> [min columns]");
-                       System.exit(1);
-               }
-               
-               int minColumns = -1;
-               if(args.length >= 2) {
-                       minColumns = Integer.parseInt(args[1]);
-               }
-               
-               XLS2CSVmra xls2csv = new XLS2CSVmra(args[0], minColumns);
-               xls2csv.process();
-       }
-}
index ad311eb27169b96121bca10f98b4e450a719b4ff..63d67ee7716b21b499a85bede9e3e7480fe9a701 100644 (file)
@@ -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
         */