zcommon/src/main/java/org/zkoss/text/MessageFormats.java

Summary

Maintainability
B
4 hrs
Test Coverage
/* MessageFormats.java


    Purpose: 
    Description: 
    History:
    2002/02/08 11:03:32, Create, Tom M. Yeh.

Copyright (C) 2001 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
    This program is distributed under LGPL Version 2.1 in the hope that
    it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.text;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Collection;
import java.util.Locale;
import java.net.InetAddress;
import java.text.MessageFormat;

import org.zkoss.lang.Strings;
import org.zkoss.util.Locales;

/**
 * The message formatting relevant utilities.
 *
 * <p>See also java.text.MessageFormat.
 *
 * @author tomyeh
 */
public class MessageFormats {
    /** Creates a MessageFormat with the given pattern and uses it to
     * format the given arguments.
     *
     * <p>An extension to java.text.MessageFormat.format by allowing
     * to specify a Locale.
     *
     * @param locale the locale; null for {@link Locales#getCurrent}
     */
    public static final String
    format(String pattern, Object[] args, Locale locale) {
        return getFormat(pattern, locale)
            .format(args, new StringBuffer(), null).toString();
    }
    private static MessageFormat getFormat(String pattern, Locale locale) {
        if (locale == null)
            locale = getLocale();
        return new MessageFormat(pattern, locale);
    }
    private static final Locale getLocale() {
        return Locales.getCurrent();
    }
    /** Creates a MessageFormat with the given pattern and uses it to
     * format the given arguments, by use of
     * {@link Locales#getCurrent}.
     *
     * <p>Note: java.text.MessageFormat.format uses Locale.getDefault,
     * which might not be correct in a multi-user environment.
     */
    public static final String format(String pattern, Object[] args) {
        return format(pattern, args, null);
    }
    /** Creates a MessageFormat with the given pattern and uses it to
     * format the given arguments, by use of StringBuffer.
     *
     * <p>If you want to concatenate a sequence of formated string, use
     * this method.
     */
    public static final StringBuffer format(
    StringBuffer result, String pattern, Object[] args, Locale locale) {
        return getFormat(pattern, locale).format(args, result, null);
    }
    /** Creates a MessageFormat with the given pattern and uses it to
     * format the given arguments, by use of StringBuffer and
     * {@link Locales#getCurrent}.
     */
    public static final StringBuffer format(
    StringBuffer result, String pattern, Object[] args) {
        return format(result, pattern, args, null);
    }

    /** Parses a pattern and converts it to the format of
     * java.text.MessageFormat.
     * Names are the first element of a substring enclosing with {}
     * in the pattern.
     *
     * <p>Example: "{a} is {b,number} of {a}" will return
     * <code>new NameInfo("{0} is {1,number} of {0}", {"a", "b"})</code>.
     *
     * @see #formatByName(String, Map, Locale)
     */
    public static final NameInfo parseByName(String pattern) {
        final Map<String,Integer> names = new LinkedHashMap<String,Integer>();
        final int len = pattern.length();
        final StringBuffer sb = new StringBuffer(len + 32);
        int j = 0;
        for (;;) {
            final int k = Strings.anyOf(pattern, "'{", j);
            if (k >= len) //not found
                break; //done

            final char cc = pattern.charAt(k);
            if (cc == '\'') {
                final int l = pattern.indexOf('\'', k + 1);
                if (l < 0) //not found
                    break; //done

                sb.append(pattern.substring(j, l + 1));
                j = l + 1;
            } else {
                final int l = Strings.anyOf(pattern, ",}", k + 1);
                if (l >= len) //not found
                    break; //done
                final int m = pattern.indexOf('}', l);
                if (m < 0) //not found
                    break; //done

                final String nm = pattern.substring(k + 1, l);
                Integer pos = names.get(nm);
                if (pos == null) {
                    pos = new Integer(names.size());
                    names.put(nm, pos);
                }
                sb.append(pattern.substring(j, k + 1))
                    .append(pos).append(pattern.substring(l, m + 1));
                j = m + 1;
            }
        }
        if (j < len)
            sb.append(pattern.substring(j));
        return new NameInfo(sb.toString(), names.keySet());
    }
    /** The name info returned by {@link #parseByName}).
     */
    public static class NameInfo {
        /** The pattern in the format of java.text.MessageFormat. */
        public final String pattern;
        /** The name of relative position. */
        public final String[] names;
        NameInfo(String pattern, Collection<String> names) {
            this.pattern = pattern;
            this.names = names.toArray(new String[names.size()]);
        }
        NameInfo(String pattern, String[] names) {
            this.pattern = pattern;
            this.names = names;
        }
    }
    /** Formats a pattern by substituting names, enclosing with {}, with
     * values found in the giving map.
     *
     * <p>This is an extension to java.text.MessageFormat.
     * The only difference is that this method use arbitrary name
     * instead of a number. For example, it use {name} instead of {0}.
     * The quotation rule and formating pattern are the same as
     * java.text.MessageFormat. Example, {var,number,$'#',###}.
     *
     * <p>It actually uses {@link #parseByName} to convert names to numbers,
     * and then passes to java.text.MessageFormat.
     *
     * @param locale the locale; null for
     * {@link Locales#getCurrent}
     * @exception IllegalArgumentException if the pattern is invalid
     * @see #parseByName
     */
    public static final String
    formatByName(String pattern, Map<String, ?> mapping, Locale locale) {
        final NameInfo ni = parseByName(pattern);
        final Object[] args = new Object[ni.names.length];
        for (int j = args.length; --j >= 0;)
            args[j] = mapping.get(ni.names[j]);
        return format(ni.pattern, args, locale);
    }
    /** Formats a pattern by substituting names with values found in
     * the giving map, by use of
     * {@link Locales#getCurrent}.
     *
     * @see #parseByName
     */
    public static final String formatByName(String pattern, Map<String, ?> mapping) {
        return formatByName(pattern, mapping, null);
    }
}