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.CoverageNodeImpl; 16 import org.jacoco.core.analysis.ICounter; 17 import org.jacoco.core.analysis.ILine; 18 import org.jacoco.core.analysis.ISourceNode; 19 20 /** 21 * Implementation of {@link ISourceNode}. 22 */ 23 public class SourceNodeImpl extends CoverageNodeImpl implements ISourceNode { 24 25 private LineImpl[] lines; 26 27 /** first line number in {@link #lines} */ 28 private int offset; 29 30 /** 31 * Create a new source node implementation instance. 32 * 33 * @param elementType 34 * element type 35 * @param name 36 * name of the element 37 */ SourceNodeImpl(final ElementType elementType, final String name)38 public SourceNodeImpl(final ElementType elementType, final String name) { 39 super(elementType, name); 40 lines = null; 41 offset = UNKNOWN_LINE; 42 } 43 44 /** 45 * Make sure that the internal buffer can keep lines from first to last. 46 * While the buffer is also incremented automatically, this method allows 47 * optimization in case the total range is known in advance. 48 * 49 * @param first 50 * first line number or {@link ISourceNode#UNKNOWN_LINE} 51 * @param last 52 * last line number or {@link ISourceNode#UNKNOWN_LINE} 53 */ ensureCapacity(final int first, final int last)54 public void ensureCapacity(final int first, final int last) { 55 if (first == UNKNOWN_LINE || last == UNKNOWN_LINE) { 56 return; 57 } 58 if (lines == null) { 59 offset = first; 60 lines = new LineImpl[last - first + 1]; 61 } else { 62 final int newFirst = Math.min(getFirstLine(), first); 63 final int newLast = Math.max(getLastLine(), last); 64 final int newLength = newLast - newFirst + 1; 65 if (newLength > lines.length) { 66 final LineImpl[] newLines = new LineImpl[newLength]; 67 System.arraycopy(lines, 0, newLines, offset - newFirst, 68 lines.length); 69 offset = newFirst; 70 lines = newLines; 71 } 72 } 73 } 74 75 /** 76 * Increments all counters by the values of the given child. When 77 * incrementing the line counter it is assumed that the child refers to the 78 * same source file. 79 * 80 * @param child 81 * child node to add 82 */ increment(final ISourceNode child)83 public void increment(final ISourceNode child) { 84 instructionCounter = instructionCounter 85 .increment(child.getInstructionCounter()); 86 branchCounter = branchCounter.increment(child.getBranchCounter()); 87 complexityCounter = complexityCounter 88 .increment(child.getComplexityCounter()); 89 methodCounter = methodCounter.increment(child.getMethodCounter()); 90 classCounter = classCounter.increment(child.getClassCounter()); 91 final int firstLine = child.getFirstLine(); 92 if (firstLine != UNKNOWN_LINE) { 93 final int lastLine = child.getLastLine(); 94 ensureCapacity(firstLine, lastLine); 95 for (int i = firstLine; i <= lastLine; i++) { 96 final ILine line = child.getLine(i); 97 incrementLine(line.getInstructionCounter(), 98 line.getBranchCounter(), i); 99 } 100 } 101 } 102 103 /** 104 * Increments instructions and branches by the given counter values. If a 105 * optional line number is specified the instructions and branches are added 106 * to the given line. The line counter is incremented accordingly. 107 * 108 * @param instructions 109 * instructions to add 110 * @param branches 111 * branches to add 112 * @param line 113 * optional line number or {@link ISourceNode#UNKNOWN_LINE} 114 */ increment(final ICounter instructions, final ICounter branches, final int line)115 public void increment(final ICounter instructions, final ICounter branches, 116 final int line) { 117 if (line != UNKNOWN_LINE) { 118 incrementLine(instructions, branches, line); 119 } 120 instructionCounter = instructionCounter.increment(instructions); 121 branchCounter = branchCounter.increment(branches); 122 } 123 incrementLine(final ICounter instructions, final ICounter branches, final int line)124 private void incrementLine(final ICounter instructions, 125 final ICounter branches, final int line) { 126 ensureCapacity(line, line); 127 final LineImpl l = getLine(line); 128 final int oldTotal = l.getInstructionCounter().getTotalCount(); 129 final int oldCovered = l.getInstructionCounter().getCoveredCount(); 130 lines[line - offset] = l.increment(instructions, branches); 131 132 // Increment line counter: 133 if (instructions.getTotalCount() > 0) { 134 if (instructions.getCoveredCount() == 0) { 135 if (oldTotal == 0) { 136 lineCounter = lineCounter 137 .increment(CounterImpl.COUNTER_1_0); 138 } 139 } else { 140 if (oldTotal == 0) { 141 lineCounter = lineCounter 142 .increment(CounterImpl.COUNTER_0_1); 143 } else { 144 if (oldCovered == 0) { 145 lineCounter = lineCounter.increment(-1, +1); 146 } 147 } 148 } 149 } 150 } 151 152 // === ISourceNode implementation === 153 getFirstLine()154 public int getFirstLine() { 155 return offset; 156 } 157 getLastLine()158 public int getLastLine() { 159 return lines == null ? UNKNOWN_LINE : (offset + lines.length - 1); 160 } 161 getLine(final int nr)162 public LineImpl getLine(final int nr) { 163 if (lines == null || nr < getFirstLine() || nr > getLastLine()) { 164 return LineImpl.EMPTY; 165 } 166 final LineImpl line = lines[nr - offset]; 167 return line == null ? LineImpl.EMPTY : line; 168 } 169 170 } 171