Is there a standard or at least widespread implementation of something like String.format
, but with named arguments?
I'd like to format a templatized string in a way like that:
Map<String, Object> args = new HashMap<String, Object>();
args.put("PATH", "/usr/bin");
args.put("file", "foo");
String s = someHypotheticalMethod("#{PATH}/ls #{file}");
// "/usr/bin/ls foo"
Technically, it's almost the same as:
String[] args = new String[] { "/usr/bin", "foo" };
String s = String.format("%1$s/ls %2$s", args);
// "/usr/bin/ls foo"
but with named arguments.
I'm aware of:
but all of them use ordered or at least numbered arguments, not named ones. I know it's trivial to implement one, but is there a mechanism I'm looking for in standard Java libraries or at least in Apache Commons / Guava / something similar, without bringing in high-profile template engines?
NOTE: I'm not really interested in full-blown template engines, with features like some imperative / functional logic, flow control, modifiers, sub-templates / inclusions, iterators, etc. Generally the following method is a working 4-line implementation - that's all I need:
public static String interpolate(String format, Map<String, ? extends Object> args) {
String out = format;
for (String arg : args.keySet()) {
out = Pattern.compile(Pattern.quote("#{" + arg + "}")).
matcher(out).
replaceAll(args.get(arg).toString());
}
return out;
}
%d means number. %0nd means zero-padded number with a length. You build n by subtraction in your example. %s is a string. Your format string ends up being this: "%03d%s", 0, "Apple"
the %s is a 'format character', indicating "insert a string here". The extra parameters after the string in your two function calls are the values to fill into the format character placeholders: In the first example, %s will be replaced with the contents of the command variable.
%s specifically is used to perform concatenation of strings together. It allows us to format a value inside a string.
%d: Specifies Decimal integer. %c: Specifies character. %T or %t: Specifies Time and date. %n: Inserts newline character.
I recently discovered JUEL which fits the description nicely. It is the expression language taken out of JSP. It claims to be very fast, too.
I'm about to try it out in one of my own projects.
But for a lighter-weight, which is a variant of yours, try this (wrapped in a unit test):
public class TestInterpolation {
public static class NamedFormatter {
public final static Pattern pattern = Pattern.compile("#\\{(?<key>.*)}");
public static String format(final String format, Map<String, ? extends Object> kvs) {
final StringBuffer buffer = new StringBuffer();
final Matcher match = pattern.matcher(format);
while (match.find()) {
final String key = match.group("key");
final Object value = kvs.get(key);
if (value != null)
match.appendReplacement(buffer, value.toString());
else if (kvs.containsKey(key))
match.appendReplacement(buffer, "null");
else
match.appendReplacement(buffer, "");
}
match.appendTail(buffer);
return buffer.toString();
}
}
@Test
public void test() {
assertEquals("hello world", NamedFormatter.format("hello #{name}", map("name", "world")));
assertEquals("hello null", NamedFormatter.format("hello #{name}", map("name", null)));
assertEquals("hello ", NamedFormatter.format("hello #{name}", new HashMap<String, Object>()));
}
private Map<String, Object> map(final String key, final Object value) {
final Map<String, Object> kvs = new HashMap<>();
kvs.put(key, value);
return kvs;
}
}
I'd extend it to add convenience methods to for quick key-value pairs
format(format, key1, value1)
format(format, key1, value1, key2, value2)
format(format, key1, value1, key2, value2, key3, value3)
...
And it shouldn't be too hard to convert from java 7+ to java 6-
You might also try org.apache.commons.lang3.text.StrSubstitutor
if Java 7 is not an option. It does exactly what you want it to do. Whether it’s light-weight might depend on whether you use something else of commons-lang as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With