web-dev-qa-db-fra.com

Comment remplacer un espace réservé dans une chaîne avec un modèle SimpleDateFormat

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);
}
10
d0x

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);
27
NilsH

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é.

9
Walery Strauch

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();
}
0
anydoby