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

hazendaz / displaytag / 1187

05 Sep 2025 01:04AM UTC coverage: 77.034%. Remained the same
1187

push

github

hazendaz
Formatting / copy right date update

1376 of 1931 branches covered (71.26%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

42 existing lines in 2 files now uncovered.

3897 of 4914 relevant lines covered (79.3%)

0.79 hits per line

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

91.19
/displaytag/src/main/java/org/displaytag/render/HtmlTableWriter.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 jakarta.servlet.jsp.JspWriter;
25

26
import java.io.IOException;
27
import java.text.MessageFormat;
28
import java.util.Iterator;
29
import java.util.Map;
30
import java.util.Map.Entry;
31
import java.util.Objects;
32

33
import org.apache.commons.lang3.StringUtils;
34
import org.apache.commons.lang3.Strings;
35
import org.displaytag.exception.DecoratorException;
36
import org.displaytag.exception.ObjectLookupException;
37
import org.displaytag.exception.WrappedRuntimeException;
38
import org.displaytag.model.Column;
39
import org.displaytag.model.HeaderCell;
40
import org.displaytag.model.Row;
41
import org.displaytag.model.TableModel;
42
import org.displaytag.pagination.PaginatedList;
43
import org.displaytag.pagination.SmartListHelper;
44
import org.displaytag.properties.MediaTypeEnum;
45
import org.displaytag.properties.SortOrderEnum;
46
import org.displaytag.properties.TableProperties;
47
import org.displaytag.tags.CaptionTag;
48
import org.displaytag.tags.TableTagParameters;
49
import org.displaytag.util.Anchor;
50
import org.displaytag.util.Href;
51
import org.displaytag.util.HtmlAttributeMap;
52
import org.displaytag.util.ParamEncoder;
53
import org.displaytag.util.PostHref;
54
import org.displaytag.util.TagConstants;
55
import org.slf4j.Logger;
56
import org.slf4j.LoggerFactory;
57

58
/**
59
 * A table writer that formats a table in HTML and writes it to a JSP page.
60
 *
61
 * @see org.displaytag.render.TableWriterTemplate
62
 *
63
 * @since 1.1
64
 */
65
public class HtmlTableWriter extends TableWriterAdapter {
66

67
    /** Logger. */
68
    private static Logger logger = LoggerFactory.getLogger(HtmlTableWriter.class);
1✔
69

70
    /** <code>TableProperties</code>. */
71
    private final TableProperties properties;
72

73
    /**
74
     * Output destination.
75
     */
76
    private final JspWriter out;
77

78
    /**
79
     * The param encoder used to generate unique parameter names. Initialized at the first use of encodeParameter().
80
     */
81
    private ParamEncoder paramEncoder;
82

83
    /**
84
     * base href used for links.
85
     */
86
    private final Href baseHref;
87

88
    /**
89
     * add export links.
90
     */
91
    private final boolean export;
92

93
    /** The caption tag. */
94
    private final CaptionTag captionTag;
95

96
    /**
97
     * The paginated list containing the external pagination and sort parameters The presence of this paginated list is
98
     * what determines if external pagination and sorting is used or not.
99
     */
100
    private final PaginatedList<Row> paginatedList;
101

102
    /**
103
     * Used by various functions when the person wants to do paging.
104
     */
105
    private final SmartListHelper listHelper;
106

107
    /**
108
     * page size.
109
     */
110
    private final int pagesize;
111

112
    /** The attribute map. */
113
    private final HtmlAttributeMap attributeMap;
114

115
    /**
116
     * Unique table id.
117
     */
118
    private final String uid;
119

120
    /**
121
     * This table writer uses a <code>TableTag</code> and a <code>JspWriter</code> to do its work.
122
     *
123
     * @param tableProperties
124
     *            the table properties
125
     * @param baseHref
126
     *            the base href
127
     * @param export
128
     *            the export
129
     * @param out
130
     *            The output destination.
131
     * @param captionTag
132
     *            the caption tag
133
     * @param paginatedList
134
     *            the paginated list
135
     * @param listHelper
136
     *            the list helper
137
     * @param pagesize
138
     *            the pagesize
139
     * @param attributeMap
140
     *            the attribute map
141
     * @param uid
142
     *            the uid
143
     */
144
    public HtmlTableWriter(final TableProperties tableProperties, final Href baseHref, final boolean export,
145
            final JspWriter out, final CaptionTag captionTag, final PaginatedList<Row> paginatedList,
146
            final SmartListHelper listHelper, final int pagesize, final HtmlAttributeMap attributeMap,
147
            final String uid) {
1✔
148
        this.properties = tableProperties;
1✔
149
        this.baseHref = baseHref;
1✔
150
        this.export = export;
1✔
151
        this.out = out;
1✔
152
        this.captionTag = captionTag;
1✔
153
        this.paginatedList = paginatedList;
1✔
154
        this.listHelper = listHelper;
1✔
155
        this.pagesize = pagesize;
1✔
156
        this.attributeMap = attributeMap;
1✔
157
        this.uid = uid;
1✔
158
    }
1✔
159

160
    /**
161
     * Writes a banner containing search result and paging navigation above an HTML table to a JSP page.
162
     *
163
     * @param model
164
     *            the model
165
     *
166
     * @see org.displaytag.render.TableWriterTemplate#writeTopBanner(org.displaytag.model.TableModel)
167
     */
168
    @Override
169
    protected void writeTopBanner(final TableModel model) {
170
        if (model.getForm() != null) {
1✔
171

172
            final String js = "<script type=\"text/javascript\">\n" + "function displaytagform(formname, fields){\n"
1✔
173
                    + "    var objfrm = document.forms[formname];\n"
174
                    + "    for (j=fields.length-1;j>=0;j--){var f= objfrm.elements[fields[j].f];if (f){f.value=fields[j].v};}\n"
175
                    + "    objfrm.submit();\n" + "}\n" + "</script>";
176
            this.writeFormFields(model);
1✔
177
            this.write(js);
1✔
178
        }
179

180
        // Put the page stuff there if it needs to be there...
181
        if (this.properties.getAddPagingBannerTop()) {
1✔
182
            this.writeSearchResultAndNavigation(model);
1✔
183
        }
184

185
        // add export links (only if the table is not empty)
186
        if (this.export && this.properties.getAddExportBannerTop() && !model.getRowListPage().isEmpty()) {
1!
187
            // generate export link
UNCOV
188
            this.writeExportLinks(model);
×
189
        }
190
    }
1✔
191

192
    /**
193
     * Writes an HTML table's opening tags to a JSP page.
194
     *
195
     * @param model
196
     *            the model
197
     *
198
     * @see org.displaytag.render.TableWriterTemplate#writeTableOpener(org.displaytag.model.TableModel)
199
     */
200
    @Override
201
    protected void writeTableOpener(final TableModel model) {
202
        this.write(this.getOpenTag());
1✔
203
    }
1✔
204

205
    /**
206
     * Write form fields.
207
     *
208
     * @param model
209
     *            the model
210
     */
211
    private void writeFormFields(final TableModel model) {
212
        final Map<String, String[]> parameters = this.baseHref.getParameterMap();
1✔
213

214
        final ParamEncoder pe = new ParamEncoder(model.getId());
1✔
215

216
        this.addIfMissing(parameters, pe.encodeParameterName(TableTagParameters.PARAMETER_ORDER));
1✔
217
        this.addIfMissing(parameters, pe.encodeParameterName(TableTagParameters.PARAMETER_PAGE));
1✔
218
        this.addIfMissing(parameters, pe.encodeParameterName(TableTagParameters.PARAMETER_SORT));
1✔
219

220
        for (final Entry<String, String[]> entry : parameters.entrySet()) {
1✔
221
            final Object value = entry.getValue();
1✔
222

223
            if (value != null && value.getClass().isArray()) {
1!
224
                final Object[] arr = (Object[]) value;
1✔
225
                for (final Object element : arr) {
1✔
226
                    this.writeField(entry.getKey(), element);
1✔
227
                }
228
            } else {
1✔
UNCOV
229
                this.writeField(entry.getKey(), value);
×
230
            }
231
        }
1✔
232
    }
1✔
233

234
    /**
235
     * Write field.
236
     *
237
     * @param key
238
     *            the key
239
     * @param value
240
     *            the value
241
     */
242
    private void writeField(final String key, final Object value) {
243
        final StringBuilder buffer = new StringBuilder();
1✔
244
        buffer.append("<input type=\"hidden\" name=\"");
1✔
245
        buffer.append(this.esc(key));
1✔
246
        buffer.append("\" value=\"");
1✔
247
        buffer.append(value);
1✔
248
        buffer.append("\"/>");
1✔
249

250
        this.write(buffer.toString());
1✔
251
    }
1✔
252

253
    /**
254
     * Esc.
255
     *
256
     * @param value
257
     *            the value
258
     *
259
     * @return the string
260
     */
261
    private String esc(final Object value) {
262
        return Strings.CS.replace(value != null ? value.toString() : StringUtils.EMPTY, "\"", "\\\"");
1!
263
    }
264

265
    /**
266
     * Adds an element to the given map if empty (use an empty string as value).
267
     *
268
     * @param parameters
269
     *            Map of parameters
270
     * @param key
271
     *            param key
272
     */
273
    private void addIfMissing(final Map<String, String[]> parameters, final String key) {
274
        parameters.computeIfAbsent(key, k -> new String[] { StringUtils.EMPTY });
1✔
275
    }
1✔
276

277
    /**
278
     * Writes an HTML table's caption to a JSP page.
279
     *
280
     * @param model
281
     *            the model
282
     *
283
     * @see org.displaytag.render.TableWriterTemplate#writeCaption(org.displaytag.model.TableModel)
284
     */
285
    @Override
286
    protected void writeCaption(final TableModel model) {
287
        this.write(this.captionTag.getOpenTag() + model.getCaption() + this.captionTag.getCloseTag());
1✔
288
    }
1✔
289

290
    /**
291
     * Writes an HTML table's footer to a JSP page; HTML requires tfoot to appear before tbody.
292
     *
293
     * @param model
294
     *            the model
295
     *
296
     * @see org.displaytag.render.TableWriterTemplate#writePreBodyFooter(org.displaytag.model.TableModel)
297
     */
298
    @Override
299
    protected void writePreBodyFooter(final TableModel model) {
300
        this.write(TagConstants.TAG_TFOOTER_OPEN);
1✔
301
        this.write(model.getFooter());
1✔
302
        this.write(TagConstants.TAG_TFOOTER_CLOSE);
1✔
303
    }
1✔
304

305
    /**
306
     * Writes the start of an HTML table's body to a JSP page.
307
     *
308
     * @param model
309
     *            the model
310
     *
311
     * @see org.displaytag.render.TableWriterTemplate#writeTableBodyOpener(org.displaytag.model.TableModel)
312
     */
313
    @Override
314
    protected void writeTableBodyOpener(final TableModel model) {
315
        this.write(TagConstants.TAG_TBODY_OPEN);
1✔
316

317
    }
1✔
318

319
    /**
320
     * Writes the end of an HTML table's body to a JSP page.
321
     *
322
     * @param model
323
     *            the model
324
     *
325
     * @see org.displaytag.render.TableWriterTemplate#writeTableBodyCloser(org.displaytag.model.TableModel)
326
     */
327
    @Override
328
    protected void writeTableBodyCloser(final TableModel model) {
329
        this.write(TagConstants.TAG_TBODY_CLOSE);
1✔
330
    }
1✔
331

332
    /**
333
     * Writes the closing structure of an HTML table to a JSP page.
334
     *
335
     * @param model
336
     *            the model
337
     *
338
     * @see org.displaytag.render.TableWriterTemplate#writeTableCloser(org.displaytag.model.TableModel)
339
     */
340
    @Override
341
    protected void writeTableCloser(final TableModel model) {
342
        this.write(TagConstants.TAG_OPENCLOSING);
1✔
343
        this.write(TagConstants.TABLE_TAG_NAME);
1✔
344
        this.write(TagConstants.TAG_CLOSE);
1✔
345
    }
1✔
346

347
    /**
348
     * Writes a banner containing search result, paging navigation, and export links below an HTML table to a JSP page.
349
     *
350
     * @param model
351
     *            the model
352
     *
353
     * @see org.displaytag.render.TableWriterTemplate#writeBottomBanner(org.displaytag.model.TableModel)
354
     */
355
    @Override
356
    protected void writeBottomBanner(final TableModel model) {
357
        this.writeNavigationAndExportLinks(model);
1✔
358
    }
1✔
359

360
    /**
361
     * Write decorated table finish.
362
     *
363
     * @param model
364
     *            the model
365
     *
366
     * @see org.displaytag.render.TableWriterTemplate#writeDecoratedTableFinish(org.displaytag.model.TableModel)
367
     */
368
    @Override
369
    protected void writeDecoratedTableFinish(final TableModel model) {
370
        model.getTableDecorator().finish();
1✔
371
    }
1✔
372

373
    /**
374
     * Write decorated row start.
375
     *
376
     * @param model
377
     *            the model
378
     *
379
     * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowStart(org.displaytag.model.TableModel)
380
     */
381
    @Override
382
    protected void writeDecoratedRowStart(final TableModel model) {
383
        this.write(model.getTableDecorator().startRow());
1✔
384
    }
1✔
385

386
    /**
387
     * Writes an HTML table's row-opening tag to a JSP page.
388
     *
389
     * @param row
390
     *            the row
391
     *
392
     * @see org.displaytag.render.TableWriterTemplate#writeRowOpener(org.displaytag.model.Row)
393
     */
394
    @Override
395
    protected void writeRowOpener(final Row row) {
396
        this.write(row.getOpenTag());
1✔
397
    }
1✔
398

399
    /**
400
     * Writes an HTML table's column-opening tag to a JSP page.
401
     *
402
     * @param column
403
     *            the column
404
     *
405
     * @throws ObjectLookupException
406
     *             the object lookup exception
407
     * @throws DecoratorException
408
     *             the decorator exception
409
     *
410
     * @see org.displaytag.render.TableWriterTemplate#writeColumnOpener(org.displaytag.model.Column)
411
     */
412
    @Override
413
    protected void writeColumnOpener(final Column column) throws ObjectLookupException, DecoratorException {
414
        this.write(column.getOpenTag());
1✔
415
    }
1✔
416

417
    /**
418
     * Writes an HTML table's column-closing tag to a JSP page.
419
     *
420
     * @param column
421
     *            the column
422
     *
423
     * @see org.displaytag.render.TableWriterTemplate#writeColumnCloser(org.displaytag.model.Column)
424
     */
425
    @Override
426
    protected void writeColumnCloser(final Column column) {
427
        this.write(column.getCloseTag());
1✔
428
    }
1✔
429

430
    /**
431
     * Writes to a JSP page an HTML table row that has no columns.
432
     *
433
     * @param rowValue
434
     *            the row value
435
     *
436
     * @see org.displaytag.render.TableWriterTemplate#writeRowWithNoColumns(java.lang.String)
437
     */
438
    @Override
439
    protected void writeRowWithNoColumns(final String rowValue) {
440
        this.write(TagConstants.TAG_TD_OPEN);
1✔
441
        this.write(rowValue);
1✔
442
        this.write(TagConstants.TAG_TD_CLOSE);
1✔
443
    }
1✔
444

445
    /**
446
     * Writes an HTML table's row-closing tag to a JSP page.
447
     *
448
     * @param row
449
     *            the row
450
     *
451
     * @see org.displaytag.render.TableWriterTemplate#writeRowCloser(org.displaytag.model.Row)
452
     */
453
    @Override
454
    protected void writeRowCloser(final Row row) {
455
        this.write(row.getCloseTag());
1✔
456
    }
1✔
457

458
    /**
459
     * Write decorated row finish.
460
     *
461
     * @param model
462
     *            the model
463
     *
464
     * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowFinish(org.displaytag.model.TableModel)
465
     */
466
    @Override
467
    protected void writeDecoratedRowFinish(final TableModel model) {
468
        this.write(model.getTableDecorator().finishRow());
1✔
469
    }
1✔
470

471
    /**
472
     * Writes an HTML message to a JSP page explaining that the table model contains no data.
473
     *
474
     * @param emptyListMessage
475
     *            the empty list message
476
     *
477
     * @see org.displaytag.render.TableWriterTemplate#writeEmptyListMessage(java.lang.String)
478
     */
479
    @Override
480
    protected void writeEmptyListMessage(final String emptyListMessage) {
481
        this.write(emptyListMessage);
1✔
482
    }
1✔
483

484
    /**
485
     * Writes a HTML table column value to a JSP page.
486
     *
487
     * @param value
488
     *            the value
489
     * @param column
490
     *            the column
491
     *
492
     * @see org.displaytag.render.TableWriterTemplate#writeColumnValue(java.lang.Object,org.displaytag.model.Column)
493
     */
494
    @Override
495
    protected void writeColumnValue(final Object value, final Column column) {
496
        this.write(value);
1✔
497
    }
1✔
498

499
    /**
500
     * Writes an HTML message to a JSP page explaining that the row contains no data.
501
     *
502
     * @param message
503
     *            the message
504
     *
505
     * @see org.displaytag.render.TableWriterTemplate#writeEmptyListRowMessage(java.lang.String)
506
     */
507
    @Override
508
    protected void writeEmptyListRowMessage(final String message) {
509
        this.write(message);
1✔
510
    }
1✔
511

512
    /**
513
     * Writes an HTML table's column header to a JSP page.
514
     *
515
     * @param model
516
     *            the model
517
     *
518
     * @see org.displaytag.render.TableWriterTemplate#writeTableHeader(org.displaytag.model.TableModel)
519
     */
520
    @Override
521
    protected void writeTableHeader(final TableModel model) {
522

523
        if (HtmlTableWriter.logger.isDebugEnabled()) {
1!
UNCOV
524
            HtmlTableWriter.logger.debug("[{}] getTableHeader called", model.getId());
×
525
        }
526

527
        // open thead
528
        this.write(TagConstants.TAG_THEAD_OPEN);
1✔
529

530
        // open tr
531
        this.write(TagConstants.TAG_TR_OPEN);
1✔
532

533
        // no columns?
534
        if (model.isEmpty()) {
1✔
535
            this.write(TagConstants.TAG_TH_OPEN);
1✔
536
            this.write(TagConstants.TAG_TH_CLOSE);
1✔
537
        }
538

539
        // iterator on columns for header
540
        final Iterator<HeaderCell> iterator = model.getHeaderCellList().iterator();
1✔
541

542
        while (iterator.hasNext()) {
1✔
543
            // get the header cell
544
            final HeaderCell headerCell = iterator.next();
1✔
545

546
            if (headerCell.getSortable()) {
1✔
547
                final String cssSortable = this.properties.getCssSortable();
1✔
548
                headerCell.addHeaderClass(cssSortable);
1✔
549
            }
550

551
            // if sorted add styles
552
            if (headerCell.isAlreadySorted()) {
1✔
553
                // sorted css class
554
                headerCell.addHeaderClass(this.properties.getCssSorted());
1✔
555

556
                // sort order css class
557
                headerCell.addHeaderClass(this.properties.getCssOrder(model.isSortOrderAscending()));
1✔
558
            }
559

560
            // append th with html attributes
561
            this.write(headerCell.getHeaderOpenTag());
1✔
562

563
            // title
564
            String header = headerCell.getTitle();
1✔
565

566
            // column is sortable, create link
567
            if (headerCell.getSortable()) {
1✔
568
                // creates the link for sorting
569
                final Anchor anchor = new Anchor(this.getSortingHref(headerCell, model), header);
1✔
570

571
                // append to buffer
572
                header = anchor.toString();
1✔
573
            }
574

575
            this.write(header);
1✔
576
            this.write(headerCell.getHeaderCloseTag());
1✔
577
        }
1✔
578

579
        // close tr
580
        this.write(TagConstants.TAG_TR_CLOSE);
1✔
581

582
        // close thead
583
        this.write(TagConstants.TAG_THEAD_CLOSE);
1✔
584

585
        if (HtmlTableWriter.logger.isDebugEnabled()) {
1!
UNCOV
586
            HtmlTableWriter.logger.debug("[{}] getTableHeader end", model.getId());
×
587
        }
588
    }
1✔
589

590
    /**
591
     * Generates the link to be added to a column header for sorting.
592
     *
593
     * @param headerCell
594
     *            header cell the link should be added to
595
     * @param model
596
     *            the model
597
     *
598
     * @return Href for sorting
599
     */
600
    private Href getSortingHref(final HeaderCell headerCell, final TableModel model) {
601
        // costruct Href from base href, preserving parameters
602
        Href href = (Href) this.baseHref.clone();
1✔
603

604
        if (model.getForm() != null) {
1!
UNCOV
605
            href = new PostHref(href, model.getForm());
×
606
        }
607

608
        if (this.paginatedList == null) {
1✔
609
            // add column number as link parameter
610
            if (!model.isLocalSort() && headerCell.getSortName() != null) {
1✔
611
                href.addParameter(this.encodeParameter(TableTagParameters.PARAMETER_SORT, model),
1✔
612
                        headerCell.getSortName());
1✔
613
                href.addParameter(this.encodeParameter(TableTagParameters.PARAMETER_SORTUSINGNAME, model), "1");
1✔
614
            } else {
615
                href.addParameter(this.encodeParameter(TableTagParameters.PARAMETER_SORT, model),
1✔
616
                        headerCell.getColumnNumber());
1✔
617
            }
618

619
            boolean nowOrderAscending = true;
1✔
620

621
            if (headerCell.getDefaultSortOrder() != null) {
1✔
622
                final boolean sortAscending = SortOrderEnum.ASCENDING.equals(headerCell.getDefaultSortOrder());
1✔
623
                nowOrderAscending = headerCell.isAlreadySorted() ? !model.isSortOrderAscending() : sortAscending;
1✔
624
            } else {
1✔
625
                nowOrderAscending = (!headerCell.isAlreadySorted() || !model.isSortOrderAscending());
1✔
626
            }
627

628
            final int sortOrderParam = nowOrderAscending ? SortOrderEnum.ASCENDING.getCode()
1✔
629
                    : SortOrderEnum.DESCENDING.getCode();
1✔
630
            href.addParameter(this.encodeParameter(TableTagParameters.PARAMETER_ORDER, model), sortOrderParam);
1✔
631

632
            // If user want to sort the full table I need to reset the page number.
633
            // or if we aren't sorting locally we need to reset the page as well.
634
            if (model.isSortFullTable() || !model.isLocalSort()) {
1✔
635
                href.addParameter(this.encodeParameter(TableTagParameters.PARAMETER_PAGE, model), 1);
1✔
636
            }
637
        } else {
1✔
638
            if (this.properties.getPaginationSkipPageNumberInSort()) {
1!
639
                href.removeParameter(this.properties.getPaginationPageNumberParam());
1✔
640
            }
641

642
            String sortProperty = headerCell.getSortProperty();
1✔
643
            if (sortProperty == null) {
1!
644
                sortProperty = headerCell.getBeanPropertyName();
1✔
645
            }
646

647
            href.addParameter(this.properties.getPaginationSortParam(), sortProperty);
1✔
648
            String dirParam;
649
            if (headerCell.isAlreadySorted()) {
1!
650
                dirParam = model.isSortOrderAscending() ? this.properties.getPaginationDescValue()
1!
651
                        : this.properties.getPaginationAscValue();
1✔
652
            } else {
UNCOV
653
                dirParam = this.properties.getPaginationAscValue();
×
654
            }
655
            href.addParameter(this.properties.getPaginationSortDirectionParam(), dirParam);
1✔
656
            if (this.paginatedList.getSearchId() != null) {
1!
657
                href.addParameter(this.properties.getPaginationSearchIdParam(), this.paginatedList.getSearchId());
1✔
658
            }
659
        }
660

661
        return href;
1✔
662
    }
663

664
    /**
665
     * encode a parameter name to be unique in the page using ParamEncoder.
666
     *
667
     * @param parameterName
668
     *            parameter name to encode
669
     * @param model
670
     *            the model
671
     *
672
     * @return String encoded parameter name
673
     */
674
    private String encodeParameter(final String parameterName, final TableModel model) {
675
        // paramEncoder has been already instantiated?
676
        if (this.paramEncoder == null) {
1✔
677
            // use the id attribute to get the unique identifier
678
            this.paramEncoder = new ParamEncoder(model.getId());
1✔
679
        }
680

681
        return this.paramEncoder.encodeParameterName(parameterName);
1✔
682
    }
683

684
    /**
685
     * Generates table footer with links for export commands.
686
     *
687
     * @param model
688
     *            the model
689
     */
690
    protected void writeNavigationAndExportLinks(final TableModel model) {
691
        // Put the page stuff there if it needs to be there...
692
        if (this.properties.getAddPagingBannerBottom()) {
1✔
693
            this.writeSearchResultAndNavigation(model);
1✔
694
        }
695

696
        // add export links (only if the table is not empty)
697
        if (this.export && this.properties.getAddExportBannerBottom() && !model.getRowListPage().isEmpty()) {
1!
698
            this.writeExportLinks(model);
1✔
699
        }
700
    }
1✔
701

702
    /**
703
     * generates the search result and navigation bar.
704
     *
705
     * @param model
706
     *            the model
707
     */
708
    protected void writeSearchResultAndNavigation(final TableModel model) {
709
        if (this.paginatedList == null && this.pagesize != 0 && this.listHelper != null || this.paginatedList != null) {
1!
710
            // create a new href
711
            Href navigationHref = (Href) this.baseHref.clone();
1✔
712

713
            if (model.getForm() != null) {
1✔
714
                navigationHref = new PostHref(navigationHref, model.getForm());
1✔
715
            }
716

717
            this.write(this.listHelper.getSearchResultsSummary());
1✔
718

719
            String pageParameter;
720
            if (this.paginatedList == null) {
1✔
721
                pageParameter = this.encodeParameter(TableTagParameters.PARAMETER_PAGE, model);
1✔
722
            } else {
723
                pageParameter = this.properties.getPaginationPageNumberParam();
1✔
724
                if (this.paginatedList.getSearchId() != null && !navigationHref.getParameterMap()
1!
725
                        .containsKey(this.properties.getPaginationSearchIdParam())) {
1!
726
                    navigationHref.addParameter(this.properties.getPaginationSearchIdParam(),
1✔
727
                            this.paginatedList.getSearchId());
1✔
728
                }
729
            }
730
            this.write(this.listHelper.getPageNavigationBar(navigationHref, pageParameter));
1✔
731
        }
732
    }
1✔
733

734
    /**
735
     * Writes the formatted export links section.
736
     *
737
     * @param model
738
     *            the model
739
     */
740
    private void writeExportLinks(final TableModel model) {
741
        // Figure out what formats they want to export, make up a little string
742
        final Href exportHref = (Href) this.baseHref.clone();
1✔
743

744
        final StringBuilder buffer = new StringBuilder(200);
1✔
745
        final Iterator<MediaTypeEnum> iterator = MediaTypeEnum.iterator();
1✔
746

747
        while (iterator.hasNext()) {
1✔
748
            final MediaTypeEnum currentExportType = iterator.next();
1✔
749

750
            if (this.properties.getAddExport(currentExportType)) {
1✔
751

752
                if (buffer.length() > 0) {
1✔
753
                    buffer.append(this.properties.getExportBannerSeparator());
1✔
754
                }
755

756
                exportHref.addParameter(this.encodeParameter(TableTagParameters.PARAMETER_EXPORTTYPE, model),
1✔
757
                        currentExportType.getCode());
1✔
758

759
                // export marker
760
                exportHref.addParameter(TableTagParameters.PARAMETER_EXPORTING, "1");
1✔
761

762
                final String exportBannerItem = Objects.toString(this.properties.getExportBannerItem(),
1✔
763
                        "<a href=\"{0}\">{1}</a>");
764

765
                buffer.append(MessageFormat.format(exportBannerItem, exportHref,
1✔
766
                        this.properties.getExportLabel(currentExportType)));
1✔
767
            }
768
        }
1✔
769

770
        final Object[] exportOptions = { buffer.toString() };
1✔
771
        this.write(new MessageFormat(this.properties.getExportBanner(), this.properties.getLocale())
1✔
772
                .format(exportOptions));
1✔
773
    }
1✔
774

775
    /**
776
     * create the open tag containing all the attributes.
777
     *
778
     * @return open tag string: <code>%lt;table attribute="value" ... &gt;</code>
779
     */
780
    public String getOpenTag() {
781

782
        if (this.uid != null && this.attributeMap.get(TagConstants.ATTRIBUTE_ID) == null) {
1✔
783
            // we need to clone the attribute map in order to "fix" the html id when using only the "uid" attribute
784
            final Map<String, String> localAttributeMap = (Map<String, String>) this.attributeMap.clone();
1✔
785
            localAttributeMap.put(TagConstants.ATTRIBUTE_ID, this.uid);
1✔
786

787
            final StringBuilder buffer = new StringBuilder();
1✔
788
            buffer.append(TagConstants.TAG_OPEN).append(TagConstants.TABLE_TAG_NAME);
1✔
789
            buffer.append(localAttributeMap);
1✔
790
            buffer.append(TagConstants.TAG_CLOSE);
1✔
791

792
            return buffer.toString();
1✔
793

794
        }
795

796
        // fast, no clone
797
        final StringBuilder buffer = new StringBuilder();
1✔
798

799
        buffer.append(TagConstants.TAG_OPEN).append(TagConstants.TABLE_TAG_NAME);
1✔
800
        buffer.append(this.attributeMap);
1✔
801
        buffer.append(TagConstants.TAG_CLOSE);
1✔
802

803
        return buffer.toString();
1✔
804
    }
805

806
    /**
807
     * Utility method.
808
     *
809
     * @param string
810
     *            String
811
     */
812
    public void write(final String string) {
813
        if (string != null) {
1✔
814
            try {
815
                this.out.write(string);
1✔
816
            } catch (final IOException e) {
×
UNCOV
817
                throw new WrappedRuntimeException(this.getClass(), e);
×
818
            }
1✔
819
        }
820

821
    }
1✔
822

823
    /**
824
     * Utility method.
825
     *
826
     * @param string
827
     *            String
828
     */
829
    public void write(final Object string) {
830
        if (string != null) {
1!
831
            try {
832
                this.out.write(string.toString());
1✔
833
            } catch (final IOException e) {
×
UNCOV
834
                throw new WrappedRuntimeException(this.getClass(), e);
×
835
            }
1✔
836
        }
837

838
    }
1✔
839

840
}
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