1 /*
2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
339 * @see SimpleDateFormat
340 *
341 * @author Mark Davis
342 */
343
344 public class MessageFormat extends Format {
345
346 private static final long serialVersionUID = 6479157306784022952L;
347
348 /**
349 * Constructs a MessageFormat for the default
350 * {@link java.util.Locale.Category#FORMAT FORMAT} locale and the
351 * specified pattern.
352 * The constructor first sets the locale, then parses the pattern and
353 * creates a list of subformats for the format elements contained in it.
354 * Patterns and their interpretation are specified in the
355 * <a href="#patterns">class description</a>.
356 *
357 * @param pattern the pattern for this message format
358 * @exception IllegalArgumentException if the pattern is invalid
359 */
360 public MessageFormat(String pattern) {
361 this.locale = Locale.getDefault(Locale.Category.FORMAT);
362 applyPattern(pattern);
363 }
364
365 /**
366 * Constructs a MessageFormat for the specified locale and
367 * pattern.
368 * The constructor first sets the locale, then parses the pattern and
369 * creates a list of subformats for the format elements contained in it.
370 * Patterns and their interpretation are specified in the
371 * <a href="#patterns">class description</a>.
372 *
373 * @param pattern the pattern for this message format
374 * @param locale the locale for this message format
375 * @exception IllegalArgumentException if the pattern is invalid
376 * @since 1.4
377 */
378 public MessageFormat(String pattern, Locale locale) {
379 this.locale = locale;
380 applyPattern(pattern);
381 }
382
383 /**
384 * Sets the locale to be used when creating or comparing subformats.
385 * This affects subsequent calls
386 * <ul>
387 * <li>to the {@link #applyPattern applyPattern}
388 * and {@link #toPattern toPattern} methods if format elements specify
389 * a format type and therefore have the subformats created in the
390 * <code>applyPattern</code> method, as well as
391 * <li>to the <code>format</code> and
392 * {@link #formatToCharacterIterator formatToCharacterIterator} methods
393 * if format elements do not specify a format type and therefore have
394 * the subformats created in the formatting methods.
395 * </ul>
403
404 /**
405 * Gets the locale that's used when creating or comparing subformats.
406 *
407 * @return the locale used when creating or comparing subformats
408 */
409 public Locale getLocale() {
410 return locale;
411 }
412
413
414 /**
415 * Sets the pattern used by this message format.
416 * The method parses the pattern and creates a list of subformats
417 * for the format elements contained in it.
418 * Patterns and their interpretation are specified in the
419 * <a href="#patterns">class description</a>.
420 *
421 * @param pattern the pattern for this message format
422 * @exception IllegalArgumentException if the pattern is invalid
423 */
424 @SuppressWarnings("fallthrough") // fallthrough in switch is expected, suppress it
425 public void applyPattern(String pattern) {
426 StringBuilder[] segments = new StringBuilder[4];
427 // Allocate only segments[SEG_RAW] here. The rest are
428 // allocated on demand.
429 segments[SEG_RAW] = new StringBuilder();
430
431 int part = SEG_RAW;
432 int formatNumber = 0;
433 boolean inQuote = false;
434 int braceStack = 0;
435 maxOffset = -1;
436 for (int i = 0; i < pattern.length(); ++i) {
437 char ch = pattern.charAt(i);
438 if (part == SEG_RAW) {
439 if (ch == '\'') {
440 if (i + 1 < pattern.length()
441 && pattern.charAt(i+1) == '\'') {
442 segments[part].append(ch); // handle doubles
797 * <td><code>argument</code>
798 * <tr>
799 * <td><code>null</code>
800 * <td><i>any</i>
801 * <td><code>argument.toString()</code>
802 * </table>
803 * <p>
804 * If <code>pos</code> is non-null, and refers to
805 * <code>Field.ARGUMENT</code>, the location of the first formatted
806 * string will be returned.
807 *
808 * @param arguments an array of objects to be formatted and substituted.
809 * @param result where text is appended.
810 * @param pos On input: an alignment field, if desired.
811 * On output: the offsets of the alignment field.
812 * @return the string buffer passed in as {@code result}, with formatted
813 * text appended
814 * @exception IllegalArgumentException if an argument in the
815 * <code>arguments</code> array is not of the type
816 * expected by the format element(s) that use it.
817 */
818 public final StringBuffer format(Object[] arguments, StringBuffer result,
819 FieldPosition pos)
820 {
821 return subformat(arguments, result, pos, null);
822 }
823
824 /**
825 * Creates a MessageFormat with the given pattern and uses it
826 * to format the given arguments. This is equivalent to
827 * <blockquote>
828 * <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
829 * </blockquote>
830 *
831 * @param pattern the pattern string
832 * @param arguments object(s) to format
833 * @return the formatted string
834 * @exception IllegalArgumentException if the pattern is invalid,
835 * or if an argument in the <code>arguments</code> array
836 * is not of the type expected by the format element(s)
837 * that use it.
838 */
839 public static String format(String pattern, Object ... arguments) {
840 MessageFormat temp = new MessageFormat(pattern);
841 return temp.format(arguments);
842 }
843
844 // Overrides
845 /**
846 * Formats an array of objects and appends the <code>MessageFormat</code>'s
847 * pattern, with format elements replaced by the formatted objects, to the
848 * provided <code>StringBuffer</code>.
849 * This is equivalent to
850 * <blockquote>
851 * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>
852 * </blockquote>
853 *
854 * @param arguments an array of objects to be formatted and substituted.
855 * @param result where text is appended.
856 * @param pos On input: an alignment field, if desired.
857 * On output: the offsets of the alignment field.
858 * @exception IllegalArgumentException if an argument in the
859 * <code>arguments</code> array is not of the type
860 * expected by the format element(s) that use it.
861 */
862 public final StringBuffer format(Object arguments, StringBuffer result,
863 FieldPosition pos)
864 {
865 return subformat((Object[]) arguments, result, pos, null);
866 }
867
868 /**
869 * Formats an array of objects and inserts them into the
870 * <code>MessageFormat</code>'s pattern, producing an
871 * <code>AttributedCharacterIterator</code>.
872 * You can use the returned <code>AttributedCharacterIterator</code>
873 * to build the resulting String, as well as to determine information
874 * about the resulting String.
875 * <p>
876 * The text of the returned <code>AttributedCharacterIterator</code> is
877 * the same that would be returned by
878 * <blockquote>
879 * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
880 * </blockquote>
930 * the substituted strings contain {n} references.)
931 * <li>Will not always find a match (or the correct match)
932 * if some part of the parse is ambiguous.
933 * For example, if the pattern "{1},{2}" is used with the
934 * string arguments {"a,b", "c"}, it will format as "a,b,c".
935 * When the result is parsed, it will return {"a", "b,c"}.
936 * <li>If a single argument is parsed more than once in the string,
937 * then the later parse wins.
938 * </ul>
939 * When the parse fails, use ParsePosition.getErrorIndex() to find out
940 * where in the string the parsing failed. The returned error
941 * index is the starting offset of the sub-patterns that the string
942 * is comparing with. For example, if the parsing string "AAA {0} BBB"
943 * is comparing against the pattern "AAD {0} BBB", the error index is
944 * 0. When an error occurs, the call to this method will return null.
945 * If the source is null, return an empty array.
946 *
947 * @param source the string to parse
948 * @param pos the parse position
949 * @return an array of parsed objects
950 */
951 public Object[] parse(String source, ParsePosition pos) {
952 if (source == null) {
953 Object[] empty = {};
954 return empty;
955 }
956
957 int maximumArgumentNumber = -1;
958 for (int i = 0; i <= maxOffset; i++) {
959 if (argumentNumbers[i] > maximumArgumentNumber) {
960 maximumArgumentNumber = argumentNumbers[i];
961 }
962 }
963 Object[] resultArray = new Object[maximumArgumentNumber + 1];
964
965 int patternOffset = 0;
966 int sourceOffset = pos.index;
967 ParsePosition tempStatus = new ParsePosition(0);
968 for (int i = 0; i <= maxOffset; ++i) {
969 // match up to format
|
1 /*
2 * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
339 * @see SimpleDateFormat
340 *
341 * @author Mark Davis
342 */
343
344 public class MessageFormat extends Format {
345
346 private static final long serialVersionUID = 6479157306784022952L;
347
348 /**
349 * Constructs a MessageFormat for the default
350 * {@link java.util.Locale.Category#FORMAT FORMAT} locale and the
351 * specified pattern.
352 * The constructor first sets the locale, then parses the pattern and
353 * creates a list of subformats for the format elements contained in it.
354 * Patterns and their interpretation are specified in the
355 * <a href="#patterns">class description</a>.
356 *
357 * @param pattern the pattern for this message format
358 * @exception IllegalArgumentException if the pattern is invalid
359 * @exception NullPointerException if {@code pattern} is
360 * {@code null}
361 */
362 public MessageFormat(String pattern) {
363 this.locale = Locale.getDefault(Locale.Category.FORMAT);
364 applyPattern(pattern);
365 }
366
367 /**
368 * Constructs a MessageFormat for the specified locale and
369 * pattern.
370 * The constructor first sets the locale, then parses the pattern and
371 * creates a list of subformats for the format elements contained in it.
372 * Patterns and their interpretation are specified in the
373 * <a href="#patterns">class description</a>.
374 *
375 * @param pattern the pattern for this message format
376 * @param locale the locale for this message format
377 * @exception IllegalArgumentException if the pattern is invalid
378 * @exception NullPointerException if {@code pattern} is
379 * {@code null}
380 * @since 1.4
381 */
382 public MessageFormat(String pattern, Locale locale) {
383 this.locale = locale;
384 applyPattern(pattern);
385 }
386
387 /**
388 * Sets the locale to be used when creating or comparing subformats.
389 * This affects subsequent calls
390 * <ul>
391 * <li>to the {@link #applyPattern applyPattern}
392 * and {@link #toPattern toPattern} methods if format elements specify
393 * a format type and therefore have the subformats created in the
394 * <code>applyPattern</code> method, as well as
395 * <li>to the <code>format</code> and
396 * {@link #formatToCharacterIterator formatToCharacterIterator} methods
397 * if format elements do not specify a format type and therefore have
398 * the subformats created in the formatting methods.
399 * </ul>
407
408 /**
409 * Gets the locale that's used when creating or comparing subformats.
410 *
411 * @return the locale used when creating or comparing subformats
412 */
413 public Locale getLocale() {
414 return locale;
415 }
416
417
418 /**
419 * Sets the pattern used by this message format.
420 * The method parses the pattern and creates a list of subformats
421 * for the format elements contained in it.
422 * Patterns and their interpretation are specified in the
423 * <a href="#patterns">class description</a>.
424 *
425 * @param pattern the pattern for this message format
426 * @exception IllegalArgumentException if the pattern is invalid
427 * @exception NullPointerException if {@code pattern} is
428 * {@code null}
429 */
430 @SuppressWarnings("fallthrough") // fallthrough in switch is expected, suppress it
431 public void applyPattern(String pattern) {
432 StringBuilder[] segments = new StringBuilder[4];
433 // Allocate only segments[SEG_RAW] here. The rest are
434 // allocated on demand.
435 segments[SEG_RAW] = new StringBuilder();
436
437 int part = SEG_RAW;
438 int formatNumber = 0;
439 boolean inQuote = false;
440 int braceStack = 0;
441 maxOffset = -1;
442 for (int i = 0; i < pattern.length(); ++i) {
443 char ch = pattern.charAt(i);
444 if (part == SEG_RAW) {
445 if (ch == '\'') {
446 if (i + 1 < pattern.length()
447 && pattern.charAt(i+1) == '\'') {
448 segments[part].append(ch); // handle doubles
803 * <td><code>argument</code>
804 * <tr>
805 * <td><code>null</code>
806 * <td><i>any</i>
807 * <td><code>argument.toString()</code>
808 * </table>
809 * <p>
810 * If <code>pos</code> is non-null, and refers to
811 * <code>Field.ARGUMENT</code>, the location of the first formatted
812 * string will be returned.
813 *
814 * @param arguments an array of objects to be formatted and substituted.
815 * @param result where text is appended.
816 * @param pos On input: an alignment field, if desired.
817 * On output: the offsets of the alignment field.
818 * @return the string buffer passed in as {@code result}, with formatted
819 * text appended
820 * @exception IllegalArgumentException if an argument in the
821 * <code>arguments</code> array is not of the type
822 * expected by the format element(s) that use it.
823 * @exception NullPointerException if {@code result} is {@code null}
824 */
825 public final StringBuffer format(Object[] arguments, StringBuffer result,
826 FieldPosition pos)
827 {
828 return subformat(arguments, result, pos, null);
829 }
830
831 /**
832 * Creates a MessageFormat with the given pattern and uses it
833 * to format the given arguments. This is equivalent to
834 * <blockquote>
835 * <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
836 * </blockquote>
837 *
838 * @param pattern the pattern string
839 * @param arguments object(s) to format
840 * @return the formatted string
841 * @exception IllegalArgumentException if the pattern is invalid,
842 * or if an argument in the <code>arguments</code> array
843 * is not of the type expected by the format element(s)
844 * that use it.
845 * @exception NullPointerException if {@code pattern} is {@code null}
846 */
847 public static String format(String pattern, Object ... arguments) {
848 MessageFormat temp = new MessageFormat(pattern);
849 return temp.format(arguments);
850 }
851
852 // Overrides
853 /**
854 * Formats an array of objects and appends the <code>MessageFormat</code>'s
855 * pattern, with format elements replaced by the formatted objects, to the
856 * provided <code>StringBuffer</code>.
857 * This is equivalent to
858 * <blockquote>
859 * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>
860 * </blockquote>
861 *
862 * @param arguments an array of objects to be formatted and substituted.
863 * @param result where text is appended.
864 * @param pos On input: an alignment field, if desired.
865 * On output: the offsets of the alignment field.
866 * @exception IllegalArgumentException if an argument in the
867 * <code>arguments</code> array is not of the type
868 * expected by the format element(s) that use it.
869 * @exception NullPointerException if {@code result} is {@code null}
870 */
871 public final StringBuffer format(Object arguments, StringBuffer result,
872 FieldPosition pos)
873 {
874 return subformat((Object[]) arguments, result, pos, null);
875 }
876
877 /**
878 * Formats an array of objects and inserts them into the
879 * <code>MessageFormat</code>'s pattern, producing an
880 * <code>AttributedCharacterIterator</code>.
881 * You can use the returned <code>AttributedCharacterIterator</code>
882 * to build the resulting String, as well as to determine information
883 * about the resulting String.
884 * <p>
885 * The text of the returned <code>AttributedCharacterIterator</code> is
886 * the same that would be returned by
887 * <blockquote>
888 * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
889 * </blockquote>
939 * the substituted strings contain {n} references.)
940 * <li>Will not always find a match (or the correct match)
941 * if some part of the parse is ambiguous.
942 * For example, if the pattern "{1},{2}" is used with the
943 * string arguments {"a,b", "c"}, it will format as "a,b,c".
944 * When the result is parsed, it will return {"a", "b,c"}.
945 * <li>If a single argument is parsed more than once in the string,
946 * then the later parse wins.
947 * </ul>
948 * When the parse fails, use ParsePosition.getErrorIndex() to find out
949 * where in the string the parsing failed. The returned error
950 * index is the starting offset of the sub-patterns that the string
951 * is comparing with. For example, if the parsing string "AAA {0} BBB"
952 * is comparing against the pattern "AAD {0} BBB", the error index is
953 * 0. When an error occurs, the call to this method will return null.
954 * If the source is null, return an empty array.
955 *
956 * @param source the string to parse
957 * @param pos the parse position
958 * @return an array of parsed objects
959 * @exception NullPointerException if {@code pos} is {@code null}
960 * for a non-null {@code source} string.
961 */
962 public Object[] parse(String source, ParsePosition pos) {
963 if (source == null) {
964 Object[] empty = {};
965 return empty;
966 }
967
968 int maximumArgumentNumber = -1;
969 for (int i = 0; i <= maxOffset; i++) {
970 if (argumentNumbers[i] > maximumArgumentNumber) {
971 maximumArgumentNumber = argumentNumbers[i];
972 }
973 }
974 Object[] resultArray = new Object[maximumArgumentNumber + 1];
975
976 int patternOffset = 0;
977 int sourceOffset = pos.index;
978 ParsePosition tempStatus = new ParsePosition(0);
979 for (int i = 0; i <= maxOffset; ++i) {
980 // match up to format
|