KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > displaytag > tags > TableTag


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.tags;
13
14 import java.io.ByteArrayOutputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.io.StringWriter JavaDoc;
17 import java.io.Writer JavaDoc;
18 import java.util.Collection JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import javax.servlet.http.HttpServletRequest JavaDoc;
25 import javax.servlet.http.HttpServletResponse JavaDoc;
26 import javax.servlet.jsp.JspException JavaDoc;
27 import javax.servlet.jsp.JspTagException JavaDoc;
28 import javax.servlet.jsp.JspWriter JavaDoc;
29
30 import org.apache.commons.beanutils.BeanUtils;
31 import org.apache.commons.collections.IteratorUtils;
32 import org.apache.commons.lang.ObjectUtils;
33 import org.apache.commons.lang.StringUtils;
34 import org.apache.commons.lang.math.LongRange;
35 import org.apache.commons.lang.math.NumberUtils;
36 import org.apache.commons.lang.math.Range;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.displaytag.Messages;
40 import org.displaytag.decorator.TableDecorator;
41 import org.displaytag.exception.ExportException;
42 import org.displaytag.exception.FactoryInstantiationException;
43 import org.displaytag.exception.InvalidTagAttributeValueException;
44 import org.displaytag.exception.WrappedRuntimeException;
45 import org.displaytag.export.BinaryExportView;
46 import org.displaytag.export.ExportView;
47 import org.displaytag.export.ExportViewFactory;
48 import org.displaytag.export.TextExportView;
49 import org.displaytag.model.Cell;
50 import org.displaytag.model.Column;
51 import org.displaytag.model.HeaderCell;
52 import org.displaytag.model.Row;
53 import org.displaytag.model.TableModel;
54 import org.displaytag.pagination.PaginatedList;
55 import org.displaytag.pagination.PaginatedListSmartListHelper;
56 import org.displaytag.pagination.SmartListHelper;
57 import org.displaytag.properties.MediaTypeEnum;
58 import org.displaytag.properties.SortOrderEnum;
59 import org.displaytag.properties.TableProperties;
60 import org.displaytag.render.HtmlTableWriter;
61 import org.displaytag.util.CollectionUtil;
62 import org.displaytag.util.DependencyChecker;
63 import org.displaytag.util.Href;
64 import org.displaytag.util.ParamEncoder;
65 import org.displaytag.util.RequestHelper;
66 import org.displaytag.util.RequestHelperFactory;
67 import org.displaytag.util.TagConstants;
68
69
70 /**
71  * This tag takes a list of objects and creates a table to display those objects. With the help of column tags, you
72  * simply provide the name of properties (get Methods) that are called against the objects in your list that gets
73  * displayed. This tag works very much like the struts iterator tag, most of the attributes have the same name and
74  * functionality as the struts tag.
75  * @author mraible
76  * @author Fabrizio Giustina
77  * @version $Revision: 1025 $ ($Author: fgiust $)
78  */

79 public class TableTag extends HtmlTableTag
80 {
81
82     /**
83      * name of the attribute added to page scope when exporting, containing an MediaTypeEnum this can be used in column
84      * content to detect the output type and to return different data when exporting.
85      */

86     public static final String JavaDoc PAGE_ATTRIBUTE_MEDIA = "mediaType"; //$NON-NLS-1$
87

88     /**
89      * If this variable is found in the request, assume the export filter is enabled.
90      */

91     public static final String JavaDoc FILTER_CONTENT_OVERRIDE_BODY = //
92
"org.displaytag.filter.ResponseOverrideFilter.CONTENT_OVERRIDE_BODY"; //$NON-NLS-1$
93

94     /**
95      * D1597A17A6.
96      */

97     private static final long serialVersionUID = 899149338534L;
98
99     /**
100      * logger.
101      */

102     private static Log log = LogFactory.getLog(TableTag.class);
103
104     /**
105      * RequestHelperFactory instance used for link generation.
106      */

107     private static RequestHelperFactory rhf;
108
109     /**
110      * Object (collection, list) on which the table is based. This is not set directly using a tag attribute and can be
111      * cleaned.
112      */

113     protected Object JavaDoc list;
114
115     // -- start tag attributes --
116

117     /**
118      * Object (collection, list) on which the table is based. Set directly using the "list" attribute or evaluated from
119      * expression.
120      */

121     protected Object JavaDoc listAttribute;
122
123     /**
124      * actual row number, updated during iteration.
125      */

126     private int rowNumber = 1;
127
128     /**
129      * name of the object to use for iteration. Can contain expressions.
130      */

131     private String JavaDoc name;
132
133     /**
134      * length of list to display.
135      */

136     private int length;
137
138     /**
139      * table decorator class name.
140      */

141     private String JavaDoc decoratorName;
142
143     /**
144      * page size.
145      */

146     private int pagesize;
147
148     /**
149      * list contains only viewable data.
150      */

151     private boolean partialList;
152
153     /**
154      * add export links.
155      */

156     private boolean export;
157
158     /**
159      * list offset.
160      */

161     private int offset;
162
163     /**
164      * Integer containing total size of the data displaytag is paginating
165      */

166     private Object JavaDoc size;
167
168     /**
169      * Name of the Integer in some scope containing the size of the data displaytag is paginating
170      */

171     private String JavaDoc sizeObjectName;
172
173     /**
174      * sort the full list?
175      */

176     private Boolean JavaDoc sortFullTable;
177
178     /**
179      * are we doing any local sorting? (defaults to True)
180      */

181     private boolean localSort = true;
182
183     /**
184      * Request uri.
185      */

186     private String JavaDoc requestUri;
187
188     /**
189      * Prepend application context to generated links.
190      */

191     private boolean dontAppendContext;
192
193     /**
194      * the index of the column sorted by default.
195      */

196     private int defaultSortedColumn = -1;
197
198     /**
199      * the sorting order for the sorted column.
200      */

201     private SortOrderEnum defaultSortOrder;
202
203     /**
204      * Name of parameter which should not be forwarded during sorting or pagination.
205      */

206     private String JavaDoc excludedParams;
207
208     /**
209      * Unique table id.
210      */

211     private String JavaDoc uid;
212
213     /**
214      * The variable name to store totals in.
215      */

216     private String JavaDoc varTotals;
217
218     // -- end tag attributes --
219

220     /**
221      * table model - initialized in doStartTag().
222      */

223     private TableModel tableModel;
224
225     /**
226      * current row.
227      */

228     private Row currentRow;
229
230     /**
231      * next row.
232      */

233
234     /**
235      * Used by various functions when the person wants to do paging - cleaned in doEndTag().
236      */

237     private SmartListHelper listHelper;
238
239     /**
240      * base href used for links - set in initParameters().
241      */

242     private Href baseHref;
243
244     /**
245      * table properties - set in doStartTag().
246      */

247     private TableProperties properties;
248
249     /**
250      * page number - set in initParameters().
251      */

252     private int pageNumber = 1;
253
254     /**
255      * Iterator on collection.
256      */

257     private Iterator JavaDoc tableIterator;
258
259     /**
260      * export type - set in initParameters().
261      */

262     private MediaTypeEnum currentMediaType;
263
264     /**
265      * daAfterBody() has been executed at least once?
266      */

267     private boolean doAfterBodyExecuted;
268
269     /**
270      * The param encoder used to generate unique parameter names. Initialized at the first use of encodeParameter().
271      */

272     private ParamEncoder paramEncoder;
273
274     /**
275      * Static footer added using the footer tag.
276      */

277     private String JavaDoc footer;
278
279     /**
280      * Is this the last iteration we will be performing? We only output the footer on the last iteration.
281      */

282     private boolean lastIteration;
283
284     /**
285      * Static caption added using the footer tag.
286      */

287     private String JavaDoc caption;
288
289     /**
290      * Child caption tag.
291      */

292     private CaptionTag captionTag;
293
294     /**
295      * Included row range. If no rows can be skipped the range is from 0 to Long.MAX_VALUE. Range check should be always
296      * done using containsLong(). This is an instance of org.apache.commons.lang.math.Range, but it's declared as Object
297      * to avoid runtime errors while Jasper tries to compile the page and commons lang 2.0 is not available. Commons
298      * lang version will be checked in the doStartTag() method in order to provide a more user friendly message.
299      */

300     private Object JavaDoc filteredRows;
301
302     /**
303      * The paginated list containing the external pagination and sort parameters The presence of this paginated list is
304      * what determines if external pagination and sorting is used or not.
305      */

306     private PaginatedList paginatedList;
307
308     /**
309      * Is this the last iteration?
310      * @return boolean <code>true</code> if this is the last iteration
311      */

312     protected boolean isLastIteration()
313     {
314         return this.lastIteration;
315     }
316
317     /**
318      * Sets the list of parameter which should not be forwarded during sorting or pagination.
319      * @param value whitespace separated list of parameters which should not be included (* matches all parameters)
320      */

321     public void setExcludedParams(String JavaDoc value)
322     {
323         this.excludedParams = value;
324     }
325
326     /**
327      * Sets the content of the footer. Called by a nested footer tag.
328      * @param string footer content
329      */

330     public void setFooter(String JavaDoc string)
331     {
332         this.footer = string;
333         this.tableModel.setFooter(this.footer);
334     }
335
336     /**
337      * Sets the content of the caption. Called by a nested caption tag.
338      * @param string caption content
339      */

340     public void setCaption(String JavaDoc string)
341     {
342         this.caption = string;
343         this.tableModel.setCaption(this.caption);
344     }
345
346     /**
347      * Set the child caption tag.
348      * @param captionTag Child caption tag
349      */

350     public void setCaptionTag(CaptionTag captionTag)
351     {
352         this.captionTag = captionTag;
353     }
354
355     /**
356      * Obtain the child caption tag.
357      * @return The child caption tag
358      */

359     public CaptionTag getCaptionTag()
360     {
361         return this.captionTag;
362     }
363
364     /**
365      * Is the current row empty?
366      * @return true if the current row is empty
367      */

368     protected boolean isEmpty()
369     {
370         return this.currentRow == null;
371     }
372
373     /**
374      * set the Integer containing the total size of the data displaytag is paginating
375      * @param size Integer containing the total size of the data
376      */

377     public void setSize(Object JavaDoc size)
378     {
379         if (size instanceof String JavaDoc)
380         {
381             this.sizeObjectName = (String JavaDoc) size;
382         }
383         else
384         {
385             this.size = size;
386         }
387     }
388
389     /**
390      * set the name of the Integer in some scope containing the total size of the data to be paginated
391      * @param sizeObjectName name of the Integer containing the total size of the data to be paginated
392      */

393     public void setSizeObjectName(String JavaDoc sizeObjectName)
394     {
395         this.sizeObjectName = sizeObjectName;
396     }
397
398     /**
399      * setter for the "sort" attribute.
400      * @param value "page" (sort a single page) or "list" (sort the full list)
401      * @throws InvalidTagAttributeValueException if value is not "page" or "list"
402      */

403     public void setSort(String JavaDoc value) throws InvalidTagAttributeValueException
404     {
405         if (TableTagParameters.SORT_AMOUNT_PAGE.equals(value))
406         {
407             this.sortFullTable = Boolean.FALSE;
408         }
409         else if (TableTagParameters.SORT_AMOUNT_LIST.equals(value))
410         {
411             this.sortFullTable = Boolean.TRUE;
412         }
413         else if (TableTagParameters.SORT_AMOUNT_EXTERNAL.equals(value))
414         {
415             this.localSort = false;
416         }
417         else
418         {
419             throw new InvalidTagAttributeValueException(getClass(), "sort", value); //$NON-NLS-1$
420
}
421     }
422
423     /**
424      * setter for the "requestURI" attribute. Context path is automatically added to path starting with "/".
425      * @param value base URI for creating links
426      */

427     public void setRequestURI(String JavaDoc value)
428     {
429         this.requestUri = value;
430     }
431
432     /**
433      * Setter for the "requestURIcontext" attribute.
434      * @param value base URI for creating links
435      */

436     public void setRequestURIcontext(boolean value)
437     {
438         this.dontAppendContext = !value;
439     }
440
441     /**
442      * Used to directly set a list (or any object you can iterate on).
443      * @param value Object
444      * @deprecated use setName() to get the object from the page or request scope instead of setting it directly here
445      */

446     public void setList(Object JavaDoc value)
447     {
448         this.listAttribute = value;
449     }
450
451     /**
452      * Sets the name of the object to use for iteration.
453      * @param value name of the object to use for iteration (can contain expression). It also supports direct setting of
454      * a list, for jsp 2.0 containers where users can set up a data source here using EL expressions.
455      */

456     public void setName(Object JavaDoc value)
457     {
458         if (value instanceof String JavaDoc)
459         {
460             // ok, assuming this is the name of the object
461
this.name = (String JavaDoc) value;
462         }
463         else
464         {
465             // is this the list?
466
this.list = value;
467         }
468     }
469
470     /**
471      * Sets the name of the object to use for iteration. This setter is needed for jsp 1.1 container which doesn't
472      * support the String - Object conversion. The bean info class will swith to this setter.
473      * @param value name of the object
474      */

475     public void setNameString(String JavaDoc value)
476     {
477         this.name = value;
478     }
479
480     /**
481      * sets the sorting order for the sorted column.
482      * @param value "ascending" or "descending"
483      * @throws InvalidTagAttributeValueException if value is not one of "ascending" or "descending"
484      */

485     public void setDefaultorder(String JavaDoc value) throws InvalidTagAttributeValueException
486     {
487         this.defaultSortOrder = SortOrderEnum.fromName(value);
488         if (this.defaultSortOrder == null)
489         {
490             throw new InvalidTagAttributeValueException(getClass(), "defaultorder", value); //$NON-NLS-1$
491
}
492     }
493
494     /**
495      * Setter for the decorator class name.
496      * @param decorator fully qualified name of the table decorator to use
497      */

498     public void setDecorator(String JavaDoc decorator)
499     {
500         this.decoratorName = decorator;
501     }
502
503     /**
504      * Is export enabled?
505      * @param value <code>true</code> if export should be enabled
506      */

507     public void setExport(boolean value)
508     {
509         this.export = value;
510     }
511
512     /**
513      * The variable name in which the totals map is stored.
514      * @param varTotalsName the value
515      */

516     public void setVarTotals(String JavaDoc varTotalsName)
517     {
518         this.varTotals = varTotalsName;
519     }
520
521     /**
522      * Get the name that the totals should be stored under.
523      * @return the var name in pageContext
524      */

525     public String JavaDoc getVarTotals()
526     {
527         return this.varTotals;
528     }
529
530     /**
531      * sets the number of items to be displayed in the page.
532      * @param value number of items to display in a page
533      */

534     public void setLength(int value)
535     {
536         this.length = value;
537     }
538
539     /**
540      * sets the index of the default sorted column.
541      * @param value index of the column to sort
542      */

543     public void setDefaultsort(int value)
544     {
545         // subtract one (internal index is 0 based)
546
this.defaultSortedColumn = value - 1;
547     }
548
549     /**
550      * sets the number of items that should be displayed for a single page.
551      * @param value number of items that should be displayed for a single page
552      */

553     public void setPagesize(int value)
554     {
555         this.pagesize = value;
556     }
557
558     /**
559      * tells display tag that the values contained in the list are the viewable data only, there may be more results not
560      * given to displaytag
561      * @param partialList boolean value telling us there may be more data not given to displaytag
562      */

563     public void setPartialList(boolean partialList)
564     {
565         this.partialList = partialList;
566     }
567
568     /**
569      * Setter for the list offset attribute.
570      * @param value String
571      */

572     public void setOffset(int value)
573     {
574         if (value < 1)
575         {
576             // negative values has no meaning, simply treat them as 0
577
this.offset = 0;
578         }
579         else
580         {
581             this.offset = value - 1;
582         }
583     }
584
585     /**
586      * Sets the unique id used to identify for this table.
587      * @param value String
588      */

589     public void setUid(String JavaDoc value)
590     {
591         this.uid = value;
592     }
593
594     /**
595      * Returns the unique id used to identify for this table.
596      * @return id for this table
597      */

598     public String JavaDoc getUid()
599     {
600         return this.uid;
601     }
602
603     /**
604      * Returns the properties.
605      * @return TableProperties
606      */

607     protected TableProperties getProperties()
608     {
609         return this.properties;
610     }
611
612     /**
613      * Returns the base href with parameters. This is the instance used for links, need to be cloned before being
614      * modified.
615      * @return base Href with parameters
616      */

617     protected Href getBaseHref()
618     {
619         return this.baseHref;
620     }
621
622     /**
623      * Called by interior column tags to help this tag figure out how it is supposed to display the information in the
624      * List it is supposed to display.
625      * @param column an internal tag describing a column in this tableview
626      */

627     public void addColumn(HeaderCell column)
628     {
629         if (log.isDebugEnabled())
630         {
631             log.debug("[" + getUid() + "] addColumn " + column);
632         }
633
634         if ((this.paginatedList != null) && (column.getSortable()))
635         {
636             String JavaDoc sortCriterion = paginatedList.getSortCriterion();
637
638             String JavaDoc sortProperty = column.getSortProperty();
639             if (sortProperty == null)
640             {
641                 sortProperty = column.getBeanPropertyName();
642             }
643
644             if ((sortCriterion != null) && sortCriterion.equals(sortProperty))
645             {
646                 this.tableModel.setSortedColumnNumber(this.tableModel.getNumberOfColumns());
647                 column.setAlreadySorted();
648             }
649         }
650
651         this.tableModel.addColumnHeader(column);
652     }
653
654     /**
655      * Adds a cell to the current row. This method is usually called by a contained ColumnTag
656      * @param cell Cell to add to the current row
657      */

658     public void addCell(Cell cell)
659     {
660         // check if null: could be null if list is empty, we don't need to fill rows
661
if (this.currentRow != null)
662         {
663             int columnNumber = this.currentRow.getCellList().size();
664             this.currentRow.addCell(cell);
665
666             // just be sure that the number of columns has not been altered by conditionally including column tags in
667
// different rows. This is not supported, but better avoid IndexOutOfBounds...
668
if (columnNumber < tableModel.getHeaderCellList().size())
669             {
670                 HeaderCell header = (HeaderCell) tableModel.getHeaderCellList().get(columnNumber);
671                 header.addCell(new Column(header, cell, currentRow));
672             }
673         }
674     }
675
676     /**
677      * Is this the first iteration?
678      * @return boolean <code>true</code> if this is the first iteration
679      */

680     protected boolean isFirstIteration()
681     {
682         if (log.isDebugEnabled())
683         {
684             log.debug("["
685                 + getUid()
686                 + "] first iteration="
687                 + (this.rowNumber == 1)
688                 + " (row number="
689                 + this.rowNumber
690                 + ")");
691         }
692         // in first iteration this.rowNumber is 1
693
// (this.rowNumber is incremented in doAfterBody)
694
return this.rowNumber == 1;
695     }
696
697     /**
698      * When the tag starts, we just initialize some of our variables, and do a little bit of error checking to make sure
699      * that the user is not trying to give us parameters that we don't expect.
700      * @return int
701      * @throws JspException generic exception
702      * @see javax.servlet.jsp.tagext.Tag#doStartTag()
703      */

704     public int doStartTag() throws JspException JavaDoc
705     {
706         DependencyChecker.check();
707
708         // needed before column processing, elsewhere registered views will not be added
709
ExportViewFactory.getInstance();
710
711         if (log.isDebugEnabled())
712         {
713             log.debug("[" + getUid() + "] doStartTag called");
714         }
715
716         this.properties = TableProperties.getInstance((HttpServletRequest JavaDoc) pageContext.getRequest());
717         this.tableModel = new TableModel(this.properties, pageContext.getResponse().getCharacterEncoding(), pageContext);
718
719         // copying id to the table model for logging
720
this.tableModel.setId(getUid());
721
722         initParameters();
723
724         this.tableModel.setMedia(this.currentMediaType);
725
726         Object JavaDoc previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA);
727         // set the PAGE_ATTRIBUTE_MEDIA attribute in the page scope
728
if (previousMediaType == null || MediaTypeEnum.HTML.equals(previousMediaType))
729         {
730             if (log.isDebugEnabled())
731             {
732                 log.debug("[" + getUid() + "] setting media [" + this.currentMediaType + "] in this.pageContext");
733             }
734             this.pageContext.setAttribute(PAGE_ATTRIBUTE_MEDIA, this.currentMediaType);
735         }
736
737         doIteration();
738
739         // always return EVAL_BODY_TAG to get column headers also if the table is empty
740
// using int to avoid deprecation error in compilation using j2ee 1.3
741
return 2;
742     }
743
744     /**
745      * @see javax.servlet.jsp.tagext.BodyTag#doAfterBody()
746      */

747     public int doAfterBody()
748     {
749         // doAfterBody() has been called, body is not empty
750
this.doAfterBodyExecuted = true;
751
752         if (log.isDebugEnabled())
753         {
754             log.debug("[" + getUid() + "] doAfterBody called - iterating on row " + this.rowNumber);
755         }
756
757         // increment this.rowNumber
758
this.rowNumber++;
759
760         // Call doIteration() to do the common work
761
return doIteration();
762     }
763
764     /**
765      * Utility method that is used by both doStartTag() and doAfterBody() to perform an iteration.
766      * @return <code>int</code> either EVAL_BODY_TAG or SKIP_BODY depending on whether another iteration is desired.
767      */

768     protected int doIteration()
769     {
770
771         if (log.isDebugEnabled())
772         {
773             log.debug("[" + getUid() + "] doIteration called");
774         }
775
776         // Row already filled?
777
if (this.currentRow != null)
778         {
779             // if yes add to table model and remove
780
this.tableModel.addRow(this.currentRow);
781             this.currentRow = null;
782         }
783
784         if (this.tableIterator.hasNext())
785         {
786
787             Object JavaDoc iteratedObject = this.tableIterator.next();
788             if (getUid() != null)
789             {
790                 if ((iteratedObject != null))
791                 {
792                     // set object into this.pageContext
793
if (log.isDebugEnabled())
794                     {
795                         log.debug("[" + getUid() + "] setting attribute \"" + getUid() + "\" in pageContext");
796                     }
797                     this.pageContext.setAttribute(getUid(), iteratedObject);
798
799                 }
800                 else
801                 {
802                     // if row is null remove previous object
803
this.pageContext.removeAttribute(getUid());
804                 }
805                 // set the current row number into this.pageContext
806
this.pageContext.setAttribute(getUid() + TableTagExtraInfo.ROWNUM_SUFFIX, new Integer JavaDoc(this.rowNumber));
807             }
808
809             // Row object for Cell values
810
this.currentRow = new Row(iteratedObject, this.rowNumber);
811
812             this.lastIteration = !this.tableIterator.hasNext();
813
814             // new iteration
815
// using int to avoid deprecation error in compilation using j2ee 1.3
816
return 2;
817         }
818         this.lastIteration = true;
819
820         if (log.isDebugEnabled())
821         {
822             log.debug("[" + getUid() + "] doIteration() - iterator ended after " + (this.rowNumber - 1) + " rows");
823         }
824
825         // end iteration
826
return SKIP_BODY;
827     }
828
829     /**
830      * Reads parameters from the request and initialize all the needed table model attributes.
831      * @throws FactoryInstantiationException for problems in instantiating a RequestHelperFactory
832      */

833     private void initParameters() throws JspTagException JavaDoc, FactoryInstantiationException
834     {
835
836         if (rhf == null)
837         {
838             // first time initialization
839
rhf = this.properties.getRequestHelperFactoryInstance();
840         }
841
842         String JavaDoc fullName = getFullObjectName();
843
844         // only evaluate if needed, else use list attribute
845
if (fullName != null)
846         {
847             this.list = evaluateExpression(fullName);
848         }
849         else if (this.list == null)
850         {
851             // needed to allow removing the collection of objects if not set directly
852
this.list = this.listAttribute;
853         }
854
855         if (this.list instanceof PaginatedList)
856         {
857             this.paginatedList = (PaginatedList) this.list;
858             this.list = this.paginatedList.getList();
859         }
860
861         // set the table model to perform in memory local sorting
862
this.tableModel.setLocalSort(this.localSort && (this.paginatedList == null));
863
864         RequestHelper requestHelper = rhf.getRequestHelperInstance(this.pageContext);
865
866         initHref(requestHelper);
867
868         Integer JavaDoc pageNumberParameter = requestHelper.getIntParameter(encodeParameter(TableTagParameters.PARAMETER_PAGE));
869         this.pageNumber = (pageNumberParameter == null) ? 1 : pageNumberParameter.intValue();
870
871         int sortColumn = -1;
872         if (!this.tableModel.isLocalSort())
873         {
874             // our sort column parameter may be a string, check that first
875
String JavaDoc sortColumnName = requestHelper.getParameter(encodeParameter(TableTagParameters.PARAMETER_SORT));
876
877             // if usename is not null, sortColumnName is the name, if not is the column index
878
String JavaDoc usename = requestHelper.getParameter(encodeParameter(TableTagParameters.PARAMETER_SORTUSINGNAME));
879
880             if (sortColumnName == null)
881             {
882                 this.tableModel.setSortedColumnNumber(this.defaultSortedColumn);
883             }
884             else
885             {
886                 if (usename != null)
887                 {
888
889                     this.tableModel.setSortedColumnName(sortColumnName); // its a string, set as string
890
}
891                 else if (NumberUtils.isNumber(sortColumnName))
892                 {
893                     sortColumn = Integer.parseInt(sortColumnName);
894                     this.tableModel.setSortedColumnNumber(sortColumn); // its an int set as normal
895
}
896             }
897         }
898         else if (this.paginatedList == null)
899         {
900             Integer JavaDoc sortColumnParameter = requestHelper
901                 .getIntParameter(encodeParameter(TableTagParameters.PARAMETER_SORT));
902             sortColumn = (sortColumnParameter == null) ? this.defaultSortedColumn : sortColumnParameter.intValue();
903             this.tableModel.setSortedColumnNumber(sortColumn);
904         }
905         else
906         {
907             sortColumn = defaultSortedColumn;
908         }
909
910         // default value
911
boolean finalSortFull = this.properties.getSortFullList();
912
913         // user value for this single table
914
if (this.sortFullTable != null)
915         {
916             finalSortFull = this.sortFullTable.booleanValue();
917         }
918
919         // if a partial list is used and sort="list" is specified, assume the partial list is already sorted
920
if (!this.partialList || !finalSortFull)
921         {
922             this.tableModel.setSortFullTable(finalSortFull);
923         }
924
925         if (this.paginatedList == null)
926         {
927             SortOrderEnum paramOrder = SortOrderEnum.fromCode(requestHelper
928                 .getIntParameter(encodeParameter(TableTagParameters.PARAMETER_ORDER)));
929
930             // if no order parameter is set use default
931
if (paramOrder == null)
932             {
933                 paramOrder = this.defaultSortOrder;
934             }
935
936             boolean order = SortOrderEnum.DESCENDING != paramOrder;
937             this.tableModel.setSortOrderAscending(order);
938         }
939         else
940         {
941             SortOrderEnum direction = paginatedList.getSortDirection();
942             this.tableModel.setSortOrderAscending(direction == SortOrderEnum.ASCENDING);
943         }
944
945         Integer JavaDoc exportTypeParameter = requestHelper
946             .getIntParameter(encodeParameter(TableTagParameters.PARAMETER_EXPORTTYPE));
947
948         this.currentMediaType = (MediaTypeEnum) ObjectUtils.defaultIfNull(
949             MediaTypeEnum.fromCode(exportTypeParameter),
950             MediaTypeEnum.HTML);
951
952         // if we are doing partialLists then ensure we have our size object
953
if (this.partialList)
954         {
955             if ((this.sizeObjectName == null) && (this.size == null))
956             {
957                 // ?
958
}
959             if (this.sizeObjectName != null)
960             {
961                 // retrieve the object from scope
962
this.size = evaluateExpression(this.sizeObjectName);
963             }
964             if (size == null)
965             {
966                 throw new JspTagException JavaDoc(Messages.getString("MissingAttributeException.msg", new Object JavaDoc[]{"size"}));
967             }
968             else if (!(size instanceof Integer JavaDoc))
969             {
970                 throw new JspTagException JavaDoc(Messages.getString(
971                     "InvalidTypeException.msg",
972                     new Object JavaDoc[]{"size", "Integer"}));
973             }
974         }
975
976         // do we really need to skip any row?
977
boolean wishOptimizedIteration = ((this.pagesize > 0 // we are paging
978
|| this.offset > 0 // or we are skipping some records using offset
979
|| this.length > 0 // or we are limiting the records using length
980
) && !partialList); // only optimize if we have the full list
981

982         // can we actually skip any row?
983
if (wishOptimizedIteration && (this.list instanceof Collection JavaDoc) // we need to know the size
984
&& ((sortColumn == -1 // and we are not sorting
985
|| !finalSortFull // or we are sorting with the "page" behaviour
986
) && (this.currentMediaType == MediaTypeEnum.HTML // and we are not exporting
987
|| !this.properties.getExportFullList()) // or we are exporting a single page
988
))
989         {
990             int start = 0;
991             int end = 0;
992             if (this.offset > 0)
993             {
994                 start = this.offset;
995             }
996             if (length > 0)
997             {
998                 end = start + this.length;
999             }
1000
1001            if (this.pagesize > 0)
1002            {
1003                int fullSize = ((Collection JavaDoc) this.list).size();
1004                start = (this.pageNumber - 1) * this.pagesize;
1005
1006                // invalid page requested, go back to last page
1007
if (start > fullSize)
1008                {
1009                    int div = fullSize / this.pagesize;
1010                    start = (fullSize % this.pagesize == 0) ? div : div + 1;
1011                }
1012
1013                end = start + this.pagesize;
1014            }
1015
1016            // rowNumber starts from 1
1017
filteredRows = new LongRange(start + 1, end);
1018        }
1019        else
1020        {
1021            filteredRows = new LongRange(1, Long.MAX_VALUE);
1022        }
1023
1024        this.tableIterator = IteratorUtils.getIterator(this.list);
1025    }
1026
1027    /**
1028     * Is the current row included in the "to-be-evaluated" range? Called by nested ColumnTags. If <code>false</code>
1029     * column body is skipped.
1030     * @return <code>true</code> if the current row must be evaluated because is included in output or because is
1031     * included in sorting.
1032     */

1033    protected boolean isIncludedRow()
1034    {
1035        return ((Range) filteredRows).containsLong(this.rowNumber);
1036    }
1037
1038    /**
1039     * Create a complete string for compatibility with previous version before expression evaluation. This approach is
1040     * optimized for new expressions, not for previous property/scope parameters.
1041     * @return Expression composed by scope + name + property
1042     */

1043    private String JavaDoc getFullObjectName()
1044    {
1045        // only evaluate if needed, else preserve original list
1046
if (this.name == null)
1047        {
1048            return null;
1049        }
1050
1051        return this.name;
1052    }
1053
1054    /**
1055     * init the href object used to generate all the links for pagination, sorting, exporting.
1056     * @param requestHelper request helper used to extract the base Href
1057     */

1058    protected void initHref(RequestHelper requestHelper)
1059    {
1060        // get the href for this request
1061
this.baseHref = requestHelper.getHref();
1062
1063        if (this.excludedParams != null)
1064        {
1065            String JavaDoc[] splittedExcludedParams = StringUtils.split(this.excludedParams);
1066
1067            // handle * keyword
1068
if (splittedExcludedParams.length == 1 && "*".equals(splittedExcludedParams[0]))
1069            {
1070                // @todo cleanup: paramEncoder initialization should not be done here
1071
if (this.paramEncoder == null)
1072                {
1073                    this.paramEncoder = new ParamEncoder(getUid());
1074                }
1075
1076                Iterator JavaDoc paramsIterator = baseHref.getParameterMap().keySet().iterator();
1077                while (paramsIterator.hasNext())
1078                {
1079                    String JavaDoc key = (String JavaDoc) paramsIterator.next();
1080
1081                    // don't remove parameters added by the table tag
1082
if (!this.paramEncoder.isParameterEncoded(key))
1083                    {
1084                        baseHref.removeParameter(key);
1085                    }
1086                }
1087            }
1088            else
1089            {
1090                for (int j = 0; j < splittedExcludedParams.length; j++)
1091                {
1092                    baseHref.removeParameter(splittedExcludedParams[j]);
1093                }
1094            }
1095        }
1096
1097        if (this.requestUri != null)
1098        {
1099            // if user has added a requestURI create a new href
1100
String JavaDoc fullURI = requestUri;
1101            if (!this.dontAppendContext)
1102            {
1103                String JavaDoc contextPath = ((HttpServletRequest JavaDoc) this.pageContext.getRequest()).getContextPath();
1104
1105                // prepend the context path if any.
1106
// actually checks if context path is already there for people which manually add it
1107
if (!StringUtils.isEmpty(contextPath)
1108                    && requestUri != null
1109                    && requestUri.startsWith("/")
1110                    && !requestUri.startsWith(contextPath))
1111                {
1112                    fullURI = contextPath + this.requestUri;
1113                }
1114            }
1115
1116            // call encodeURL to preserve session id when cookies are disabled
1117
fullURI = ((HttpServletResponse JavaDoc) this.pageContext.getResponse()).encodeURL(fullURI);
1118
1119            baseHref.setFullUrl(fullURI);
1120
1121            // // ... and copy parameters from the current request
1122
// Map parameterMap = normalHref.getParameterMap();
1123
// this.baseHref.addParameterMap(parameterMap);
1124
}
1125
1126    }
1127
1128    /**
1129     * Draw the table. This is where everything happens, we figure out what values we are supposed to be showing, we
1130     * figure out how we are supposed to be showing them, then we draw them.
1131     * @return int
1132     * @throws JspException generic exception
1133     * @see javax.servlet.jsp.tagext.Tag#doEndTag()
1134     */

1135    public int doEndTag() throws JspException JavaDoc
1136    {
1137
1138        if (log.isDebugEnabled())
1139        {
1140            log.debug("[" + getUid() + "] doEndTag called");
1141        }
1142
1143        if (!this.doAfterBodyExecuted)
1144        {
1145            if (log.isDebugEnabled())
1146            {
1147                log.debug("[" + getUid() + "] tag body is empty.");
1148            }
1149
1150            // first row (created in doStartTag)
1151
if (this.currentRow != null)
1152            {
1153                // if yes add to table model and remove
1154
this.tableModel.addRow(this.currentRow);
1155            }
1156
1157            // other rows
1158
while (this.tableIterator.hasNext())
1159            {
1160                Object JavaDoc iteratedObject = this.tableIterator.next();
1161                this.rowNumber++;
1162
1163                // Row object for Cell values
1164
this.currentRow = new Row(iteratedObject, this.rowNumber);
1165
1166                this.tableModel.addRow(this.currentRow);
1167            }
1168        }
1169
1170        // if no rows are defined automatically get all properties from bean
1171
if (this.tableModel.isEmpty())
1172        {
1173            describeEmptyTable();
1174        }
1175
1176        // TableDecorator tableDecorator = DecoratorFactory.loadTableDecorator(this.decoratorName);
1177
String JavaDoc tableDecoratorName = null;
1178        Object JavaDoc previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA);
1179        if (MediaTypeEnum.HTML.equals(this.currentMediaType)
1180            && (previousMediaType == null || MediaTypeEnum.HTML.equals(previousMediaType)))
1181        {
1182            tableDecoratorName = this.decoratorName;
1183        }
1184        else if (!MediaTypeEnum.HTML.equals(this.currentMediaType))
1185        {
1186            tableDecoratorName = this.properties.getExportDecoratorName(this.currentMediaType);
1187        }
1188        TableDecorator tableDecorator = this.properties.getDecoratorFactoryInstance().loadTableDecorator(
1189            this.pageContext,
1190            tableDecoratorName);
1191
1192        if (tableDecorator != null)
1193        {
1194            tableDecorator.init(this.pageContext, this.list, this.tableModel);
1195            this.tableModel.setTableDecorator(tableDecorator);
1196        }
1197
1198        setupViewableData();
1199
1200        // Figure out how we should sort this data, typically we just sort
1201
// the data being shown, but the programmer can override this behavior
1202
if (this.paginatedList == null && this.tableModel.isLocalSort())
1203        {
1204            if (!this.tableModel.isSortFullTable())
1205            {
1206                this.tableModel.sortPageList();
1207            }
1208        }
1209
1210        // Get the data back in the representation that the user is after, do they want HTML/XML/CSV/EXCEL/etc...
1211
int returnValue = EVAL_PAGE;
1212
1213        // check for nested tables
1214
// Object previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA);
1215
if (MediaTypeEnum.HTML.equals(this.currentMediaType)
1216            && (previousMediaType == null || MediaTypeEnum.HTML.equals(previousMediaType)))
1217        {
1218            writeHTMLData();
1219        }
1220        else if (!MediaTypeEnum.HTML.equals(this.currentMediaType))
1221        {
1222            if (log.isDebugEnabled())
1223            {
1224                log.debug("[" + getUid() + "] doEndTag - exporting");
1225            }
1226
1227            returnValue = doExport();
1228        }
1229
1230        // do not remove media attribute! if the table is nested in other tables this is still needed
1231
// this.pageContext.removeAttribute(PAGE_ATTRIBUTE_MEDIA);
1232

1233        if (log.isDebugEnabled())
1234        {
1235            log.debug("[" + getUid() + "] doEndTag - end");
1236        }
1237
1238        cleanUp();
1239        return returnValue;
1240    }
1241
1242    /**
1243     * clean up instance variables, but not the ones representing tag attributes.
1244     */

1245    private void cleanUp()
1246    {
1247        // reset instance variables (non attributes)
1248
this.currentMediaType = null;
1249        this.baseHref = null;
1250        this.caption = null;
1251        this.captionTag = null;
1252        this.currentRow = null;
1253        this.doAfterBodyExecuted = false;
1254        this.footer = null;
1255        this.listHelper = null;
1256        this.pageNumber = 0;
1257        this.paramEncoder = null;
1258        this.properties = null;
1259        this.rowNumber = 1;
1260        this.tableIterator = null;
1261        this.tableModel = null;
1262        this.list = null;
1263    }
1264
1265    /**
1266     * If no columns are provided, automatically add them from bean properties. Get the first object in the list and get
1267     * all the properties (except the "class" property which is automatically skipped). Of course this isn't possible
1268     * for empty lists.
1269     */

1270    private void describeEmptyTable()
1271    {
1272        this.tableIterator = IteratorUtils.getIterator(this.list);
1273
1274        if (this.tableIterator.hasNext())
1275        {
1276            Object JavaDoc iteratedObject = this.tableIterator.next();
1277            Map JavaDoc objectProperties = new HashMap JavaDoc();
1278
1279            // if it's a String don't add the "Bytes" column
1280
if (iteratedObject instanceof String JavaDoc)
1281            {
1282                return;
1283            }
1284            // if it's a map already use key names for column headers
1285
if (iteratedObject instanceof Map JavaDoc)
1286            {
1287                objectProperties = (Map JavaDoc) iteratedObject;
1288            }
1289            else
1290            {
1291                try
1292                {
1293                    objectProperties = BeanUtils.describe(iteratedObject);
1294                }
1295                catch (Exception JavaDoc e)
1296                {
1297                    log.warn("Unable to automatically add columns: " + e.getMessage(), e);
1298                }
1299            }
1300
1301            // iterator on properties names
1302
Iterator JavaDoc propertiesIterator = objectProperties.keySet().iterator();
1303
1304            while (propertiesIterator.hasNext())
1305            {
1306                // get the property name
1307
String JavaDoc propertyName = (String JavaDoc) propertiesIterator.next();
1308
1309                // dont't want to add the standard "class" property
1310
if (!"class".equals(propertyName)) //$NON-NLS-1$
1311
{
1312                    // creates a new header and add to the table model
1313
HeaderCell headerCell = new HeaderCell();
1314                    headerCell.setBeanPropertyName(propertyName);
1315
1316                    // handle title i18n
1317
headerCell.setTitle(this.properties.geResourceProvider().getResource(
1318                        null,
1319                        propertyName,
1320                        this,
1321                        this.pageContext));
1322
1323                    this.tableModel.addColumnHeader(headerCell);
1324                }
1325            }
1326        }
1327    }
1328
1329    /**
1330     * Called when data are not displayed in a html page but should be exported.
1331     * @return int SKIP_PAGE
1332     * @throws JspException generic exception
1333     */

1334    protected int doExport() throws JspException JavaDoc
1335    {
1336
1337        boolean exportFullList = this.properties.getExportFullList();
1338
1339        if (log.isDebugEnabled())
1340        {
1341            log.debug("[" + getUid() + "] currentMediaType=" + this.currentMediaType);
1342        }
1343
1344        boolean exportHeader = this.properties.getExportHeader(this.currentMediaType);
1345        boolean exportDecorated = this.properties.getExportDecorated();
1346
1347        ExportView exportView = ExportViewFactory.getInstance().getView(
1348            this.currentMediaType,
1349            this.tableModel,
1350            exportFullList,
1351            exportHeader,
1352            exportDecorated);
1353
1354        try
1355        {
1356            writeExport(exportView);
1357        }
1358        catch (IOException JavaDoc e)
1359        {
1360            throw new WrappedRuntimeException(getClass(), e);
1361        }
1362
1363        return SKIP_PAGE;
1364    }
1365
1366    /**
1367     * Will write the export. The default behavior is to write directly to the response. If the ResponseOverrideFilter
1368     * is configured for this request, will instead write the exported content to a map in the Request object.
1369     * @param exportView export view
1370     * @throws JspException for problem in clearing the response or for invalid export views
1371     * @throws IOException exception thrown when writing content to the response
1372     */

1373    protected void writeExport(ExportView exportView) throws IOException JavaDoc, JspException JavaDoc
1374    {
1375        String JavaDoc filename = properties.getExportFileName(this.currentMediaType);
1376
1377        HttpServletResponse JavaDoc response = (HttpServletResponse JavaDoc) this.pageContext.getResponse();
1378        HttpServletRequest JavaDoc request = (HttpServletRequest JavaDoc) this.pageContext.getRequest();
1379
1380        Map JavaDoc bean = (Map JavaDoc) request.getAttribute(FILTER_CONTENT_OVERRIDE_BODY);
1381        boolean usingFilter = bean != null;
1382
1383        String JavaDoc mimeType = exportView.getMimeType();
1384        // original encoding, be sure to add it back after reset()
1385
String JavaDoc characterEncoding = response.getCharacterEncoding();
1386
1387        if (usingFilter)
1388        {
1389            if (!bean.containsKey(TableTagParameters.BEAN_BUFFER))
1390            {
1391                // We are running under the export filter, call it
1392
log.debug("Exportfilter enabled in unbuffered mode, setting headers");
1393                response.addHeader(TableTagParameters.PARAMETER_EXPORTING, TagConstants.EMPTY_STRING);
1394            }
1395            else
1396            {
1397                // We are running under the export filter in buffered mode
1398
bean.put(TableTagParameters.BEAN_CONTENTTYPE, mimeType);
1399                bean.put(TableTagParameters.BEAN_FILENAME, filename);
1400
1401                if (exportView instanceof TextExportView)
1402                {
1403                    StringWriter JavaDoc writer = new StringWriter JavaDoc();
1404                    ((TextExportView) exportView).doExport(writer);
1405                    bean.put(TableTagParameters.BEAN_BODY, writer.toString());
1406                }
1407                else if (exportView instanceof BinaryExportView)
1408                {
1409                    ByteArrayOutputStream JavaDoc stream = new ByteArrayOutputStream JavaDoc();
1410                    ((BinaryExportView) exportView).doExport(stream);
1411                    bean.put(TableTagParameters.BEAN_BODY, stream.toByteArray());
1412
1413                }
1414                else
1415                {
1416                    throw new JspTagException JavaDoc("Export view "
1417                        + exportView.getClass().getName()
1418                        + " must implement TextExportView or BinaryExportView");
1419                }
1420
1421                return;
1422            }
1423        }
1424        else
1425        {
1426            log.debug("Exportfilter NOT enabled");
1427            // response can't be already committed at this time
1428
if (response.isCommitted())
1429            {
1430                throw new ExportException(getClass());
1431            }
1432
1433            try
1434            {
1435                response.reset();
1436                pageContext.getOut().clearBuffer();
1437            }
1438            catch (Exception JavaDoc e)
1439            {
1440                throw new ExportException(getClass());
1441            }
1442        }
1443
1444        if (!usingFilter && characterEncoding != null && mimeType.indexOf("charset") == -1) //$NON-NLS-1$
1445
{
1446            mimeType += "; charset=" + characterEncoding; //$NON-NLS-1$
1447
}
1448
1449        response.setContentType(mimeType);
1450
1451        if (StringUtils.isNotEmpty(filename))
1452        {
1453            response.setHeader("Content-Disposition", //$NON-NLS-1$
1454
"attachment; filename=\"" + filename + "\""); //$NON-NLS-1$ //$NON-NLS-2$
1455
}
1456
1457        if (exportView instanceof TextExportView)
1458        {
1459            Writer JavaDoc writer;
1460            if (usingFilter)
1461            {
1462                writer = response.getWriter();
1463            }
1464            else
1465            {
1466                writer = pageContext.getOut();
1467            }
1468
1469            ((TextExportView) exportView).doExport(writer);
1470        }
1471        else if (exportView instanceof BinaryExportView)
1472        {
1473            // dealing with binary content
1474
// note that this is not assured to work on any application server if the filter is not enabled. According
1475
// to the jsp specs response.getOutputStream() should no be called in jsps.
1476
((BinaryExportView) exportView).doExport(response.getOutputStream());
1477        }
1478        else
1479        {
1480            throw new JspTagException JavaDoc("Export view "
1481                + exportView.getClass().getName()
1482                + " must implement TextExportView or BinaryExportView");
1483        }
1484
1485        log.debug("Export completed");
1486
1487    }
1488
1489    /**
1490     * This sets the list of all of the data that will be displayed on the page via the table tag. This might include
1491     * just a subset of the total data in the list due to to paging being active, or the user asking us to just show a
1492     * subset, etc...
1493     */

1494    protected void setupViewableData()
1495    {
1496
1497        // If the user has changed the way our default behavior works, then we need to look for it now, and resort
1498
// things if needed before we ask for the viewable part. (this is a bad place for this, this should be
1499
// refactored and moved somewhere else).
1500

1501        if (this.paginatedList == null || this.tableModel.isLocalSort())
1502        {
1503            if (this.tableModel.isSortFullTable())
1504            {
1505                // Sort the total list...
1506
this.tableModel.sortFullList();
1507            }
1508        }
1509
1510        Object JavaDoc originalData = this.tableModel.getRowListFull();
1511
1512        // If they have asked for a subset of the list via the length
1513
// attribute, then only fetch those items out of the master list.
1514
List JavaDoc fullList = CollectionUtil.getListFromObject(originalData, this.offset, this.length);
1515
1516        int pageOffset = this.offset;
1517        // If they have asked for just a page of the data, then use the
1518
// SmartListHelper to figure out what page they are after, etc...
1519
if (this.paginatedList == null && this.pagesize > 0)
1520        {
1521            this.listHelper = new SmartListHelper(fullList, (this.partialList) ? ((Integer JavaDoc) size).intValue() : fullList
1522                .size(), this.pagesize, this.properties, this.partialList);
1523            this.listHelper.setCurrentPage(this.pageNumber);
1524            pageOffset = this.listHelper.getFirstIndexForCurrentPage();
1525            fullList = this.listHelper.getListForCurrentPage();
1526        }
1527        else if (this.paginatedList != null)
1528        {
1529            this.listHelper = new PaginatedListSmartListHelper(this.paginatedList, this.properties);
1530        }
1531        this.tableModel.setRowListPage(fullList);
1532        this.tableModel.setPageOffset(pageOffset);
1533    }
1534
1535    /**
1536     * Uses HtmlTableWriter to write table called when data have to be displayed in a html page.
1537     * @throws JspException generic exception
1538     */

1539    private void writeHTMLData() throws JspException JavaDoc
1540    {
1541        JspWriter JavaDoc out = this.pageContext.getOut();
1542
1543        String JavaDoc css = this.properties.getCssTable();
1544        if (StringUtils.isNotBlank(css))
1545        {
1546            this.addClass(css);
1547        }
1548        // use HtmlTableWriter to write table
1549
new HtmlTableWriter(
1550            this.tableModel,
1551            this.properties,
1552            this.baseHref,
1553            this.export,
1554            out,
1555            getCaptionTag(),
1556            this.paginatedList,
1557            this.listHelper,
1558            this.pagesize,
1559            getAttributeMap(),
1560            this.uid).writeTable(this.tableModel, this.getUid());
1561
1562        if (this.varTotals != null)
1563        {
1564            pageContext.setAttribute(this.varTotals, getTotals());
1565        }
1566    }
1567
1568    /**
1569     * Get the column totals Map. If there is no varTotals defined, there are no totals.
1570     * @return a Map of totals where the key is the column number and the value is the total for that column
1571     */

1572    public Map JavaDoc getTotals()
1573    {
1574        Map JavaDoc totalsMap = new HashMap JavaDoc();
1575        if (this.varTotals != null)
1576        {
1577            List JavaDoc headers = this.tableModel.getHeaderCellList();
1578            for (Iterator JavaDoc iterator = headers.iterator(); iterator.hasNext();)
1579            {
1580                HeaderCell headerCell = (HeaderCell) iterator.next();
1581                if (headerCell.isTotaled())
1582                {
1583                    totalsMap.put("column" + (headerCell.getColumnNumber() + 1), new Double JavaDoc(headerCell.getTotal()));
1584                }
1585            }
1586        }
1587        return totalsMap;
1588    }
1589
1590    /**
1591     * Get the table model for this tag. Sometimes required by local tags that cooperate with DT. USE THIS METHOD WITH
1592     * EXTREME CAUTION; IT PROVIDES ACCESS TO THE INTERNALS OF DISPLAYTAG, WHICH ARE NOT TO BE CONSIDERED STABLE PUBLIC
1593     * INTERFACES.
1594     * @return the TableModel
1595     */

1596    public TableModel getTableModel()
1597    {
1598        return this.tableModel;
1599    }
1600
1601    /**
1602     * Called by the setProperty tag to override some default behavior or text String.
1603     * @param propertyName String property name
1604     * @param propertyValue String property value
1605     */

1606    public void setProperty(String JavaDoc propertyName, String JavaDoc propertyValue)
1607    {
1608        this.properties.setProperty(propertyName, propertyValue);
1609    }
1610
1611    /**
1612     * @see javax.servlet.jsp.tagext.Tag#release()
1613     */

1614    public void release()
1615    {
1616        if (log.isDebugEnabled())
1617        {
1618            log.debug("[" + getUid() + "] release() called");
1619        }
1620
1621        super.release();
1622
1623        // tag attributes
1624
this.decoratorName = null;
1625        this.defaultSortedColumn = -1;
1626        this.defaultSortOrder = null;
1627        this.export = false;
1628        this.length = 0;
1629        this.listAttribute = null;
1630        this.localSort = true;
1631        this.name = null;
1632        this.offset = 0;
1633        this.pagesize = 0;
1634        this.partialList = false;
1635        this.requestUri = null;
1636        this.dontAppendContext = false;
1637        this.sortFullTable = null;
1638        this.excludedParams = null;
1639        this.filteredRows = null;
1640        this.uid = null;
1641        this.paginatedList = null;
1642    }
1643
1644    /**
1645     * Returns the name.
1646     * @return String
1647     */

1648    protected String JavaDoc getName()
1649    {
1650        return this.name;
1651    }
1652
1653    /**
1654     * encode a parameter name to be unique in the page using ParamEncoder.
1655     * @param parameterName parameter name to encode
1656     * @return String encoded parameter name
1657     */

1658    private String JavaDoc encodeParameter(String JavaDoc parameterName)
1659    {
1660        // paramEncoder has been already instantiated?
1661
if (this.paramEncoder == null)
1662        {
1663            // use the id attribute to get the unique identifier
1664
this.paramEncoder = new ParamEncoder(getUid());
1665        }
1666
1667        return this.paramEncoder.encodeParameterName(parameterName);
1668    }
1669
1670}
Popular Tags