提交 660d1da9 编写于 作者: O okutsu

7003643: [Fmt-Me] MessageFormat.toPattern produces wrong quoted string and subformat modifiers

7008195: [Fmt-Me] Improve MessageFormat.applyPattern performance
Reviewed-by: naoto, peytoia
上级 46ce17b5
......@@ -423,18 +423,19 @@ public class MessageFormat extends Format {
* @exception IllegalArgumentException if the pattern is invalid
*/
public void applyPattern(String pattern) {
StringBuffer[] segments = new StringBuffer[4];
for (int i = 0; i < segments.length; ++i) {
segments[i] = new StringBuffer();
}
int part = 0;
StringBuilder[] segments = new StringBuilder[4];
// Allocate only segments[SEG_RAW] here. The rest are
// allocated on demand.
segments[SEG_RAW] = new StringBuilder();
int part = SEG_RAW;
int formatNumber = 0;
boolean inQuote = false;
int braceStack = 0;
maxOffset = -1;
for (int i = 0; i < pattern.length(); ++i) {
char ch = pattern.charAt(i);
if (part == 0) {
if (part == SEG_RAW) {
if (ch == '\'') {
if (i + 1 < pattern.length()
&& pattern.charAt(i+1) == '\'') {
......@@ -444,43 +445,61 @@ public class MessageFormat extends Format {
inQuote = !inQuote;
}
} else if (ch == '{' && !inQuote) {
part = 1;
part = SEG_INDEX;
if (segments[SEG_INDEX] == null) {
segments[SEG_INDEX] = new StringBuilder();
}
} else {
segments[part].append(ch);
}
} else if (inQuote) { // just copy quotes in parts
segments[part].append(ch);
if (ch == '\'') {
inQuote = false;
}
} else {
switch (ch) {
case ',':
if (part < 3)
part += 1;
else
segments[part].append(ch);
break;
case '{':
++braceStack;
} else {
if (inQuote) { // just copy quotes in parts
segments[part].append(ch);
break;
case '}':
if (braceStack == 0) {
part = 0;
makeFormat(i, formatNumber, segments);
formatNumber++;
} else {
--braceStack;
if (ch == '\'') {
inQuote = false;
}
} else {
switch (ch) {
case ',':
if (part < SEG_MODIFIER) {
if (segments[++part] == null) {
segments[part] = new StringBuilder();
}
} else {
segments[part].append(ch);
}
break;
case '{':
++braceStack;
segments[part].append(ch);
break;
case '}':
if (braceStack == 0) {
part = SEG_RAW;
makeFormat(i, formatNumber, segments);
formatNumber++;
// throw away other segments
segments[SEG_INDEX] = null;
segments[SEG_TYPE] = null;
segments[SEG_MODIFIER] = null;
} else {
--braceStack;
segments[part].append(ch);
}
break;
case ' ':
// Skip any leading space chars for SEG_TYPE.
if (part != SEG_TYPE || segments[SEG_TYPE].length() > 0) {
segments[part].append(ch);
}
break;
case '\'':
inQuote = true;
// fall through, so we keep quotes in other parts
default:
segments[part].append(ch);
break;
}
break;
case '\'':
inQuote = true;
// fall through, so we keep quotes in other parts
default:
segments[part].append(ch);
break;
}
}
}
......@@ -502,65 +521,57 @@ public class MessageFormat extends Format {
public String toPattern() {
// later, make this more extensible
int lastOffset = 0;
StringBuffer result = new StringBuffer();
StringBuilder result = new StringBuilder();
for (int i = 0; i <= maxOffset; ++i) {
copyAndFixQuotes(pattern, lastOffset, offsets[i],result);
copyAndFixQuotes(pattern, lastOffset, offsets[i], result);
lastOffset = offsets[i];
result.append('{');
result.append(argumentNumbers[i]);
if (formats[i] == null) {
result.append('{').append(argumentNumbers[i]);
Format fmt = formats[i];
if (fmt == null) {
// do nothing, string format
} else if (formats[i] instanceof DecimalFormat) {
if (formats[i].equals(NumberFormat.getInstance(locale))) {
} else if (fmt instanceof NumberFormat) {
if (fmt.equals(NumberFormat.getInstance(locale))) {
result.append(",number");
} else if (formats[i].equals(NumberFormat.getCurrencyInstance(locale))) {
} else if (fmt.equals(NumberFormat.getCurrencyInstance(locale))) {
result.append(",number,currency");
} else if (formats[i].equals(NumberFormat.getPercentInstance(locale))) {
} else if (fmt.equals(NumberFormat.getPercentInstance(locale))) {
result.append(",number,percent");
} else if (formats[i].equals(NumberFormat.getIntegerInstance(locale))) {
} else if (fmt.equals(NumberFormat.getIntegerInstance(locale))) {
result.append(",number,integer");
} else {
result.append(",number," +
((DecimalFormat)formats[i]).toPattern());
if (fmt instanceof DecimalFormat) {
result.append(",number,").append(((DecimalFormat)fmt).toPattern());
} else if (fmt instanceof ChoiceFormat) {
result.append(",choice,").append(((ChoiceFormat)fmt).toPattern());
} else {
// UNKNOWN
}
}
} else if (formats[i] instanceof SimpleDateFormat) {
if (formats[i].equals(DateFormat.getDateInstance(
DateFormat.DEFAULT,locale))) {
result.append(",date");
} else if (formats[i].equals(DateFormat.getDateInstance(
DateFormat.SHORT,locale))) {
result.append(",date,short");
} else if (formats[i].equals(DateFormat.getDateInstance(
DateFormat.DEFAULT,locale))) {
result.append(",date,medium");
} else if (formats[i].equals(DateFormat.getDateInstance(
DateFormat.LONG,locale))) {
result.append(",date,long");
} else if (formats[i].equals(DateFormat.getDateInstance(
DateFormat.FULL,locale))) {
result.append(",date,full");
} else if (formats[i].equals(DateFormat.getTimeInstance(
DateFormat.DEFAULT,locale))) {
result.append(",time");
} else if (formats[i].equals(DateFormat.getTimeInstance(
DateFormat.SHORT,locale))) {
result.append(",time,short");
} else if (formats[i].equals(DateFormat.getTimeInstance(
DateFormat.DEFAULT,locale))) {
result.append(",time,medium");
} else if (formats[i].equals(DateFormat.getTimeInstance(
DateFormat.LONG,locale))) {
result.append(",time,long");
} else if (formats[i].equals(DateFormat.getTimeInstance(
DateFormat.FULL,locale))) {
result.append(",time,full");
} else {
result.append(",date,"
+ ((SimpleDateFormat)formats[i]).toPattern());
} else if (fmt instanceof DateFormat) {
int index;
for (index = MODIFIER_DEFAULT; index < DATE_TIME_MODIFIERS.length; index++) {
DateFormat df = DateFormat.getDateInstance(DATE_TIME_MODIFIERS[index],
locale);
if (fmt.equals(df)) {
result.append(",date");
break;
}
df = DateFormat.getTimeInstance(DATE_TIME_MODIFIERS[index],
locale);
if (fmt.equals(df)) {
result.append(",time");
break;
}
}
if (index >= DATE_TIME_MODIFIERS.length) {
if (fmt instanceof SimpleDateFormat) {
result.append(",date,").append(((SimpleDateFormat)fmt).toPattern());
} else {
// UNKNOWN
}
} else if (index != MODIFIER_DEFAULT) {
result.append(',').append(DATE_TIME_MODIFIER_KEYWORDS[index]);
}
} else if (formats[i] instanceof ChoiceFormat) {
result.append(",choice,"
+ ((ChoiceFormat)formats[i]).toPattern());
} else {
//result.append(", unknown");
}
......@@ -674,7 +685,7 @@ public class MessageFormat extends Format {
*
* @param formatElementIndex the index of a format element within the pattern
* @param newFormat the format to use for the specified format element
* @exception ArrayIndexOutOfBoundsException if formatElementIndex is equal to or
* @exception ArrayIndexOutOfBoundsException if {@code formatElementIndex} is equal to or
* larger than the number of format elements in the pattern string
*/
public void setFormat(int formatElementIndex, Format newFormat) {
......@@ -968,7 +979,8 @@ public class MessageFormat extends Format {
if (patternOffset >= tempLength) {
next = source.length();
}else{
next = source.indexOf( pattern.substring(patternOffset,tempLength), sourceOffset);
next = source.indexOf(pattern.substring(patternOffset, tempLength),
sourceOffset);
}
if (next < 0) {
......@@ -1222,7 +1234,7 @@ public class MessageFormat extends Format {
lastOffset = offsets[i];
int argumentNumber = argumentNumbers[i];
if (arguments == null || argumentNumber >= arguments.length) {
result.append("{" + argumentNumber + "}");
result.append('{').append(argumentNumber).append('}');
continue;
}
// int argRecursion = ((recursionProtection >> (argumentNumber*2)) & 0x3);
......@@ -1334,25 +1346,83 @@ public class MessageFormat extends Format {
}
}
private static final String[] typeList =
{"", "", "number", "", "date", "", "time", "", "choice"};
private static final String[] modifierList =
{"", "", "currency", "", "percent", "", "integer"};
private static final String[] dateModifierList =
{"", "", "short", "", "medium", "", "long", "", "full"};
// Indices for segments
private static final int SEG_RAW = 0;
private static final int SEG_INDEX = 1;
private static final int SEG_TYPE = 2;
private static final int SEG_MODIFIER = 3; // modifier or subformat
// Indices for type keywords
private static final int TYPE_NULL = 0;
private static final int TYPE_NUMBER = 1;
private static final int TYPE_DATE = 2;
private static final int TYPE_TIME = 3;
private static final int TYPE_CHOICE = 4;
private static final String[] TYPE_KEYWORDS = {
"",
"number",
"date",
"time",
"choice"
};
// Indices for number modifiers
private static final int MODIFIER_DEFAULT = 0; // common in number and date-time
private static final int MODIFIER_CURRENCY = 1;
private static final int MODIFIER_PERCENT = 2;
private static final int MODIFIER_INTEGER = 3;
private static final String[] NUMBER_MODIFIER_KEYWORDS = {
"",
"currency",
"percent",
"integer"
};
// Indices for date-time modifiers
private static final int MODIFIER_SHORT = 1;
private static final int MODIFIER_MEDIUM = 2;
private static final int MODIFIER_LONG = 3;
private static final int MODIFIER_FULL = 4;
private static final String[] DATE_TIME_MODIFIER_KEYWORDS = {
"",
"short",
"medium",
"long",
"full"
};
// Date-time style values corresponding to the date-time modifiers.
private static final int[] DATE_TIME_MODIFIERS = {
DateFormat.DEFAULT,
DateFormat.SHORT,
DateFormat.MEDIUM,
DateFormat.LONG,
DateFormat.FULL,
};
private void makeFormat(int position, int offsetNumber,
StringBuffer[] segments)
StringBuilder[] textSegments)
{
String[] segments = new String[textSegments.length];
for (int i = 0; i < textSegments.length; i++) {
StringBuilder oneseg = textSegments[i];
segments[i] = (oneseg != null) ? oneseg.toString() : "";
}
// get the argument number
int argumentNumber;
try {
argumentNumber = Integer.parseInt(segments[1].toString()); // always unlocalized!
argumentNumber = Integer.parseInt(segments[SEG_INDEX]); // always unlocalized!
} catch (NumberFormatException e) {
throw new IllegalArgumentException("can't parse argument number: " + segments[1]);
throw new IllegalArgumentException("can't parse argument number: "
+ segments[SEG_INDEX], e);
}
if (argumentNumber < 0) {
throw new IllegalArgumentException("negative argument number: " + argumentNumber);
throw new IllegalArgumentException("negative argument number: "
+ argumentNumber);
}
// resize format information arrays if necessary
......@@ -1370,120 +1440,129 @@ public class MessageFormat extends Format {
}
int oldMaxOffset = maxOffset;
maxOffset = offsetNumber;
offsets[offsetNumber] = segments[0].length();
offsets[offsetNumber] = segments[SEG_RAW].length();
argumentNumbers[offsetNumber] = argumentNumber;
// now get the format
Format newFormat = null;
switch (findKeyword(segments[2].toString(), typeList)) {
case 0:
break;
case 1: case 2:// number
switch (findKeyword(segments[3].toString(), modifierList)) {
case 0: // default;
newFormat = NumberFormat.getInstance(locale);
break;
case 1: case 2:// currency
newFormat = NumberFormat.getCurrencyInstance(locale);
break;
case 3: case 4:// percent
newFormat = NumberFormat.getPercentInstance(locale);
break;
case 5: case 6:// integer
newFormat = NumberFormat.getIntegerInstance(locale);
break;
default: // pattern
newFormat = new DecimalFormat(segments[3].toString(), DecimalFormatSymbols.getInstance(locale));
break;
}
break;
case 3: case 4: // date
switch (findKeyword(segments[3].toString(), dateModifierList)) {
case 0: // default
newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
break;
case 1: case 2: // short
newFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale);
if (segments[SEG_TYPE].length() != 0) {
int type = findKeyword(segments[SEG_TYPE], TYPE_KEYWORDS);
switch (type) {
case TYPE_NULL:
// Type "" is allowed. e.g., "{0,}", "{0,,}", and "{0,,#}"
// are treated as "{0}".
break;
case 3: case 4: // medium
newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
break;
case 5: case 6: // long
newFormat = DateFormat.getDateInstance(DateFormat.LONG, locale);
break;
case 7: case 8: // full
newFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);
break;
default:
newFormat = new SimpleDateFormat(segments[3].toString(), locale);
break;
}
break;
case 5: case 6:// time
switch (findKeyword(segments[3].toString(), dateModifierList)) {
case 0: // default
newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
break;
case 1: case 2: // short
newFormat = DateFormat.getTimeInstance(DateFormat.SHORT, locale);
break;
case 3: case 4: // medium
newFormat = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
case TYPE_NUMBER:
switch (findKeyword(segments[SEG_MODIFIER], NUMBER_MODIFIER_KEYWORDS)) {
case MODIFIER_DEFAULT:
newFormat = NumberFormat.getInstance(locale);
break;
case MODIFIER_CURRENCY:
newFormat = NumberFormat.getCurrencyInstance(locale);
break;
case MODIFIER_PERCENT:
newFormat = NumberFormat.getPercentInstance(locale);
break;
case MODIFIER_INTEGER:
newFormat = NumberFormat.getIntegerInstance(locale);
break;
default: // DecimalFormat pattern
try {
newFormat = new DecimalFormat(segments[SEG_MODIFIER],
DecimalFormatSymbols.getInstance(locale));
} catch (IllegalArgumentException e) {
maxOffset = oldMaxOffset;
throw e;
}
break;
}
break;
case 5: case 6: // long
newFormat = DateFormat.getTimeInstance(DateFormat.LONG, locale);
case TYPE_DATE:
case TYPE_TIME:
int mod = findKeyword(segments[SEG_MODIFIER], DATE_TIME_MODIFIER_KEYWORDS);
if (mod >= 0 && mod < DATE_TIME_MODIFIER_KEYWORDS.length) {
if (type == TYPE_DATE) {
newFormat = DateFormat.getDateInstance(DATE_TIME_MODIFIERS[mod],
locale);
} else {
newFormat = DateFormat.getTimeInstance(DATE_TIME_MODIFIERS[mod],
locale);
}
} else {
// SimpleDateFormat pattern
try {
newFormat = new SimpleDateFormat(segments[SEG_MODIFIER], locale);
} catch (IllegalArgumentException e) {
maxOffset = oldMaxOffset;
throw e;
}
}
break;
case 7: case 8: // full
newFormat = DateFormat.getTimeInstance(DateFormat.FULL, locale);
case TYPE_CHOICE:
try {
// ChoiceFormat pattern
newFormat = new ChoiceFormat(segments[SEG_MODIFIER]);
} catch (Exception e) {
maxOffset = oldMaxOffset;
throw new IllegalArgumentException("Choice Pattern incorrect: "
+ segments[SEG_MODIFIER], e);
}
break;
default:
newFormat = new SimpleDateFormat(segments[3].toString(), locale);
break;
}
break;
case 7: case 8:// choice
try {
newFormat = new ChoiceFormat(segments[3].toString());
} catch (Exception e) {
maxOffset = oldMaxOffset;
throw new IllegalArgumentException(
"Choice Pattern incorrect");
throw new IllegalArgumentException("unknown format type: " +
segments[SEG_TYPE]);
}
break;
default:
maxOffset = oldMaxOffset;
throw new IllegalArgumentException("unknown format type: " +
segments[2].toString());
}
formats[offsetNumber] = newFormat;
segments[1].setLength(0); // throw away other segments
segments[2].setLength(0);
segments[3].setLength(0);
}
private static final int findKeyword(String s, String[] list) {
s = s.trim().toLowerCase();
for (int i = 0; i < list.length; ++i) {
if (s.equals(list[i]))
return i;
}
// Try trimmed lowercase.
String ls = s.trim().toLowerCase(Locale.ROOT);
if (ls != s) {
for (int i = 0; i < list.length; ++i) {
if (ls.equals(list[i]))
return i;
}
}
return -1;
}
private static final void copyAndFixQuotes(
String source, int start, int end, StringBuffer target) {
private static final void copyAndFixQuotes(String source, int start, int end,
StringBuilder target) {
boolean quoted = false;
for (int i = start; i < end; ++i) {
char ch = source.charAt(i);
if (ch == '{') {
target.append("'{'");
} else if (ch == '}') {
target.append("'}'");
if (!quoted) {
target.append('\'');
quoted = true;
}
target.append(ch);
} else if (ch == '\'') {
target.append("''");
} else {
if (quoted) {
target.append('\'');
quoted = false;
}
target.append(ch);
}
}
if (quoted) {
target.append('\'');
}
}
/**
......
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 7003643
* @summary Make sure MessageFormat.toPattern produces correct quoting. (SPI part is tested in PluggableLocale tests.)
*/
import java.text.*;
import java.util.*;
public class Bug7003643 {
private static final int N = 5;
private static final String[] elements = {
"'{'", "'{", "{", "''", "}", "a", "'",
};
public static void main(String[] args) {
Random rand = new Random();
int count = 0;
int max = (int) (Math.pow((double)elements.length, (double)N)/0.52);
while (count < max) {
// Create a random pattern. If the produced pattern is
// valid, then proceed with the round-trip testing.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < N; i++) {
sb.append(elements[rand.nextInt(elements.length)]);
}
String pattern = sb.toString();
MessageFormat mf = null;
try {
mf = new MessageFormat(pattern);
} catch (IllegalArgumentException e) {
// bad pattern data
}
if (mf == null) {
continue;
}
count++;
String res1 = MessageFormat.format(pattern, 123);
String toPattern = mf.toPattern();
String res2 = MessageFormat.format(toPattern, 123);
if (!res1.equals(res2)) {
String s = String.format("Failed%n pattern=\"%s\" => result=\"%s\"%n"
+ " toPattern()=\"%s\" => result=\"%s\"%n",
pattern, res1, toPattern, res2);
throw new RuntimeException(s);
}
}
}
}
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -44,6 +44,7 @@ public class DateFormatProviderTest extends ProviderTest {
availableLocalesTest();
objectValidityTest();
extendedVariantTest();
messageFormatTest();
}
void availableLocalesTest() {
......@@ -118,4 +119,48 @@ public class DateFormatProviderTest extends ProviderTest {
}
}
}
private static final String[] TYPES = {
"date",
"time"
};
private static final String[] MODIFIERS = {
"",
"short",
"medium", // Same as DEFAULT
"long",
"full"
};
void messageFormatTest() {
for (Locale target : providerloc) {
for (String type : TYPES) {
for (String modifier : MODIFIERS) {
String pattern, expected;
if (modifier.equals("")) {
pattern = String.format("%s={0,%s}", type, type);
} else {
pattern = String.format("%s={0,%s,%s}", type, type, modifier);
}
if (modifier.equals("medium")) {
// medium is default.
expected = String.format("%s={0,%s}", type, type);
} else {
expected = pattern;
}
MessageFormat mf = new MessageFormat(pattern, target);
Format[] fmts = mf.getFormats();
if (fmts[0] instanceof SimpleDateFormat) {
continue;
}
String toPattern = mf.toPattern();
if (!toPattern.equals(expected)) {
throw new RuntimeException("messageFormatTest: got '" + toPattern
+ "', expected '" + expected + "'");
}
}
}
}
}
}
#
# Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
......@@ -23,6 +23,6 @@
#!/bin/sh
#
# @test
# @bug 4052440
# @bug 4052440 7003643
# @summary DateFormatProvider tests
# @run shell ExecTest.sh foo DateFormatProviderTest true
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -29,6 +29,8 @@ import java.util.*;
import sun.util.*;
import sun.util.resources.*;
import com.foo.FooNumberFormat;
public class NumberFormatProviderTest extends ProviderTest {
com.foo.NumberFormatProviderImpl nfp = new com.foo.NumberFormatProviderImpl();
......@@ -43,6 +45,7 @@ public class NumberFormatProviderTest extends ProviderTest {
NumberFormatProviderTest() {
availableLocalesTest();
objectValidityTest();
messageFormatTest();
}
void availableLocalesTest() {
......@@ -72,14 +75,10 @@ public class NumberFormatProviderTest extends ProviderTest {
}
// result object
String resultCur =
((DecimalFormat)NumberFormat.getCurrencyInstance(target)).toPattern();
String resultInt =
((DecimalFormat)NumberFormat.getIntegerInstance(target)).toPattern();
String resultNum =
((DecimalFormat)NumberFormat.getNumberInstance(target)).toPattern();
String resultPer =
((DecimalFormat)NumberFormat.getPercentInstance(target)).toPattern();
String resultCur = getPattern(NumberFormat.getCurrencyInstance(target));
String resultInt = getPattern(NumberFormat.getIntegerInstance(target));
String resultNum = getPattern(NumberFormat.getNumberInstance(target));
String resultPer = getPattern(NumberFormat.getPercentInstance(target));
// provider's object (if any)
String providersCur = null;
......@@ -87,21 +86,21 @@ public class NumberFormatProviderTest extends ProviderTest {
String providersNum = null;
String providersPer = null;
if (providerloc.contains(target)) {
DecimalFormat dfCur = (DecimalFormat)nfp.getCurrencyInstance(target);
NumberFormat dfCur = nfp.getCurrencyInstance(target);
if (dfCur != null) {
providersCur = dfCur.toPattern();
providersCur = getPattern(dfCur);
}
DecimalFormat dfInt = (DecimalFormat)nfp.getIntegerInstance(target);
NumberFormat dfInt = nfp.getIntegerInstance(target);
if (dfInt != null) {
providersInt = dfInt.toPattern();
providersInt = getPattern(dfInt);
}
DecimalFormat dfNum = (DecimalFormat)nfp.getNumberInstance(target);
NumberFormat dfNum = nfp.getNumberInstance(target);
if (dfNum != null) {
providersNum = dfNum.toPattern();
providersNum = getPattern(dfNum);
}
DecimalFormat dfPer = (DecimalFormat)nfp.getPercentInstance(target);
NumberFormat dfPer = nfp.getPercentInstance(target);
if (dfPer != null) {
providersPer = dfPer.toPattern();
providersPer = getPattern(dfPer);
}
}
......@@ -174,4 +173,35 @@ public class NumberFormatProviderTest extends ProviderTest {
}
}
}
private static String getPattern(NumberFormat nf) {
if (nf instanceof DecimalFormat) {
return ((DecimalFormat)nf).toPattern();
}
if (nf instanceof FooNumberFormat) {
return ((FooNumberFormat)nf).toPattern();
}
return null;
}
private static final String[] NUMBER_PATTERNS = {
"num={0,number}",
"num={0,number,currency}",
"num={0,number,percent}",
"num={0,number,integer}"
};
void messageFormatTest() {
for (Locale target : providerloc) {
for (String pattern : NUMBER_PATTERNS) {
MessageFormat mf = new MessageFormat(pattern, target);
String toPattern = mf.toPattern();
if (!pattern.equals(toPattern)) {
throw new RuntimeException("MessageFormat.toPattern: got '"
+ toPattern
+ "', expected '" + pattern + "'");
}
}
}
}
}
#
# Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
......@@ -23,6 +23,6 @@
#!/bin/sh
#
# @test
# @bug 4052440
# @bug 4052440 7003643
# @summary NumberFormatProvider tests
# @run shell ExecTest.sh foo NumberFormatProviderTest true
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -42,7 +42,7 @@ public class DateFormatProviderImpl extends DateFormatProvider {
static String[] datePattern = {
"yyyy'\u5e74'M'\u6708'd'\u65e5'", // full date pattern
"yyyy/MM/dd", // long date pattern
"yyyy/MMM/dd", // long date pattern
"yyyy/MM/dd", // medium date pattern
"yy/MM/dd" // short date pattern
};
......@@ -68,7 +68,7 @@ public class DateFormatProviderImpl extends DateFormatProvider {
public DateFormat getDateInstance(int style, Locale locale) {
for (int i = 0; i < avail.length; i ++) {
if (Utils.supportsLocale(avail[i], locale)) {
return new SimpleDateFormat(datePattern[style]+dialect[i], locale);
return new FooDateFormat(datePattern[style]+dialect[i], locale);
}
}
throw new IllegalArgumentException("locale is not supported: "+locale);
......@@ -77,7 +77,7 @@ public class DateFormatProviderImpl extends DateFormatProvider {
public DateFormat getTimeInstance(int style, Locale locale) {
for (int i = 0; i < avail.length; i ++) {
if (Utils.supportsLocale(avail[i], locale)) {
return new SimpleDateFormat(timePattern[style]+dialect[i], locale);
return new FooDateFormat(timePattern[style]+dialect[i], locale);
}
}
throw new IllegalArgumentException("locale is not supported: "+locale);
......@@ -86,7 +86,7 @@ public class DateFormatProviderImpl extends DateFormatProvider {
public DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
for (int i = 0; i < avail.length; i ++) {
if (Utils.supportsLocale(avail[i], locale)) {
return new SimpleDateFormat(
return new FooDateFormat(
datePattern[dateStyle]+" "+timePattern[timeStyle]+dialect[i], locale);
}
}
......
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.foo;
import java.text.*;
import java.util.*;
/**
* FooDateFormat provides SimpleDateFormat methods required for the SPI testing.
*/
public class FooDateFormat extends DateFormat {
private SimpleDateFormat sdf;
public FooDateFormat(String pattern, Locale loc) {
sdf = new SimpleDateFormat(pattern, loc);
}
@Override
public StringBuffer format(Date date,
StringBuffer toAppendTo,
FieldPosition fieldPosition) {
return sdf.format(date, toAppendTo, fieldPosition);
}
@Override
public Date parse(String source, ParsePosition pos) {
return sdf.parse(source, pos);
}
@Override
public boolean equals(Object other) {
return other instanceof FooDateFormat
&& sdf.equals(((FooDateFormat)other).sdf);
}
@Override
public int hashCode() {
return sdf.hashCode();
}
}
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.foo;
import java.text.*;
/**
* FooNumberFormat provides DecimalFormat methods required for the SPI testing.
*/
public class FooNumberFormat extends NumberFormat {
private DecimalFormat df;
public FooNumberFormat(String pattern, DecimalFormatSymbols dfs) {
df = new DecimalFormat(pattern, dfs);
}
@Override
public StringBuffer format(double number,
StringBuffer toAppendTo,
FieldPosition pos) {
return df.format(number, toAppendTo, pos);
}
@Override
public StringBuffer format(long number,
StringBuffer toAppendTo,
FieldPosition pos) {
return df.format(number, toAppendTo, pos);
}
@Override
public Number parse(String source, ParsePosition parsePosition) {
return df.parse(source, parsePosition);
}
@Override
public boolean equals(Object other) {
return other instanceof FooNumberFormat
&& df.equals(((FooNumberFormat)other).df);
}
@Override
public int hashCode() {
return df.hashCode();
}
// DecimalFormat specific methods required for testing
public String toPattern() {
return df.toPattern();
}
public DecimalFormatSymbols getDecimalFormatSymbols() {
return df.getDecimalFormatSymbols();
}
public void setDecimalSeparatorAlwaysShown(boolean newValue) {
df.setDecimalSeparatorAlwaysShown(newValue);
}
}
......@@ -28,6 +28,8 @@ FOOFILES_JAVA = \
DateFormatSymbolsProviderImpl.java \
DecimalFormatSymbolsProviderImpl.java \
NumberFormatProviderImpl.java \
FooDateFormat.java \
FooNumberFormat.java \
Utils.java
BARFILES_JAVA = \
......
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -49,13 +49,15 @@ public class NumberFormatProviderImpl extends NumberFormatProvider {
static String[] patterns = {
"#,##0.###{0};-#,##0.###{1}", // decimal pattern
"#{0};(#){1}", // integer pattern
"\u00A4#,##0{0};-\u00A4#,##0{1}", // currency pattern
"#,##0%{0}" // percent pattern
};
// Constants used by factory methods to specify a style of format.
static final int NUMBERSTYLE = 0;
static final int CURRENCYSTYLE = 1;
static final int PERCENTSTYLE = 2;
static final int INTEGERSTYLE = 1;
static final int CURRENCYSTYLE = 2;
static final int PERCENTSTYLE = 3;
public Locale[] getAvailableLocales() {
return avail;
......@@ -68,10 +70,10 @@ public class NumberFormatProviderImpl extends NumberFormatProvider {
MessageFormat.format(patterns[CURRENCYSTYLE],
dialect[i],
dialect[i]);
DecimalFormat df = new DecimalFormat(pattern,
FooNumberFormat nf = new FooNumberFormat(pattern,
DecimalFormatSymbols.getInstance(locale));
adjustForCurrencyDefaultFractionDigits(df);
return df;
adjustForCurrencyDefaultFractionDigits(nf);
return nf;
}
}
throw new IllegalArgumentException("locale is not supported: "+locale);
......@@ -81,15 +83,15 @@ public class NumberFormatProviderImpl extends NumberFormatProvider {
for (int i = 0; i < avail.length; i ++) {
if (Utils.supportsLocale(avail[i], locale)) {
String pattern =
MessageFormat.format(patterns[NUMBERSTYLE],
MessageFormat.format(patterns[INTEGERSTYLE],
dialect[i],
dialect[i]);
DecimalFormat df = new DecimalFormat(pattern,
FooNumberFormat nf = new FooNumberFormat(pattern,
DecimalFormatSymbols.getInstance(locale));
df.setMaximumFractionDigits(0);
df.setDecimalSeparatorAlwaysShown(false);
df.setParseIntegerOnly(true);
return df;
nf.setMaximumFractionDigits(0);
nf.setDecimalSeparatorAlwaysShown(false);
nf.setParseIntegerOnly(true);
return nf;
}
}
throw new IllegalArgumentException("locale is not supported: "+locale);
......@@ -102,7 +104,7 @@ public class NumberFormatProviderImpl extends NumberFormatProvider {
MessageFormat.format(patterns[NUMBERSTYLE],
dialect[i],
dialect[i]);
return new DecimalFormat(pattern,
return new FooNumberFormat(pattern,
DecimalFormatSymbols.getInstance(locale));
}
}
......@@ -115,7 +117,7 @@ public class NumberFormatProviderImpl extends NumberFormatProvider {
String pattern =
MessageFormat.format(patterns[PERCENTSTYLE],
dialect[i]);
return new DecimalFormat(pattern,
return new FooNumberFormat(pattern,
DecimalFormatSymbols.getInstance(locale));
}
}
......@@ -126,8 +128,8 @@ public class NumberFormatProviderImpl extends NumberFormatProvider {
* Adjusts the minimum and maximum fraction digits to values that
* are reasonable for the currency's default fraction digits.
*/
void adjustForCurrencyDefaultFractionDigits(DecimalFormat df) {
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
void adjustForCurrencyDefaultFractionDigits(FooNumberFormat nf) {
DecimalFormatSymbols dfs = nf.getDecimalFormatSymbols();
Currency currency = dfs.getCurrency();
if (currency == null) {
try {
......@@ -138,15 +140,15 @@ public class NumberFormatProviderImpl extends NumberFormatProvider {
if (currency != null) {
int digits = currency.getDefaultFractionDigits();
if (digits != -1) {
int oldMinDigits = df.getMinimumFractionDigits();
int oldMinDigits = nf.getMinimumFractionDigits();
// Common patterns are "#.##", "#.00", "#".
// Try to adjust all of them in a reasonable way.
if (oldMinDigits == df.getMaximumFractionDigits()) {
df.setMinimumFractionDigits(digits);
df.setMaximumFractionDigits(digits);
if (oldMinDigits == nf.getMaximumFractionDigits()) {
nf.setMinimumFractionDigits(digits);
nf.setMaximumFractionDigits(digits);
} else {
df.setMinimumFractionDigits(Math.min(digits, oldMinDigits));
df.setMaximumFractionDigits(digits);
nf.setMinimumFractionDigits(Math.min(digits, oldMinDigits));
nf.setMaximumFractionDigits(digits);
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册