• 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

91.57
/displaytag/src/main/java/org/displaytag/model/TableModel.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.model;
8

9
import java.util.ArrayList;
10
import java.util.Collections;
11
import java.util.List;
12

13
import javax.servlet.jsp.PageContext;
14

15
import org.apache.commons.lang3.builder.ToStringBuilder;
16
import org.apache.commons.lang3.builder.ToStringStyle;
17
import org.displaytag.decorator.TableDecorator;
18
import org.displaytag.properties.MediaTypeEnum;
19
import org.displaytag.properties.TableProperties;
20
import org.displaytag.render.TableTotaler;
21
import org.slf4j.Logger;
22
import org.slf4j.LoggerFactory;
23

24
/**
25
 * Table Model. Holds table data for presentation.
26
 */
27
public class TableModel {
28

29
    /**
30
     * logger.
31
     */
32
    private static Logger log = LoggerFactory.getLogger(TableModel.class);
1✔
33

34
    /**
35
     * list of HeaderCell.
36
     */
37
    private final List<HeaderCell> headerCellList;
38

39
    /**
40
     * full list (contains Row objects).
41
     */
42
    private final List<Row> rowListFull;
43

44
    /**
45
     * list of data to be displayed in page.
46
     */
47
    private List<Row> rowListPage;
48

49
    /**
50
     * Name of the column currently sorted (only used when sort=external).
51
     */
52
    private String sortedColumnName;
53

54
    /** sort order = ascending?. */
55
    private boolean sortOrderAscending = true;
1✔
56

57
    /**
58
     * sort full List? (false sort only displayed page).
59
     */
60
    private boolean sortFullTable = true;
1✔
61

62
    /**
63
     * index of the sorted column (-1 if the table is not sorted).
64
     */
65
    private int sortedColumn = -1;
1✔
66

67
    /**
68
     * Table decorator.
69
     */
70
    private TableDecorator tableDecorator;
71

72
    /**
73
     * id inherited from the TableTag (needed only for logging).
74
     */
75
    private String id;
76

77
    /**
78
     * configurable table properties.
79
     */
80
    private final TableProperties properties;
81

82
    /**
83
     * Starting offset for elements in the viewable list.
84
     */
85
    private int pageOffset;
86

87
    /**
88
     * Response encoding.
89
     */
90
    private final String encoding;
91

92
    /** Are we sorting locally? (Default True). */
93
    private boolean localSort = true;
1✔
94

95
    /**
96
     * Table caption.
97
     */
98
    private String caption;
99

100
    /**
101
     * Table footer.
102
     */
103
    private String footer;
104

105
    /**
106
     * Jsp page context.
107
     */
108
    private final PageContext pageContext;
109

110
    /**
111
     * Current media.
112
     */
113
    private MediaTypeEnum media;
114

115
    /**
116
     * Uses post for links.
117
     */
118
    private String form;
119

120
    /** The totaler. */
121
    private TableTotaler totaler;
122

123
    /**
124
     * Column visibilities to apply sort column properly
125
     */
126
    private List<Boolean> columnVisibilities = new ArrayList<>();
1✔
127

128
    /**
129
     * Constructor for TableModel.
130
     *
131
     * @param tableProperties
132
     *            table properties
133
     * @param charEncoding
134
     *            response encoding
135
     * @param pageContext
136
     *            the page context
137
     */
138
    public TableModel(final TableProperties tableProperties, final String charEncoding, final PageContext pageContext) {
1✔
139
        this.rowListFull = new ArrayList<>(20);
1✔
140
        this.headerCellList = new ArrayList<>(20);
1✔
141
        this.properties = tableProperties;
1✔
142
        this.encoding = charEncoding;
1✔
143
        this.pageContext = pageContext;
1✔
144
    }
1✔
145

146
    /**
147
     * Returns the jsp page context.
148
     *
149
     * @return page context
150
     */
151
    protected PageContext getPageContext() {
152
        return this.pageContext;
1✔
153
    }
154

155
    /**
156
     * Gets the current media type.
157
     *
158
     * @return current media (html, pdf ...)
159
     */
160
    public MediaTypeEnum getMedia() {
161
        return this.media;
1✔
162
    }
163

164
    /**
165
     * sets the current media type.
166
     *
167
     * @param media
168
     *            current media (html, pdf ...)
169
     */
170
    public void setMedia(final MediaTypeEnum media) {
171
        this.media = media;
1✔
172
    }
1✔
173

174
    /**
175
     * Sets whether the table performs local in memory sorting of the data.
176
     *
177
     * @param localSort
178
     *            the new local sort
179
     */
180
    public void setLocalSort(final boolean localSort) {
181
        this.localSort = localSort;
1✔
182
    }
1✔
183

184
    /**
185
     * Checks if is local sort.
186
     *
187
     * @return sorting in local memory
188
     */
189
    public boolean isLocalSort() {
190
        return this.localSort;
1✔
191
    }
192

193
    /**
194
     * Getter for <code>form</code>.
195
     *
196
     * @return Returns the form.
197
     */
198
    public String getForm() {
199
        return this.form;
1✔
200
    }
201

202
    /**
203
     * Setter for <code>form</code>.
204
     *
205
     * @param form
206
     *            The form to set.
207
     */
208
    public void setForm(final String form) {
209
        this.form = form;
1✔
210
    }
1✔
211

212
    /**
213
     * Getter for <code>pageOffset</code>.
214
     *
215
     * @return Returns the page offset.
216
     */
217
    public int getPageOffset() {
218
        return this.pageOffset;
1✔
219
    }
220

221
    /**
222
     * Sets the starting offset for elements in the viewable list.
223
     *
224
     * @param offset
225
     *            The page offset to set.
226
     */
227
    public void setPageOffset(final int offset) {
228
        this.pageOffset = offset;
1✔
229
    }
1✔
230

231
    /**
232
     * Setter for the tablemodel id.
233
     *
234
     * @param tableId
235
     *            same id of table tag, needed for logging
236
     */
237
    public void setId(final String tableId) {
238
        this.id = tableId;
1✔
239
    }
1✔
240

241
    /**
242
     * get the table id.
243
     *
244
     * @return table id
245
     */
246
    public String getId() {
247
        return this.id;
1✔
248
    }
249

250
    /**
251
     * get the full list.
252
     *
253
     * @return the full list containing Row objects
254
     */
255
    public List<Row> getRowListFull() {
256
        return this.rowListFull;
1✔
257
    }
258

259
    /**
260
     * gets the partial (paginated) list.
261
     *
262
     * @return the partial list to display in page (contains Row objects)
263
     */
264
    public List<Row> getRowListPage() {
265
        return this.rowListPage;
1✔
266
    }
267

268
    /**
269
     * adds a Row object to the table.
270
     *
271
     * @param row
272
     *            Row
273
     */
274
    public void addRow(final Row row) {
275
        row.setParentTable(this);
1✔
276

277
        if (TableModel.log.isDebugEnabled()) {
1!
278
            TableModel.log.debug("[{}] adding row {}", this.id, row);
×
279
        }
280
        this.rowListFull.add(row);
1✔
281
    }
1✔
282

283
    /**
284
     * sets the name of the currently sorted column.
285
     *
286
     * @param sortedColumnName
287
     *            the new sorted column name
288
     */
289
    public void setSortedColumnName(final String sortedColumnName) {
290
        this.sortedColumnName = sortedColumnName;
1✔
291
    }
1✔
292

293
    /**
294
     * sets the sort full table property. If true the full list is sorted, if false sorting is applied only to the
295
     * displayed sublist.
296
     *
297
     * @param sortFull
298
     *            boolean
299
     */
300
    public void setSortFullTable(final boolean sortFull) {
301
        this.sortFullTable = sortFull;
1✔
302
    }
1✔
303

304
    /**
305
     * return the sort full table property.
306
     *
307
     * @return boolean true if sorting is applied to the full list
308
     */
309
    public boolean isSortFullTable() {
310
        return this.sortFullTable;
1✔
311
    }
312

313
    /**
314
     * return the sort order of the page.
315
     *
316
     * @return true if sort order is ascending
317
     */
318
    public boolean isSortOrderAscending() {
319
        return this.sortOrderAscending;
1✔
320

321
    }
322

323
    /**
324
     * set the sort order of the list.
325
     *
326
     * @param isSortOrderAscending
327
     *            true to sort in ascending order
328
     */
329
    public void setSortOrderAscending(final boolean isSortOrderAscending) {
330
        this.sortOrderAscending = isSortOrderAscending;
1✔
331
    }
1✔
332

333
    /**
334
     * Sets the row list page.
335
     *
336
     * @param rowList
337
     *            - the new value for this.rowListPage
338
     */
339
    public void setRowListPage(final List<Row> rowList) {
340
        this.rowListPage = rowList;
1✔
341
    }
1✔
342

343
    /**
344
     * getter for the Table Decorator.
345
     *
346
     * @return TableDecorator
347
     */
348
    public TableDecorator getTableDecorator() {
349
        return this.tableDecorator;
1✔
350
    }
351

352
    /**
353
     * setter for the table decorator.
354
     *
355
     * @param decorator
356
     *            - the TableDecorator object
357
     */
358
    public void setTableDecorator(final TableDecorator decorator) {
359
        this.tableDecorator = decorator;
1✔
360
    }
1✔
361

362
    /**
363
     * returns true if the table is sorted.
364
     *
365
     * @return boolean true if the table is sorted
366
     */
367
    public boolean isSorted() {
368
        return this.sortedColumn != -1;
1✔
369
    }
370

371
    /**
372
     * returns the HeaderCell for the sorted column.
373
     *
374
     * @return HeaderCell
375
     */
376
    public HeaderCell getSortedColumnHeader() {
377
        if (this.sortedColumn < 0 || this.sortedColumn > this.headerCellList.size() - 1) {
1!
378
            return null;
1✔
379
        }
380
        return this.headerCellList.get(this.sortedColumn);
1✔
381
    }
382

383
    /**
384
     * return the number of columns in the table.
385
     *
386
     * @return int number of columns
387
     */
388
    public int getNumberOfColumns() {
389
        return this.headerCellList.size();
1✔
390
    }
391

392
    /**
393
     * return true is the table has no columns.
394
     *
395
     * @return boolean
396
     */
397
    public boolean isEmpty() {
398
        return this.headerCellList.isEmpty();
1✔
399
    }
400

401
    /**
402
     * return the index of the sorted column.
403
     *
404
     * @return index of the sorted column or -1 if the table is not sorted
405
     */
406
    public int getSortedColumnNumber() {
407
        return this.sortedColumn;
1✔
408
    }
409

410
    /**
411
     * set the sorted column index.
412
     *
413
     * @param sortIndex
414
     *            - the index of the sorted column
415
     */
416
    public void setSortedColumnNumber(final int sortIndex) {
417
        this.sortedColumn = sortIndex;
1✔
418
    }
1✔
419

420
    /**
421
     * Adds a column header (HeaderCell object).
422
     *
423
     * @param headerCell
424
     *            HeaderCell
425
     */
426
    public void addColumnHeader(final HeaderCell headerCell) {
427
        if (this.sortedColumnName == null) {
1✔
428
            if (this.sortedColumn == this.headerCellList.size()) {
1✔
429
                headerCell.setAlreadySorted();
1✔
430
            }
431
        } else // the sorted parameter was a string so try and find that column name and set it as sorted
432
        if (this.sortedColumnName.equals(headerCell.getSortName())) {
1✔
433
            headerCell.setAlreadySorted();
1✔
434
        }
435
        headerCell.setColumnNumber(this.headerCellList.size());
1✔
436

437
        this.headerCellList.add(headerCell);
1✔
438
    }
1✔
439

440
    /**
441
     * List containing headerCell objects.
442
     *
443
     * @return List containing headerCell objects
444
     */
445
    public List<HeaderCell> getHeaderCellList() {
446
        return this.headerCellList;
1✔
447
    }
448

449
    /**
450
     * returns a RowIterator on the requested (full|page) list.
451
     *
452
     * @param full
453
     *            if <code>true</code> returns an iterator on te full list, if <code>false</code> only on the viewable
454
     *            part.
455
     *
456
     * @return RowIterator
457
     *
458
     * @see org.displaytag.model.RowIterator
459
     */
460
    public RowIterator getRowIterator(final boolean full) {
461
        final RowIterator iterator = new RowIterator(full ? this.rowListFull : this.rowListPage, this.headerCellList,
1✔
462
                this.tableDecorator, this.pageOffset);
463
        // copy id for logging
464
        iterator.setId(this.id);
1✔
465
        return iterator;
1✔
466
    }
467

468
    /**
469
     * sorts the given list of Rows. The method is called internally by sortFullList() and sortPageList().
470
     *
471
     * @param list
472
     *            List
473
     */
474
    private void sortRowList(final List<Row> list) {
475
        if (this.isSorted()) {
1✔
476
            // Sorted Column may have more items than columns that are visible. Account for this difference during
477
            // sorting.
478
            int oldSortedColumn = this.sortedColumn;
1✔
479
            for (int i = 0; i < oldSortedColumn && i < columnVisibilities.size() && this.sortedColumn > 0; i++) {
1!
480
                if (!columnVisibilities.get(i)) {
1!
481
                    this.sortedColumn--;
×
482
                }
483
            }
484
            try {
485
                final HeaderCell sortedHeaderCell = this.getSortedColumnHeader();
1✔
486

487
                // If it is an explicit value, then sort by that, otherwise sort by the property...
488
                if ((sortedHeaderCell != null) && (sortedHeaderCell.getBeanPropertyName() != null
1!
489
                        || this.sortedColumn != -1 && this.sortedColumn < this.headerCellList.size())) {
1!
490
                    final String sorted = sortedHeaderCell.getSortProperty() != null
1✔
491
                            ? sortedHeaderCell.getSortProperty()
1✔
492
                            : sortedHeaderCell.getBeanPropertyName();
1✔
493

494
                    Collections.sort(list, new RowSorter(this.sortedColumn, sorted, this.getTableDecorator(),
1✔
495
                            this.sortOrderAscending, sortedHeaderCell.getComparator()));
1✔
496
                }
497
            } finally {
498
                this.sortedColumn = oldSortedColumn;
1✔
499
            }
500
        }
501
    }
1✔
502

503
    /**
504
     * sort the list displayed in page.
505
     */
506
    public void sortPageList() {
507
        if (TableModel.log.isDebugEnabled()) {
1!
508
            TableModel.log.debug("[{}] sorting page list", this.id);
×
509
        }
510
        this.sortRowList(this.rowListPage);
1✔
511

512
    }
1✔
513

514
    /**
515
     * sort the full list of data.
516
     */
517
    public void sortFullList() {
518
        if (TableModel.log.isDebugEnabled()) {
1!
519
            TableModel.log.debug("[{}] sorting full data", this.id);
×
520
        }
521
        this.sortRowList(this.rowListFull);
1✔
522
    }
1✔
523

524
    /**
525
     * Returns the table properties.
526
     *
527
     * @return the configured table properties.
528
     */
529
    public TableProperties getProperties() {
530
        return this.properties;
1✔
531
    }
532

533
    /**
534
     * Getter for character encoding.
535
     *
536
     * @return Returns the encoding used for response.
537
     */
538
    public String getEncoding() {
539
        return this.encoding;
1✔
540
    }
541

542
    /**
543
     * Obtain this table's caption.
544
     *
545
     * @return This table's caption.
546
     */
547
    public String getCaption() {
548
        return this.caption;
1✔
549
    }
550

551
    /**
552
     * Set this table's caption.
553
     *
554
     * @param caption
555
     *            This table's caption.
556
     */
557
    public void setCaption(final String caption) {
558
        this.caption = caption;
1✔
559
    }
1✔
560

561
    /**
562
     * Obtain this table's footer.
563
     *
564
     * @return This table's footer.
565
     */
566
    public String getFooter() {
567
        return this.footer;
1✔
568
    }
569

570
    /**
571
     * Set this table's footer.
572
     *
573
     * @param footer
574
     *            This table's footer.
575
     */
576
    public void setFooter(final String footer) {
577
        this.footer = footer;
1✔
578
    }
1✔
579

580
    /**
581
     * To string.
582
     *
583
     * @return the string
584
     *
585
     * @see java.lang.Object#toString()
586
     */
587
    @Override
588
    public String toString() {
589
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) //
1✔
590
                .append("rowListFull", this.rowListFull) //$NON-NLS-1$
1✔
591
                .append("rowListPage", this.rowListPage) //$NON-NLS-1$
1✔
592
                .append("properties", this.properties) //$NON-NLS-1$
1✔
593
                .append("empty", this.isEmpty()) //$NON-NLS-1$
1✔
594
                .append("encoding", this.encoding) //$NON-NLS-1$
1✔
595
                .append("numberOfColumns", this.getNumberOfColumns()) //$NON-NLS-1$
1✔
596
                .append("headerCellList", this.headerCellList) //$NON-NLS-1$
1✔
597
                .append("sortFullTable", this.sortFullTable) //$NON-NLS-1$
1✔
598
                .append("sortedColumnNumber", this.getSortedColumnNumber()) //$NON-NLS-1$
1✔
599
                .append("sortOrderAscending", this.sortOrderAscending) //$NON-NLS-1$
1✔
600
                .append("sortedColumnHeader", this.getSortedColumnHeader()) //$NON-NLS-1$
1✔
601
                .append("sorted", this.isSorted()) //$NON-NLS-1$
1✔
602
                .append("tableDecorator", this.tableDecorator) //$NON-NLS-1$
1✔
603
                .append("caption", this.caption) // $NON-NLS-1
1✔
604
                .append("footer", this.footer) // $NON-NLS-1
1✔
605
                .append("media", this.media) // $NON-NLS-1
1✔
606
                .toString();
1✔
607
    }
608

609
    /**
610
     * Gets the totaler.
611
     *
612
     * @return the totaler
613
     */
614
    public TableTotaler getTotaler() {
615
        return this.totaler;
1✔
616
    }
617

618
    /**
619
     * Sets the totaler.
620
     *
621
     * @param totaler
622
     *            the new totaler
623
     */
624
    public void setTotaler(final TableTotaler totaler) {
625
        this.totaler = totaler;
1✔
626
    }
1✔
627

628
    /**
629
     * Reset.
630
     */
631
    public void reset() {
632
        this.totaler.reset();
1✔
633
        this.totaler.init(this);
1✔
634
    }
1✔
635

636
    /**
637
     * Returns the column visibilities to apply sort column properly.
638
     *
639
     * @return The column visibilities.
640
     */
641
    public List<Boolean> getColumnVisibilities() {
642
        return this.columnVisibilities;
1✔
643
    }
644
}
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