1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.text;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertNotEquals;
21 import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
22
23 import java.text.DateFormat;
24 import java.text.FieldPosition;
25 import java.text.Format;
26 import java.text.MessageFormat;
27 import java.text.NumberFormat;
28 import java.text.ParsePosition;
29 import java.util.Arrays;
30 import java.util.Calendar;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Locale;
35 import java.util.Map;
36
37 import org.apache.commons.lang3.AbstractLangTest;
38 import org.junit.jupiter.api.BeforeEach;
39 import org.junit.jupiter.api.Test;
40 import org.junit.jupiter.params.ParameterizedTest;
41 import org.junit.jupiter.params.provider.MethodSource;
42 import org.junitpioneer.jupiter.cartesian.ArgumentSets;
43 import org.junitpioneer.jupiter.cartesian.CartesianTest;
44
45
46
47
48 @Deprecated
49 class ExtendedMessageFormatTest extends AbstractLangTest {
50
51
52
53
54 private static final class LowerCaseFormat extends Format {
55 private static final long serialVersionUID = 1L;
56
57 @Override
58 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
59 return toAppendTo.append(((String) obj).toLowerCase(Locale.ROOT));
60 }
61 @Override
62 public Object parseObject(final String source, final ParsePosition pos) {
63 throw new UnsupportedOperationException();
64 }
65 }
66
67
68
69
70 private static final class LowerCaseFormatFactory implements FormatFactory {
71 private static final Format LOWER_INSTANCE = new LowerCaseFormat();
72
73 @Override
74 public Format getFormat(final String name, final String arguments, final Locale locale) {
75 return LOWER_INSTANCE;
76 }
77 }
78
79
80
81
82 private static final class OtherExtendedMessageFormat extends ExtendedMessageFormat {
83 private static final long serialVersionUID = 1L;
84
85 OtherExtendedMessageFormat(final String pattern, final Locale locale,
86 final Map<String, ? extends FormatFactory> registry) {
87 super(pattern, locale, registry);
88 }
89
90 }
91
92
93
94
95 private static final class OverrideShortDateFormatFactory implements FormatFactory {
96
97 @Override
98 public Format getFormat(final String name, final String arguments, final Locale locale) {
99 return !"short".equals(arguments) ? null
100 : locale == null ? DateFormat
101 .getDateInstance(DateFormat.DEFAULT) : DateFormat
102 .getDateInstance(DateFormat.DEFAULT, locale);
103 }
104 }
105
106
107
108
109 private static final class UpperCaseFormat extends Format {
110 private static final long serialVersionUID = 1L;
111
112 @Override
113 public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
114 return toAppendTo.append(((String) obj).toUpperCase(Locale.ROOT));
115 }
116
117 @Override
118 public Object parseObject(final String source, final ParsePosition pos) {
119 throw new UnsupportedOperationException();
120 }
121 }
122
123
124
125
126 private static final class UpperCaseFormatFactory implements FormatFactory {
127 private static final Format UPPER_INSTANCE = new UpperCaseFormat();
128
129 @Override
130 public Format getFormat(final String name, final String arguments, final Locale locale) {
131 return UPPER_INSTANCE;
132 }
133 }
134
135 static ArgumentSets testBuiltInChoiceFormatFactory() {
136
137 return ArgumentSets
138 .argumentsForFirstParameter(Integer.valueOf(1), Double.valueOf("2.2"), Double.valueOf("1234.5"))
139 .argumentsForNextParameter(DateFormat.getAvailableLocales());
140
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 static ArgumentSets testBuiltInDateTimeFormatFactory() {
199
200 return ArgumentSets
201 .argumentsForFirstParameter(
202 "1: {0,date,short}",
203 "2: {0,date,medium}",
204 "3: {0,date,long}",
205 "4: {0,date,full}",
206 "5: {0,date,d MMM yy}",
207 "6: {0,time,short}",
208 "7: {0,time,medium}",
209 "8: {0,time,long}",
210 "9: {0,time,full}",
211 "10: {0,time,HH:mm}",
212 "11: {0,date}", "12: {0,time}")
213 .argumentsForNextParameter(DateFormat.getAvailableLocales());
214
215 }
216
217 static ArgumentSets testBuiltInNumberFormatFactory() {
218
219 return ArgumentSets
220 .argumentsForFirstParameter(
221 "1: {0,number}",
222 "2: {0,number,integer}",
223 "3: {0,number,currency}",
224 "4: {0,number,percent}",
225 "5: {0,number,00000.000}")
226 .argumentsForNextParameter(DateFormat.getAvailableLocales());
227
228 }
229
230 static ArgumentSets testOverriddenBuiltinFormatFactory() {
231
232 return ArgumentSets
233 .argumentsForFirstParameter(
234 "1: {0,date}",
235 "2: {0,date,medium}",
236 "3: {0,date,long}",
237 "4: {0,date,full}",
238 "5: {0,date,d MMM yy}")
239 .argumentsForNextParameter(DateFormat.getAvailableLocales());
240
241 }
242
243 private final Map<String, FormatFactory> registry = new HashMap<>();
244
245
246
247
248
249
250
251
252
253 private void checkBuiltInFormat(final String pattern, final Map<String, ?> registryUnused, final Object[] args, final Locale locale) {
254 final StringBuilder buffer = new StringBuilder();
255 buffer.append("Pattern=[");
256 buffer.append(pattern);
257 buffer.append("], locale=[");
258 buffer.append(locale);
259 buffer.append("]");
260 final MessageFormat mf = createMessageFormat(pattern, locale);
261 final ExtendedMessageFormat emf;
262 if (locale == null) {
263 emf = new ExtendedMessageFormat(pattern);
264 } else {
265 emf = new ExtendedMessageFormat(pattern, locale);
266 }
267 assertEquals(mf.format(args), emf.format(args), "format " + buffer);
268 assertEquals(mf.toPattern(), emf.toPattern(), "toPattern " + buffer);
269 }
270
271
272
273
274
275
276
277 private MessageFormat createMessageFormat(final String pattern, final Locale locale) {
278 final MessageFormat result = new MessageFormat(pattern);
279 if (locale != null) {
280 result.setLocale(locale);
281 result.applyPattern(pattern);
282 }
283 return result;
284 }
285
286 @BeforeEach
287 public void setUp() {
288 registry.put("lower", new LowerCaseFormatFactory());
289 registry.put("upper", new UpperCaseFormatFactory());
290 }
291
292 @Test
293 void testArgumentIndexTrailingWhitespaceAtEnd() {
294 assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendedMessageFormat("{0 ", new HashMap<>()));
295 }
296
297
298
299
300 @CartesianTest
301 @CartesianTest.MethodFactory("testBuiltInChoiceFormatFactory")
302 void testBuiltInChoiceFormat(final Object value, final Locale locale) {
303 String choicePattern;
304 choicePattern = "{0,choice,1#One|2#Two|3#Many {0,number}}";
305 checkBuiltInFormat(value + ": " + choicePattern, null, new Object[] { value }, (Locale) null);
306 checkBuiltInFormat(value + ": " + choicePattern, null, new Object[] { value }, locale);
307
308 choicePattern = "{0,choice,1#''One''|2#\"Two\"|3#''{Many}'' {0,number}}";
309 checkBuiltInFormat(value + ": " + choicePattern, null, new Object[] { value }, (Locale) null);
310 checkBuiltInFormat(value + ": " + choicePattern, null, new Object[] { value }, locale);
311 }
312
313
314
315
316 @CartesianTest
317 @CartesianTest.MethodFactory("testBuiltInDateTimeFormatFactory")
318 void testBuiltInDateTimeFormat(final String pattern, final Locale locale) {
319 final Calendar cal = Calendar.getInstance();
320 cal.set(2007, Calendar.JANUARY, 23, 18, 33, 5);
321 final Object[] args = { cal.getTime() };
322 checkBuiltInFormat(pattern, null, args, (Locale) null);
323 checkBuiltInFormat(pattern, null, args, locale);
324 }
325
326
327
328
329 @CartesianTest
330 @CartesianTest.MethodFactory("testBuiltInNumberFormatFactory")
331 void testBuiltInNumberFormat(final String pattern, final Locale locale) {
332 final Object[] args = { Double.valueOf("6543.21") };
333 checkBuiltInFormat(pattern, null, args, (Locale) null);
334 checkBuiltInFormat(pattern, null, args, locale);
335 }
336
337
338
339
340 @Test
341 void testEmbeddedPatternInChoice() {
342 final String pattern = "Hi {0,lower}, got {1,choice,0#none|1#one|1<{1,number}}, {2,upper}!";
343 final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
344 assertEquals(emf.format(new Object[] {"there", 3, "great"}), "Hi there, got 3, GREAT!");
345 }
346
347
348
349
350 @Test
351 void testEqualsHashcode() {
352 final Map<String, ? extends FormatFactory> fmtRegistry = Collections.singletonMap("testfmt", new LowerCaseFormatFactory());
353 final Map<String, ? extends FormatFactory> otherRegistry = Collections.singletonMap("testfmt", new UpperCaseFormatFactory());
354
355 final String pattern = "Pattern: {0,testfmt}";
356 final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry);
357
358 ExtendedMessageFormat other;
359
360
361 assertEquals(emf, emf, "same, equals()");
362 assertEquals(emf.hashCode(), emf.hashCode(), "same, hashCode()");
363
364
365 other = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry);
366 assertEquals(emf, other, "equal, equals()");
367 assertEquals(emf.hashCode(), other.hashCode(), "equal, hashCode()");
368
369
370 other = new OtherExtendedMessageFormat(pattern, Locale.US, fmtRegistry);
371 assertNotEquals(emf, other, "class, equals()");
372 assertEquals(emf.hashCode(), other.hashCode(), "class, hashCode()");
373
374
375 other = new ExtendedMessageFormat("X" + pattern, Locale.US, fmtRegistry);
376 assertNotEquals(emf, other, "pattern, equals()");
377 assertNotEquals(emf.hashCode(), other.hashCode(), "pattern, hashCode()");
378
379
380 other = new ExtendedMessageFormat(pattern, Locale.US, otherRegistry);
381 assertNotEquals(emf, other, "registry, equals()");
382 assertNotEquals(emf.hashCode(), other.hashCode(), "registry, hashCode()");
383
384
385 other = new ExtendedMessageFormat(pattern, Locale.FRANCE, fmtRegistry);
386 assertNotEquals(emf, other, "locale, equals()");
387 assertEquals(emf.hashCode(), other.hashCode(), "locale, hashCode()");
388 }
389
390
391
392
393 @Test
394 void testEscapedBraces_LANG_948() {
395
396 final String pattern = "Message without placeholders '{}'";
397 final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
398 assertEquals("Message without placeholders {}", emf.format(new Object[] {"DUMMY"}));
399
400
401 final String pattern2 = "Message with placeholder ''{0}''";
402 final ExtendedMessageFormat emf2 = new ExtendedMessageFormat(pattern2, registry);
403 assertEquals("Message with placeholder 'DUMMY'", emf2.format(new Object[] {"DUMMY"}));
404 }
405
406
407
408
409 @Test
410 void testEscapedQuote_LANG_477() {
411 final String pattern = "it''s a {0,lower} 'test'!";
412 final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
413 assertEquals("it's a dummy test!", emf.format(new Object[] {"DUMMY"}));
414 }
415
416
417
418
419 @Test
420 void testExtendedAndBuiltInFormatsWithAvailableLocales() {
421 final String extendedPattern = "Name: {0,upper} ";
422 final String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}";
423 final String pattern = extendedPattern + builtinsPattern;
424
425 final Calendar cal = Calendar.getInstance();
426 cal.set(2007, Calendar.JANUARY, 23, 18, 33, 5);
427 final Object[] args = {"John Doe", cal.getTime(), Double.valueOf("12345.67")};
428
429 final HashSet<Locale> testLocales = new HashSet<>(Arrays.asList(DateFormat.getAvailableLocales()));
430 testLocales.retainAll(Arrays.asList(NumberFormat.getAvailableLocales()));
431
432 for (final Locale locale : testLocales) {
433 final MessageFormat builtins = createMessageFormat(builtinsPattern, locale);
434 final String expectedPattern = extendedPattern + builtins.toPattern();
435 final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, locale, registry);
436 assertEquals(expectedPattern, emf.toPattern(), "pattern comparison for locale " + locale);
437
438 final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
439 final NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
440 final StringBuilder expected = new StringBuilder();
441 expected.append("Name: ");
442 expected.append(args[0].toString().toUpperCase(locale));
443 expected.append(" DOB: ");
444 expected.append(df.format(args[1]));
445 expected.append(" Salary: ");
446 expected.append(nf.format(args[2]));
447 assertEquals(expected.toString(), emf.format(args), String.valueOf(locale));
448 }
449 }
450
451
452
453
454 @Test
455 void testExtendedAndBuiltInFormatsWithDefaultLocale() {
456 final String extendedPattern = "Name: {0,upper} ";
457 final String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}";
458 final String pattern = extendedPattern + builtinsPattern;
459
460 final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
461 final MessageFormat builtins = createMessageFormat(builtinsPattern, null);
462 final String expectedPattern = extendedPattern + builtins.toPattern();
463 assertEquals(expectedPattern, emf.toPattern(), "pattern comparison for default locale");
464
465 final Calendar cal = Calendar.getInstance();
466 cal.set(2007, Calendar.JANUARY, 23, 18, 33, 5);
467 final Object[] args = {"John Doe", cal.getTime(), Double.valueOf("12345.67")};
468 final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
469 final NumberFormat nf = NumberFormat.getCurrencyInstance();
470 final StringBuilder expected = new StringBuilder();
471 expected.append("Name: ");
472 expected.append(args[0].toString().toUpperCase());
473 expected.append(" DOB: ");
474 expected.append(df.format(args[1]));
475 expected.append(" Salary: ");
476 expected.append(nf.format(args[2]));
477 assertEquals(expected.toString(), emf.format(args));
478 }
479
480
481
482
483 @Test
484 void testExtendedFormats() {
485 final String pattern = "Lower: {0,lower} Upper: {1,upper}";
486 final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
487 assertEquals(pattern, emf.toPattern(), "TOPATTERN");
488 assertEquals(emf.format(new Object[] {"foo", "bar"}), "Lower: foo Upper: BAR");
489 assertEquals(emf.format(new Object[] {"Foo", "Bar"}), "Lower: foo Upper: BAR");
490 assertEquals(emf.format(new Object[] {"FOO", "BAR"}), "Lower: foo Upper: BAR");
491 assertEquals(emf.format(new Object[] {"FOO", "bar"}), "Lower: foo Upper: BAR");
492 assertEquals(emf.format(new Object[] {"foo", "BAR"}), "Lower: foo Upper: BAR");
493 }
494
495 @CartesianTest
496 @CartesianTest.MethodFactory("testOverriddenBuiltinFormatFactory")
497 void testOverriddenBuiltinFormat(final String pattern, final Locale locale) {
498 final Calendar cal = Calendar.getInstance();
499 cal.set(2007, Calendar.JANUARY, 23);
500 final Object[] args = { cal.getTime() };
501
502 checkBuiltInFormat(pattern, null, args, (Locale) null);
503 checkBuiltInFormat(pattern, null, args, locale);
504 }
505
506 @ParameterizedTest
507 @MethodSource("java.text.DateFormat#getAvailableLocales")
508 void testOverriddenBuiltinFormat2(final Locale locale) {
509 final Calendar cal = Calendar.getInstance();
510 cal.set(2007, Calendar.JANUARY, 23);
511 final Object[] args = { cal.getTime() };
512 final Map<String, ? extends FormatFactory> dateRegistry = Collections.singletonMap("date", new OverrideShortDateFormatFactory());
513
514 final MessageFormat dateDefault = createMessageFormat("{0,date}", locale);
515 final String pattern = "{0,date,short}";
516 final ExtendedMessageFormat dateShort = new ExtendedMessageFormat(pattern, locale, dateRegistry);
517 assertEquals(dateDefault.format(args), dateShort.format(args), "overridden date,short format");
518 assertEquals(pattern, dateShort.toPattern(), "overridden date,short pattern");
519 }
520
521
522
523
524
525
526 @Test
527 void testQuotedLiteralAtEndOfFormatElement() {
528 assertEquals("ABC", new ExtendedMessageFormat("{0,upper,'x'}", registry).format(new Object[] {"abc"}));
529 assertEquals("ABC", new ExtendedMessageFormat("{0,upper,'}'}", registry).format(new Object[] {"abc"}));
530
531 assertEquals("5%", new ExtendedMessageFormat("{0,number,0'%'}", registry).format(new Object[] {Integer.valueOf(5)}));
532 }
533
534 @Test
535 void testTruncatedFormatElementWithRegistry() {
536
537 assertThrowsExactly(IllegalArgumentException.class, () -> new MessageFormat("{"));
538 assertThrowsExactly(IllegalArgumentException.class, () -> new MessageFormat("{0,"));
539 assertThrowsExactly(IllegalArgumentException.class, () -> new MessageFormat("{0}extra{"));
540
541
542
543
544 assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendedMessageFormat("{", new HashMap<>()));
545 assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendedMessageFormat("{0,", new HashMap<>()));
546 assertThrowsExactly(IllegalArgumentException.class, () -> new ExtendedMessageFormat("{0}extra{", new HashMap<>()));
547 }
548
549 }