2010-04-28 14 views
6

Attenzione avanzata FreeMarker guru:Utilizzare un unico modello freemarker per visualizzare le tabelle di POJO arbitrarie

voglio usare un singolo modello freemarker essere in grado di tabelle di output di POJO arbitrarie, con le colonne da visualizzare definite separatamente rispetto alla dati. Il problema è che non riesco a capire come ottenere un handle su una funzione su un pojo in fase di runtime, e quindi fare in modo che freemarker invochi quella funzione (stile lambda). Dalla scrematura dei documenti sembra che Freemarker supporti la programmazione funzionale, ma non riesco a trovare il giusto incantesimo.

Ho inventato un esempio concreto semplicistico. Diciamo che ho due elenchi: un elenco di persone con un nome e cognome e un elenco di automobili con marca e modello. vorrebbe uscita queste due tabelle:

<table> 
    <tr> 
    <th>firstName</th> 
    <th>lastName</th> 
    </tr> 
    <tr> 
    <td>Joe</td> 
    <td>Blow</d> 
    </tr> 
    <tr> 
    <td>Mary</td> 
    <td>Jane</d> 
    </tr> 
</table> 

e

<table> 
    <tr> 
    <th>make</th> 
    <th>model</th> 
    </tr> 
    <tr> 
    <td>Toyota</td> 
    <td>Tundra</d> 
    </tr> 
    <tr> 
    <td>Honda</td> 
    <td>Odyssey</d> 
    </tr> 
</table> 

ma voglio utilizzare lo stesso modello, dal momento che questo è parte di un quadro che ha a che fare con decine di diversi tipi POJO.

Dato il seguente codice:

public class FreemarkerTest { 

    public static class Table { 
    private final List<Column> columns = new ArrayList<Column>(); 

    public Table(Column[] columns) { 
     this.columns.addAll(Arrays.asList(columns)); 
    } 

    public List<Column> getColumns() { 
     return columns; 
    } 

    } 

    public static class Column { 
    private final String name; 

    public Column(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 
    } 

    public static class Person { 
    private final String firstName; 
    private final String lastName; 

    public Person(String firstName, String lastName) { 
     this.firstName = firstName; 
     this.lastName = lastName; 
    } 

    public String getFirstName() { 
     return firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 
    } 

    public static class Car { 
    String make; 
    String model; 

    public Car(String make, String model) { 
     this.make = make; 
     this.model = model; 
    } 

    public String getMake() { 
     return make; 
    } 

    public String getModel() { 
     return model; 
    } 
    } 

    public static void main(String[] args) throws Exception { 
    final Table personTableDefinition = new Table(new Column[] { new Column("firstName"), new Column("lastName") }); 
    final List<Person> people = Arrays.asList(new Person[] { new Person("Joe", "Blow"), new Person("Mary", "Jane") }); 
    final Table carTable = new Table(new Column[] { new Column("make"), new Column("model") }); 
    final List<Car> cars = Arrays.asList(new Car[] { new Car("Toyota", "Tundra"), new Car("Honda", "Odyssey") }); 

    final Configuration cfg = new Configuration(); 
    cfg.setClassForTemplateLoading(FreemarkerTest.class, ""); 
    cfg.setObjectWrapper(new DefaultObjectWrapper()); 
    final Template template = cfg.getTemplate("test.ftl"); 

    process(template, personTableDefinition, people); 
    process(template, carTable, cars); 
    } 

    private static void process(Template template, Table tableDefinition, List<? extends Object> data) throws Exception { 
    final Map<String, Object> dataMap = new HashMap<String, Object>(); 
    dataMap.put("tableDefinition", tableDefinition); 
    dataMap.put("data", data); 
    final Writer out = new OutputStreamWriter(System.out); 
    template.process(dataMap, out); 
    out.flush(); 
    } 

} 

Tutto questo è un dato di fatto per questo problema. Quindi, ecco il modello su cui ho hackerato. Nota il commento dove sto avendo problemi.

<table> 
    <tr> 
<#list tableDefinition.columns as col> 
    <th>${col.name}</th> 
</#list> 
    </tr> 
<#list data as pojo> 
    <tr> 
<#list tableDefinition.columns as col> 
    <td><#-- what goes here? --></td>  
</#list> 
    </tr> 
</#list> 
</table> 

Così col.name ha il nome della proprietà che voglio accedere dal POJO. Ho provato un paio di cose, come

pojo.col.name 

e

<#assign property = col.name/> 
${pojo.property} 

ma naturalmente queste non funzionano, ho solo incluso per aiutare a trasmettere il mio intento. Sto cercando un modo per ottenere un handle per una funzione e avere un freemarker per invocarlo, o forse una sorta di funzionalità di "valutazione" che può assumere un'espressione arbitraria come stringa e valutarla in fase di runtime.

risposta

2

Trovato la risposta.

${("pojo." + col.name)?eval} 
5

?eval è (quasi?) Sempre una cattiva idea, perché viene spesso con svantaggi di prestazione (ad esempio un sacco di parsing) e problemi di sicurezza (ad esempio, "FTL iniezione").

Un approccio migliore sta usando la sintassi parentesi quadra:

C'è una sintassi alternativa se vogliamo specificare il nome subvariable con un'espressione: libro [ "title"]. Nelle parentesi quadre puoi dare qualsiasi espressione purché valuti una stringa.

(Dal FreeMarker documentation about retrieving data from a hash)

Nel tuo caso io consiglierei qualcosa come ${pojo[col.name]}.

Problemi correlati