• 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

78.69
/displaytag/src/main/java/org/displaytag/decorator/MultilevelTotalTableDecorator.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.decorator;
8

9
import java.text.MessageFormat;
10
import java.util.ArrayList;
11
import java.util.HashMap;
12
import java.util.List;
13
import java.util.Locale;
14
import java.util.Map;
15

16
import javax.servlet.jsp.PageContext;
17

18
import org.apache.commons.lang3.StringUtils;
19
import org.displaytag.exception.DecoratorException;
20
import org.displaytag.exception.ObjectLookupException;
21
import org.displaytag.model.Column;
22
import org.displaytag.model.ColumnIterator;
23
import org.displaytag.model.HeaderCell;
24
import org.displaytag.model.Row;
25
import org.displaytag.model.TableModel;
26
import org.displaytag.util.TagConstants;
27
import org.slf4j.Logger;
28
import org.slf4j.LoggerFactory;
29

30
/**
31
 * A TableDecorator that, in conjunction with totaled and grouped columns, produces multi level subtotals on arbitrary
32
 * String groupings. Use it directly, subclass it, or use it as an example to better meet your local needs.
33
 */
34
public class MultilevelTotalTableDecorator extends TableDecorator {
1✔
35

36
    /**
37
     * If there are no columns that are totaled, we should not issue a totals row.
38
     */
39
    private final boolean containsTotaledColumns = true;
1✔
40

41
    /**
42
     * No current reset group.
43
     */
44
    private static final int NO_RESET_GROUP = 4200;
45

46
    /**
47
     * Maps the groups to their current totals.
48
     */
49
    private final Map<Integer, GroupTotals> groupNumberToGroupTotal = new HashMap<>();
1✔
50

51
    /**
52
     * The deepest reset group. Resets on an outer group will force any deeper groups to reset as well.
53
     */
54
    private int deepestResetGroup = MultilevelTotalTableDecorator.NO_RESET_GROUP;
1✔
55

56
    /**
57
     * Controls when the subgroup is ended.
58
     */
59
    protected int innermostGroup;
60

61
    /**
62
     * Logger.
63
     */
64
    private final Logger logger = LoggerFactory.getLogger(MultilevelTotalTableDecorator.class);
1✔
65

66
    /**
67
     * CSS class applied to grand total totals.
68
     */
69
    protected String grandTotalSum = "grandtotal-sum";
1✔
70

71
    /**
72
     * CSS class applied to grand total cells where the column is not totaled.
73
     */
74
    protected String grandTotalNoSum = "grandtotal-nosum";
1✔
75

76
    /**
77
     * CSS class applied to grand total lablels.
78
     */
79
    protected String grandTotalLabel = "grandtotal-label";
1✔
80

81
    /**
82
     * Grandtotal description.
83
     */
84
    protected String grandTotalDescription = "Grand Total";
1✔
85

86
    /**
87
     * CSS class appplied to subtotal headers.
88
     */
89
    private String subtotalHeaderClass = "subtotal-header";
1✔
90

91
    /**
92
     * CSS class applied to subtotal labels.
93
     */
94
    private String subtotalLabelClass = "subtotal-label";
1✔
95

96
    /**
97
     * Message format for subtotal descriptions.
98
     */
99
    private MessageFormat subtotalDesc = new MessageFormat("{0} Total");
1✔
100

101
    /**
102
     * CSS class applied to subtotal totals.
103
     */
104
    private String subtotalValueClass = "subtotal-sum";
1✔
105

106
    /**
107
     * Holds the header rows and their content for a particular group.
108
     */
109
    private final List<String> headerRows = new ArrayList<>(5);
1✔
110

111
    /**
112
     * Inits the.
113
     *
114
     * @param context
115
     *            the context
116
     * @param decorated
117
     *            the decorated
118
     * @param model
119
     *            the model
120
     */
121
    @Override
122
    public void init(final PageContext context, final Object decorated, final TableModel model) {
123
        super.init(context, decorated, model);
1✔
124
        final List<HeaderCell> headerCells = model.getHeaderCellList();
1✔
125
        // go through each column, looking for grouped columns; add them to the group number map
126
        for (final HeaderCell headerCell : headerCells) {
1✔
127
            if (headerCell.getGroup() > 0) {
1✔
128
                final GroupTotals groupTotals = new GroupTotals(headerCell.getColumnNumber());
1✔
129
                groupTotals.setStartRow(this.tableModel.getPageOffset());
1✔
130
                this.groupNumberToGroupTotal.put(Integer.valueOf(headerCell.getGroup()), groupTotals);
1✔
131
                if (headerCell.getGroup() > this.innermostGroup) {
1!
132
                    this.innermostGroup = headerCell.getGroup();
1✔
133
                }
134
            }
135
        }
1✔
136
    }
1✔
137

138
    /**
139
     * Gets the grand total description.
140
     *
141
     * @return the grand total description
142
     */
143
    public String getGrandTotalDescription() {
144
        return this.grandTotalDescription;
1✔
145
    }
146

147
    /**
148
     * Sets the grand total description.
149
     *
150
     * @param grandTotalDescription
151
     *            the new grand total description
152
     */
153
    public void setGrandTotalDescription(final String grandTotalDescription) {
154
        this.grandTotalDescription = grandTotalDescription;
×
155
    }
×
156

157
    /**
158
     * The pattern to use to generate the subtotal labels. The grouping value of the cell will be the first arg. The
159
     * default value is "{0} Total".
160
     *
161
     * @param pattern
162
     *            the pattern
163
     * @param locale
164
     *            the locale
165
     */
166
    public void setSubtotalLabel(final String pattern, final Locale locale) {
167
        this.subtotalDesc = new MessageFormat(pattern, locale);
×
168
    }
×
169

170
    /**
171
     * Gets the grand total label.
172
     *
173
     * @return the grand total label
174
     */
175
    public String getGrandTotalLabel() {
176
        return this.grandTotalLabel;
1✔
177
    }
178

179
    /**
180
     * Gets the grand total sum.
181
     *
182
     * @return the grand total sum
183
     */
184
    public String getGrandTotalSum() {
185
        return this.grandTotalSum;
1✔
186
    }
187

188
    /**
189
     * Gets the grand total no sum.
190
     *
191
     * @return the grand total no sum
192
     */
193
    public String getGrandTotalNoSum() {
194
        return this.grandTotalNoSum;
1✔
195
    }
196

197
    /**
198
     * Sets the grand total no sum.
199
     *
200
     * @param grandTotalNoSum
201
     *            the new grand total no sum
202
     */
203
    public void setGrandTotalNoSum(final String grandTotalNoSum) {
204
        this.grandTotalNoSum = grandTotalNoSum;
×
205
    }
×
206

207
    /**
208
     * Sets the grand total sum.
209
     *
210
     * @param grandTotalSum
211
     *            the new grand total sum
212
     */
213
    public void setGrandTotalSum(final String grandTotalSum) {
214
        this.grandTotalSum = grandTotalSum;
×
215
    }
×
216

217
    /**
218
     * Sets the grand total label.
219
     *
220
     * @param grandTotalLabel
221
     *            the new grand total label
222
     */
223
    public void setGrandTotalLabel(final String grandTotalLabel) {
224
        this.grandTotalLabel = grandTotalLabel;
×
225
    }
×
226

227
    /**
228
     * Gets the subtotal value class.
229
     *
230
     * @return the subtotal value class
231
     */
232
    public String getSubtotalValueClass() {
233
        return this.subtotalValueClass;
1✔
234
    }
235

236
    /**
237
     * Sets the subtotal value class.
238
     *
239
     * @param subtotalValueClass
240
     *            the new subtotal value class
241
     */
242
    public void setSubtotalValueClass(final String subtotalValueClass) {
243
        this.subtotalValueClass = subtotalValueClass;
×
244
    }
×
245

246
    /**
247
     * Gets the subtotal label class.
248
     *
249
     * @return the subtotal label class
250
     */
251
    public String getSubtotalLabelClass() {
252
        return this.subtotalLabelClass;
1✔
253
    }
254

255
    /**
256
     * Sets the subtotal label class.
257
     *
258
     * @param subtotalLabelClass
259
     *            the new subtotal label class
260
     */
261
    public void setSubtotalLabelClass(final String subtotalLabelClass) {
262
        this.subtotalLabelClass = subtotalLabelClass;
×
263
    }
×
264

265
    /**
266
     * Gets the subtotal header class.
267
     *
268
     * @return the subtotal header class
269
     */
270
    public String getSubtotalHeaderClass() {
271
        return this.subtotalHeaderClass;
1✔
272
    }
273

274
    /**
275
     * Sets the subtotal header class.
276
     *
277
     * @param subtotalHeaderClass
278
     *            the new subtotal header class
279
     */
280
    public void setSubtotalHeaderClass(final String subtotalHeaderClass) {
281
        this.subtotalHeaderClass = subtotalHeaderClass;
×
282
    }
×
283

284
    /**
285
     * Start of group.
286
     *
287
     * @param value
288
     *            the value
289
     * @param group
290
     *            the group
291
     */
292
    @Override
293
    public void startOfGroup(final String value, final int group) {
294
        if (this.containsTotaledColumns) {
1✔
295
            final StringBuilder tr = new StringBuilder();
1✔
296
            tr.append("<tr>");
1✔
297
            final GroupTotals groupTotals = this.groupNumberToGroupTotal.get(Integer.valueOf(group));
1✔
298
            final int myColumnNumber = groupTotals.columnNumber;
1✔
299
            for (int i = 0; i < myColumnNumber; i++) {
1!
300
                tr.append("<td></td>\n");
×
301
            }
302
            tr.append("<td class=\"").append(this.getSubtotalHeaderClass()).append(" group-").append(group)
1✔
303
                    .append("\" >");
1✔
304
            tr.append(value).append("</td>\n");
1✔
305
            final List<HeaderCell> headerCells = this.tableModel.getHeaderCellList();
1✔
306
            for (int i = myColumnNumber; i < headerCells.size() - 1; i++) {
1✔
307
                tr.append("<td></td>\n");
1✔
308
            }
309
            tr.append("</tr>\n");
1✔
310
            this.headerRows.add(tr.toString());
1✔
311
        }
312
    }
1✔
313

314
    /**
315
     * Display grouped value.
316
     *
317
     * @param value
318
     *            the value
319
     * @param groupingStatus
320
     *            the grouping status
321
     * @param columnNumber
322
     *            the column number
323
     *
324
     * @return the string
325
     */
326
    @Override
327
    public String displayGroupedValue(final String value, final short groupingStatus, final int columnNumber) {
328
        return "";
1✔
329
    }
330

331
    /**
332
     * Start row.
333
     *
334
     * @return the string
335
     */
336
    @Override
337
    public String startRow() {
338
        final StringBuilder sb = new StringBuilder();
1✔
339
        for (final String string : this.headerRows) {
1✔
340
            sb.append(string);
1✔
341
        }
1✔
342
        return sb.toString();
1✔
343
    }
344

345
    /**
346
     * End of group.
347
     *
348
     * @param value
349
     *            the value
350
     * @param groupNumber
351
     *            the group number
352
     */
353
    @Override
354
    public void endOfGroup(final String value, final int groupNumber) {
355
        if (this.deepestResetGroup > groupNumber) {
1!
356
            this.deepestResetGroup = groupNumber;
1✔
357
        }
358
    }
1✔
359

360
    /**
361
     * Finish row.
362
     *
363
     * @return the string
364
     */
365
    @Override
366
    public String finishRow() {
367
        String returnValue = "";
1✔
368
        if (this.containsTotaledColumns) {
1✔
369
            if (this.innermostGroup > 0 && this.deepestResetGroup != MultilevelTotalTableDecorator.NO_RESET_GROUP) {
1!
370
                final StringBuilder out = new StringBuilder();
1✔
371
                // Starting with the deepest group, print the current total and reset. Do not reset unaffected groups.
372
                for (int i = this.innermostGroup; i >= this.deepestResetGroup; i--) {
1✔
373
                    final Integer groupNumber = Integer.valueOf(i);
1✔
374

375
                    final GroupTotals totals = this.groupNumberToGroupTotal.get(groupNumber);
1✔
376
                    if (totals == null) {
1!
377
                        this.logger.warn("There is a gap in the defined groups - no group defined for {}", groupNumber);
×
378
                        continue;
×
379
                    }
380
                    totals.printTotals(this.getListIndex(), out);
1✔
381
                    this.finishGroup(totals.getColumnNumber(), out);
1✔
382
                    totals.setStartRow(this.getListIndex() + 1);
1✔
383
                }
384
                returnValue = out.toString();
1✔
385
            } else {
1✔
386
                returnValue = null;
1✔
387
            }
388
            this.deepestResetGroup = MultilevelTotalTableDecorator.NO_RESET_GROUP;
1✔
389
            this.headerRows.clear();
1✔
390
            if (this.isLastRow()) {
1✔
391
                returnValue = StringUtils.defaultString(returnValue);
1✔
392
                returnValue += this.totalAllRows();
1✔
393
            }
394
        }
395
        return returnValue;
1✔
396
    }
397

398
    /**
399
     * Finish group.
400
     *
401
     * @param columnNumber
402
     *            the column number
403
     * @param out
404
     *            the out
405
     */
406
    protected void finishGroup(final int columnNumber, final StringBuilder out) {
407
        // Not Implemented
408
    }
1✔
409

410
    /**
411
     * Issue a grand total row at the bottom.
412
     *
413
     * @return the suitable string
414
     */
415
    protected String totalAllRows() {
416
        if (!this.containsTotaledColumns) {
1✔
417
            return "";
418
        }
419
        final List<HeaderCell> headerCells = this.tableModel.getHeaderCellList();
1✔
420
        final StringBuilder output = new StringBuilder();
1✔
421
        final int currentRow = this.getListIndex();
1✔
422
        output.append(TagConstants.TAG_OPEN + TagConstants.TAGNAME_ROW + " class=\"grandtotal-row\""
1✔
423
                + TagConstants.TAG_CLOSE);
424
        boolean first = true;
1✔
425
        for (final HeaderCell headerCell : headerCells) {
1✔
426
            if (first) {
1✔
427
                output.append(this.getTotalsTdOpen(headerCell, this.getGrandTotalLabel()));
1✔
428
                output.append(this.getGrandTotalDescription());
1✔
429
                first = false;
1✔
430
            } else if (headerCell.isTotaled()) {
1✔
431
                // a total if the column should be totaled
432
                final Object total = this.getTotalForColumn(headerCell.getColumnNumber(),
1✔
433
                        this.tableModel.getPageOffset(), currentRow);
1✔
434
                output.append(this.getTotalsTdOpen(headerCell, this.getGrandTotalSum()));
1✔
435
                output.append(this.formatTotal(headerCell, total));
1✔
436
            } else {
1✔
437
                // blank, if it is not a totals column
438
                output.append(this.getTotalsTdOpen(headerCell, this.getGrandTotalNoSum()));
1✔
439
            }
440
            output.append(TagConstants.TAG_OPENCLOSING + TagConstants.TAGNAME_COLUMN + TagConstants.TAG_CLOSE);
1✔
441
        }
1✔
442
        output.append("\n</tr>\n");
1✔
443

444
        return output.toString();
1✔
445
    }
446

447
    /**
448
     * Gets the cell value.
449
     *
450
     * @param columnNumber
451
     *            the column number
452
     * @param rowNumber
453
     *            the row number
454
     *
455
     * @return the cell value
456
     */
457
    protected String getCellValue(final int columnNumber, final int rowNumber) {
458
        final List<Row> fullList = this.tableModel.getRowListFull();
1✔
459
        final Row row = fullList.get(rowNumber);
1✔
460
        final ColumnIterator columnIterator = row.getColumnIterator(this.tableModel.getHeaderCellList());
1✔
461
        while (columnIterator.hasNext()) {
1!
462
            final Column column = columnIterator.nextColumn();
1✔
463
            if (column.getHeaderCell().getColumnNumber() == columnNumber) {
1!
464
                try {
465
                    column.initialize();
1✔
466
                    return column.getChoppedAndLinkedValue();
1✔
467
                } catch (ObjectLookupException | DecoratorException e) {
×
468
                    this.logger.error("Error: {}", e.getMessage(), e);
×
469
                    throw new RuntimeException("Error: " + e.getMessage(), e);
×
470
                }
471
            }
472
        }
×
473
        throw new RuntimeException("Unable to find column " + columnNumber + " in the list of columns");
×
474
    }
475

476
    /**
477
     * Gets the total for column.
478
     *
479
     * @param columnNumber
480
     *            the column number
481
     * @param startRow
482
     *            the start row
483
     * @param stopRow
484
     *            the stop row
485
     *
486
     * @return the total for column
487
     */
488
    protected Object getTotalForColumn(final int columnNumber, final int startRow, final int stopRow) {
489
        final List<Row> fullList = this.tableModel.getRowListFull();
1✔
490
        final List<Row> window = fullList.subList(startRow, stopRow + 1);
1✔
491
        Object total = null;
1✔
492
        for (final Row row : window) {
1✔
493
            final ColumnIterator columnIterator = row.getColumnIterator(this.tableModel.getHeaderCellList());
1✔
494
            while (columnIterator.hasNext()) {
1✔
495
                final Column column = columnIterator.nextColumn();
1✔
496
                if (column.getHeaderCell().getColumnNumber() == columnNumber) {
1✔
497
                    Object value = null;
1✔
498
                    try {
499
                        value = column.getValue(false);
1✔
500
                    } catch (ObjectLookupException | DecoratorException e) {
×
501
                        this.logger.error("", e);
×
502
                    }
1✔
503
                    if (value != null && !TagConstants.EMPTY_STRING.equals(value)) {
1!
504
                        total = this.add(column, total, value);
1✔
505
                    }
506
                }
507
            }
1✔
508
        }
1✔
509
        return total;
1✔
510
    }
511

512
    /**
513
     * Adds the.
514
     *
515
     * @param column
516
     *            the column
517
     * @param total
518
     *            the total
519
     * @param value
520
     *            the value
521
     *
522
     * @return the object
523
     */
524
    protected Object add(final Column column, final Object total, final Object value) {
525
        if (value == null) {
1!
526
            return total;
×
527
        }
528
        if (value instanceof Number) {
1!
529
            Number oldTotal = Double.valueOf(0);
1✔
530
            if (total != null) {
1✔
531
                oldTotal = (Number) total;
1✔
532
            }
533
            return Double.valueOf(oldTotal.doubleValue() + ((Number) value).doubleValue());
1✔
534
        } else {
535
            throw new UnsupportedOperationException(
×
536
                    "Cannot add a value of " + value + " in column " + column.getHeaderCell().getTitle());
×
537
        }
538
    }
539

540
    /**
541
     * Gets the totals td open.
542
     *
543
     * @param header
544
     *            the header
545
     * @param totalClass
546
     *            the total class
547
     *
548
     * @return the totals td open
549
     */
550
    public String getTotalsTdOpen(final HeaderCell header, final String totalClass) {
551

552
        final Object cssClassObj = header.getHtmlAttributes().get("class");
1✔
553
        final String cssClass = cssClassObj != null ? cssClassObj.toString() : StringUtils.EMPTY;
1!
554

555
        final StringBuilder buffer = new StringBuilder();
1✔
556
        buffer.append(TagConstants.TAG_OPEN);
1✔
557
        buffer.append(TagConstants.TAGNAME_COLUMN);
1✔
558
        if (cssClass != null || totalClass != null) {
1!
559
            buffer.append(" class=\"");
1✔
560

561
            if (cssClass != null) {
1!
562
                buffer.append(cssClass);
1✔
563
                if (totalClass != null) {
1!
564
                    buffer.append(" ");
1✔
565
                }
566
            }
567
            if (totalClass != null) {
1!
568
                buffer.append(totalClass);
1✔
569
            }
570
            buffer.append("\"");
1✔
571
        }
572
        buffer.append(TagConstants.TAG_CLOSE);
1✔
573
        return buffer.toString();
1✔
574
    }
575

576
    /**
577
     * Gets the totals row open.
578
     *
579
     * @return the totals row open
580
     */
581
    public String getTotalsRowOpen() {
582
        return TagConstants.TAG_OPEN + TagConstants.TAGNAME_ROW + " class=\"subtotal\"" + TagConstants.TAG_CLOSE;
1✔
583
    }
584

585
    /**
586
     * Gets the total row label.
587
     *
588
     * @param groupingValue
589
     *            the grouping value
590
     *
591
     * @return the total row label
592
     */
593
    public String getTotalRowLabel(final String groupingValue) {
594
        return this.subtotalDesc.format(new Object[] { groupingValue });
1✔
595
    }
596

597
    /**
598
     * Format total.
599
     *
600
     * @param header
601
     *            the header
602
     * @param total
603
     *            the total
604
     *
605
     * @return the string
606
     */
607
    public String formatTotal(final HeaderCell header, final Object total) {
608
        Object displayValue = total;
1✔
609
        if (header.getColumnDecorators().length > 0) {
1!
610
            for (int i = 0; i < header.getColumnDecorators().length; i++) {
×
611
                final DisplaytagColumnDecorator decorator = header.getColumnDecorators()[i];
×
612
                try {
613
                    displayValue = decorator.decorate(total, this.getPageContext(), this.tableModel.getMedia());
×
614
                } catch (final DecoratorException e) {
×
615
                    this.logger.warn(e.getMessage(), e);
×
616
                    // ignore, use undecorated value for totals
617
                }
×
618
            }
619
        }
620
        return displayValue != null ? displayValue.toString() : "";
1!
621
    }
622

623
    /**
624
     * The Class GroupTotals.
625
     */
626
    class GroupTotals {
627

628
        /**
629
         * The label class.
630
         */
631
        protected String totalLabelClass = MultilevelTotalTableDecorator.this.getSubtotalLabelClass();
1✔
632

633
        /** The row opener. */
634
        protected String totalsRowOpen = MultilevelTotalTableDecorator.this.getTotalsRowOpen();
1✔
635

636
        /**
637
         * The value class.
638
         */
639
        protected String totalValueClass = MultilevelTotalTableDecorator.this.getSubtotalValueClass();
1✔
640

641
        /** The column number. */
642
        private final int columnNumber;
643

644
        /** The first row of current set. */
645
        private int firstRowOfCurrentSet;
646

647
        /**
648
         * Instantiates a new group totals.
649
         *
650
         * @param headerCellColumn
651
         *            the header cell column
652
         */
653
        public GroupTotals(final int headerCellColumn) {
1✔
654
            this.columnNumber = headerCellColumn;
1✔
655
            this.firstRowOfCurrentSet = 0;
1✔
656
        }
1✔
657

658
        /**
659
         * Prints the totals.
660
         *
661
         * @param currentRow
662
         *            the current row
663
         * @param out
664
         *            the out
665
         */
666
        public void printTotals(final int currentRow, final StringBuilder out) {
667

668
            // For each column, output:
669
            final List<HeaderCell> headerCells = MultilevelTotalTableDecorator.this.tableModel.getHeaderCellList();
1✔
670
            if (this.firstRowOfCurrentSet < currentRow) // If there is more than one row, show a total
1✔
671
            {
672
                out.append(this.totalsRowOpen);
1✔
673
                for (final HeaderCell headerCell : headerCells) {
1✔
674
                    if (this.columnNumber == headerCell.getColumnNumber()) {
1✔
675
                        // a totals label if it is the column for the current group
676
                        final String currentLabel = MultilevelTotalTableDecorator.this.getCellValue(this.columnNumber,
1✔
677
                                this.firstRowOfCurrentSet);
678
                        out.append(MultilevelTotalTableDecorator.this.getTotalsTdOpen(headerCell,
1✔
679
                                this.getTotalLabelClass() + " group-" + (this.columnNumber + 1)));
1✔
680
                        out.append(MultilevelTotalTableDecorator.this.getTotalRowLabel(currentLabel));
1✔
681
                    } else if (headerCell.isTotaled()) {
1✔
682
                        // a total if the column should be totaled
683
                        final Object total = MultilevelTotalTableDecorator.this
1✔
684
                                .getTotalForColumn(headerCell.getColumnNumber(), this.firstRowOfCurrentSet, currentRow);
1✔
685
                        out.append(MultilevelTotalTableDecorator.this.getTotalsTdOpen(headerCell,
1✔
686
                                this.getTotalValueClass() + " group-" + (this.columnNumber + 1)));
1✔
687
                        out.append(MultilevelTotalTableDecorator.this.formatTotal(headerCell, total));
1✔
688
                    } else {
1✔
689
                        // blank, if it is not a totals column
690
                        final StringBuilder style = new StringBuilder("group-" + (this.columnNumber + 1));
1✔
691
                        if (headerCell.getColumnNumber() < MultilevelTotalTableDecorator.this.innermostGroup) {
1!
692
                            style.append(" ").append(this.getTotalLabelClass()).append(" ");
×
693
                        }
694
                        out.append(MultilevelTotalTableDecorator.this.getTotalsTdOpen(headerCell, style.toString()));
1✔
695
                    }
696
                    out.append(TagConstants.TAG_OPENCLOSING + TagConstants.TAGNAME_COLUMN + TagConstants.TAG_CLOSE);
1✔
697
                }
1✔
698
                out.append("\n</tr>\n");
1✔
699
            }
700
        }
1✔
701

702
        /**
703
         * Gets the column number.
704
         *
705
         * @return the column number
706
         */
707
        public int getColumnNumber() {
708
            return this.columnNumber;
1✔
709
        }
710

711
        /**
712
         * Sets the start row.
713
         *
714
         * @param i
715
         *            the new start row
716
         */
717
        public void setStartRow(final int i) {
718
            this.firstRowOfCurrentSet = i;
1✔
719
        }
1✔
720

721
        /**
722
         * Gets the total label class.
723
         *
724
         * @return the total label class
725
         */
726
        public String getTotalLabelClass() {
727
            return this.totalLabelClass;
1✔
728
        }
729

730
        /**
731
         * Sets the totals row open.
732
         *
733
         * @param totalsRowOpen
734
         *            the new totals row open
735
         */
736
        public void setTotalsRowOpen(final String totalsRowOpen) {
737
            this.totalsRowOpen = totalsRowOpen;
×
738
        }
×
739

740
        /**
741
         * Sets the total label class.
742
         *
743
         * @param totalLabelClass
744
         *            the new total label class
745
         */
746
        public void setTotalLabelClass(final String totalLabelClass) {
747
            this.totalLabelClass = totalLabelClass;
×
748
        }
×
749

750
        /**
751
         * Gets the total value class.
752
         *
753
         * @return the total value class
754
         */
755
        public String getTotalValueClass() {
756
            return this.totalValueClass;
1✔
757
        }
758

759
        /**
760
         * Sets the total value class.
761
         *
762
         * @param totalValueClass
763
         *            the new total value class
764
         */
765
        public void setTotalValueClass(final String totalValueClass) {
766
            this.totalValueClass = totalValueClass;
×
767
        }
×
768
    }
769
}
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