2013-05-08 6 views
8

In una data stringa come questoCome sostituire un segnaposto in una stringa con un modello SimpleDateFormat

".../uploads/${customer}/${dateTime('yyyyMMdd')}/report.pdf" 

ho bisogno di sostituire un customer e un timestamp yyyyMMdd.

Per sostituire il segnaposto customer, è possibile utilizzare lo StrSubstitutor da Apache Commons. Ma come sostituire il? Stiamo correndo in un ambiente di primavera, quindi forse Spring EL è un'opzione?

Il markup per i segnaposto non è corretto, è ok se un'altra libreria ha bisogno di modifiche sintattiche.

Questo piccolo test mostra il problema:

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); 
} 
+1

è quella data parte sempre l'ultima cartella nella struttura della directory? –

+0

No, in alcuni altri report è nel mezzo. O nel nome del file. Questo è solo un esempio. – d0x

risposta

24

Perché non usare MessageFormat invece?

String result = MessageFormat.format(".../uploads/{0}/{1,date,yyyyMMdd}/report.pdf", customer, date); 

O con String.format

String result = String.format(".../uploads/%1$s/%2$tY%2$tm%2$td/report.pdf", customer, date); 
+0

Mi piace il modo con variabili denominate. Perché la stringa proviene da un altro modulo. Non sanno in quale uso. Con 'MessageFormat' ho bisogno di comunicare l'indice 0 = cliente, 1 = data corrente, 2 = .... hm .... – d0x

+0

La formattazione non è un dettaglio di implementazione? Come è "esposto" ai tuoi clienti? Non è solo una chiamata al metodo con parametri digitati? – NilsH

8

Come NilsH suggerito MessageFormat è veramente bello per questo scopo. Per avere variabili chiamate si può nascondere dietro la MessageFormat 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; 
    } 
} 

E modificare il tuo 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); 
} 

Ho rimosso i generici e sostituire quelli con stringa. Anche isPlaceholder e getPlaceholderValue sono hardcoded e si aspetta la sintassi $ {value}.

Ma questa è solo l'idea per risolvere il tuo problema. Per fare ciò è possibile utilizzare i metodi da StrSubstitutor (è sufficiente utilizzare o fare FormattedStrSubstitutor extends StrSubstitutor).

Inoltre è possibile utilizzare per esempio $ d {valore} per la formattazione di data e $ pippo {valore} per la formattazione foo.

UPDATE

non poteva dormire senza la piena soluzione. È possibile aggiungere questo metodo per FormattedStrSubstitutor classe:

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; 
} 

tuo testcase è come la tua domanda (piccoli cambiamenti nel segnaposto):

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

stessa limitazione come prima; no generici e prefisso e suffisso di correzione per segnaposto.

Problemi correlati