[Bug 60422] fix data formatter issue with specific format in German locale

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1800713 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2017-07-03 20:56:02 +00:00
parent 68be989d22
commit 86f7d0b896
9 changed files with 213 additions and 89 deletions

View File

@ -138,6 +138,16 @@ public class CellDateFormatter extends CellFormatter {
* @param format The format. * @param format The format.
*/ */
public CellDateFormatter(String format) { public CellDateFormatter(String format) {
this(LocaleUtil.getUserLocale(), format);
}
/**
* Creates a new date formatter with the given specification.
*
* @param locale The locale.
* @param format The format.
*/
public CellDateFormatter(Locale locale, String format) {
super(format); super(format);
DatePartHandler partHandler = new DatePartHandler(); DatePartHandler partHandler = new DatePartHandler();
StringBuffer descBuf = CellFormatPart.parseFormat(format, StringBuffer descBuf = CellFormatPart.parseFormat(format,
@ -146,7 +156,7 @@ public class CellDateFormatter extends CellFormatter {
// tweak the format pattern to pass tests on JDK 1.7, // tweak the format pattern to pass tests on JDK 1.7,
// See https://issues.apache.org/bugzilla/show_bug.cgi?id=53369 // See https://issues.apache.org/bugzilla/show_bug.cgi?id=53369
String ptrn = descBuf.toString().replaceAll("((y)(?!y))(?<!yy)", "yy"); String ptrn = descBuf.toString().replaceAll("((y)(?!y))(?<!yy)", "yy");
dateFmt = new SimpleDateFormat(ptrn, LocaleUtil.getUserLocale()); dateFmt = new SimpleDateFormat(ptrn, locale);
dateFmt.setTimeZone(LocaleUtil.getUserTimeZone()); dateFmt.setTimeZone(LocaleUtil.getUserTimeZone());
} }
@ -182,7 +192,7 @@ public class CellDateFormatter extends CellFormatter {
Formatter formatter = new Formatter(toAppendTo, Locale.ROOT); Formatter formatter = new Formatter(toAppendTo, Locale.ROOT);
try { try {
long msecs = dateObj.getTime() % 1000; long msecs = dateObj.getTime() % 1000;
formatter.format(LocaleUtil.getUserLocale(), sFmt, msecs / 1000.0); formatter.format(locale, sFmt, msecs / 1000.0);
} finally { } finally {
formatter.close(); formatter.close();
} }

View File

@ -20,6 +20,7 @@ package org.apache.poi.ss.format;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.logging.Level; import java.util.logging.Level;
@ -35,6 +36,7 @@ import org.apache.poi.ss.usermodel.ConditionalFormattingRule;
import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.util.DateFormatConverter; import org.apache.poi.ss.util.DateFormatConverter;
import org.apache.poi.util.LocaleUtil;
/** /**
* Format a value according to the standard Excel behavior. This "standard" is * Format a value according to the standard Excel behavior. This "standard" is
@ -90,6 +92,7 @@ import org.apache.poi.ss.util.DateFormatConverter;
* native character numbers, as documented at https://help.libreoffice.org/Common/Number_Format_Codes * native character numbers, as documented at https://help.libreoffice.org/Common/Number_Format_Codes
*/ */
public class CellFormat { public class CellFormat {
private final Locale locale;
private final String format; private final String format;
private final CellFormatPart posNumFmt; private final CellFormatPart posNumFmt;
private final CellFormatPart zeroNumFmt; private final CellFormatPart zeroNumFmt;
@ -101,9 +104,6 @@ public class CellFormat {
CellFormatPart.FORMAT_PAT.pattern() + "(;|$)", CellFormatPart.FORMAT_PAT.pattern() + "(;|$)",
Pattern.COMMENTS | Pattern.CASE_INSENSITIVE); Pattern.COMMENTS | Pattern.CASE_INSENSITIVE);
private static final CellFormatPart DEFAULT_TEXT_FORMAT =
new CellFormatPart("@");
/* /*
* Cells that cannot be formatted, e.g. cells that have a date or time * Cells that cannot be formatted, e.g. cells that have a date or time
* format and have an invalid date or time value, are displayed as 255 * format and have an invalid date or time value, are displayed as 255
@ -121,18 +121,23 @@ public class CellFormat {
/** /**
* Format a value as it would be were no format specified. This is also * Format a value as it would be were no format specified. This is also
* used when the format specified is <tt>General</tt>. * used when the format specified is <tt>General</tt>.
* @deprecated use {@link #getInstance(Locale, "General")} instead
*/ */
public static final CellFormat GENERAL_FORMAT = new CellFormat("General") { public static final CellFormat GENERAL_FORMAT = createGeneralFormat(LocaleUtil.getUserLocale());
@Override
public CellFormatResult apply(Object value) { private static CellFormat createGeneralFormat(final Locale locale) {
String text = (new CellGeneralFormatter()).format(value); return new CellFormat(locale, "General") {
return new CellFormatResult(true, text, null); @Override
} public CellFormatResult apply(Object value) {
}; String text = (new CellGeneralFormatter(locale)).format(value);
return new CellFormatResult(true, text, null);
}
};
}
/** Maps a format string to its parsed version for efficiencies sake. */ /** Maps a format string to its parsed version for efficiencies sake. */
private static final Map<String, CellFormat> formatCache = private static final Map<Locale, Map<String, CellFormat>> formatCache =
new WeakHashMap<String, CellFormat>(); new WeakHashMap<Locale, Map<String, CellFormat>>();
/** /**
* Returns a {@link CellFormat} that applies the given format. Two calls * Returns a {@link CellFormat} that applies the given format. Two calls
@ -143,13 +148,31 @@ public class CellFormat {
* @return A {@link CellFormat} that applies the given format. * @return A {@link CellFormat} that applies the given format.
*/ */
public static CellFormat getInstance(String format) { public static CellFormat getInstance(String format) {
CellFormat fmt = formatCache.get(format); return getInstance(LocaleUtil.getUserLocale(), format);
}
/**
* Returns a {@link CellFormat} that applies the given format. Two calls
* with the same format may or may not return the same object.
*
* @param locale The locale.
* @param format The format.
*
* @return A {@link CellFormat} that applies the given format.
*/
public static synchronized CellFormat getInstance(Locale locale, String format) {
Map<String, CellFormat> formatMap = formatCache.get(locale);
if (formatMap == null) {
formatMap = new WeakHashMap<String, CellFormat>();
formatCache.put(locale, formatMap);
}
CellFormat fmt = formatMap.get(format);
if (fmt == null) { if (fmt == null) {
if (format.equals("General") || format.equals("@")) if (format.equals("General") || format.equals("@"))
fmt = GENERAL_FORMAT; fmt = createGeneralFormat(locale);
else else
fmt = new CellFormat(format); fmt = new CellFormat(locale, format);
formatCache.put(format, fmt); formatMap.put(format, fmt);
} }
return fmt; return fmt;
} }
@ -159,8 +182,10 @@ public class CellFormat {
* *
* @param format The format. * @param format The format.
*/ */
private CellFormat(String format) { private CellFormat(Locale locale, String format) {
this.locale = locale;
this.format = format; this.format = format;
CellFormatPart defaultTextFormat = new CellFormatPart(locale, "@");
Matcher m = ONE_PART.matcher(format); Matcher m = ONE_PART.matcher(format);
List<CellFormatPart> parts = new ArrayList<CellFormatPart>(); List<CellFormatPart> parts = new ArrayList<CellFormatPart>();
@ -172,7 +197,7 @@ public class CellFormat {
if (valueDesc.endsWith(";")) if (valueDesc.endsWith(";"))
valueDesc = valueDesc.substring(0, valueDesc.length() - 1); valueDesc = valueDesc.substring(0, valueDesc.length() - 1);
parts.add(new CellFormatPart(valueDesc)); parts.add(new CellFormatPart(locale, valueDesc));
} catch (RuntimeException e) { } catch (RuntimeException e) {
CellFormatter.logger.log(Level.WARNING, CellFormatter.logger.log(Level.WARNING,
"Invalid format: " + CellFormatter.quote(m.group()), e); "Invalid format: " + CellFormatter.quote(m.group()), e);
@ -187,19 +212,19 @@ public class CellFormat {
posNumFmt = parts.get(0); posNumFmt = parts.get(0);
negNumFmt = null; negNumFmt = null;
zeroNumFmt = null; zeroNumFmt = null;
textFmt = DEFAULT_TEXT_FORMAT; textFmt = defaultTextFormat;
break; break;
case 2: case 2:
posNumFmt = parts.get(0); posNumFmt = parts.get(0);
negNumFmt = parts.get(1); negNumFmt = parts.get(1);
zeroNumFmt = null; zeroNumFmt = null;
textFmt = DEFAULT_TEXT_FORMAT; textFmt = defaultTextFormat;
break; break;
case 3: case 3:
posNumFmt = parts.get(0); posNumFmt = parts.get(0);
negNumFmt = parts.get(1); negNumFmt = parts.get(1);
zeroNumFmt = parts.get(2); zeroNumFmt = parts.get(2);
textFmt = DEFAULT_TEXT_FORMAT; textFmt = defaultTextFormat;
break; break;
case 4: case 4:
default: default:
@ -384,7 +409,7 @@ public class CellFormat {
|| (posNumFmt.hasCondition() && posNumFmt.applies(val))) { || (posNumFmt.hasCondition() && posNumFmt.applies(val))) {
return posNumFmt; return posNumFmt;
} else { } else {
return new CellFormatPart("General"); return new CellFormatPart(locale, "General");
} }
} else if (formatPartCount == 2) { } else if (formatPartCount == 2) {
if ((!posNumFmt.hasCondition() && val >= 0) if ((!posNumFmt.hasCondition() && val >= 0)

View File

@ -17,6 +17,7 @@
package org.apache.poi.ss.format; package org.apache.poi.ss.format;
import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.util.LocaleUtil;
import javax.swing.*; import javax.swing.*;
@ -173,6 +174,16 @@ public class CellFormatPart {
* @param desc The string to parse. * @param desc The string to parse.
*/ */
public CellFormatPart(String desc) { public CellFormatPart(String desc) {
this(LocaleUtil.getUserLocale(), desc);
}
/**
* Create an object to represent a format part.
*
* @param locale The locale to use.
* @param desc The string to parse.
*/
public CellFormatPart(Locale locale, String desc) {
Matcher m = FORMAT_PAT.matcher(desc); Matcher m = FORMAT_PAT.matcher(desc);
if (!m.matches()) { if (!m.matches()) {
throw new IllegalArgumentException("Unrecognized format: " + quote( throw new IllegalArgumentException("Unrecognized format: " + quote(
@ -181,7 +192,7 @@ public class CellFormatPart {
color = getColor(m); color = getColor(m);
condition = getCondition(m); condition = getCondition(m);
type = getCellFormatType(m); type = getCellFormatType(m);
format = getFormatter(m); format = getFormatter(locale, m);
} }
/** /**
@ -287,7 +298,7 @@ public class CellFormatPart {
* *
* @return The formatter. * @return The formatter.
*/ */
private CellFormatter getFormatter(Matcher matcher) { private CellFormatter getFormatter(Locale locale, 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 // For now, we don't support localised currencies, so simplify if there
@ -305,7 +316,7 @@ public class CellFormatPart {
} }
// Build a formatter for this simplified string // Build a formatter for this simplified string
return type.formatter(fdesc); return type.formatter(locale, fdesc);
} }
/** /**

View File

@ -17,6 +17,8 @@
package org.apache.poi.ss.format; package org.apache.poi.ss.format;
import java.util.Locale;
/** /**
* The different kinds of formats that the formatter understands. * The different kinds of formats that the formatter understands.
* *
@ -26,11 +28,14 @@ public enum CellFormatType {
/** The general (default) format; also used for <tt>"General"</tt>. */ /** The general (default) format; also used for <tt>"General"</tt>. */
GENERAL { GENERAL {
boolean isSpecial(char ch) {
return false;
}
CellFormatter formatter(String pattern) { CellFormatter formatter(String pattern) {
return new CellGeneralFormatter(); return new CellGeneralFormatter();
} }
boolean isSpecial(char ch) { CellFormatter formatter(Locale locale, String pattern) {
return false; return new CellGeneralFormatter(locale);
} }
}, },
/** A numeric format. */ /** A numeric format. */
@ -41,6 +46,9 @@ public enum CellFormatType {
CellFormatter formatter(String pattern) { CellFormatter formatter(String pattern) {
return new CellNumberFormatter(pattern); return new CellNumberFormatter(pattern);
} }
CellFormatter formatter(Locale locale, String pattern) {
return new CellNumberFormatter(locale, pattern);
}
}, },
/** A date format. */ /** A date format. */
DATE { DATE {
@ -50,6 +58,9 @@ public enum CellFormatType {
CellFormatter formatter(String pattern) { CellFormatter formatter(String pattern) {
return new CellDateFormatter(pattern); return new CellDateFormatter(pattern);
} }
CellFormatter formatter(Locale locale, String pattern) {
return new CellDateFormatter(locale, pattern);
}
}, },
/** An elapsed time format. */ /** An elapsed time format. */
ELAPSED { ELAPSED {
@ -59,6 +70,9 @@ public enum CellFormatType {
CellFormatter formatter(String pattern) { CellFormatter formatter(String pattern) {
return new CellElapsedFormatter(pattern); return new CellElapsedFormatter(pattern);
} }
CellFormatter formatter(Locale locale, String pattern) {
return new CellElapsedFormatter(pattern);
}
}, },
/** A text format. */ /** A text format. */
TEXT { TEXT {
@ -68,6 +82,9 @@ public enum CellFormatType {
CellFormatter formatter(String pattern) { CellFormatter formatter(String pattern) {
return new CellTextFormatter(pattern); return new CellTextFormatter(pattern);
} }
CellFormatter formatter(Locale locale, String pattern) {
return new CellTextFormatter(pattern);
}
}; };
/** /**
@ -88,4 +105,15 @@ public enum CellFormatType {
* @return A new formatter of the appropriate type, for the given pattern. * @return A new formatter of the appropriate type, for the given pattern.
*/ */
abstract CellFormatter formatter(String pattern); abstract CellFormatter formatter(String pattern);
/**
* Returns a new formatter of the appropriate type, for the given pattern.
* The pattern must be appropriate for the type.
*
* @param locale The locale to use.
* @param pattern The pattern to use.
*
* @return A new formatter of the appropriate type, for the given pattern.
*/
abstract CellFormatter formatter(Locale locale, String pattern);
} }

View File

@ -16,8 +16,11 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.ss.format; package org.apache.poi.ss.format;
import java.util.Locale;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.poi.util.LocaleUtil;
/** /**
* This is the abstract supertype for the various cell formatters. * This is the abstract supertype for the various cell formatters.
* *
@ -26,6 +29,7 @@ import java.util.logging.Logger;
public abstract class CellFormatter { public abstract class CellFormatter {
/** The original specified format. */ /** The original specified format. */
protected final String format; protected final String format;
protected final Locale locale;
/** /**
* Creates a new formatter object, storing the format in {@link #format}. * Creates a new formatter object, storing the format in {@link #format}.
@ -33,6 +37,17 @@ public abstract class CellFormatter {
* @param format The format. * @param format The format.
*/ */
public CellFormatter(String format) { public CellFormatter(String format) {
this(LocaleUtil.getUserLocale(), format);
}
/**
* Creates a new formatter object, storing the format in {@link #format}.
*
* @param locale The locale.
* @param format The format.
*/
public CellFormatter(Locale locale, String format) {
this.locale = locale;
this.format = format; this.format = format;
} }

View File

@ -29,7 +29,11 @@ import org.apache.poi.util.LocaleUtil;
public class CellGeneralFormatter extends CellFormatter { public class CellGeneralFormatter extends CellFormatter {
/** Creates a new general formatter. */ /** Creates a new general formatter. */
public CellGeneralFormatter() { public CellGeneralFormatter() {
super("General"); this(LocaleUtil.getUserLocale());
}
/** Creates a new general formatter. */
public CellGeneralFormatter(Locale locale) {
super(locale, "General");
} }
/** /**
@ -59,9 +63,9 @@ public class CellGeneralFormatter extends CellFormatter {
stripZeros = false; stripZeros = false;
} }
Formatter formatter = new Formatter(toAppendTo, LocaleUtil.getUserLocale()); Formatter formatter = new Formatter(toAppendTo, locale);
try { try {
formatter.format(LocaleUtil.getUserLocale(), fmt, value); formatter.format(locale, fmt, value);
} finally { } finally {
formatter.close(); formatter.close();
} }

View File

@ -26,6 +26,7 @@ import java.util.Formatter;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
@ -48,7 +49,7 @@ public class CellNumberFormatter extends CellFormatter {
private final Special numerator; private final Special numerator;
private final Special afterInteger; private final Special afterInteger;
private final Special afterFractional; private final Special afterFractional;
private final boolean integerCommas; private final boolean showGroupingSeparator;
private final List<Special> specials = new ArrayList<Special>(); private final List<Special> specials = new ArrayList<Special>();
private final List<Special> integerSpecials = new ArrayList<Special>(); private final List<Special> integerSpecials = new ArrayList<Special>();
private final List<Special> fractionalSpecials = new ArrayList<Special>(); private final List<Special> fractionalSpecials = new ArrayList<Special>();
@ -69,13 +70,11 @@ public class CellNumberFormatter extends CellFormatter {
// ("#" for integer values, and "#.#" for floating-point values) is // ("#" for integer values, and "#.#" for floating-point values) is
// different from the 'General' format for numbers ("#" for integer // different from the 'General' format for numbers ("#" for integer
// values and "#.#########" for floating-point values). // values and "#.#########" for floating-point values).
private static final CellFormatter SIMPLE_NUMBER = new GeneralNumberFormatter(); private final CellFormatter SIMPLE_NUMBER = new GeneralNumberFormatter(locale);
private static final CellFormatter SIMPLE_INT = new CellNumberFormatter("#");
private static final CellFormatter SIMPLE_FLOAT = new CellNumberFormatter("#.#");
private static class GeneralNumberFormatter extends CellFormatter { private static class GeneralNumberFormatter extends CellFormatter {
private GeneralNumberFormatter() { private GeneralNumberFormatter(Locale locale) {
super("General"); super(locale, "General");
} }
public void formatValue(StringBuffer toAppendTo, Object value) { public void formatValue(StringBuffer toAppendTo, Object value) {
@ -86,7 +85,8 @@ public class CellNumberFormatter extends CellFormatter {
CellFormatter cf; CellFormatter cf;
if (value instanceof Number) { if (value instanceof Number) {
Number num = (Number) value; Number num = (Number) value;
cf = (num.doubleValue() % 1.0 == 0) ? SIMPLE_INT : SIMPLE_FLOAT; cf = (num.doubleValue() % 1.0 == 0) ? new CellNumberFormatter(locale, "#") :
new CellNumberFormatter(locale, "#.#");
} else { } else {
cf = CellTextFormatter.SIMPLE_TEXT; cf = CellTextFormatter.SIMPLE_TEXT;
} }
@ -124,7 +124,17 @@ public class CellNumberFormatter extends CellFormatter {
* @param format The format to parse. * @param format The format to parse.
*/ */
public CellNumberFormatter(String format) { public CellNumberFormatter(String format) {
super(format); this(LocaleUtil.getUserLocale(), format);
}
/**
* Creates a new cell number formatter.
*
* @param locale The locale to use.
* @param format The format to parse.
*/
public CellNumberFormatter(Locale locale, String format) {
super(locale, format);
CellNumberPartHandler ph = new CellNumberPartHandler(); CellNumberPartHandler ph = new CellNumberPartHandler();
StringBuffer descBuf = CellFormatPart.parseFormat(format, CellFormatType.NUMBER, ph); StringBuffer descBuf = CellFormatPart.parseFormat(format, CellFormatType.NUMBER, ph);
@ -177,7 +187,7 @@ public class CellNumberFormatter extends CellFormatter {
} }
double scaleByRef[] = { ph.getScale() }; double scaleByRef[] = { ph.getScale() };
integerCommas = interpretIntegerCommas(descBuf, specials, decimalPoint, integerEnd(), fractionalEnd(), scaleByRef); showGroupingSeparator = interpretIntegerCommas(descBuf, specials, decimalPoint, integerEnd(), fractionalEnd(), scaleByRef);
if (exponent == null) { if (exponent == null) {
scale = scaleByRef[0]; scale = scaleByRef[0];
} else { } else {
@ -259,14 +269,17 @@ public class CellNumberFormatter extends CellFormatter {
} }
fmtBuf.append('E'); fmtBuf.append('E');
placeZeros(fmtBuf, exponentSpecials.subList(2, exponentSpecials.size())); placeZeros(fmtBuf, exponentSpecials.subList(2, exponentSpecials.size()));
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(LocaleUtil.getUserLocale()); decimalFmt = new DecimalFormat(fmtBuf.toString(), getDecimalFormatSymbols());
decimalFmt = new DecimalFormat(fmtBuf.toString(), dfs);
printfFmt = null; printfFmt = null;
} }
desc = descBuf.toString(); desc = descBuf.toString();
} }
private DecimalFormatSymbols getDecimalFormatSymbols() {
return DecimalFormatSymbols.getInstance(locale);
}
private static void placeZeros(StringBuffer sb, List<Special> specials) { private static void placeZeros(StringBuffer sb, List<Special> specials) {
for (Special s : specials) { for (Special s : specials) {
if (isDigitFmt(s)) { if (isDigitFmt(s)) {
@ -436,7 +449,7 @@ public class CellNumberFormatter extends CellFormatter {
} }
Set<CellNumberStringMod> mods = new TreeSet<CellNumberStringMod>(); Set<CellNumberStringMod> mods = new TreeSet<CellNumberStringMod>();
StringBuffer output = new StringBuffer(desc); StringBuffer output = new StringBuffer(localiseFormat(desc));
if (exponent != null) { if (exponent != null) {
writeScientific(value, output, mods); writeScientific(value, output, mods);
@ -444,21 +457,24 @@ public class CellNumberFormatter extends CellFormatter {
writeFraction(value, null, fractional, output, mods); writeFraction(value, null, fractional, output, mods);
} else { } else {
StringBuffer result = new StringBuffer(); StringBuffer result = new StringBuffer();
Formatter f = new Formatter(result, LocaleUtil.getUserLocale()); Formatter f = new Formatter(result, locale);
try { try {
f.format(LocaleUtil.getUserLocale(), printfFmt, value); f.format(locale, printfFmt, value);
} finally { } finally {
f.close(); f.close();
} }
if (numerator == null) { if (numerator == null) {
writeFractional(result, output); writeFractional(result, output);
writeInteger(result, output, integerSpecials, mods, integerCommas); writeInteger(result, output, integerSpecials, mods, showGroupingSeparator);
} else { } else {
writeFraction(value, result, fractional, output, mods); writeFraction(value, result, fractional, output, mods);
} }
} }
DecimalFormatSymbols dfs = getDecimalFormatSymbols();
String groupingSeparator = Character.toString(dfs.getGroupingSeparator());
// Now strip out any remaining '#'s and add any pending text ... // Now strip out any remaining '#'s and add any pending text ...
Iterator<CellNumberStringMod> changes = mods.iterator(); Iterator<CellNumberStringMod> changes = mods.iterator();
CellNumberStringMod nextChange = (changes.hasNext() ? changes.next() : null); CellNumberStringMod nextChange = (changes.hasNext() ? changes.next() : null);
@ -478,7 +494,7 @@ public class CellNumberFormatter extends CellFormatter {
switch (nextChange.getOp()) { switch (nextChange.getOp()) {
case CellNumberStringMod.AFTER: case CellNumberStringMod.AFTER:
// ignore adding a comma after a deleted char (which was a '#') // ignore adding a comma after a deleted char (which was a '#')
if (nextChange.getToAdd().equals(",") && deletedChars.get(s.pos)) { if (nextChange.getToAdd().equals(groupingSeparator) && deletedChars.get(s.pos)) {
break; break;
} }
output.insert(modPos + 1, nextChange.getToAdd()); output.insert(modPos + 1, nextChange.getToAdd());
@ -545,7 +561,7 @@ public class CellNumberFormatter extends CellFormatter {
StringBuffer result = new StringBuffer(); StringBuffer result = new StringBuffer();
FieldPosition fractionPos = new FieldPosition(DecimalFormat.FRACTION_FIELD); FieldPosition fractionPos = new FieldPosition(DecimalFormat.FRACTION_FIELD);
decimalFmt.format(value, result, fractionPos); decimalFmt.format(value, result, fractionPos);
writeInteger(result, output, integerSpecials, mods, integerCommas); writeInteger(result, output, integerSpecials, mods, showGroupingSeparator);
writeFractional(result, output); writeFractional(result, output);
/* /*
@ -683,6 +699,27 @@ public class CellNumberFormatter extends CellFormatter {
LOG.log(POILogger.ERROR, "error while fraction evaluation", ignored); LOG.log(POILogger.ERROR, "error while fraction evaluation", ignored);
} }
} }
private String localiseFormat(String format) {
DecimalFormatSymbols dfs = getDecimalFormatSymbols();
if(format.contains(",") && dfs.getGroupingSeparator() != ',') {
if(format.contains(".") && dfs.getDecimalSeparator() != '.') {
format = replaceLast(format, "\\.", "[DECIMAL_SEPARATOR]");
format = format.replace(',', dfs.getGroupingSeparator())
.replace("[DECIMAL_SEPARATOR]", Character.toString(dfs.getDecimalSeparator()));
} else {
format = format.replace(',', dfs.getGroupingSeparator());
}
} else if(format.contains(".") && dfs.getDecimalSeparator() != '.') {
format = format.replace('.', dfs.getDecimalSeparator());
}
return format;
}
private static String replaceLast(String text, String regex, String replacement) {
return text.replaceFirst("(?s)(.*)" + regex, "$1" + replacement);
}
private static boolean hasChar(char ch, List<Special>... numSpecials) { private static boolean hasChar(char ch, List<Special>... numSpecials) {
for (List<Special> specials : numSpecials) { for (List<Special> specials : numSpecials) {
@ -698,9 +735,9 @@ public class CellNumberFormatter extends CellFormatter {
private void writeSingleInteger(String fmt, int num, StringBuffer output, List<Special> numSpecials, Set<CellNumberStringMod> mods) { private void writeSingleInteger(String fmt, int num, StringBuffer output, List<Special> numSpecials, Set<CellNumberStringMod> mods) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
Formatter formatter = new Formatter(sb, LocaleUtil.getUserLocale()); Formatter formatter = new Formatter(sb, locale);
try { try {
formatter.format(LocaleUtil.getUserLocale(), fmt, num); formatter.format(locale, fmt, num);
} finally { } finally {
formatter.close(); formatter.close();
} }
@ -709,9 +746,13 @@ public class CellNumberFormatter extends CellFormatter {
private void writeInteger(StringBuffer result, StringBuffer output, private void writeInteger(StringBuffer result, StringBuffer output,
List<Special> numSpecials, Set<CellNumberStringMod> mods, List<Special> numSpecials, Set<CellNumberStringMod> mods,
boolean showCommas) { boolean showGroupingSeparator) {
int pos = result.indexOf(".") - 1; DecimalFormatSymbols dfs = getDecimalFormatSymbols();
String decimalSeparator = Character.toString(dfs.getDecimalSeparator());
String groupingSeparator = Character.toString(dfs.getGroupingSeparator());
int pos = result.indexOf(decimalSeparator) - 1;
if (pos < 0) { if (pos < 0) {
if (exponent != null && numSpecials == integerSpecials) { if (exponent != null && numSpecials == integerSpecials) {
pos = result.indexOf("E") - 1; pos = result.indexOf("E") - 1;
@ -723,13 +764,13 @@ public class CellNumberFormatter extends CellFormatter {
int strip; int strip;
for (strip = 0; strip < pos; strip++) { for (strip = 0; strip < pos; strip++) {
char resultCh = result.charAt(strip); char resultCh = result.charAt(strip);
if (resultCh != '0' && resultCh != ',') { if (resultCh != '0' && resultCh != dfs.getGroupingSeparator()) {
break; break;
} }
} }
ListIterator<Special> it = numSpecials.listIterator(numSpecials.size()); ListIterator<Special> it = numSpecials.listIterator(numSpecials.size());
boolean followWithComma = false; boolean followWithGroupingSeparator = false;
Special lastOutputIntegerDigit = null; Special lastOutputIntegerDigit = null;
int digit = 0; int digit = 0;
while (it.hasPrevious()) { while (it.hasPrevious()) {
@ -741,16 +782,16 @@ public class CellNumberFormatter extends CellFormatter {
resultCh = '0'; resultCh = '0';
} }
Special s = it.previous(); Special s = it.previous();
followWithComma = showCommas && digit > 0 && digit % 3 == 0; followWithGroupingSeparator = showGroupingSeparator && digit > 0 && digit % 3 == 0;
boolean zeroStrip = false; boolean zeroStrip = false;
if (resultCh != '0' || s.ch == '0' || s.ch == '?' || pos >= strip) { if (resultCh != '0' || s.ch == '0' || s.ch == '?' || pos >= strip) {
zeroStrip = s.ch == '?' && pos < strip; zeroStrip = s.ch == '?' && pos < strip;
output.setCharAt(s.pos, (zeroStrip ? ' ' : resultCh)); output.setCharAt(s.pos, (zeroStrip ? ' ' : resultCh));
lastOutputIntegerDigit = s; lastOutputIntegerDigit = s;
} }
if (followWithComma) { if (followWithGroupingSeparator) {
mods.add(insertMod(s, zeroStrip ? " " : ",", CellNumberStringMod.AFTER)); mods.add(insertMod(s, zeroStrip ? " " : groupingSeparator, CellNumberStringMod.AFTER));
followWithComma = false; followWithGroupingSeparator = false;
} }
digit++; digit++;
--pos; --pos;
@ -761,10 +802,10 @@ public class CellNumberFormatter extends CellFormatter {
// pos was decremented at the end of the loop above when the iterator was at its end // pos was decremented at the end of the loop above when the iterator was at its end
++pos; ++pos;
extraLeadingDigits = new StringBuffer(result.substring(0, pos)); extraLeadingDigits = new StringBuffer(result.substring(0, pos));
if (showCommas) { if (showGroupingSeparator) {
while (pos > 0) { while (pos > 0) {
if (digit > 0 && digit % 3 == 0) { if (digit > 0 && digit % 3 == 0) {
extraLeadingDigits.insert(pos, ','); extraLeadingDigits.insert(pos, groupingSeparator);
} }
digit++; digit++;
--pos; --pos;
@ -778,7 +819,8 @@ public class CellNumberFormatter extends CellFormatter {
int digit; int digit;
int strip; int strip;
if (fractionalSpecials.size() > 0) { if (fractionalSpecials.size() > 0) {
digit = result.indexOf(".") + 1; String decimalSeparator = Character.toString(getDecimalFormatSymbols().getDecimalSeparator());
digit = result.indexOf(decimalSeparator) + 1;
if (exponent != null) { if (exponent != null) {
strip = result.indexOf("e") - 1; strip = result.indexOf("e") - 1;
} else { } else {

View File

@ -292,10 +292,6 @@ public class DataFormatter implements Observer {
* @param cell The cell to retrieve a Format for * @param cell The cell to retrieve a Format for
* @return A Format for the format String * @return A Format for the format String
*/ */
private Format getFormat(Cell cell) {
return getFormat(cell, null);
}
private Format getFormat(Cell cell, ConditionalFormattingEvaluator cfEvaluator) { private Format getFormat(Cell cell, ConditionalFormattingEvaluator cfEvaluator) {
if (cell == null) return null; if (cell == null) return null;
@ -315,12 +311,12 @@ public class DataFormatter implements Observer {
private Format getFormat(double cellValue, int formatIndex, String formatStrIn) { private Format getFormat(double cellValue, int formatIndex, String formatStrIn) {
localeChangedObservable.checkForLocaleChange(); localeChangedObservable.checkForLocaleChange();
// // Might be better to separate out the n p and z formats, falling back to p when n and z are not set. // Might be better to separate out the n p and z formats, falling back to p when n and z are not set.
// // That however would require other code to be re factored. // That however would require other code to be re factored.
// String[] formatBits = formatStrIn.split(";"); // String[] formatBits = formatStrIn.split(";");
// int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2; // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2;
// String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0]; // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0];
String formatStr = formatStrIn; String formatStr = formatStrIn;
@ -336,7 +332,7 @@ public class DataFormatter implements Observer {
) ) { ) ) {
try { try {
// Ask CellFormat to get a formatter for it // Ask CellFormat to get a formatter for it
CellFormat cfmt = CellFormat.getInstance(formatStr); CellFormat cfmt = CellFormat.getInstance(locale, formatStr);
// CellFormat requires callers to identify date vs not, so do so // CellFormat requires callers to identify date vs not, so do so
Object cellValueO = Double.valueOf(cellValue); Object cellValueO = Double.valueOf(cellValue);
if (DateUtil.isADateFormat(formatIndex, formatStr) && if (DateUtil.isADateFormat(formatIndex, formatStr) &&
@ -607,7 +603,7 @@ public class DataFormatter implements Observer {
try { try {
return new ExcelStyleDateFormatter(formatStr, dateSymbols); return new ExcelStyleDateFormatter(formatStr, dateSymbols);
} catch(IllegalArgumentException iae) { } catch(IllegalArgumentException iae) {
logger.log(POILogger.DEBUG, "Formatting failed for format " + formatStr + ", falling back", iae);
// the pattern could not be parsed correctly, // the pattern could not be parsed correctly,
// so fall back to the default number format // so fall back to the default number format
return getDefaultFormat(cellValue); return getDefaultFormat(cellValue);
@ -718,7 +714,7 @@ public class DataFormatter implements Observer {
setExcelStyleRoundingMode(df); setExcelStyleRoundingMode(df);
return df; return df;
} catch(IllegalArgumentException iae) { } catch(IllegalArgumentException iae) {
logger.log(POILogger.DEBUG, "Formatting failed for format " + formatStr + ", falling back", iae);
// the pattern could not be parsed correctly, // the pattern could not be parsed correctly,
// so fall back to the default number format // so fall back to the default number format
return getDefaultFormat(cellValue); return getDefaultFormat(cellValue);

View File

@ -811,7 +811,6 @@ public class TestDataFormatter {
CellReference ref = new CellReference("D47"); CellReference ref = new CellReference("D47");
Cell cell = wb.getSheetAt(0).getRow(ref.getRow()).getCell(ref.getCol()); Cell cell = wb.getSheetAt(0).getRow(ref.getRow()).getCell(ref.getCol());
//noinspection deprecation
assertEquals(CellType.FORMULA, cell.getCellTypeEnum()); assertEquals(CellType.FORMULA, cell.getCellTypeEnum());
assertEquals("G9:K9 I7:I12", cell.getCellFormula()); assertEquals("G9:K9 I7:I12", cell.getCellFormula());
@ -888,18 +887,12 @@ public class TestDataFormatter {
*/ */
@Test @Test
public void testBug60422() { public void testBug60422() {
//when this is set to Locale.Germany, the result is char euro = '\u20AC';
LocaleUtil.setUserLocale(Locale.ROOT); DataFormatter df = new DataFormatter(Locale.GERMANY);
try { String formatString = String.format(Locale.ROOT,
char euro = '\u20AC'; "_-* #,##0.00\\ \"%s\"_-;\\-* #,##0.00\\ \"%s\"_-;_-* \"-\"??\\ \"%s\"_-;_-@_-",
DataFormatter df = new DataFormatter(Locale.GERMANY); euro, euro, euro);
String formatString = String.format(Locale.ROOT, assertEquals("4,33 " + euro, df.formatRawCellContents(4.33, 178, formatString));
"_-* #,##0.00\\ \"%s\"_-;\\-* #,##0.00\\ \"%s\"_-;_-* \"-\"??\\ \"%s\"_-;_-@_-", assertEquals("1.234,33 " + euro, df.formatRawCellContents(1234.33, 178, formatString));
euro, euro, euro);
//this should be 4,33
assertEquals("4.33 " + euro, df.formatRawCellContents(4.33, 178, formatString));
} finally {
LocaleUtil.resetUserLocale();
}
} }
} }