Home | History | Annotate | Download | only in data
      1 /*******************************************************************************
      2  * Copyright (c) 2009, 2018 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  *******************************************************************************/
     12 package org.jacoco.core.data;
     13 
     14 import java.util.ArrayList;
     15 import java.util.Collection;
     16 import java.util.HashMap;
     17 import java.util.HashSet;
     18 import java.util.Map;
     19 import java.util.Set;
     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 probe 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 public final class ExecutionDataStore implements IExecutionDataVisitor {
     30 
     31 	private final Map<Long, ExecutionData> entries = new HashMap<Long, ExecutionData>();
     32 
     33 	private final Set<String> names = new HashSet<String>();
     34 
     35 	/**
     36 	 * Adds the given {@link ExecutionData} object into the store. If there is
     37 	 * already execution data with this same class id, this structure is merged
     38 	 * with the given one.
     39 	 *
     40 	 * @param data
     41 	 *            execution data to add or merge
     42 	 * @throws IllegalStateException
     43 	 *             if the given {@link ExecutionData} object is not compatible
     44 	 *             to a corresponding one, that is already contained
     45 	 * @see ExecutionData#assertCompatibility(long, String, int)
     46 	 */
     47 	public void put(final ExecutionData data) throws IllegalStateException {
     48 		final Long id = Long.valueOf(data.getId());
     49 		final ExecutionData entry = entries.get(id);
     50 		if (entry == null) {
     51 			entries.put(id, data);
     52 			names.add(data.getName());
     53 		} else {
     54 			entry.merge(data);
     55 		}
     56 	}
     57 
     58 	/**
     59 	 * Subtracts the probes in the given {@link ExecutionData} object from the
     60 	 * store. I.e. for all set probes in the given data object the corresponding
     61 	 * probes in this store will be unset. If there is no execution data with id
     62 	 * of the given data object this operation will have no effect.
     63 	 *
     64 	 * @param data
     65 	 *            execution data to subtract
     66 	 * @throws IllegalStateException
     67 	 *             if the given {@link ExecutionData} object is not compatible
     68 	 *             to a corresponding one, that is already contained
     69 	 * @see ExecutionData#assertCompatibility(long, String, int)
     70 	 */
     71 	public void subtract(final ExecutionData data) throws IllegalStateException {
     72 		final Long id = Long.valueOf(data.getId());
     73 		final ExecutionData entry = entries.get(id);
     74 		if (entry != null) {
     75 			entry.merge(data, false);
     76 		}
     77 	}
     78 
     79 	/**
     80 	 * Subtracts all probes in the given execution data store from this store.
     81 	 *
     82 	 * @param store
     83 	 *            execution data store to subtract
     84 	 * @see #subtract(ExecutionData)
     85 	 */
     86 	public void subtract(final ExecutionDataStore store) {
     87 		for (final ExecutionData data : store.getContents()) {
     88 			subtract(data);
     89 		}
     90 	}
     91 
     92 	/**
     93 	 * Returns the {@link ExecutionData} entry with the given id if it exists in
     94 	 * this store.
     95 	 *
     96 	 * @param id
     97 	 *            class id
     98 	 * @return execution data or <code>null</code>
     99 	 */
    100 	public ExecutionData get(final long id) {
    101 		return entries.get(Long.valueOf(id));
    102 	}
    103 
    104 	/**
    105 	 * Checks whether execution data for classes with the given name are
    106 	 * contained in the store.
    107 	 *
    108 	 * @param name
    109 	 *            VM name
    110 	 * @return <code>true</code> if at least one class with the name is
    111 	 *         contained.
    112 	 */
    113 	public boolean contains(final String name) {
    114 		return names.contains(name);
    115 	}
    116 
    117 	/**
    118 	 * Returns the coverage data for the class with the given identifier. If
    119 	 * there is no data available under the given id a new entry is created.
    120 	 *
    121 	 * @param id
    122 	 *            class identifier
    123 	 * @param name
    124 	 *            VM name of the class
    125 	 * @param probecount
    126 	 *            probe data length
    127 	 * @return execution data
    128 	 */
    129 	public ExecutionData get(final Long id, final String name,
    130 			final int probecount) {
    131 		ExecutionData entry = entries.get(id);
    132 		if (entry == null) {
    133 			entry = new ExecutionData(id.longValue(), name, probecount);
    134 			entries.put(id, entry);
    135 			names.add(name);
    136 		} else {
    137 			entry.assertCompatibility(id.longValue(), name, probecount);
    138 		}
    139 		return entry;
    140 	}
    141 
    142 	/**
    143 	 * Resets all execution data probes, i.e. marks them as not executed. The
    144 	 * execution data objects itself are not removed.
    145 	 */
    146 	public void reset() {
    147 		for (final ExecutionData executionData : this.entries.values()) {
    148 			executionData.reset();
    149 		}
    150 	}
    151 
    152 	/**
    153 	 * Returns a collection that represents current contents of the store.
    154 	 *
    155 	 * @return current contents
    156 	 */
    157 	public Collection<ExecutionData> getContents() {
    158 		return new ArrayList<ExecutionData>(entries.values());
    159 	}
    160 
    161 	/**
    162 	 * Writes the content of the store to the given visitor interface.
    163 	 *
    164 	 * @param visitor
    165 	 *            interface to write content to
    166 	 */
    167 	public void accept(final IExecutionDataVisitor visitor) {
    168 		for (final ExecutionData data : getContents()) {
    169 			visitor.visitClassExecution(data);
    170 		}
    171 	}
    172 
    173 	// === IExecutionDataVisitor ===
    174 
    175 	public void visitClassExecution(final ExecutionData data) {
    176 		put(data);
    177 	}
    178 }
    179