1/*******************************************************************************
2 * Copyright (c) 2009, 2015 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 *******************************************************************************/
12package org.jacoco.core.internal.analysis;
13
14import org.jacoco.core.analysis.ICounter;
15import org.jacoco.core.analysis.ILine;
16
17/**
18 * Implementation of {@link ILine}.
19 */
20public abstract class LineImpl implements ILine {
21
22	/** Max instruction counter value for which singletons are created */
23	private static final int SINGLETON_INS_LIMIT = 8;
24
25	/** Max branch counter value for which singletons are created */
26	private static final int SINGLETON_BRA_LIMIT = 4;
27
28	private static final LineImpl[][][][] SINGLETONS = new LineImpl[SINGLETON_INS_LIMIT + 1][][][];
29
30	static {
31		for (int i = 0; i <= SINGLETON_INS_LIMIT; i++) {
32			SINGLETONS[i] = new LineImpl[SINGLETON_INS_LIMIT + 1][][];
33			for (int j = 0; j <= SINGLETON_INS_LIMIT; j++) {
34				SINGLETONS[i][j] = new LineImpl[SINGLETON_BRA_LIMIT + 1][];
35				for (int k = 0; k <= SINGLETON_BRA_LIMIT; k++) {
36					SINGLETONS[i][j][k] = new LineImpl[SINGLETON_BRA_LIMIT + 1];
37					for (int l = 0; l <= SINGLETON_BRA_LIMIT; l++) {
38						SINGLETONS[i][j][k][l] = new Fix(i, j, k, l);
39					}
40				}
41			}
42		}
43	}
44
45	/**
46	 * Empty line without instructions or branches.
47	 */
48	public static final LineImpl EMPTY = SINGLETONS[0][0][0][0];
49
50	private static LineImpl getInstance(final CounterImpl instructions,
51			final CounterImpl branches) {
52		final int im = instructions.getMissedCount();
53		final int ic = instructions.getCoveredCount();
54		final int bm = branches.getMissedCount();
55		final int bc = branches.getCoveredCount();
56		if (im <= SINGLETON_INS_LIMIT && ic <= SINGLETON_INS_LIMIT
57				&& bm <= SINGLETON_BRA_LIMIT && bc <= SINGLETON_BRA_LIMIT) {
58			return SINGLETONS[im][ic][bm][bc];
59		}
60		return new Var(instructions, branches);
61	}
62
63	/**
64	 * Mutable version.
65	 */
66	private static final class Var extends LineImpl {
67		Var(final CounterImpl instructions, final CounterImpl branches) {
68			super(instructions, branches);
69		}
70
71		@Override
72		public LineImpl increment(final ICounter instructions,
73				final ICounter branches) {
74			this.instructions = this.instructions.increment(instructions);
75			this.branches = this.branches.increment(branches);
76			return this;
77		}
78	}
79
80	/**
81	 * Immutable version.
82	 */
83	private static final class Fix extends LineImpl {
84		public Fix(final int im, final int ic, final int bm, final int bc) {
85			super(CounterImpl.getInstance(im, ic), CounterImpl.getInstance(bm,
86					bc));
87		}
88
89		@Override
90		public LineImpl increment(final ICounter instructions,
91				final ICounter branches) {
92			return getInstance(this.instructions.increment(instructions),
93					this.branches.increment(branches));
94		}
95	}
96
97	/** instruction counter */
98	protected CounterImpl instructions;
99
100	/** branch counter */
101	protected CounterImpl branches;
102
103	private LineImpl(final CounterImpl instructions, final CounterImpl branches) {
104		this.instructions = instructions;
105		this.branches = branches;
106	}
107
108	/**
109	 * Adds the given counters to this line.
110	 *
111	 * @param instructions
112	 *            instructions to add
113	 * @param branches
114	 *            branches to add
115	 * @return instance with new counter values
116	 */
117	public abstract LineImpl increment(final ICounter instructions,
118			final ICounter branches);
119
120	// === ILine implementation ===
121
122	public int getStatus() {
123		return instructions.getStatus() | branches.getStatus();
124	}
125
126	public ICounter getInstructionCounter() {
127		return instructions;
128	}
129
130	public ICounter getBranchCounter() {
131		return branches;
132	}
133
134	@Override
135	public int hashCode() {
136		return 23 * instructions.hashCode() ^ branches.hashCode();
137	}
138
139	@Override
140	public boolean equals(final Object obj) {
141		if (obj instanceof ILine) {
142			final ILine that = (ILine) obj;
143			return this.instructions.equals(that.getInstructionCounter())
144					&& this.branches.equals(that.getBranchCounter());
145		}
146		return false;
147	}
148
149}
150