ExecutionDataStore.java
1/*******************************************************************************
2 * Copyright (c) 2009, 2010 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 * $Id: $
12 *******************************************************************************/
13package org.jacoco.core.data;
14
15import static java.lang.String.format;
16
17import java.util.Arrays;
18import java.util.HashMap;
19import java.util.Map;
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 block 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 * @author Marc R. Hoffmann
30 * @version $Revision: $
31 */
32public class ExecutionDataStore implements IExecutionDataVisitor {
33
34 private final Map<Long, Entry> entries = new HashMap<Long, Entry>();
35
36 /**
37 * Adds the given block data structure into the store. If there is already a
38 * data structure for this class ID, this structure is merged with the given
39 * one. In this case a {@link IllegalStateException} is thrown, if both
40 * executions data structure do have different sizes or the name is
41 * different.
42 *
43 * @param classid
44 * unique class identifier
45 * @param name
46 * VM name of the class
47 * @param data
48 * execution data
49 */
50 public void put(final Long classid, final String name, final boolean[] data) {
51 final Entry entry = entries.get(classid);
52 if (entry == null) {
53 entries.put(classid, new Entry(name, data));
54 } else {
55 entry.merge(classid, name, data);
56 }
57 }
58
59 /**
60 * Adds the given block data structure into the store. If there is already a
61 * data structure for this class ID, this structure is merged with the given
62 * one. In this case a {@link IllegalStateException} is thrown, if both
63 * executions data structure do have different sizes or the name is
64 * different.
65 *
66 * @param classid
67 * unique class identifier
68 * @param name
69 * VM name of the class
70 * @param data
71 * execution data
72 */
73 public void put(final long classid, final String name, final boolean[] data) {
74 put(Long.valueOf(classid), name, data);
75 }
76
77 /**
78 * Returns the coverage data for the class with the given identifier if
79 * available.
80 *
81 * @param classid
82 * class identifier
83 * @return coverage data or <code>null</code>
84 */
85 public boolean[] getData(final Long classid) {
86 final Entry entry = entries.get(classid);
87 return entry == null ? null : entry.data;
88 }
89
90 /**
91 * Returns the coverage data for the class with the given identifier if
92 * available.
93 *
94 * @param classid
95 * class identifier
96 * @return coverage data or <code>null</code>
97 */
98 public boolean[] getData(final long classid) {
99 return getData(Long.valueOf(classid));
100 }
101
102 /**
103 * Returns the coverage data for the class with the given identifier. If
104 * there is no data available under the given id a new entry is created.
105 *
106 * @param classid
107 * class identifier
108 * @param name
109 * VM name of the class
110 * @param probecount
111 * probe array length
112 * @return execution data
113 */
114 public boolean[] getData(final Long classid, final String name,
115 final int probecount) {
116 Entry entry = entries.get(classid);
117 if (entry == null) {
118 entry = new Entry(name, new boolean[probecount]);
119 entries.put(classid, entry);
120 } else {
121 entry.checkCompatibility(classid, name, probecount);
122 }
123 return entry.data;
124 }
125
126 /**
127 * Returns the vm name of the class with the given id.
128 *
129 * @param classid
130 * class identifier
131 * @return vm name or <code>null</code>
132 */
133 public String getName(final Long classid) {
134 final Entry entry = entries.get(classid);
135 return entry == null ? null : entry.name;
136 }
137
138 /**
139 * Returns the vm name of the class with the given id.
140 *
141 * @param classid
142 * class identifier
143 * @return vm name or <code>null</code>
144 */
145 public String getName(final long classid) {
146 return getName(Long.valueOf(classid));
147 }
148
149 /**
150 * Resets all execution data structures, i.e. marks them as not executed.
151 * The data structures itself are not deleted.
152 */
153 public void reset() {
154 for (final Entry executionData : this.entries.values()) {
155 Arrays.fill(executionData.data, false);
156 }
157 }
158
159 /**
160 * Writes the content of the store to the given visitor interface.
161 *
162 * @param visitor
163 * interface to write content to
164 */
165 public void accept(final IExecutionDataVisitor visitor) {
166 for (final Map.Entry<Long, Entry> i : entries.entrySet()) {
167 final long id = i.getKey().longValue();
168 final Entry entry = i.getValue();
169 visitor.visitClassExecution(id, entry.name, entry.data);
170 }
171 }
172
173 // === IExecutionDataVisitor ===
174
175 public void visitClassExecution(final long classid, final String name,
176 final boolean[] data) {
177 put(classid, name, data);
178 }
179
180 private static class Entry {
181
182 final String name;
183 final boolean[] data;
184
185 Entry(final String name, final boolean[] data) {
186 this.name = name;
187 this.data = data;
188 }
189
190 void checkCompatibility(final Long classid, final String otherName,
191 final int otherLength) {
192 if (!otherName.equals(name)) {
193 throw new IllegalStateException(format(
194 "Duplicate id %x for classes %s and %s.", classid,
195 name, otherName));
196 }
197 if (data.length != otherLength) {
198 throw new IllegalStateException(format(
199 "Incompatible execution data for class %s (id %s).",
200 name, classid));
201 }
202 }
203
204 void merge(final Long classid, final String newName,
205 final boolean[] newData) {
206 checkCompatibility(classid, newName, newData.length);
207 for (int i = 0; i < data.length; i++) {
208 if (!data[i]) {
209 data[i] = newData[i];
210 }
211 }
212 }
213 }
214
215}