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