Table.java

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