View Javadoc

1   /***
2    * Copyright (c) 2003 held jointly by the individual authors.            
3    *                                                                          
4    * This library is free software; you can redistribute it and/or modify it    
5    * under the terms of the GNU Lesser General Public License as published      
6    * by the Free Software Foundation; either version 2.1 of the License, or 
7    * (at your option) any later version.                                            
8    *                                                                            
9    * This library is distributed in the hope that it will be useful, but 
10   * WITHOUT ANY WARRANTY; with out even the implied warranty of 
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
12   * GNU Lesser General Public License for more details.                                                  
13   *                                                                           
14   * You should have received a copy of the GNU Lesser General Public License   
15   * along with this library;  if not, write to the Free Software Foundation,   
16   * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.              
17   *                                                                            
18   * > http://www.gnu.org/copyleft/lesser.html                                  
19   * > http://www.opensource.org/licenses/lgpl-license.php
20   */
21  package net.mlw.vlh.web.tag;
22  
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.servlet.jsp.JspException;
29  
30  import net.mlw.vlh.ValueList;
31  import net.mlw.vlh.ValueListInfo;
32  import net.mlw.vlh.web.ValueListConfigBean;
33  import net.mlw.vlh.web.tag.support.ColumnInfo;
34  import net.mlw.vlh.web.tag.support.DisplayProvider;
35  import net.mlw.vlh.web.tag.support.Spacer;
36  import net.mlw.vlh.web.tag.support.ValueListNullSpacer;
37  import net.mlw.vlh.web.util.JspUtils;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  
42  /***
43   * This tag creates a table. It is ment to be used along with vlh:root tag.
44   * If the valuelist is empty, render only header.
45   * @todo Document this tag.
46   * 
47   * @author Matthew L. Wilson, Andrej Zachar
48   * @version $Revision: 1.37 $ $Date: 2005/12/15 12:49:12 $
49   */
50  public class DefaultRowTag extends ConfigurableTag
51  {
52  
53     private static final long serialVersionUID = 4050760477275928119L;
54  
55     /*** Commons logger. */
56     private static final Log LOGGER = LogFactory.getLog(DefaultRowTag.class);
57  
58     /*** Parent root tag.*/
59     private ValueListSpaceTag rootTag;
60  
61     private DisplayProvider displayProvider;
62  
63     private List columns = new ArrayList();
64  
65     protected int currentRowNumber = 0;
66  
67     protected Object beanInPageScope;
68  
69     /*** The name and type of the bean put in the pageContext. */
70     protected String bean;
71  
72     private int focusRowNumber = -1;
73  
74     /***
75      * True - Disable rendering of the header.
76      * False - Check if at least one column has a title, if yes, render header row, 
77      * 		   if not, skip it.
78      */
79     private boolean disableHeader = false;
80  
81     /***
82      * How to render the header in case of nested value list (the default is <code>true</code>).
83      * <br><code>false</code> - the header of the nested value list is rendered as a part of the top value list 
84      * <br><code>true</code> - the header is rendered for each nested valuelist
85      */
86     private boolean nestedHeader = true;
87  
88     /***
89      * Initialization is called at the beginning of <code>doStart</code>.
90      * Subclasses have to call either <code>super.init()</code> or <code>super.doStart()</code>.
91      * @throws JspException
92      */
93     protected void init() throws JspException
94     {
95        this.columns.clear();
96     }
97  
98     /***
99      * @see javax.servlet.jsp.tagext.Tag#doStartTag()
100     */
101    public int doStartTag() throws JspException
102    {
103 
104       init();
105 
106       // If the valuelist is empty, render only header
107       ValueList valueList = getRootTag().getValueList();
108       if (valueList == null || valueList.getList() == null || valueList.getList().size() == 0)
109       {
110          getRootTag().setValueList(ValueListNullSpacer.getInstance());
111          LOGGER.warn("ValueList '" + getRootTag().getTableInfo().getName()
112                + "' is empty or null. Continue working with the singleton ValueListNullSpacer.");
113       }
114 
115       if (getDisplayProvider().getMimeType() != null)
116       {
117          try
118          {
119             pageContext.getResponse().setContentType(getDisplayProvider().getMimeType());
120          }
121          catch (Exception e)
122          {
123             LOGGER.error("DefaultRowTag.doStartTag() exception...", e);
124             throw new JspException(e);
125          }
126       }
127 
128       if (LOGGER.isDebugEnabled())
129       {
130          LOGGER.debug("Starting procesing rows of the table '" + getRootTag().getTableInfo().getId() + "' with the ValueList '"
131                + getRootTag().getTableInfo().getName() + "'.");
132       }
133 
134       processIteration();
135 
136       return EVAL_BODY_AGAIN;
137    }
138 
139    /***
140     * @see javax.servlet.jsp.tagext.IterationTag#doAfterBody()
141     * 
142     * @todo figure out why release is not working.
143     */
144    public int doAfterBody() throws JspException
145    {
146       // If this is the first row, then print the column headers!
147       if (currentRowNumber == 0 && getRootTag() != null)
148       {
149          if (isEnabledHeader())
150          {
151             DefaultColumnTag parentColumnTag = (DefaultColumnTag) findAncestorWithClass(this, DefaultColumnTag.class);
152             if (parentColumnTag == null || isNestedHeader())
153             {
154                StringBuffer sb = new StringBuffer();
155                renderHeaderRow(sb, getColumns(), getRootTag().getTableInfo(), getRootTag().getValueList().getValueListInfo(), getRootTag()
156                      .getTableInfo().getParameters());
157                JspUtils.writePrevious(pageContext, sb.toString());
158                columns.clear();
159             }
160             else
161             {
162                // nested valuelist
163                parentColumnTag.setNestedColumnInfoList(columns);
164                columns = new ArrayList();
165             }
166          }
167          else
168          {
169             if (LOGGER.isDebugEnabled())
170             {
171                LOGGER.debug("Header of the table '" + getRootTag().getTableInfo().getId() + "' is skiped.");
172             }
173             columns.clear();
174          }
175       }
176 
177       if (beanInPageScope == null || beanInPageScope instanceof Spacer)
178       {
179          // release();			
180          return SKIP_BODY;
181       }
182 
183       // Render the output from this iteration to the output stream
184       if (bodyContent != null)
185       {
186          String style = getRowStyleClass();
187          pageContext.setAttribute(bean + "Style", style);
188 
189          appendClassCellAttribute(style);
190 
191          JspUtils.writePrevious(pageContext, getDisplayProvider().getRowPreProcess(getCellAttributes()));
192          if (getDisplayProvider().doesIncludeBodyContent())
193          {
194             JspUtils.writePrevious(pageContext, bodyContent.getString());
195          }
196          else
197          {
198             String html = bodyContent.getString().replaceAll("//n", "").replaceAll("//r", "");
199             JspUtils.writePrevious(pageContext, html);
200          }
201 
202          JspUtils.writePrevious(pageContext, getDisplayProvider().getRowPostProcess());
203          bodyContent.clearBody();
204       }
205 
206       currentRowNumber++;
207       return processIteration();
208    }
209 
210    /***
211     * Renders header row. Subclasses can overide or extend the method to provide different behaviour.
212     * 
213     * @param sb
214     * @param tableInfo
215     * @param vlInfo
216     * @param parameters
217     * @throws JspException 
218     */
219    protected void renderHeaderRow(StringBuffer sb, List columns, TableInfo tableInfo, ValueListInfo vlInfo, Map parameters)
220          throws JspException
221    {
222       sb.append(getDisplayProvider().getHeaderRowPreProcess());
223       for (Iterator iter = columns.iterator(); iter.hasNext();)
224       {
225          renderHeaderCell(sb, (ColumnInfo) iter.next(), tableInfo, vlInfo, parameters);
226       }
227       sb.append(getDisplayProvider().getHeaderRowPostProcess());
228    }
229 
230    /***
231     * Renders header cell. Subclasses can overide or extend the method to provide different behaviour.
232     * 
233     * @param sb
234     * @param columnInfo
235     * @param tableInfo
236     * @param vlInfo
237     * @param parameters
238     * @throws JspException 
239     */
240    protected void renderHeaderCell(StringBuffer sb, ColumnInfo columnInfo, TableInfo tableInfo, ValueListInfo vlInfo, Map parameters)
241          throws JspException
242    {
243       sb.append(getDisplayProvider().getHeaderCellPreProcess(columnInfo, vlInfo));
244       if (columnInfo.getNestedList() != null)
245       {
246          renderNestedHeader(sb, columnInfo, tableInfo, vlInfo, parameters);
247       }
248       else
249       {
250          sb.append(getDisplayProvider().getHeaderLabel(columnInfo, tableInfo, vlInfo, parameters));
251       }
252       sb.append(getDisplayProvider().getHeaderCellPostProcess());
253    }
254 
255    protected void renderNestedHeader(StringBuffer sb, ColumnInfo columnInfo, TableInfo tableInfo, ValueListInfo vlInfo, Map parameters)
256          throws JspException
257    {
258       sb.append(getDisplayProvider().getNestedHeaderPreProcess(columnInfo, null));
259       renderHeaderRow(sb, columnInfo.getNestedList(), tableInfo, vlInfo, parameters);
260       sb.append(getDisplayProvider().getNestedHeaderPostProcess());
261    }
262 
263    /***
264     * This option could be overwritten by disableHeader (default set to false)
265     * @return true when at least one column's title is not null. false all
266     *         titles are null.
267     */
268    protected boolean isEnabledHeader()
269    {
270       if (disableHeader)
271       {
272          return false;
273       }
274       return hasTitle(this.columns);
275    }
276 
277    private boolean hasTitle(List columns)
278    {
279       for (Iterator iter = columns.iterator(); iter.hasNext();)
280       {
281          ColumnInfo columnInfo = (ColumnInfo) iter.next();
282          if (columnInfo.getNestedList() != null && hasTitle(columnInfo.getNestedList()) || columnInfo.getTitle() != null)
283          {
284             return true;
285          }
286       }
287       if (LOGGER.isDebugEnabled())
288       {
289          LOGGER.debug("No titles are specified in the table");
290       }
291       return false;
292    }
293 
294    /***
295     * @see javax.servlet.jsp.tagext.Tag#doStartTag()
296     */
297    public int doEndTag() throws JspException
298    {
299       if (LOGGER.isDebugEnabled())
300       {
301          LOGGER.debug("End of processing rows of the table '" + getRootTag().getTableInfo().getId() + "' with the ValueList '"
302                + getRootTag().getTableInfo().getName() + "'.");
303       }
304 
305       release();
306 
307       return EVAL_PAGE;
308    }
309 
310    /***
311     * Produces one row.
312     * 
313     * @return
314     * @see javax.servlet.jsp.tagext.Tag#doEndTag()
315     * @throws JspException
316     *             is an error occurs.
317     */
318    protected int processIteration() throws JspException
319    {
320       if (getRootTag() != null && getRootTag().getValueList() != null && getRootTag().getValueList().hasNext())
321       {
322          pageContext.setAttribute(bean + "RowNumber", new Integer(currentRowNumber));
323          beanInPageScope = getRootTag().getValueList().next();
324 
325          if (LOGGER.isDebugEnabled())
326          {
327             LOGGER.debug("In the row no.'" + currentRowNumber + "' was setted the JavaBean '" + bean + "' in the pageContext.");
328          }
329 
330          if (beanInPageScope == null || beanInPageScope instanceof Spacer)
331          {
332             pageContext.removeAttribute(bean);
333 
334             if (LOGGER.isDebugEnabled())
335             {
336                LOGGER.debug("In the row no.'" + currentRowNumber + "' was not found any ValueList's List's (JavaBean) items to render.");
337             }
338          }
339          else
340          {
341             pageContext.setAttribute(bean, beanInPageScope);
342          }
343          return (EVAL_BODY_AGAIN);
344       }
345       else
346       {
347          //          @todo check why you like to call release - it sets BodyTagSupport.parent to null!
348          //			release();		
349          return SKIP_BODY;
350       }
351    }
352 
353    public final ValueListSpaceTag getRootTag() throws JspException
354    {
355       if (rootTag == null)
356       {
357          rootTag = (ValueListSpaceTag) JspUtils.getParent(this, ValueListSpaceTag.class);
358       }
359       return rootTag;
360    }
361 
362    /***
363     * @return Returns the displayProvider.
364     * @throws JspException 
365     */
366    public DisplayProvider getDisplayProvider() throws JspException
367    {
368       if (displayProvider == null)
369       {
370          displayProvider = getRootTag().getConfig().getDisplayProvider("html");
371          if (displayProvider == null)
372          {
373             LOGGER.error("Error getting default DisplayProvider (html).");
374             displayProvider = ValueListConfigBean.DEFAULT_DISPLAY_PROVIDER;
375          }
376       }
377 
378       return displayProvider;
379    }
380 
381    public void setDisplay(String display) throws JspException
382    {
383       this.displayProvider = getRootTag().getConfig().getDisplayProvider(display);
384    }
385 
386    /***
387     * Gets the current row in the iteration.
388     * 
389     * @return The current row in the iteration.
390     */
391    public int getCurrentRowNumber()
392    {
393       return currentRowNumber;
394    }
395 
396    /***
397     * Adds a column to this table.
398     * 
399     * @param column
400     *            The Column to add.
401     */
402    public void addColumnInfo(ColumnInfo column)
403    {
404       columns.add(column);
405    }
406 
407    /***
408     * Returns list of <code>ColumnInfo</code> objects.
409     * 
410     * @return Returns the columns.
411     */
412    protected List getColumns()
413    {
414       return columns;
415    }
416 
417    /***
418     * Sets the name of the bean put in the pageContext.
419     * 
420     * @param bean
421     *            The name of the bean put in the pageContext.
422     */
423    public void setBean(String bean)
424    {
425       this.bean = bean;
426    }
427 
428    /***
429     * Gets the name of the bean put in the pageContext.
430     * 
431     * @return The name of the bean put in the pageContext.
432     */
433    public String getBeanName()
434    {
435       return bean;
436    }
437 
438    /***
439     * @param focusRowNumber
440     *            (0 is considered to be first row) The focusRowNumber to set.
441     */
442    public void setFocusRowNumber(String focusRowNumber)
443    {
444       try
445       {
446          this.focusRowNumber = Integer.parseInt(focusRowNumber);
447       }
448       catch (NumberFormatException e)
449       {
450          this.focusRowNumber = -1;
451       }
452    }
453 
454    /***
455     * @param disableHeader The disableHeader to set.
456     */
457    public void setDisableHeader(boolean disableHeader)
458    {
459       this.disableHeader = disableHeader;
460    }
461 
462    /***
463     * @return Returns the nestedHeader.
464     * @see #setNestedHeader(boolean)
465     */
466    public boolean isNestedHeader()
467    {
468       return nestedHeader;
469    }
470 
471    /***
472     * Set how to render the header in case of nested value list (the default is <code>true</code>).
473     * @param nestedHeader 
474     *        <code>false</code> - the header of the nested value list is rendered as a part of the top value list 
475     *        <code>true</code> - the header is rendered for each nested valuelist
476     */
477    public void setNestedHeader(boolean nestedHeader)
478    {
479       this.nestedHeader = nestedHeader;
480    }
481 
482    /***
483     * @return style 
484     * @throws JspException 
485     */
486    public String getRowStyleClass() throws JspException
487    {
488       ValueListConfigBean config = getRootTag().getConfig();
489       if (currentRowNumber == getRootTag().getValueList().getValueListInfo().getFocusedRowNumberInTable())
490       {
491          return config.getFocusedRowStyle();
492 
493       }
494       else
495       {
496          return config.getStylePrefix() + (currentRowNumber % config.getStyleCount());
497 
498       }
499    }
500 
501    private void reset()
502    {
503       this.rootTag = null;
504       this.bean = null;
505       this.beanInPageScope = null;
506       this.columns.clear();
507       this.currentRowNumber = 0;
508       this.disableHeader = false;
509       this.nestedHeader = true;
510       this.displayProvider = null;
511       this.currentRowNumber = 0;
512       this.focusRowNumber = -1;
513    }
514 
515    /***
516     * Called on a Tag handler to release state.
517     * The page compiler guarantees that JSP page implementation
518     * objects will invoke this method on all tag handlers,
519     * but there may be multiple invocations on doStartTag and doEndTag in between.
520     * 
521     * @see javax.servlet.jsp.tagext.Tag#release()
522     */
523    public void release()
524    {
525       super.release();
526       reset();
527    }
528 }