1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Marc R. Hoffmann - initial API and implementation 11 * 12 *******************************************************************************/ 13 package org.jacoco.core.internal.analysis; 14 15 import org.jacoco.core.analysis.ICounter; 16 17 /** 18 * {@link ICounter} implementations. Implementing a factory pattern allows to 19 * share counter instances. 20 */ 21 public abstract class CounterImpl implements ICounter { 22 23 /** Max counter value for which singletons are created */ 24 private static final int SINGLETON_LIMIT = 30; 25 26 private static final CounterImpl[][] SINGLETONS = new CounterImpl[SINGLETON_LIMIT 27 + 1][]; 28 29 static { 30 for (int i = 0; i <= SINGLETON_LIMIT; i++) { 31 SINGLETONS[i] = new CounterImpl[SINGLETON_LIMIT + 1]; 32 for (int j = 0; j <= SINGLETON_LIMIT; j++) { 33 SINGLETONS[i][j] = new Fix(i, j); 34 } 35 } 36 } 37 38 /** Constant for Counter with 0/0 values. */ 39 public static final CounterImpl COUNTER_0_0 = SINGLETONS[0][0]; 40 41 /** Constant for Counter with 1/0 values. */ 42 public static final CounterImpl COUNTER_1_0 = SINGLETONS[1][0]; 43 44 /** Constant for Counter with 0/1 values. */ 45 public static final CounterImpl COUNTER_0_1 = SINGLETONS[0][1]; 46 47 /** 48 * Mutable version of the counter. 49 */ 50 private static class Var extends CounterImpl { Var(final int missed, final int covered)51 public Var(final int missed, final int covered) { 52 super(missed, covered); 53 } 54 55 @Override increment(final int missed, final int covered)56 public CounterImpl increment(final int missed, final int covered) { 57 this.missed += missed; 58 this.covered += covered; 59 return this; 60 } 61 } 62 63 /** 64 * Immutable version of the counter. 65 */ 66 private static class Fix extends CounterImpl { Fix(final int missed, final int covered)67 public Fix(final int missed, final int covered) { 68 super(missed, covered); 69 } 70 71 @Override increment(final int missed, final int covered)72 public CounterImpl increment(final int missed, final int covered) { 73 return getInstance(this.missed + missed, this.covered + covered); 74 } 75 } 76 77 /** 78 * Factory method to retrieve a counter with the given number of items. 79 * 80 * @param missed 81 * number of missed items 82 * @param covered 83 * number of covered items 84 * @return counter instance 85 */ getInstance(final int missed, final int covered)86 public static CounterImpl getInstance(final int missed, final int covered) { 87 if (missed <= SINGLETON_LIMIT && covered <= SINGLETON_LIMIT) { 88 return SINGLETONS[missed][covered]; 89 } else { 90 return new Var(missed, covered); 91 } 92 } 93 94 /** 95 * Factory method to retrieve a clone of the given counter. 96 * 97 * @param counter 98 * counter to copy 99 * @return counter instance 100 */ getInstance(final ICounter counter)101 public static CounterImpl getInstance(final ICounter counter) { 102 return getInstance(counter.getMissedCount(), counter.getCoveredCount()); 103 } 104 105 /** number of missed items */ 106 protected int missed; 107 108 /** number of covered items */ 109 protected int covered; 110 111 /** 112 * Creates a new instance with the given numbers. 113 * 114 * @param missed 115 * number of missed items 116 * @param covered 117 * number of covered items 118 */ CounterImpl(final int missed, final int covered)119 protected CounterImpl(final int missed, final int covered) { 120 this.missed = missed; 121 this.covered = covered; 122 } 123 124 /** 125 * Returns a counter with values incremented by the numbers of the given 126 * counter. It is up to the implementation whether this counter instance is 127 * modified or a new instance is returned. 128 * 129 * @param counter 130 * number of additional total and covered items 131 * @return counter instance with incremented values 132 */ increment(final ICounter counter)133 public CounterImpl increment(final ICounter counter) { 134 return increment(counter.getMissedCount(), counter.getCoveredCount()); 135 } 136 137 /** 138 * Returns a counter with values incremented by the given numbers. It is up 139 * to the implementation whether this counter instance is modified or a new 140 * instance is returned. 141 * 142 * @param missed 143 * number of missed items 144 * @param covered 145 * number of covered items 146 * @return counter instance with incremented values 147 */ increment(int missed, int covered)148 public abstract CounterImpl increment(int missed, int covered); 149 150 // === ICounter implementation === 151 getValue(final CounterValue value)152 public double getValue(final CounterValue value) { 153 switch (value) { 154 case TOTALCOUNT: 155 return getTotalCount(); 156 case MISSEDCOUNT: 157 return getMissedCount(); 158 case COVEREDCOUNT: 159 return getCoveredCount(); 160 case MISSEDRATIO: 161 return getMissedRatio(); 162 case COVEREDRATIO: 163 return getCoveredRatio(); 164 default: 165 throw new AssertionError(value); 166 } 167 } 168 getTotalCount()169 public int getTotalCount() { 170 return missed + covered; 171 } 172 getCoveredCount()173 public int getCoveredCount() { 174 return covered; 175 } 176 getMissedCount()177 public int getMissedCount() { 178 return missed; 179 } 180 getCoveredRatio()181 public double getCoveredRatio() { 182 return (double) covered / (missed + covered); 183 } 184 getMissedRatio()185 public double getMissedRatio() { 186 return (double) missed / (missed + covered); 187 } 188 getStatus()189 public int getStatus() { 190 int status = covered > 0 ? FULLY_COVERED : EMPTY; 191 if (missed > 0) { 192 status |= NOT_COVERED; 193 } 194 return status; 195 } 196 197 @Override equals(final Object obj)198 public boolean equals(final Object obj) { 199 if (obj instanceof ICounter) { 200 final ICounter that = (ICounter) obj; 201 return this.missed == that.getMissedCount() 202 && this.covered == that.getCoveredCount(); 203 } else { 204 return false; 205 } 206 } 207 208 @Override hashCode()209 public int hashCode() { 210 return missed ^ covered * 17; 211 } 212 213 @Override toString()214 public String toString() { 215 final StringBuilder b = new StringBuilder("Counter["); //$NON-NLS-1$ 216 b.append(getMissedCount()); 217 b.append('/').append(getCoveredCount()); 218 b.append(']'); 219 return b.toString(); 220 } 221 222 } 223