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 	 */
put(final ExecutionData data)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 	 */
subtract(final ExecutionData data)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 	 */
subtract(final ExecutionDataStore store)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 	 */
get(final long id)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 	 */
contains(final String name)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 	 */
get(final Long id, final String name, final int probecount)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 	 */
reset()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 	 */
getContents()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 	 */
accept(final IExecutionDataVisitor visitor)167 	public void accept(final IExecutionDataVisitor visitor) {
168 		for (final ExecutionData data : getContents()) {
169 			visitor.visitClassExecution(data);
170 		}
171 	}
172 
173 	// === IExecutionDataVisitor ===
174 
visitClassExecution(final ExecutionData data)175 	public void visitClassExecution(final ExecutionData data) {
176 		put(data);
177 	}
178 }
179