undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,58 @@
#
# Copyright (c) 1999, 2016, 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.
#
# Hex dump of a serialized MessageFormat for Bug4185816Test.
aced0005737200176a6176612e746578742e4d657373616765466f726d617459
ea973e12bd01a80200064900096d61784f66667365745b000f617267756d656e
744e756d626572737400025b495b0007666f726d6174737400135b4c6a617661
2f746578742f466f726d61743b4c00066c6f63616c657400124c6a6176612f75
74696c2f4c6f63616c653b5b00076f66667365747371007e00014c0007706174
7465726e7400124c6a6176612f6c616e672f537472696e673b787200106a6176
612e746578742e466f726d6174fbd8bc12e90f18430200007870000000017572
00025b494dba602676eab2a502000078700000000a0000000100000000000000
0000000000000000000000000000000000000000000000000000000000757200
135b4c6a6176612e746578742e466f726d61743b5eacf7515deec1f802000078
700000000a70737200166a6176612e746578742e43686f696365466f726d6174
18e9c6bee365b6040200025b000d63686f696365466f726d6174737400135b4c
6a6176612f6c616e672f537472696e673b5b000c63686f6963654c696d697473
7400025b44787200166a6176612e746578742e4e756d626572466f726d6174df
f6b3bf137d07e803000b5a000c67726f7570696e67557365644200116d617846
72616374696f6e4469676974734200106d6178496e7465676572446967697473
4900156d6178696d756d4672616374696f6e4469676974734900146d6178696d
756d496e74656765724469676974734200116d696e4672616374696f6e446967
6974734200106d696e496e74656765724469676974734900156d696e696d756d
4672616374696f6e4469676974734900146d696e696d756d496e746567657244
69676974735a00107061727365496e74656765724f6e6c794900157365726961
6c56657273696f6e4f6e53747265616d7871007e000501032800000003000000
2800010000000000000001000000000178757200135b4c6a6176612e6c616e67
2e537472696e673badd256e7e91d7b470200007870000000037400086e6f2066
696c65737400086f6e652066696c657400107b302c6e756d6265727d2066696c
6573757200025b443ea68c14ab635a1e02000078700000000300000000000000
003ff000000000000040000000000000007070707070707070737200106a6176
612e7574696c2e4c6f63616c657ef811609c30f9ec0300044900086861736863
6f64654c0007636f756e74727971007e00044c00086c616e677561676571007e
00044c000776617269616e7471007e00047870ffffffff740002555374000265
6e740000787571007e00070000000a0000000a00000015000000000000000000
0000000000000000000000000000000000000000000000740016546865206469
736b20222220636f6e7461696e73202e

View file

@ -0,0 +1,58 @@
#
# Copyright (c) 1999, 2016, 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.
#
# Hex dump of a corrupted serialized MessageFormat for Bug4185732Test.
aced0005737200176a6176612e746578742e4d657373616765466f726d617459
ea973e12bd01a80200064900096d61784f66667365745b000f617267756d656e
744e756d626572737400025b495b0007666f726d6174737400135b4c6a617661
2f746578742f466f726d61743b4c00066c6f63616c657400124c6a6176612f75
74696c2f4c6f63616c653b5b00076f66667365747371007e00014c0007706174
7465726e7400124c6a6176612f6c616e672f537472696e673b787200106a6176
612e746578742e466f726d6174fbd8bc12e90f18430200007870000000017572
00025b494dba602676eab2a502000078700000000a0000000100000000000000
0000000000000000000000000000000000000000000000000000000000757200
135b4c6a6176612e746578742e466f726d61743b5eacf7515deec1f802000078
700000000a70737200166a6176612e746578742e43686f696365466f726d6174
18e9c6bee365b6040200025b000d63686f696365466f726d6174737400135b4c
6a6176612f6c616e672f537472696e673b5b000c63686f6963654c696d697473
7400025b44787200166a6176612e746578742e4e756d626572466f726d6174df
f6b3bf137d07e803000b5a000c67726f7570696e67557365644200116d617846
72616374696f6e4469676974734200106d6178496e7465676572446967697473
4900156d6178696d756d4672616374696f6e4469676974734900146d6178696d
756d496e74656765724469676974734200116d696e4672616374696f6e446967
6974734200106d696e496e74656765724469676974734900156d696e696d756d
4672616374696f6e4469676974734900146d696e696d756d496e746567657244
69676974735a00107061727365496e74656765724f6e6c794900157365726961
6c56657273696f6e4f6e53747265616d7871007e000501032800000003000000
2800010000000000000001000000000178757200135b4c6a6176612e6c616e67
2e537472696e673badd256e7e91d7b470200007870000000037400086e6f2066
696c65737400086f6e652066696c657400107b302c6e756d6265727d2066696c
6573757200025b443ea68c14ab635a1e02000078700000000300000000000000
003ff000000000000040000000000000007070707070707070737200106a6176
612e7574696c2e4c6f63616c657ef811609c30f9ec0300044900086861736863
6f64654c0007636f756e74727971007e00044c00086c616e677561676571007e
00044c000776617269616e7471007e00047870ffffffff740002555374000265
6e740000787571007e00070000000a0000000a00000015000000000000000000
0000000000000000000000000000000000000000000000740010546865206469
736b20222220636f6e74

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 1999, 2023, 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 4185816
* @library /java/text/testlib
* @build Bug4185816Test HexDumpReader
* @run junit Bug4185816Test
* @summary test that MessageFormat invariants are preserved across serialization.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file and, per its terms, should not be removed:
*
* (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
*
* Portions copyright (c) 2007 Sun Microsystems, Inc.
* All Rights Reserved.
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
import java.util.*;
import java.io.*;
import java.text.ChoiceFormat;
import java.text.MessageFormat;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
/**
* A Locale can never contain language codes of he, yi or id.
*/
public class Bug4185816Test {
private static final String FILE_NAME = "Bug4185816.ser";
private static final String CORRUPT_FILE_NAME = "Bug4185816Corrupt.ser";
@Test
public void testIt() throws Exception {
Exception e = checkStreaming(FILE_NAME);
if (e != null) {
fail("MessageFormat did not stream in valid stream: "+e);
e.printStackTrace();
}
e = checkStreaming(CORRUPT_FILE_NAME);
if (!(e instanceof InvalidObjectException)) {
fail("MessageFormat did NOT detect corrupt stream: "+e);
e.printStackTrace();
}
}
public Exception checkStreaming(final String fileName) {
try {
final InputStream is = HexDumpReader.getStreamFromHexDump(fileName + ".txt");
final ObjectInputStream in = new ObjectInputStream(is);
final MessageFormat form = (MessageFormat)in.readObject();
final Object[] testArgs = {12373L, "MyDisk"};
final String result = form.format(testArgs);
in.close();
} catch (Exception e) {
return e;
}
return null;
}
}

View file

@ -0,0 +1,74 @@
/*
* 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.)
* @key randomness
*/
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);
}
}
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2024, 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 8318761 8329118
* @summary Test MessageFormatPattern ability to recognize and produce
* appropriate FormatType and FormatStyle for CompactNumberFormat.
* @run junit CompactSubFormats
*/
import java.text.CompactNumberFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.Locale;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
// This test expects an US locale, as this locale provides distinct instances
// for different styles.
public class CompactSubFormats {
// Ensure the built-in FormatType and FormatStyles for cnFmt are as expected
@Test
public void applyPatternTest() {
var mFmt = new MessageFormat(
"{0,number,compact_short}{1,number,compact_long}", Locale.US);
var compactShort = NumberFormat.getCompactNumberInstance(
mFmt.getLocale(), NumberFormat.Style.SHORT);
var compactLong = NumberFormat.getCompactNumberInstance(
mFmt.getLocale(), NumberFormat.Style.LONG);
assertEquals(mFmt.getFormatsByArgumentIndex()[0], compactShort);
assertEquals(mFmt.getFormatsByArgumentIndex()[1], compactLong);
}
// Ensure that only 'compact_short' and 'compact_long' are recognized as
// compact number modifiers. All other compact_XX should be interpreted as
// a subformatPattern for a DecimalFormat
@Test
public void recognizedCompactStylesTest() {
// An exception won't be thrown since 'compact_regular' will be interpreted as a
// subformatPattern.
DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
assertEquals(new DecimalFormat("compact_regular", symbols),
new MessageFormat("{0,number,compact_regular}", Locale.US).getFormatsByArgumentIndex()[0]);
}
// SHORT and LONG CompactNumberFormats should produce correct patterns
@Test
public void toPatternTest() {
var mFmt = new MessageFormat("{0}{1}", Locale.US);
mFmt.setFormatByArgumentIndex(0, NumberFormat.getCompactNumberInstance(
mFmt.getLocale(), NumberFormat.Style.SHORT));
mFmt.setFormatByArgumentIndex(1, NumberFormat.getCompactNumberInstance(
mFmt.getLocale(), NumberFormat.Style.LONG));
assertEquals("{0,number,compact_short}{1,number,compact_long}", mFmt.toPattern());
}
// A custom cnFmt cannot be recognized, thus does not produce any built-in pattern
@Test
public void badToPatternTest() {
var mFmt = new MessageFormat("{0}", Locale.US);
// Non-recognizable compactNumberFormat
mFmt.setFormatByArgumentIndex(0, new CompactNumberFormat("",
DecimalFormatSymbols.getInstance(Locale.US), new String[]{""}));
// Default behavior of unrecognizable Formats is a FormatElement
// in the form of { ArgumentIndex }
assertEquals("{0}", mFmt.toPattern());
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2000, 2024, 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 4112090 8008577 8174269
* @summary verify that MessageFormat can handle large numbers of arguments
* @modules jdk.localedata
* @run main LargeMessageFormat
*/
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class LargeMessageFormat {
public static void main(String[] args) throws ParseException {
Locale reservedLocale = Locale.getDefault();
TimeZone reservedTimeZone = TimeZone.getDefault();
try {
Locale.setDefault(Locale.GERMANY);
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
testFormat();
testParse();
} finally {
// restore the reserved locale and time zone
Locale.setDefault(reservedLocale);
TimeZone.setDefault(reservedTimeZone);
}
}
private static final int REPEATS = 89;
private static void testFormat() {
// construct large argument array
@SuppressWarnings("deprecation")
Object[] sample = {
0, // replace with running count below
"hello",
new Date(89, 10, 9),
567890,
1234.50
};
int samples = sample.length;
Object[] arguments = new Object[REPEATS * (samples + 1)];
for (int i = 0; i < REPEATS; i++) {
System.arraycopy(sample, 0, arguments, i * samples, samples);
arguments[i * samples] = i;
}
// construct large template
StringBuffer template = new StringBuffer();
for (int i = 0; i < REPEATS; i++) {
template.append("section {" + (i * samples) + ", number} - ");
template.append("string: {" + (i * samples + 1) + "}; ");
template.append("date: {" + (i * samples + 2) + ", date}; ");
template.append("integer: {" + (i * samples + 3) + ", number}; ");
template.append("currency: {" + (i * samples + 4) + ", number, currency};\n");
}
// construct expected result string
StringBuffer expected = new StringBuffer();
for (int i = 0; i < REPEATS; i++) {
expected.append("section " + i + " - ");
expected.append("string: hello; ");
expected.append("date: 09.11.1989; ");
expected.append("integer: 567.890; ");
expected.append("currency: 1.234,50\u00a0\u20AC;\n");
}
// create message format
MessageFormat format = new MessageFormat(template.toString());
String result = format.format(arguments);
if (!result.equals(expected.toString())) {
System.out.println("Template:");
System.out.println(template);
System.out.println("Expected result: ");
System.out.println(expected);
System.out.println("Actual result: ");
System.out.println(result);
throw new RuntimeException();
}
}
private static void testParse() throws ParseException {
StringBuffer parseTemplate = new StringBuffer();
StringBuffer parseInput = new StringBuffer();
for (int i = 0; i < REPEATS; i++) {
parseTemplate.append("{" + i + ", number} ");
parseInput.append(i + " ");
}
MessageFormat parseFormat = new MessageFormat(parseTemplate.toString());
Object[] parseResult = parseFormat.parse(parseInput.toString());
for (int i = 0; i < REPEATS; i++) {
if (((Number) parseResult[i]).intValue() != i) {
throw new RuntimeException("got wrong parse result");
}
}
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2024, 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 8318761 8329118
* @summary Test MessageFormatPattern ability to recognize and produce the
* appropriate FormatType and FormatStyle for ListFormat. ListFormat's
* STANDARD, OR, and UNIT types are supported as built-in patterns for
* MessageFormat. All types use the FULL style.
* @run junit ListSubFormats
*/
import java.text.ListFormat;
import java.text.MessageFormat;
import java.util.Locale;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
// This test expects an US locale, as this locale provides distinct instances
// for different styles.
public class ListSubFormats {
// Recognize the 'list' FormatType as well as '', 'or', and
// 'unit' associated FormatStyles
@Test
public void applyPatternTest() {
var mFmt = new MessageFormat("{0,list}{1,list,or}{2,list,unit}", Locale.US);
var listStandard = ListFormat.getInstance(mFmt.getLocale(),
ListFormat.Type.STANDARD, ListFormat.Style.FULL);
var listOr = ListFormat.getInstance(mFmt.getLocale(),
ListFormat.Type.OR, ListFormat.Style.FULL);
var listUnit = ListFormat.getInstance(mFmt.getLocale(),
ListFormat.Type.UNIT, ListFormat.Style.FULL);
assertEquals(mFmt.getFormatsByArgumentIndex()[0], listStandard);
assertEquals(mFmt.getFormatsByArgumentIndex()[1], listOr);
assertEquals(mFmt.getFormatsByArgumentIndex()[2], listUnit);
}
// Ensure incorrect FormatElement pattern throws IAE
// java.text.ListFormat does not support String subformatPatterns
@Test
public void badApplyPatternTest() {
// Wrong FormatStyle
IllegalArgumentException exc = assertThrows(IllegalArgumentException.class, () ->
new MessageFormat("{0,list,standard}", Locale.US));
assertEquals("Unexpected modifier for List: standard", exc.getMessage());
// Wrong FormatType
exc = assertThrows(IllegalArgumentException.class, () ->
new MessageFormat("{0,listt,or}", Locale.US));
assertEquals("unknown format type: listt", exc.getMessage());
}
// STANDARD, OR, UNIT ListFormats (with FULL style) should
// produce correct patterns.
@Test
public void toPatternTest() {
var mFmt = new MessageFormat("{0}{1}{2}", Locale.US);
mFmt.setFormatByArgumentIndex(0,
ListFormat.getInstance(mFmt.getLocale(), ListFormat.Type.STANDARD, ListFormat.Style.FULL));
mFmt.setFormatByArgumentIndex(1,
ListFormat.getInstance(mFmt.getLocale(), ListFormat.Type.OR, ListFormat.Style.FULL));
mFmt.setFormatByArgumentIndex(2,
ListFormat.getInstance(mFmt.getLocale(), ListFormat.Type.UNIT, ListFormat.Style.FULL));
assertEquals("{0,list}{1,list,or}{2,list,unit}", mFmt.toPattern());
}
// A custom ListFormat cannot be recognized, thus does not produce any built-in pattern
@Test
public void badToPatternTest() {
var mFmt = new MessageFormat("{0}", Locale.US);
mFmt.setFormatByArgumentIndex(0,
ListFormat.getInstance(mFmt.getLocale(),
ListFormat.Type.UNIT, ListFormat.Style.NARROW));
assertEquals("{0}", mFmt.toPattern());
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2024, 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 8331446
* @summary Enforce the MAX_ARGUMENT_INDEX(10,000) implementation limit for the
* ArgumentIndex element in the MessageFormat pattern syntax. This
* should be checked during construction/applyPattern/readObject and should effectively
* prevent parse/format from being invoked with values over the limit.
* @run junit MaxArgumentIndexTest
*/
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class MaxArgumentIndexTest {
// A MessageFormat pattern that contains an ArgumentIndex value
// which violates this implementation's limit: MAX_ARGUMENT_INDEX(10,000)
// As this check is exclusive, 10,000 will violate the limit
private static final String VIOLATES_MAX_ARGUMENT_INDEX = "{10000}";
// Check String constructor enforces the limit
@Test
public void constructorTest() {
assertThrows(IllegalArgumentException.class,
() -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX));
}
// Check String, Locale constructor enforces the limit
@ParameterizedTest
@MethodSource
public void constructorWithLocaleTest(Locale locale) {
assertThrows(IllegalArgumentException.class,
() -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX, locale));
}
// Provide some basic common locale values
private static Stream<Locale> constructorWithLocaleTest() {
return Stream.of(null, Locale.US, Locale.ROOT);
}
// Edge case: Test a locale dependent subformat (with null locale) with a
// violating ArgumentIndex. In this instance, the violating ArgumentIndex
// will be caught and IAE thrown instead of the NPE
@Test
public void localeDependentSubFormatTest() {
assertThrows(IllegalArgumentException.class,
() -> new MessageFormat("{10000,number,short}", null));
// For reference
assertThrows(NullPointerException.class,
() -> new MessageFormat("{999,number,short}", null));
}
// Check that the static format method enforces the limit
@Test
public void staticFormatTest() {
assertThrows(IllegalArgumentException.class,
() -> MessageFormat.format(VIOLATES_MAX_ARGUMENT_INDEX, new Object[]{1}));
}
// Check that applyPattern(String) enforces the limit
@Test
public void applyPatternTest() {
MessageFormat mf = new MessageFormat("");
assertThrows(IllegalArgumentException.class,
() -> mf.applyPattern(VIOLATES_MAX_ARGUMENT_INDEX));
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2011, 2024, 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
* @summary Validate some exceptions in MessageFormat
* @bug 6481179 8039165 8318761
* @run junit MessageFormatExceptions
*/
import java.text.MessageFormat;
import java.util.Locale;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class MessageFormatExceptions {
// Any exception for a Subformat should be re-thrown as propagated as an IAE
// to the MessageFormat
@Test
public void rethrowAsIAE() {
// Same Subformat pattern for ChoiceFormat throws NumberFormatException
assertThrows(IllegalArgumentException.class,
() -> new MessageFormat("{0,choice,0foo#foo}"));
}
// MessageFormat should throw NPE when constructed with a null pattern
@Test
public void nullPatternTest() {
assertThrows(NullPointerException.class, () -> new MessageFormat(null));
assertThrows(NullPointerException.class, () -> new MessageFormat(null, Locale.US));
assertThrows(NullPointerException.class,
() -> MessageFormat.format(null, new Object[] { "val0", "val1" }));
}
// 8039165: When MessageFormat is constructed with a null locale a NPE
// can potentially be thrown depending on the subformat created. Either
// during the creation of the object itself, or later when format() is called.
// The following are some examples.
@Test
public void nullLocaleTest() {
// Fails when constructor invokes applyPattern()
assertThrows(NullPointerException.class,
() -> new MessageFormat("{0, date}", null));
// Same as above, but with Subformat pattern
assertThrows(NullPointerException.class,
() -> new MessageFormat("{0, date,dd}", null));
// Fail when constructor invokes applyPattern()
assertThrows(NullPointerException.class,
() -> new MessageFormat("{0, number}", null));
// Fail when object calls format()
assertThrows(NullPointerException.class,
() -> new MessageFormat("{0}", null).format(new Object[]{42}));
// Fail when object calls format(), but locale is set via .setLocale()
MessageFormat msgFmt = new MessageFormat("{0}");
msgFmt.setLocale(null);
assertThrows(NullPointerException.class, () -> msgFmt.format(new Object[]{42}));
// Does not always fail if locale is null
assertDoesNotThrow(() ->
new MessageFormat("{0}", null).format(new Object[]{"hello"}));
}
// 6481179: Invalid format type should be provided in error message of IAE
@Test
public void formatMsgTest() {
IllegalArgumentException iae = assertThrows(IllegalArgumentException.class,
() -> MessageFormat.format("Testdata {1,invalid_format_type}", new Object[] { "val0", "val1" }));
assertEquals("unknown format type: invalid_format_type", iae.getMessage());
}
}

View file

@ -0,0 +1,364 @@
/*
* Copyright (c) 2024, 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
* @summary Verify that MessageFormat.toPattern() properly escapes special curly braces
* @bug 8323699
* @run junit MessageFormatToPatternTest
*/
import java.text.ChoiceFormat;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MessageFormatToPatternTest {
private static final int NUM_RANDOM_TEST_CASES = 1000;
// Max levels of nesting of ChoiceFormats inside MessageFormats
private static final int MAX_FORMAT_NESTING = 3;
private static Locale savedLocale;
private static long randomSeed; // set this to a non-zero value for reproducibility
private static Random random;
private static boolean spitSeed;
private static int textCount;
// Setup & Teardown
@BeforeAll
public static void setup() {
savedLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
if (randomSeed == 0)
randomSeed = new Random().nextLong();
random = new Random(randomSeed);
}
@AfterAll
public static void teardown() {
Locale.setDefault(savedLocale);
}
// Tests
// Test expected output when given a MessageFormat pattern string and value 1.23
@ParameterizedTest
@MethodSource("generateOutputTestCases")
public void testOutput(String pattern, String expected) {
// Test we get the expected output
MessageFormat format = new MessageFormat(pattern);
String actual = format.format(new Object[] { 1.23 });
assertEquals(expected, actual);
// Test round trip as well
testRoundTrip(format);
}
public static Stream<Arguments> generateOutputTestCases() {
return Stream.of(
// This is the test case from JDK-8323699
Arguments.of("{0,choice,0.0#option A: {0}|1.0#option B: {0}'}'}", "option B: 1.23}"),
Arguments.of("{0,choice,0.0#option A: {0}|2.0#option B: {0}'}'}", "option A: 1.23"),
// A few more test cases from the PR#17416 discussion
Arguments.of("Test: {0,number,foo'{'#.00}", "Test: foo{1.23"),
Arguments.of("Test: {0,number,foo'}'#.00}", "Test: foo}1.23"),
Arguments.of("{0,number,' abc }'' ' 0.00}", " abc }' 1.23"),
Arguments.of("Wayne ''The Great One'' Gretsky", "Wayne 'The Great One' Gretsky"),
Arguments.of("'Wayne ''The Great One'' Gretsky'", "Wayne 'The Great One' Gretsky"),
Arguments.of("{0,choice,0.0#'''{''curly''}'' braces'}", "{curly} braces"),
Arguments.of("{0,choice,0.0#''{''curly''}'' braces}", "{curly} braces"),
Arguments.of("{0,choice,0.0#'{0,choice,0.0#''{0,choice,0.0#''''{0,choice,0.0#foo}''''}''}'}", "foo"),
// Absurd double quote examples
Arguments.of("Foo '}''''''''}' {0,number,bar'}' '}' } baz ", "Foo }''''} bar} } 1 baz "),
Arguments.of("'''}''{'''}''''}'", "'}'{'}''}"),
// An absurdly complicated example
Arguments.of("{0,choice,0.0#text2887 [] '{'1,date,YYYY-MM-DD'}' text2888 [''*'']|1.0#found|2.0#'text2901 [oog'')!''] {2,choice,0.0#''text2897 ['''']''''wq1Q] {2,choice,0.0#''''text2891 [s''''''''&''''''''] {0,number,#0.##} text2892 [8''''''''|$'''''''''''''''''''''''']''''|1.0#''''text2893 [] {0,number,#0.##} text2894 [S'''''''']'''''''']''''|2.0#text2895 [''''''''.''''''''eB] {1,date,YYYY-MM-DD} text2896 [9Y]} text2898 []''|1.0#''text2899 [xk7] {0,number,#0.##} text2900 []''} text2902 [7'':$)''O]'}{0,choice,0.0#'text2903 [] {0,number,#0.##} text2904 [S'':'']'|1.0#'me'}", "foundme")
);
}
// Go roundrip from MessageFormat -> pattern string -> MessageFormat and verify equivalence
@ParameterizedTest
@MethodSource("generateRoundTripTestCases")
public void testRoundTrip(MessageFormat format1) {
// Prepare MessageFormat argument
Object[] args = new Object[] {
8.5, // argument for DecimalFormat
new Date(1705502102677L), // argument for SimpleDateFormat
random.nextInt(6) // argument for ChoiceFormat
};
String pattern1 = null;
String result1 = null;
String pattern2 = null;
String result2 = null;
try {
// Format using the given MessageFormat
pattern1 = format1.toPattern();
result1 = format1.format(args);
// Round-trip via toPattern() and repeat
MessageFormat format2 = new MessageFormat(pattern1);
pattern2 = format2.toPattern();
result2 = format2.format(args);
// Check equivalence
assertEquals(result1, result2);
assertEquals(pattern1, pattern2);
// Debug
//showRoundTrip(format1, pattern1, result1, pattern2, result2);
} catch (RuntimeException | Error e) {
System.out.println(String.format("%n********** FAILURE **********%n"));
System.out.println(String.format("%s%n", e));
if (!spitSeed) {
System.out.println(String.format("*** Random seed was 0x%016xL%n", randomSeed));
spitSeed = true;
}
showRoundTrip(format1, pattern1, result1, pattern2, result2);
throw e;
}
}
public static Stream<Arguments> generateRoundTripTestCases() {
final ArrayList<Arguments> argList = new ArrayList<>();
for (int i = 0; i < NUM_RANDOM_TEST_CASES; i++)
argList.add(Arguments.of(randomFormat()));
return argList.stream();
}
// Generate a "random" MessageFormat. We do this by creating a MessageFormat with "{0}" placeholders
// and then substituting in random DecimalFormat, DateFormat, and ChoiceFormat subformats. The goal here
// is to avoid using pattern strings to construct formats, because they're what we're trying to check.
private static MessageFormat randomFormat() {
// Create a temporary MessageFormat containing "{0}" placeholders and random text
StringBuilder tempPattern = new StringBuilder();
int numParts = random.nextInt(3) + 1;
for (int i = 0; i < numParts; i++) {
if (random.nextBoolean())
tempPattern.append("{0}"); // temporary placeholder for a subformat
else
tempPattern.append(quoteText(randomText()));
}
// Replace all the "{0}" placeholders with random subformats
MessageFormat format = new MessageFormat(tempPattern.toString());
Format[] formats = format.getFormats();
for (int i = 0; i < formats.length; i++)
formats[i] = randomSubFormat(0);
format.setFormats(formats);
// Done
return format;
}
// Generate some random text
private static String randomText() {
StringBuilder buf = new StringBuilder();
int length = random.nextInt(6);
for (int i = 0; i < length; i++) {
char ch = (char)(0x20 + random.nextInt(0x5f));
buf.append(ch);
}
return buf.toString();
}
// Quote non-alphanumeric characters in the given plain text
private static String quoteText(String string) {
StringBuilder buf = new StringBuilder();
boolean quoted = false;
for (int i = 0; i < string.length(); i++) {
char ch = string.charAt(i);
if (ch == '\'')
buf.append("''");
else if (!(ch == ' ' || Character.isLetter(ch) || Character.isDigit(ch))) {
if (!quoted) {
buf.append('\'');
quoted = true;
}
buf.append(ch);
} else {
if (quoted) {
buf.append('\'');
quoted = false;
}
buf.append(ch);
}
}
if (quoted)
buf.append('\'');
return buf.toString();
}
// Create a random subformat for a MessageFormat
private static Format randomSubFormat(int nesting) {
int which;
if (nesting >= MAX_FORMAT_NESTING)
which = random.nextInt(2); // no more recursion
else
which = random.nextInt(3);
switch (which) {
case 0:
return new DecimalFormat("#.##");
case 1:
return new SimpleDateFormat("YYYY-MM-DD");
default:
int numChoices = random.nextInt(3) + 1;
assert numChoices > 0;
final double[] limits = new double[numChoices];
final String[] formats = new String[numChoices];
for (int i = 0; i < limits.length; i++) {
limits[i] = (double)i;
formats[i] = randomMessageFormatContaining(randomSubFormat(nesting + 1));
}
return new ChoiceFormat(limits, formats);
}
}
// Create a MessageFormat pattern string that includes the given Format as a subformat.
// The result will be one option in a ChoiceFormat which is nested in an outer MessageFormat.
// A ChoiceFormat option string is just a plain string; it's only when that plain string
// bubbles up to a containing MessageFormat that it gets interpreted as a MessageFormat string,
// and that only happens if the option string contains a '{' character. That will always
// be the case for the strings returned by this method of course.
private static String randomMessageFormatContaining(Format format) {
String beforeText = quoteText(randomText().replaceAll("\\{", "")); // avoid invalid MessageFormat syntax
String afterText = quoteText(randomText().replaceAll("\\{", "")); // avoid invalid MessageFormat syntax
String middleText;
if (format instanceof DecimalFormat dfmt)
middleText = String.format("{0,number,%s}", dfmt.toPattern());
else if (format instanceof SimpleDateFormat sdfmt)
middleText = String.format("{1,date,%s}", sdfmt.toPattern());
else if (format instanceof ChoiceFormat cfmt)
middleText = String.format("{2,choice,%s}", cfmt.toPattern());
else
throw new RuntimeException("internal error");
return String.format("text%d [%s] %s text%d [%s]", ++textCount, beforeText, middleText, ++textCount, afterText);
}
// Debug printing
private void showRoundTrip(MessageFormat format1, String pattern1, String result1, String pattern2, String result2) {
print(0, format1);
System.out.println();
if (pattern1 != null)
System.out.println(String.format(" pattern1 = %s", javaLiteral(pattern1)));
if (result1 != null)
System.out.println(String.format(" result1 = %s", javaLiteral(result1)));
if (pattern2 != null)
System.out.println(String.format(" pattern2 = %s", javaLiteral(pattern2)));
if (result2 != null)
System.out.println(String.format(" result2 = %s", javaLiteral(result2)));
System.out.println();
}
private static void print(int depth, Object format) {
if (format == null)
return;
if (format instanceof String)
System.out.println(String.format("%s- %s", indent(depth), javaLiteral((String)format)));
else if (format instanceof MessageFormat)
print(depth, (MessageFormat)format);
else if (format instanceof DecimalFormat)
print(depth, (DecimalFormat)format);
else if (format instanceof SimpleDateFormat)
print(depth, (SimpleDateFormat)format);
else if (format instanceof ChoiceFormat)
print(depth, (ChoiceFormat)format);
else
throw new RuntimeException("internal error: " + format.getClass());
}
private static void print(int depth, MessageFormat format) {
System.out.println(String.format("%s- %s: %s", indent(depth), "MessageFormat", javaLiteral(format.toPattern())));
for (Format subformat : format.getFormats())
print(depth + 1, subformat);
}
private static void print(int depth, DecimalFormat format) {
System.out.println(String.format("%s- %s: %s", indent(depth), "DecimalFormat", javaLiteral(format.toPattern())));
}
private static void print(int depth, SimpleDateFormat format) {
System.out.println(String.format("%s- %s: %s", indent(depth), "SimpleDateFormat", javaLiteral(format.toPattern())));
}
private static void print(int depth, ChoiceFormat format) {
System.out.println(String.format("%s- %s: %s", indent(depth), "ChoiceFormat", javaLiteral(format.toPattern())));
for (Object subformat : format.getFormats())
print(depth + 1, subformat);
}
private static String indent(int depth) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < depth; i++)
buf.append(" ");
return buf.toString();
}
// Print a Java string in double quotes so it looks like a String literal (for easy pasting into jshell)
private static String javaLiteral(String string) {
StringBuilder buf = new StringBuilder();
buf.append('"');
for (int i = 0; i < string.length(); i++) {
char ch = string.charAt(i);
switch (ch) {
case '"':
case '\\':
buf.append('\\');
// FALLTHROUGH
default:
buf.append(ch);
break;
}
}
return buf.append('"').toString();
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2000, 2024, 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 4105380
* @summary basic tests for new methods getFormatsByArgumentIndex, setFormatByArgumentIndex, setFormatsByArgumentIndex
*/
import java.text.ChoiceFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.text.NumberFormat;
public class MessageFormatsByArgumentIndex {
private static String choicePattern = "0.0#are no files|1.0#is one file|1.0<are {0,number,integer} files";
private static String quotedChoicePattern = choicePattern.replaceAll("([{}])", "'$1'");
public static void main(String[] args) {
Format[] subformats;
MessageFormat format = new MessageFormat("{3, choice," + choicePattern + "}, {2}, {0}");
subformats = format.getFormatsByArgumentIndex();
checkSubformatLength(subformats, 4);
checkSubformat(subformats, 0, null);
checkSubformat(subformats, 1, null);
checkSubformat(subformats, 2, null);
checkSubformat(subformats, 3, new ChoiceFormat(choicePattern));
subformats = format.getFormats();
checkSubformatLength(subformats, 3);
checkSubformat(subformats, 0, new ChoiceFormat(choicePattern));
checkSubformat(subformats, 1, null);
checkSubformat(subformats, 2, null);
format.setFormatByArgumentIndex(0, NumberFormat.getInstance());
checkPattern(format.toPattern(), "{3,choice," + quotedChoicePattern + "}, {2}, {0,number}");
subformats = format.getFormatsByArgumentIndex();
checkSubformatLength(subformats, 4);
checkSubformat(subformats, 0, NumberFormat.getInstance());
checkSubformat(subformats, 1, null);
checkSubformat(subformats, 2, null);
checkSubformat(subformats, 3, new ChoiceFormat(choicePattern));
subformats = format.getFormats();
checkSubformatLength(subformats, 3);
checkSubformat(subformats, 0, new ChoiceFormat(choicePattern));
checkSubformat(subformats, 1, null);
checkSubformat(subformats, 2, NumberFormat.getInstance());
format.setFormatsByArgumentIndex(subformats);
checkPattern(format.toPattern(),
"{3,choice," + quotedChoicePattern + "}, {2,number}, {0,choice," + quotedChoicePattern + "}");
subformats = format.getFormatsByArgumentIndex();
checkSubformatLength(subformats, 4);
checkSubformat(subformats, 0, new ChoiceFormat(choicePattern));
checkSubformat(subformats, 1, null);
checkSubformat(subformats, 2, NumberFormat.getInstance());
checkSubformat(subformats, 3, new ChoiceFormat(choicePattern));
subformats = format.getFormats();
checkSubformatLength(subformats, 3);
checkSubformat(subformats, 0, new ChoiceFormat(choicePattern));
checkSubformat(subformats, 1, NumberFormat.getInstance());
checkSubformat(subformats, 2, new ChoiceFormat(choicePattern));
}
private static void checkPattern(String actual, String expected) {
if (!expected.equals(actual)) {
throw new RuntimeException("unexpected pattern:\n expected: " + expected + "\n actual: " + actual);
}
}
private static void checkSubformatLength(Format[] subformats, int expected) {
if (subformats.length != expected) {
throw new RuntimeException("unexpected subformat length:\n expected: " + expected + "\n actual: " + subformats.length);
}
}
private static void checkSubformat(Format[] subformats, int index, Format expected) {
Format subformat = subformats[index];
if (subformat == expected) {
return;
}
if ((subformat != null) && subformat.equals(expected)) {
return;
}
throw new RuntimeException("found unexpected subformat for argument " + index + ":\n expected: " + expected + "\n actual: " + subformat);
}
}

View file

@ -0,0 +1,709 @@
/*
* Copyright (c) 1997, 2025, 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 4031438 4058973 4074764 4094906 4104976 4105380 4106659 4106660 4106661
* 4111739 4112104 4113018 4114739 4114743 4116444 4118592 4118594 4120552
* 4142938 4169959 4232154 4293229 8187551
* @summary Regression tests for MessageFormat and associated classes
* @library /java/text/testlib
* @run junit MessageRegression
*/
/*
(C) Copyright Taligent, Inc. 1996 - All Rights Reserved
(C) Copyright IBM Corp. 1996 - All Rights Reserved
The original version of this source code and documentation is copyrighted and
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
provided under terms of a License Agreement between Taligent and Sun. This
technology is protected by multiple US and International patents. This notice and
attribution to Taligent may not be removed.
Taligent is a registered trademark of Taligent, Inc.
*/
import java.text.*;
import java.util.*;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
public class MessageRegression {
/* @bug 4074764
* Null exception when formatting pattern with MessageFormat
* with no parameters.
*/
@Test
public void Test4074764() {
String[] pattern = {"Message without param",
"Message with param:{0}",
"Longer Message with param {0}"};
//difference between the two param strings are that
//in the first one, the param position is within the
//length of the string without param while it is not so
//in the other case.
MessageFormat messageFormatter = new MessageFormat("");
try {
//Apply pattern with param and print the result
messageFormatter.applyPattern(pattern[1]);
Object[] params = {new String("BUG"), new Date()};
String tempBuffer = messageFormatter.format(params);
if (!tempBuffer.equals("Message with param:BUG"))
fail("MessageFormat with one param test failed.");
System.out.println("Formatted with one extra param : " + tempBuffer);
//Apply pattern without param and print the result
messageFormatter.applyPattern(pattern[0]);
tempBuffer = messageFormatter.format(null);
if (!tempBuffer.equals("Message without param"))
fail("MessageFormat with no param test failed.");
System.out.println("Formatted with no params : " + tempBuffer);
tempBuffer = messageFormatter.format(params);
if (!tempBuffer.equals("Message without param"))
fail("Formatted with arguments > subsitution failed. result = " + tempBuffer.toString());
System.out.println("Formatted with extra params : " + tempBuffer);
//This statement gives an exception while formatting...
//If we use pattern[1] for the message with param,
//we get an NullPointerException in MessageFormat.java(617)
//If we use pattern[2] for the message with param,
//we get an StringArrayIndexOutOfBoundsException in MessageFormat.java(614)
//Both are due to maxOffset not being reset to -1
//in applyPattern() when the pattern does not
//contain any param.
} catch (Exception foo) {
fail("Exception when formatting with no params.");
}
}
/* @bug 4058973
* MessageFormat.toPattern has weird rounding behavior.
*/
@Test
public void Test4058973() {
MessageFormat fmt = new MessageFormat("{0,choice,0.0#no files|1.0#one file|1.0< '{'0,number,integer'}' files}");
String pat = fmt.toPattern();
if (!pat.equals("{0,choice,0.0#no files|1.0#one file|1.0< '{'0,number,integer'}' files}")) {
fail("MessageFormat.toPattern failed");
}
}
/* @bug 4031438
* More robust message formats.
*/
@Test
public void Test4031438() {
Locale locale = Locale.getDefault();
Assumptions.assumeTrue(TestUtils.usesAsciiDigits(locale),
locale + " does not use ASCII digits");
String pattern1 = "Impossible {1} has occurred -- status code is {0} and message is {2}.";
String pattern2 = "Double '' Quotes {0} test and quoted '{1}' test plus 'other {2} stuff'.";
MessageFormat messageFormatter = new MessageFormat("");
try {
System.out.println("Apply with pattern : " + pattern1);
messageFormatter.applyPattern(pattern1);
Object[] params = {7};
String tempBuffer = messageFormatter.format(params);
if (!tempBuffer.equals("Impossible {1} has occurred -- status code is 7 and message is {2}."))
fail("Tests arguments < substitution failed. Formatted text=" +
"<" + tempBuffer + ">");
System.out.println("Formatted with 7 : " + tempBuffer);
ParsePosition status = new ParsePosition(0);
Object[] objs = messageFormatter.parse(tempBuffer, status);
if (objs[params.length] != null)
fail("Parse failed with more than expected arguments");
for (int i = 0; i < objs.length; i++) {
if (objs[i] != null && !objs[i].toString().equals(params[i].toString())) {
fail("Parse failed on object " + objs[i] + " at index : " + i);
}
}
tempBuffer = messageFormatter.format(null);
if (!tempBuffer.equals("Impossible {1} has occurred -- status code is {0} and message is {2}."))
fail("Tests with no arguments failed");
System.out.println("Formatted with null : " + tempBuffer);
System.out.println("Apply with pattern : " + pattern2);
messageFormatter.applyPattern(pattern2);
tempBuffer = messageFormatter.format(params);
if (!tempBuffer.equals("Double ' Quotes 7 test and quoted {1} test plus other {2} stuff."))
fail("quote format test (w/ params) failed.");
System.out.println("Formatted with params : " + tempBuffer);
tempBuffer = messageFormatter.format(null);
if (!tempBuffer.equals("Double ' Quotes {0} test and quoted {1} test plus other {2} stuff."))
fail("quote format test (w/ null) failed.");
System.out.println("Formatted with null : " + tempBuffer);
System.out.println("toPattern : " + messageFormatter.toPattern());
} catch (Exception foo) {
fail("Exception when formatting in bug 4031438. "+foo.getMessage());
}
}
@Test
public void Test4052223()
{
ParsePosition pos = new ParsePosition(0);
if (pos.getErrorIndex() != -1) {
fail("ParsePosition.getErrorIndex initialization failed.");
}
MessageFormat fmt = new MessageFormat("There are {0} apples growing on the {1} tree.");
String str = new String("There is one apple growing on the peach tree.");
Object[] objs = fmt.parse(str, pos);
System.out.println("unparsable string , should fail at " + pos.getErrorIndex());
if (pos.getErrorIndex() == -1)
fail("Bug 4052223 failed : parsing string " + str);
pos.setErrorIndex(4);
if (pos.getErrorIndex() != 4)
fail("setErrorIndex failed, got " + pos.getErrorIndex() + " instead of 4");
ChoiceFormat f = new ChoiceFormat(
"-1#are negative|0#are no or fraction|1#is one|1.0<is 1+|2#are two|2<are more than 2.");
pos.setIndex(0); pos.setErrorIndex(-1);
Number obj = f.parse("are negative", pos);
if (pos.getErrorIndex() != -1 && obj.doubleValue() == -1.0)
fail("Parse with \"are negative\" failed, at " + pos.getErrorIndex());
pos.setIndex(0); pos.setErrorIndex(-1);
obj = f.parse("are no or fraction ", pos);
if (pos.getErrorIndex() != -1 && obj.doubleValue() == 0.0)
fail("Parse with \"are no or fraction\" failed, at " + pos.getErrorIndex());
pos.setIndex(0); pos.setErrorIndex(-1);
obj = f.parse("go postal", pos);
if (pos.getErrorIndex() == -1 && !Double.isNaN(obj.doubleValue()))
fail("Parse with \"go postal\" failed, at " + pos.getErrorIndex());
}
/* @bug 4104976
* ChoiceFormat.equals(null) throws NullPointerException
*/
@Test
public void Test4104976()
{
double[] limits = {1, 20};
String[] formats = {"xyz", "abc"};
ChoiceFormat cf = new ChoiceFormat(limits, formats);
try {
System.out.println("Compares to null is always false, returned : ");
System.out.println(cf.equals(null) ? "TRUE" : "FALSE");
} catch (Exception foo) {
fail("ChoiceFormat.equals(null) throws exception.");
}
}
/* @bug 4106659
* ChoiceFormat.ctor(double[], String[]) doesn't check
* whether lengths of input arrays are equal.
*/
@Test
public void Test4106659()
{
double[] limits = {1, 2, 3};
String[] formats = {"one", "two"};
ChoiceFormat cf = null;
try {
cf = new ChoiceFormat(limits, formats);
} catch (Exception foo) {
System.out.println("ChoiceFormat constructor should check for the array lengths");
cf = null;
}
if (cf != null) fail(cf.format(5));
}
/* @bug 4106660
* ChoiceFormat.ctor(double[], String[]) allows unordered double array.
* This is not a bug, added javadoc to emphasize the use of limit
* array must be in ascending order.
*/
@Test
public void Test4106660()
{
double[] limits = {3, 1, 2};
String[] formats = {"Three", "One", "Two"};
ChoiceFormat cf = new ChoiceFormat(limits, formats);
double d = 5.0;
String str = cf.format(d);
if (!str.equals("Two"))
fail("format(" + d + ") = " + cf.format(d));
}
/* @bug 4111739
* MessageFormat is incorrectly serialized/deserialized.
*/
@Test
public void Test4111739()
{
MessageFormat format1 = null;
MessageFormat format2 = null;
ObjectOutputStream ostream = null;
ByteArrayOutputStream baos = null;
ObjectInputStream istream = null;
try {
baos = new ByteArrayOutputStream();
ostream = new ObjectOutputStream(baos);
} catch(IOException e) {
fail("Unexpected exception : " + e.getMessage());
return;
}
try {
format1 = new MessageFormat("pattern{0}");
ostream.writeObject(format1);
ostream.flush();
byte bytes[] = baos.toByteArray();
istream = new ObjectInputStream(new ByteArrayInputStream(bytes));
format2 = (MessageFormat)istream.readObject();
} catch(Exception e) {
fail("Unexpected exception : " + e.getMessage());
}
if (!format1.equals(format2)) {
fail("MessageFormats before and after serialization are not" +
" equal\nformat1 = " + format1 + "(" + format1.toPattern() + ")\nformat2 = " +
format2 + "(" + format2.toPattern() + ")");
} else {
System.out.println("Serialization for MessageFormat is OK.");
}
}
/* @bug 4114743
* MessageFormat.applyPattern allows illegal patterns.
*/
@Test
public void Test4114743()
{
String originalPattern = "initial pattern";
MessageFormat mf = new MessageFormat(originalPattern);
try {
String illegalPattern = "ab { '}' de";
mf.applyPattern(illegalPattern);
fail("illegal pattern: \"" + illegalPattern + "\"");
} catch (IllegalArgumentException foo) {
if (!originalPattern.equals(mf.toPattern()))
fail("pattern after: \"" + mf.toPattern() + "\"");
}
}
/* @bug 4116444
* MessageFormat.parse has different behavior in case of null.
*/
@Test
public void Test4116444()
{
String[] patterns = {"", "one", "{0,date,short}"};
MessageFormat mf = new MessageFormat("");
for (int i = 0; i < patterns.length; i++) {
String pattern = patterns[i];
mf.applyPattern(pattern);
try {
Object[] array = mf.parse(null, new ParsePosition(0));
System.out.println("pattern: \"" + pattern + "\"");
System.out.println(" parsedObjects: ");
if (array != null) {
System.out.println("{");
for (int j = 0; j < array.length; j++) {
if (array[j] != null)
fail("\"" + array[j].toString() + "\"");
else
System.out.println("null");
if (j < array.length - 1) System.out.println(",");
}
System.out.println("}") ;
} else {
System.out.println("null");
}
System.out.println("");
} catch (Exception e) {
fail("pattern: \"" + pattern + "\"");
fail(" Exception: " + e.getMessage());
}
}
}
/* @bug 4114739 (FIX and add javadoc)
* MessageFormat.format has undocumented behavior about empty format objects.
*/
@Test
public void Test4114739()
{
MessageFormat mf = new MessageFormat("<{0}>");
Object[] objs1 = null;
Object[] objs2 = {};
Object[] objs3 = {null};
try {
System.out.println("pattern: \"" + mf.toPattern() + "\"");
System.out.println("format(null) : ");
System.out.println("\"" + mf.format(objs1) + "\"");
System.out.println("format({}) : ");
System.out.println("\"" + mf.format(objs2) + "\"");
System.out.println("format({null}) :");
System.out.println("\"" + mf.format(objs3) + "\"");
} catch (Exception e) {
fail("Exception thrown for null argument tests.");
}
}
/* @bug 4113018
* MessageFormat.applyPattern works wrong with illegal patterns.
*/
@Test
public void Test4113018()
{
String originalPattern = "initial pattern";
MessageFormat mf = new MessageFormat(originalPattern);
String illegalPattern = "format: {0, xxxYYY}";
System.out.println("pattern before: \"" + mf.toPattern() + "\"");
System.out.println("illegal pattern: \"" + illegalPattern + "\"");
try {
mf.applyPattern(illegalPattern);
fail("Should have thrown IllegalArgumentException for pattern : " + illegalPattern);
} catch (IllegalArgumentException e) {
if (!originalPattern.equals(mf.toPattern()))
fail("pattern after: \"" + mf.toPattern() + "\"");
}
}
/* @bug 4106661
* ChoiceFormat is silent about the pattern usage in javadoc.
*/
@Test
public void Test4106661()
{
ChoiceFormat fmt = new ChoiceFormat(
"-1#are negative| 0#are no or fraction | 1#is one |1.0<is 1+ |2#are two |2<are more than 2.");
System.out.println("Formatter Pattern : " + fmt.toPattern());
System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY));
System.out.println("Format with -1.0 : " + fmt.format(-1.0));
System.out.println("Format with 0 : " + fmt.format(0));
System.out.println("Format with 0.9 : " + fmt.format(0.9));
System.out.println("Format with 1.0 : " + fmt.format(1));
System.out.println("Format with 1.5 : " + fmt.format(1.5));
System.out.println("Format with 2 : " + fmt.format(2));
System.out.println("Format with 2.1 : " + fmt.format(2.1));
System.out.println("Format with NaN : " + fmt.format(Double.NaN));
System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY));
}
/* @bug 4094906
* ChoiceFormat should accept \u221E as eq. to INF.
*/
@Test
public void Test4094906()
{
ChoiceFormat fmt = new ChoiceFormat(
"-\u221E<are negative|0<are no or fraction|1#is one|1.0<is 1+|\u221E<are many.");
if (!fmt.toPattern().startsWith("-\u221E<are negative|0.0<are no or fraction|1.0#is one|1.0<is 1+|\u221E<are many."))
fail("Formatter Pattern : " + fmt.toPattern());
System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY));
System.out.println("Format with -1.0 : " + fmt.format(-1.0));
System.out.println("Format with 0 : " + fmt.format(0));
System.out.println("Format with 0.9 : " + fmt.format(0.9));
System.out.println("Format with 1.0 : " + fmt.format(1));
System.out.println("Format with 1.5 : " + fmt.format(1.5));
System.out.println("Format with 2 : " + fmt.format(2));
System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY));
}
/* @bug 4118592
* MessageFormat.parse fails with ChoiceFormat.
*/
@Test
public void Test4118592()
{
MessageFormat mf = new MessageFormat("");
String pattern = "{0,choice,1#YES|2#NO}";
String prefix = "";
for (int i = 0; i < 5; i++) {
String formatted = prefix + "YES";
mf.applyPattern(prefix + pattern);
prefix += "x";
Object[] objs = mf.parse(formatted, new ParsePosition(0));
System.out.println(i + ". pattern :\"" + mf.toPattern() + "\"");
System.out.println(" \"" + formatted + "\" parsed as ");
if (objs == null) System.out.println(" null");
else System.out.println(" " + objs[0]);
}
}
/* @bug 4118594
* MessageFormat.parse fails for some patterns.
*/
@Test
public void Test4118594()
{
MessageFormat mf = new MessageFormat("{0}, {0}, {0}");
String forParsing = "x, y, z";
Object[] objs = mf.parse(forParsing, new ParsePosition(0));
System.out.println("pattern: \"" + mf.toPattern() + "\"");
System.out.println("text for parsing: \"" + forParsing + "\"");
if (!objs[0].toString().equals("z"))
fail("argument0: \"" + objs[0] + "\"");
mf.setLocale(Locale.US);
mf.applyPattern("{0,number,#.##}, {0,number,#.#}");
Object[] oldobjs = {3.1415};
String result = mf.format( oldobjs );
System.out.println("pattern: \"" + mf.toPattern() + "\"");
System.out.println("text for parsing: \"" + result + "\"");
// result now equals "3.14, 3.1"
if (!result.equals("3.14, 3.1"))
fail("result = " + result);
Object[] newobjs = mf.parse(result, new ParsePosition(0));
// newobjs now equals {new Double(3.1)}
if (((Double)newobjs[0]).doubleValue() != 3.1)
fail( "newobjs[0] = " + newobjs[0]);
}
/* @bug 4105380
* When using ChoiceFormat, MessageFormat is not good for I18n.
*/
@Test
public void Test4105380()
{
String patternText1 = "The disk \"{1}\" contains {0}.";
String patternText2 = "There are {0} on the disk \"{1}\"";
MessageFormat form1 = new MessageFormat(patternText1);
MessageFormat form2 = new MessageFormat(patternText2);
double[] filelimits = {0,1,2};
String[] filepart = {"no files","one file","{0,number} files"};
ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
form1.setFormat(1, fileform);
form2.setFormat(0, fileform);
Object[] testArgs = {12373L, "MyDisk"};
System.out.println(form1.format(testArgs));
System.out.println(form2.format(testArgs));
}
/* @bug 4120552
* MessageFormat.parse incorrectly sets errorIndex.
*/
@Test
public void Test4120552()
{
MessageFormat mf = new MessageFormat("pattern");
String texts[] = {"pattern", "pat", "1234"};
System.out.println("pattern: \"" + mf.toPattern() + "\"");
for (int i = 0; i < texts.length; i++) {
ParsePosition pp = new ParsePosition(0);
Object[] objs = mf.parse(texts[i], pp);
System.out.println(" text for parsing: \"" + texts[i] + "\"");
if (objs == null) {
System.out.println(" (incorrectly formatted string)");
if (pp.getErrorIndex() == -1)
fail("Incorrect error index: " + pp.getErrorIndex());
} else {
System.out.println(" (correctly formatted string)");
}
}
}
/**
* @bug 4142938
* MessageFormat handles single quotes in pattern wrong.
* This is actually a problem in ChoiceFormat; it doesn't
* understand single quotes.
*/
@Test
public void Test4142938() {
String pat = "''Vous'' {0,choice,0#n''|1#}avez s\u00E9lectionne\u00E9 " +
"{0,choice,0#aucun|1#{0}} client{0,choice,0#s|1#|2#s} " +
"personnel{0,choice,0#s|1#|2#s}.";
MessageFormat mf = new MessageFormat(pat);
String[] PREFIX = {
"'Vous' n'avez s\u00E9lectionne\u00E9 aucun clients personnels.",
"'Vous' avez s\u00E9lectionne\u00E9 ",
"'Vous' avez s\u00E9lectionne\u00E9 "
};
String[] SUFFIX = {
null,
" client personnel.",
" clients personnels."
};
for (int i=0; i<3; i++) {
String out = mf.format(new Object[]{i});
if (SUFFIX[i] == null) {
if (!out.equals(PREFIX[i]))
fail("" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\"");
}
else {
if (!out.startsWith(PREFIX[i]) ||
!out.endsWith(SUFFIX[i]))
fail("" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\"...\"" +
SUFFIX[i] + "\"");
}
}
}
/**
* @bug 4142938
* Test the applyPattern and toPattern handling of single quotes
* by ChoiceFormat. (This is in here because this was a bug reported
* against MessageFormat.) The single quote is used to quote the
* pattern characters '|', '#', '<', and '\u2264'. Two quotes in a row
* is a quote literal.
*/
@Test
public void TestChoicePatternQuote() {
String[] DATA = {
// Pattern 0 value 1 value
"0#can''t|1#can", "can't", "can",
"0#'pound(#)=''#'''|1#xyz", "pound(#)='#'", "xyz",
"0#'1<2 | 1\u22641'|1#''", "1<2 | 1\u22641", "'",
};
for (int i=0; i<DATA.length; i+=3) {
try {
ChoiceFormat cf = new ChoiceFormat(DATA[i]);
for (int j=0; j<=1; ++j) {
String out = cf.format(j);
if (!out.equals(DATA[i+1+j]))
fail("Fail: Pattern \"" + DATA[i] + "\" x "+j+" -> " +
out + "; want \"" + DATA[i+1+j] + '"');
}
String pat = cf.toPattern();
String pat2 = new ChoiceFormat(pat).toPattern();
if (!pat.equals(pat2))
fail("Fail: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + '"');
else
System.out.println("Ok: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + '"');
}
catch (IllegalArgumentException e) {
fail("Fail: Pattern \"" + DATA[i] + "\" -> " + e);
}
}
}
/**
* @bug 4112104
* MessageFormat.equals(null) throws a NullPointerException. The JLS states
* that it should return false.
*/
@Test
public void Test4112104() {
MessageFormat format = new MessageFormat("");
try {
// This should NOT throw an exception
if (format.equals(null)) {
// It also should return false
fail("MessageFormat.equals(null) returns false");
}
}
catch (NullPointerException e) {
fail("MessageFormat.equals(null) throws " + e);
}
}
/**
* @bug 4169959
* MessageFormat does not format null objects. CANNOT REPRODUCE THIS BUG.
*/
@Test
public void Test4169959() {
// This works
System.out.println(MessageFormat.format( "This will {0}", "work"));
// This fails
System.out.println(MessageFormat.format( "This will {0}",
new Object[]{ null } ) );
}
@Test
public void test4232154() {
boolean gotException = false;
try {
MessageFormat format = new MessageFormat("The date is {0:date}");
} catch (Exception e) {
gotException = true;
if (!(e instanceof IllegalArgumentException)) {
throw new RuntimeException("got wrong exception type");
}
if ("argument number too large at ".equals(e.getMessage())) {
throw new RuntimeException("got wrong exception message");
}
}
if (!gotException) {
throw new RuntimeException("didn't get exception for invalid input");
}
}
@Test
public void test4293229() {
MessageFormat format = new MessageFormat("'''{'0}'' '''{0}'''");
Object[] args = { null };
String expected = "'{0}' '{0}'";
String result = format.format(args);
if (!result.equals(expected)) {
throw new RuntimeException("wrong format result - expected \"" +
expected + "\", got \"" + result + "\"");
}
}
/**
* @bug 8187551
* test MessageFormat.setFormat() method to throw AIOOBE on invalid index.
*/
@Test
public void test8187551() {
//invalid cases ("pattern", "invalid format element index")
String[][] invalidCases = {{"The disk \"{1}\" contains {0}.", "2"},
{"The disk \"{1}\" contains {0}.", "9"},
{"On {1}, there are {0} and {2} folders", "3"}};
//invalid cases (must throw exception)
Arrays.stream(invalidCases).forEach(entry -> messageSetFormat(entry[0],
Integer.valueOf(entry[1])));
}
// test MessageFormat.setFormat() method for the given pattern and
// format element index
private void messageSetFormat(String pattern, int elemIndex) {
MessageFormat form = new MessageFormat(pattern);
double[] fileLimits = {0, 1, 2};
String[] filePart = {"no files", "one file", "{0,number} files"};
ChoiceFormat fileForm = new ChoiceFormat(fileLimits, filePart);
boolean AIOOBEThrown = false;
try {
form.setFormat(elemIndex, fileForm);
} catch (ArrayIndexOutOfBoundsException ex) {
AIOOBEThrown = true;
}
if (!AIOOBEThrown) {
throw new RuntimeException("[FAILED: Must throw" +
" ArrayIndexOutOfBoundsException for" +
" invalid index " + elemIndex + " used in" +
" MessageFormat.setFormat(index, format)]");
}
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 1997, 2023, 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
* @summary test MessageFormat
* @run junit MessageTest
*/
/*
(C) Copyright Taligent, Inc. 1996 - All Rights Reserved
(C) Copyright IBM Corp. 1996 - All Rights Reserved
The original version of this source code and documentation is copyrighted and
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
provided under terms of a License Agreement between Taligent and Sun. This
technology is protected by multiple US and International patents. This notice and
attribution to Taligent may not be removed.
Taligent is a registered trademark of Taligent, Inc.
*/
import java.util.*;
import java.io.*;
import java.text.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
public class MessageTest {
@Test
public void TestMSGPatternTest() {
Object[] testArgs = {
1D, 3456D,
"Disk", new Date(10000000000L)};
String[] testCases = {
"Quotes '', '{', 'a' {0} '{0}'",
"Quotes '', '{', 'a' {0,number} '{0}'",
"'{'1,number,'#',##} {1,number,'#',##}",
"There are {1} files on {2} at {3}",
"On {2}, there are {1} files, with {0,number,currency}.",
"'{1,number,percent}', {1,number,percent}, ",
"'{1,date,full}', {1,date,full}, ",
"'{3,date,full}', {3,date,full}, ",
"'{1,number,#,##}' {1,number,#,##}",
};
for (int i = 0; i < testCases.length; ++i) {
Locale save = Locale.getDefault();
try {
Locale.setDefault(Locale.US);
System.out.println("");
System.out.println( i + " Pat in: " + testCases[i]);
MessageFormat form = new MessageFormat(testCases[i]);
System.out.println( i + " Pat out: " + form.toPattern());
String result = form.format(testArgs);
System.out.println( i + " Result: " + result);
Object[] values = form.parse(result);
for (int j = 0; j < testArgs.length; ++j) {
Object testArg = testArgs[j];
Object value = null;
if (j < values.length) {
value = values[j];
}
if ((testArg == null && value != null)
|| (testArg != null && !testArg.equals(value))) {
System.out.println( i + " " + j + " old: " + testArg);
System.out.println( i + " " + j + " new: " + value);
}
}
}
catch(java.text.ParseException pe ) {
throw new RuntimeException("Error: MessageFormat.parse throws ParseException");
}
finally{
Locale.setDefault(save);
}
}
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2024, 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 8331446 8340554
* @summary Check correctness of deserialization
* @run junit SerializationTest
*/
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class SerializationTest {
// Ensure basic correctness of serialization round trip
@ParameterizedTest
@MethodSource
public void serializationRoundTrip(MessageFormat expectedMf)
throws IOException, ClassNotFoundException {
byte[] bytes = ser(expectedMf);
MessageFormat actualMf = (MessageFormat) deSer(bytes);
assertEquals(expectedMf, actualMf);
}
// Various valid MessageFormats
private static Stream<MessageFormat> serializationRoundTrip() {
return Stream.of(
// basic pattern
new MessageFormat("{0} foo"),
// Multiple arguments
new MessageFormat("{0} {1} foo"),
// duplicate arguments
new MessageFormat("{0} {0} {1} foo"),
// Non-ascending arguments
new MessageFormat("{1} {0} foo"),
// With locale
new MessageFormat("{1} {0} foo", Locale.UK),
// With null locale. (NPE not thrown, if no format defined)
new MessageFormat("{1} {0} foo", null),
// With formats
new MessageFormat("{0,number,short} {0} {1,date,long} foo"),
// Offset equal to pattern length (0)
new MessageFormat("{0}"),
// Offset equal to pattern length (1)
new MessageFormat("X{0}"),
// Offset 1 under pattern length
new MessageFormat("X{0}X")
);
}
// Utility method to serialize
private static byte[] ser(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new
ByteArrayOutputStream();
ObjectOutputStream oos = new
ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(obj);
return byteArrayOutputStream.toByteArray();
}
// Utility method to deserialize
private static Object deSer(byte[] bytes) throws
IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new
ByteArrayInputStream(bytes);
ObjectInputStream ois = new
ObjectInputStream(byteArrayInputStream);
return ois.readObject();
}
}

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2024, 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 8318761
* @summary Test MessageFormatPattern ability to recognize the appropriate
* FormatType and FormatStyle for DateTimeFormatter(ClassicFormat).
* This includes the types dtf_time, dtf_date, dtf_datetime,
* and the DateTimeFormatter predefined formatters.
* @run junit TemporalSubFormats
*/
import java.text.Format;
import java.text.MessageFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TemporalSubFormats {
// Check that applying the built-in DateTimeFormatter types returns the
// correct Format and formats properly. Patterns are case-insensitive
@ParameterizedTest
@MethodSource("preDefinedTypes")
public void preDefinedPatternsTest(String pattern, Format fmt) {
var mFmt = new MessageFormat("quux{0,"+pattern+"}quux");
Object[] temporals = new Object[]{LocalDate.now(), LocalTime.now(),
ZonedDateTime.now(), LocalDateTime.now(), OffsetDateTime.now(), Instant.now()};
for (Object val : temporals) {
// Wrap in Object array for MessageFormat
Object[] wrappedVal = new Object[]{val};
try {
String mFmtted = mFmt.format(wrappedVal);
// If current format can support the time object. Check equality of result
assertEquals(mFmtted, "quux"+fmt.format(val)+"quux");
} catch (IllegalArgumentException ignored) {
// Otherwise, ensure both throw IAE on unsupported field
assertThrows(IllegalArgumentException.class, () -> fmt.format(val));
}
}
}
// Provides String patterns and the associated (standalone) FormatType
// Values should be case-insensitive
private static Stream<Arguments> preDefinedTypes() {
return Stream.of(
Arguments.of("BASIC_ISO_DATE", DateTimeFormatter.BASIC_ISO_DATE.toFormat()),
Arguments.of("ISO_LOCAL_DATE", DateTimeFormatter.ISO_LOCAL_DATE.toFormat()),
Arguments.of("ISO_OFFSET_DATE", DateTimeFormatter.ISO_OFFSET_DATE.toFormat()),
Arguments.of("ISO_DATE", DateTimeFormatter.ISO_DATE.toFormat()),
Arguments.of("iso_local_time", DateTimeFormatter.ISO_LOCAL_TIME.toFormat()),
Arguments.of("ISO_OFFSET_TIME", DateTimeFormatter.ISO_OFFSET_TIME.toFormat()),
Arguments.of("iso_time", DateTimeFormatter.ISO_TIME.toFormat()),
Arguments.of("ISO_LOCAL_DATE_TIME", DateTimeFormatter.ISO_LOCAL_DATE_TIME.toFormat()),
Arguments.of("ISO_OFFSET_DATE_TIME", DateTimeFormatter.ISO_OFFSET_DATE_TIME.toFormat()),
Arguments.of("ISO_ZONED_DATE_TIME", DateTimeFormatter.ISO_ZONED_DATE_TIME.toFormat()),
Arguments.of("ISO_DATE_TIME", DateTimeFormatter.ISO_DATE_TIME.toFormat()),
Arguments.of("ISO_ORDINAL_DATE", DateTimeFormatter.ISO_ORDINAL_DATE.toFormat()),
Arguments.of("iso_week_date", DateTimeFormatter.ISO_WEEK_DATE.toFormat()),
Arguments.of("ISO_INSTANT", DateTimeFormatter.ISO_INSTANT.toFormat()),
Arguments.of("RFC_1123_DATE_TIME", DateTimeFormatter.RFC_1123_DATE_TIME.toFormat())
);
}
// Check that the appropriate FormatType/Style combo returns correct Format
// Unlike the other pattern tests, the formatted output is used to check
// equality, as DateTimeFormatter does not implement equals()
@ParameterizedTest
@MethodSource("styles")
public void applyPatternTest(String style, FormatStyle fStyle) {
var time = ZonedDateTime.now();
var date = LocalDate.now();
// Test dtf_date
var dFmt = new MessageFormat("{0,dtf_date"+style+"}");
assertEquals(DateTimeFormatter.ofLocalizedDate(fStyle).withLocale(
dFmt.getLocale()).toFormat().format(date),
dFmt.getFormatsByArgumentIndex()[0].format(date));
// Test dtf_time
var tFmt = new MessageFormat("{0,dtf_time"+style+"}");
assertEquals(DateTimeFormatter.ofLocalizedTime(fStyle).withLocale(
tFmt.getLocale()).toFormat().format(time),
tFmt.getFormatsByArgumentIndex()[0].format(time));
// Test dtf_datetime
var dtFmt = new MessageFormat("{0,dtf_datetime"+style+"}");
assertEquals(DateTimeFormatter.ofLocalizedDateTime(fStyle).withLocale(
dtFmt.getLocale()).toFormat().format(time),
dtFmt.getFormatsByArgumentIndex()[0].format(time));
}
// Provides String patterns and the associated FormatStyle
private static Stream<Arguments> styles() {
return Stream.of(
Arguments.of("", FormatStyle.MEDIUM),
Arguments.of(",short", FormatStyle.SHORT),
Arguments.of(",medium", FormatStyle.MEDIUM),
Arguments.of(",long", FormatStyle.LONG),
Arguments.of(",full", FormatStyle.FULL)
);
}
// Test that a proper Format from a SubformatPattern can be reproduced
@Test
public void subformatPatternTest() {
// SubformatPattern invokes the same method for both dtf_date,
// dtf_time, and dtf_datetime
var pattern = "d MMM uuuu";
var date = LocalDate.now();
// Test dtf_date
var dFmt = new MessageFormat("{0,dtf_date,"+pattern+"}");
assertEquals(DateTimeFormatter.ofPattern(pattern,dFmt.getLocale()).toFormat().format(date),
dFmt.getFormatsByArgumentIndex()[0].format(date));
// Test dtf_time
var tFmt = new MessageFormat("{0,dtf_time,"+pattern+"}");
assertEquals(DateTimeFormatter.ofPattern(pattern,tFmt.getLocale()).toFormat().format(date),
tFmt.getFormatsByArgumentIndex()[0].format(date));
// Test dtf_datetime
var dtFmt = new MessageFormat("{0,dtf_datetime,"+pattern+"}");
assertEquals(DateTimeFormatter.ofPattern(pattern,dtFmt.getLocale()).toFormat().format(date),
dtFmt.getFormatsByArgumentIndex()[0].format(date));
}
// Ensure that only the supported built-in FormatStyles or a
// valid SubformatPattern are recognized
@Test
public void badApplyPatternTest() {
// Not a supported FormatStyle: throws the underlying IAE from DTF
// as it is interpreted as a subformatPattern
IllegalArgumentException exc = assertThrows(IllegalArgumentException.class, () ->
new MessageFormat("{0,dtf_date,longer}"));
assertEquals("Unknown pattern letter: l", exc.getMessage());
// Not a legal SubformatPattern: throws the underlying IAE from DTF
exc = assertThrows(IllegalArgumentException.class, () ->
new MessageFormat("{0,dtf_date,VVV}"));
assertEquals("Pattern letter count must be 2: V", exc.getMessage());
// Pre-defined ISO style does not exist and should be ignored
assertDoesNotThrow(() -> new MessageFormat("{0,BASIC_ISO_DATE,foo}"),
"Style on a pre-defined DTF should be ignored, instead of throwing an exception");
}
// DateTimeFormatters cannot be recognized when toPattern() is invoked
// Default behavior of unrecognizable Formats is a FormatElement
// in the form of { ArgumentIndex }
@Test
public void nonRecognizableToPatternTest() {
// Check SubformatPattern
var validPattern = "yy";
var mFmt = new MessageFormat("{0}");
mFmt.setFormatByArgumentIndex(0, DateTimeFormatter.ofPattern(validPattern).toFormat());
assertEquals("{0}", mFmt.toPattern());
// Check pre-defined styles
var dFmt = new MessageFormat("{0,dtf_date,long}");
assertEquals("{0}", dFmt.toPattern());
var tFmt = new MessageFormat("{0,dtf_time,long}");
assertEquals("{0}", tFmt.toPattern());
var dtFmt = new MessageFormat("{0,dtf_datetime,long}");
assertEquals("{0}", dtFmt.toPattern());
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2024, 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 8321545
* @summary Ensure value returned by overridden toString method is as expected
* @run junit ToStringTest
*/
import java.text.MessageFormat;
import java.util.Locale;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ToStringTest {
// Check a normal expected value
@Test
public void expectedValueTest() {
String expectedStr =
"MessageFormat [locale: \"English (Canada)\", pattern: \"foo {0}\"]\n";
var m = new MessageFormat("foo {0}", Locale.CANADA);
assertEquals(expectedStr, m.toString());
}
// Check the expected value when the locale is null
@Test
public void nullLocaleTest() {
String expectedStr =
"MessageFormat [locale: null, pattern: \"foo {0}\"]\n";
var m = new MessageFormat("foo {0}", null);
assertEquals(expectedStr, m.toString());
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2001, 2023, 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 4492719
* @summary Confirm that Message.parse() interprets time zone which uses "GMT+/-" format correctly and doesn't throw ParseException.
* @run junit/othervm bug4492719
*/
import java.util.*;
import java.text.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;
import static org.junit.jupiter.api.Assertions.fail;
public class bug4492719 {
// MessageFormat.parse() should be able to interpret a time zone
// that uses "GMT+/-".
@Test
public void testParse() throws Exception {
Locale savedLocale = Locale.getDefault();
TimeZone savedTimeZone = TimeZone.getDefault();
MessageFormat mf;
boolean err =false;
String[] formats = {
"short", "medium", "long", "full"
};
String[] timezones = {
"America/Los_Angeles", "GMT", "GMT+09:00", "GMT-8:00",
"GMT+123", "GMT-1234", "GMT+2", "GMT-13"
};
String text;
Locale.setDefault(Locale.US);
try {
for (int i = 0; i < timezones.length; i++) {
TimeZone.setDefault(TimeZone.getTimeZone(timezones[i]));
for (int j = 0; j < formats.length; j++) {
mf = new MessageFormat("{0,time," + formats[j] + "} - time");
text = MessageFormat.format("{0,time," + formats[j] + "} - time",
new Object [] { new Date(123456789012L)});
Object[] objs = mf.parse(text);
}
}
} catch (ParseException e) {
err = true;
System.err.println("Invalid ParseException occurred : " +
e.getMessage());
System.err.println(" TimeZone=" + TimeZone.getDefault());
}
finally {
Locale.setDefault(savedLocale);
TimeZone.setDefault(savedTimeZone);
if (err) {
throw new Exception("MessageFormat.parse(\"GMT format\") failed.");
}
}
}
}