7003643: [Fmt-Me] MessageFormat.toPattern produces wrong quoted string and subformat modifiers
7008195: [Fmt-Me] Improve MessageFormat.applyPattern performance
Reviewed-by: naoto, peytoia
--- a/jdk/src/share/classes/java/text/MessageFormat.java Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/src/share/classes/java/text/MessageFormat.java Mon Dec 27 14:13:52 2010 +0900
@@ -423,18 +423,19 @@
* @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 @@
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 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;
+ }
}
- } else if (formats[i] instanceof ChoiceFormat) {
- result.append(",choice,"
- + ((ChoiceFormat)formats[i]).toPattern());
+ 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 {
//result.append(", unknown");
}
@@ -674,7 +685,7 @@
*
* @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 @@
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 @@
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 @@
}
}
- 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 @@
}
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);
+ 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 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));
+
+ 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;
- }
- 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);
+
+ 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 3: case 4: // medium
- newFormat = DateFormat.getDateInstance(DateFormat.DEFAULT, 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;
- 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;
+ maxOffset = oldMaxOffset;
+ throw new IllegalArgumentException("unknown format type: " +
+ segments[SEG_TYPE]);
}
- 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);
- break;
- case 5: case 6: // long
- newFormat = DateFormat.getTimeInstance(DateFormat.LONG, locale);
- break;
- case 7: case 8: // full
- newFormat = DateFormat.getTimeInstance(DateFormat.FULL, locale);
- 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");
- }
- 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('\'');
+ }
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/text/Format/MessageFormat/Bug7003643.java Mon Dec 27 14:13:52 2010 +0900
@@ -0,0 +1,73 @@
+/*
+ * 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);
+ }
+ }
+ }
+}
--- a/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java Mon Dec 27 14:13:52 2010 +0900
@@ -1,5 +1,5 @@
/*
- * 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 @@
availableLocalesTest();
objectValidityTest();
extendedVariantTest();
+ messageFormatTest();
}
void availableLocalesTest() {
@@ -118,4 +119,48 @@
}
}
}
+
+
+ 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 + "'");
+ }
+ }
+ }
+ }
+ }
}
--- a/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.sh Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.sh Mon Dec 27 14:13:52 2010 +0900
@@ -1,5 +1,5 @@
#
-# 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
--- a/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java Mon Dec 27 14:13:52 2010 +0900
@@ -1,5 +1,5 @@
/*
- * 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 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 @@
NumberFormatProviderTest() {
availableLocalesTest();
objectValidityTest();
+ messageFormatTest();
}
void availableLocalesTest() {
@@ -72,14 +75,10 @@
}
// 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 @@
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 @@
}
}
}
+
+ 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 + "'");
+ }
+ }
+ }
+ }
}
--- a/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.sh Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.sh Mon Dec 27 14:13:52 2010 +0900
@@ -1,5 +1,5 @@
#
-# 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
Binary file jdk/test/java/util/PluggableLocale/fooprovider.jar has changed
--- a/jdk/test/java/util/PluggableLocale/providersrc/DateFormatProviderImpl.java Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/test/java/util/PluggableLocale/providersrc/DateFormatProviderImpl.java Mon Dec 27 14:13:52 2010 +0900
@@ -1,5 +1,5 @@
/*
- * 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 @@
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 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 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 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);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/PluggableLocale/providersrc/FooDateFormat.java Mon Dec 27 14:13:52 2010 +0900
@@ -0,0 +1,61 @@
+/*
+ * 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();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/PluggableLocale/providersrc/FooNumberFormat.java Mon Dec 27 14:13:52 2010 +0900
@@ -0,0 +1,81 @@
+/*
+ * 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);
+ }
+}
--- a/jdk/test/java/util/PluggableLocale/providersrc/Makefile Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/test/java/util/PluggableLocale/providersrc/Makefile Mon Dec 27 14:13:52 2010 +0900
@@ -28,6 +28,8 @@
DateFormatSymbolsProviderImpl.java \
DecimalFormatSymbolsProviderImpl.java \
NumberFormatProviderImpl.java \
+ FooDateFormat.java \
+ FooNumberFormat.java \
Utils.java
BARFILES_JAVA = \
--- a/jdk/test/java/util/PluggableLocale/providersrc/NumberFormatProviderImpl.java Thu Dec 23 18:25:35 2010 +0300
+++ b/jdk/test/java/util/PluggableLocale/providersrc/NumberFormatProviderImpl.java Mon Dec 27 14:13:52 2010 +0900
@@ -1,5 +1,5 @@
/*
- * 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 @@
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 @@
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 @@
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 @@
MessageFormat.format(patterns[NUMBERSTYLE],
dialect[i],
dialect[i]);
- return new DecimalFormat(pattern,
+ return new FooNumberFormat(pattern,
DecimalFormatSymbols.getInstance(locale));
}
}
@@ -115,7 +117,7 @@
String pattern =
MessageFormat.format(patterns[PERCENTSTYLE],
dialect[i]);
- return new DecimalFormat(pattern,
+ return new FooNumberFormat(pattern,
DecimalFormatSymbols.getInstance(locale));
}
}
@@ -126,8 +128,8 @@
* 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 @@
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);
}
}
}