/** * Licensed under the Artistic License; you may not use this file * except in compliance with the License. * You may obtain a copy of the License at * * http://displaytag.sourceforge.net/license.html * * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ package org.displaytag.decorator; import java.text.MessageFormat; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.jsp.PageContext; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.displaytag.exception.DecoratorException; import org.displaytag.model.HeaderCell; import org.displaytag.model.TableModel; /** * A table decorator which adds rows with totals (for column with the "total" attribute set) and subtotals (grouping by * the column with a group="1" attribute). * @author Fabrizio Giustina * @version $Id: TotalTableDecorator.java 8904 2006-05-01 18:02:06 -0400 (Mon, 01 May 2006) charles $ */ public class TotalTableDecorator extends TableDecorator { /** * Logger. */ private static Log log = LogFactory.getLog(TotalTableDecorator.class); /** * total amount. */ private Map grandTotals = new HashMap(); /** * total amount for current group. */ private Map subTotals = new HashMap(); /** * Previous values needed for grouping. */ private Map previousValues = new HashMap(); /** * Name of the property used for grouping. */ private String groupPropertyName; /** * Label used for subtotals. Default: "{0} total". */ private String subtotalLabel = "{0} subtotal"; /** * Label used for totals. Default: "Total". */ private String totalLabel = "Total"; /** * Setter for <code>subtotalLabel</code>. * @param subtotalLabel The subtotalLabel to set. */ public void setSubtotalLabel(String subtotalLabel) { this.subtotalLabel = subtotalLabel; } /** * Setter for <code>totalLabel</code>. * @param totalLabel The totalLabel to set. */ public void setTotalLabel(String totalLabel) { this.totalLabel = totalLabel; } /** * @see org.displaytag.decorator.Decorator#init(PageContext, Object, TableModel) */ public void init(PageContext context, Object decorated, TableModel tableModel) { super.init(context, decorated, tableModel); // reset groupPropertyName = null; grandTotals.clear(); subTotals.clear(); previousValues.clear(); for (Iterator it = tableModel.getHeaderCellList().iterator(); it.hasNext();) { HeaderCell cell = (HeaderCell) it.next(); if (cell.getGroup() == 1) { groupPropertyName = cell.getBeanPropertyName(); } } } public String startRow() { String subtotalRow = null; if (groupPropertyName != null) { Object groupedPropertyValue = evaluate(groupPropertyName); Object previousGroupedPropertyValue = previousValues.get(groupPropertyName); // subtotals if (previousGroupedPropertyValue != null && !ObjectUtils.equals(previousGroupedPropertyValue, groupedPropertyValue)) { subtotalRow = createTotalRow(false); } previousValues.put(groupPropertyName, groupedPropertyValue); } for (Iterator it = tableModel.getHeaderCellList().iterator(); it.hasNext();) { HeaderCell cell = (HeaderCell) it.next(); if (cell.isTotaled()) { String totalPropertyName = cell.getBeanPropertyName(); Number amount = (Number) evaluate(totalPropertyName); Number previousSubTotal = (Number) subTotals.get(totalPropertyName); Number previousGrandTotals = (Number) grandTotals.get(totalPropertyName); subTotals.put(totalPropertyName, new Double((previousSubTotal != null ? previousSubTotal.doubleValue() : 0) + (amount != null ? amount.doubleValue() : 0))); grandTotals.put(totalPropertyName, new Double((previousGrandTotals != null ? previousGrandTotals .doubleValue() : 0) + (amount != null ? amount.doubleValue() : 0))); } } return subtotalRow; } /** * After every row completes we evaluate to see if we should be drawing a new total line and summing the results * from the previous group. * @return String */ public final String finishRow() { StringBuffer buffer = new StringBuffer(1000); // Grand totals... if (getViewIndex() == ((List) getDecoratedObject()).size() - 1) { if (groupPropertyName != null) { buffer.append(createTotalRow(false)); } buffer.append(createTotalRow(true)); } return buffer.toString(); } protected String createTotalRow(boolean grandTotal) { StringBuffer buffer = new StringBuffer(1000); buffer.append("\n<tr class=\"total\">"); //$NON-NLS-1$ List headerCells = tableModel.getHeaderCellList(); for (Iterator it = headerCells.iterator(); it.hasNext();) { HeaderCell cell = (HeaderCell) it.next(); String cssClass = ObjectUtils.toString(cell.getHtmlAttributes().get("class")); buffer.append("<td"); //$NON-NLS-1$ if (StringUtils.isNotEmpty(cssClass)) { buffer.append(" class=\""); //$NON-NLS-1$ buffer.append(cssClass); buffer.append("\""); //$NON-NLS-1$ } buffer.append(">"); //$NON-NLS-1$ if (cell.isTotaled()) { String totalPropertyName = cell.getBeanPropertyName(); Object total = grandTotal ? grandTotals.get(totalPropertyName) : subTotals.get(totalPropertyName); DisplaytagColumnDecorator[] decorators = cell.getColumnDecorators(); for (int j = 0; j < decorators.length; j++) { try { total = decorators[j].decorate(total, this.getPageContext(), tableModel.getMedia()); } catch (DecoratorException e) { log.warn(e.getMessage(), e); // ignore, use undecorated value for totals } } buffer.append(total); } else if (groupPropertyName != null && groupPropertyName.equals(cell.getBeanPropertyName())) { buffer.append(grandTotal ? totalLabel : MessageFormat.format(subtotalLabel, new Object[]{previousValues .get(groupPropertyName)})); } buffer.append("</td>"); //$NON-NLS-1$ } buffer.append("</tr>"); //$NON-NLS-1$ // reset subtotal this.subTotals.clear(); return buffer.toString(); } }