How to replace a placeholder in a String with a SimpleDateFormat Pattern
Solution 1
Why don't you use MessageFormat
instead?
String result = MessageFormat.format(".../uploads/{0}/{1,date,yyyyMMdd}/report.pdf", customer, date);
Or with String.format
String result = String.format(".../uploads/%1$s/%2$tY%2$tm%2$td/report.pdf", customer, date);
Solution 2
As NilsH suggested MessageFormat is really nice 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 have to adjust your testcase:
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 have removed generics and replace those with String. Also isPlaceholder
and getPlaceholderValue
is hardcoded and expect ${value} syntax.
But this is just just the idea to solve your problem. To do this can use the methods from StrSubstitutor
(just use is or make FormattedStrSubstitutor extends StrSubstitutor
).
Also you can use for example $d{value} for date formatting and $foo{value} for foo formatting.
UPDATE
Could not sleep without full solution. You can add this method to 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 testcase is like in your question (small changes in 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);
}
Same limitation as before; no generics and fix prefix and suffix for placeholder.
Comments
-
d0x almost 4 years
In a given String like this
".../uploads/${customer}/${dateTime('yyyyMMdd')}/report.pdf"
I need to replace a
customer
and ayyyyMMdd
timestamp.To replace the
customer
placeholder, I could use theStrSubstitutor
from Apache Commons. But how to replace theSimpleDateFormat
? We are running in a Spring enviourment, so maybeSpring EL
is an option?The markup for the placeholders is not fixed, it is ok if another library needs syntactical changes.
This small tests shows the problem:
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); String template = ".../uploads/${customer}/${dateTime('yyyyMMdd')}/report.pdf"; @Test public void shouldResolvePlaceholder() { final Map<String, String> model = new HashMap<String, String>(); model.put("customer", "Mr. Foobar"); final String filledTemplate = StrSubstitutor.replace(this.template, model); assertEquals(".../uploads/Mr. Foobar/" + this.formatter.format(new Date()) + "/report.pdf", filledTemplate); }
-
d0x almost 11 yearsI like they way with named variables. Because the String comes from another module. They don't know in which I use. With the
MessageFormat
I need to communicate Index 0 = customer, 1 = current Date, 2 = .... hm.... -
NilsH almost 11 yearsIsn't the formatting an implementation detail? How it it "exposed" to your customers? Isn't it just a method call with typed parameters?