View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.text;
18  
19  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
20  import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.text.DateFormat;
26  import java.text.FieldPosition;
27  import java.text.Format;
28  import java.text.MessageFormat;
29  import java.text.NumberFormat;
30  import java.text.ParsePosition;
31  import java.util.Arrays;
32  import java.util.Calendar;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.Locale;
37  import java.util.Map;
38  
39  import org.junit.jupiter.api.BeforeEach;
40  import org.junit.jupiter.api.Test;
41  
42  /**
43   * Test case for {@link ExtendedMessageFormat}.
44   */
45  public class ExtendedMessageFormatTest {
46  
47      private final Map<String, FormatFactory> registry = new HashMap<>();
48  
49      @BeforeEach
50      public void setUp() {
51          registry.put("lower", new LowerCaseFormatFactory());
52          registry.put("upper", new UpperCaseFormatFactory());
53      }
54  
55      /**
56       * Test extended formats.
57       */
58      @Test
59      public void testExtendedFormats() {
60          final String pattern = "Lower: {0,lower} Upper: {1,upper}";
61          final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
62          assertEquals(pattern, emf.toPattern(), "TOPATTERN");
63          assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "bar"}));
64          assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"Foo", "Bar"}));
65          assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "BAR"}));
66          assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "bar"}));
67          assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "BAR"}));
68      }
69  
70      /**
71       * Test Bug LANG-477 - out of memory error with escaped quote
72       */
73      @Test
74      public void testEscapedQuote_LANG_477() {
75          final String pattern = "it''s a {0,lower} 'test'!";
76          final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
77          assertEquals("it's a dummy test!", emf.format(new Object[] {"DUMMY"}));
78      }
79  
80      /**
81       * Test Bug LANG-917 - IndexOutOfBoundsException and/or infinite loop when using a choice pattern
82       */
83      @Test
84      public void testEmbeddedPatternInChoice() {
85          final String pattern = "Hi {0,lower}, got {1,choice,0#none|1#one|1<{1,number}}, {2,upper}!";
86          final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
87          assertEquals(emf.format(new Object[] {"there", 3, "great"}), "Hi there, got 3, GREAT!");
88      }
89  
90      /**
91       * Test Bug LANG-948 - Exception while using ExtendedMessageFormat and escaping braces
92       */
93      @Test
94      public void testEscapedBraces_LANG_948() {
95          // message without placeholder because braces are escaped by quotes
96          final String pattern = "Message without placeholders '{}'";
97          final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
98          assertEquals("Message without placeholders {}", emf.format(new Object[] {"DUMMY"}));
99  
100         // message with placeholder because quotes are escaped by quotes
101         final String pattern2 = "Message with placeholder ''{0}''";
102         final ExtendedMessageFormat emf2 = new ExtendedMessageFormat(pattern2, registry);
103         assertEquals("Message with placeholder 'DUMMY'", emf2.format(new Object[] {"DUMMY"}));
104     }
105 
106     /**
107      * Test Bug TEXT-106 - Exception while using ExtendedMessageFormat and choice format element with quote just
108      * before brace end
109      */
110     @Test
111     public void testChoiceQuoteJustBeforeBraceEnd_TEXT_106() {
112         final String pattern2 = "Choice format element with quote just before brace end ''{0,choice,0#0|0<'1'}''";
113         final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern2, registry);
114         assertEquals("Choice format element with quote just before brace end '0'", emf.format(new Object[] {0}));
115         assertEquals("Choice format element with quote just before brace end '1'", emf.format(new Object[] {1}));
116     }
117 
118     /**
119      * Test extended and built in formats.
120      */
121     @Test
122     public void testExtendedAndBuiltInFormats() {
123         final Calendar cal = Calendar.getInstance();
124         cal.set(2007, Calendar.JANUARY, 23, 18, 33, 5);
125         final Object[] args = new Object[] {"John Doe", cal.getTime(), Double.valueOf("12345.67")};
126         final String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}";
127         final String extendedPattern = "Name: {0,upper} ";
128         final String pattern = extendedPattern + builtinsPattern;
129 
130         final HashSet<Locale> testLocales = new HashSet<>();
131         testLocales.addAll(Arrays.asList(DateFormat.getAvailableLocales()));
132         testLocales.retainAll(Arrays.asList(NumberFormat.getAvailableLocales()));
133         testLocales.add(null);
134 
135         for (final Locale locale : testLocales) {
136             final MessageFormat builtins = createMessageFormat(builtinsPattern, locale);
137             final String expectedPattern = extendedPattern + builtins.toPattern();
138             DateFormat df = null;
139             NumberFormat nf = null;
140             ExtendedMessageFormat emf = null;
141             if (locale == null) {
142                 df = DateFormat.getDateInstance(DateFormat.SHORT);
143                 nf = NumberFormat.getCurrencyInstance();
144                 emf = new ExtendedMessageFormat(pattern, registry);
145             } else {
146                 df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
147                 nf = NumberFormat.getCurrencyInstance(locale);
148                 emf = new ExtendedMessageFormat(pattern, locale, registry);
149             }
150             final StringBuilder expected = new StringBuilder();
151             expected.append("Name: ");
152             expected.append(args[0].toString().toUpperCase(Locale.ROOT));
153             expected.append(" DOB: ");
154             expected.append(df.format(args[1]));
155             expected.append(" Salary: ");
156             expected.append(nf.format(args[2]));
157             assertEquals(expectedPattern, emf.toPattern(), "pattern comparison for locale " + locale);
158             assertEquals(expected.toString(), emf.format(args), String.valueOf(locale));
159         }
160     }
161 
162 //    /**
163 //     * Test extended formats with choice format.
164 //     *
165 //     * NOTE: FAILING - currently sub-formats not supported
166 //     */
167 //    public void testExtendedWithChoiceFormat() {
168 //        String pattern = "Choice: {0,choice,1.0#{1,lower}|2.0#{1,upper}}";
169 //        ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry);
170 //        assertPatterns(null, pattern, emf.toPattern());
171 //        try {
172 //            assertEquals("one", emf.format(new Object[] {Integer.valueOf(1), "ONE"}));
173 //            assertEquals("TWO", emf.format(new Object[] {Integer.valueOf(2), "two"}));
174 //        } catch (IllegalArgumentException e) {
175 //            // currently sub-formats not supported
176 //        }
177 //    }
178 
179 //    /**
180 //     * Test mixed extended and built-in formats with choice format.
181 //     *
182 //     * NOTE: FAILING - currently sub-formats not supported
183 //     */
184 //    public void testExtendedAndBuiltInWithChoiceFormat() {
185 //        String pattern = "Choice: {0,choice,1.0#{0} {1,lower} {2,number}|2.0#{0} {1,upper} {2,number,currency}}";
186 //        Object[] lowArgs  = new Object[] {Integer.valueOf(1), "Low",  Double.valueOf("1234.56")};
187 //        Object[] highArgs = new Object[] {Integer.valueOf(2), "High", Double.valueOf("9876.54")};
188 //        Locale[] availableLocales = ChoiceFormat.getAvailableLocales();
189 //        Locale[] testLocales = new Locale[availableLocales.length + 1];
190 //        testLocales[0] = null;
191 //        System.arraycopy(availableLocales, 0, testLocales, 1, availableLocales.length);
192 //        for (int i = 0; i < testLocales.length; i++) {
193 //            NumberFormat nf = null;
194 //            NumberFormat cf = null;
195 //            ExtendedMessageFormat emf = null;
196 //            if (testLocales[i] == null) {
197 //                nf = NumberFormat.getNumberInstance();
198 //                cf = NumberFormat.getCurrencyInstance();
199 //                emf = new ExtendedMessageFormat(pattern, registry);
200 //            } else {
201 //                nf = NumberFormat.getNumberInstance(testLocales[i]);
202 //                cf = NumberFormat.getCurrencyInstance(testLocales[i]);
203 //                emf = new ExtendedMessageFormat(pattern, testLocales[i], registry);
204 //            }
205 //            assertPatterns(null, pattern, emf.toPattern());
206 //            try {
207 //                String lowExpected = lowArgs[0] + " low "    + nf.format(lowArgs[2]);
208 //                String highExpected = highArgs[0] + " HIGH "  + cf.format(highArgs[2]);
209 //                assertEquals(lowExpected,  emf.format(lowArgs));
210 //                assertEquals(highExpected, emf.format(highArgs));
211 //            } catch (IllegalArgumentException e) {
212 //                // currently sub-formats not supported
213 //            }
214 //        }
215 //    }
216 
217     /**
218      * Test the built in choice format.
219      */
220     @Test
221     public void testBuiltInChoiceFormat() {
222         final Object[] values = new Number[] {1, Double.valueOf("2.2"), Double.valueOf("1234.5")};
223         String choicePattern = null;
224         final Locale[] availableLocales = NumberFormat.getAvailableLocales();
225 
226         choicePattern = "{0,choice,1#One|2#Two|3#Many {0,number}}";
227         for (final Object value : values) {
228             checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales);
229         }
230 
231         choicePattern = "{0,choice,1#''One''|2#\"Two\"|3#''{Many}'' {0,number}}";
232         for (final Object value : values) {
233             checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales);
234         }
235     }
236 
237     /**
238      * Test the built in date/time formats
239      */
240     @Test
241     public void testBuiltInDateTimeFormat() {
242         final Calendar cal = Calendar.getInstance();
243         cal.set(2007, Calendar.JANUARY, 23, 18, 33, 5);
244         final Object[] args = new Object[] {cal.getTime()};
245         final Locale[] availableLocales = DateFormat.getAvailableLocales();
246 
247         checkBuiltInFormat("1: {0,date,short}",    args, availableLocales);
248         checkBuiltInFormat("2: {0,date,medium}",   args, availableLocales);
249         checkBuiltInFormat("3: {0,date,long}",     args, availableLocales);
250         checkBuiltInFormat("4: {0,date,full}",     args, availableLocales);
251         checkBuiltInFormat("5: {0,date,d MMM yy}", args, availableLocales);
252         checkBuiltInFormat("6: {0,time,short}",    args, availableLocales);
253         checkBuiltInFormat("7: {0,time,medium}",   args, availableLocales);
254         checkBuiltInFormat("8: {0,time,long}",     args, availableLocales);
255         checkBuiltInFormat("9: {0,time,full}",     args, availableLocales);
256         checkBuiltInFormat("10: {0,time,HH:mm}",   args, availableLocales);
257         checkBuiltInFormat("11: {0,date}",         args, availableLocales);
258         checkBuiltInFormat("12: {0,time}",         args, availableLocales);
259     }
260 
261     @Test
262     public void testOverriddenBuiltinFormat() {
263         final Calendar cal = Calendar.getInstance();
264         cal.set(2007, Calendar.JANUARY, 23);
265         final Object[] args = new Object[] {cal.getTime()};
266         final Locale[] availableLocales = DateFormat.getAvailableLocales();
267         final Map<String, ? extends FormatFactory> dateRegistry =
268                 Collections.singletonMap("date", new OverrideShortDateFormatFactory());
269 
270         //check the non-overridden builtins:
271         checkBuiltInFormat("1: {0,date}", dateRegistry,          args, availableLocales);
272         checkBuiltInFormat("2: {0,date,medium}", dateRegistry,   args, availableLocales);
273         checkBuiltInFormat("3: {0,date,long}", dateRegistry,     args, availableLocales);
274         checkBuiltInFormat("4: {0,date,full}", dateRegistry,     args, availableLocales);
275         checkBuiltInFormat("5: {0,date,d MMM yy}", dateRegistry, args, availableLocales);
276 
277         //check the overridden format:
278         for (int i = -1; i < availableLocales.length; i++) {
279             final Locale locale = i < 0 ? null : availableLocales[i];
280             final MessageFormat dateDefault = createMessageFormat("{0,date}", locale);
281             final String pattern = "{0,date,short}";
282             final ExtendedMessageFormat dateShort = new ExtendedMessageFormat(pattern, locale, dateRegistry);
283             assertEquals(dateDefault.format(args), dateShort.format(args), "overridden date,short format");
284             assertEquals(pattern, dateShort.toPattern(), "overridden date,short pattern");
285         }
286     }
287 
288     /**
289      * Test the built in number formats.
290      */
291     @Test
292     public void testBuiltInNumberFormat() {
293         final Object[] args = new Object[] {Double.valueOf("6543.21")};
294         final Locale[] availableLocales = NumberFormat.getAvailableLocales();
295         checkBuiltInFormat("1: {0,number}",           args, availableLocales);
296         checkBuiltInFormat("2: {0,number,integer}",   args, availableLocales);
297         checkBuiltInFormat("3: {0,number,currency}",  args, availableLocales);
298         checkBuiltInFormat("4: {0,number,percent}",   args, availableLocales);
299         checkBuiltInFormat("5: {0,number,00000.000}", args, availableLocales);
300     }
301 
302     /**
303      * Test equals() and hashcode.
304      */
305     @Test
306     public void testEqualsHashcode() {
307         final Map<String, ? extends FormatFactory> fmtRegistry =
308                 Collections.singletonMap("testfmt", new LowerCaseFormatFactory());
309         final Map<String, ? extends FormatFactory> otherRegitry =
310                 Collections.singletonMap("testfmt", new UpperCaseFormatFactory());
311 
312         final String pattern = "Pattern: {0,testfmt}";
313         final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry);
314 
315         ExtendedMessageFormat other = null;
316 
317         // Same object
318         assertTrue(emf.equals(emf), "same, equals()");
319         assertEquals(emf.hashCode(), emf.hashCode(), "same, hashcode()");
320 
321         assertFalse(emf.equals(null), "null, equals");
322 
323         // Equal Object
324         other = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry);
325         assertTrue(emf.equals(other), "equal, equals()");
326         assertTrue(emf.hashCode() == other.hashCode(), "equal, hashcode()");
327 
328         // Different Class
329         other = new OtherExtendedMessageFormat(pattern, Locale.US, fmtRegistry);
330         assertFalse(emf.equals(other), "class, equals()");
331         assertTrue(emf.hashCode() == other.hashCode(), "class, hashcode()"); // same hashcode
332 
333         // Different pattern
334         other = new ExtendedMessageFormat("X" + pattern, Locale.US, fmtRegistry);
335         assertFalse(emf.equals(other), "pattern, equals()");
336         assertFalse(emf.hashCode() == other.hashCode(), "pattern, hashcode()");
337 
338         // Different registry
339         other = new ExtendedMessageFormat(pattern, Locale.US, otherRegitry);
340         assertFalse(emf.equals(other), "registry, equals()");
341         assertFalse(emf.hashCode() == other.hashCode(), "registry, hashcode()");
342 
343         // Different Locale
344         other = new ExtendedMessageFormat(pattern, Locale.FRANCE, fmtRegistry);
345         assertFalse(emf.equals(other), "locale, equals()");
346         assertTrue(emf.hashCode() == other.hashCode(), "locale, hashcode()"); // same hashcode
347     }
348 
349     /**
350      * Test a built in format for the specified Locales, plus <code>null</code> Locale.
351      * @param pattern MessageFormat pattern
352      * @param args MessageFormat arguments
353      * @param locales to test
354      */
355     private void checkBuiltInFormat(final String pattern, final Object[] args, final Locale[] locales) {
356         checkBuiltInFormat(pattern, null, args, locales);
357     }
358 
359     /**
360      * Test a built in format for the specified Locales, plus <code>null</code> Locale.
361      * @param pattern MessageFormat pattern
362      * @param fmtRegistry FormatFactory registry to use
363      * @param args MessageFormat arguments
364      * @param locales to test
365      */
366     private void checkBuiltInFormat(final String pattern, final Map<String, ?> fmtRegistry, final Object[] args,
367             final Locale[] locales) {
368         checkBuiltInFormat(pattern, fmtRegistry, args, (Locale) null);
369         for (final Locale locale : locales) {
370             checkBuiltInFormat(pattern, fmtRegistry, args, locale);
371         }
372     }
373 
374     /**
375      * Create an ExtendedMessageFormat for the specified pattern and locale and check the
376      * formated output matches the expected result for the parameters.
377      * @param pattern string
378      * @param registryUnused map (currently unused)
379      * @param args Object[]
380      * @param locale Locale
381      */
382     private void checkBuiltInFormat(final String pattern, final Map<String, ?> registryUnused, final Object[] args,
383             final Locale locale) {
384         final StringBuilder buffer = new StringBuilder();
385         buffer.append("Pattern=[");
386         buffer.append(pattern);
387         buffer.append("], locale=[");
388         buffer.append(locale);
389         buffer.append("]");
390         final MessageFormat mf = createMessageFormat(pattern, locale);
391         // System.out.println(buffer + ", result=[" + mf.format(args) +"]");
392         ExtendedMessageFormat emf = null;
393         if (locale == null) {
394             emf = new ExtendedMessageFormat(pattern);
395         } else {
396             emf = new ExtendedMessageFormat(pattern, locale);
397         }
398         assertEquals(mf.format(args), emf.format(args), "format "    + buffer.toString());
399         assertEquals(mf.toPattern(), emf.toPattern(), "toPattern " + buffer.toString());
400     }
401 
402     /**
403      * Replace MessageFormat(String, Locale) constructor (not available until JDK 1.4).
404      * @param pattern string
405      * @param locale Locale
406      * @return MessageFormat
407      */
408     private MessageFormat createMessageFormat(final String pattern, final Locale locale) {
409         final MessageFormat result = new MessageFormat(pattern);
410         if (locale != null) {
411             result.setLocale(locale);
412             result.applyPattern(pattern);
413         }
414         return result;
415     }
416 
417     @Test
418     public void testSetFormatIsUnsupported() {
419         assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> {
420             final ExtendedMessageFormat emf = new ExtendedMessageFormat("");
421             emf.setFormat(0, new LowerCaseFormat());
422         });
423     }
424 
425     @Test
426     public void testSetFormatByArgumentIndexIsUnsupported() {
427         assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> {
428             final ExtendedMessageFormat emf = new ExtendedMessageFormat("");
429             emf.setFormatByArgumentIndex(0, new LowerCaseFormat());
430         });
431     }
432 
433     @Test
434     public void testSetFormatsIsUnsupported() {
435         assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> {
436             final ExtendedMessageFormat emf = new ExtendedMessageFormat("");
437             emf.setFormats(new Format[] {new LowerCaseFormat(), new UpperCaseFormat()});
438         });
439     }
440 
441     @Test
442     public void testSetFormatsByArgumentIndex() {
443         assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> {
444             final ExtendedMessageFormat emf = new ExtendedMessageFormat("");
445             emf.setFormatsByArgumentIndex(new Format[] {new LowerCaseFormat(), new UpperCaseFormat()});
446         });
447     }
448 
449     @Test
450     public void testFailsToCreateExtendedMessageFormatTakingTwoArgumentsThrowsIllegalArgumentExceptionOne() {
451         assertThatIllegalArgumentException().isThrownBy(() -> {
452             new ExtendedMessageFormat("agdXdkR;T1{9 ^,LzXf?", new HashMap<String, FormatFactory>());
453         });
454     }
455 
456     @Test
457     public void testFailsToCreateExtendedMessageFormatTakingTwoArgumentsThrowsIllegalArgumentExceptionTwo() {
458         assertThatIllegalArgumentException().isThrownBy(() -> {
459             new ExtendedMessageFormat("a5XdkR;T1{9 ,LzXf?", new HashMap<String, FormatFactory>());
460         });
461     }
462 
463     @Test
464     public void testFailsToCreateExtendedMessageFormatTakingTwoArgumentsThrowsIllegalArgumentExceptionThree() {
465         assertThatIllegalArgumentException().isThrownBy(() -> {
466             new ExtendedMessageFormat("9jLh_D9{ ", new HashMap<String, FormatFactory>());
467         });
468     }
469 
470     @Test
471     public void testFailsToCreateExtendedMessageFormatTakingTwoArgumentsThrowsIllegalArgumentExceptionFour() {
472         assertThatIllegalArgumentException().isThrownBy(() -> {
473             new ExtendedMessageFormat("RD,nXhM{}{", new HashMap<String, FormatFactory>());
474         });
475     }
476 
477     @Test
478     public void testFailsToCreateExtendedMessageFormatTakingTwoArgumentsThrowsIllegalArgumentExceptionFive() {
479         assertThatIllegalArgumentException().isThrownBy(() -> {
480             new ExtendedMessageFormat("j/[_D9{0,\"&'+0o", new HashMap<String, FormatFactory>());
481         });
482     }
483 
484     @Test
485     public void testCreatesExtendedMessageFormatTakingString() {
486         final ExtendedMessageFormat extendedMessageFormat =
487                 new ExtendedMessageFormat("Unterminated format element at position ");
488         final Map<String, FormatFactory> map = new HashMap<>();
489         final ExtendedMessageFormat extendedMessageFormatTwo =
490                 new ExtendedMessageFormat("Unterminated format element at position ", map);
491 
492         assertEquals("Unterminated format element at position ", extendedMessageFormatTwo.toPattern());
493         assertFalse(extendedMessageFormat.equals(extendedMessageFormatTwo));
494     }
495 
496     // ------------------------ Test Formats ------------------------
497 
498     /**
499      * {@link Format} implementation which converts to lower case.
500      */
501     private static class LowerCaseFormat extends Format {
502         private static final long serialVersionUID = 1L;
503 
504         @Override
505         public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
506             return toAppendTo.append(((String) obj).toLowerCase(Locale.ROOT));
507         }
508 
509         @Override
510         public Object parseObject(final String source, final ParsePosition pos) {
511             throw new UnsupportedOperationException();
512         }
513     }
514 
515     /**
516      * {@link Format} implementation which converts to upper case.
517      */
518     private static class UpperCaseFormat extends Format {
519         private static final long serialVersionUID = 1L;
520 
521         @Override
522         public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
523             return toAppendTo.append(((String) obj).toUpperCase(Locale.ROOT));
524         }
525 
526         @Override
527         public Object parseObject(final String source, final ParsePosition pos) {
528             throw new UnsupportedOperationException();
529         }
530     }
531 
532 
533     // ------------------------ Test Format Factories ---------------
534     /**
535      * {@link FormatFactory} implementation for lower case format.
536      */
537     private static class LowerCaseFormatFactory implements FormatFactory {
538         private static final Format LOWER_INSTANCE = new LowerCaseFormat();
539         @Override
540         public Format getFormat(final String name, final String arguments, final Locale locale) {
541             return LOWER_INSTANCE;
542         }
543     }
544     /**
545      * {@link FormatFactory} implementation for upper case format.
546      */
547     private static class UpperCaseFormatFactory implements FormatFactory {
548         private static final Format UPPER_INSTANCE = new UpperCaseFormat();
549         @Override
550         public Format getFormat(final String name, final String arguments, final Locale locale) {
551             return UPPER_INSTANCE;
552         }
553     }
554     /**
555      * {@link FormatFactory} implementation to override date format "short" to "default".
556      */
557     private static class OverrideShortDateFormatFactory implements FormatFactory {
558         @Override
559         public Format getFormat(final String name, final String arguments, final Locale locale) {
560             return !"short".equals(arguments) ? null
561                     : locale == null ? DateFormat
562                             .getDateInstance(DateFormat.DEFAULT) : DateFormat
563                             .getDateInstance(DateFormat.DEFAULT, locale);
564         }
565     }
566 
567     /**
568      * Alternative ExtendedMessageFormat impl.
569      */
570     private static class OtherExtendedMessageFormat extends ExtendedMessageFormat {
571         private static final long serialVersionUID = 1L;
572 
573         OtherExtendedMessageFormat(final String pattern, final Locale locale,
574                 final Map<String, ? extends FormatFactory> registry) {
575             super(pattern, locale, registry);
576         }
577     }
578 
579 }