web-dev-qa-db-fra.com

Formateur de chaînes dans GWT

Comment formater ma chaîne en GWT?

J'ai fait une méthode

  Formatter format = new Formatter();
    int matches = 0;
    Formatter formattedString = format.format("%d numbers(s, args) in correct position", matches);
    return formattedString.toString();

Mais il se plaint en disant

Validating newly compiled units
   [ERROR] Errors in 'file:/C:/Documents%20and%20Settings/kkshetri/workspace/MasterMind/MasterMind/src/com/kunjan/MasterMind/client/MasterMind.Java'
      [ERROR] Line 84: No source code is available for type Java.util.Formatter; did you forget to inherit a required module?

Formatter n'est-il pas inclus?

37
unj2

MISE À JOUR: S'il vous plaît voir (et voter) le post de Joseph Lust ci-dessous avant de regarder plus loin cette réponse.

On dirait que le formateur n'est pas inclus selon cet article . Cependant, ils suggèrent quelques alternatives.

21
Pace

Voir le page officielle sur la mise en forme de la date et du nombre GWT.

Ils suggèrent ce qui suit:

myNum decimal = 33.23232;
myString = NumberFormat.getFormat("#.00").format(decimal);

Il est préférable d'utiliser leurs méthodes optimisées prises en charge, plutôt que de préparer votre propre méthode non optimale. Leur compilateur les optimisera tous à peu près de la même façon de toute façon à la fin.

33
Joseph Lust

Un remplacement très simple pour String.format () dans GWT 2.1+:

import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.regexp.shared.SplitResult;

public static String format(final String format, final Object... args) {
  final RegExp regex = RegExp.compile("%[a-z]");
  final SplitResult split = regex.split(format);
  final StringBuffer msg = new StringBuffer();
  for (int pos = 0; pos < split.length() - 1; ++pos) {
    msg.append(split.get(pos));
    msg.append(args[pos].toString());
  }
  msg.append(split.get(split.length() - 1));
  return msg.toString();
}
22
yegor256

Ou encore plus simple, sans utiliser RegExp et en utilisant uniquement des chaînes:

public static String format(final String format, final String... args) {
    String[] split = format.split("%s");
    final StringBuffer msg = new StringBuffer();
    for (int pos = 0; pos < split.length - 1; pos += 1) {
        msg.append(split[pos]);
        msg.append(args[pos]);
    }
    msg.append(split[split.length - 1]);
    return msg.toString();
 }
5
ka2

Une autre suggestion qui utilise JSNI et une fonction de format JavaScript agréable de ne autre publication :

import com.google.gwt.core.client.JsArrayString;

public abstract class StringFormatter {
    public static String format(final String format, final Object... args) {
        if (null == args || 0 == args.length)
            return format;
        JsArrayString array = newArray();
        for (Object arg : args) {
            array.Push(String.valueOf(arg)); // TODO: smarter conversion?
        }
        return nativeFormat(format, array);
    }

    private static native JsArrayString newArray()/*-{
        return [];
    }-*/;

    private static native String nativeFormat(final String format, final JsArrayString args)/*-{
        return format.replace(/{(\d+)}/g, function(match, number) {
            return typeof args[number] != 'undefined' ? args[number] : match;
        });
    }-*/;
}

On peut alors passer un appel comme celui-ci:

StringFormatter.format("Greetings {0}, it's {1} o'clock, which is a {2} statement", "Master", 8, false);

... avec pour résultat

Salutations Maître, il est 8 heures, ce qui est une fausse déclaration

Il existe un potentiel d'amélioration supplémentaire au niveau du commentaire [~ # ~] [~ # ~] , par exemple utilisez NumberFormat. Les suggestions sont les bienvenues.

3
Mikha

Une extension de la solution Daniels: prend également en charge l'échappement à l'aide de 'et lance si un nombre ne peut pas être analysé (comme le fait la version JVM):

private static final char OPEN = '{';
private static final char CLOSE = '}';
private static final char ESCAPE = '\'';

@Override
public String format(String pattern, Object... arguments) {
    if (pattern == null || pattern.isEmpty())
        return "";

    // Approximate the result length: format string + 16 character args
    StringBuilder sb = new StringBuilder(pattern.length() + (arguments.length * 16));

    int cur = 0;
    int len = pattern.length();
    // if escaped, then its >= 0
    int escapedAtIndex = -1;

    while (cur < len) {
        char currentChar = pattern.charAt(cur);
        switch (currentChar) {
            case OPEN:
                if (escapedAtIndex >= 0) {
                    // currently escaped
                    sb.append(currentChar);
                } else {
                    // find close
                    int close = pattern.indexOf(CLOSE, cur + 1);
                    switch (close) {
                        case -1:
                            // Missing close. Actually an error. But just ignore
                            sb.append(currentChar);
                            break;
                        default:
                            // Ok, we have a close
                            final String nStr = pattern.substring(cur + 1, close);
                            try {
                                // Append the corresponding argument value
                                sb.append(arguments[Integer.parseInt(nStr)]);
                            } catch (Exception e) {
                                if (e instanceof NumberFormatException) {
                                    throw new IllegalArgumentException(nStr +
                                            " is not a number.");
                                }
                                // Append the curlies and the original delimited value
                                sb.append(OPEN).append(nStr).append(CLOSE);
                            }
                            // Continue after the close
                            cur = close;
                            break;
                    }
                }
                cur++;
                break;
            case ESCAPE:
                // Special case: two '' are just converted to '
                boolean nextIsEscapeToo = (cur + 1 < len) && pattern.charAt(cur + 1) == ESCAPE;
                if (nextIsEscapeToo) {
                    sb.append(ESCAPE);
                    cur = cur + 2;
                } else {
                    if (escapedAtIndex >= 0) {
                        // Escape end.
                        escapedAtIndex = -1;
                    } else {
                        // Escape start.
                        escapedAtIndex = cur;
                    }
                    cur++;
                }
                break;
            default:
                // 90% case: Nothing special, just a normal character
                sb.append(currentChar);
                cur++;
                break;
        }
    }
    return sb.toString();
}

Cette implémentation et la version JVM réussissent toutes deux ces tests:

    // Replace: 0 items
    assertFormat("Nothing to replace", "Nothing to replace");
    // Replace: 1 item
    assertFormat("{0} apples", "15 apples", 15);
    assertFormat("number of apples: {0}", "number of apples: zero", "zero");
    assertFormat("you ate {0} apples", "you ate some apples", "some");
    // Replace 2 items
    assertFormat("{1} text {0}", "second text first", "first", "second");
    assertFormat("X {1} text {0}", "X second text first", "first", "second");
    assertFormat("{0} text {1} X", "first text second X", "first", "second");

Échapper-tests:

    // Escaping with no replacement
    assertFormat("It's the world", "Its the world");
    assertFormat("It''s the world", "It's the world");
    assertFormat("Open ' and now a second ' (closes)", "Open  and now a second  (closes)");
    assertFormat("It'''s the world", "It's the world");
    assertFormat("'{0}' {1} {2}", "{0} one two", "zero", "one", "two");
    // Stays escaped (if end escape is missing)
    assertFormat("'{0} {1} {2}", "{0} {1} {2}", "zero", "one", "two");
    assertFormat("'{0} {1}' {2}", "{0} {1} two", "zero", "one", "two");
    // No matter where we escape, stays escaped
    assertFormat("It's a {0} world", "Its a {0} world", "blue");
    // But we can end escape everywhere
    assertFormat("It's a {0} world, but not '{1}",
            "Its a {0} world, but not always", "blue", "always");
    // I think we want this
    assertFormat("It''s a {0} world, but not {1}",
            "It's a blue world, but not always", "blue", "always");
    // Triple
    assertFormat("' '' '", " ' ");
    // From Oracle docs
    assertFormat("'{''}'", "{'}");
    // Missing argument (just stays 0)
    assertFormat("begin {0} end", "begin {0} end");
    // Throws
    try {
        assertFormat("begin {not_a_number} end", "begin {not_a_number} end");
        throw new AssertionError("Should not get here");
    } catch (IllegalArgumentException iae) {
        // OK
    }
2
SRU

Peut-être que la façon la plus simple de faire quelque chose comme String.format peut être de le faire avec un String.replace, par exemple;

au lieu de faire String.format("Hello %s", "Daniel"); ==> "Hello %s".replace("%s", "Daniel"),

les deux nous donnent le même résultat, mais juste la deuxième façon fonctionne du côté client GWT

1
Gio

Celui-ci est assez rapide et ignore les mauvaises valeurs délimitées par des bouclés:

public static String format(final String format, final Object... args)
{
    if (format == null || format.isEmpty()) return "";

    // Approximate the result length: format string + 16 character args
    StringBuilder sb = new StringBuilder(format.length() + (args.length*16));

    final char openDelim = '{';
    final char closeDelim = '}';

    int cur = 0;
    int len = format.length();
    int open;
    int close;

    while (cur < len)
    {
        switch (open = format.indexOf(openDelim, cur))
        {
            case -1:
                return sb.append(format.substring(cur, len)).toString();

            default:
                sb.append(format.substring(cur, open));
                switch (close = format.indexOf(closeDelim, open))
                {
                    case -1:
                        return sb.append(format.substring(open)).toString();

                    default:
                        String nStr = format.substring(open + 1, close);
                        try
                        {
                            // Append the corresponding argument value
                            sb.append(args[Integer.parseInt(nStr)]);
                        }
                        catch (Exception e)
                        {
                            // Append the curlies and the original delimited value
                            sb.append(openDelim).append(nStr).append(closeDelim);
                        }
                        cur = close + 1;
                }
        }
    }

    return sb.toString();
}
1
Daniel Harvey

Je ne souhaite pas abuser de la manipulation des chaînes pour faire le travail des regexps, mais, basé sur la solution de bodrin, vous pouvez coder:

public static String format (String pattern, final Object ... args) {
    for (Object arg : args) {
        String part1 = pattern.substring(0,pattern.indexOf('{'));
        String part2 = pattern.substring(pattern.indexOf('}') + 1);
        pattern = part1 + arg + part2;
    }   
    return pattern;
}
1
user743489

Comme mentionné ci-dessus, il existe des formats GWT pour les nombres et les dates: NumberFormat et DateTimeFormat. Pourtant, j'avais besoin d'une solution pour le cas bien connu de String.format(...). Je me retrouve avec cette solution, je ne sais pas si c'est mauvais pour les performances, mais c'est visuellement propre. Je serais heureux d'entendre tout commentaire à ce sujet ou sur une autre solution.

Mon formateur de cordes:

public class Strings {

    public static String format(final String format, final Object... args) {
        String retVal = format;
        for (final Object current : args) {
            retVal = retVal.replaceFirst("[%][s]", current.toString());
        }
        return retVal;
    }

}

et le JUTest si l'on veut réutiliser ceci:

public class StringsTest {

    @Test
    public final void testFormat() {
        this.assertFormat("Some test here  %s.", 54);
        this.assertFormat("Some test here %s and there %s, and test [%s].  sfsfs !!!", 54, 59, "HAHA");

    }

    private void assertFormat(final String format, final Object... args) {
        Assert.assertEquals("Formatting is not working", String.format(format, args), Strings.format(format, args));
    }

}
0
elkaonline

Comme alternative, vous pouvez utiliser la classe NumberFormat :

NumberFormat fmt = NumberFormat.getDecimalFormat();
double value = 12345.6789;
String formatted = fmt.format(value);
// Prints 1,2345.6789 in the default locale
0
Antonio

un autre remplacement très très simple pour Java.text.MessageFormat.format ():

public static String format(final String format, final Object... args) {
    StringBuilder sb = new StringBuilder();
    int cur = 0;
    int len = format.length();
    while (cur < len) {
        int fi = format.indexOf('{', cur);
        if (fi != -1) {
            sb.append(format.substring(cur, fi));
            int si = format.indexOf('}', fi);
            if (si != -1) {
                String nStr = format.substring(fi + 1, si);
                int i = Integer.parseInt(nStr);
                sb.append(args[i]);
                cur = si + 1;
            } else {
                sb.append(format.substring(fi));
                break;
            }
        } else {
            sb.append(format.substring(cur, len));
            break;
        }
    }
    return sb.toString();
}
0
bodrin