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 static java.lang.String.format;
15 
16 import java.util.Arrays;
17 
18 /**
19  * Execution data for a single Java class. While instances are immutable care
20  * has to be taken about the probe data array of type <code>boolean[]</code>
21  * which can be modified.
22  */
23 public final class ExecutionData {
24 
25 	private final long id;
26 
27 	private final String name;
28 
29 	private final boolean[] probes;
30 
31 	/**
32 	 * Creates a new {@link ExecutionData} object with the given probe data.
33 	 *
34 	 * @param id
35 	 *            class identifier
36 	 * @param name
37 	 *            VM name
38 	 * @param probes
39 	 *            probe data
40 	 */
ExecutionData(final long id, final String name, final boolean[] probes)41 	public ExecutionData(final long id, final String name,
42 			final boolean[] probes) {
43 		this.id = id;
44 		this.name = name;
45 		this.probes = probes;
46 	}
47 
48 	/**
49 	 * Creates a new {@link ExecutionData} object with the given probe data
50 	 * length. All probes are set to <code>false</code>.
51 	 *
52 	 * @param id
53 	 *            class identifier
54 	 * @param name
55 	 *            VM name
56 	 * @param probeCount
57 	 *            probe count
58 	 */
ExecutionData(final long id, final String name, final int probeCount)59 	public ExecutionData(final long id, final String name, final int probeCount) {
60 		this.id = id;
61 		this.name = name;
62 		this.probes = new boolean[probeCount];
63 	}
64 
65 	/**
66 	 * Return the unique identifier for this class. The identifier is the CRC64
67 	 * checksum of the raw class file definition.
68 	 *
69 	 * @return class identifier
70 	 */
getId()71 	public long getId() {
72 		return id;
73 	}
74 
75 	/**
76 	 * The VM name of the class.
77 	 *
78 	 * @return VM name
79 	 */
getName()80 	public String getName() {
81 		return name;
82 	}
83 
84 	/**
85 	 * Returns the execution data probes. A value of <code>true</code> indicates
86 	 * that the corresponding probe was executed.
87 	 *
88 	 * @return probe data
89 	 */
getProbes()90 	public boolean[] getProbes() {
91 		return probes;
92 	}
93 
94 	/**
95 	 * Sets all probes to <code>false</code>.
96 	 */
reset()97 	public void reset() {
98 		Arrays.fill(probes, false);
99 	}
100 
101 	/**
102 	 * Checks whether any probe has been hit.
103 	 *
104 	 * @return <code>true</code>, if at least one probe has been hit
105 	 */
hasHits()106 	public boolean hasHits() {
107 		for (final boolean p : probes) {
108 			if (p) {
109 				return true;
110 			}
111 		}
112 		return false;
113 	}
114 
115 	/**
116 	 * Merges the given execution data into the probe data of this object. I.e.
117 	 * a probe entry in this object is marked as executed (<code>true</code>) if
118 	 * this probe or the corresponding other probe was executed. So the result
119 	 * is
120 	 *
121 	 * <pre>
122 	 * A or B
123 	 * </pre>
124 	 *
125 	 * The probe array of the other object is not modified.
126 	 *
127 	 * @param other
128 	 *            execution data to merge
129 	 */
merge(final ExecutionData other)130 	public void merge(final ExecutionData other) {
131 		merge(other, true);
132 	}
133 
134 	/**
135 	 * Merges the given execution data into the probe data of this object. A
136 	 * probe in this object is set to the value of <code>flag</code> if the
137 	 * corresponding other probe was executed. For <code>flag==true</code> this
138 	 * corresponds to
139 	 *
140 	 * <pre>
141 	 * A or B
142 	 * </pre>
143 	 *
144 	 * For <code>flag==false</code> this can be considered as a subtraction
145 	 *
146 	 * <pre>
147 	 * A and not B
148 	 * </pre>
149 	 *
150 	 * The probe array of the other object is not modified.
151 	 *
152 	 * @param other
153 	 *            execution data to merge
154 	 * @param flag
155 	 *            merge mode
156 	 */
merge(final ExecutionData other, final boolean flag)157 	public void merge(final ExecutionData other, final boolean flag) {
158 		assertCompatibility(other.getId(), other.getName(),
159 				other.getProbes().length);
160 		final boolean[] otherData = other.getProbes();
161 		for (int i = 0; i < probes.length; i++) {
162 			if (otherData[i]) {
163 				probes[i] = flag;
164 			}
165 		}
166 	}
167 
168 	/**
169 	 * Asserts that this execution data object is compatible with the given
170 	 * parameters. The purpose of this check is to detect a very unlikely class
171 	 * id collision.
172 	 *
173 	 * @param id
174 	 *            other class id, must be the same
175 	 * @param name
176 	 *            other name, must be equal to this name
177 	 * @param probecount
178 	 *            probe data length, must be the same as for this data
179 	 * @throws IllegalStateException
180 	 *             if the given parameters do not match this instance
181 	 */
assertCompatibility(final long id, final String name, final int probecount)182 	public void assertCompatibility(final long id, final String name,
183 			final int probecount) throws IllegalStateException {
184 		if (this.id != id) {
185 			throw new IllegalStateException(format(
186 					"Different ids (%016x and %016x).", Long.valueOf(this.id),
187 					Long.valueOf(id)));
188 		}
189 		if (!this.name.equals(name)) {
190 			throw new IllegalStateException(format(
191 					"Different class names %s and %s for id %016x.", this.name,
192 					name, Long.valueOf(id)));
193 		}
194 		if (this.probes.length != probecount) {
195 			throw new IllegalStateException(format(
196 					"Incompatible execution data for class %s with id %016x.",
197 					name, Long.valueOf(id)));
198 		}
199 	}
200 
201 	@Override
toString()202 	public String toString() {
203 		return String.format("ExecutionData[name=%s, id=%016x]", name,
204 				Long.valueOf(id));
205 	}
206 
207 }
208