/******************************************************************************* * Copyright (c) 2009, 2012 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.CoverageNodeImpl; import org.jacoco.core.analysis.ICounter; import org.jacoco.core.analysis.ILine; import org.jacoco.core.analysis.ISourceNode; /** * Implementation of {@link ISourceNode}. */ public class SourceNodeImpl extends CoverageNodeImpl implements ISourceNode { private LineImpl[] lines; /** first line number in {@link #lines} */ private int offset; /** * Create a new source node implementation instance. * * @param elementType * element type * @param name * name of the element */ public SourceNodeImpl(final ElementType elementType, final String name) { super(elementType, name); lines = null; offset = UNKNOWN_LINE; } /** * Make sure that the internal buffer can keep lines from first to last. * While the buffer is also incremented automatically, this method allows * optimization in case the total range in known in advance. * * @param first * first line number or {@link ISourceNode#UNKNOWN_LINE} * @param last * last line number or {@link ISourceNode#UNKNOWN_LINE} */ public void ensureCapacity(final int first, final int last) { if (first == UNKNOWN_LINE || last == UNKNOWN_LINE) { return; } if (lines == null) { offset = first; lines = new LineImpl[last - first + 1]; } else { final int newFirst = Math.min(getFirstLine(), first); final int newLast = Math.max(getLastLine(), last); final int newLength = newLast - newFirst + 1; if (newLength > lines.length) { final LineImpl[] newLines = new LineImpl[newLength]; System.arraycopy(lines, 0, newLines, offset - newFirst, lines.length); offset = newFirst; lines = newLines; } } } /** * Increments all counters by the values of the given child. When * incrementing the line counter it is assumed that the child refers to the * same source file. * * @param child * child node to add */ public void increment(final ISourceNode child) { instructionCounter = instructionCounter.increment(child .getInstructionCounter()); branchCounter = branchCounter.increment(child.getBranchCounter()); complexityCounter = complexityCounter.increment(child .getComplexityCounter()); methodCounter = methodCounter.increment(child.getMethodCounter()); classCounter = classCounter.increment(child.getClassCounter()); final int firstLine = child.getFirstLine(); if (firstLine != UNKNOWN_LINE) { final int lastLine = child.getLastLine(); ensureCapacity(firstLine, lastLine); for (int i = firstLine; i <= lastLine; i++) { final ILine line = child.getLine(i); incrementLine(line.getInstructionCounter(), line.getBranchCounter(), i); } } } /** * Increments instructions and branches by the given counter values. If a * optional line number is specified the instructions and branches are added * to the given line. The line counter is incremented accordingly. * * @param instructions * instructions to add * @param branches * branches to add * @param line * optional line number or {@link ISourceNode#UNKNOWN_LINE} */ public void increment(final ICounter instructions, final ICounter branches, final int line) { if (line != UNKNOWN_LINE) { incrementLine(instructions, branches, line); } instructionCounter = instructionCounter.increment(instructions); branchCounter = branchCounter.increment(branches); } private void incrementLine(final ICounter instructions, final ICounter branches, final int line) { ensureCapacity(line, line); final LineImpl l = getLine(line); final int oldTotal = l.getInstructionCounter().getTotalCount(); final int oldCovered = l.getInstructionCounter().getCoveredCount(); lines[line - offset] = l.increment(instructions, branches); // Increment line counter: if (instructions.getTotalCount() > 0) { if (instructions.getCoveredCount() == 0) { if (oldTotal == 0) { lineCounter = lineCounter .increment(CounterImpl.COUNTER_1_0); } } else { if (oldTotal == 0) { lineCounter = lineCounter .increment(CounterImpl.COUNTER_0_1); } else { if (oldCovered == 0) { lineCounter = lineCounter.increment(-1, +1); } } } } } // === ISourceNode implementation === public int getFirstLine() { return offset; } public int getLastLine() { return lines == null ? UNKNOWN_LINE : (offset + lines.length - 1); } public LineImpl getLine(final int nr) { if (lines == null || nr < getFirstLine() || nr > getLastLine()) { return LineImpl.EMPTY; } final LineImpl line = lines[nr - offset]; return line == null ? LineImpl.EMPTY : line; } }