Fix bug #45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@675671 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-07-10 18:41:25 +00:00
parent 575c0b4763
commit bed3ab6871
6 changed files with 105 additions and 50 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! -->
<release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
<action dev="POI-DEVELOPERS" type="fix">45373 - Improve the performance of HSSFSheet.shiftRows</action>
<action dev="POI-DEVELOPERS" type="fix">45367 - Fixed bug when last row removed from sheet is row zero</action>
<action dev="POI-DEVELOPERS" type="fix">45348 - Tweaks to RVA formula logic</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
<action dev="POI-DEVELOPERS" type="fix">45373 - Improve the performance of HSSFSheet.shiftRows</action>
<action dev="POI-DEVELOPERS" type="fix">45367 - Fixed bug when last row removed from sheet is row zero</action>
<action dev="POI-DEVELOPERS" type="fix">45348 - Tweaks to RVA formula logic</action>

View File

@ -20,10 +20,6 @@ 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;
@ -37,7 +33,6 @@ 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;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
@ -47,7 +42,6 @@ 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.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@ -180,7 +174,7 @@ public class XLS2CSVmra implements HSSFListener {
nextRow = frec.getRow();
nextColumn = frec.getColumn();
} else {
thisStr = formatNumberDateCell(frec, frec.getValue());
thisStr = formatListener.formatNumberDateCell(frec);
}
} else {
thisStr = '"' +
@ -231,7 +225,7 @@ public class XLS2CSVmra implements HSSFListener {
thisColumn = numrec.getColumn();
// Format
thisStr = formatNumberDateCell(numrec, numrec.getValue());
thisStr = formatListener.formatNumberDateCell(numrec);
break;
case RKRecord.sid:
RKRecord rkrec = (RKRecord) record;
@ -290,44 +284,6 @@ public class XLS2CSVmra implements HSSFListener {
}
}
/**
* 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:");

View File

@ -16,7 +16,11 @@
==================================================================== */
package org.apache.poi.hssf.eventusermodel;
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;
@ -24,8 +28,11 @@ import java.util.Map;
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.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
/**
* A proxy HSSFListener that keeps track of the document
@ -69,6 +76,61 @@ public class FormatTrackingHSSFListener implements HSSFListener {
}
}
/**
* Formats the given numeric of date Cell's contents
* as a String, in as close as we can to the way
* that Excel would do so.
* Uses the various format records to manage this.
*
* TODO - move this to a central class in such a
* way that hssf.usermodel can make use of it too
*/
public String formatNumberDateCell(CellValueRecordInterface cell) {
double value;
if(cell instanceof NumberRecord) {
value = ((NumberRecord)cell).getValue();
} else if(cell instanceof FormulaRecord) {
value = ((FormulaRecord)cell).getValue();
} else {
throw new IllegalArgumentException("Unsupported CellValue Record passed in " + cell);
}
// Get the built in format, if there is one
int formatIndex = getFormatIndex(cell);
String formatString = 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);
}
if(formatString == "0.00E+00") {
// This seems to mean output as a normal double
return Double.toString(value);
}
// Format as a number
DecimalFormat df = new DecimalFormat(formatString);
return df.format(value);
}
}
}
/**
* Returns the format string, eg $##.##, for the
* given number format index.

Binary file not shown.

View File

@ -24,6 +24,9 @@ import java.util.List;
import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
@ -31,16 +34,17 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
*/
public final class TestFormatTrackingHSSFListener extends TestCase {
private FormatTrackingHSSFListener listener;
public void setUp() {
private MockHSSFListener mockListen;
private void processFile(String filename) throws Exception {
HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener();
mockListen = new MockHSSFListener();
listener = new FormatTrackingHSSFListener(mockListen);
req.addListenerForAllRecords(listener);
HSSFEventFactory factory = new HSSFEventFactory();
try {
InputStream is = HSSFTestDataSamples.openSampleFileStream("MissingBits.xls");
InputStream is = HSSFTestDataSamples.openSampleFileStream(filename);
POIFSFileSystem fs = new POIFSFileSystem(is);
factory.processWorkbookEvents(req, fs);
} catch (IOException e) {
@ -49,11 +53,42 @@ public final class TestFormatTrackingHSSFListener extends TestCase {
}
public void testFormats() throws Exception {
processFile("MissingBits.xls");
assertEquals("_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)", listener.getFormatString(41));
assertEquals("_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)", listener.getFormatString(42));
assertEquals("_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)", listener.getFormatString(43));
}
/**
* Ensure that all number and formula records can be
* turned into strings without problems
*/
public void testTurnToString() throws Exception {
processFile("45365.xls");
for(int i=0; i<mockListen._records.size(); i++) {
Record r = (Record)mockListen._records.get(i);
CellValueRecordInterface cvr = null;
if(r instanceof NumberRecord) {
cvr = (CellValueRecordInterface)r;
}
if(r instanceof FormulaRecord) {
cvr = (CellValueRecordInterface)r;
}
if(cvr != null) {
// Should always give us a string
String s = listener.formatNumberDateCell(cvr);
assertNotNull(s);
assertTrue(s.length() > 0);
}
}
// TODO - test some specific format strings
}
private static final class MockHSSFListener implements HSSFListener {
public MockHSSFListener() {}
private final List _records = new ArrayList();