• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / displaytag / 1753

12 Feb 2026 03:17AM UTC coverage: 77.321% (-0.01%) from 77.334%
1753

push

github

web-flow
Merge pull request #1102 from hazendaz/renovate/javax-support-logback-monorepo

Update dependency ch.qos.logback:logback-classic to v1.5.29 (javax-support)

1438 of 2003 branches covered (71.79%)

Branch coverage included in aggregate %.

4034 of 5074 relevant lines covered (79.5%)

0.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.44
/displaytag/src/main/java/org/displaytag/tags/ColumnTag.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2002-2026 Fabrizio Giustina, the Displaytag team
6
 */
7
package org.displaytag.tags;
8

9
import java.lang.reflect.InvocationTargetException;
10
import java.util.ArrayList;
11
import java.util.Comparator;
12
import java.util.List;
13

14
import javax.servlet.http.HttpServletRequest;
15
import javax.servlet.http.HttpServletResponse;
16
import javax.servlet.jsp.JspException;
17
import javax.servlet.jsp.tagext.BodyTagSupport;
18
import javax.servlet.jsp.tagext.Tag;
19
import javax.servlet.jsp.tagext.TagSupport;
20

21
import org.apache.commons.lang3.StringUtils;
22
import org.apache.commons.lang3.builder.ToStringBuilder;
23
import org.apache.commons.lang3.builder.ToStringStyle;
24
import org.displaytag.decorator.AutolinkColumnDecorator;
25
import org.displaytag.decorator.DisplaytagColumnDecorator;
26
import org.displaytag.decorator.EscapeXmlColumnDecorator;
27
import org.displaytag.decorator.MessageFormatColumnDecorator;
28
import org.displaytag.exception.DecoratorInstantiationException;
29
import org.displaytag.exception.InvalidTagAttributeValueException;
30
import org.displaytag.exception.ObjectLookupException;
31
import org.displaytag.exception.TagStructureException;
32
import org.displaytag.model.Cell;
33
import org.displaytag.model.HeaderCell;
34
import org.displaytag.properties.MediaTypeEnum;
35
import org.displaytag.properties.SortOrderEnum;
36
import org.displaytag.util.DefaultHref;
37
import org.displaytag.util.Href;
38
import org.displaytag.util.HtmlAttributeMap;
39
import org.displaytag.util.MediaUtil;
40
import org.displaytag.util.MultipleHtmlAttribute;
41
import org.displaytag.util.TagConstants;
42
import org.slf4j.Logger;
43
import org.slf4j.LoggerFactory;
44

45
/**
46
 * This tag works hand in hand with the TableTag to display a list of objects. This describes a column of data in the
47
 * TableTag. There can be any number of columns that make up the list.
48
 * <p>
49
 * This tag does no work itself, it is simply a container of information. The TableTag does all the work based on the
50
 * information provided in the attributes of this tag.
51
 */
52
public class ColumnTag extends BodyTagSupport implements MediaUtil.SupportsMedia {
1✔
53

54
    /**
55
     * Serial ID.
56
     */
57
    private static final long serialVersionUID = 899149338534L;
58

59
    /**
60
     * logger.
61
     */
62
    private static Logger log = LoggerFactory.getLogger(ColumnTag.class);
1✔
63

64
    /**
65
     * html pass-through attributes for cells.
66
     */
67
    private final HtmlAttributeMap attributeMap = new HtmlAttributeMap();
1✔
68

69
    /**
70
     * html pass-through attributes for cell headers.
71
     */
72
    private final HtmlAttributeMap headerAttributeMap = new HtmlAttributeMap();
1✔
73

74
    /**
75
     * the property method that is called to retrieve the information to be displayed in this column. This method is
76
     * called on the current object in the iteration for the given row. The property format is in typical struts format
77
     * for properties (required)
78
     */
79
    private String property;
80

81
    /**
82
     * the title displayed for this column. if this is omitted then the property name is used for the title of the
83
     * column (optional).
84
     */
85
    private String title;
86

87
    /**
88
     * by default, null values don't appear in the list, by setting viewNulls to 'true', then null values will appear as
89
     * "null" in the list (mostly useful for debugging) (optional).
90
     */
91
    private boolean nulls;
92

93
    /** is the column sortable?. */
94
    private boolean sortable;
95

96
    /**
97
     * Name given to the server when sorting this column.
98
     */
99
    private String sortName;
100

101
    /**
102
     * Defalt sort order for this column.
103
     */
104
    private SortOrderEnum defaultorder;
105

106
    /**
107
     * The comparator to use when sorting this column.
108
     */
109
    private transient Comparator<Object> comparator;
110

111
    /**
112
     * if set to true, then any email addresses and URLs found in the content of the column are automatically converted
113
     * into a hypertext link.
114
     */
115
    private boolean autolink;
116

117
    /**
118
     * Automatically escape column content for html and xml media.
119
     */
120
    private Boolean escapeXml;
121

122
    /**
123
     * A MessageFormat patter that will be used to decorate objects in the column. Can be used as a "shortcut" for
124
     * simple column decorations.
125
     */
126
    private String format;
127

128
    /**
129
     * the grouping level (starting at 1 and incrementing) of this column (indicates if successive contain the same
130
     * values, then they should not be displayed). The level indicates that if a lower level no longer matches, then the
131
     * matching for this higher level should start over as well. If this attribute is not included, then no grouping is
132
     * performed. (optional)
133
     */
134
    private int group = -1;
1✔
135

136
    /**
137
     * if this attribute is provided, then the data that is shown for this column is wrapped inside a &lt;a href&gt; tag
138
     * with the url provided through this attribute. Typically you would use this attribute along with one of the
139
     * struts-like param attributes below to create a dynamic link so that each row creates a different URL based on the
140
     * data that is being viewed. (optional)
141
     */
142
    private Href href;
143

144
    /**
145
     * The name of the request parameter that will be dynamically added to the generated href URL. The corresponding
146
     * value is defined by the paramProperty and (optional) paramName attributes, optionally scoped by the paramScope
147
     * attribute. (optional)
148
     */
149
    private String paramId;
150

151
    /**
152
     * The name of a JSP bean that is a String containing the value for the request parameter named by paramId (if
153
     * paramProperty is not specified), or a JSP bean whose property getter is called to return a String (if
154
     * paramProperty is specified). The JSP bean is constrained to the bean scope specified by the paramScope property,
155
     * if it is specified. If paramName is omitted, then it is assumed that the current object being iterated on is the
156
     * target bean. (optional)
157
     */
158
    private String paramName;
159

160
    /**
161
     * The name of a property of the bean specified by the paramName attribute (or the current object being iterated on
162
     * if paramName is not provided), whose return value must be a String containing the value of the request parameter
163
     * (named by the paramId attribute) that will be dynamically added to this href URL. (optional)
164
     *
165
     * @deprecated use Expressions in paramName
166
     */
167
    @Deprecated
168
    private String paramProperty;
169

170
    /**
171
     * If this attribute is provided, then the column's displayed is limited to this number of characters. An elipse
172
     * (...) is appended to the end if this column is linked, and the user can mouseover the elipse to get the full
173
     * text. (optional)
174
     */
175
    private int maxLength;
176

177
    /**
178
     * If this attribute is provided, then the column's displayed is limited to this number of words. An elipse (...) is
179
     * appended to the end if this column is linked, and the user can mouseover the elipse to get the full text.
180
     * (optional)
181
     */
182
    private int maxWords;
183

184
    /**
185
     * a class that should be used to "decorate" the underlying object being displayed. If a decorator is specified for
186
     * the entire table, then this decorator will decorate that decorator. (optional)
187
     */
188
    private String decorator;
189

190
    /** is the column already sorted?. */
191
    private boolean alreadySorted;
192

193
    /**
194
     * The media supported attribute.
195
     */
196
    private transient List<MediaTypeEnum> supportedMedia;
197

198
    /**
199
     * Property in a resource bundle to be used as the title for the column.
200
     */
201
    private String titleKey;
202

203
    /**
204
     * The name of the bean property if a decorator is used and sorting need to be still on on the property itself.
205
     * Useful for displaying data with links but sorting on original value.
206
     */
207
    private String sortProperty;
208

209
    /**
210
     * Should the value of the column be summed? Requires that the value of the column be convertible to a Number.
211
     */
212
    private boolean totaled;
213

214
    /**
215
     * Static value for this cell, equivalent to column body.
216
     */
217
    private transient Object value;
218

219
    /**
220
     * Setter for totals.
221
     *
222
     * @param totals
223
     *            the value
224
     */
225
    public void setTotal(final boolean totals) {
226
        this.totaled = totals;
1✔
227
    }
1✔
228

229
    /**
230
     * setter for the "property" tag attribute.
231
     *
232
     * @param value
233
     *            attribute value
234
     */
235
    public void setProperty(final String value) {
236
        this.property = value;
1✔
237
    }
1✔
238

239
    /**
240
     * setter for the "value" tag attribute.
241
     *
242
     * @param value
243
     *            attribute value
244
     */
245
    public void setValue(final Object value) {
246
        this.value = value;
1✔
247
    }
1✔
248

249
    /**
250
     * Set the comparator, classname or object.
251
     *
252
     * @param comparatorObj
253
     *            the comparator, classname or object
254
     */
255
    public void setComparator(final Object comparatorObj) {
256
        // @todo don't do this! Setters should remains simple setters and any evaluation should be done in doEndTag()!
257
        if (comparatorObj instanceof Comparator) {
1!
258
            this.comparator = (Comparator<Object>) comparatorObj;
×
259
        } else if (comparatorObj instanceof String) {
1!
260
            final String comparatorClassname = (String) comparatorObj;
1✔
261
            Class<Comparator<Object>> compClass;
262
            try {
263
                compClass = (Class<Comparator<Object>>) Thread.currentThread().getContextClassLoader()
1✔
264
                        .loadClass(comparatorClassname);
1✔
265
            } catch (final ClassNotFoundException e) {
×
266
                throw new RuntimeException("InstantiationException setting column comparator as " + comparatorClassname
×
267
                        + ": " + e.getMessage(), e);
×
268
            }
1✔
269
            try {
270
                this.comparator = compClass.getDeclaredConstructor().newInstance();
1✔
271
            } catch (final InstantiationException e) {
×
272
                throw new RuntimeException("InstantiationException setting column comparator as " + comparatorClassname
×
273
                        + ": " + e.getMessage(), e);
×
274
            } catch (final IllegalAccessException e) {
×
275
                throw new RuntimeException("IllegalAccessException setting column comparator as " + comparatorClassname
×
276
                        + ": " + e.getMessage(), e);
×
277
            } catch (IllegalArgumentException e) {
×
278
                throw new RuntimeException("IllegalArgumentException setting column comparator as "
×
279
                        + comparatorClassname + ": " + e.getMessage(), e);
×
280
            } catch (InvocationTargetException e) {
×
281
                throw new RuntimeException("InvocationTargetException setting column comparator as "
×
282
                        + comparatorClassname + ": " + e.getMessage(), e);
×
283
            } catch (NoSuchMethodException e) {
×
284
                throw new RuntimeException("NoSuchMethodException setting column comparator as " + comparatorClassname
×
285
                        + ": " + e.getMessage(), e);
×
286
            } catch (SecurityException e) {
×
287
                throw new RuntimeException(
×
288
                        "SecurityException setting column comparator as " + comparatorClassname + ": " + e.getMessage(),
×
289
                        e);
290
            }
1✔
291
        } else {
1✔
292
            throw new IllegalArgumentException(
×
293
                    "Value for comparator: " + comparatorObj + " of type " + comparatorObj.getClass().getName());
×
294
        }
295
    }
1✔
296

297
    /**
298
     * setter for the "title" tag attribute.
299
     *
300
     * @param value
301
     *            attribute value
302
     */
303
    public void setTitle(final String value) {
304
        this.title = value;
1✔
305
    }
1✔
306

307
    /**
308
     * setter for the "format" tag attribute.
309
     *
310
     * @param value
311
     *            attribute value
312
     */
313
    public void setFormat(final String value) {
314
        this.format = value;
1✔
315
    }
1✔
316

317
    /**
318
     * setter for the "nulls" tag attribute.
319
     *
320
     * @param value
321
     *            attribute value
322
     */
323
    public void setNulls(final boolean value) {
324
        this.nulls = value;
1✔
325
    }
1✔
326

327
    /**
328
     * setter for the "sortable" tag attribute.
329
     *
330
     * @param value
331
     *            attribute value
332
     */
333
    public void setSortable(final boolean value) {
334
        this.sortable = value;
1✔
335
    }
1✔
336

337
    /**
338
     * setter for the "autolink" tag attribute.
339
     *
340
     * @param value
341
     *            attribute value
342
     */
343
    public void setAutolink(final boolean value) {
344
        this.autolink = value;
×
345
    }
×
346

347
    /**
348
     * setter for the "escapeXml" tag attribute.
349
     *
350
     * @param value
351
     *            attribute value
352
     */
353
    public void setEscapeXml(final boolean value) {
354
        this.escapeXml = value;
1✔
355
    }
1✔
356

357
    /**
358
     * setter for the "group" tag attribute.
359
     *
360
     * @param value
361
     *            attribute value
362
     */
363
    public void setGroup(final int value) {
364
        this.group = value;
1✔
365
    }
1✔
366

367
    /**
368
     * setter for the "titleKey" tag attribute.
369
     *
370
     * @param value
371
     *            property name
372
     */
373
    public void setTitleKey(final String value) {
374
        this.titleKey = value;
1✔
375
    }
1✔
376

377
    /**
378
     * setter for the "href" tag attribute.
379
     *
380
     * @param value
381
     *            attribute value
382
     */
383
    public void setHref(final String value) {
384
        // call encodeURL to preserve session id when cookies are disabled
385
        final String encodedHref = ((HttpServletResponse) this.pageContext.getResponse())
1✔
386
                .encodeURL(StringUtils.defaultString(value));
1✔
387
        this.href = new DefaultHref(encodedHref);
1✔
388
    }
1✔
389

390
    /**
391
     * setter for the "url" tag attribute. This has the same meaning of href, but prepends the context path to the given
392
     * URI.
393
     *
394
     * @param value
395
     *            attribute value
396
     */
397
    public void setUrl(final String value) {
398
        final HttpServletRequest req = (HttpServletRequest) this.pageContext.getRequest();
1✔
399
        // call encodeURL to preserve session id when cookies are disabled
400
        final String encodedHref = ((HttpServletResponse) this.pageContext.getResponse())
1✔
401
                .encodeURL(StringUtils.defaultString(req.getContextPath() + value));
1✔
402
        this.href = new DefaultHref(encodedHref);
1✔
403
    }
1✔
404

405
    /**
406
     * setter for the "paramId" tag attribute.
407
     *
408
     * @param value
409
     *            attribute value
410
     */
411
    public void setParamId(final String value) {
412
        this.paramId = value;
1✔
413
    }
1✔
414

415
    /**
416
     * setter for the "paramName" tag attribute.
417
     *
418
     * @param value
419
     *            attribute value
420
     */
421
    public void setParamName(final String value) {
422
        this.paramName = value;
×
423
    }
×
424

425
    /**
426
     * setter for the "paramProperty" tag attribute.
427
     *
428
     * @param value
429
     *            attribute value
430
     */
431
    public void setParamProperty(final String value) {
432
        this.paramProperty = value;
1✔
433
    }
1✔
434

435
    /**
436
     * setter for the "scope" tag attribute.
437
     *
438
     * @param value
439
     *            attribute value
440
     */
441
    public void setScope(final String value) {
442
        this.attributeMap.put(TagConstants.ATTRIBUTE_SCOPE, value);
1✔
443
    }
1✔
444

445
    /**
446
     * setter for the "headerScope" tag attribute.
447
     *
448
     * @param value
449
     *            attribute value
450
     */
451
    public void setHeaderScope(final String value) {
452
        this.headerAttributeMap.put(TagConstants.ATTRIBUTE_SCOPE, value);
1✔
453
    }
1✔
454

455
    /**
456
     * setter for the "maxLength" tag attribute.
457
     *
458
     * @param value
459
     *            attribute value
460
     */
461
    public void setMaxLength(final int value) {
462
        this.maxLength = value;
1✔
463
    }
1✔
464

465
    /**
466
     * setter for the "maxWords" tag attribute.
467
     *
468
     * @param value
469
     *            attribute value
470
     */
471
    public void setMaxWords(final int value) {
472
        this.maxWords = value;
×
473
    }
×
474

475
    /**
476
     * setter for the "style" tag attribute.
477
     *
478
     * @param value
479
     *            attribute value
480
     */
481
    public void setStyle(final String value) {
482
        this.attributeMap.put(TagConstants.ATTRIBUTE_STYLE, value);
1✔
483
    }
1✔
484

485
    /**
486
     * setter for the "headerStyle" tag attribute.
487
     *
488
     * @param value
489
     *            attribute value
490
     */
491
    public void setHeaderStyle(final String value) {
492
        this.headerAttributeMap.put(TagConstants.ATTRIBUTE_STYLE, value);
1✔
493
    }
1✔
494

495
    /**
496
     * setter for the "class" tag attribute.
497
     *
498
     * @param value
499
     *            attribute value
500
     */
501
    public void setClass(final String value) {
502
        this.attributeMap.put(TagConstants.ATTRIBUTE_CLASS, new MultipleHtmlAttribute(value));
1✔
503
    }
1✔
504

505
    /**
506
     * setter for the "headerClass" tag attribute.
507
     *
508
     * @param value
509
     *            attribute value
510
     */
511
    public void setHeaderClass(final String value) {
512
        this.headerAttributeMap.put(TagConstants.ATTRIBUTE_CLASS, new MultipleHtmlAttribute(value));
1✔
513
    }
1✔
514

515
    /**
516
     * setter for the "decorator" tag attribute.
517
     *
518
     * @param value
519
     *            attribute value
520
     */
521
    public void setDecorator(final String value) {
522
        this.decorator = value;
1✔
523
    }
1✔
524

525
    /**
526
     * setter for the "sortProperty" tag attribute.
527
     *
528
     * @param value
529
     *            attribute value
530
     */
531
    public void setSortProperty(final String value) {
532
        this.sortProperty = value;
1✔
533
    }
1✔
534

535
    /**
536
     * Looks up the parent table tag.
537
     *
538
     * @return a table tag instance.
539
     */
540
    protected TableTag getTableTag() {
541
        return (TableTag) TagSupport.findAncestorWithClass(this, TableTag.class);
1✔
542
    }
543

544
    /**
545
     * Tag setter.
546
     *
547
     * @param media
548
     *            the space delimited list of supported types
549
     */
550
    public void setMedia(final String media) {
551
        MediaUtil.setMedia(this, media);
1✔
552
    }
1✔
553

554
    /**
555
     * Sets the supported media.
556
     *
557
     * @param media
558
     *            the new supported media
559
     *
560
     * @see org.displaytag.util.MediaUtil.SupportsMedia#setSupportedMedia(java.util.List)
561
     */
562
    @Override
563
    public void setSupportedMedia(final List<MediaTypeEnum> media) {
564
        this.supportedMedia = media;
1✔
565
    }
1✔
566

567
    /**
568
     * Gets the supported media.
569
     *
570
     * @return the supported media
571
     *
572
     * @see org.displaytag.util.MediaUtil.SupportsMedia#getSupportedMedia()
573
     */
574
    @Override
575
    public List<MediaTypeEnum> getSupportedMedia() {
576
        return this.supportedMedia;
1✔
577
    }
578

579
    /**
580
     * sets the name given to the server when sorting this column.
581
     *
582
     * @param sortName
583
     *            name given to the server to sort this column
584
     */
585
    public void setSortName(final String sortName) {
586
        this.sortName = sortName;
1✔
587
    }
1✔
588

589
    /**
590
     * sets the sorting order for the sorted column.
591
     *
592
     * @param value
593
     *            "ascending" or "descending"
594
     *
595
     * @throws InvalidTagAttributeValueException
596
     *             if value is not one of "ascending" or "descending"
597
     */
598
    public void setDefaultorder(final String value) throws InvalidTagAttributeValueException {
599
        this.defaultorder = SortOrderEnum.fromName(value);
1✔
600
        if (this.defaultorder == null) {
1!
601
            throw new InvalidTagAttributeValueException(this.getClass(), "defaultorder", value); //$NON-NLS-1$
×
602
        }
603
    }
1✔
604

605
    /**
606
     * Passes attribute information up to the parent TableTag.
607
     * <p>
608
     * When we hit the end of the tag, we simply let our parent (which better be a TableTag) know what the user wants to
609
     * do with this column. We do that by simple registering this tag with the parent. This tag's only job is to hold
610
     * the configuration information to describe this particular column. The TableTag does all the work.
611
     * </p>
612
     *
613
     * @return int
614
     *
615
     * @throws JspException
616
     *             if this tag is being used outside of a &lt;display:list...&gt; tag.
617
     *
618
     * @see javax.servlet.jsp.tagext.Tag#doEndTag()
619
     */
620
    @Override
621
    public int doEndTag() throws JspException {
622
        final TableTag tableTag = this.getTableTag();
1✔
623

624
        final MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext
1✔
625
                .findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA);
1✔
626
        if (currentMediaType != null && !MediaUtil.availableForMedia(this, currentMediaType)) {
1!
627
            if (ColumnTag.log.isDebugEnabled()) {
1!
628
                ColumnTag.log.debug("skipping column body, currentMediaType={}", currentMediaType);
×
629
            }
630
            tableTag.getTableModel().getColumnVisibilities().add(false);
1✔
631
            return Tag.SKIP_BODY;
1✔
632
        }
633
        tableTag.getTableModel().getColumnVisibilities().add(true);
1✔
634

635
        // add column header only once
636
        if (tableTag.isFirstIteration()) {
1✔
637
            this.addHeaderToTable(tableTag);
1✔
638
        }
639

640
        if (!tableTag.isIncludedRow()) {
1✔
641
            return super.doEndTag();
1✔
642
        }
643

644
        Cell cell = null;
1✔
645
        if (this.property == null && this.value != null) {
1✔
646
            cell = new Cell(this.value);
1✔
647
        } else if (this.property == null && this.bodyContent != null) {
1✔
648
            cell = new Cell(this.bodyContent.getString());
1✔
649
        }
650

651
        final Object rowStyle = this.attributeMap.get(TagConstants.ATTRIBUTE_STYLE);
1✔
652
        final Object rowClass = this.attributeMap.get(TagConstants.ATTRIBUTE_CLASS);
1✔
653
        if (rowStyle != null || rowClass != null) {
1!
654
            final HtmlAttributeMap perRowValues = new HtmlAttributeMap();
1✔
655
            if (rowStyle != null) {
1!
656
                perRowValues.put(TagConstants.ATTRIBUTE_STYLE, rowStyle);
1✔
657
            }
658
            if (rowClass != null) {
1✔
659
                perRowValues.put(TagConstants.ATTRIBUTE_CLASS, rowClass);
1✔
660
            }
661
            if (cell == null) {
1✔
662
                cell = new Cell(null);
1✔
663
            }
664
            cell.setPerRowAttributes(perRowValues);
1✔
665
        }
666

667
        tableTag.addCell(cell != null ? cell : Cell.EMPTY_CELL);
1✔
668

669
        // cleanup non-attribute variables
670
        this.alreadySorted = false;
1✔
671

672
        return super.doEndTag();
1✔
673
    }
674

675
    /**
676
     * Adds the current header to the table model calling addColumn in the parent table tag. This method should be
677
     * called only at first iteration.
678
     *
679
     * @param tableTag
680
     *            parent table tag
681
     *
682
     * @throws DecoratorInstantiationException
683
     *             for error during column decorator instantiation
684
     * @throws ObjectLookupException
685
     *             for errors in looking up values
686
     */
687
    private void addHeaderToTable(final TableTag tableTag)
688
            throws DecoratorInstantiationException, ObjectLookupException {
689
        // don't modify "title" directly
690
        String evalTitle = this.title;
1✔
691

692
        // title has precedence over titleKey
693
        if (evalTitle == null && (this.titleKey != null || this.property != null)) {
1✔
694
            // handle title i18n
695
            evalTitle = tableTag.getProperties().geResourceProvider().getResource(this.titleKey, this.property,
1✔
696
                    tableTag, this.pageContext);
697
        }
698

699
        final HeaderCell headerCell = new HeaderCell();
1✔
700
        headerCell.setHeaderAttributes((HtmlAttributeMap) this.headerAttributeMap.clone());
1✔
701
        headerCell.setHtmlAttributes((HtmlAttributeMap) this.attributeMap.clone());
1✔
702
        headerCell.setTitle(evalTitle);
1✔
703
        headerCell.setSortable(this.sortable);
1✔
704

705
        final List<DisplaytagColumnDecorator> decorators = new ArrayList<>();
1✔
706

707
        // handle multiple chained decorators, whitespace separated
708
        if (StringUtils.isNotEmpty(this.decorator)) {
1✔
709
            final String[] decoratorNames = StringUtils.split(this.decorator);
1✔
710
            for (final String decoratorName : decoratorNames) {
1✔
711
                decorators.add(tableTag.getProperties().getDecoratorFactoryInstance()
1✔
712
                        .loadColumnDecorator(this.pageContext, decoratorName));
1✔
713
            }
714
        }
715

716
        // "special" decorators
717
        if (this.escapeXml != null && this.escapeXml) {
1✔
718
            decorators.add(EscapeXmlColumnDecorator.INSTANCE);
1✔
719
        }
720
        if (this.autolink) {
1!
721
            decorators.add(AutolinkColumnDecorator.INSTANCE);
×
722
        }
723
        if (StringUtils.isNotBlank(this.format)) {
1✔
724
            decorators.add(new MessageFormatColumnDecorator(this.format, tableTag.getProperties().getLocale()));
1✔
725
        }
726

727
        headerCell.setColumnDecorators(decorators.toArray(new DisplaytagColumnDecorator[decorators.size()]));
1✔
728

729
        headerCell.setBeanPropertyName(this.property);
1✔
730
        headerCell.setShowNulls(this.nulls);
1✔
731
        headerCell.setMaxLength(this.maxLength);
1✔
732
        headerCell.setMaxWords(this.maxWords);
1✔
733
        headerCell.setGroup(this.group);
1✔
734
        headerCell.setSortProperty(this.sortProperty);
1✔
735
        headerCell.setTotaled(this.totaled);
1✔
736

737
        final Comparator<Object> headerComparator = this.comparator != null ? this.comparator
1✔
738
                : tableTag.getProperties().getDefaultComparator();
1✔
739

740
        headerCell.setComparator(headerComparator);
1✔
741
        headerCell.setDefaultSortOrder(this.defaultorder);
1✔
742
        headerCell.setSortName(this.sortName);
1✔
743

744
        // href and parameter, create link
745
        if (this.href != null) {
1✔
746
            Href colHref;
747

748
            // empty base url, use href with parameters from parent table
749
            if (StringUtils.isEmpty(this.href.getBaseUrl())) {
1✔
750
                colHref = (Href) tableTag.getBaseHref().clone();
1✔
751
            } else {
752
                colHref = (Href) this.href.clone();
1✔
753
            }
754

755
            if (this.paramId != null) {
1✔
756
                // parameter value is in a different object than the iterated one
757
                if (this.paramName != null) {
1!
758
                    // create a complete string for compatibility with previous version before expression evaluation.
759
                    // this approach is optimized for new expressions, not for previous property/scope parameters
760
                    final StringBuilder expression = new StringBuilder();
×
761

762
                    // base bean name
763
                    if (this.paramId != null) {
×
764
                        expression.append(this.paramName);
×
765
                    } else {
766
                        expression.append(tableTag.getName());
×
767
                    }
768

769
                    // append property
770
                    if (StringUtils.isNotBlank(this.paramProperty)) {
×
771
                        expression.append('.').append(this.paramProperty);
×
772
                    }
773

774
                    // evaluate expression.
775
                    // note the value is fixed, not based on any object created during iteration
776
                    // this is here for compatibility with the old version mainly
777
                    final Object paramValue = tableTag.evaluateExpression(expression.toString());
×
778

779
                    // add parameter
780
                    colHref.addParameter(this.paramId, paramValue);
×
781
                } else {
×
782
                    // set id
783
                    headerCell.setParamName(this.paramId);
1✔
784

785
                    // set property
786
                    headerCell.setParamProperty(this.paramProperty);
1✔
787
                }
788
            }
789

790
            // sets the base href
791
            headerCell.setHref(colHref);
1✔
792

793
        }
794

795
        tableTag.addColumn(headerCell);
1✔
796

797
        if (ColumnTag.log.isDebugEnabled()) {
1!
798
            ColumnTag.log.debug("columnTag.addHeaderToTable() :: first iteration - adding header {}", headerCell);
×
799
        }
800
    }
1✔
801

802
    /**
803
     * Release.
804
     *
805
     * @see javax.servlet.jsp.tagext.Tag#release()
806
     */
807
    @Override
808
    public void release() {
809
        super.release();
1✔
810
        this.attributeMap.clear();
1✔
811
        this.autolink = false;
1✔
812
        this.decorator = null;
1✔
813
        this.group = -1;
1✔
814
        this.headerAttributeMap.clear();
1✔
815
        this.href = null;
1✔
816
        this.maxLength = 0;
1✔
817
        this.maxWords = 0;
1✔
818
        this.nulls = false;
1✔
819
        this.paramId = null;
1✔
820
        this.paramName = null;
1✔
821
        this.paramProperty = null;
1✔
822
        this.property = null;
1✔
823
        this.sortable = false;
1✔
824
        this.sortName = null;
1✔
825
        this.supportedMedia = null;
1✔
826
        this.title = null;
1✔
827
        this.titleKey = null;
1✔
828
        this.sortProperty = null;
1✔
829
        this.comparator = null;
1✔
830
        this.defaultorder = null;
1✔
831
        this.escapeXml = null;
1✔
832
        this.format = null;
1✔
833
        this.value = null;
1✔
834
        this.totaled = false;
1✔
835
    }
1✔
836

837
    /**
838
     * Do start tag.
839
     *
840
     * @return the int
841
     *
842
     * @throws JspException
843
     *             the jsp exception
844
     *
845
     * @see javax.servlet.jsp.tagext.Tag#doStartTag()
846
     */
847
    @Override
848
    public int doStartTag() throws JspException {
849
        final TableTag tableTag = this.getTableTag();
1✔
850
        if (tableTag == null) {
1!
851
            throw new TagStructureException(this.getClass(), "column", "table");
×
852
        }
853

854
        // If the list is empty, do not execute the body; may result in NPE
855
        if (tableTag.isEmpty() || !tableTag.isIncludedRow()) {
1✔
856
            return Tag.SKIP_BODY;
1✔
857
        }
858

859
        final MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext
1✔
860
                .findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA);
1✔
861
        if (!MediaUtil.availableForMedia(this, currentMediaType)) {
1✔
862
            return Tag.SKIP_BODY;
1✔
863
        }
864

865
        // Configure escapeXml default value from properties
866
        if (this.escapeXml == null) {
1✔
867
            this.escapeXml = tableTag.getProperties().getEscapeXmlDefault();
1✔
868
        }
869

870
        return super.doStartTag();
1✔
871
    }
872

873
    /**
874
     * To string.
875
     *
876
     * @return the string
877
     *
878
     * @see java.lang.Object#toString()
879
     */
880
    @Override
881
    public String toString() {
882
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) //
1✔
883
                .append("bodyContent", this.bodyContent) //$NON-NLS-1$
1✔
884
                .append("group", this.group) //$NON-NLS-1$
1✔
885
                .append("maxLength", this.maxLength) //$NON-NLS-1$
1✔
886
                .append("decorator", this.decorator) //$NON-NLS-1$
1✔
887
                .append("href", this.href) //$NON-NLS-1$
1✔
888
                .append("title", this.title) //$NON-NLS-1$
1✔
889
                .append("property", this.property) //$NON-NLS-1$
1✔
890
                .append("paramProperty", this.paramProperty) //$NON-NLS-1$
1✔
891
                .append("headerAttributeMap", this.headerAttributeMap) //$NON-NLS-1$
1✔
892
                .append("paramName", this.paramName) //$NON-NLS-1$
1✔
893
                .append("autolink", this.autolink) //$NON-NLS-1$
1✔
894
                .append("format", this.format) //$NON-NLS-1$
1✔
895
                .append("nulls", this.nulls) //$NON-NLS-1$
1✔
896
                .append("maxWords", this.maxWords) //$NON-NLS-1$
1✔
897
                .append("attributeMap", this.attributeMap) //$NON-NLS-1$
1✔
898
                .append("sortable", this.sortable) //$NON-NLS-1$
1✔
899
                .append("paramId", this.paramId) //$NON-NLS-1$
1✔
900
                .append("alreadySorted", this.alreadySorted) //$NON-NLS-1$
1✔
901
                .append("sortProperty", this.sortProperty) //$NON-NLS-1$
1✔
902
                .append("defaultSortOrder", this.defaultorder) //$NON-NLS-1$
1✔
903
                .toString();
1✔
904
    }
905

906
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc