Add patch from Jukka from bug #48617 + test - Optionally allow the overriding of the Locale used by DataFormatter to control how the default number and date formats should look

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@903303 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2010-01-26 16:21:17 +00:00
parent a2e6cafca9
commit 81755dc8d2
5 changed files with 119 additions and 17 deletions

View File

@ -34,6 +34,7 @@
<changes> <changes>
<release version="3.7-SNAPSHOT" date="2010-??-??"> <release version="3.7-SNAPSHOT" date="2010-??-??">
<action dev="POI-DEVELOPERS" type="add">48617 - Optionally allow the overriding of the Locale used by DataFormatter to control how the default number and date formats should look</action>
<action dev="POI-DEVELOPERS" type="add">New event based xssf text extractor (XSSFEventBasedExcelExtractor)</action> <action dev="POI-DEVELOPERS" type="add">New event based xssf text extractor (XSSFEventBasedExcelExtractor)</action>
<action dev="POI-DEVELOPERS" type="add">ExtractorFactory can now be told to prefer Event Based extractors (current Excel only) on a per-thread or overall basis</action> <action dev="POI-DEVELOPERS" type="add">ExtractorFactory can now be told to prefer Event Based extractors (current Excel only) on a per-thread or overall basis</action>
<action dev="POI-DEVELOPERS" type="fix">48544 - avoid failures in XLSX2CSV when shared string table is missing</action> <action dev="POI-DEVELOPERS" type="fix">48544 - avoid failures in XLSX2CSV when shared string table is missing</action>

View File

@ -16,9 +16,11 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.eventusermodel; package org.apache.poi.hssf.eventusermodel;
import java.text.NumberFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.CellValueRecordInterface;
@ -37,12 +39,28 @@ import org.apache.poi.hssf.usermodel.HSSFDataFormatter;
*/ */
public class FormatTrackingHSSFListener implements HSSFListener { public class FormatTrackingHSSFListener implements HSSFListener {
private final HSSFListener _childListener; private final HSSFListener _childListener;
private HSSFDataFormatter _formatter = new HSSFDataFormatter(); private final HSSFDataFormatter _formatter;
private final NumberFormat _defaultFormat;
private final Map<Integer, FormatRecord> _customFormatRecords = new Hashtable<Integer, FormatRecord>(); private final Map<Integer, FormatRecord> _customFormatRecords = new Hashtable<Integer, FormatRecord>();
private final List<ExtendedFormatRecord> _xfRecords = new ArrayList<ExtendedFormatRecord>(); private final List<ExtendedFormatRecord> _xfRecords = new ArrayList<ExtendedFormatRecord>();
/**
* Creates a format tracking wrapper around the given listener, using
* the {@link Locale#getDefault() default locale} for the formats.
*/
public FormatTrackingHSSFListener(HSSFListener childListener) { public FormatTrackingHSSFListener(HSSFListener childListener) {
this(childListener, Locale.getDefault());
}
/**
* Creates a format tracking wrapper around the given listener, using
* the given locale for the formats.
*/
public FormatTrackingHSSFListener(
HSSFListener childListener, Locale locale) {
_childListener = childListener; _childListener = childListener;
_formatter = new HSSFDataFormatter(locale);
_defaultFormat = NumberFormat.getInstance(locale);
} }
protected int getNumberOfCustomFormats() { protected int getNumberOfCustomFormats() {
@ -104,7 +122,7 @@ public class FormatTrackingHSSFListener implements HSSFListener {
String formatString = getFormatString(cell); String formatString = getFormatString(cell);
if (formatString == null) { if (formatString == null) {
return Double.toString(value); return _defaultFormat.format(value);
} }
// Format, using the nice new // Format, using the nice new
// HSSFDataFormatter to do the work for us // HSSFDataFormatter to do the work for us

View File

@ -20,6 +20,7 @@ package org.apache.poi.hssf.usermodel;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.Format; import java.text.Format;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Locale;
import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DataFormatter;
@ -66,4 +67,18 @@ import org.apache.poi.ss.usermodel.DataFormatter;
*/ */
public final class HSSFDataFormatter extends DataFormatter { public final class HSSFDataFormatter extends DataFormatter {
/**
* Creates a formatter using the given locale.
*/
public HSSFDataFormatter(Locale locale) {
super(locale);
}
/**
* Creates a formatter using the {@link Locale#getDefault() default locale}.
*/
public HSSFDataFormatter() {
this(Locale.getDefault());
}
} }

View File

@ -77,11 +77,21 @@ public class DataFormatter {
/** A regex to find patterns like [$$-1009] and [$?-452]. */ /** A regex to find patterns like [$$-1009] and [$?-452]. */
private static final Pattern specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])"); private static final Pattern specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
/**
* The decimal symbols of the locale used for formatting values.
*/
private final DecimalFormatSymbols decimalSymbols;
/**
* The date symbols of the locale used for formatting values.
*/
private final DateFormatSymbols dateSymbols;
/** <em>General</em> format for whole numbers. */ /** <em>General</em> format for whole numbers. */
private static final Format generalWholeNumFormat = new DecimalFormat("#"); private final Format generalWholeNumFormat;
/** <em>General</em> format for decimal numbers. */ /** <em>General</em> format for decimal numbers. */
private static final Format generalDecimalNumFormat = new DecimalFormat("#.##########"); private final Format generalDecimalNumFormat;
/** A default format to use when a number pattern cannot be parsed. */ /** A default format to use when a number pattern cannot be parsed. */
private Format defaultNumFormat; private Format defaultNumFormat;
@ -90,13 +100,25 @@ public class DataFormatter {
* A map to cache formats. * A map to cache formats.
* Map<String,Format> formats * Map<String,Format> formats
*/ */
private final Map formats; private final Map<String,Format> formats;
/** /**
* Constructor * Creates a formatter using the {@link Locale#getDefault() default locale}.
*/ */
public DataFormatter() { public DataFormatter() {
formats = new HashMap(); this(Locale.getDefault());
}
/**
* Creates a formatter using the given locale.
*/
public DataFormatter(Locale locale) {
dateSymbols = new DateFormatSymbols(locale);
decimalSymbols = new DecimalFormatSymbols(locale);
generalWholeNumFormat = new DecimalFormat("#", decimalSymbols);
generalDecimalNumFormat = new DecimalFormat("#.##########", decimalSymbols);
formats = new HashMap<String,Format>();
// init built-in formats // init built-in formats
@ -143,7 +165,7 @@ public class DataFormatter {
} }
private Format getFormat(double cellValue, int formatIndex, String formatStr) { private Format getFormat(double cellValue, int formatIndex, String formatStr) {
Format format = (Format)formats.get(formatStr); Format format = formats.get(formatStr);
if (format != null) { if (format != null) {
return format; return format;
} }
@ -242,7 +264,7 @@ public class DataFormatter {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
char[] chars = formatStr.toCharArray(); char[] chars = formatStr.toCharArray();
boolean mIsMonth = true; boolean mIsMonth = true;
List ms = new ArrayList(); List<Integer> ms = new ArrayList<Integer>();
for(int j=0; j<chars.length; j++) { for(int j=0; j<chars.length; j++) {
char c = chars[j]; char c = chars[j];
if (c == 'h' || c == 'H') { if (c == 'h' || c == 'H') {
@ -267,7 +289,7 @@ public class DataFormatter {
sb.append('s'); sb.append('s');
// if 'M' precedes 's' it should be minutes ('m') // if 'M' precedes 's' it should be minutes ('m')
for (int i = 0; i < ms.size(); i++) { for (int i = 0; i < ms.size(); i++) {
int index = ((Integer)ms.get(i)).intValue(); int index = ms.get(i).intValue();
if (sb.charAt(index) == 'M') { if (sb.charAt(index) == 'M') {
sb.replace(index, index+1, "m"); sb.replace(index, index+1, "m");
} }
@ -295,7 +317,7 @@ public class DataFormatter {
formatStr = sb.toString(); formatStr = sb.toString();
try { try {
return new SimpleDateFormat(formatStr); return new SimpleDateFormat(formatStr, dateSymbols);
} catch(IllegalArgumentException iae) { } catch(IllegalArgumentException iae) {
// the pattern could not be parsed correctly, // the pattern could not be parsed correctly,
@ -335,7 +357,7 @@ public class DataFormatter {
} }
try { try {
return new DecimalFormat(sb.toString()); return new DecimalFormat(sb.toString(), decimalSymbols);
} catch(IllegalArgumentException iae) { } catch(IllegalArgumentException iae) {
// the pattern could not be parsed correctly, // the pattern could not be parsed correctly,
@ -520,9 +542,9 @@ public class DataFormatter {
* @see java.text.Format#format * @see java.text.Format#format
*/ */
public void setDefaultNumberFormat(Format format) { public void setDefaultNumberFormat(Format format) {
Iterator itr = formats.entrySet().iterator(); Iterator<Map.Entry<String,Format>> itr = formats.entrySet().iterator();
while(itr.hasNext()) { while(itr.hasNext()) {
Map.Entry entry = (Map.Entry)itr.next(); Map.Entry<String,Format> entry = itr.next();
if (entry.getValue() == generalDecimalNumFormat if (entry.getValue() == generalDecimalNumFormat
|| entry.getValue() == generalWholeNumFormat) { || entry.getValue() == generalWholeNumFormat) {
entry.setValue(format); entry.setValue(format);
@ -562,6 +584,7 @@ public class DataFormatter {
* *
* @author James May * @author James May
*/ */
@SuppressWarnings("serial")
private static final class SSNFormat extends Format { private static final class SSNFormat extends Format {
public static final Format instance = new SSNFormat(); public static final Format instance = new SSNFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
@ -593,6 +616,7 @@ public class DataFormatter {
* built-in formatting for Zip + 4. * built-in formatting for Zip + 4.
* @author James May * @author James May
*/ */
@SuppressWarnings("serial")
private static final class ZipPlusFourFormat extends Format { private static final class ZipPlusFourFormat extends Format {
public static final Format instance = new ZipPlusFourFormat(); public static final Format instance = new ZipPlusFourFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
@ -623,6 +647,7 @@ public class DataFormatter {
* built-in phone number formatting. * built-in phone number formatting.
* @author James May * @author James May
*/ */
@SuppressWarnings("serial")
private static final class PhoneFormat extends Format { private static final class PhoneFormat extends Format {
public static final Format instance = new PhoneFormat(); public static final Format instance = new PhoneFormat();
private static final DecimalFormat df = createIntegerOnlyFormat("##########"); private static final DecimalFormat df = createIntegerOnlyFormat("##########");

View File

@ -0,0 +1,43 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.usermodel;
import java.util.Locale;
import junit.framework.TestCase;
/**
* Tests of {@link DataFormatter}
*
*/
public class TestDataFormatter extends TestCase {
/**
* Test that we use the specified locale when deciding
* how to format normal numbers
*/
public void testLocale() {
DataFormatter dfUS = new DataFormatter(Locale.US);
DataFormatter dfFR = new DataFormatter(Locale.FRENCH);
assertEquals("1234", dfUS.formatRawCellContents(1234, -1, "@"));
assertEquals("1234", dfFR.formatRawCellContents(1234, -1, "@"));
assertEquals("12.34", dfUS.formatRawCellContents(12.34, -1, "@"));
assertEquals("12,34", dfFR.formatRawCellContents(12.34, -1, "@"));
}
}