Save This Page
Home » displaytag-1.1.1-src » org » displaytag » tags » [javadoc | source]
    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;
   15   import java.io.IOException;
   16   import java.io.StringWriter;
   17   import java.io.Writer;
   18   import java.util.Collection;
   19   import java.util.HashMap;
   20   import java.util.Iterator;
   21   import java.util.List;
   22   import java.util.Map;
   23   
   24   import javax.servlet.http.HttpServletRequest;
   25   import javax.servlet.http.HttpServletResponse;
   26   import javax.servlet.jsp.JspException;
   27   import javax.servlet.jsp.JspTagException;
   28   import javax.servlet.jsp.JspWriter;
   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: 1081 $ ($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 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 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 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 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 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 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 size;
  167   
  168       /**
  169        * Name of the Integer in some scope containing the size of the data displaytag is paginating
  170        */
  171       private String sizeObjectName;
  172   
  173       /**
  174        * sort the full list?
  175        */
  176       private Boolean 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 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 excludedParams;
  207   
  208       /**
  209        * Unique table id.
  210        */
  211       private String uid;
  212   
  213       /**
  214        * The variable name to store totals in.
  215        */
  216       private String 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 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 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 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 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 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 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 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 size)
  378       {
  379           if (size instanceof String)
  380           {
  381               this.sizeObjectName = (String) 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 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 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 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 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 value)
  457       {
  458           if (value instanceof String)
  459           {
  460               // ok, assuming this is the name of the object
  461               this.name = (String) 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 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 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 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 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 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 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 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 sortCriterion = paginatedList.getSortCriterion();
  637   
  638               String 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
  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) 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 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 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(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, FactoryInstantiationException
  834       {
  835   
  836           if (rhf == null)
  837           {
  838               // first time initialization
  839               rhf = this.properties.getRequestHelperFactoryInstance();
  840           }
  841   
  842           String 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 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 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 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 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 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(Messages.getString("MissingAttributeException.msg", new Object[]{"size"}));
  967               }
  968               else if (!(size instanceof Integer))
  969               {
  970                   throw new JspTagException(Messages.getString(
  971                       "InvalidTypeException.msg",
  972                       new Object[]{"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) // 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) 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 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[] 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 paramsIterator = baseHref.getParameterMap().keySet().iterator();
 1077                   while (paramsIterator.hasNext())
 1078                   {
 1079                       String key = (String) 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 fullURI = requestUri;
 1101               if (!this.dontAppendContext)
 1102               {
 1103                   String contextPath = ((HttpServletRequest) 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) 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
 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 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 = this.properties.getDecoratorFactoryInstance().
 1177                                           loadTableDecorator(this.pageContext, getConfiguredDecoratorName());
 1178   
 1179           if (tableDecorator != null)
 1180           {
 1181               tableDecorator.init(this.pageContext, this.list, this.tableModel);
 1182               this.tableModel.setTableDecorator(tableDecorator);
 1183           }
 1184   
 1185           setupViewableData();
 1186   
 1187           // Figure out how we should sort this data, typically we just sort
 1188           // the data being shown, but the programmer can override this behavior
 1189           if (this.paginatedList == null && this.tableModel.isLocalSort())
 1190           {
 1191               if (!this.tableModel.isSortFullTable())
 1192               {
 1193                   this.tableModel.sortPageList();
 1194               }
 1195           }
 1196   
 1197           // Get the data back in the representation that the user is after, do they want HTML/XML/CSV/EXCEL/etc...
 1198           int returnValue = EVAL_PAGE;
 1199   
 1200           // check for nested tables
 1201           // Object previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA);
 1202           Object previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA);
 1203           if (MediaTypeEnum.HTML.equals(this.currentMediaType)
 1204               && (previousMediaType == null || MediaTypeEnum.HTML.equals(previousMediaType)))
 1205           {
 1206               writeHTMLData();
 1207           }
 1208           else if (!MediaTypeEnum.HTML.equals(this.currentMediaType))
 1209           {
 1210               if (log.isDebugEnabled())
 1211               {
 1212                   log.debug("[" + getUid() + "] doEndTag - exporting");
 1213               }
 1214   
 1215               returnValue = doExport();
 1216           }
 1217   
 1218           // do not remove media attribute! if the table is nested in other tables this is still needed
 1219           // this.pageContext.removeAttribute(PAGE_ATTRIBUTE_MEDIA);
 1220   
 1221           if (log.isDebugEnabled())
 1222           {
 1223               log.debug("[" + getUid() + "] doEndTag - end");
 1224           }
 1225   
 1226           cleanUp();
 1227           return returnValue;
 1228       }
 1229   
 1230       /**
 1231        * Returns the name of the table decorator that should be applied to this table,
 1232        * which is either the decorator configured in the property "decorator", or if
 1233        * none is configured in said property, a decorator configured with the
 1234        * "decorator.media.[media type]" property, or null if none is configured.  
 1235        * 
 1236        * @return Name of the table decorator that should be applied to this table.
 1237        */
 1238   	private String getConfiguredDecoratorName()
 1239       {
 1240           String
 1241           tableDecoratorName = (this.decoratorName == null) ?
 1242                                      this.properties.getMediaTypeDecoratorName(this.currentMediaType) :
 1243                                      this.decoratorName;
 1244           tableDecoratorName = (tableDecoratorName == null) ?
 1245                                      this.properties.getExportDecoratorName(this.currentMediaType) :
 1246                                      tableDecoratorName;
 1247           return tableDecoratorName;
 1248   	}
 1249   
 1250       /**
 1251        * clean up instance variables, but not the ones representing tag attributes.
 1252        */
 1253       private void cleanUp()
 1254       {
 1255           // reset instance variables (non attributes)
 1256           this.currentMediaType = null;
 1257           this.baseHref = null;
 1258           this.caption = null;
 1259           this.captionTag = null;
 1260           this.currentRow = null;
 1261           this.doAfterBodyExecuted = false;
 1262           this.footer = null;
 1263           this.listHelper = null;
 1264           this.pageNumber = 0;
 1265           this.paramEncoder = null;
 1266           this.properties = null;
 1267           this.rowNumber = 1;
 1268           this.tableIterator = null;
 1269           this.tableModel = null;
 1270           this.list = null;
 1271       }
 1272   
 1273       /**
 1274        * If no columns are provided, automatically add them from bean properties. Get the first object in the list and get
 1275        * all the properties (except the "class" property which is automatically skipped). Of course this isn't possible
 1276        * for empty lists.
 1277        */
 1278       private void describeEmptyTable()
 1279       {
 1280           this.tableIterator = IteratorUtils.getIterator(this.list);
 1281   
 1282           if (this.tableIterator.hasNext())
 1283           {
 1284               Object iteratedObject = this.tableIterator.next();
 1285               Map objectProperties = new HashMap();
 1286   
 1287               // if it's a String don't add the "Bytes" column
 1288               if (iteratedObject instanceof String)
 1289               {
 1290                   return;
 1291               }
 1292               // if it's a map already use key names for column headers
 1293               if (iteratedObject instanceof Map)
 1294               {
 1295                   objectProperties = (Map) iteratedObject;
 1296               }
 1297               else
 1298               {
 1299                   try
 1300                   {
 1301                       objectProperties = BeanUtils.describe(iteratedObject);
 1302                   }
 1303                   catch (Exception e)
 1304                   {
 1305                       log.warn("Unable to automatically add columns: " + e.getMessage(), e);
 1306                   }
 1307               }
 1308   
 1309               // iterator on properties names
 1310               Iterator propertiesIterator = objectProperties.keySet().iterator();
 1311   
 1312               while (propertiesIterator.hasNext())
 1313               {
 1314                   // get the property name
 1315                   String propertyName = (String) propertiesIterator.next();
 1316   
 1317                   // dont't want to add the standard "class" property
 1318                   if (!"class".equals(propertyName)) //$NON-NLS-1$
 1319                   {
 1320                       // creates a new header and add to the table model
 1321                       HeaderCell headerCell = new HeaderCell();
 1322                       headerCell.setBeanPropertyName(propertyName);
 1323   
 1324                       // handle title i18n
 1325                       headerCell.setTitle(this.properties.geResourceProvider().getResource(
 1326                           null,
 1327                           propertyName,
 1328                           this,
 1329                           this.pageContext));
 1330   
 1331                       this.tableModel.addColumnHeader(headerCell);
 1332                   }
 1333               }
 1334           }
 1335       }
 1336   
 1337       /**
 1338        * Called when data are not displayed in a html page but should be exported.
 1339        * @return int SKIP_PAGE
 1340        * @throws JspException generic exception
 1341        */
 1342       protected int doExport() throws JspException
 1343       {
 1344   
 1345           boolean exportFullList = this.properties.getExportFullList();
 1346   
 1347           if (log.isDebugEnabled())
 1348           {
 1349               log.debug("[" + getUid() + "] currentMediaType=" + this.currentMediaType);
 1350           }
 1351   
 1352           boolean exportHeader = this.properties.getExportHeader(this.currentMediaType);
 1353           boolean exportDecorated = this.properties.getExportDecorated();
 1354   
 1355           ExportView exportView = ExportViewFactory.getInstance().getView(
 1356               this.currentMediaType,
 1357               this.tableModel,
 1358               exportFullList,
 1359               exportHeader,
 1360               exportDecorated);
 1361   
 1362           try
 1363           {
 1364               writeExport(exportView);
 1365           }
 1366           catch (IOException e)
 1367           {
 1368               throw new WrappedRuntimeException(getClass(), e);
 1369           }
 1370   
 1371           return SKIP_PAGE;
 1372       }
 1373   
 1374       /**
 1375        * Will write the export. The default behavior is to write directly to the response. If the ResponseOverrideFilter
 1376        * is configured for this request, will instead write the exported content to a map in the Request object.
 1377        * @param exportView export view
 1378        * @throws JspException for problem in clearing the response or for invalid export views
 1379        * @throws IOException exception thrown when writing content to the response
 1380        */
 1381       protected void writeExport(ExportView exportView) throws IOException, JspException
 1382       {
 1383           String filename = properties.getExportFileName(this.currentMediaType);
 1384   
 1385           HttpServletResponse response = (HttpServletResponse) this.pageContext.getResponse();
 1386           HttpServletRequest request = (HttpServletRequest) this.pageContext.getRequest();
 1387   
 1388           Map bean = (Map) request.getAttribute(FILTER_CONTENT_OVERRIDE_BODY);
 1389           boolean usingFilter = bean != null;
 1390   
 1391           String mimeType = exportView.getMimeType();
 1392           // original encoding, be sure to add it back after reset()
 1393           String characterEncoding = response.getCharacterEncoding();
 1394   
 1395           if (usingFilter)
 1396           {
 1397               if (!bean.containsKey(TableTagParameters.BEAN_BUFFER))
 1398               {
 1399                   // We are running under the export filter, call it
 1400                   log.debug("Exportfilter enabled in unbuffered mode, setting headers");
 1401                   response.addHeader(TableTagParameters.PARAMETER_EXPORTING, TagConstants.EMPTY_STRING);
 1402               }
 1403               else
 1404               {
 1405                   // We are running under the export filter in buffered mode
 1406                   bean.put(TableTagParameters.BEAN_CONTENTTYPE, mimeType);
 1407                   bean.put(TableTagParameters.BEAN_FILENAME, filename);
 1408   
 1409                   if (exportView instanceof TextExportView)
 1410                   {
 1411                       StringWriter writer = new StringWriter();
 1412                       ((TextExportView) exportView).doExport(writer);
 1413                       bean.put(TableTagParameters.BEAN_BODY, writer.toString());
 1414                   }
 1415                   else if (exportView instanceof BinaryExportView)
 1416                   {
 1417                       ByteArrayOutputStream stream = new ByteArrayOutputStream();
 1418                       ((BinaryExportView) exportView).doExport(stream);
 1419                       bean.put(TableTagParameters.BEAN_BODY, stream.toByteArray());
 1420   
 1421                   }
 1422                   else
 1423                   {
 1424                       throw new JspTagException("Export view "
 1425                           + exportView.getClass().getName()
 1426                           + " must implement TextExportView or BinaryExportView");
 1427                   }
 1428   
 1429                   return;
 1430               }
 1431           }
 1432           else
 1433           {
 1434               log.debug("Exportfilter NOT enabled");
 1435               // response can't be already committed at this time
 1436               if (response.isCommitted())
 1437               {
 1438                   throw new ExportException(getClass());
 1439               }
 1440   
 1441               try
 1442               {
 1443                   response.reset();
 1444                   pageContext.getOut().clearBuffer();
 1445               }
 1446               catch (Exception e)
 1447               {
 1448                   throw new ExportException(getClass());
 1449               }
 1450           }
 1451   
 1452           if (!usingFilter && characterEncoding != null && mimeType.indexOf("charset") == -1) //$NON-NLS-1$
 1453           {
 1454               mimeType += "; charset=" + characterEncoding; //$NON-NLS-1$
 1455           }
 1456   
 1457           response.setContentType(mimeType);
 1458   
 1459           if (StringUtils.isNotEmpty(filename))
 1460           {
 1461               response.setHeader("Content-Disposition", //$NON-NLS-1$
 1462                   "attachment; filename=\"" + filename + "\""); //$NON-NLS-1$ //$NON-NLS-2$
 1463           }
 1464   
 1465           if (exportView instanceof TextExportView)
 1466           {
 1467               Writer writer;
 1468               if (usingFilter)
 1469               {
 1470                   writer = response.getWriter();
 1471               }
 1472               else
 1473               {
 1474                   writer = pageContext.getOut();
 1475               }
 1476   
 1477               ((TextExportView) exportView).doExport(writer);
 1478           }
 1479           else if (exportView instanceof BinaryExportView)
 1480           {
 1481               // dealing with binary content
 1482               // note that this is not assured to work on any application server if the filter is not enabled. According
 1483               // to the jsp specs response.getOutputStream() should no be called in jsps.
 1484               ((BinaryExportView) exportView).doExport(response.getOutputStream());
 1485           }
 1486           else
 1487           {
 1488               throw new JspTagException("Export view "
 1489                   + exportView.getClass().getName()
 1490                   + " must implement TextExportView or BinaryExportView");
 1491           }
 1492   
 1493           log.debug("Export completed");
 1494   
 1495       }
 1496   
 1497       /**
 1498        * This sets the list of all of the data that will be displayed on the page via the table tag. This might include
 1499        * 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
 1500        * subset, etc...
 1501        */
 1502       protected void setupViewableData()
 1503       {
 1504   
 1505           // If the user has changed the way our default behavior works, then we need to look for it now, and resort
 1506           // things if needed before we ask for the viewable part. (this is a bad place for this, this should be
 1507           // refactored and moved somewhere else).
 1508   
 1509           if (this.paginatedList == null || this.tableModel.isLocalSort())
 1510           {
 1511               if (this.tableModel.isSortFullTable())
 1512               {
 1513                   // Sort the total list...
 1514                   this.tableModel.sortFullList();
 1515               }
 1516           }
 1517   
 1518           Object originalData = this.tableModel.getRowListFull();
 1519   
 1520           // If they have asked for a subset of the list via the length
 1521           // attribute, then only fetch those items out of the master list.
 1522           List fullList = CollectionUtil.getListFromObject(originalData, this.offset, this.length);
 1523   
 1524           int pageOffset = this.offset;
 1525           // If they have asked for just a page of the data, then use the
 1526           // SmartListHelper to figure out what page they are after, etc...
 1527           if (this.paginatedList == null && this.pagesize > 0)
 1528           {
 1529               this.listHelper = new SmartListHelper(fullList, (this.partialList) ? ((Integer) size).intValue() : fullList
 1530                   .size(), this.pagesize, this.properties, this.partialList);
 1531               this.listHelper.setCurrentPage(this.pageNumber);
 1532               pageOffset = this.listHelper.getFirstIndexForCurrentPage();
 1533               fullList = this.listHelper.getListForCurrentPage();
 1534           }
 1535           else if (this.paginatedList != null)
 1536           {
 1537               this.listHelper = new PaginatedListSmartListHelper(this.paginatedList, this.properties);
 1538           }
 1539           this.tableModel.setRowListPage(fullList);
 1540           this.tableModel.setPageOffset(pageOffset);
 1541       }
 1542   
 1543       /**
 1544        * Uses HtmlTableWriter to write table called when data have to be displayed in a html page.
 1545        * @throws JspException generic exception
 1546        */
 1547       private void writeHTMLData() throws JspException
 1548       {
 1549           JspWriter out = this.pageContext.getOut();
 1550   
 1551           String css = this.properties.getCssTable();
 1552           if (StringUtils.isNotBlank(css))
 1553           {
 1554               this.addClass(css);
 1555           }
 1556           // use HtmlTableWriter to write table
 1557           new HtmlTableWriter(
 1558               this.tableModel,
 1559               this.properties,
 1560               this.baseHref,
 1561               this.export,
 1562               out,
 1563               getCaptionTag(),
 1564               this.paginatedList,
 1565               this.listHelper,
 1566               this.pagesize,
 1567               getAttributeMap(),
 1568               this.uid).writeTable(this.tableModel, this.getUid());
 1569   
 1570           if (this.varTotals != null)
 1571           {
 1572               pageContext.setAttribute(this.varTotals, getTotals());
 1573           }
 1574       }
 1575   
 1576       /**
 1577        * Get the column totals Map. If there is no varTotals defined, there are no totals.
 1578        * @return a Map of totals where the key is the column number and the value is the total for that column
 1579        */
 1580       public Map getTotals()
 1581       {
 1582           Map totalsMap = new HashMap();
 1583           if (this.varTotals != null)
 1584           {
 1585               List headers = this.tableModel.getHeaderCellList();
 1586               for (Iterator iterator = headers.iterator(); iterator.hasNext();)
 1587               {
 1588                   HeaderCell headerCell = (HeaderCell) iterator.next();
 1589                   if (headerCell.isTotaled())
 1590                   {
 1591                       totalsMap.put("column" + (headerCell.getColumnNumber() + 1), new Double(headerCell.getTotal()));
 1592                   }
 1593               }
 1594           }
 1595           return totalsMap;
 1596       }
 1597   
 1598       /**
 1599        * Get the table model for this tag. Sometimes required by local tags that cooperate with DT. USE THIS METHOD WITH
 1600        * EXTREME CAUTION; IT PROVIDES ACCESS TO THE INTERNALS OF DISPLAYTAG, WHICH ARE NOT TO BE CONSIDERED STABLE PUBLIC
 1601        * INTERFACES.
 1602        * @return the TableModel
 1603        */
 1604       public TableModel getTableModel()
 1605       {
 1606           return this.tableModel;
 1607       }
 1608   
 1609       /**
 1610        * Called by the setProperty tag to override some default behavior or text String.
 1611        * @param propertyName String property name
 1612        * @param propertyValue String property value
 1613        */
 1614       public void setProperty(String propertyName, String propertyValue)
 1615       {
 1616           this.properties.setProperty(propertyName, propertyValue);
 1617       }
 1618   
 1619       /**
 1620        * @see javax.servlet.jsp.tagext.Tag#release()
 1621        */
 1622       public void release()
 1623       {
 1624           if (log.isDebugEnabled())
 1625           {
 1626               log.debug("[" + getUid() + "] release() called");
 1627           }
 1628   
 1629           super.release();
 1630   
 1631           // tag attributes
 1632           this.decoratorName = null;
 1633           this.defaultSortedColumn = -1;
 1634           this.defaultSortOrder = null;
 1635           this.export = false;
 1636           this.length = 0;
 1637           this.listAttribute = null;
 1638           this.localSort = true;
 1639           this.name = null;
 1640           this.offset = 0;
 1641           this.pagesize = 0;
 1642           this.partialList = false;
 1643           this.requestUri = null;
 1644           this.dontAppendContext = false;
 1645           this.sortFullTable = null;
 1646           this.excludedParams = null;
 1647           this.filteredRows = null;
 1648           this.uid = null;
 1649           this.paginatedList = null;
 1650       }
 1651   
 1652       /**
 1653        * Returns the name.
 1654        * @return String
 1655        */
 1656       protected String getName()
 1657       {
 1658           return this.name;
 1659       }
 1660   
 1661       /**
 1662        * encode a parameter name to be unique in the page using ParamEncoder.
 1663        * @param parameterName parameter name to encode
 1664        * @return String encoded parameter name
 1665        */
 1666       private String encodeParameter(String parameterName)
 1667       {
 1668           // paramEncoder has been already instantiated?
 1669           if (this.paramEncoder == null)
 1670           {
 1671               // use the id attribute to get the unique identifier
 1672               this.paramEncoder = new ParamEncoder(getUid());
 1673           }
 1674   
 1675           return this.paramEncoder.encodeParameterName(parameterName);
 1676       }
 1677   
 1678   }

Save This Page
Home » displaytag-1.1.1-src » org » displaytag » tags » [javadoc | source]