KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > displaytag > render > TableWriterTemplate


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.render;
13
14 import java.text.MessageFormat JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.Map JavaDoc;
19
20 import javax.servlet.jsp.JspException JavaDoc;
21
22 import org.apache.commons.lang.ObjectUtils;
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.displaytag.decorator.TableDecorator;
26 import org.displaytag.model.Column;
27 import org.displaytag.model.ColumnIterator;
28 import org.displaytag.model.HeaderCell;
29 import org.displaytag.model.Row;
30 import org.displaytag.model.RowIterator;
31 import org.displaytag.model.TableModel;
32 import org.displaytag.properties.TableProperties;
33 import org.displaytag.util.TagConstants;
34
35
36 /**
37  * A template that encapsulates and drives the construction of a table based on a given table model and configuration.
38  * This class is meant to be extended by classes that build tables sharing the same structure, sorting, and grouping,
39  * but that write them in different formats and to various destinations. Subclasses must provide the format- and
40  * destination-specific implementations of the abstract methods this class calls to build a table. (Background: This
41  * class came about because our users wanted to export tables to Excel and PDF just as they were presented in HTML. It
42  * originates with the TableTagData.writeHTMLData method, factoring its logic so that it can be re-used by classes that
43  * write the tables as PDF, Excel, RTF and other formats. TableTagData.writeHTMLData now calls an HTML extension of this
44  * class to write tables in HTML format to a JSP page.)
45  * @author Jorge L. Barroso
46  * @version $Id$
47  */

48 public abstract class TableWriterTemplate
49 {
50
51     public static final short GROUP_START = -2;
52
53     public static final short GROUP_END = 5;
54
55     public static final short GROUP_START_AND_END = 3;
56
57     public static final short GROUP_NO_CHANGE = 0;
58
59     /**
60      * logger.
61      */

62     private static Log log = LogFactory.getLog(TableWriterTemplate.class);
63
64     /**
65      * Table unique id.
66      */

67     private String JavaDoc id;
68
69     /**
70      * Given a table model, this method creates a table, sorting and grouping it per its configuration, while delegating
71      * where and how it writes the table to subclass objects. (Background: This method refactors
72      * TableTagData.writeHTMLData method. See above.)
73      * @param model The table model used to build the table.
74      * @param id This table's page id.
75      * @throws JspException if any exception thrown while constructing the tablei, it is caught and rethrown as a
76      * JspException. Extension classes may throw all sorts of exceptions, depending on their respective formats and
77      * destinations.
78      */

79     public void writeTable(TableModel model, String JavaDoc id) throws JspException JavaDoc
80     {
81         try
82         {
83             // table id used for logging
84
this.id = id;
85
86             TableProperties properties = model.getProperties();
87
88             if (log.isDebugEnabled())
89             {
90                 log.debug("[" + this.id + "] writeTable called for table [" + this.id + "]");
91             }
92
93             // Handle empty table
94
boolean noItems = model.getRowListPage().size() == 0;
95             if (noItems && !properties.getEmptyListShowTable())
96             {
97                 writeEmptyListMessage(properties.getEmptyListMessage());
98                 return;
99             }
100
101             // Put the page stuff there if it needs to be there...
102
if (properties.getAddPagingBannerTop())
103             {
104                 // search result and navigation bar
105
writeTopBanner(model);
106             }
107
108             // open table
109
writeTableOpener(model);
110
111             // render caption
112
if (model.getCaption() != null)
113             {
114                 writeCaption(model);
115             }
116
117             // render headers
118
if (model.getProperties().getShowHeader())
119             {
120                 writeTableHeader(model);
121             }
122
123             // render footer prior to body
124
if (model.getFooter() != null)
125             {
126                 writePreBodyFooter(model);
127             }
128
129             // open table body
130
writeTableBodyOpener(model);
131
132             // render table body
133
writeTableBody(model);
134
135             // close table body
136
writeTableBodyCloser(model);
137
138             // render footer after body
139
if (model.getFooter() != null)
140             {
141                 writePostBodyFooter(model);
142             }
143
144             // close table
145
writeTableCloser(model);
146
147             if (model.getTableDecorator() != null)
148             {
149                 writeDecoratedTableFinish(model);
150             }
151
152             writeBottomBanner(model);
153
154             if (log.isDebugEnabled())
155             {
156                 log.debug("[" + this.id + "] writeTable end");
157             }
158         }
159         catch (Exception JavaDoc e)
160         {
161             throw new JspException JavaDoc(e);
162         }
163     }
164
165     /**
166      * Called by writeTable to write a message explaining that the table model contains no data.
167      * @param emptyListMessage A message explaining that the table model contains no data.
168      * @throws Exception if it encounters an error while writing.
169      */

170     protected abstract void writeEmptyListMessage(String JavaDoc emptyListMessage) throws Exception JavaDoc;
171
172     /**
173      * Called by writeTable to write a summary of the search result this table reports and the table's pagination
174      * interface.
175      * @param model The table model for which the banner is written.
176      * @throws Exception if it encounters an error while writing.
177      */

178     protected abstract void writeTopBanner(TableModel model) throws Exception JavaDoc;
179
180     /**
181      * Called by writeTable to write the start of the table structure.
182      * @param model The table model for which the content is written.
183      * @throws Exception if it encounters an error while writing.
184      */

185     protected abstract void writeTableOpener(TableModel model) throws Exception JavaDoc;
186
187     /**
188      * Called by writeTable to write the table's caption.
189      * @param model The table model for which the content is written.
190      * @throws Exception if it encounters an error while writing.
191      */

192     protected abstract void writeCaption(TableModel model) throws Exception JavaDoc;
193
194     /**
195      * Called by writeTable to write the table's header columns.
196      * @param model The table model for which the content is written.
197      * @throws Exception if it encounters an error while writing.
198      */

199     protected abstract void writeTableHeader(TableModel model) throws Exception JavaDoc;
200
201     /**
202      * Called by writeTable to write table footer before table body.
203      * @param model The table model for which the content is written.
204      * @throws Exception if it encounters an error while writing.
205      */

206     protected abstract void writePreBodyFooter(TableModel model) throws Exception JavaDoc;
207
208     /**
209      * Called by writeTable to write the start of the table's body.
210      * @param model The table model for which the content is written.
211      * @throws Exception if it encounters an error while writing.
212      */

213     protected abstract void writeTableBodyOpener(TableModel model) throws Exception JavaDoc;
214
215     // protected abstract void writeTableBody(TableModel model);
216

217     /**
218      * Called by writeTable to write the end of the table's body.
219      * @param model The table model for which the content is written.
220      * @throws Exception if it encounters an error while writing.
221      */

222     protected abstract void writeTableBodyCloser(TableModel model) throws Exception JavaDoc;
223
224     /**
225      * Called by writeTable to write table footer after table body.
226      * @param model The table model for which the content is written.
227      * @throws Exception if it encounters an error while writing.
228      */

229     protected abstract void writePostBodyFooter(TableModel model) throws Exception JavaDoc;
230
231     /**
232      * Called by writeTable to write the end of the table's structure.
233      * @param model The table model for which the content is written.
234      * @throws Exception if it encounters an error while writing.
235      */

236     protected abstract void writeTableCloser(TableModel model) throws Exception JavaDoc;
237
238     /**
239      * Called by writeTable to decorate the table.
240      * @param model The table model for which the content is written.
241      * @throws Exception if it encounters an error while writing.
242      */

243     protected abstract void writeDecoratedTableFinish(TableModel model) throws Exception JavaDoc;
244
245     /**
246      * Called by writeTable to write the table's footer.
247      * @param model The table model for which the content is written.
248      * @throws Exception if it encounters an error while writing.
249      */

250     protected abstract void writeBottomBanner(TableModel model) throws Exception JavaDoc;
251
252     /**
253      * Given a table model, writes the table body content, sorting and grouping it per its configuration, while
254      * delegating where and how it writes to subclass objects. (Background: This method refactors
255      * TableTagData.writeTableBody method. See above.)
256      * @param model The table model used to build the table body.
257      * @throws Exception if an error is encountered while writing the table body.
258      */

259     private void writeTableBody(TableModel model) throws Exception JavaDoc
260     {
261         // Ok, start bouncing through our list (only the visible part)
262
RowIterator rowIterator = model.getRowIterator(false);
263
264         // iterator on rows
265
TableDecorator tableDecorator = model.getTableDecorator();
266         Row previousRow = null;
267         Row currentRow = null;
268         Row nextRow = null;
269         Map JavaDoc previousRowValues = new HashMap JavaDoc(10);
270         Map JavaDoc currentRowValues = new HashMap JavaDoc(10);
271         Map JavaDoc nextRowValues = new HashMap JavaDoc(10);
272
273         while (nextRow != null || rowIterator.hasNext())
274         {
275             // The first pass
276
if (currentRow == null)
277             {
278                 currentRow = rowIterator.next();
279             }
280             else
281             {
282                 previousRow = currentRow;
283                 currentRow = nextRow;
284             }
285
286             if (previousRow != null)
287             {
288                 previousRowValues.putAll(currentRowValues);
289             }
290             if (!nextRowValues.isEmpty())
291             {
292                 currentRowValues.putAll(nextRowValues);
293             }
294             // handle the first pass
295
else
296             {
297                 ColumnIterator columnIterator = currentRow.getColumnIterator(model.getHeaderCellList());
298                 // iterator on columns
299
if (log.isDebugEnabled())
300                 {
301                     log.debug(" creating ColumnIterator on " + model.getHeaderCellList());
302                 }
303                 while (columnIterator.hasNext())
304                 {
305                     Column column = columnIterator.nextColumn();
306
307                     // Get the value to be displayed for the column
308
column.initialize();
309                     CellStruct struct = new CellStruct(column, column.getChoppedAndLinkedValue());
310                     currentRowValues.put(new Integer JavaDoc(column.getHeaderCell().getColumnNumber()), struct);
311                 }
312             }
313
314             nextRowValues.clear();
315             // Populate the next row values
316
nextRow = rowIterator.hasNext() ? rowIterator.next() : null;
317             if (nextRow != null)
318             {
319                 ColumnIterator columnIterator = nextRow.getColumnIterator(model.getHeaderCellList());
320                 // iterator on columns
321
if (log.isDebugEnabled())
322                 {
323                     log.debug(" creating ColumnIterator on " + model.getHeaderCellList());
324                 }
325                 while (columnIterator.hasNext())
326                 {
327                     Column column = columnIterator.nextColumn();
328                     column.initialize();
329                     // Get the value to be displayed for the column
330
CellStruct struct = new CellStruct(column, column.getChoppedAndLinkedValue());
331                     nextRowValues.put(new Integer JavaDoc(column.getHeaderCell().getColumnNumber()), struct);
332                 }
333             }
334             // now we are going to create the current row; reset the decorator to the current row
335
if (tableDecorator != null)
336             {
337                 tableDecorator.initRow(currentRow.getObject(), currentRow.getRowNumber(), currentRow.getRowNumber()
338                     + rowIterator.getPageOffset());
339             }
340
341             if (tableDecorator != null)
342             {
343                 writeDecoratedRowStart(model);
344             }
345             // open row
346
writeRowOpener(currentRow);
347
348             Iterator JavaDoc headerCellsIter = model.getHeaderCellList().iterator();
349             boolean hasReachedGroupEnd = false;
350             ArrayList JavaDoc structsForRow = new ArrayList JavaDoc(model.getHeaderCellList().size());
351             while (headerCellsIter.hasNext())
352             {
353                 HeaderCell header = (HeaderCell) headerCellsIter.next();
354
355                 // Get the value to be displayed for the column
356
CellStruct struct = (CellStruct) currentRowValues.get(new Integer JavaDoc(header.getColumnNumber()));
357                 struct.decoratedValue = struct.bodyValue;
358                 // Check and see if there is a grouping transition. If there is, then notify the decorator
359
if (header.getGroup() != -1)
360                 {
361                     CellStruct prior = (CellStruct) previousRowValues.get(new Integer JavaDoc(header.getColumnNumber()));
362                     CellStruct next = (CellStruct) nextRowValues.get(new Integer JavaDoc(header.getColumnNumber()));
363                     // Why npe?
364
String JavaDoc priorBodyValue = prior != null ? prior.bodyValue : null;
365                     String JavaDoc nextBodyValue = next != null ? next.bodyValue : null;
366                     short groupingValue = groupColumns(struct.bodyValue, priorBodyValue, nextBodyValue);
367                     hasReachedGroupEnd = hasReachedGroupEnd
368                         || groupingValue == GROUP_END
369                         || groupingValue == GROUP_NO_CHANGE;
370
371                     if (tableDecorator != null)
372                     {
373                         switch (groupingValue)
374                         {
375                             case GROUP_START :
376                                 tableDecorator.startOfGroup(struct.bodyValue, header.getGroup());
377                                 break;
378                             case GROUP_END :
379                                 tableDecorator.endOfGroup(struct.bodyValue, header.getGroup());
380                                 break;
381                             case GROUP_START_AND_END :
382                                 tableDecorator.startOfGroup(struct.bodyValue, header.getGroup());
383                                 tableDecorator.endOfGroup(struct.bodyValue, header.getGroup());
384                                 break;
385                             default :
386                                 break;
387                         }
388                     }
389                     if (tableDecorator != null)
390                     {
391                         struct.decoratedValue = tableDecorator.displayGroupedValue(struct.bodyValue, groupingValue);
392                     }
393                     else if (groupingValue == GROUP_END || groupingValue == GROUP_NO_CHANGE)
394                     {
395                         struct.decoratedValue = TagConstants.EMPTY_STRING;
396                     }
397                 }
398                 structsForRow.add(struct);
399             }
400
401             for (Iterator JavaDoc iterator = structsForRow.iterator(); iterator.hasNext();)
402             {
403                 CellStruct struct = (CellStruct) iterator.next();
404                 writeColumnOpener(struct.column);
405                 writeColumnValue(struct.decoratedValue, struct.column);
406                 writeColumnCloser(struct.column);
407             }
408
409             if (model.isEmpty())
410             {
411                 if (log.isDebugEnabled())
412                 {
413                     log.debug("[" + this.id + "] table has no columns");
414                 }
415                 // render empty row
416
writeRowWithNoColumns(currentRow.getObject().toString());
417             }
418
419             // close row
420
writeRowCloser(currentRow);
421             // decorate row finish
422
if (model.getTableDecorator() != null)
423             {
424                 writeDecoratedRowFinish(model);
425             }
426         }
427
428         // render empty list message
429
if (model.getRowListPage().size() == 0)
430         {
431             writeEmptyListRowMessage(MessageFormat.format(
432                 model.getProperties().getEmptyListRowMessage(),
433                 new Object JavaDoc[]{new Integer JavaDoc(model.getNumberOfColumns())}));
434         }
435     }
436
437     /*
438      * writeTableBody callback methods
439      */

440
441     /**
442      * Called by writeTableBody to write to decorate the table.
443      * @param model The table model for which the content is written.
444      * @throws Exception if it encounters an error while writing.
445      */

446     protected abstract void writeDecoratedRowStart(TableModel model) throws Exception JavaDoc;
447
448     /**
449      * Called by writeTableBody to write the start of the row structure.
450      * @param row The table row for which the content is written.
451      * @throws Exception if it encounters an error while writing.
452      */

453     protected abstract void writeRowOpener(Row row) throws Exception JavaDoc;
454
455     /**
456      * Called by writeTableBody to write the start of the column structure.
457      * @param column The table column for which the content is written.
458      * @throws Exception if it encounters an error while writing.
459      */

460     protected abstract void writeColumnOpener(Column column) throws Exception JavaDoc;
461
462     /**
463      * Called by writeTableBody to write a column's value.
464      * @param value The column value.
465      * @param column The table column for which the content is written.
466      * @throws Exception if it encounters an error while writing.
467      */

468     protected abstract void writeColumnValue(Object JavaDoc value, Column column) throws Exception JavaDoc;
469
470     /**
471      * Called by writeTableBody to write the end of the column structure.
472      * @param column The table column for which the content is written.
473      * @throws Exception if it encounters an error while writing.
474      */

475     protected abstract void writeColumnCloser(Column column) throws Exception JavaDoc;
476
477     /**
478      * Called by writeTableBody to write a row that has no columns.
479      * @param value The row value.
480      * @throws Exception if it encounters an error while writing.
481      */

482     protected abstract void writeRowWithNoColumns(String JavaDoc value) throws Exception JavaDoc;
483
484     /**
485      * Called by writeTableBody to write the end of the row structure.
486      * @param row The table row for which the content is written.
487      * @throws Exception if it encounters an error while writing.
488      */

489     protected abstract void writeRowCloser(Row row) throws Exception JavaDoc;
490
491     /**
492      * Called by writeTableBody to decorate the table.
493      * @param model The table model for which the content is written.
494      * @throws Exception if it encounters an error while writing.
495      */

496     protected abstract void writeDecoratedRowFinish(TableModel model) throws Exception JavaDoc;
497
498     /**
499      * Called by writeTableBody to write a message explaining that the row contains no data.
500      * @param message The message explaining that the row contains no data.
501      * @throws Exception if it encounters an error while writing.
502      */

503     protected abstract void writeEmptyListRowMessage(String JavaDoc message) throws Exception JavaDoc;
504
505     /**
506      * This takes a column value and grouping index as the argument. It then groups the column and returns the
507      * appropriate string back to the caller.
508      * @param value String current cell value
509      * @return String
510      */

511     private short groupColumns(String JavaDoc value, String JavaDoc previous, String JavaDoc next)
512     {
513         short groupingKey = GROUP_NO_CHANGE;
514         if (next == null || !ObjectUtils.equals(value, next))
515         {
516             // at the end of the list
517
groupingKey += GROUP_END;
518         }
519
520         if (previous == null || !ObjectUtils.equals(value, previous))
521         {
522             // At the start of the list
523
groupingKey += GROUP_START;
524         }
525         return groupingKey;
526     }
527
528     static class CellStruct
529     {
530
531         Column column;
532
533         String JavaDoc bodyValue;
534
535         String JavaDoc decoratedValue;
536
537         public CellStruct(Column theColumn, String JavaDoc bodyValueParam)
538         {
539             this.column = theColumn;
540             this.bodyValue = bodyValueParam;
541         }
542     }
543 }
544
Popular Tags