ExecutionDataStore.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 * $Id: $
   12 *******************************************************************************/
   13package org.jacoco.core.data;
   14
   15import static java.lang.String.format;
   16
   17import java.util.Arrays;
   18import java.util.HashMap;
   19import java.util.Map;
   20
   21/**
   22 * In-memory data store for execution data. The data can be added through its
   23 * {@link IExecutionDataVisitor} interface. If execution data is provided
   24 * multiple times for the same class the data is merged, i.e. a block is marked
   25 * as executed if it is reported as executed at least once. This allows to merge
   26 * coverage date from multiple runs. A instance of this class is not thread
   27 * safe.
   28 * 
   29 * @author Marc R. Hoffmann
   30 * @version $Revision: $
   31 */
   32public class ExecutionDataStore implements IExecutionDataVisitor {
   33
   34    private final Map<Long, Entry> entries = new HashMap<Long, Entry>();
   35
   36    /**
   37     * Adds the given block data structure into the store. If there is already a
   38     * data structure for this class ID, this structure is merged with the given
   39     * one. In this case a {@link IllegalStateException} is thrown, if both
   40     * executions data structure do have different sizes or the name is
   41     * different.
   42     * 
   43     * @param classid
   44     *            unique class identifier
   45     * @param name
   46     *            VM name of the class
   47     * @param data
   48     *            execution data
   49     */
   50    public void put(final Long classid, final String name, final boolean[] data) {
   51        final Entry entry = entries.get(classid);
   52        if (entry == null) {
   53            entries.put(classid, new Entry(name, data));
   54        } else {
   55            entry.merge(classid, name, data);
   56        }
   57    }
   58
   59    /**
   60     * Adds the given block data structure into the store. If there is already a
   61     * data structure for this class ID, this structure is merged with the given
   62     * one. In this case a {@link IllegalStateException} is thrown, if both
   63     * executions data structure do have different sizes or the name is
   64     * different.
   65     * 
   66     * @param classid
   67     *            unique class identifier
   68     * @param name
   69     *            VM name of the class
   70     * @param data
   71     *            execution data
   72     */
   73    public void put(final long classid, final String name, final boolean[] data) {
   74        put(Long.valueOf(classid), name, data);
   75    }
   76
   77    /**
   78     * Returns the coverage data for the class with the given identifier if
   79     * available.
   80     * 
   81     * @param classid
   82     *            class identifier
   83     * @return coverage data or <code>null</code>
   84     */
   85    public boolean[] getData(final Long classid) {
   86        final Entry entry = entries.get(classid);
   87        return entry == null ? null : entry.data;
   88    }
   89
   90    /**
   91     * Returns the coverage data for the class with the given identifier if
   92     * available.
   93     * 
   94     * @param classid
   95     *            class identifier
   96     * @return coverage data or <code>null</code>
   97     */
   98    public boolean[] getData(final long classid) {
   99        return getData(Long.valueOf(classid));
  100    }
  101
  102    /**
  103     * Returns the coverage data for the class with the given identifier. If
  104     * there is no data available under the given id a new entry is created.
  105     * 
  106     * @param classid
  107     *            class identifier
  108     * @param name
  109     *            VM name of the class
  110     * @param probecount
  111     *            probe array length
  112     * @return execution data
  113     */
  114    public boolean[] getData(final Long classid, final String name,
  115            final int probecount) {
  116        Entry entry = entries.get(classid);
  117        if (entry == null) {
  118            entry = new Entry(name, new boolean[probecount]);
  119            entries.put(classid, entry);
  120        } else {
  121            entry.checkCompatibility(classid, name, probecount);
  122        }
  123        return entry.data;
  124    }
  125
  126    /**
  127     * Returns the vm name of the class with the given id.
  128     * 
  129     * @param classid
  130     *            class identifier
  131     * @return vm name or <code>null</code>
  132     */
  133    public String getName(final Long classid) {
  134        final Entry entry = entries.get(classid);
  135        return entry == null ? null : entry.name;
  136    }
  137
  138    /**
  139     * Returns the vm name of the class with the given id.
  140     * 
  141     * @param classid
  142     *            class identifier
  143     * @return vm name or <code>null</code>
  144     */
  145    public String getName(final long classid) {
  146        return getName(Long.valueOf(classid));
  147    }
  148
  149    /**
  150     * Resets all execution data structures, i.e. marks them as not executed.
  151     * The data structures itself are not deleted.
  152     */
  153    public void reset() {
  154        for (final Entry executionData : this.entries.values()) {
  155            Arrays.fill(executionData.data, false);
  156        }
  157    }
  158
  159    /**
  160     * Writes the content of the store to the given visitor interface.
  161     * 
  162     * @param visitor
  163     *            interface to write content to
  164     */
  165    public void accept(final IExecutionDataVisitor visitor) {
  166        for (final Map.Entry<Long, Entry> i : entries.entrySet()) {
  167            final long id = i.getKey().longValue();
  168            final Entry entry = i.getValue();
  169            visitor.visitClassExecution(id, entry.name, entry.data);
  170        }
  171    }
  172
  173    // === IExecutionDataVisitor ===
  174
  175    public void visitClassExecution(final long classid, final String name,
  176            final boolean[] data) {
  177        put(classid, name, data);
  178    }
  179
  180    private static class Entry {
  181
  182        final String name;
  183        final boolean[] data;
  184
  185        Entry(final String name, final boolean[] data) {
  186            this.name = name;
  187            this.data = data;
  188        }
  189
  190        void checkCompatibility(final Long classid, final String otherName,
  191                final int otherLength) {
  192            if (!otherName.equals(name)) {
  193                throw new IllegalStateException(format(
  194                        "Duplicate id %x for classes %s and %s.", classid,
  195                        name, otherName));
  196            }
  197            if (data.length != otherLength) {
  198                throw new IllegalStateException(format(
  199                        "Incompatible execution data for class %s (id %s).",
  200                        name, classid));
  201            }
  202        }
  203
  204        void merge(final Long classid, final String newName,
  205                final boolean[] newData) {
  206            checkCompatibility(classid, newName, newData.length);
  207            for (int i = 0; i < data.length; i++) {
  208                if (!data[i]) {
  209                    data[i] = newData[i];
  210                }
  211            }
  212        }
  213    }
  214
  215}