3

Is there any possibility to define a format for java.text.MessageFormat that formats numbers without grouping separators and other objects with .toString()?

My trouble is that I have to format an argument for which I don't in advance whether it is a number or not. If I just use

java.text.MessageFormat.format("The object: {0}",someobject)

that usually just calls someobject.toString() as I want, but when someobject is actually a number 1231231, it returns (in the German locale) The object: 1,231,231, but I want The object: 1231231 here. Formatting with

java.text.MessageFormat.format("{0,number,#}",1231231)

returns The object: 1231231, but this doesn't work when the argument is not a number. Is there a way to specify a format that works for both numbers and other objects?

(In case your wondering: unfortunately, changing the locale of the MessageFormat is not an option, since that's deeply buried in JBoss' logging framework and inaccessible.)

UPDATE: calling String.valueOf(someobject) before passing it as an argument does solve this problem, but also isn't feasible in my case, since this problem occurs when logging, and I don't want to call the potentially expensive someobject.toString() when the logging level doesn't allow the log message and the MessageFormat is thus never applied.

Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
  • [Here](http://stackoverflow.com/questions/1942118/how-do-i-format-a-long-integer-as-a-string-without-separator-in-java) they suggest calling `Long.toString()` on the number you need without commas, and [it seems to work](http://ideone.com/1wBpV5) – BackSlash Jan 24 '17 at 10:18

3 Answers3

0
    final String format = MessageFormat.format("The object: {0}", a == null ? null : a.toString());

    System.out.println("format = " + format);
0

A variant of Shylock.Gou's answer that would avoid calling someobject.toString() when this isn't actually used because the loglevel is forbidden, one could wrap the object so that toString is called on use only. This still seems somewhat awkward, though.

public Object wrapToString(Object obj) {
    return new Object() {
        /** {@inheritDoc} */
        @Override
        public String toString() {
            return String.valueOf(obj);
        }
    };
}

Then again, one could also pollute the code by wraping the logging statement with a loglevel check - which might or might not be better, depending on taste.

Community
  • 1
  • 1
Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
0

I don't know a way to do this within the format string. But you can do it at the level of the MessageFormat object.

The way a MessageFormat works is that it converts a pattern into a template string and an array of subsidiary formats for each argument. It then delegates formatting of each argument to the corresponding argument format.

It's possible to inspect and replace the argument formats after creating the MessageFormat. So, we can identify the format(s) for arguments we want to format specially, and replace them with a format which does what we want. And doing what we want is simple, if a little verbose - we end up with the value and a StringBuffer, so we can just append the value to the StringBuffer.

Helpfully, where you use a default format, like {0}, MessageFormat actually just puts a null in the array (when formatting, when it finds a null, falls through to some default formatting code). So if we want to change the way default formatting is done, we can just look for those nulls.

This code:

MessageFormat messageFormat = new MessageFormat("The object: {0}");

Format[] argumentFormats = messageFormat.getFormats();
for (int i = 0; i < argumentFormats.length; i++) {
    if (argumentFormats[i] == null) {
        messageFormat.setFormat(i, new Format() {
            @Override
            public StringBuffer format(Object obj, StringBuffer buf, FieldPosition pos) {
                pos.setBeginIndex(buf.length());
                buf.append(obj);
                pos.setEndIndex(buf.length());
                return buf;
            }

            @Override
            public Object parseObject(String source, ParsePosition pos) {
                throw new UnsupportedOperationException();
            }
        });
    }
}

System.out.println(messageFormat.format(new Object[]{1231231}));
System.out.println(messageFormat.format(new Object[]{LocalDate.now()}));

Prints:

The object: 1231231
The object: 2023-07-20

Which i think is what you want.

Now, the approach i have taken here replaces all default formats with this simple appending format. You might not want to do that. If there was a specific argument you wanted to format specially, you could forget about the loop, and just set the format at the right place directly. Otherwise, you could use some other logic for deciding which formats to replace.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133