#58532 For Excel cell formats with 3+ parts to them (eg +ve,-ve,0), which

DataFormatter didn't properly support, call out to the alternate CellFormat
 instead for the formatting.
This also allows us to enable some disabled parts of DataFormatter unit tests
We still need to rationalise DataFormatter and CellFormatter though, so we
 only have one set of cell formatting logic...


git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1710399 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2015-10-24 23:34:47 +00:00
parent e86a116d0a
commit e966499ad2
8 changed files with 127 additions and 37 deletions

View File

@ -40,8 +40,12 @@ import java.util.Observer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.ss.format.CellFormat;
import org.apache.poi.ss.format.CellFormatResult;
import org.apache.poi.ss.util.NumberToTextConverter;
import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
@ -197,6 +201,9 @@ public class DataFormatter implements Observer {
/** the Observable to notify, when the locale has been changed */
private final LocaleChangeObservable localeChangedObervable = new LocaleChangeObservable();
/** For logging any problems we find */
private static POILogger logger = POILogFactory.getLogger(DataFormatter.class);
/**
* Creates a formatter using the {@link Locale#getDefault() default locale}.
*/
@ -270,28 +277,30 @@ public class DataFormatter implements Observer {
// String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0];
String formatStr = formatStrIn;
// Excel supports positive/negative/zero, but java
// doesn't, so we need to do it specially
final int firstAt = formatStr.indexOf(';');
final int lastAt = formatStr.lastIndexOf(';');
// p and p;n are ok by default. p;n;z and p;n;z;s need to be fixed.
if (firstAt != -1 && firstAt != lastAt) {
final int secondAt = formatStr.indexOf(';', firstAt + 1);
if (secondAt == lastAt) { // p;n;z
if (cellValue == 0.0) {
formatStr = formatStr.substring(lastAt + 1);
} else {
formatStr = formatStr.substring(0, lastAt);
}
} else {
if (cellValue == 0.0) { // p;n;z;s
formatStr = formatStr.substring(secondAt + 1, lastAt);
} else {
formatStr = formatStr.substring(0, secondAt);
// Excel supports 3+ part conditional data formats, eg positive/negative/zero,
// or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds
// of different formats for different ranges, just +ve/-ve, we need to
// handle these ourselves in a special way.
// For now, if we detect 3+ parts, we call out to CellFormat to handle it
// TODO Going forward, we should really merge the logic between the two classes
if (formatStr.indexOf(";") != -1 &&
formatStr.indexOf(';') != formatStr.lastIndexOf(';')) {
try {
// Ask CellFormat to get a formatter for it
CellFormat cfmt = CellFormat.getInstance(formatStr);
// CellFormat requires callers to identify date vs not, so do so
Object cellValueO = Double.valueOf(cellValue);
if (DateUtil.isADateFormat(formatIndex, formatStr)) {
cellValueO = DateUtil.getJavaDate(cellValue);
}
// Wrap and return (non-cachable - CellFormat does that)
return new CellFormatResultWrapper( cfmt.apply(cellValueO) );
} catch (Exception e) {
logger.log(POILogger.WARN, "Formatting failed as " + formatStr + ", falling back", e);
}
}
// Excel's # with value 0 will output empty where Java will output 0. This hack removes the # from the format.
if (emulateCsv && cellValue == 0.0 && formatStr.contains("#") && !formatStr.contains("0")) {
formatStr = formatStr.replaceAll("#", "");
@ -310,7 +319,6 @@ public class DataFormatter implements Observer {
// Build a formatter, and cache it
format = createFormat(cellValue, formatIndex, formatStr);
formats.put(formatStr, format);
return format;
}
@ -1116,4 +1124,21 @@ public class DataFormatter implements Observer {
return df.parseObject(source, pos);
}
}
/**
* Workaround until we merge {@link DataFormatter} with {@link CellFormat}.
* Constant, non-cachable wrapper around a {@link CellFormatResult}
*/
@SuppressWarnings("serial")
private static final class CellFormatResultWrapper extends Format {
private final CellFormatResult result;
private CellFormatResultWrapper(CellFormatResult result) {
this.result = result;
}
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return toAppendTo.append(result.text);
}
public Object parseObject(String source, ParsePosition pos) {
return null; // Not supported
}
}
}

View File

@ -35,7 +35,7 @@ public final class TestXSSFDataFormat extends BaseTestDataFormat {
/**
* [Bug 49928] formatCellValue returns incorrect value for \u00a3 formatted cells
*/
public void test49928(){
public void test49928() {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("49928.xlsx");
doTest49928Core(wb);
@ -49,4 +49,12 @@ public final class TestXSSFDataFormat extends BaseTestDataFormat {
assertTrue(customFmtIdx > BuiltinFormats.FIRST_USER_DEFINED_FORMAT_INDEX );
assertEquals("\u00a3##.00[Yellow]", dataFormat.getFormat(customFmtIdx));
}
/**
* [Bug 58532] Handle formats that go numnum, numK, numM etc
*/
public void test58532() {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("FormatKM.xlsx");
doTest58532Core(wb);
}
}

View File

@ -51,6 +51,14 @@ public final class TestHSSFDataFormat extends BaseTestDataFormat {
assertEquals("\u00a3##.00[Yellow]", dataFormat.getFormat(customFmtIdx));
}
/**
* [Bug 58532] Handle formats that go numnum, numK, numM etc
*/
public void test58532() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("FormatKM.xls");
doTest58532Core(wb);
}
/**
* Bug 51378: getDataFormatString method call crashes when reading the test file
*/

View File

@ -265,8 +265,8 @@ public final class TestHSSFDataFormatter {
assertTrue( ! "555.47431".equals(fmtval));
// check we found the time properly
assertTrue("Format came out incorrect - " + fmt + ": " + fmtval + ", but expected to find '11:23'",
fmtval.indexOf("11:23") > -1);
assertTrue("Format came out incorrect - " + fmt + " - found " + fmtval +
", but expected to find '11:23'", fmtval.indexOf("11:23") > -1);
}
// test number formats
@ -358,8 +358,10 @@ public final class TestHSSFDataFormatter {
Cell cell = it.next();
cell.setCellValue(cell.getNumericCellValue() * Math.random() / 1000000 - 1000);
log(formatter.formatCellValue(cell));
assertTrue(formatter.formatCellValue(cell).startsWith("Balance "));
assertTrue(formatter.formatCellValue(cell).endsWith(" USD"));
String formatted = formatter.formatCellValue(cell);
assertTrue("Doesn't start with Balance: " + formatted, formatted.startsWith("Balance "));
assertTrue("Doesn't end with USD: " + formatted, formatted.endsWith(" USD"));
}
}

View File

@ -87,4 +87,48 @@ public abstract class BaseTestDataFormat extends TestCase {
assertEquals(poundFmtIdx, dataFormat.getFormat(poundFmt));
assertEquals(poundFmt, dataFormat.getFormat(poundFmtIdx));
}
public abstract void test58532();
public void doTest58532Core(Workbook wb) {
Sheet s = wb.getSheetAt(0);
DataFormatter fmt = new DataFormatter();
FormulaEvaluator eval = wb.getCreationHelper().createFormulaEvaluator();
// Column A is the raw values
// Column B is the ##/#K/#M values
// Column C is strings of what they should look like
// Column D is the #.##/#.#K/#.#M values
// Column E is strings of what they should look like
String formatKMWhole = "[>999999]#,,\"M\";[>999]#,\"K\";#";
String formatKM3dp = "[>999999]#.000,,\"M\";[>999]#.000,\"K\";#.000";
// Check the formats are as expected
Row headers = s.getRow(0);
assertNotNull(headers);
assertEquals(formatKMWhole, headers.getCell(1).getStringCellValue());
assertEquals(formatKM3dp, headers.getCell(3).getStringCellValue());
Row r2 = s.getRow(1);
assertNotNull(r2);
assertEquals(formatKMWhole, r2.getCell(1).getCellStyle().getDataFormatString());
assertEquals(formatKM3dp, r2.getCell(3).getCellStyle().getDataFormatString());
// For all of the contents rows, check that DataFormatter is able
// to format the cells to the same value as the one next to it
for (int rn=1; rn<s.getLastRowNum(); rn++) {
Row r = s.getRow(rn);
if (r == null) break;
double value = r.getCell(0).getNumericCellValue();
String expWhole = r.getCell(2).getStringCellValue();
String exp3dp = r.getCell(4).getStringCellValue();
assertEquals("Wrong formatting of " + value + " for row " + rn,
expWhole, fmt.formatCellValue(r.getCell(1), eval));
assertEquals("Wrong formatting of " + value + " for row " + rn,
exp3dp, fmt.formatCellValue(r.getCell(3), eval));
}
}
}

View File

@ -219,15 +219,15 @@ public class TestDataFormatter {
assertEquals("26027/81", dfUS.formatRawCellContents(321.321, -1, "?/??"));
// p;n;z;s parts
assertEquals( "321 1/3", dfUS.formatRawCellContents(321.321, -1, "# #/#;# ##/#;0;xxx"));
assertEquals("-321 1/3", dfUS.formatRawCellContents(-321.321, -1, "# #/#;# ##/#;0;xxx"));
assertEquals("0", dfUS.formatRawCellContents(0, -1, "# #/#;# ##/#;0;xxx"));
// assertEquals("0.0", dfUS.formatRawCellContents(0, -1, "# #/#;# ##/#;#.#;xxx")); // currently hard coded to 0
assertEquals("321 1/3", dfUS.formatRawCellContents(321.321, -1, "# #/#;# ##/#;0;xxx"));
assertEquals("321 1/3", dfUS.formatRawCellContents(-321.321, -1, "# #/#;# ##/#;0;xxx")); // Note the lack of - sign!
assertEquals("0", dfUS.formatRawCellContents(0, -1, "# #/#;# ##/#;0;xxx"));
// assertEquals(".", dfUS.formatRawCellContents(0, -1, "# #/#;# ##/#;#.#;xxx")); // Currently shows as 0. not .
// Custom formats with text are not currently supported
// assertEquals("+ve", dfUS.formatRawCellContents(0, -1, "+ve;-ve;zero;xxx"));
// assertEquals("-ve", dfUS.formatRawCellContents(0, -1, "-ve;-ve;zero;xxx"));
// assertEquals("zero", dfUS.formatRawCellContents(0, -1, "zero;-ve;zero;xxx"));
// Custom formats with text
assertEquals("+ve", dfUS.formatRawCellContents(1, -1, "+ve;-ve;zero;xxx"));
assertEquals("-ve", dfUS.formatRawCellContents(-1, -1, "-ve;-ve;zero;xxx"));
assertEquals("zero", dfUS.formatRawCellContents(0, -1, "zero;-ve;zero;xxx"));
// Custom formats - check text is stripped, including multiple spaces
assertEquals("321 1/3", dfUS.formatRawCellContents(321.321, -1, "# #/#"));
@ -258,8 +258,9 @@ public class TestDataFormatter {
assertEquals("321 1/3", dfUS.formatRawCellContents(321.321, -1, "# ?/? ?/?"));
assertEquals("321 1/3", dfUS.formatRawCellContents(321.321, -1, "# ?/? #/# #/#"));
// Where both p and n don't include a fraction, so cannot always be formatted
// assertEquals("123", dfUS.formatRawCellContents(-123.321, -1, "0 ?/?;0"));
// Where +ve has a fraction, but -ve doesnt, we currently show both
assertEquals("123 1/3", dfUS.formatRawCellContents( 123.321, -1, "0 ?/?;0"));
//assertEquals("123", dfUS.formatRawCellContents(-123.321, -1, "0 ?/?;0"));
//Bug54868 patch has a hit on the first string before the ";"
assertEquals("-123 1/3", dfUS.formatRawCellContents(-123.321, -1, "0 ?/?;0"));
@ -504,12 +505,14 @@ public class TestDataFormatter {
assertEquals("1901/01/01", dfUS.formatRawCellContents(367.0, -1, "yyyy\\/mm\\/dd"));
}
// TODO Fix this to work
@Test
@Ignore("CellFormat and DataFormatter don't quite agree...")
public void testOther() {
DataFormatter dfUS = new DataFormatter(Locale.US, true);
assertEquals(" 12.34 ", dfUS.formatRawCellContents(12.34, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
assertEquals("-12.34 ", dfUS.formatRawCellContents(-12.34, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
assertEquals(" 12.34 ", dfUS.formatRawCellContents( 12.34, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
assertEquals("- 12.34 ", dfUS.formatRawCellContents(-12.34, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
assertEquals(" - ", dfUS.formatRawCellContents(0.0, -1, "_-* #,##0.00_-;-* #,##0.00_-;_-* \"-\"??_-;_-@_-"));
assertEquals(" $- ", dfUS.formatRawCellContents(0.0, -1, "_-$* #,##0.00_-;-$* #,##0.00_-;_-$* \"-\"??_-;_-@_-"));
}

Binary file not shown.

Binary file not shown.