ExecutionDataStore.java
1/*******************************************************************************
2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others
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. This class is not thread safe.
27 *
28 * @author Marc R. Hoffmann
29 * @version $Revision: $
30 */
31public class ExecutionDataStore implements IExecutionDataVisitor {
32
33 private final Map<Long, boolean[][]> data = new HashMap<Long, boolean[][]>();
34
35 private final Map<Long, String> names = new HashMap<Long, String>();
36
37 /**
38 * Adds the given block data structure into the store. If there is already a
39 * data structure for this class ID, this structure is merged with the given
40 * one. In this case a {@link IllegalStateException} is thrown, if both
41 * executions data structure do have different bloc sizes.
42 *
43 * @param classid
44 * unique class identifier
45 * @param name
46 * VM name of the class
47 * @param blockdata
48 * execution data
49 */
50 public void put(final Long classid, final String name, boolean[][] blockdata) {
51 final boolean[][] current = data.get(classid);
52 if (current != null) {
53 checkName(classid, name);
54 merge(current, blockdata);
55 blockdata = current;
56 }
57 names.put(classid, name);
58 data.put(classid, blockdata);
59 }
60
61 /**
62 * Adds the given block data structure into the store. If there is already a
63 * data structure for this class ID, this structure is merged with the given
64 * one. In this case a {@link IllegalStateException} is thrown, if both
65 * executions data structure do have different bloc sizes.
66 *
67 * @param classid
68 * unique class identifier
69 * @param name
70 * VM name of the class
71 * @param blockdata
72 * execution data
73 */
74 public void put(final long classid, final String name,
75 final boolean[][] blockdata) {
76 put(Long.valueOf(classid), name, blockdata);
77 }
78
79 private void checkName(final Long classid, final String name) {
80 final String oldName = names.get(classid);
81 if (!name.equals(oldName)) {
82 throw new IllegalArgumentException(format(
83 "Duplicate id %x for classes %s and %s.", classid, oldName,
84 name));
85 }
86 }
87
88 private static void merge(final boolean[][] target, final boolean[][] data) {
89 if (target.length != data.length) {
90 throw new IllegalStateException("Incompatible execution data.");
91 }
92 for (int i = 0; i < target.length; i++) {
93 merge(target[i], data[i]);
94 }
95 }
96
97 private static void merge(final boolean[] target, final boolean[] data) {
98 if (target.length != data.length) {
99 throw new IllegalStateException("Incompatible execution data.");
100 }
101 for (int i = 0; i < target.length; i++) {
102 if (!target[i]) {
103 target[i] = data[i];
104 }
105 }
106 }
107
108 /**
109 * Returns the coverage data for the class with the given identifier if
110 * available.
111 *
112 * @param classid
113 * class identifier
114 * @return coverage data or <code>null</code>
115 */
116 public boolean[][] getData(final long classid) {
117 return getData(Long.valueOf(classid));
118 }
119
120 /**
121 * Returns the coverage data for the class with the given identifier if
122 * available.
123 *
124 * @param classid
125 * class identifier
126 * @return coverage data or <code>null</code>
127 */
128 public boolean[][] getData(final Long classid) {
129 return data.get(classid);
130 }
131
132 /**
133 * Returns the vm name of the class with the given id.
134 *
135 * @param classid
136 * class identifier
137 * @return vm name or <code>null</code>
138 */
139 public String getName(final long classid) {
140 return getName(Long.valueOf(classid));
141 }
142
143 /**
144 * Returns the vm name of the class with the given id.
145 *
146 * @param classid
147 * class identifier
148 * @return vm name or <code>null</code>
149 */
150 public String getName(final Long classid) {
151 return names.get(classid);
152 }
153
154 /**
155 * Resets all execution data structures, i.e. marks them as not executed.
156 * The data structures itself are not deleted.
157 */
158 public void reset() {
159 for (final boolean[][] struct : data.values()) {
160 for (final boolean[] arr : struct) {
161 Arrays.fill(arr, false);
162 }
163 }
164 }
165
166 /**
167 * Writes the content of the store to the given visitor interface.
168 *
169 * @param visitor
170 * interface to write content to
171 */
172 public void accept(final IExecutionDataVisitor visitor) {
173 for (final Map.Entry<Long, boolean[][]> entry : data.entrySet()) {
174 final Long key = entry.getKey();
175 final long id = key.longValue();
176 visitor.visitClassExecution(id, names.get(key), entry.getValue());
177 }
178 }
179
180 // === IExecutionDataVisitor ===
181
182 public void visitClassExecution(final long classid, final String name,
183 final boolean[][] blockdata) {
184 put(classid, name, blockdata);
185 }
186
187}