git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1710484 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_14_BETA1
@@ -66,7 +66,7 @@ import org.apache.poi.ss.util.DateFormatConverter; | |||
* surround by brackets). </dl> | |||
* <p/> | |||
* 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} | |||
* to look these up into Java Locales if desired. | |||
* <p/> | |||
@@ -78,6 +78,8 @@ import org.apache.poi.ss.util.DateFormatConverter; | |||
* code for formatting numbers. | |||
* TODO Re-use parts of this logic with {@link ConditionalFormatting} / | |||
* {@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 { | |||
private final String format; |
@@ -19,6 +19,7 @@ package org.apache.poi.ss.format; | |||
import org.apache.poi.hssf.util.HSSFColor; | |||
import javax.swing.*; | |||
import java.awt.*; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
@@ -80,6 +81,8 @@ public class CellFormatPart { | |||
public static final Pattern CONDITION_PAT; | |||
/** Pattern for the format specification part of a cell format part. */ | |||
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. */ | |||
public static final Pattern FORMAT_PAT; | |||
@@ -105,6 +108,9 @@ public class CellFormatPart { | |||
// A condition specification | |||
String condition = "([<>=]=?|!=|<>) # The operator\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 = | |||
"\\[(black|blue|cyan|green|magenta|red|white|yellow|color [0-9]+)\\]"; | |||
@@ -115,6 +121,7 @@ public class CellFormatPart { | |||
// A part of a specification | |||
String part = "\\\\. # Quoted single character\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" + | |||
"|\\*. # Repeating fill character\n" + | |||
"|@ # Text: cell text\n" + | |||
@@ -131,14 +138,15 @@ public class CellFormatPart { | |||
"|\\[s{1,2}\\] # Elapsed time: second spec\n" + | |||
"|[^;] # A character\n" + ""; | |||
String format = "(?:" + color + ")? # Text color\n" + | |||
"(?:\\[" + condition + "\\])? # Condition\n" + | |||
String format = "(?:" + color + ")? # Text color\n" + | |||
"(?:\\[" + condition + "\\])? # Condition\n" + | |||
"((?:" + part + ")+) # Format spec\n"; | |||
int flags = Pattern.COMMENTS | Pattern.CASE_INSENSITIVE; | |||
COLOR_PAT = Pattern.compile(color, flags); | |||
CONDITION_PAT = Pattern.compile(condition, flags); | |||
SPECIFICATION_PAT = Pattern.compile(part, flags); | |||
CURRENCY_PAT = Pattern.compile(currency, flags); | |||
FORMAT_PAT = Pattern.compile(format, flags); | |||
// 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 | |||
* string. The search starts with group 1. | |||
* string. Starts from group 1. | |||
* | |||
* @param pat The pattern to use. | |||
* @param str The string to match against the pattern. | |||
@@ -278,6 +286,22 @@ public class CellFormatPart { | |||
*/ | |||
private CellFormatter getFormatter(Matcher matcher) { | |||
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); | |||
} | |||
@@ -298,8 +322,14 @@ public class CellFormatPart { | |||
boolean seenZero = false; | |||
while (m.find()) { | |||
String repl = m.group(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 '@': | |||
return CellFormatType.TEXT; | |||
case 'd': | |||
@@ -321,7 +351,16 @@ public class CellFormatPart { | |||
seenZero = true; | |||
break; | |||
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 '?': | |||
return CellFormatType.NUMBER; |
@@ -36,7 +36,6 @@ import org.apache.poi.ss.usermodel.Workbook; | |||
import org.apache.poi.util.LocaleUtil; | |||
import org.junit.AfterClass; | |||
import org.junit.BeforeClass; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
public class TestCellFormat { | |||
@@ -918,7 +917,7 @@ public class TestCellFormat { | |||
Row row = sheet.createRow(0); | |||
Cell cell = row.createCell(0); | |||
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); | |||
} finally { | |||
wb.close(); | |||
@@ -926,7 +925,6 @@ public class TestCellFormat { | |||
} | |||
@Test | |||
@Ignore("TODO") // TODO | |||
public void testAccountingFormats() throws IOException { | |||
char pound = '\u00A3'; | |||
char euro = '\u20AC'; | |||
@@ -934,11 +932,11 @@ public class TestCellFormat { | |||
// Accounting -> 0 decimal places, default currency symbol | |||
String formatDft = "_-\"$\"* #,##0_-;\\-\"$\"* #,##0_-;_-\"$\"* \"-\"_-;_-@_-"; | |||
// 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 | |||
String formatUK = "_-[$"+pound+"-809]* #,##0_-;\\-[$"+pound+"-809]* #,##0_-;_-[$"+pound+"-809]* \"-\"??_-;_-@_-"; | |||
// Accounting -> 0 decimal places, French currency symbol | |||
String formatFR = "_-[$"+euro+"-40C]* #,##0_-;\\-[$"+euro+"-40C]* #,##0_-;_-[$"+euro+"-40C]* \"-\"??_-;_-@_-"; | |||
// French style accounting, euro sign comes after not before | |||
String formatFR = "_-#,##0* [$"+euro+"-40C]_-;\\-#,##0* [$"+euro+"-40C]_-;_-\"-\"??* [$"+euro+"-40C] _-;_-@_-"; | |||
// Has +ve, -ve and zero rules | |||
CellFormat cfDft = CellFormat.getInstance(formatDft); | |||
@@ -947,17 +945,34 @@ public class TestCellFormat { | |||
CellFormat cfFR = CellFormat.getInstance(formatFR); | |||
// For +ve numbers, should be Space + currency symbol + spaces + whole number with commas + space | |||
assertEquals(" $ 12 ",cfDft.apply(Double.valueOf(12.33)).text); | |||
assertEquals(" $ 12 ", cfUS.apply(Double.valueOf(12.33)).text); | |||
// (Except French, which is mostly reversed...) | |||
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 ", cfFR.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); | |||
// TODO More | |||
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); | |||
// For -ve numbers, should be Minus + currency symbol + spaces + whole number with commas | |||
// TODO | |||
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 | |||
// 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)); | |||
} | |||
} | |||
/** | |||
* 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)); | |||
} | |||
} |