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