Home | History | Annotate | Download | only in data
      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 	 */
     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 	 */
     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 	 */
     71 	public long getId() {
     72 		return id;
     73 	}
     74 
     75 	/**
     76 	 * The VM name of the class.
     77 	 *
     78 	 * @return VM name
     79 	 */
     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 	 */
     90 	public boolean[] getProbes() {
     91 		return probes;
     92 	}
     93 
     94 	/**
     95 	 * Sets all probes to <code>false</code>.
     96 	 */
     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 	 */
    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 	 */
    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 	 */
    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 	 */
    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
    202 	public String toString() {
    203 		return String.format("ExecutionData[name=%s, id=%016x]", name,
    204 				Long.valueOf(id));
    205 	}
    206 
    207 }
    208