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

hazendaz / displaytag / 1525

05 Dec 2025 01:30AM UTC coverage: 76.996% (-0.04%) from 77.034%
1525

push

github

hazendaz
Cleanup deprecations and various code issues

1377 of 1935 branches covered (71.16%)

Branch coverage included in aggregate %.

22 of 27 new or added lines in 13 files covered. (81.48%)

1 existing line in 1 file now uncovered.

3898 of 4916 relevant lines covered (79.29%)

0.79 hits per line

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

77.01
/displaytag/src/main/java/org/displaytag/render/HssfTableWriter.java
1
/*
2
 * Copyright (C) 2002-2025 Fabrizio Giustina, the Displaytag team
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to deal
6
 * in the Software without restriction, including without limitation the rights
7
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
 * copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
 * THE SOFTWARE.
21
 */
22
package org.displaytag.render;
23

24
import java.text.MessageFormat;
25
import java.util.Calendar;
26
import java.util.Collections;
27
import java.util.Date;
28
import java.util.List;
29

30
import org.apache.commons.lang3.StringUtils;
31
import org.apache.commons.lang3.Strings;
32
import org.apache.commons.lang3.math.NumberUtils;
33
import org.apache.poi.hssf.usermodel.HSSFCell;
34
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
35
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
36
import org.apache.poi.hssf.usermodel.HSSFFont;
37
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
38
import org.apache.poi.hssf.usermodel.HSSFRow;
39
import org.apache.poi.hssf.usermodel.HSSFSheet;
40
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
41
import org.apache.poi.ss.usermodel.BorderStyle;
42
import org.apache.poi.ss.usermodel.CellStyle;
43
import org.apache.poi.ss.usermodel.HorizontalAlignment;
44
import org.apache.poi.ss.usermodel.IndexedColors;
45
import org.apache.poi.ss.util.CellRangeAddress;
46
import org.displaytag.decorator.TableDecorator;
47
import org.displaytag.decorator.hssf.DecoratesHssf;
48
import org.displaytag.export.XmlTotalsWriter;
49
import org.displaytag.export.excel.ExcelUtils;
50
import org.displaytag.model.Column;
51
import org.displaytag.model.HeaderCell;
52
import org.displaytag.model.Row;
53
import org.displaytag.model.TableModel;
54

55
/**
56
 * A table writer that formats a table in Excel's spreadsheet format, and writes it to an HSSF workbook.
57
 *
58
 * @see org.displaytag.render.TableWriterTemplate
59
 */
60
public class HssfTableWriter extends TableWriterAdapter {
61

62
    /** The Constant EMPTY_TEXT. */
63
    public static final HSSFRichTextString EMPTY_TEXT = new HSSFRichTextString("");
1✔
64

65
    /** The total label. */
66
    protected MessageFormat totalLabel = new MessageFormat("{0} Total");
1✔
67

68
    /** The decorated. */
69
    protected boolean decorated = false;
1✔
70

71
    /**
72
     * The workbook to which the table is written.
73
     */
74
    private final HSSFWorkbook wb;
75

76
    /**
77
     * Generated sheet.
78
     */
79
    protected HSSFSheet sheet;
80

81
    /**
82
     * Current row number.
83
     */
84
    protected int sheetRowNum;
85

86
    /**
87
     * Current row.
88
     */
89
    private HSSFRow currentRow;
90

91
    /**
92
     * Current column number.
93
     */
94
    protected int colNum;
95

96
    /**
97
     * Current cell.
98
     */
99
    protected HSSFCell currentCell;
100

101
    /** The current grouping. */
102
    protected int currentGrouping = 0;
1✔
103

104
    /**
105
     * Percent Excel format.
106
     */
107

108
    protected short intFormat = HSSFDataFormat.getBuiltinFormat("0");
1✔
109

110
    /**
111
     * Some operations require the model.
112
     */
113
    protected TableModel model;
114

115
    /** The sheet name. */
116
    protected String sheetName = "-";
1✔
117

118
    /** The utils. */
119
    protected ExcelUtils utils;
120

121
    /**
122
     * This table writer uses an HSSF workbook to write the table.
123
     *
124
     * @param wb
125
     *            The HSSF workbook to write the table.
126
     */
127
    public HssfTableWriter(final HSSFWorkbook wb) {
1✔
128
        this.wb = wb;
1✔
129
        this.utils = new ExcelUtils(wb);
1✔
130
    }
1✔
131

132
    /**
133
     * Write table opener.
134
     *
135
     * @param model
136
     *            the model
137
     *
138
     * @throws Exception
139
     *             the exception
140
     *
141
     * @see org.displaytag.render.TableWriterTemplate#writeTableOpener(org.displaytag.model.TableModel)
142
     */
143
    @Override
144
    protected void writeTableOpener(final TableModel model) throws Exception {
145
        this.sheet = this.wb.createSheet(this.sheetName);
1✔
146
        this.setModel(model);
1✔
147
        this.init(model);
1✔
148
        this.sheetRowNum = 0;
1✔
149

150
    }
1✔
151

152
    /**
153
     * Override this to do local config, but you should call super() first so that this can set up the ExcelUtils.
154
     *
155
     * @param model
156
     *            the model
157
     */
158
    protected void init(final TableModel model) {
159
        this.utils.initCellStyles(model.getProperties());
1✔
160
    }
1✔
161

162
    /**
163
     * Write caption.
164
     *
165
     * @param model
166
     *            the model
167
     *
168
     * @throws Exception
169
     *             the exception
170
     *
171
     * @see org.displaytag.render.TableWriterTemplate#writeCaption(org.displaytag.model.TableModel)
172
     */
173
    @Override
174
    protected void writeCaption(final TableModel model) throws Exception {
175
        final HSSFCellStyle style = this.wb.createCellStyle();
×
176
        final HSSFFont bold = this.wb.createFont();
×
177
        bold.setBold(true);
×
178
        bold.setFontHeightInPoints((short) 14);
×
179
        style.setFont(bold);
×
180
        style.setAlignment(HorizontalAlignment.CENTER);
×
181

182
        this.colNum = 0;
×
183
        this.currentRow = this.sheet.createRow(this.sheetRowNum++);
×
184
        this.currentCell = this.currentRow.createCell(this.colNum);
×
185
        this.currentCell.setCellStyle(style);
×
186
        final String caption = model.getCaption();
×
187
        this.currentCell.setCellValue(new HSSFRichTextString(caption));
×
188
        this.rowSpanTable(model);
×
189
    }
×
190

191
    /**
192
     * Obtain the region over which to merge a cell.
193
     *
194
     * @param first
195
     *            Column number of first cell from which to merge.
196
     * @param last
197
     *            Column number of last cell over which to merge.
198
     *
199
     * @return The region over which to merge a cell.
200
     */
201
    private CellRangeAddress getMergeCellsRegion(final int first, final int last) {
202
        return new CellRangeAddress(this.currentRow.getRowNum(), this.currentRow.getRowNum(), first, last);
×
203
    }
204

205
    /**
206
     * Write table header.
207
     *
208
     * @param model
209
     *            the model
210
     *
211
     * @throws Exception
212
     *             the exception
213
     *
214
     * @see org.displaytag.render.TableWriterTemplate#writeTableHeader(org.displaytag.model.TableModel)
215
     */
216
    @Override
217
    protected void writeTableHeader(final TableModel model) throws Exception {
218
        this.currentRow = this.sheet.createRow(this.sheetRowNum++);
1✔
219
        this.colNum = 0;
1✔
220
        final HSSFCellStyle headerStyle = this.getHeaderFooterStyle();
1✔
221
        for (final HeaderCell headerCell : model.getHeaderCellList()) {
1✔
222
            String columnHeader = headerCell.getTitle();
1✔
223
            if (columnHeader == null) {
1!
224
                columnHeader = StringUtils.capitalize(headerCell.getBeanPropertyName());
×
225
            }
226

227
            this.writeHeaderFooter(columnHeader, this.currentRow, headerStyle);
1✔
228
        }
1✔
229
    }
1✔
230

231
    /**
232
     * Write decorated row start.
233
     *
234
     * @param model
235
     *            the model
236
     *
237
     * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowStart(org.displaytag.model.TableModel)
238
     */
239
    @Override
240
    protected void writeDecoratedRowStart(final TableModel model) {
241
        model.getTableDecorator().startRow();
1✔
242
    }
1✔
243

244
    /**
245
     * Write row opener.
246
     *
247
     * @param row
248
     *            the row
249
     *
250
     * @throws Exception
251
     *             the exception
252
     */
253
    @Override
254
    protected void writeRowOpener(final Row row) throws Exception {
255
        this.currentRow = this.sheet.createRow(this.sheetRowNum++);
1✔
256
        this.colNum = 0;
1✔
257
    }
1✔
258

259
    /**
260
     * Write a column's opening structure to a HSSF document.
261
     *
262
     * @param column
263
     *            the column
264
     *
265
     * @throws Exception
266
     *             the exception
267
     *
268
     * @see org.displaytag.render.TableWriterTemplate#writeColumnOpener(org.displaytag.model.Column)
269
     */
270
    @Override
271
    protected void writeColumnOpener(final Column column) throws Exception {
272
        if (column != null) {
1✔
273
            column.getOpenTag(); // has side effect, setting its stringValue, which affects grouping logic.
1✔
274
        }
275
        this.currentCell = this.currentRow.createCell(this.colNum++);
1✔
276
    }
1✔
277

278
    /**
279
     * Write column value.
280
     *
281
     * @param value
282
     *            the value
283
     * @param column
284
     *            the column
285
     *
286
     * @throws Exception
287
     *             the exception
288
     *
289
     * @see org.displaytag.render.TableWriterTemplate#writeColumnValue(Object,org.displaytag.model.Column)
290
     */
291
    @Override
292
    protected void writeColumnValue(final Object value, final Column column) throws Exception {
293
        // is this a detail row for a column that is currently grouped?
294
        final int myGroup = column.getHeaderCell().getGroup();
1✔
295
        Object cellValue = column.getValue(this.decorated);
1✔
296
        if (myGroup > 0) {
1✔
297
            cellValue = "";
1✔
298
        }
299
        this.writeCellValue(cellValue);
1✔
300
    }
1✔
301

302
    /**
303
     * Override in subclasses to handle local data types.
304
     *
305
     * @param value
306
     *            the value object to write
307
     */
308
    protected void writeCellValue(final Object value) {
309
        if (value instanceof Number) {
1✔
310
            final Number num = (Number) value;
1✔
311
            // Percentage
312
            if (value.toString().indexOf('%') > -1) {
1!
313
                this.currentCell.setCellValue(num.doubleValue() / 100);
×
314
                this.currentCell.setCellStyle(this.utils.getStyle(ExcelUtils.STYLE_PCT));
×
315
            } else if (value instanceof Integer) {
1✔
316
                this.currentCell.setCellStyle(this.utils.getStyle(ExcelUtils.STYLE_INTEGER));
1✔
317
                this.currentCell.setCellValue(num.intValue());
1✔
318
            } else {
319
                this.currentCell.setCellValue(num.doubleValue());
1✔
320
            }
321

322
        } else if (value instanceof Date) {
1✔
323
            this.currentCell.setCellValue((Date) value);
1✔
324
            this.currentCell.setCellStyle(this.utils.getStyle(ExcelUtils.STYLE_DATE));
1✔
325
        } else if (value instanceof Calendar) {
1!
326
            final Calendar c = (Calendar) value;
×
327
            this.currentCell.setCellValue(c);
×
328
            this.currentCell.setCellStyle(this.utils.getStyle(ExcelUtils.STYLE_DATE));
×
329
        } else if (value == null) {
1✔
330
            this.currentCell.setCellValue(HssfTableWriter.EMPTY_TEXT);
1✔
331
        } else {
332
            final String v = value.toString();
1✔
333
            if (v.length() > this.utils.getWrapAtLength()) {
1✔
334
                this.currentCell.getCellStyle().setWrapText(true);
1✔
335
            }
336
            this.currentCell.setCellValue(new HSSFRichTextString(ExcelUtils.escapeColumnValue(value)));
1✔
337
        }
338

339
    }
1✔
340

341
    /**
342
     * Decorators that help render the table to an HSSF table must implement DecoratesHssf.
343
     *
344
     * @param model
345
     *            the model
346
     *
347
     * @throws Exception
348
     *             the exception
349
     *
350
     * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowFinish(org.displaytag.model.TableModel)
351
     */
352
    @Override
353
    protected void writeDecoratedRowFinish(final TableModel model) throws Exception {
354
        final TableDecorator decorator = model.getTableDecorator();
1✔
355
        if (decorator instanceof DecoratesHssf) {
1!
356
            final DecoratesHssf hdecorator = (DecoratesHssf) decorator;
×
357
            hdecorator.setSheet(this.sheet);
×
358
        }
359
        decorator.finishRow();
1✔
360
        this.sheetRowNum = this.sheet.getLastRowNum();
1✔
361
        this.sheetRowNum++;
1✔
362
    }
1✔
363

364
    /**
365
     * Write post body footer.
366
     *
367
     * @param model
368
     *            the model
369
     *
370
     * @throws Exception
371
     *             the exception
372
     *
373
     * @see org.displaytag.render.TableWriterTemplate#writePostBodyFooter(org.displaytag.model.TableModel)
374
     */
375
    @Override
376
    protected void writePostBodyFooter(final TableModel model) throws Exception {
377
        this.colNum = 0;
×
378
        this.currentRow = this.sheet.createRow(this.sheetRowNum++);
×
379
        this.writeHeaderFooter(model.getFooter(), this.currentRow, this.getHeaderFooterStyle());
×
380
        this.rowSpanTable(model);
×
381
    }
×
382

383
    /**
384
     * Make a row span the width of the table.
385
     *
386
     * @param model
387
     *            The table model representing the rendered table.
388
     */
389
    private void rowSpanTable(final TableModel model) {
390
        this.sheet.addMergedRegion(
×
391
                this.getMergeCellsRegion(this.currentCell.getColumnIndex(), model.getNumberOfColumns() - 1));
×
392
    }
×
393

394
    /**
395
     * Write decorated table finish.
396
     *
397
     * @param model
398
     *            the model
399
     *
400
     * @see org.displaytag.render.TableWriterTemplate#writeDecoratedTableFinish(org.displaytag.model.TableModel)
401
     */
402
    @Override
403
    protected void writeDecoratedTableFinish(final TableModel model) {
404
        model.getTableDecorator().finish();
1✔
405
    }
1✔
406

407
    /**
408
     * Is this value numeric? You should probably override this method to handle your locale.
409
     *
410
     * @param rawValue
411
     *            the object value
412
     *
413
     * @return true if numeric
414
     */
415
    protected boolean isNumber(final String rawValue) {
416
        if (rawValue == null) {
×
417
            return false;
×
418
        }
419
        String rawV = rawValue;
×
420
        if (rawV.indexOf('%') > -1) {
×
421
            rawV = rawV.replace('%', ' ').trim();
×
422
        }
423
        if (rawV.indexOf('$') > -1) {
×
424
            rawV = rawV.replace('$', ' ').trim();
×
425
        }
426
        if (rawV.indexOf(',') > -1) {
×
NEW
427
            rawV = Strings.CS.replace(rawV, ",", "");
×
428
        }
429
        return NumberUtils.isCreatable(rawV.trim());
×
430
    }
431

432
    /**
433
     * Writes a table header or a footer.
434
     *
435
     * @param value
436
     *            Header or footer value to be rendered.
437
     * @param row
438
     *            The row in which to write the header or footer.
439
     * @param style
440
     *            Style used to render the header or footer.
441
     */
442
    private void writeHeaderFooter(final String value, final HSSFRow row, final HSSFCellStyle style) {
443
        this.currentCell = row.createCell(this.colNum++);
1✔
444
        this.currentCell.setCellValue(new HSSFRichTextString(value));
1✔
445
        this.currentCell.setCellStyle(style);
1✔
446
    }
1✔
447

448
    /**
449
     * Obtain the style used to render a header or footer.
450
     *
451
     * @return The style used to render a header or footer.
452
     */
453
    private HSSFCellStyle getHeaderFooterStyle() {
454
        final HSSFCellStyle style = this.wb.createCellStyle();
1✔
455
        final HSSFFont bold = this.wb.createFont();
1✔
456
        bold.setBold(true);
1✔
457
        style.setBorderBottom(BorderStyle.THIN);
1✔
458
        style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
1✔
459

460
        style.setFont(bold);
1✔
461
        return style;
1✔
462
    }
463

464
    /**
465
     * Write bottom banner.
466
     *
467
     * @param model
468
     *            the model
469
     *
470
     * @throws Exception
471
     *             the exception
472
     *
473
     * @see org.displaytag.render.TableWriterAdapter#writeBottomBanner(org.displaytag.model.TableModel)
474
     */
475
    @Override
476
    protected void writeBottomBanner(final TableModel model) throws Exception {
477
        // adjust the column widths
478
        int colCount = 0;
1✔
479
        while (colCount <= this.colNum) {
1✔
480
            this.sheet.autoSizeColumn((short) colCount);
1✔
481
            colCount++;
1✔
482
        }
483
    }
1✔
484

485
    /**
486
     * Write subgroup start.
487
     *
488
     * @param model
489
     *            the model
490
     *
491
     * @throws Exception
492
     *             the exception
493
     */
494
    @Override
495
    protected void writeSubgroupStart(final TableModel model) throws Exception {
496
        final TableTotaler tt = model.getTotaler();
1✔
497
        if (tt.howManyGroups == 0) {
1✔
498
            return;
1✔
499
        }
500

501
        // for each newly opened subgroup we need to output the opener, in order;
502
        // so we need to know somehow which groups are new since we last wrote out openers; how about we track a list of
503
        // the
504
        // already opened groups, and ask the tt for a list of all known groups?
505

506
        for (final int dtColumnNumber : tt.getOpenedColumns()) {
1✔
507
            this.currentGrouping++;
1✔
508
            this.writeRowOpener(null);
1✔
509
            // for each subgroup
510

511
            for (final HeaderCell cell : model.getHeaderCellList()) {
1✔
512
                this.writeColumnOpener(null);
1✔
513
                final int thisCellAsDtNumber = this.asDtColNumber(cell.getColumnNumber());
1✔
514
                final String columnValue = thisCellAsDtNumber != dtColumnNumber ? ""
1✔
515
                        : tt.getGroupingValue(dtColumnNumber);
1✔
516
                this.writeCellValue(columnValue);
1✔
517
                this.writeColumnCloser(null);
1✔
518
            }
1✔
519

520
            this.writeRowCloser(null);
1✔
521
            // Have to handle a case where this is a nested subgroup start;
522
            // put out the blanks for any column that has already exists
523
            // now write the label for the group that is opening
524
        }
1✔
525
    }
1✔
526

527
    /**
528
     * DT columns are 1 based, excel columns are 0 based.
529
     *
530
     * @param cellColumnNumber
531
     *            the cell column number
532
     *
533
     * @return the int
534
     */
535
    protected int asDtColNumber(final int cellColumnNumber) {
536
        return cellColumnNumber + 1;
1✔
537
    }
538

539
    /**
540
     * Gets the total label.
541
     *
542
     * @param groupingValue
543
     *            the grouping value
544
     *
545
     * @return the total label
546
     */
547
    public String getTotalLabel(final String groupingValue) {
548
        final String gv = StringUtils.defaultString(groupingValue);
1✔
549
        return MessageFormat.format("{0} Total", gv);
1✔
550
    }
551

552
    /**
553
     * Write subgroup stop.
554
     *
555
     * @param model
556
     *            the model
557
     *
558
     * @throws Exception
559
     *             the exception
560
     */
561
    @Override
562
    protected void writeSubgroupStop(final TableModel model) throws Exception {
563
        final TableTotaler tt = model.getTotaler();
1✔
564

565
        // for each newly opened subgroup we need to output the opener, in order;
566
        // so we need to know somehow which groups are new since we last wrote out openers; how about we track a list of
567
        // the
568
        // already opened groups, and ask the tt for a list of all known groups?
569

570
        if (tt.howManyGroups == 0) {
1✔
571
            return;
1✔
572
        }
573
        final List<Integer> closedColumns = tt.getClosedColumns();
1✔
574
        Collections.reverse(closedColumns);
1✔
575
        for (final int columnNumber : closedColumns) {
1✔
576
            this.writeRowOpener(null);
1✔
577
            // for each subgroup
578

579
            for (final HeaderCell cell : model.getHeaderCellList()) {
1✔
580
                this.writeColumnOpener(null);
1✔
581
                Object columnValue;
582
                final int cellColumnNumberAsDt = this.asDtColNumber(cell.getColumnNumber());
1✔
583
                if (cellColumnNumberAsDt > columnNumber && cell.isTotaled()) {
1✔
584
                    columnValue = tt.getTotalForColumn(cell.getColumnNumber(), this.currentGrouping);
1✔
585
                } else if (cellColumnNumberAsDt == columnNumber) {
1✔
586
                    columnValue = this.getTotalLabel(tt.getGroupingValue(columnNumber));
1✔
587
                } else {
588
                    columnValue = null;
1✔
589
                }
590
                this.writeCellValue(columnValue);
1✔
591
                this.writeColumnCloser(null);
1✔
592
            }
1✔
593

594
            this.writeRowCloser(null);
1✔
595
            this.writeGroupExtraInfo(model);
1✔
596
            this.currentGrouping--;
1✔
597
        }
1✔
598

599
        assert this.currentGrouping > -1;
1!
600
        super.writeSubgroupStop(model);
1✔
601
    }
1✔
602

603
    /**
604
     * Sets the model.
605
     *
606
     * @param m
607
     *            the new model
608
     */
609
    public void setModel(final TableModel m) {
610
        m.setTableDecorator(XmlTotalsWriter.NOOP);
1✔
611
        if (m.getTotaler() == null || m.getTotaler() == TableTotaler.NULL) {
1!
612
            final TableTotaler tt = new TableTotaler();
×
613
            tt.init(m);
×
614
            m.setTotaler(tt);
×
615
        }
616
        this.model = m;
1✔
617
    }
1✔
618

619
    /**
620
     * Gets the sheet name.
621
     *
622
     * @return the sheet name
623
     */
624
    public String getSheetName() {
625
        return this.sheetName;
×
626
    }
627

628
    /**
629
     * Sets the sets the sheet name.
630
     *
631
     * @param name
632
     *            the new sets the sheet name
633
     */
634
    public void setSetSheetName(final String name) {
635
        this.sheetName = name;
1✔
636
    }
1✔
637

638
    /**
639
     * Gets the sheet.
640
     *
641
     * @return the sheet
642
     */
643
    public HSSFSheet getSheet() {
644
        return this.sheet;
×
645
    }
646

647
    /**
648
     * Write table body closer.
649
     *
650
     * @param model
651
     *            the model
652
     *
653
     * @throws Exception
654
     *             the exception
655
     */
656
    @Override
657
    protected void writeTableBodyCloser(final TableModel model) throws Exception {
658
        // write totals, if there are any
659
        boolean hasTotals = false;
1✔
660
        for (final HeaderCell cell : model.getHeaderCellList()) {
1✔
661
            hasTotals = hasTotals || cell.isTotaled();
1✔
662
        }
1✔
663
        if (!hasTotals) {
1!
664
            return;
×
665
        }
666
        final TableTotaler tt = model.getTotaler();
1✔
667
        this.writeRowOpener(null);
1✔
668
        for (final HeaderCell cell : model.getHeaderCellList()) {
1✔
669
            this.writeColumnOpener(null);
1✔
670
            final Object columnValue = cell.isTotaled() ? tt.getTotalForColumn(cell.getColumnNumber(), 0) : null;
1✔
671
            this.writeCellValue(columnValue);
1✔
672
            final CellStyle st = this.utils.getNewCellStyle();
1✔
673
            st.cloneStyleFrom(this.currentCell.getCellStyle());
1✔
674
            st.setBorderTop(BorderStyle.THIN);
1✔
675
            st.setTopBorderColor(IndexedColors.BLACK.getIndex());
1✔
676
            this.currentCell.setCellStyle(st);
1✔
677
            this.writeColumnCloser(null);
1✔
678
        }
1✔
679
        this.writeRowCloser(null);
1✔
680
    }
1✔
681

682
    /**
683
     * Write group extra info.
684
     *
685
     * @param model
686
     *            the model
687
     *
688
     * @throws Exception
689
     *             the exception
690
     */
691
    protected void writeGroupExtraInfo(final TableModel model) throws Exception {
692
    }
1✔
693
}
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