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