package tags; import groovy.lang.Closure; import play.Play; import play.db.Model; import play.exceptions.TagInternalException; import play.exceptions.TemplateExecutionException; import play.i18n.Messages; import play.templates.FastTags; import play.templates.GroovyTemplate.ExecutableTemplate; import play.templates.TagContext; import java.io.PrintWriter; import java.rmi.UnexpectedException; import java.util.LinkedHashMap; import java.util.Map; import java.util.List; public class Table extends FastTags { public static void _table(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { // Retrieve the data final Iterable<?> data = (Iterable<?>) args.remove("arg"); if (data == null) { throw new TemplateExecutionException(template.template, fromLine, "Please specify the data to display", new TagInternalException("Please specifiy the data to display")); } // Ensure data are not empty if (!data.iterator().hasNext()) { out.println(Messages.get("table.nodata")); return; } TableContentPrinter contentPrinter; if (body == null) { // Automatically fill the table content with the models properties final Iterable<? extends Model> modelData = (Iterable<? extends Model>)data; final Class<? extends Model> clazz = modelData.iterator().next().getClass(); if (Model.class.isAssignableFrom(clazz)) { Map<String, String> properties; if (args.containsKey("columns")) { properties = (Map<String, String>)args.remove("columns"); } else { // Display all properties properties = new LinkedHashMap<String, String>(); for(Model.Property property : Model.Manager.factoryFor(clazz).listProperties()) { properties.put(property.name, Messages.get(property.name)); } } contentPrinter = new ModelPrinter(properties); } else { throw new TemplateExecutionException(template.template, fromLine, "Please use Play! models in the 'table' tag", new TagInternalException("Please use Play! models in the 'table' tag")); } } else { // Fill the table content with the execution of its body final String it = (String) args.remove("as"); if (it == null) { throw new TemplateExecutionException(template.template, fromLine, "Missing parameter 'as'", new TagInternalException("Missing parameter 'as'")); } contentPrinter = new TagPrinter(body, it); } // Handle the optional “rowClass” parameter final String rowClass = args.containsKey("rowClass") ? (String) args.remove("rowClass") : ""; // Interpret all remaining parameters as HTML attributes for the <table> tag printTag("table", args, out); // A first time for the header row printStartTr(rowClass, out); contentPrinter.printHead(out); printEndTr(out); // Then for each row int index = 1; for (Object row : data) { String parity = (index % 2) == 0 ? " even" : " odd"; printStartTr(rowClass + parity, out); contentPrinter.printRow(row, out); printEndTr(out); index++; } out.println("</table>"); } public static void _column(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { // Retrieve the table state (“head” or “content”) final String state = (String) TagContext.parent("table").data.get("dataview.state"); if (state == null) { throw new TemplateExecutionException(template.template, fromLine, "The 'column' tag should be used inside a 'table' tag", new TagInternalException("The 'column' tag should be used inside a 'table' tag")); } if (state.equals("head")) { // Display the label out.print("<th>"); if (args.containsKey("arg")) { out.print(args.remove("arg")); } out.println("</th>"); } else { // Display the content printTag("td", args, out); if (body != null) { body.call(); } out.println("</td>"); } } /** * Helper to print a tag with attributes * @param name Name of the tag, e.g. table or tr * @param attributes Map of name-value attributes * @param out Target */ private static void printTag(final String name, final Map<?, ?> attributes, final PrintWriter out) { StringBuilder tag = new StringBuilder("<").append(name); for (Map.Entry<?, ?> entry : attributes.entrySet()) { tag .append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\""); } tag.append(">"); out.println(tag); } private static void printStartTr(final String rowClass, final PrintWriter out) { if (rowClass.isEmpty()) { out.println("<tr>"); } else { out.println("<tr class=\"" + rowClass + "\">"); } } private static void printEndTr(final PrintWriter out) { out.println("</tr>"); } private interface TableContentPrinter { void printHead(final PrintWriter out); void printRow(final Object data, final PrintWriter out); } private static class TagPrinter implements TableContentPrinter { private Closure body; private String it; public TagPrinter(final Closure body, final String it) { this.it = it; this.body = body; } @Override public void printHead(final PrintWriter out) { TagContext.current().data.put("dataview.state", "head"); body.call(); TagContext.current().data.put("dataview.state", "content"); } @Override public void printRow(final Object data, final PrintWriter out) { // Provide a variable to the body and call it body.setProperty(it, data); body.call(); } } private static class ModelPrinter implements TableContentPrinter { private Map<String, String> properties; public ModelPrinter(final Map<String, String> properties) { this.properties = properties; } @Override public void printHead(final PrintWriter out) { for (String label : properties.values()) { out.print("<th>"); out.print(label); out.println("</th>"); } } @Override public void printRow(final Object data, final PrintWriter out) { Model model = (Model)data; for (String property : properties.keySet()) { out.print("<td>"); try { out.print(model.getClass().getField(property).get(model)); } catch (Throwable t) {} out.println("</td>"); } } } }