• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors
3  * This program and the accompanying materials are made available under
4  * the terms of the Eclipse Public License 2.0 which is available at
5  * http://www.eclipse.org/legal/epl-2.0
6  *
7  * SPDX-License-Identifier: EPL-2.0
8  *
9  * Contributors:
10  *    Marc R. Hoffmann - initial API and implementation
11  *
12  *******************************************************************************/
13 package org.jacoco.report.internal.html.table;
14 
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.List;
20 
21 import org.jacoco.core.analysis.ICoverageNode;
22 import org.jacoco.report.internal.ReportOutputFolder;
23 import org.jacoco.report.internal.html.HTMLElement;
24 import org.jacoco.report.internal.html.resources.Resources;
25 import org.jacoco.report.internal.html.resources.Styles;
26 
27 /**
28  * Renderer for a table of {@link ITableItem}s.
29  */
30 public class Table {
31 
32 	private final List<Column> columns;
33 
34 	private Comparator<ITableItem> defaultComparator;
35 
36 	/**
37 	 * Create a new table without any columns yet.
38 	 */
Table()39 	public Table() {
40 		this.columns = new ArrayList<Table.Column>();
41 	}
42 
43 	/**
44 	 * Adds a new column with the given properties to the table.
45 	 *
46 	 * @param header
47 	 *            column header caption
48 	 * @param style
49 	 *            optional CSS style class name for the td-Elements of this
50 	 *            column
51 	 * @param renderer
52 	 *            callback for column rendering
53 	 * @param defaultSorting
54 	 *            If <code>true</code>, this column is the default sorting
55 	 *            column. Only one column can be selected for default sorting.
56 	 *
57 	 */
add(final String header, final String style, final IColumnRenderer renderer, final boolean defaultSorting)58 	public void add(final String header, final String style,
59 			final IColumnRenderer renderer, final boolean defaultSorting) {
60 		columns.add(new Column(columns.size(), header, style, renderer,
61 				defaultSorting));
62 		if (defaultSorting) {
63 			if (defaultComparator != null) {
64 				throw new IllegalStateException(
65 						"Default sorting only allowed for one column.");
66 			}
67 			this.defaultComparator = renderer.getComparator();
68 		}
69 	}
70 
71 	/**
72 	 * Renders a table for the given icon
73 	 *
74 	 * @param parent
75 	 *            parent element in which the table is created
76 	 * @param items
77 	 *            items that will make the table rows
78 	 * @param total
79 	 *            the summary of all coverage data items in the table static
80 	 *            resources that might be referenced
81 	 * @param resources
82 	 *            static resources that might be referenced
83 	 * @param base
84 	 *            base folder of the table
85 	 * @throws IOException
86 	 *             in case of IO problems with the element output
87 	 */
render(final HTMLElement parent, final List<? extends ITableItem> items, final ICoverageNode total, final Resources resources, final ReportOutputFolder base)88 	public void render(final HTMLElement parent,
89 			final List<? extends ITableItem> items, final ICoverageNode total,
90 			final Resources resources, final ReportOutputFolder base)
91 			throws IOException {
92 		final List<? extends ITableItem> sortedItems = sort(items);
93 		final HTMLElement table = parent.table(Styles.COVERAGETABLE);
94 		table.attr("id", "coveragetable");
95 		header(table, sortedItems, total);
96 		footer(table, total, resources, base);
97 		body(table, sortedItems, resources, base);
98 	}
99 
header(final HTMLElement table, final List<? extends ITableItem> items, final ICoverageNode total)100 	private void header(final HTMLElement table,
101 			final List<? extends ITableItem> items, final ICoverageNode total)
102 			throws IOException {
103 		final HTMLElement tr = table.thead().tr();
104 		for (final Column c : columns) {
105 			c.init(tr, items, total);
106 		}
107 	}
108 
footer(final HTMLElement table, final ICoverageNode total, final Resources resources, final ReportOutputFolder base)109 	private void footer(final HTMLElement table, final ICoverageNode total,
110 			final Resources resources, final ReportOutputFolder base)
111 			throws IOException {
112 		final HTMLElement tr = table.tfoot().tr();
113 		for (final Column c : columns) {
114 			c.footer(tr, total, resources, base);
115 		}
116 	}
117 
body(final HTMLElement table, final List<? extends ITableItem> items, final Resources resources, final ReportOutputFolder base)118 	private void body(final HTMLElement table,
119 			final List<? extends ITableItem> items, final Resources resources,
120 			final ReportOutputFolder base) throws IOException {
121 		final HTMLElement tbody = table.tbody();
122 		int idx = 0;
123 		for (final ITableItem item : items) {
124 			final HTMLElement tr = tbody.tr();
125 			for (final Column c : columns) {
126 				c.body(tr, idx, item, resources, base);
127 			}
128 			idx++;
129 		}
130 	}
131 
sort( final List<? extends ITableItem> items)132 	private List<? extends ITableItem> sort(
133 			final List<? extends ITableItem> items) {
134 		if (defaultComparator != null) {
135 			final List<ITableItem> result = new ArrayList<ITableItem>(items);
136 			Collections.sort(result, defaultComparator);
137 			return result;
138 		}
139 		return items;
140 	}
141 
142 	private static class Column {
143 
144 		private final char idprefix;
145 		private final String header;
146 		private final IColumnRenderer renderer;
147 		private final SortIndex<ITableItem> index;
148 		private final String style, headerStyle;
149 
150 		private boolean visible;
151 
Column(final int idx, final String header, final String style, final IColumnRenderer renderer, final boolean defaultSorting)152 		Column(final int idx, final String header, final String style,
153 				final IColumnRenderer renderer, final boolean defaultSorting) {
154 			this.idprefix = (char) ('a' + idx);
155 			this.header = header;
156 			this.renderer = renderer;
157 			index = new SortIndex<ITableItem>(renderer.getComparator());
158 			this.style = style;
159 			this.headerStyle = Styles.combine(
160 					defaultSorting ? Styles.DOWN : null, Styles.SORTABLE,
161 					style);
162 		}
163 
init(final HTMLElement tr, final List<? extends ITableItem> items, final ICoverageNode total)164 		void init(final HTMLElement tr, final List<? extends ITableItem> items,
165 				final ICoverageNode total) throws IOException {
166 			visible = renderer.init(items, total);
167 			if (visible) {
168 				index.init(items);
169 				final HTMLElement td = tr.td(headerStyle);
170 				td.attr("id", String.valueOf(idprefix));
171 				td.attr("onclick", "toggleSort(this)");
172 				td.text(header);
173 			}
174 		}
175 
footer(final HTMLElement tr, final ICoverageNode total, final Resources resources, final ReportOutputFolder base)176 		void footer(final HTMLElement tr, final ICoverageNode total,
177 				final Resources resources, final ReportOutputFolder base)
178 				throws IOException {
179 			if (visible) {
180 				renderer.footer(tr.td(style), total, resources, base);
181 			}
182 		}
183 
body(final HTMLElement tr, final int idx, final ITableItem item, final Resources resources, final ReportOutputFolder base)184 		void body(final HTMLElement tr, final int idx, final ITableItem item,
185 				final Resources resources, final ReportOutputFolder base)
186 				throws IOException {
187 			if (visible) {
188 				final HTMLElement td = tr.td(style);
189 				td.attr("id",
190 						idprefix + String.valueOf(index.getPosition(idx)));
191 				renderer.item(td, item, resources, base);
192 			}
193 		}
194 
195 	}
196 
197 }
198