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