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

hazendaz / displaytag / 2298

01 Aug 2026 04:32PM UTC coverage: 84.569% (+7.6%) from 76.997%
2298

push

github

hazendaz
[gha] update actions

1508 of 1941 branches covered (77.69%)

Branch coverage included in aggregate %.

4296 of 4922 relevant lines covered (87.28%)

0.87 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 jakarta.servlet.jsp.PageContext;
10

11
import java.util.ArrayList;
12
import java.util.List;
13

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

320
    }
321

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

511
    }
1✔
512

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

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

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

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

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

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

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

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

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

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

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

635
    /**
636
     * Returns the column visibilities to apply sort column properly.
637
     *
638
     * @return The column visibilities.
639
     */
640
    public List<Boolean> getColumnVisibilities() {
641
        return this.columnVisibilities;
1✔
642
    }
643
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc