KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > displaytag > decorator > MultilevelTotalTableDecorator


1 /**
2  * Licensed under the Artistic License; you may not use this file
3  * except in compliance with the License.
4  * You may obtain a copy of the License at
5  *
6  * http://displaytag.sourceforge.net/license.html
7  *
8  * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */

12 package org.displaytag.decorator;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.Map JavaDoc;
19
20 import javax.servlet.jsp.PageContext JavaDoc;
21
22 import org.apache.commons.lang.ObjectUtils;
23 import org.apache.commons.lang.StringUtils;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.displaytag.exception.DecoratorException;
27 import org.displaytag.exception.ObjectLookupException;
28 import org.displaytag.model.Column;
29 import org.displaytag.model.ColumnIterator;
30 import org.displaytag.model.HeaderCell;
31 import org.displaytag.model.Row;
32 import org.displaytag.model.TableModel;
33 import org.displaytag.util.TagConstants;
34
35
36 /**
37  * A TableDecorator that, in conjunction with totaled and grouped columns, produces multi level subtotals on arbitrary
38  * String groupings.
39  * @author rapruitt
40  * @author Fabrizio Giustina
41  */

42 public class MultilevelTotalTableDecorator extends TableDecorator
43 {
44
45     /**
46      * No current reset group.
47      */

48     private static final int NO_RESET_GROUP = 4200;
49
50     /**
51      * Controls when the subgroup is ended.
52      */

53     protected int innermostGroup;
54
55     /**
56      * Logger.
57      */

58     private Log logger = LogFactory.getLog(MultilevelTotalTableDecorator.class);
59
60     /**
61      * CSS class applied to grand totals.
62      */

63     private String JavaDoc grandTotalLabel = "grandtotal-sum";
64
65     /**
66      * CSS class appplied to subtotal headers.
67      */

68     private String JavaDoc subtotalHeaderClass = "subtotal-header";
69
70     /**
71      * CSS class applied to subtotal labels.
72      */

73     private String JavaDoc subtotalLabelClass = "subtotal-label";
74
75     /**
76      * CSS class applied to subtotal totals.
77      */

78     private String JavaDoc subtotalValueClass = "subtotal-sum";
79
80     /**
81      * Maps the groups to their current totals.
82      */

83     private Map JavaDoc groupNumberToGroupTotal = new HashMap JavaDoc();
84
85     /**
86      * The deepest reset group.
87      */

88     private int deepestResetGroup = NO_RESET_GROUP;
89
90     /**
91      * Holds the header rows and their content for a particular group.
92      */

93     private List JavaDoc headerRows = new ArrayList JavaDoc(5);
94
95     public void init(PageContext JavaDoc context, Object JavaDoc decorated, TableModel model)
96     {
97         super.init(context, decorated, model);
98         List JavaDoc headerCells = model.getHeaderCellList();
99         // go through each column, looking for grouped columns; add them to the group number map
100
for (Iterator JavaDoc iterator = headerCells.iterator(); iterator.hasNext();)
101         {
102             HeaderCell headerCell = (HeaderCell) iterator.next();
103             if (headerCell.getGroup() > 0)
104             {
105                 groupNumberToGroupTotal.put(new Integer JavaDoc(headerCell.getGroup()), new GroupTotals(headerCell
106                     .getColumnNumber()));
107                 if (headerCell.getGroup() > innermostGroup)
108                 {
109                     innermostGroup = headerCell.getGroup();
110                 }
111             }
112         }
113     }
114
115     public String JavaDoc getGrandTotalLabel()
116     {
117         return grandTotalLabel;
118     }
119
120     public void setGrandTotalLabel(String JavaDoc grandTotalLabel)
121     {
122         this.grandTotalLabel = grandTotalLabel;
123     }
124
125     public String JavaDoc getSubtotalValueClass()
126     {
127         return subtotalValueClass;
128     }
129
130     public void setSubtotalValueClass(String JavaDoc subtotalValueClass)
131     {
132         this.subtotalValueClass = subtotalValueClass;
133     }
134
135     public String JavaDoc getSubtotalLabelClass()
136     {
137         return subtotalLabelClass;
138     }
139
140     public void setSubtotalLabelClass(String JavaDoc subtotalLabelClass)
141     {
142         this.subtotalLabelClass = subtotalLabelClass;
143     }
144
145     public String JavaDoc getSubtotalHeaderClass()
146     {
147         return subtotalHeaderClass;
148     }
149
150     public void setSubtotalHeaderClass(String JavaDoc subtotalHeaderClass)
151     {
152         this.subtotalHeaderClass = subtotalHeaderClass;
153     }
154
155     public void startOfGroup(String JavaDoc value, int group)
156     {
157         StringBuffer JavaDoc tr = new StringBuffer JavaDoc();
158         tr.append("<tr>");
159         for (int i = 1; i < group; i++)
160         {
161             tr.append("<td></td>\n");
162         }
163         tr.append("<td class=\"").append(getSubtotalHeaderClass()).append(" group-").append(group).append("\">");
164         tr.append(value).append("</td>\n");
165         tr.append("</tr>");
166         headerRows.add(tr);
167     }
168
169     public String JavaDoc displayGroupedValue(String JavaDoc value, short groupingStatus)
170     {
171         return "";
172     }
173
174     public String JavaDoc startRow()
175     {
176         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
177         for (Iterator JavaDoc iterator = headerRows.iterator(); iterator.hasNext();)
178         {
179             StringBuffer JavaDoc stringBuffer = (StringBuffer JavaDoc) iterator.next();
180             sb.append(stringBuffer);
181         }
182         return sb.toString();
183     }
184
185     public void endOfGroup(String JavaDoc value, int groupNumber)
186     {
187         if (deepestResetGroup > groupNumber)
188         {
189             deepestResetGroup = groupNumber;
190         }
191     }
192
193     public String JavaDoc finishRow()
194     {
195         String JavaDoc returnValue;
196         if (innermostGroup > 0 && deepestResetGroup != NO_RESET_GROUP)
197         {
198             StringBuffer JavaDoc out = new StringBuffer JavaDoc();
199             // Starting with the deepest group, print the current total and reset. Do not reset unaffected groups.
200
for (int i = innermostGroup; i >= deepestResetGroup; i--)
201             {
202                 Integer JavaDoc groupNumber = new Integer JavaDoc(i);
203
204                 GroupTotals totals = (GroupTotals) groupNumberToGroupTotal.get(groupNumber);
205                 if (totals == null)
206                 {
207                     logger.warn("There is a gap in the defined groups - no group defined for " + groupNumber);
208                     continue;
209                 }
210                 totals.printTotals(getListIndex(), out);
211                 totals.setStartRow(getListIndex() + 1);
212             }
213             returnValue = out.toString();
214         }
215         else
216         {
217             returnValue = null;
218         }
219         deepestResetGroup = NO_RESET_GROUP;
220         headerRows.clear();
221         if (isLastRow())
222         {
223             returnValue = StringUtils.defaultString(returnValue);
224             returnValue += totalAllRows();
225         }
226         return returnValue;
227     }
228
229     /**
230      * Issue a grand total row at the bottom.
231      * @return the suitable string
232      */

233     protected String JavaDoc totalAllRows()
234     {
235         GroupTotals grandTotal = new GroupTotals(-1);
236         StringBuffer JavaDoc out = new StringBuffer JavaDoc();
237         grandTotal.setStartRow(0);
238         grandTotal.setTotalValueClass(getGrandTotalLabel());
239         grandTotal.printTotals(getListIndex(), out);
240         return out.toString();
241     }
242
243     protected String JavaDoc getCellValue(int columnNumber, int rowNumber)
244     {
245         List JavaDoc fullList = tableModel.getRowListFull();
246         Row row = (Row) fullList.get(rowNumber);
247         ColumnIterator columnIterator = row.getColumnIterator(tableModel.getHeaderCellList());
248         while (columnIterator.hasNext())
249         {
250             Column column = columnIterator.nextColumn();
251             if (column.getHeaderCell().getColumnNumber() == columnNumber)
252             {
253                 try
254                 {
255                     column.initialize();
256                     return column.getChoppedAndLinkedValue();
257                 }
258                 catch (ObjectLookupException e)
259                 {
260                     logger.error("Error: " + e.getMessage(), e);
261                     throw new RuntimeException JavaDoc("Error: " + e.getMessage(), e);
262                 }
263                 catch (DecoratorException e)
264                 {
265                     logger.error("Error: " + e.getMessage(), e);
266                     throw new RuntimeException JavaDoc("Error: " + e.getMessage(), e);
267                 }
268             }
269         }
270         throw new RuntimeException JavaDoc("Unable to find column " + columnNumber + " in the list of columns");
271     }
272
273     protected double getTotalForColumn(int columnNumber, int startRow, int stopRow)
274     {
275         List JavaDoc fullList = tableModel.getRowListFull();
276         List JavaDoc window = fullList.subList(startRow, stopRow + 1);
277         double total = 0;
278         for (Iterator JavaDoc iterator = window.iterator(); iterator.hasNext();)
279         {
280             Row row = (Row) iterator.next();
281             ColumnIterator columnIterator = row.getColumnIterator(tableModel.getHeaderCellList());
282             while (columnIterator.hasNext())
283             {
284                 Column column = columnIterator.nextColumn();
285                 if (column.getHeaderCell().getColumnNumber() == columnNumber)
286                 {
287                     Number JavaDoc value = null;
288                     try
289                     {
290                         value = (Number JavaDoc) column.getValue(false);
291                     }
292                     catch (ObjectLookupException e)
293                     {
294                         logger.error(e);
295                     }
296                     catch (DecoratorException e)
297                     {
298                         logger.error(e);
299                     }
300                     if (value != null)
301                     {
302                         total += value.doubleValue();
303                     }
304                 }
305             }
306         }
307         return total;
308     }
309
310     public String JavaDoc getTotalsTdOpen(HeaderCell header, String JavaDoc totalClass)
311     {
312
313         String JavaDoc cssClass = ObjectUtils.toString(header.getHtmlAttributes().get("class"));
314
315         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
316         buffer.append(TagConstants.TAG_OPEN);
317         buffer.append(TagConstants.TAGNAME_COLUMN);
318         if (cssClass != null || totalClass != null)
319         {
320             buffer.append(" class=\"");
321
322             if (cssClass != null)
323             {
324                 buffer.append(cssClass);
325                 if (totalClass != null)
326                 {
327                     buffer.append(" ");
328                 }
329             }
330             if (totalClass != null)
331             {
332                 buffer.append(totalClass);
333             }
334             buffer.append("\"");
335         }
336         buffer.append(TagConstants.TAG_CLOSE);
337         return buffer.toString();
338     }
339
340     public String JavaDoc getTotalsRowOpen()
341     {
342         return TagConstants.TAG_OPEN + TagConstants.TAGNAME_ROW + " class=\"subtotal\"" + TagConstants.TAG_CLOSE;
343     }
344
345     public String JavaDoc getTotalRowLabel(String JavaDoc groupingValue)
346     {
347         return groupingValue + " Total";
348     }
349
350     public String JavaDoc formatTotal(HeaderCell header, double total)
351     {
352         Object JavaDoc displayValue = new Double JavaDoc(total);
353         if (header.getColumnDecorators().length > 0)
354         {
355             for (int i = 0; i < header.getColumnDecorators().length; i++)
356             {
357                 DisplaytagColumnDecorator decorator = header.getColumnDecorators()[i];
358                 try
359                 {
360                     displayValue = decorator.decorate(displayValue, this.getPageContext(), tableModel.getMedia());
361                 }
362                 catch (DecoratorException e)
363                 {
364                     logger.warn(e.getMessage(), e);
365                     // ignore, use undecorated value for totals
366
}
367             }
368         }
369         return displayValue.toString();
370     }
371
372     class GroupTotals
373     {
374
375         /**
376          * The label class.
377          */

378         protected String JavaDoc totalLabelClass = getSubtotalLabelClass();
379
380         /**
381          * The value class.
382          */

383         protected String JavaDoc totalValueClass = getSubtotalValueClass();
384
385         private int columnNumber;
386
387         private int firstRowOfCurrentSet;
388
389         public GroupTotals(int headerCellColumn)
390         {
391             this.columnNumber = headerCellColumn;
392             this.firstRowOfCurrentSet = 0;
393         }
394
395         public void printTotals(int currentRow, StringBuffer JavaDoc out)
396         {
397
398             // For each column, output:
399
List JavaDoc headerCells = tableModel.getHeaderCellList();
400             if (firstRowOfCurrentSet < currentRow) // If there is more than one row, show a total
401
{
402                 out.append(getTotalsRowOpen());
403                 for (Iterator JavaDoc iterator = headerCells.iterator(); iterator.hasNext();)
404                 {
405                     HeaderCell headerCell = (HeaderCell) iterator.next();
406
407                     if (columnNumber == headerCell.getColumnNumber())
408                     {
409                         // a totals label if it is the column for the current group
410
String JavaDoc currentLabel = getCellValue(columnNumber, firstRowOfCurrentSet);
411                         out.append(getTotalsTdOpen(headerCell, getTotalLabelClass() + " group-" + (columnNumber + 1)));
412                         out.append(getTotalRowLabel(currentLabel));
413                     }
414                     else if (headerCell.isTotaled())
415                     {
416                         // a total if the column should be totaled
417
double total = getTotalForColumn(headerCell.getColumnNumber(), firstRowOfCurrentSet, currentRow);
418                         out.append(getTotalsTdOpen(headerCell, getTotalValueClass() + " group-" + (columnNumber + 1)));
419                         out.append(formatTotal(headerCell, total));
420                     }
421                     else
422                     {
423                         // blank, if it is not a totals column
424
String JavaDoc style = "group-" + (columnNumber + 1);
425                         if (headerCell.getColumnNumber() < innermostGroup)
426                         {
427                             style += " " + getTotalLabelClass() + " ";
428                         }
429                         out.append(getTotalsTdOpen(headerCell, style));
430                     }
431                     out.append(TagConstants.TAG_OPENCLOSING + TagConstants.TAGNAME_COLUMN + TagConstants.TAG_CLOSE);
432                 }
433                 out.append("</tr>\n");
434             }
435         }
436
437         public void setStartRow(int i)
438         {
439             firstRowOfCurrentSet = i;
440         }
441
442         public String JavaDoc getTotalLabelClass()
443         {
444             return totalLabelClass;
445         }
446
447         public void setTotalLabelClass(String JavaDoc totalLabelClass)
448         {
449             this.totalLabelClass = totalLabelClass;
450         }
451
452         public String JavaDoc getTotalValueClass()
453         {
454             return totalValueClass;
455         }
456
457         public void setTotalValueClass(String JavaDoc totalValueClass)
458         {
459             this.totalValueClass = totalValueClass;
460         }
461     }
462 }
463
Popular Tags