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.text.Collator;
15 import java.util.ArrayList;
16 import java.util.Comparator;
17 import java.util.List;
18
19 import javax.servlet.http.HttpServletRequest;
20 import javax.servlet.http.HttpServletResponse;
21 import javax.servlet.jsp.JspException;
22 import javax.servlet.jsp.tagext.BodyTagSupport;
23
24 import org.apache.commons.lang.StringUtils;
25 import org.apache.commons.lang.builder.ToStringBuilder;
26 import org.apache.commons.lang.builder.ToStringStyle;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.displaytag.decorator.AutolinkColumnDecorator;
30 import org.displaytag.decorator.DisplaytagColumnDecorator;
31 import org.displaytag.decorator.EscapeXmlColumnDecorator;
32 import org.displaytag.decorator.MessageFormatColumnDecorator;
33 import org.displaytag.exception.DecoratorInstantiationException;
34 import org.displaytag.exception.InvalidTagAttributeValueException;
35 import org.displaytag.exception.ObjectLookupException;
36 import org.displaytag.exception.TagStructureException;
37 import org.displaytag.model.Cell;
38 import org.displaytag.model.DefaultComparator;
39 import org.displaytag.model.HeaderCell;
40 import org.displaytag.properties.MediaTypeEnum;
41 import org.displaytag.properties.SortOrderEnum;
42 import org.displaytag.util.DefaultHref;
43 import org.displaytag.util.Href;
44 import org.displaytag.util.HtmlAttributeMap;
45 import org.displaytag.util.MediaUtil;
46 import org.displaytag.util.MultipleHtmlAttribute;
47 import org.displaytag.util.TagConstants;
48
49
50 /**
51 * <p>
52 * This tag works hand in hand with the TableTag to display a list of objects. This describes a column of data in the
53 * TableTag. There can be any number of columns that make up the list.
54 * </p>
55 * <p>
56 * This tag does no work itself, it is simply a container of information. The TableTag does all the work based on the
57 * information provided in the attributes of this tag.
58 * <p>
59 * @author mraible
60 * @author Fabrizio Giustina
61 * @version $Revision: 1097 $ ($Author: rapruitt $)
62 */
63 public class ColumnTag extends BodyTagSupport implements MediaUtil.SupportsMedia
64 {
65
66 /**
67 * D1597A17A6.
68 */
69 private static final long serialVersionUID = 899149338534L;
70
71 /**
72 * logger.
73 */
74 private static Log log = LogFactory.getLog(ColumnTag.class);
75
76 /**
77 * html pass-through attributes for cells.
78 */
79 private HtmlAttributeMap attributeMap = new HtmlAttributeMap();
80
81 /**
82 * html pass-through attributes for cell headers.
83 */
84 private HtmlAttributeMap headerAttributeMap = new HtmlAttributeMap();
85
86 /**
87 * the property method that is called to retrieve the information to be displayed in this column. This method is
88 * called on the current object in the iteration for the given row. The property format is in typical struts format
89 * for properties (required)
90 */
91 private String property;
92
93 /**
94 * the title displayed for this column. if this is omitted then the property name is used for the title of the
95 * column (optional).
96 */
97 private String title;
98
99 /**
100 * by default, null values don't appear in the list, by setting viewNulls to 'true', then null values will appear as
101 * "null" in the list (mostly useful for debugging) (optional).
102 */
103 private boolean nulls;
104
105 /**
106 * is the column sortable?
107 */
108 private boolean sortable;
109
110 /**
111 * Name given to the server when sorting this column.
112 */
113 private String sortName;
114
115 /**
116 * Defalt sort order for this column.
117 */
118 private SortOrderEnum defaultorder;
119
120 /**
121 * The comparator to use when sorting this column.
122 */
123 private Comparator comparator;
124
125 /**
126 * if set to true, then any email addresses and URLs found in the content of the column are automatically converted
127 * into a hypertext link.
128 */
129 private boolean autolink;
130
131 /**
132 * Automatically escape column content for html and xml media.
133 */
134 private boolean escapeXml;
135
136 /**
137 * A MessageFormat patter that will be used to decorate objects in the column. Can be used as a "shortcut" for
138 * simple column decorations.
139 */
140 private String format;
141
142 /**
143 * the grouping level (starting at 1 and incrementing) of this column (indicates if successive contain the same
144 * values, then they should not be displayed). The level indicates that if a lower level no longer matches, then the
145 * matching for this higher level should start over as well. If this attribute is not included, then no grouping is
146 * performed. (optional)
147 */
148 private int group = -1;
149
150 /**
151 * if this attribute is provided, then the data that is shown for this column is wrapped inside a <a href> tag
152 * with the url provided through this attribute. Typically you would use this attribute along with one of the
153 * struts-like param attributes below to create a dynamic link so that each row creates a different URL based on the
154 * data that is being viewed. (optional)
155 */
156 private Href href;
157
158 /**
159 * The name of the request parameter that will be dynamically added to the generated href URL. The corresponding
160 * value is defined by the paramProperty and (optional) paramName attributes, optionally scoped by the paramScope
161 * attribute. (optional)
162 */
163 private String paramId;
164
165 /**
166 * The name of a JSP bean that is a String containing the value for the request parameter named by paramId (if
167 * paramProperty is not specified), or a JSP bean whose property getter is called to return a String (if
168 * paramProperty is specified). The JSP bean is constrained to the bean scope specified by the paramScope property,
169 * if it is specified. If paramName is omitted, then it is assumed that the current object being iterated on is the
170 * target bean. (optional)
171 */
172 private String paramName;
173
174 /**
175 * The name of a property of the bean specified by the paramName attribute (or the current object being iterated on
176 * if paramName is not provided), whose return value must be a String containing the value of the request parameter
177 * (named by the paramId attribute) that will be dynamically added to this href URL. (optional)
178 * @deprecated use Expressions in paramName
179 */
180 private String paramProperty;
181
182 /**
183 * The scope within which to search for the bean specified by the paramName attribute. If not specified, all scopes
184 * are searched. If paramName is not provided, then the current object being iterated on is assumed to be the target
185 * bean. (optional)
186 * @deprecated use Expressions in paramName
187 */
188 private String paramScope;
189
190 /**
191 * If this attribute is provided, then the column's displayed is limited to this number of characters. An elipse
192 * (...) is appended to the end if this column is linked, and the user can mouseover the elipse to get the full
193 * text. (optional)
194 */
195 private int maxLength;
196
197 /**
198 * If this attribute is provided, then the column's displayed is limited to this number of words. An elipse (...) is
199 * appended to the end if this column is linked, and the user can mouseover the elipse to get the full text.
200 * (optional)
201 */
202 private int maxWords;
203
204 /**
205 * a class that should be used to "decorate" the underlying object being displayed. If a decorator is specified for
206 * the entire table, then this decorator will decorate that decorator. (optional)
207 */
208 private String decorator;
209
210 /**
211 * is the column already sorted?
212 */
213 private boolean alreadySorted;
214
215 /**
216 * The media supported attribute.
217 */
218 private List supportedMedia;
219
220 /**
221 * Property in a resource bundle to be used as the title for the column.
222 */
223 private String titleKey;
224
225 /**
226 * The name of the bean property if a decorator is used and sorting need to be still on on the property itself.
227 * Useful for displaying data with links but sorting on original value.
228 */
229 private String sortProperty;
230
231 /**
232 * Should the value of the column be summed? Requires that the value of the column be convertible to a Number.
233 */
234 private boolean totaled;
235
236 /**
237 * Static value for this cell, equivalent to column body.
238 */
239 private Object value;
240
241 /**
242 * Setter for totals.
243 * @param totals the value
244 */
245 public void setTotal(boolean totals)
246 {
247 this.totaled = totals;
248 }
249
250 /**
251 * setter for the "property" tag attribute.
252 * @param value attribute value
253 */
254 public void setProperty(String value)
255 {
256 this.property = value;
257 }
258
259 /**
260 * setter for the "value" tag attribute.
261 * @param value attribute value
262 */
263 public void setValue(Object value)
264 {
265 this.value = value;
266 }
267
268 /**
269 * Set the comparator, classname or object.
270 * @param comparatorObj the comparator, classname or object
271 */
272 public void setComparator(Object comparatorObj)
273 {
274 // @todo don't do this! Setters should remains simple setters and any evaluation should be done in doEndTag()!
275 if (comparatorObj instanceof Comparator)
276 {
277 this.comparator = (Comparator) comparatorObj;
278 }
279 else if (comparatorObj instanceof String)
280 {
281 String comparatorClassname = (String) comparatorObj;
282 Class compClass;
283 try
284 {
285 compClass = Thread.currentThread().getContextClassLoader().loadClass(comparatorClassname);
286 }
287 catch (ClassNotFoundException e)
288 {
289 throw new RuntimeException("InstantiationException setting column comparator as "
290 + comparatorClassname
291 + ": "
292 + e.getMessage(), e);
293 }
294 try
295 {
296 this.comparator = (Comparator) compClass.newInstance();
297 }
298 catch (InstantiationException e)
299 {
300 throw new RuntimeException("InstantiationException setting column comparator as "
301 + comparatorClassname
302 + ": "
303 + e.getMessage(), e);
304 }
305 catch (IllegalAccessException e)
306 {
307 throw new RuntimeException("IllegalAccessException setting column comparator as "
308 + comparatorClassname
309 + ": "
310 + e.getMessage(), e);
311 }
312 }
313 else
314 {
315 throw new IllegalArgumentException("Value for comparator: "
316 + comparatorObj
317 + " of type "
318 + comparatorObj.getClass().getName());
319 }
320 }
321
322 /**
323 * setter for the "title" tag attribute.
324 * @param value attribute value
325 */
326 public void setTitle(String value)
327 {
328 this.title = value;
329 }
330
331 /**
332 * setter for the "format" tag attribute.
333 * @param value attribute value
334 */
335 public void setFormat(String value)
336 {
337 this.format = value;
338 }
339
340 /**
341 * setter for the "nulls" tag attribute.
342 * @param value attribute value
343 */
344 public void setNulls(boolean value)
345 {
346 this.nulls = value;
347 }
348
349 /**
350 * setter for the "sortable" tag attribute.
351 * @param value attribute value
352 */
353 public void setSortable(boolean value)
354 {
355 this.sortable = value;
356 }
357
358 /**
359 * setter for the "autolink" tag attribute.
360 * @param value attribute value
361 */
362 public void setAutolink(boolean value)
363 {
364 this.autolink = value;
365 }
366
367 /**
368 * setter for the "escapeXml" tag attribute.
369 * @param value attribute value
370 */
371 public void setEscapeXml(boolean value)
372 {
373 this.escapeXml = value;
374 }
375
376 /**
377 * setter for the "group" tag attribute.
378 * @param value attribute value
379 */
380 public void setGroup(int value)
381 {
382 this.group = value;
383 }
384
385 /**
386 * setter for the "titleKey" tag attribute.
387 * @param value property name
388 */
389 public void setTitleKey(String value)
390 {
391 this.titleKey = value;
392 }
393
394 /**
395 * setter for the "href" tag attribute.
396 * @param value attribute value
397 */
398 public void setHref(String value)
399 {
400 // call encodeURL to preserve session id when cookies are disabled
401 String encodedHref = ((HttpServletResponse) this.pageContext.getResponse()).encodeURL(StringUtils
402 .defaultString(value));
403 this.href = new DefaultHref(encodedHref);
404 }
405
406 /**
407 * setter for the "url" tag attribute. This has the same meaning of href, but prepends the context path to the given
408 * URI.
409 * @param value attribute value
410 */
411 public void setUrl(String value)
412 {
413 HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
414 // call encodeURL to preserve session id when cookies are disabled
415 String encodedHref = ((HttpServletResponse) this.pageContext.getResponse()).encodeURL(StringUtils
416 .defaultString(req.getContextPath() + value));
417 this.href = new DefaultHref(encodedHref);
418 }
419
420 /**
421 * setter for the "paramId" tag attribute.
422 * @param value attribute value
423 */
424 public void setParamId(String value)
425 {
426 this.paramId = value;
427 }
428
429 /**
430 * setter for the "paramName" tag attribute.
431 * @param value attribute value
432 */
433 public void setParamName(String value)
434 {
435 this.paramName = value;
436 }
437
438 /**
439 * setter for the "paramProperty" tag attribute.
440 * @param value attribute value
441 */
442 public void setParamProperty(String value)
443 {
444 this.paramProperty = value;
445 }
446
447 /**
448 * setter for the "paramScope" tag attribute.
449 * @param value attribute value
450 */
451 public void setParamScope(String value)
452 {
453 this.paramScope = value;
454 }
455
456 /**
457 * setter for the "scope" tag attribute.
458 * @param value attribute value
459 */
460 public void setScope(String value)
461 {
462 this.attributeMap.put(TagConstants.ATTRIBUTE_SCOPE, value);
463 }
464
465 /**
466 * setter for the "headerScope" tag attribute.
467 * @param value attribute value
468 */
469 public void setHeaderScope(String value)
470 {
471 this.headerAttributeMap.put(TagConstants.ATTRIBUTE_SCOPE, value);
472 }
473
474 /**
475 * setter for the "maxLength" tag attribute.
476 * @param value attribute value
477 */
478 public void setMaxLength(int value)
479 {
480 this.maxLength = value;
481 }
482
483 /**
484 * setter for the "maxWords" tag attribute.
485 * @param value attribute value
486 */
487 public void setMaxWords(int value)
488 {
489 this.maxWords = value;
490 }
491
492 /**
493 * setter for the "style" tag attribute.
494 * @param value attribute value
495 */
496 public void setStyle(String value)
497 {
498 this.attributeMap.put(TagConstants.ATTRIBUTE_STYLE, value);
499 }
500
501 /**
502 * setter for the "class" tag attribute.
503 * @param value attribute value
504 */
505 public void setClass(String value)
506 {
507 this.attributeMap.put(TagConstants.ATTRIBUTE_CLASS, new MultipleHtmlAttribute(value));
508 }
509
510 /**
511 * setter for the "headerClass" tag attribute.
512 * @param value attribute value
513 */
514 public void setHeaderClass(String value)
515 {
516 this.headerAttributeMap.put(TagConstants.ATTRIBUTE_CLASS, new MultipleHtmlAttribute(value));
517 }
518
519 /**
520 * setter for the "decorator" tag attribute.
521 * @param value attribute value
522 */
523 public void setDecorator(String value)
524 {
525 this.decorator = value;
526 }
527
528 /**
529 * setter for the "sortProperty" tag attribute.
530 * @param value attribute value
531 */
532 public void setSortProperty(String value)
533 {
534 this.sortProperty = value;
535 }
536
537 /**
538 * Looks up the parent table tag.
539 * @return a table tag instance.
540 */
541 protected TableTag getTableTag()
542 {
543 return (TableTag) findAncestorWithClass(this, TableTag.class);
544 }
545
546 /**
547 * Tag setter.
548 * @param media the space delimited list of supported types
549 */
550 public void setMedia(String media)
551 {
552 MediaUtil.setMedia(this, media);
553 }
554
555 /**
556 * @see org.displaytag.util.MediaUtil.SupportsMedia#setSupportedMedia(java.util.List)
557 */
558 public void setSupportedMedia(List media)
559 {
560 this.supportedMedia = media;
561 }
562
563 /**
564 * @see org.displaytag.util.MediaUtil.SupportsMedia#getSupportedMedia()
565 */
566 public List getSupportedMedia()
567 {
568 return this.supportedMedia;
569 }
570
571 /**
572 * sets the name given to the server when sorting this column
573 * @param sortName name given to the server to sort this column
574 */
575 public void setSortName(String sortName)
576 {
577 this.sortName = sortName;
578 }
579
580 /**
581 * sets the sorting order for the sorted column.
582 * @param value "ascending" or "descending"
583 * @throws InvalidTagAttributeValueException if value is not one of "ascending" or "descending"
584 */
585 public void setDefaultorder(String value) throws InvalidTagAttributeValueException
586 {
587 this.defaultorder = SortOrderEnum.fromName(value);
588 if (this.defaultorder == null)
589 {
590 throw new InvalidTagAttributeValueException(getClass(), "defaultorder", value); //$NON-NLS-1$
591 }
592 }
593
594 /**
595 * Passes attribute information up to the parent TableTag.
596 * <p>
597 * When we hit the end of the tag, we simply let our parent (which better be a TableTag) know what the user wants to
598 * do with this column. We do that by simple registering this tag with the parent. This tag's only job is to hold
599 * the configuration information to describe this particular column. The TableTag does all the work.
600 * </p>
601 * @return int
602 * @throws JspException if this tag is being used outside of a <display:list...> tag.
603 * @see javax.servlet.jsp.tagext.Tag#doEndTag()
604 */
605 public int doEndTag() throws JspException
606 {
607 TableTag tableTag = getTableTag();
608
609 MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA);
610 if (currentMediaType != null && !MediaUtil.availableForMedia(this, currentMediaType))
611 {
612 if (log.isDebugEnabled())
613 {
614 log.debug("skipping column body, currentMediaType=" + currentMediaType);
615 }
616 return SKIP_BODY;
617 }
618
619 // add column header only once
620 if (tableTag.isFirstIteration())
621 {
622 addHeaderToTable(tableTag);
623 }
624
625 if (!tableTag.isIncludedRow())
626 {
627 return super.doEndTag();
628 }
629
630 Cell cell = null;
631 if (this.property == null && this.value != null)
632 {
633 cell = new Cell(value);
634 }
635 else if (this.property == null && this.bodyContent != null)
636 {
637 cell = new Cell(this.bodyContent.getString());
638 }
639
640 Object rowStyle = this.attributeMap.get(TagConstants.ATTRIBUTE_STYLE);
641 Object rowClass = this.attributeMap.get(TagConstants.ATTRIBUTE_CLASS);
642 if (rowStyle != null || rowClass != null)
643 {
644 HtmlAttributeMap perRowValues = new HtmlAttributeMap();
645 if (rowStyle != null)
646 {
647 perRowValues.put(TagConstants.ATTRIBUTE_STYLE, rowStyle);
648 }
649 if (rowClass != null)
650 {
651 perRowValues.put(TagConstants.ATTRIBUTE_CLASS, rowClass);
652 }
653 if (cell == null)
654 {
655 cell = new Cell(null);
656 }
657 cell.setPerRowAttributes(perRowValues);
658 }
659
660 tableTag.addCell(cell != null ? cell : Cell.EMPTY_CELL);
661
662 // cleanup non-attribute variables
663 this.alreadySorted = false;
664
665 return super.doEndTag();
666 }
667
668 /**
669 * Adds the current header to the table model calling addColumn in the parent table tag. This method should be
670 * called only at first iteration.
671 * @param tableTag parent table tag
672 * @throws DecoratorInstantiationException for error during column decorator instantiation
673 * @throws ObjectLookupException for errors in looking up values
674 */
675 private void addHeaderToTable(TableTag tableTag) throws DecoratorInstantiationException, ObjectLookupException
676 {
677 // don't modify "title" directly
678 String evalTitle = this.title;
679
680 // title has precedence over titleKey
681 if (evalTitle == null && (this.titleKey != null || this.property != null))
682 {
683 // handle title i18n
684 evalTitle = tableTag.getProperties().geResourceProvider().getResource(
685 this.titleKey,
686 this.property,
687 tableTag,
688 this.pageContext);
689 }
690
691 HeaderCell headerCell = new HeaderCell();
692 headerCell.setHeaderAttributes((HtmlAttributeMap) this.headerAttributeMap.clone());
693 headerCell.setHtmlAttributes((HtmlAttributeMap) this.attributeMap.clone());
694 headerCell.setTitle(evalTitle);
695 headerCell.setSortable(this.sortable);
696
697 List decorators = new ArrayList();
698
699 // handle multiple chained decorators, whitespace separated
700 if (StringUtils.isNotEmpty(this.decorator))
701 {
702 String[] decoratorNames = StringUtils.split(this.decorator);
703 for (int j = 0; j < decoratorNames.length; j++)
704 {
705 decorators.add(tableTag.getProperties().getDecoratorFactoryInstance().loadColumnDecorator(
706 this.pageContext,
707 decoratorNames[j]));
708 }
709 }
710
711 // "special" decorators
712 if (this.escapeXml)
713 {
714 decorators.add(EscapeXmlColumnDecorator.INSTANCE);
715 }
716 if (this.autolink)
717 {
718 decorators.add(AutolinkColumnDecorator.INSTANCE);
719 }
720 if (StringUtils.isNotBlank(this.format))
721 {
722 decorators.add(new MessageFormatColumnDecorator(this.format, tableTag.getProperties().getLocale()));
723 }
724
725 headerCell.setColumnDecorators((DisplaytagColumnDecorator[]) decorators
726 .toArray(new DisplaytagColumnDecorator[decorators.size()]));
727
728 headerCell.setBeanPropertyName(this.property);
729 headerCell.setShowNulls(this.nulls);
730 headerCell.setMaxLength(this.maxLength);
731 headerCell.setMaxWords(this.maxWords);
732 headerCell.setGroup(this.group);
733 headerCell.setSortProperty(this.sortProperty);
734 headerCell.setTotaled(this.totaled);
735
736 Comparator headerComparator = (comparator != null) ? comparator : tableTag.getProperties().getDefaultComparator();
737
738 headerCell.setComparator(headerComparator);
739 headerCell.setDefaultSortOrder(this.defaultorder);
740 headerCell.setSortName(this.sortName);
741
742 // href and parameter, create link
743 if (this.href != null)
744 {
745 Href colHref;
746
747 // empty base url, use href with parameters from parent table
748 if (StringUtils.isEmpty(this.href.getBaseUrl()))
749 {
750 colHref = (Href) tableTag.getBaseHref().clone();
751 }
752 else
753 {
754 colHref = (Href) this.href.clone();
755 }
756
757 if (this.paramId != null)
758 {
759 // parameter value is in a different object than the iterated one
760 if (this.paramName != null || this.paramScope != null)
761 {
762 // create a complete string for compatibility with previous version before expression evaluation.
763 // this approach is optimized for new expressions, not for previous property/scope parameters
764 StringBuffer expression = new StringBuffer();
765
766 // append scope
767 if (StringUtils.isNotBlank(this.paramScope))
768 {
769 expression.append(this.paramScope).append("Scope.");
770 }
771
772 // base bean name
773 if (this.paramId != null)
774 {
775 expression.append(this.paramName);
776 }
777 else
778 {
779 expression.append(tableTag.getName());
780 }
781
782 // append property
783 if (StringUtils.isNotBlank(this.paramProperty))
784 {
785 expression.append('.').append(this.paramProperty);
786 }
787
788 // evaluate expression.
789 // note the value is fixed, not based on any object created during iteration
790 // this is here for compatibility with the old version mainly
791 Object paramValue = tableTag.evaluateExpression(expression.toString());
792
793 // add parameter
794 colHref.addParameter(this.paramId, paramValue);
795 }
796 else
797 {
798 // set id
799 headerCell.setParamName(this.paramId);
800
801 // set property
802 headerCell.setParamProperty(this.paramProperty);
803 }
804 }
805
806 // sets the base href
807 headerCell.setHref(colHref);
808
809 }
810
811 tableTag.addColumn(headerCell);
812
813 if (log.isDebugEnabled())
814 {
815 log.debug("columnTag.addHeaderToTable() :: first iteration - adding header " + headerCell);
816 }
817 }
818
819 /**
820 * @see javax.servlet.jsp.tagext.Tag#release()
821 */
822 public void release()
823 {
824 super.release();
825 this.attributeMap.clear();
826 this.autolink = false;
827 this.decorator = null;
828 this.group = -1;
829 this.headerAttributeMap.clear();
830 this.href = null;
831 this.maxLength = 0;
832 this.maxWords = 0;
833 this.nulls = false;
834 this.paramId = null;
835 this.paramName = null;
836 this.paramProperty = null;
837 this.paramScope = null;
838 this.property = null;
839 this.sortable = false;
840 this.sortName = null;
841 this.supportedMedia = null;
842 this.title = null;
843 this.titleKey = null;
844 this.sortProperty = null;
845 this.comparator = null;
846 this.defaultorder = null;
847 this.escapeXml = false;
848 this.format = null;
849 this.value = null;
850 this.totaled = false;
851 }
852
853 /**
854 * @see javax.servlet.jsp.tagext.Tag#doStartTag()
855 */
856 public int doStartTag() throws JspException
857 {
858 TableTag tableTag = getTableTag();
859 if (tableTag == null)
860 {
861 throw new TagStructureException(getClass(), "column", "table");
862 }
863
864 // If the list is empty, do not execute the body; may result in NPE
865 if (tableTag.isEmpty() || !tableTag.isIncludedRow())
866 {
867 return SKIP_BODY;
868 }
869
870 MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA);
871 if (!MediaUtil.availableForMedia(this, currentMediaType))
872 {
873 return SKIP_BODY;
874 }
875
876 return super.doStartTag();
877 }
878
879 /**
880 * @see java.lang.Object#toString()
881 */
882 public String toString()
883 {
884 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) //
885 .append("bodyContent", this.bodyContent) //$NON-NLS-1$
886 .append("group", this.group) //$NON-NLS-1$
887 .append("maxLength", this.maxLength) //$NON-NLS-1$
888 .append("decorator", this.decorator) //$NON-NLS-1$
889 .append("href", this.href) //$NON-NLS-1$
890 .append("title", this.title) //$NON-NLS-1$
891 .append("paramScope", this.paramScope) //$NON-NLS-1$
892 .append("property", this.property) //$NON-NLS-1$
893 .append("paramProperty", this.paramProperty) //$NON-NLS-1$
894 .append("headerAttributeMap", this.headerAttributeMap) //$NON-NLS-1$
895 .append("paramName", this.paramName) //$NON-NLS-1$
896 .append("autolink", this.autolink) //$NON-NLS-1$
897 .append("format", this.format) //$NON-NLS-1$
898 .append("nulls", this.nulls) //$NON-NLS-1$
899 .append("maxWords", this.maxWords) //$NON-NLS-1$
900 .append("attributeMap", this.attributeMap) //$NON-NLS-1$
901 .append("sortable", this.sortable) //$NON-NLS-1$
902 .append("paramId", this.paramId) //$NON-NLS-1$
903 .append("alreadySorted", this.alreadySorted) //$NON-NLS-1$
904 .append("sortProperty", this.sortProperty) //$NON-NLS-1$
905 .append("defaultSortOrder", this.defaultorder) //$NON-NLS-1$
906 .toString();
907 }
908
909 }