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