LANG-1109 - Number percentage formatting with fractional digits
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1671734 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d4c7f63054
commit
76cc69c3f0
|
@ -22,7 +22,7 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<release version="3.5" date="tba" description="tba">
|
<release version="3.5" date="tba" description="tba">
|
||||||
|
<action issue="LANG-1107" type="update" dev="chas">Fix parsing edge cases in FastDateParser</action>
|
||||||
</release>
|
</release>
|
||||||
|
|
||||||
<release version="3.4" date="2014-04-06" description="Feature and bugfix release">
|
<release version="3.4" date="2014-04-06" description="Feature and bugfix release">
|
||||||
|
|
|
@ -744,9 +744,13 @@ void setCalendar(final FastDateParser parser, final Calendar cal, final String v
|
||||||
/**
|
/**
|
||||||
* A strategy that handles a timezone field in the parsing pattern
|
* A strategy that handles a timezone field in the parsing pattern
|
||||||
*/
|
*/
|
||||||
private static class TimeZoneStrategy extends Strategy {
|
static class TimeZoneStrategy extends Strategy {
|
||||||
|
private static final String RFC_822_TIME_ZONE = "[+-]\\d{4}";
|
||||||
|
private static final String GMT_OPTION= "GMT[+-]\\d{1,2}:\\d{2}";
|
||||||
|
// see http://www.iana.org/time-zones and http://cldr.unicode.org/translation/timezones
|
||||||
|
static final String TZ_DATABASE= "(?:\\p{L}[\\p{L}\\p{Mc}\\p{Nd}\\p{Zs}\\p{P}&&[^-]]*-?\\p{Zs}?)*";
|
||||||
|
private static final String VALID_TZ = "((?iu)"+RFC_822_TIME_ZONE+"|"+GMT_OPTION+"|"+TZ_DATABASE+")";
|
||||||
|
|
||||||
private final String validTimeZoneChars;
|
|
||||||
private final SortedMap<String, TimeZone> tzNames= new TreeMap<String, TimeZone>(String.CASE_INSENSITIVE_ORDER);
|
private final SortedMap<String, TimeZone> tzNames= new TreeMap<String, TimeZone>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -777,9 +781,6 @@ private static class TimeZoneStrategy extends Strategy {
|
||||||
TimeZoneStrategy(final Locale locale) {
|
TimeZoneStrategy(final Locale locale) {
|
||||||
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
|
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
|
||||||
for (final String[] zone : zones) {
|
for (final String[] zone : zones) {
|
||||||
if (zone[ID].startsWith("GMT")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final TimeZone tz = TimeZone.getTimeZone(zone[ID]);
|
final TimeZone tz = TimeZone.getTimeZone(zone[ID]);
|
||||||
if (!tzNames.containsKey(zone[LONG_STD])){
|
if (!tzNames.containsKey(zone[LONG_STD])){
|
||||||
tzNames.put(zone[LONG_STD], tz);
|
tzNames.put(zone[LONG_STD], tz);
|
||||||
|
@ -796,15 +797,6 @@ private static class TimeZoneStrategy extends Strategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final StringBuilder sb= new StringBuilder();
|
|
||||||
sb.append("(GMT[+-]\\d{1,2}:\\d{2}").append('|');
|
|
||||||
sb.append("[+-]\\d{4}").append('|');
|
|
||||||
for(final String id : tzNames.keySet()) {
|
|
||||||
escapeRegex(sb, id, false).append('|');
|
|
||||||
}
|
|
||||||
sb.setCharAt(sb.length()-1, ')');
|
|
||||||
validTimeZoneChars= sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -812,7 +804,7 @@ private static class TimeZoneStrategy extends Strategy {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
|
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
|
||||||
regex.append(validTimeZoneChars);
|
regex.append(VALID_TZ);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,8 +817,8 @@ void setCalendar(final FastDateParser parser, final Calendar cal, final String v
|
||||||
if(value.charAt(0)=='+' || value.charAt(0)=='-') {
|
if(value.charAt(0)=='+' || value.charAt(0)=='-') {
|
||||||
tz= TimeZone.getTimeZone("GMT"+value);
|
tz= TimeZone.getTimeZone("GMT"+value);
|
||||||
}
|
}
|
||||||
else if(value.startsWith("GMT")) {
|
else if(value.regionMatches(true, 0, "GMT", 0, 3)) {
|
||||||
tz= TimeZone.getTimeZone(value);
|
tz= TimeZone.getTimeZone(value.toUpperCase());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tz= tzNames.get(value);
|
tz= tzNames.get(value);
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
@ -146,26 +146,36 @@ public void testGetFieldForceAccessIllegalArgumentException4() {
|
||||||
FieldUtils.getField(PublicChild.class, " ", true);
|
FieldUtils.getField(PublicChild.class, " ", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Field[] allPublicChildFields() {
|
||||||
|
Class<? super PublicChild> parentClass = PublicChild.class.getSuperclass();
|
||||||
|
final Field[] fieldsParent = parentClass.getDeclaredFields();
|
||||||
|
assertArrayEquals(fieldsParent, FieldUtils.getAllFields(parentClass));
|
||||||
|
|
||||||
|
final Field[] fieldsPublicChild = PublicChild.class.getDeclaredFields();
|
||||||
|
return ArrayUtils.addAll(fieldsPublicChild, fieldsParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Field[] allIntegerFields() {
|
||||||
|
final Field[] fieldsNumber = Number.class.getDeclaredFields();
|
||||||
|
assertArrayEquals(Number.class.getDeclaredFields(), FieldUtils.getAllFields(Number.class));
|
||||||
|
final Field[] fieldsInteger = Integer.class.getDeclaredFields();
|
||||||
|
return ArrayUtils.addAll(fieldsInteger, fieldsNumber);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAllFields() {
|
public void testGetAllFields() {
|
||||||
assertArrayEquals(new Field[0], FieldUtils.getAllFields(Object.class));
|
assertArrayEquals(new Field[0], FieldUtils.getAllFields(Object.class));
|
||||||
final Field[] fieldsNumber = Number.class.getDeclaredFields();
|
assertArrayEquals(allIntegerFields(), FieldUtils.getAllFields(Integer.class));
|
||||||
assertArrayEquals(fieldsNumber, FieldUtils.getAllFields(Number.class));
|
|
||||||
final Field[] fieldsInteger = Integer.class.getDeclaredFields();
|
assertArrayEquals(allPublicChildFields(), FieldUtils.getAllFields(PublicChild.class));
|
||||||
assertArrayEquals(ArrayUtils.addAll(fieldsInteger, fieldsNumber), FieldUtils.getAllFields(Integer.class));
|
|
||||||
assertEquals(5, FieldUtils.getAllFields(PublicChild.class).length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAllFieldsList() {
|
public void testGetAllFieldsList() {
|
||||||
assertEquals(0, FieldUtils.getAllFieldsList(Object.class).size());
|
assertEquals(Collections.emptyList(), FieldUtils.getAllFieldsList(Object.class));
|
||||||
final List<Field> fieldsNumber = Arrays.asList(Number.class.getDeclaredFields());
|
assertEquals(Arrays.asList(allIntegerFields()), FieldUtils.getAllFieldsList(Integer.class));
|
||||||
assertEquals(fieldsNumber, FieldUtils.getAllFieldsList(Number.class));
|
|
||||||
final List<Field> fieldsInteger = Arrays.asList(Integer.class.getDeclaredFields());
|
assertEquals(Arrays.asList(allPublicChildFields()), FieldUtils.getAllFieldsList(PublicChild.class));
|
||||||
final List<Field> allFieldsInteger = new ArrayList<Field>(fieldsInteger);
|
|
||||||
allFieldsInteger.addAll(fieldsNumber);
|
|
||||||
assertEquals(allFieldsInteger, FieldUtils.getAllFieldsList(Integer.class));
|
|
||||||
assertEquals(5, FieldUtils.getAllFieldsList(PublicChild.class).size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
@ -138,13 +137,11 @@ public void testUpperCasePP() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore // not currently supported
|
|
||||||
public void testLowerCase() throws Exception {
|
public void testLowerCase() throws Exception {
|
||||||
checkParse(input.toLowerCase(locale));
|
checkParse(input.toLowerCase(locale));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore // not currently supported
|
|
||||||
public void testLowerCasePP() throws Exception {
|
public void testLowerCasePP() throws Exception {
|
||||||
checkParsePosition(input.toLowerCase(locale));
|
checkParsePosition(input.toLowerCase(locale));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.text.DateFormatSymbols;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.lang3.SerializationUtils;
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
@ -640,4 +642,19 @@ public void test1806() throws ParseException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTimeZoneStrategyPattern() {
|
||||||
|
Pattern tz = Pattern.compile(FastDateParser.TimeZoneStrategy.TZ_DATABASE);
|
||||||
|
Assert.assertFalse(tz.matcher("GMT-1234").matches());
|
||||||
|
|
||||||
|
for (Locale locale : Locale.getAvailableLocales()) {
|
||||||
|
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
|
||||||
|
for (final String[] zone : zones) {
|
||||||
|
for (String zoneExpr : zone) {
|
||||||
|
Assert.assertTrue(locale.getDisplayName() + ":" + zoneExpr, tz.matcher(zoneExpr).matches());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue