• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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