content: /*******************************************************************************
   * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *    Marc R. Hoffmann - initial API and implementation
   *    
   *******************************************************************************/
  package org.jacoco.core.internal.analysis;
  
  import org.jacoco.core.analysis.ICounter;
  
  /**
   * {@link ICounter} implementations. Implementing a factory pattern allows to
   * share counter instances.
   */
  public abstract class CounterImpl implements ICounter {
  
  	/** Max counter value for which singletons are created */
  	private static final int SINGLETON_LIMIT = 30;
  
  	private static final CounterImpl[][] SINGLETONS = new CounterImpl[SINGLETON_LIMIT + 1][];
  
  	static {
  		for (int i = 0; i <= SINGLETON_LIMIT; i++) {
  			SINGLETONS[i] = new CounterImpl[SINGLETON_LIMIT + 1];
  			for (int j = 0; j <= SINGLETON_LIMIT; j++) {
  				SINGLETONS[i][j] = new Fix(i, j);
  			}
  		}
  	}
  
  	/** Constant for Counter with 0/0 values. */
  	public static final CounterImpl COUNTER_0_0 = SINGLETONS[0][0];
  
  	/** Constant for Counter with 1/0 values. */
  	public static final CounterImpl COUNTER_1_0 = SINGLETONS[1][0];
  
  	/** Constant for Counter with 0/1 values. */
  	public static final CounterImpl COUNTER_0_1 = SINGLETONS[0][1];
  
  	/**
  	 * Mutable version of the counter.
  	 */
  	private static class Var extends CounterImpl {
  		public Var(final int missed, final int covered) {
  			super(missed, covered);
  		}
  
  		@Override
  		public CounterImpl increment(final int missed, final int covered) {
  			this.missed += missed;
  			this.covered += covered;
  			return this;
  		}
  	}
  
  	/**
  	 * Immutable version of the counter.
  	 */
  	private static class Fix extends CounterImpl {
  		public Fix(final int missed, final int covered) {
  			super(missed, covered);
  		}
  
  		@Override
  		public CounterImpl increment(final int missed, final int covered) {
  			return getInstance(this.missed + missed, this.covered + covered);
  		}
  	}
  
  	/**
  	 * Factory method to retrieve a counter with the given number of items.
  	 * 
  	 * @param missed
  	 *            number of missed items
  	 * @param covered
  	 *            number of covered items
  	 * @return counter instance
  	 */
  	public static CounterImpl getInstance(final int missed, final int covered) {
  		if (missed <= SINGLETON_LIMIT && covered <= SINGLETON_LIMIT) {
  			return SINGLETONS[missed][covered];
  		} else {
  			return new Var(missed, covered);
  		}
  	}
  
  	/**
  	 * Factory method to retrieve a clone of the given counter.
  	 * 
  	 * @param counter
  	 *            counter to copy
  	 * @return counter instance
  	 */
  	public static CounterImpl getInstance(final ICounter counter) {
  		return getInstance(counter.getMissedCount(), counter.getCoveredCount());
  	}
  
  	/** number of missed items */
  	protected int missed;
  
  	/** number of covered items */
  	protected int covered;
  
  	/**
  	 * Creates a new instance with the given numbers.
  	 * 
  	 * @param missed
  	 *            number of missed items
  	 * @param covered
  	 *            number of covered items
  	 */
  	protected CounterImpl(final int missed, final int covered) {
  		this.missed = missed;
  		this.covered = covered;
  	}
  
  	/**
  	 * Returns a counter with values incremented by the numbers of the given
  	 * counter. It is up to the implementation whether this counter instance is
  	 * modified or a new instance is returned.
  	 * 
  	 * @param counter
  	 *            number of additional total and covered items
  	 * @return counter instance with incremented values
  	 */
  	public CounterImpl increment(final ICounter counter) {
  		return increment(counter.getMissedCount(), counter.getCoveredCount());
  	}
  
  	/**
  	 * Returns a counter with values incremented by the given numbers. It is up
  	 * to the implementation whether this counter instance is modified or a new
  	 * instance is returned.
  	 * 
  	 * @param missed
  	 *            number of missed items
  	 * @param covered
  	 *            number of covered items
  	 * @return counter instance with incremented values
  	 */
  	public abstract CounterImpl increment(int missed, int covered);
  
  	// === ICounter implementation ===
  
  	public double getValue(final CounterValue value) {
  		switch (value) {
  		case TOTALCOUNT:
  			return getTotalCount();
  		case MISSEDCOUNT:
  			return getMissedCount();
  		case COVEREDCOUNT:
  			return getCoveredCount();
  		case MISSEDRATIO:
  			return getMissedRatio();
  		case COVEREDRATIO:
  			return getCoveredRatio();
  		default:
  			throw new AssertionError(value);
  		}
  	}
  
  	public int getTotalCount() {
  		return missed + covered;
  	}
  
  	public int getCoveredCount() {
  		return covered;
  	}
  
  	public int getMissedCount() {
  		return missed;
  	}
  
  	public double getCoveredRatio() {
  		return (double) covered / (missed + covered);
  	}
  
  	public double getMissedRatio() {
  		return (double) missed / (missed + covered);
  	}
  
  	public int getStatus() {
  		int status = covered > 0 ? FULLY_COVERED : EMPTY;
  		if (missed > 0) {
  			status |= NOT_COVERED;
  		}
  		return status;
  	}
  
  	@Override
  	public boolean equals(final Object obj) {
  		if (obj instanceof ICounter) {
  			final ICounter that = (ICounter) obj;
  			return this.missed == that.getMissedCount()
  					&& this.covered == that.getCoveredCount();
  		} else {
  			return false;
  		}
  	}
  
  	@Override
  	public int hashCode() {
  		return missed ^ covered * 17;
  	}
  
  	@Override
  	public String toString() {
  		final StringBuilder b = new StringBuilder("Counter["); //$NON-NLS-1$
  		b.append(getMissedCount());
  		b.append('/').append(getCoveredCount());
  		b.append(']');
  		return b.toString();
  	}
  
  }
  