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