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
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
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
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
180 return SKIP_BODY;
181 }
182
183
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
348
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 }