diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 79da11ad8..b0ae08f9b 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 48617 - Optionally allow the overriding of the Locale used by DataFormatter to control how the default number and date formats should look New event based xssf text extractor (XSSFEventBasedExcelExtractor) ExtractorFactory can now be told to prefer Event Based extractors (current Excel only) on a per-thread or overall basis 48544 - avoid failures in XLSX2CSV when shared string table is missing diff --git a/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java b/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java index 5e21bdf58..60c11d352 100644 --- a/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java +++ b/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java @@ -16,9 +16,11 @@ ==================================================================== */ package org.apache.poi.hssf.eventusermodel; +import java.text.NumberFormat; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; +import java.util.Locale; import java.util.Map; import org.apache.poi.hssf.record.CellValueRecordInterface; @@ -37,12 +39,28 @@ import org.apache.poi.hssf.usermodel.HSSFDataFormatter; */ public class FormatTrackingHSSFListener implements HSSFListener { private final HSSFListener _childListener; - private HSSFDataFormatter _formatter = new HSSFDataFormatter(); + private final HSSFDataFormatter _formatter; + private final NumberFormat _defaultFormat; private final Map _customFormatRecords = new Hashtable(); private final List _xfRecords = new ArrayList(); + /** + * Creates a format tracking wrapper around the given listener, using + * the {@link Locale#getDefault() default locale} for the formats. + */ 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; + _formatter = new HSSFDataFormatter(locale); + _defaultFormat = NumberFormat.getInstance(locale); } protected int getNumberOfCustomFormats() { @@ -104,7 +122,7 @@ public class FormatTrackingHSSFListener implements HSSFListener { String formatString = getFormatString(cell); if (formatString == null) { - return Double.toString(value); + return _defaultFormat.format(value); } // Format, using the nice new // HSSFDataFormatter to do the work for us diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormatter.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormatter.java index 6396492d8..0024639ff 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormatter.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataFormatter.java @@ -20,6 +20,7 @@ package org.apache.poi.hssf.usermodel; import java.text.DecimalFormat; import java.text.Format; import java.text.SimpleDateFormat; +import java.util.Locale; import org.apache.poi.ss.usermodel.DataFormatter; @@ -66,4 +67,18 @@ import org.apache.poi.ss.usermodel.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()); + } + } diff --git a/src/java/org/apache/poi/ss/usermodel/DataFormatter.java b/src/java/org/apache/poi/ss/usermodel/DataFormatter.java index bb7ef000e..053e37cb5 100644 --- a/src/java/org/apache/poi/ss/usermodel/DataFormatter.java +++ b/src/java/org/apache/poi/ss/usermodel/DataFormatter.java @@ -77,11 +77,21 @@ public class DataFormatter { /** A regex to find patterns like [$$-1009] and [$?-452]. */ 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; + /** General format for whole numbers. */ - private static final Format generalWholeNumFormat = new DecimalFormat("#"); + private final Format generalWholeNumFormat; /** General 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. */ private Format defaultNumFormat; @@ -90,13 +100,25 @@ public class DataFormatter { * A map to cache formats. * Map formats */ - private final Map formats; + private final Map formats; /** - * Constructor + * Creates a formatter using the {@link Locale#getDefault() default locale}. */ 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(); // init built-in formats @@ -143,7 +165,7 @@ public class DataFormatter { } private Format getFormat(double cellValue, int formatIndex, String formatStr) { - Format format = (Format)formats.get(formatStr); + Format format = formats.get(formatStr); if (format != null) { return format; } @@ -242,7 +264,7 @@ public class DataFormatter { StringBuffer sb = new StringBuffer(); char[] chars = formatStr.toCharArray(); boolean mIsMonth = true; - List ms = new ArrayList(); + List ms = new ArrayList(); for(int j=0; j> itr = formats.entrySet().iterator(); while(itr.hasNext()) { - Map.Entry entry = (Map.Entry)itr.next(); + Map.Entry entry = itr.next(); if (entry.getValue() == generalDecimalNumFormat || entry.getValue() == generalWholeNumFormat) { entry.setValue(format); @@ -562,7 +584,8 @@ public class DataFormatter { * * @author James May */ - private static final class SSNFormat extends Format { + @SuppressWarnings("serial") + private static final class SSNFormat extends Format { public static final Format instance = new SSNFormat(); private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); private SSNFormat() { @@ -593,7 +616,8 @@ public class DataFormatter { * built-in formatting for Zip + 4. * @author James May */ - private static final class ZipPlusFourFormat extends Format { + @SuppressWarnings("serial") + private static final class ZipPlusFourFormat extends Format { public static final Format instance = new ZipPlusFourFormat(); private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); private ZipPlusFourFormat() { @@ -623,7 +647,8 @@ public class DataFormatter { * built-in phone number formatting. * @author James May */ - private static final class PhoneFormat extends Format { + @SuppressWarnings("serial") + private static final class PhoneFormat extends Format { public static final Format instance = new PhoneFormat(); private static final DecimalFormat df = createIntegerOnlyFormat("##########"); private PhoneFormat() { diff --git a/src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java b/src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java new file mode 100644 index 000000000..73c15e8c4 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java @@ -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, "@")); + } +}