Dans une chaîne donnée comme celle-ci
".../uploads/${customer}/${dateTime('yyyyMMdd')}/report.pdf"
Je dois remplacer un horodatage customer
et un yyyyMMdd
.
Pour remplacer l'espace réservé customer
, je pourrais utiliser la StrSubstitutor
d'Apache Commons. Mais comment remplacer le SimpleDateFormat
? Nous travaillons dans un environnement de printemps, alors peut-être que Spring EL
est une option?
Le balisage pour les espaces réservés n'est pas fixe, il est acceptable si une autre bibliothèque nécessite des modifications syntaxiques.
Ce petit test montre le problème:
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);
}
Pourquoi n'utilisez-vous pas MessageFormat
à la place?
String result = MessageFormat.format(".../uploads/{0}/{1,date,yyyyMMdd}/report.pdf", customer, date);
Ou avec String.format
String result = String.format(".../uploads/%1$s/%2$tY%2$tm%2$td/report.pdf", customer, date);
Comme NilsH l'a suggéré, MessageFormat est vraiment bien pour cela. Pour avoir des variables nommées, vous pouvez masquer MessageFormat derrière votre classe:
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;
}
}
Et vous devez ajuster votre test:
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);
}
J'ai supprimé les génériques et les remplace par String. De plus, isPlaceholder
et getPlaceholderValue
sont codés en dur et attendent $ {valeur} syntaxe.
Mais ce n’est que l’idée de résoudre votre problème. Pour ce faire, vous pouvez utiliser les méthodes de StrSubstitutor
(il suffit d'utiliser ou de créer FormattedStrSubstitutor extends StrSubstitutor
).
Vous pouvez aussi utiliser par exemple $ d {valeur} pour le formatage de la date et $ foo {valeur} pour le formatage foo.
METTRE &AGRAVE; JOUR
Ne pouvait pas dormir sans solution complète. Vous pouvez ajouter cette méthode à la classe FormattedStrSubstitutor
:
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;
}
Votre test est comme dans votre question (petits changements d’espace réservé):
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);
}
Même limitation qu'auparavant; pas de génériques et préfixe et suffixe de réparation pour espace réservé.
On dirait que c'est aussi simple que ça?
static final Pattern DOLLARS = Pattern.compile("\\$\\{([^}]+)}");
public static String resolve(String string, Map<String,String> config) {
StringBuilder builder = new StringBuilder();
Matcher matcher = DOLLARS.matcher(string);
int start = 0;
while (matcher.find(start)) {
builder.append(string.substring(start, matcher.start()));
String property = matcher.group(1);
String value = config.get(property);
builder.append(value);
start = matcher.end();
}
builder.append(string.substring(start));
return builder.toString();
}