/******************************************************************************* * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marc R. Hoffmann - initial API and implementation * *******************************************************************************/ package org.jacoco.report.internal.html.table; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.jacoco.core.analysis.ICoverageNode; import org.jacoco.report.internal.ReportOutputFolder; import org.jacoco.report.internal.html.HTMLElement; import org.jacoco.report.internal.html.resources.Resources; import org.jacoco.report.internal.html.resources.Styles; /** * Renderer for a table of {@link ITableItem}s. */ public class Table { private final List<Column> columns; private Comparator<ITableItem> defaultComparator; /** * Create a new table without any columns yet. */ public Table() { this.columns = new ArrayList<Table.Column>(); } /** * Adds a new column with the given properties to the table. * * @param header * column header caption * @param style * optional CSS style class name for the td-Elements of this * column * @param renderer * callback for column rendering * @param defaultSorting * If <code>true</code>, this column is the default sorting * column. Only one column can be selected for default sorting. * */ public void add(final String header, final String style, final IColumnRenderer renderer, final boolean defaultSorting) { columns.add(new Column(columns.size(), header, style, renderer, defaultSorting)); if (defaultSorting) { if (defaultComparator != null) { throw new IllegalStateException( "Default sorting only allowed for one column."); } this.defaultComparator = renderer.getComparator(); } } /** * Renders a table for the given icon * * @param parent * parent element in which the table is created * @param items * items that will make the table rows * @param total * the summary of all coverage data items in the table static * resources that might be referenced * @param resources * static resources that might be referenced * @param base * base folder of the table * @throws IOException * in case of IO problems with the element output */ public void render(final HTMLElement parent, final List<? extends ITableItem> items, final ICoverageNode total, final Resources resources, final ReportOutputFolder base) throws IOException { final List<? extends ITableItem> sortedItems = sort(items); final HTMLElement table = parent.table(Styles.COVERAGETABLE); table.attr("id", "coveragetable"); header(table, sortedItems, total); footer(table, total, resources, base); body(table, sortedItems, resources, base); } private void header(final HTMLElement table, final List<? extends ITableItem> items, final ICoverageNode total) throws IOException { final HTMLElement tr = table.thead().tr(); for (final Column c : columns) { c.init(tr, items, total); } } private void footer(final HTMLElement table, final ICoverageNode total, final Resources resources, final ReportOutputFolder base) throws IOException { final HTMLElement tr = table.tfoot().tr(); for (final Column c : columns) { c.footer(tr, total, resources, base); } } private void body(final HTMLElement table, final List<? extends ITableItem> items, final Resources resources, final ReportOutputFolder base) throws IOException { final HTMLElement tbody = table.tbody(); int idx = 0; for (final ITableItem item : items) { final HTMLElement tr = tbody.tr(); for (final Column c : columns) { c.body(tr, idx, item, resources, base); } idx++; } } private List<? extends ITableItem> sort( final List<? extends ITableItem> items) { if (defaultComparator != null) { final List<ITableItem> result = new ArrayList<ITableItem>(items); Collections.sort(result, defaultComparator); return result; } return items; } private static class Column { private final char idprefix; private final String header; private final IColumnRenderer renderer; private final SortIndex<ITableItem> index; private final String style, headerStyle; private boolean visible; Column(final int idx, final String header, final String style, final IColumnRenderer renderer, final boolean defaultSorting) { this.idprefix = (char) ('a' + idx); this.header = header; this.renderer = renderer; index = new SortIndex<ITableItem>(renderer.getComparator()); this.style = style; this.headerStyle = Styles.combine(defaultSorting ? Styles.DOWN : null, Styles.SORTABLE, style); } void init(final HTMLElement tr, final List<? extends ITableItem> items, final ICoverageNode total) throws IOException { visible = renderer.init(items, total); if (visible) { index.init(items); final HTMLElement td = tr.td(headerStyle); td.attr("id", String.valueOf(idprefix)); td.attr("onclick", "toggleSort(this)"); td.text(header); } } void footer(final HTMLElement tr, final ICoverageNode total, final Resources resources, final ReportOutputFolder base) throws IOException { if (visible) { renderer.footer(tr.td(style), total, resources, base); } } void body(final HTMLElement tr, final int idx, final ITableItem item, final Resources resources, final ReportOutputFolder base) throws IOException { if (visible) { final HTMLElement td = tr.td(style); td.attr("id", idprefix + String.valueOf(index.getPosition(idx))); renderer.item(td, item, resources, base); } } } }