• 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

88.12
/displaytag/src/main/java/org/displaytag/render/TableTotaler.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.render;
8

9
import java.util.ArrayList;
10
import java.util.HashMap;
11
import java.util.List;
12
import java.util.Map;
13
import java.util.TreeMap;
14

15
import org.displaytag.exception.DecoratorException;
16
import org.displaytag.exception.ObjectLookupException;
17
import org.displaytag.model.Column;
18
import org.displaytag.model.ColumnIterator;
19
import org.displaytag.model.HeaderCell;
20
import org.displaytag.model.Row;
21
import org.displaytag.model.TableModel;
22
import org.slf4j.Logger;
23
import org.slf4j.LoggerFactory;
24

25
/**
26
 * This class just keeps a running grouped total. It does not output anything; it is the responsibility of the exporter
27
 * or of the decorator to actually output the results.
28
 */
29
public class TableTotaler {
1✔
30

31
    /** The logger. */
32
    protected Logger logger = LoggerFactory.getLogger(TableTotaler.class);
1✔
33

34
    /** The Constant NULL. */
35
    public static final TableTotaler NULL = new TableTotaler();
1✔
36

37
    /** The first row for each group. */
38
    protected Map<Integer, Integer> firstRowForEachGroup = new HashMap<>();
1✔
39

40
    /** The how many groups. */
41
    protected int howManyGroups = 0;
1✔
42

43
    /** The current row number. */
44
    protected Integer currentRowNumber = 0;
1✔
45

46
    /** The table model. */
47
    protected TableModel tableModel;
48

49
    /** The opened columns. */
50
    List<Integer> openedColumns = new ArrayList<>(); // in excel, i need to know which ones are currently open; in xml,
1✔
51
                                                     // just what has just opened
52

53
    /** The grouping values by column. */
54
    TreeMap<Integer, String> groupingValuesByColumn = new TreeMap<>(); // in excel, i need to know which ones are
1✔
55
                                                                       // currently open; in xml, just what has just
56
                                                                       // opened
57

58
    /** The closed columns. */
59
    List<Integer> closedColumns = new ArrayList<>();
1✔
60
    /**
61
     * Magic constant to indicate that we want the whole list, not just a subgroup.
62
     */
63
    public static final Integer WHOLE_TABLE = 0;
1✔
64

65
    /**
66
     * Inits the.
67
     *
68
     * @param model
69
     *            the model
70
     */
71
    public void init(final TableModel model) {
72
        this.tableModel = model;
1✔
73
        this.firstRowForEachGroup = new HashMap<>();
1✔
74
        for (final HeaderCell c : model.getHeaderCellList()) {
1✔
75
            if (c.getGroup() > 0) {
1✔
76
                this.firstRowForEachGroup.put(c.getGroup(), 0);
1✔
77
                this.howManyGroups++;
1✔
78
            }
79
        }
1✔
80

81
    }
1✔
82

83
    /**
84
     * Inits the row.
85
     *
86
     * @param currentViewIndex
87
     *            the current view index
88
     * @param currentListIndex
89
     *            the current list index
90
     */
91
    public void initRow(final int currentViewIndex, final int currentListIndex) {
92
        this.openedColumns.clear();
1✔
93
        this.closedColumns.clear();
1✔
94
        this.currentRowNumber = currentListIndex;
1✔
95
    }
1✔
96

97
    /**
98
     * As column.
99
     *
100
     * @param groupNumber
101
     *            the group number
102
     *
103
     * @return the int
104
     */
105
    public int asColumn(final int groupNumber) {
106
        return groupNumber;
1✔
107
    }
108

109
    /**
110
     * As group.
111
     *
112
     * @param columnNumber
113
     *            the column number
114
     *
115
     * @return the int
116
     */
117
    public int asGroup(final int columnNumber) {
118
        return columnNumber;
1✔
119
    }
120

121
    /**
122
     * Start group.
123
     *
124
     * @param groupingValue
125
     *            the grouping value
126
     * @param groupNumber
127
     *            the group number
128
     */
129
    public void startGroup(final String groupingValue, final int groupNumber) {
130
        this.openedColumns.add(this.asColumn(groupNumber));
1✔
131
        this.groupingValuesByColumn.put(this.asColumn(groupNumber), groupingValue);
1✔
132
        this.firstRowForEachGroup.put(groupNumber, this.currentRowNumber);
1✔
133
    }
1✔
134

135
    /**
136
     * Gets the opened columns.
137
     *
138
     * @return the opened columns
139
     */
140
    public List<Integer> getOpenedColumns() {
141
        return new ArrayList<>(this.openedColumns);
1✔
142
    }
143

144
    /**
145
     * Gets the closed columns.
146
     *
147
     * @return the closed columns
148
     */
149
    public List<Integer> getClosedColumns() {
150
        return this.closedColumns;
1✔
151
    }
152

153
    /**
154
     * Stop group.
155
     *
156
     * @param value
157
     *            the value
158
     * @param groupNumber
159
     *            the group number
160
     */
161
    public void stopGroup(final String value, final int groupNumber) {
162
        this.closedColumns.add(this.asColumn(groupNumber));
1✔
163
    }
1✔
164

165
    /**
166
     * Override locally to perform your own math.
167
     *
168
     * @param column
169
     *            the column
170
     * @param total
171
     *            the total
172
     * @param value
173
     *            the value
174
     *
175
     * @return the object
176
     */
177
    public Object add(final Column column, final Object total, final Object value) {
178
        if (value == null) {
1!
179
            return total;
×
180
        }
181
        if (value instanceof Number) {
1!
182
            Number oldTotal = (double) 0;
1✔
183
            if (total != null) {
1✔
184
                oldTotal = (Number) total;
1✔
185
            }
186
            return oldTotal.doubleValue() + ((Number) value).doubleValue();
1✔
187
        } else {
188
            throw new UnsupportedOperationException(
×
189
                    "Cannot add a value of " + value + " in column " + column.getHeaderCell().getTitle());
×
190
        }
191
    }
192

193
    /**
194
     * Override locally to format it yourself.
195
     *
196
     * @param cell
197
     *            the current cell
198
     * @param total
199
     *            the current value
200
     *
201
     * @return the string
202
     */
203
    public String formatTotal(final HeaderCell cell, Object total) {
204
        if (total == null) {
1!
205
            total = "";
×
206
        }
207
        return total instanceof String ? (String) total : total.toString();
1!
208
    }
209

210
    /**
211
     * Gets the total for list.
212
     *
213
     * @param window
214
     *            the window
215
     * @param columnNumber
216
     *            the column number
217
     *
218
     * @return the total for list
219
     */
220
    protected Object getTotalForList(final List<Row> window, final int columnNumber) {
221
        Object total = null;
1✔
222
        for (final Row row : window) {
1✔
223
            final ColumnIterator columnIterator = row.getColumnIterator(this.tableModel.getHeaderCellList());
1✔
224
            while (columnIterator.hasNext()) {
1✔
225
                final Column column = columnIterator.nextColumn();
1✔
226
                if (column.getHeaderCell().getColumnNumber() == columnNumber) {
1✔
227
                    Object value = null;
1✔
228
                    try {
229
                        value = column.getValue(false);
1✔
230
                    } catch (ObjectLookupException | DecoratorException e) {
×
231
                        this.logger.error("", e);
×
232
                    }
1✔
233
                    if (value != null && !"".equals(value)) {
1!
234
                        total = this.add(column, total, value);
1✔
235
                    }
236
                }
237
            }
1✔
238
        }
1✔
239
        return total;
1✔
240
    }
241

242
    /**
243
     * Gets the total for column.
244
     *
245
     * @param columnNumber
246
     *            the column number
247
     * @param groupNumber
248
     *            the group number
249
     *
250
     * @return the total for column
251
     */
252
    public Object getTotalForColumn(final int columnNumber, final int groupNumber) {
253
        final List<Row> fullList = this.tableModel.getRowListFull();
1✔
254
        Integer startRow = this.firstRowForEachGroup.get(groupNumber);
1✔
255
        final Integer stopRow = this.currentRowNumber + 1;
1✔
256
        if (groupNumber == TableTotaler.WHOLE_TABLE) { // asking for a total for the entire table
1✔
257
            startRow = 0;
1✔
258
        }
259
        final List<Row> window = fullList.subList(startRow, stopRow);
1✔
260
        return this.getTotalForList(window, columnNumber);
1✔
261
    }
262

263
    /**
264
     * Gets the grouping value.
265
     *
266
     * @param columnNumber
267
     *            the column number
268
     *
269
     * @return the grouping value
270
     */
271
    public String getGroupingValue(final Integer columnNumber) {
272
        return this.groupingValuesByColumn.get(columnNumber);
1✔
273
    }
274

275
    /**
276
     * Reset.
277
     */
278
    public void reset() {
279
        this.closedColumns.clear();
1✔
280
        this.openedColumns.clear();
1✔
281
        this.groupingValuesByColumn.clear();
1✔
282
        this.currentRowNumber = 0;
1✔
283
        this.howManyGroups = 0;
1✔
284
        this.firstRowForEachGroup.clear();
1✔
285
    }
1✔
286
}
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