# MethodProbesAdapter.java

    /*******************************************************************************
     * Copyright (c) 2009, 2013 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.flow;

    import org.objectweb.asm.Label;
    import org.objectweb.asm.MethodVisitor;
    import org.objectweb.asm.Opcodes;

    /**
     * Adapter that creates additional visitor events for probes to be inserted into
     * a method.
     */
    public final class MethodProbesAdapter extends MethodVisitor {

    	private final MethodProbesVisitor probesVisitor;

    	private final IProbeIdGenerator idGenerator;

    	/**
    	 * Create a new adapter instance.
    	 *
    	 * @param probesVisitor
    	 *            visitor to delegate to
    	 * @param idGenerator
    	 *            generator for unique probe ids
    	 */
    	public MethodProbesAdapter(final MethodProbesVisitor probesVisitor,
    			final IProbeIdGenerator idGenerator) {
    		super(Opcodes.ASM4, probesVisitor);
    		this.probesVisitor = probesVisitor;
    		this.idGenerator = idGenerator;
    	}

    	@Override
    	public void visitLabel(final Label label) {
    		if (LabelInfo.isMultiTarget(label) && LabelInfo.isSuccessor(label)) {
    			probesVisitor.visitProbe(idGenerator.nextId());
    		}
    		probesVisitor.visitLabel(label);
    	}

    	@Override
    	public void visitInsn(final int opcode) {
    		switch (opcode) {
    		case Opcodes.IRETURN:
    		case Opcodes.LRETURN:
    		case Opcodes.FRETURN:
    		case Opcodes.DRETURN:
    		case Opcodes.ARETURN:
    		case Opcodes.RETURN:
    		case Opcodes.ATHROW:
    			probesVisitor.visitInsnWithProbe(opcode, idGenerator.nextId());
    			break;
    		default:
    			probesVisitor.visitInsn(opcode);
    			break;
    		}
    	}

    	@Override
    	public void visitJumpInsn(final int opcode, final Label label) {
    		if (LabelInfo.isMultiTarget(label)) {
    			probesVisitor.visitJumpInsnWithProbe(opcode, label,
    					idGenerator.nextId());
    		} else {
    			probesVisitor.visitJumpInsn(opcode, label);
    		}
    	}

    	@Override
    	public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
    			final Label[] labels) {
    		if (markLabels(dflt, labels)) {
    			probesVisitor.visitLookupSwitchInsnWithProbes(dflt, keys, labels);
    		} else {
    			probesVisitor.visitLookupSwitchInsn(dflt, keys, labels);
    		}
    	}

    	@Override
    	public void visitTableSwitchInsn(final int min, final int max,
    			final Label dflt, final Label... labels) {
    		if (markLabels(dflt, labels)) {
    			probesVisitor
    					.visitTableSwitchInsnWithProbes(min, max, dflt, labels);
    		} else {
    			probesVisitor.visitTableSwitchInsn(min, max, dflt, labels);
    		}
    	}

    	private boolean markLabels(final Label dflt, final Label[] labels) {
    		boolean probe = false;
    		LabelInfo.resetDone(labels);
    		if (LabelInfo.isMultiTarget(dflt)) {
    			LabelInfo.setProbeId(dflt, idGenerator.nextId());
    			probe = true;
    		}
    		LabelInfo.setDone(dflt);
    		for (final Label l : labels) {
    			if (!LabelInfo.isDone(l) && LabelInfo.isMultiTarget(l)) {
    				LabelInfo.setProbeId(l, idGenerator.nextId());
    				probe = true;
    			}
    			LabelInfo.setDone(l);
    		}
    		return probe;
    	}

    }