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