Message formatting in Java

Whatever kind application you’re working on, you will probably need to create some sort of message more complex than “Hello World!” at some point. Whether it’s a log message, or some text to plonk into your nice UI, you will probably also need to format it in some way to include some variable values, and maybe some embellishments like using the correct words for plural and singular values.

Java provides a number of ways to do this. In this post, we’ll look at a few of them.

Download the Sample application.

The wrong way

Let’s get this out of our way right now. This:

00: String message =
01:     “The ” + animal.getType().getName() +
02:     “ sat on the “ + floorCovering.getType().getName() + “.”;

is wrong. On a lot of levels. Do not use it. Ever. First of all, you’ve got your message defined in your code. Bad idea if you need to modify it at a later date. Secondly, since word ordering can be different in different languages, it makes it harder to manage good translations. Thirdly, given the fact that strings are immutable, you wouldn’t be creating one instance of a string, you’d be creating 4:

  1. The cat
  2. The cat sat on the
  3. The cat sat on the mat
  4. The cat sat on the mat.

That’s 4 strings created for a very simple message. 3 of them are useless.

String concatenation: just say no.

Using Formatters

The java.text package provides some text formatters which solve most of the problems caused by String concatenation. The String object itself provides a format method, but we will not go into it here; I find MessageFormat to be a lot more useful, so I tend to use that in favour of String.format().

There are a number of formatters, but MessageFormat should suit most situations. The message template is passed in as a single string, and the variables are passed in order. The template has to contain placeholders for them, in the form {n} – these will then be replaced by the values which you pass in. To replicate the example above:

00: MessageFormat.format(“The {0} sat on the {1}.”,
01:     animal.getType().getName(),
02:     floorCovering.getType().getName());

In this case, the value of the first argument after the template would go into placeholder 0, and the value of the second argument would go into placeholder 1.

This opens up some advantages. It is much easier to store the template as a resource in this way, so you’re free to keep it in a properties file, database, or anywhere else you can think of. Modifying the template now means modifying a single string, rather than a concatenation, which is a hell of a lot easier.

Type formatting

The MessageFormat class also facilitates the formatting of numbers, for example, currencies, and dates. By declaring more information with the placeholder, we can make it format the values in certain ways. For example, if the first argument is a date:

{0,date,yyyy-MM-dd}

Will cause the date to be written in four digit year, two-digit month, and two-digit day format (i.e. 5th December, 2009 would be 2009-12-05).

Numbers can also be formatted in a similar manner:

{0,number,#,000.00}

Would cause the value to be displayed with 2 decimal places, and, if the number exceeds 1000, comma separators for the thousands.

The JavaDoc page for MessageFormat describes the different types of formatting that are possible.

Conditional formatting using choice

When we need to form nice sentences, we come across the problems of plurals. Sometimes, we can just cop out and use the classic “1 unit(s)” notation. Sometimes, it just looks wrong, so we have to find other ways of forming the words properly.

Until not very long ago, I used to, very naively, check for the number of units in code and then drop the correct variation into the template. Here’s an example which harks back to my misspent youth playing Fallout (I regret nothing!)

00: MessageFormat.format(“{0} hits {1} for {2} {3} of damage”,
01:     attacker.getName(),
02:     target.getName(),
03:     damage,
04:     (damage == 1) ? text.get(“points_singular”) : text.get(“points_plural”));

This would return “point” if damage is 1, or “points” if it is any other value. Not the end of the world, but there’s some unnecessary messiness there. There’s an easier way: using choice formatting.

00: MessageFormat.format(“{0} hits {1} for {2} {2,choice,1#point|1<points} of damage”,
01:     attacker.getName(),
02:     target.getName(),
03:     damage);

In this case, the template contains everything it needs to form the sentence properly. The # entry means equal (eg. 1# means that the value must be equal to one, 2# means that the value must be 2, etc), while the less than sign means that the value passed in must be greater than the value to the left of the sign. The formatter will use the text in that choice, with each choice being separated by a | (pipe).

The choice format only supports numeric values, which is usually not a problem – most situations you’d use it for only make sense in terms of numeric values. However, at one point I wondered if it was possible to use it for enumerations. It turns out that this is not directly possible; however, you could, if you wanted to, attach numeric values to an enumeration and use those for formatting:

00: public enum Gender {
01:     Unknown(0),
02:     Male(1),
03:     Female(2);

04:     private final int value;

05:     Gender(int value) { this.value = value; }
06:     public int getValue() { return value; }
07: }

this would allow us to use a template such as {n,choice,0#its|1#his|2#her}, passing gender.getValue() into n to display the correct word for the gender.

No, I’m pretty sure this was not the way it was meant to be used.

Using a template engine

While formatters are very handy, they still involve some plumbing to load the resources, and the notation isn’t always the easiest to follow. We can, however, turn to a template engine to help us simplify these issues.

We’re going to look at the Velocity engine, because it is easy to use, and because the Spring framework supports it quite nicely.

Now, the first thing we need to do is set up the engine. We can do this using Spring’s VelocityEngineFactoryBean:

<bean id="velocityEngineFactory" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="velocityProperties">
<props>
<prop key="resource.loader">class</prop>
<prop key="class.resource.loader.class">
org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
</prop>
</props>
</property>
</bean>

The resource.loader property tells the engine factory what kind of loader to give the engines it creates – it tells them how they will find the message resources. The class.resource.loader.class specifies the implementation of the loader to use. In this case, we’re going to load the resource from a file in the classpath. We could also have used a jdbc loader, for example, which would look up the resource in a database.

We can then configure our formatter:

<bean id="velocityFormatter" class="com.simplepart.samples.textformatting.VelocityFormatter">
<constructor-arg ref="velocityEngineFactory" />
<constructor-arg value="message.vm" />
</bean>

Note how we’re passing in a reference to the factory in the first constructor argument. In reality, our formatter class takes a VelocityEngine as a parameter; Spring takes note of this, and, rather then popping the factory in as a value, it will ask the factory for an engine to use.

The second argument is just the path of the message file in the classpath.

Getting a formatted message

00: Map model = new HashMap();
01: model.put("attacker", attacker);
02: model.put("target", target);
03: model.put("damage", damage);

04: String message = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templatePath, model);

If you compare this snippet to the snippet for MessageFormat above, you should notice that, rather than feeding properties into the template, we’re passing in the whole object.

This gives us a great deal more flexibility; if we needed to include some additional value from, say, the target instance at some point, we would only need to change the template. If we were passing in properties, we’d have to change the template and the code. Of course, it’s still possible to pass a property value if you really want to.

The template

In this case, the template is stored in a file called “message.vm” (the path we defined in the Spring configuration file). The template looks like this:

${attacker.name} hits ${target.name} in the groin for ${damage} point#if($damage > 1)s#end of damage, effectively ending #if($target.gender == "Male")his#elseif($target.gender == "Female")her#{else}its#end child-bearing days.

Notice how the placeholders use the keys from the map we passed in. The template can access any property with a getter in these objects.

In the case of the value-dependent text, we’re using a velocity if statement – if the condition evaluates to true, the engine will use any text between the end of the condition (i.e. the closing bracket) and the next statement (i.e. #end or #else/#elseif).

Normally the statements and the text they should output can be separated with whitespace or a line break, but in the case or the output for the else statement in this template, I did not want whitespace or a line break at that position.

However, velocity allows us to wrap statements in curly braces to delimit them, so it knows where the else statement ends and the text begins.

Like any template engine, velocity provides a very useful set of directives to make your templates a little bit smarter. You can find the user documentation here.

Extra added bonus: Macros

One of the kickbacks you get from a templating engine is the ability to use macros in your templates. For example, in our template, the section that determines whether we should say “his”, “her” or “its” looks like it could be useful in a number of places, so it makes sense to pull it out and use it as a macro:

#macro( his_her_its $gender )
#if($gender == “Male”)his#elseif($gender == “Female”)her#{else}its#end
#end

Our template is now:

${attacker.name} hits ${target.name} in the groin for ${damage} point#if($damage > 1)s#end of damage, effectively ending #his_her_its($target.gender) child-bearing days.

To use a macro, we must load it somehow. My preferred way is to load it directly into the template, like so:

#parse( "macros.vm" )${attacker.name} hits ${target.name} in the groin for ${damage} point#if($damage > 1)s#end of damage, effectively ending #his_her_its($target.gender) child-bearing days.

This lets us load sets of macros where we need them.

Further thoughts

Do you use any other technique to format text? What makes that technique work for you?

13 Replies to “Message formatting in Java”

  1. Enums have an int associated with them already – .ordinal(). That’s why they can be used in switch-case statements. Any thoughts on usefulness of static MessageFormat instances as opposed to always using the static method MessageFormat.format?

    1. Thanks Don 🙂 The ordinal can be used in this case, but I prefer to explicitly define an integer to break the dependency on the ordering of the enum.
      Static MessageFormat instances can be useful in places, but in my opinion looking up the instance to use as opposed to just using the static method just adds noise to the code.
      In terms of performance, the static instance is way faster (it averages 2000ms less over 1 million iterations on my machine), so it’s definitely something to consider if the code is called often.

  2. Comment about this:

    “Thirdly, given the fact that strings are immutable, you wouldn’t be creating one instance of a string, you’d be creating 4”

    I believe Java is supposed to create efficient string builder for string concatenation. So at least as long as you have a single statement (not loop!), then it’s as efficient as it can be, as no format string parsing required. Of course your other points against string concatenation still stand, and I’d like to add, that string concatenation often makes code longer and harder to read, compared to using format strings.

  3. Pingback: JavaPins
  4. Hey Karl,

    Can’t believe I’ve been writing java this long and haven’t come across MessageFormat. Many thanks from a fellow fallout junky!!

    1. Welcome! MessageFormat is really handy, though you should really look at a templating engine if you need to format anything longer than one line messages. Take a look at Mustache, it’s great!

  5. Interesting thread….after 7 years what do we think about performance?

    long start = System.nanoTime();
    String txt = MessageFormat.format(“Test {0}”,” Hello”);
    System.out.println(“MessageFormat: ” + (System.nanoTime() – start) + ” ns”);

    start = System.nanoTime();
    String txt1 = new StringBuilder(“Test “).append(“Hello”).toString();
    System.out.println(“StringBuilder: ” + (System.nanoTime() – start) + ” ns”);

  6. Hi Clarence, and sorry for the later reply! Performance is only one aspect of this; the formatter approach is a lot more flexible than the builder, especially if you need to worry about localization! In cases where you don’t need the flexibility, StringBuilder’s great, and it avoids the issue with creating a bunch of immutables 🙂

Comments are closed.