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