Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic field numbering in String formatting

Tags:

java

I am trying to format String in java using MessageFormat class. The String contains positional field but it only work if I provide the field number. For e.g

MessageFormat.format("{0} {1}", "Hi", "Java") works but

MessageFormat.format("{} {}", "Hi", "Java") gives

Exception in thread "main" java.lang.IllegalArgumentException: can't parse argument number: 
    at java.text.MessageFormat.makeFormat(MessageFormat.java:1429)
    at java.text.MessageFormat.applyPattern(MessageFormat.java:479)
    at java.text.MessageFormat.<init>(MessageFormat.java:362)
    at java.text.MessageFormat.format(MessageFormat.java:840)
    at controller.App.main(App.java:11)
Caused by: java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:592)
    at java.lang.Integer.parseInt(Integer.java:615)
    at java.text.MessageFormat.makeFormat(MessageFormat.java:1427)

Is there a way(any other class or library) in that it can handle both automatic and manual field numbering something like python does?

In [3]: print "{} {}".format("Hi", "Java")
Hi Java

In [4]: print "{0} {1}".format("Hi", "Java")
Hi Java
like image 626
barunsthakur Avatar asked Jun 26 '15 08:06

barunsthakur


1 Answers

I'd use the pattern posted by Maroun Maroun if I was confined to standard libraries like barunsthakur.

Ben Greens clever idea is useful for everyone who uses sfl4j when logging with non-indexed arguments, in addition to preparing strings manually with indexed arguments.

There was one disadvantage for me: I can use both indexed and non-indexed arguments now, but I'm not able to use the FormatType and FormatStyle options from java.text.MessageFormat.format(), e.g. {0,number,currency}.

So I made up an extended version of Ben's solution, that utilizes all 3 variants.

import org.slf4j.helpers.MessageFormatter;
import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public static String format(String original, Object... replacements)
{
    if (Pattern.matches(".*\\{\\d+,.+,.+\\}.*", original))
    {
        return MessageFormat.format(original, replacements);
    }
    else
    {
        String r = "\\{\\d\\}";

        if(original.matches(".*" + r + ".*"))
        {
            Pattern p = Pattern.compile(r);
            Matcher m = p.matcher(original);
            StringBuffer b = null;
            int i = 0;

            while(m.find())
            {
                if(b == null) b = new StringBuffer();
                m.appendReplacement(b, replacements[i].toString());
                i++;
            }
            m.appendTail(b);
            return (i > 0) ? b.toString() : null;
        }
        else
        {
            return MessageFormatter.arrayFormat(original, replacements).getMessage();
        }
    }
}

Unit-Tests:

String expected = "pink is a disgusting color";
String actual = StringHelpers.format("{0} is a disgusting {1}", "pink", "color");
assertEquals(expected, actual);

expected = "01-02-2017 was a rather meh day";
actual = StringHelpers.format("{0,date,MM-dd-YYYY} was a rather {1} day", new Date(117,0,2), "meh");
assertEquals(expected, actual);

expected = "One Person expects the danish inquisition";
actual = StringHelpers.format("{} expects the {} inquisition", "One Person", "danish");
assertEquals(expected, actual);
like image 157
JackLeEmmerdeur Avatar answered Oct 06 '22 00:10

JackLeEmmerdeur