ExecutionDataStore.java

    1/*******************************************************************************
    2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
    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. This class is not thread safe.
   27 * 
   28 * @author Marc R. Hoffmann
   29 * @version $Revision: $
   30 */
   31public class ExecutionDataStore implements IExecutionDataVisitor {
   32
   33    private final Map<Long, boolean[][]> data = new HashMap<Long, boolean[][]>();
   34
   35    private final Map<Long, String> names = new HashMap<Long, String>();
   36
   37    /**
   38     * Adds the given block data structure into the store. If there is already a
   39     * data structure for this class ID, this structure is merged with the given
   40     * one. In this case a {@link IllegalStateException} is thrown, if both
   41     * executions data structure do have different bloc sizes.
   42     * 
   43     * @param classid
   44     *            unique class identifier
   45     * @param name
   46     *            VM name of the class
   47     * @param blockdata
   48     *            execution data
   49     */
   50    public void put(final Long classid, final String name, boolean[][] blockdata) {
   51        final boolean[][] current = data.get(classid);
   52        if (current != null) {
   53            checkName(classid, name);
   54            merge(current, blockdata);
   55            blockdata = current;
   56        }
   57        names.put(classid, name);
   58        data.put(classid, blockdata);
   59    }
   60
   61    /**
   62     * Adds the given block data structure into the store. If there is already a
   63     * data structure for this class ID, this structure is merged with the given
   64     * one. In this case a {@link IllegalStateException} is thrown, if both
   65     * executions data structure do have different bloc sizes.
   66     * 
   67     * @param classid
   68     *            unique class identifier
   69     * @param name
   70     *            VM name of the class
   71     * @param blockdata
   72     *            execution data
   73     */
   74    public void put(final long classid, final String name,
   75            final boolean[][] blockdata) {
   76        put(Long.valueOf(classid), name, blockdata);
   77    }
   78
   79    private void checkName(final Long classid, final String name) {
   80        final String oldName = names.get(classid);
   81        if (!name.equals(oldName)) {
   82            throw new IllegalArgumentException(format(
   83                    "Duplicate id %x for classes %s and %s.", classid, oldName,
   84                    name));
   85        }
   86    }
   87
   88    private static void merge(final boolean[][] target, final boolean[][] data) {
   89        if (target.length != data.length) {
   90            throw new IllegalStateException("Incompatible execution data.");
   91        }
   92        for (int i = 0; i < target.length; i++) {
   93            merge(target[i], data[i]);
   94        }
   95    }
   96
   97    private static void merge(final boolean[] target, final boolean[] data) {
   98        if (target.length != data.length) {
   99            throw new IllegalStateException("Incompatible execution data.");
  100        }
  101        for (int i = 0; i < target.length; i++) {
  102            if (!target[i]) {
  103                target[i] = data[i];
  104            }
  105        }
  106    }
  107
  108    /**
  109     * Returns the coverage data for the class with the given identifier if
  110     * available.
  111     * 
  112     * @param classid
  113     *            class identifier
  114     * @return coverage data or <code>null</code>
  115     */
  116    public boolean[][] getData(final long classid) {
  117        return getData(Long.valueOf(classid));
  118    }
  119
  120    /**
  121     * Returns the coverage data for the class with the given identifier if
  122     * available.
  123     * 
  124     * @param classid
  125     *            class identifier
  126     * @return coverage data or <code>null</code>
  127     */
  128    public boolean[][] getData(final Long classid) {
  129        return data.get(classid);
  130    }
  131
  132    /**
  133     * Returns the vm name of the class with the given id.
  134     * 
  135     * @param classid
  136     *            class identifier
  137     * @return vm name or <code>null</code>
  138     */
  139    public String getName(final long classid) {
  140        return getName(Long.valueOf(classid));
  141    }
  142
  143    /**
  144     * Returns the vm name of the class with the given id.
  145     * 
  146     * @param classid
  147     *            class identifier
  148     * @return vm name or <code>null</code>
  149     */
  150    public String getName(final Long classid) {
  151        return names.get(classid);
  152    }
  153
  154    /**
  155     * Resets all execution data structures, i.e. marks them as not executed.
  156     * The data structures itself are not deleted.
  157     */
  158    public void reset() {
  159        for (final boolean[][] struct : data.values()) {
  160            for (final boolean[] arr : struct) {
  161                Arrays.fill(arr, false);
  162            }
  163        }
  164    }
  165
  166    /**
  167     * Writes the content of the store to the given visitor interface.
  168     * 
  169     * @param visitor
  170     *            interface to write content to
  171     */
  172    public void accept(final IExecutionDataVisitor visitor) {
  173        for (final Map.Entry<Long, boolean[][]> entry : data.entrySet()) {
  174            final Long key = entry.getKey();
  175            final long id = key.longValue();
  176            visitor.visitClassExecution(id, names.get(key), entry.getValue());
  177        }
  178    }
  179
  180    // === IExecutionDataVisitor ===
  181
  182    public void visitClassExecution(final long classid, final String name,
  183            final boolean[][] blockdata) {
  184        put(classid, name, blockdata);
  185    }
  186
  187}