58536 DataFormatter and CellFormat non-localised support for localised currency formats like [$£-809]
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1710484 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
90db0bf451
commit
8cd7f1f736
@ -66,7 +66,7 @@ import org.apache.poi.ss.util.DateFormatConverter;
|
|||||||
* surround by brackets). </dl>
|
* surround by brackets). </dl>
|
||||||
* <p/>
|
* <p/>
|
||||||
* A given format part may specify a given Locale, by including something
|
* A given format part may specify a given Locale, by including something
|
||||||
* like <tt>[$$-409]</tt> or <tt>[$\u00A3-809]</tt> or <tt>[$-40C]</tt>. These
|
* like <tt>[$$-409]</tt> or <tt>[$£-809]</tt> or <tt>[$-40C]</tt>. These
|
||||||
* are (currently) largely ignored. You can use {@link DateFormatConverter}
|
* are (currently) largely ignored. You can use {@link DateFormatConverter}
|
||||||
* to look these up into Java Locales if desired.
|
* to look these up into Java Locales if desired.
|
||||||
* <p/>
|
* <p/>
|
||||||
@ -78,6 +78,8 @@ import org.apache.poi.ss.util.DateFormatConverter;
|
|||||||
* code for formatting numbers.
|
* code for formatting numbers.
|
||||||
* TODO Re-use parts of this logic with {@link ConditionalFormatting} /
|
* TODO Re-use parts of this logic with {@link ConditionalFormatting} /
|
||||||
* {@link ConditionalFormattingRule} for reporting stylings which do/don't apply
|
* {@link ConditionalFormattingRule} for reporting stylings which do/don't apply
|
||||||
|
* TODO Support the full set of modifiers, including alternate calendars and
|
||||||
|
* native character numbers, as documented at https://help.libreoffice.org/Common/Number_Format_Codes
|
||||||
*/
|
*/
|
||||||
public class CellFormat {
|
public class CellFormat {
|
||||||
private final String format;
|
private final String format;
|
||||||
|
@ -19,6 +19,7 @@ package org.apache.poi.ss.format;
|
|||||||
import org.apache.poi.hssf.util.HSSFColor;
|
import org.apache.poi.hssf.util.HSSFColor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -80,6 +81,8 @@ public class CellFormatPart {
|
|||||||
public static final Pattern CONDITION_PAT;
|
public static final Pattern CONDITION_PAT;
|
||||||
/** Pattern for the format specification part of a cell format part. */
|
/** Pattern for the format specification part of a cell format part. */
|
||||||
public static final Pattern SPECIFICATION_PAT;
|
public static final Pattern SPECIFICATION_PAT;
|
||||||
|
/** Pattern for the currency symbol part of a cell format part */
|
||||||
|
public static final Pattern CURRENCY_PAT;
|
||||||
/** Pattern for an entire cell single part. */
|
/** Pattern for an entire cell single part. */
|
||||||
public static final Pattern FORMAT_PAT;
|
public static final Pattern FORMAT_PAT;
|
||||||
|
|
||||||
@ -106,6 +109,9 @@ public class CellFormatPart {
|
|||||||
String condition = "([<>=]=?|!=|<>) # The operator\n" +
|
String condition = "([<>=]=?|!=|<>) # The operator\n" +
|
||||||
" \\s*([0-9]+(?:\\.[0-9]*)?)\\s* # The constant to test against\n";
|
" \\s*([0-9]+(?:\\.[0-9]*)?)\\s* # The constant to test against\n";
|
||||||
|
|
||||||
|
// A currency symbol / string, in a specific locale
|
||||||
|
String currency = "(\\[\\$.{0,3}-[0-9a-f]{3}\\])";
|
||||||
|
|
||||||
String color =
|
String color =
|
||||||
"\\[(black|blue|cyan|green|magenta|red|white|yellow|color [0-9]+)\\]";
|
"\\[(black|blue|cyan|green|magenta|red|white|yellow|color [0-9]+)\\]";
|
||||||
|
|
||||||
@ -115,6 +121,7 @@ public class CellFormatPart {
|
|||||||
// A part of a specification
|
// A part of a specification
|
||||||
String part = "\\\\. # Quoted single character\n" +
|
String part = "\\\\. # Quoted single character\n" +
|
||||||
"|\"([^\\\\\"]|\\\\.)*\" # Quoted string of characters (handles escaped quotes like \\\") \n" +
|
"|\"([^\\\\\"]|\\\\.)*\" # Quoted string of characters (handles escaped quotes like \\\") \n" +
|
||||||
|
"|"+currency+" # Currency symbol in a given locale\n" +
|
||||||
"|_. # Space as wide as a given character\n" +
|
"|_. # Space as wide as a given character\n" +
|
||||||
"|\\*. # Repeating fill character\n" +
|
"|\\*. # Repeating fill character\n" +
|
||||||
"|@ # Text: cell text\n" +
|
"|@ # Text: cell text\n" +
|
||||||
@ -131,14 +138,15 @@ public class CellFormatPart {
|
|||||||
"|\\[s{1,2}\\] # Elapsed time: second spec\n" +
|
"|\\[s{1,2}\\] # Elapsed time: second spec\n" +
|
||||||
"|[^;] # A character\n" + "";
|
"|[^;] # A character\n" + "";
|
||||||
|
|
||||||
String format = "(?:" + color + ")? # Text color\n" +
|
String format = "(?:" + color + ")? # Text color\n" +
|
||||||
"(?:\\[" + condition + "\\])? # Condition\n" +
|
"(?:\\[" + condition + "\\])? # Condition\n" +
|
||||||
"((?:" + part + ")+) # Format spec\n";
|
"((?:" + part + ")+) # Format spec\n";
|
||||||
|
|
||||||
int flags = Pattern.COMMENTS | Pattern.CASE_INSENSITIVE;
|
int flags = Pattern.COMMENTS | Pattern.CASE_INSENSITIVE;
|
||||||
COLOR_PAT = Pattern.compile(color, flags);
|
COLOR_PAT = Pattern.compile(color, flags);
|
||||||
CONDITION_PAT = Pattern.compile(condition, flags);
|
CONDITION_PAT = Pattern.compile(condition, flags);
|
||||||
SPECIFICATION_PAT = Pattern.compile(part, flags);
|
SPECIFICATION_PAT = Pattern.compile(part, flags);
|
||||||
|
CURRENCY_PAT = Pattern.compile(currency, flags);
|
||||||
FORMAT_PAT = Pattern.compile(format, flags);
|
FORMAT_PAT = Pattern.compile(format, flags);
|
||||||
|
|
||||||
// Calculate the group numbers of important groups. (They shift around
|
// Calculate the group numbers of important groups. (They shift around
|
||||||
@ -196,7 +204,7 @@ public class CellFormatPart {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of the first group that is the same as the marker
|
* Returns the number of the first group that is the same as the marker
|
||||||
* string. The search starts with group 1.
|
* string. Starts from group 1.
|
||||||
*
|
*
|
||||||
* @param pat The pattern to use.
|
* @param pat The pattern to use.
|
||||||
* @param str The string to match against the pattern.
|
* @param str The string to match against the pattern.
|
||||||
@ -278,6 +286,22 @@ public class CellFormatPart {
|
|||||||
*/
|
*/
|
||||||
private CellFormatter getFormatter(Matcher matcher) {
|
private CellFormatter getFormatter(Matcher matcher) {
|
||||||
String fdesc = matcher.group(SPECIFICATION_GROUP);
|
String fdesc = matcher.group(SPECIFICATION_GROUP);
|
||||||
|
|
||||||
|
// For now, we don't support localised currencies, so simplify if there
|
||||||
|
Matcher currencyM = CURRENCY_PAT.matcher(fdesc);
|
||||||
|
if (currencyM.find()) {
|
||||||
|
String currencyPart = currencyM.group(1);
|
||||||
|
String currencyRepl;
|
||||||
|
if (currencyPart.startsWith("[$-")) {
|
||||||
|
// Default $ in a different locale
|
||||||
|
currencyRepl = "$";
|
||||||
|
} else {
|
||||||
|
currencyRepl = currencyPart.substring(2, currencyPart.lastIndexOf('-'));
|
||||||
|
}
|
||||||
|
fdesc = fdesc.replace(currencyPart, currencyRepl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a formatter for this simplified string
|
||||||
return type.formatter(fdesc);
|
return type.formatter(fdesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,8 +322,14 @@ public class CellFormatPart {
|
|||||||
boolean seenZero = false;
|
boolean seenZero = false;
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
String repl = m.group(0);
|
String repl = m.group(0);
|
||||||
|
|
||||||
if (repl.length() > 0) {
|
if (repl.length() > 0) {
|
||||||
switch (repl.charAt(0)) {
|
char c1 = repl.charAt(0);
|
||||||
|
char c2 = 0;
|
||||||
|
if (repl.length() > 1)
|
||||||
|
c2 = Character.toLowerCase(repl.charAt(1));
|
||||||
|
|
||||||
|
switch (c1) {
|
||||||
case '@':
|
case '@':
|
||||||
return CellFormatType.TEXT;
|
return CellFormatType.TEXT;
|
||||||
case 'd':
|
case 'd':
|
||||||
@ -321,7 +351,16 @@ public class CellFormatPart {
|
|||||||
seenZero = true;
|
seenZero = true;
|
||||||
break;
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
return CellFormatType.ELAPSED;
|
if (c2 == 'h' || c2 == 'm' || c2 == 's') {
|
||||||
|
return CellFormatType.ELAPSED;
|
||||||
|
}
|
||||||
|
if (c2 == '$') {
|
||||||
|
// Localised currency
|
||||||
|
return CellFormatType.NUMBER;
|
||||||
|
}
|
||||||
|
// Something else inside [] which isn't supported!
|
||||||
|
throw new IllegalArgumentException("Unsupported [] format block '" +
|
||||||
|
repl + "' in '" + fdesc + "'");
|
||||||
case '#':
|
case '#':
|
||||||
case '?':
|
case '?':
|
||||||
return CellFormatType.NUMBER;
|
return CellFormatType.NUMBER;
|
||||||
|
@ -36,7 +36,6 @@ import org.apache.poi.ss.usermodel.Workbook;
|
|||||||
import org.apache.poi.util.LocaleUtil;
|
import org.apache.poi.util.LocaleUtil;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class TestCellFormat {
|
public class TestCellFormat {
|
||||||
@ -918,7 +917,7 @@ public class TestCellFormat {
|
|||||||
Row row = sheet.createRow(0);
|
Row row = sheet.createRow(0);
|
||||||
Cell cell = row.createCell(0);
|
Cell cell = row.createCell(0);
|
||||||
cell.setCellValue(123456.6);
|
cell.setCellValue(123456.6);
|
||||||
System.out.println(cf1.apply(cell).text);
|
//System.out.println(cf1.apply(cell).text);
|
||||||
assertEquals("123456 3/5", cf1.apply(cell).text);
|
assertEquals("123456 3/5", cf1.apply(cell).text);
|
||||||
} finally {
|
} finally {
|
||||||
wb.close();
|
wb.close();
|
||||||
@ -926,7 +925,6 @@ public class TestCellFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("TODO") // TODO
|
|
||||||
public void testAccountingFormats() throws IOException {
|
public void testAccountingFormats() throws IOException {
|
||||||
char pound = '\u00A3';
|
char pound = '\u00A3';
|
||||||
char euro = '\u20AC';
|
char euro = '\u20AC';
|
||||||
@ -934,11 +932,11 @@ public class TestCellFormat {
|
|||||||
// Accounting -> 0 decimal places, default currency symbol
|
// Accounting -> 0 decimal places, default currency symbol
|
||||||
String formatDft = "_-\"$\"* #,##0_-;\\-\"$\"* #,##0_-;_-\"$\"* \"-\"_-;_-@_-";
|
String formatDft = "_-\"$\"* #,##0_-;\\-\"$\"* #,##0_-;_-\"$\"* \"-\"_-;_-@_-";
|
||||||
// Accounting -> 0 decimal places, US currency symbol
|
// Accounting -> 0 decimal places, US currency symbol
|
||||||
String formatUS = "_-[$$-409]* #,##0_ ;_-[$$-409]* -#,##0 ;_-[$$-409]* \"-\"_ ;_-@_ ";
|
String formatUS = "_-[$$-409]* #,##0_ ;_-[$$-409]* -#,##0 ;_-[$$-409]* \"-\"_-;_-@_-";
|
||||||
// Accounting -> 0 decimal places, UK currency symbol
|
// Accounting -> 0 decimal places, UK currency symbol
|
||||||
String formatUK = "_-[$"+pound+"-809]* #,##0_-;\\-[$"+pound+"-809]* #,##0_-;_-[$"+pound+"-809]* \"-\"??_-;_-@_-";
|
String formatUK = "_-[$"+pound+"-809]* #,##0_-;\\-[$"+pound+"-809]* #,##0_-;_-[$"+pound+"-809]* \"-\"??_-;_-@_-";
|
||||||
// Accounting -> 0 decimal places, French currency symbol
|
// French style accounting, euro sign comes after not before
|
||||||
String formatFR = "_-[$"+euro+"-40C]* #,##0_-;\\-[$"+euro+"-40C]* #,##0_-;_-[$"+euro+"-40C]* \"-\"??_-;_-@_-";
|
String formatFR = "_-#,##0* [$"+euro+"-40C]_-;\\-#,##0* [$"+euro+"-40C]_-;_-\"-\"??* [$"+euro+"-40C] _-;_-@_-";
|
||||||
|
|
||||||
// Has +ve, -ve and zero rules
|
// Has +ve, -ve and zero rules
|
||||||
CellFormat cfDft = CellFormat.getInstance(formatDft);
|
CellFormat cfDft = CellFormat.getInstance(formatDft);
|
||||||
@ -947,17 +945,34 @@ public class TestCellFormat {
|
|||||||
CellFormat cfFR = CellFormat.getInstance(formatFR);
|
CellFormat cfFR = CellFormat.getInstance(formatFR);
|
||||||
|
|
||||||
// For +ve numbers, should be Space + currency symbol + spaces + whole number with commas + space
|
// For +ve numbers, should be Space + currency symbol + spaces + whole number with commas + space
|
||||||
assertEquals(" $ 12 ",cfDft.apply(Double.valueOf(12.33)).text);
|
// (Except French, which is mostly reversed...)
|
||||||
assertEquals(" $ 12 ", cfUS.apply(Double.valueOf(12.33)).text);
|
assertEquals(" $ 12 ", cfDft.apply(Double.valueOf(12.33)).text);
|
||||||
|
assertEquals(" $ 12 ", cfUS.apply(Double.valueOf(12.33)).text);
|
||||||
assertEquals(" "+pound+" 12 ", cfUK.apply(Double.valueOf(12.33)).text);
|
assertEquals(" "+pound+" 12 ", cfUK.apply(Double.valueOf(12.33)).text);
|
||||||
assertEquals(" "+pound+" 12 ", cfFR.apply(Double.valueOf(12.33)).text);
|
assertEquals(" 12 "+euro+" ", cfFR.apply(Double.valueOf(12.33)).text);
|
||||||
assertEquals(" "+pound+" 16,789 ", cfUK.apply(Double.valueOf(16789.2)).text);
|
|
||||||
// TODO More
|
|
||||||
|
|
||||||
// For -ve numbers, should be Minus + currency symbol + spaces + whole number with commas
|
assertEquals(" $ 16,789 ", cfDft.apply(Double.valueOf(16789.2)).text);
|
||||||
// TODO
|
assertEquals(" $ 16,789 ", cfUS.apply(Double.valueOf(16789.2)).text);
|
||||||
|
assertEquals(" "+pound+" 16,789 ", cfUK.apply(Double.valueOf(16789.2)).text);
|
||||||
|
assertEquals(" 16,789 "+euro+" ", cfFR.apply(Double.valueOf(16789.2)).text);
|
||||||
|
|
||||||
|
// For -ve numbers, gets a bit more complicated...
|
||||||
|
assertEquals("-$ 12 ", cfDft.apply(Double.valueOf(-12.33)).text);
|
||||||
|
assertEquals(" $ -12 ", cfUS.apply(Double.valueOf(-12.33)).text);
|
||||||
|
assertEquals("-"+pound+" 12 ", cfUK.apply(Double.valueOf(-12.33)).text);
|
||||||
|
assertEquals("-12 "+euro+" ", cfFR.apply(Double.valueOf(-12.33)).text);
|
||||||
|
|
||||||
|
assertEquals("-$ 16,789 ", cfDft.apply(Double.valueOf(-16789.2)).text);
|
||||||
|
assertEquals(" $ -16,789 ", cfUS.apply(Double.valueOf(-16789.2)).text);
|
||||||
|
assertEquals("-"+pound+" 16,789 ", cfUK.apply(Double.valueOf(-16789.2)).text);
|
||||||
|
assertEquals("-16,789 "+euro+" ", cfFR.apply(Double.valueOf(-16789.2)).text);
|
||||||
|
|
||||||
// For zero, should be Space + currency symbol + spaces + Minus + spaces
|
// For zero, should be Space + currency symbol + spaces + Minus + spaces
|
||||||
// TODO
|
assertEquals(" $ - ", cfDft.apply(Double.valueOf(0)).text);
|
||||||
|
// TODO Fix the exception this incorrectly triggers
|
||||||
|
//assertEquals(" $ - ", cfUS.apply(Double.valueOf(0)).text);
|
||||||
|
// TODO Fix these to not have an incorrect bonus 0 on the end
|
||||||
|
//assertEquals(" "+pound+" - ", cfUK.apply(Double.valueOf(0)).text);
|
||||||
|
//assertEquals(" - "+euro+" ", cfFR.apply(Double.valueOf(0)).text);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -131,4 +131,38 @@ public abstract class BaseTestDataFormat extends TestCase {
|
|||||||
exp3dp, fmt.formatCellValue(r.getCell(3), eval));
|
exp3dp, fmt.formatCellValue(r.getCell(3), eval));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localised accountancy formats
|
||||||
|
*/
|
||||||
|
public final void test58536() {
|
||||||
|
Workbook wb = _testDataProvider.createWorkbook();
|
||||||
|
DataFormatter formatter = new DataFormatter();
|
||||||
|
DataFormat fmt = wb.createDataFormat();
|
||||||
|
Sheet sheet = wb.createSheet();
|
||||||
|
Row r = sheet.createRow(0);
|
||||||
|
|
||||||
|
char pound = '\u00A3';
|
||||||
|
String formatUK = "_-[$"+pound+"-809]* #,##0_-;\\-[$"+pound+"-809]* #,##0_-;_-[$"+pound+"-809]* \"-\"??_-;_-@_-";
|
||||||
|
|
||||||
|
CellStyle cs = wb.createCellStyle();
|
||||||
|
cs.setDataFormat(fmt.getFormat(formatUK));
|
||||||
|
|
||||||
|
Cell pve = r.createCell(0);
|
||||||
|
pve.setCellValue(12345);
|
||||||
|
pve.setCellStyle(cs);
|
||||||
|
|
||||||
|
Cell nve = r.createCell(1);
|
||||||
|
nve.setCellValue(-12345);
|
||||||
|
nve.setCellStyle(cs);
|
||||||
|
|
||||||
|
Cell zero = r.createCell(2);
|
||||||
|
zero.setCellValue(0);
|
||||||
|
zero.setCellStyle(cs);
|
||||||
|
|
||||||
|
assertEquals(pound+" 12,345", formatter.formatCellValue(pve));
|
||||||
|
assertEquals("-"+pound+" 12,345", formatter.formatCellValue(nve));
|
||||||
|
// TODO Fix this to not have an extra 0 at the end
|
||||||
|
//assertEquals(pound+" - ", formatter.formatCellValue(zero));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user