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.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 is 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;
  	}
  
  }
  