Because NilsH suggested that MessageFormat is really good for this purpose. To have named variables, you can hide MessageFormat behind your class:
public class FormattedStrSubstitutor { public static String formatReplace(Object source, Map<String, String> valueMap) { for (Map.Entry<String, String> entry : valueMap.entrySet()) { String val = entry.getValue(); if (isPlaceholder(val)) { val = getPlaceholderValue(val); String newValue = reformat(val); entry.setValue(newValue); } } return new StrSubstitutor(valueMap).replace(source); } private static boolean isPlaceholder(String isPlaceholder) { return isPlaceholder.startsWith("${"); } private static String getPlaceholderValue(String val) { return val.substring(2, val.length()-1); } private static String reformat(String format) { String result = MessageFormat.format("{0,date," + format + "}", new Date()); return result; } }
And you should set up your test file:
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); String template = ".../uploads/${customer}/${dateTime}/report.pdf"; @Test public void shouldResolvePlaceholder() { final Map<String, String> model = new HashMap<String, String>(); model.put("customer", "Mr. Foobar"); model.put("dateTime", "${yyyyMMdd}"); final String filledTemplate = FormattedStrSubstitutor.formatReplace(this.template, model); assertEquals(".../uploads/Mr. Foobar/" + this.formatter.format(new Date()) + "/report.pdf", filledTemplate); }
I removed generics and replaced them with String. Also, isPlaceholder and getPlaceholderValue hard-coded and expect $ {value} syntax.
But it is just an idea to solve your problem. You can use the methods from StrSubstitutor (just use is or make FormattedStrSubstitutor extends StrSubstitutor ).
You can also use, for example, $ d {value} to format the date and $ foo {value} to format foo.
UPDATE
Cannot sleep without a complete solution. You can add this method to the FormattedStrSubstitutor class:
public static String replace(Object source, Map<String, String> valueMap) { String staticResolved = new StrSubstitutor(valueMap).replace(source); Pattern p = Pattern.compile("(\\$\\{date)(.*?)(\\})"); Matcher m = p.matcher(staticResolved); String dynamicResolved = staticResolved; while (m.find()) { String result = MessageFormat.format("{0,date" + m.group(2) + "}", new Date()); dynamicResolved = dynamicResolved.replace(m.group(), result); } return dynamicResolved; }
Your test case is similar to your question (small changes in the placeholder):
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); String template = ".../uploads/${customer}/${date,yyyyMMdd}/report.pdf"; @Test public void shouldResolvePlaceholder() { final Map<String, String> model = new HashMap<String, String>(); model.put("customer", "Mr. Foobar"); final String filledTemplate = FormattedStrSubstitutor.replace(this.template, model); assertEquals( ".../uploads/Mr. Foobar/" + this.formatter.format(new Date()) + "/report.pdf", filledTemplate); }
The same restriction as before; no generics and no fix for prefix and suffix for placeholder.