# ClassProbesAdapter.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.ClassVisitor;
    import org.objectweb.asm.Label;
    import org.objectweb.asm.MethodVisitor;
    import org.objectweb.asm.Opcodes;

    /**
     * A {@link org.objectweb.asm.ClassVisitor} that calculates probes for every
     * method.
     */
    public class ClassProbesAdapter extends ClassVisitor implements
    		IProbeIdGenerator {

    	private static final MethodProbesVisitor EMPTY_METHOD_PROBES_VISITOR;

    	static {
    		class Impl extends MethodProbesVisitor {

    			@Override
    			public void visitProbe(final int probeId) {
    				// nothing to do
    			}

    			@Override
    			public void visitJumpInsnWithProbe(final int opcode,
    					final Label label, final int probeId) {
    				// nothing to do
    			}

    			@Override
    			public void visitInsnWithProbe(final int opcode, final int probeId) {
    				// nothing to do
    			}

    			@Override
    			public void visitTableSwitchInsnWithProbes(final int min,
    					final int max, final Label dflt, final Label[] labels) {
    				// nothing to do
    			}

    			@Override
    			public void visitLookupSwitchInsnWithProbes(final Label dflt,
    					final int[] keys, final Label[] labels) {
    				// nothing to do
    			}
    		}
    		EMPTY_METHOD_PROBES_VISITOR = new Impl();
    	}

    	private static class ProbeCounter implements IProbeIdGenerator {
    		int count = 0;

    		public int nextId() {
    			return count++;
    		}
    	}

    	private final ClassProbesVisitor cv;

    	private int counter = 0;

    	private boolean interfaceType;

    	/**
    	 * Creates a new adapter that delegates to the given visitor.
    	 *
    	 * @param cv
    	 *            instance to delegate to
    	 */
    	public ClassProbesAdapter(final ClassProbesVisitor cv) {
    		super(Opcodes.ASM4, cv);
    		this.cv = cv;
    	}

    	@Override
    	public void visit(final int version, final int access, final String name,
    			final String signature, final String superName,
    			final String[] interfaces) {
    		interfaceType = (access & Opcodes.ACC_INTERFACE) != 0;
    		super.visit(version, access, name, signature, superName, interfaces);
    	}

    	@Override
    	public final MethodVisitor visitMethod(final int access, final String name,
    			final String desc, final String signature, final String[] exceptions) {
    		final MethodProbesVisitor methodProbes;
    		final MethodProbesVisitor mv = cv.visitMethod(access, name, desc,
    				signature, exceptions);
    		if (mv == null) {
    			// We need to visit the method in any case, otherwise probe ids
    			// are not reproducible
    			methodProbes = EMPTY_METHOD_PROBES_VISITOR;
    		} else {
    			methodProbes = mv;
    		}
    		return new MethodSanitizer(null, access, name, desc, signature,
    				exceptions) {

    			@Override
    			public void visitEnd() {
    				super.visitEnd();
    				LabelFlowAnalyzer.markLabels(this);
    				if (interfaceType) {
    					final ProbeCounter probeCounter = new ProbeCounter();
    					final MethodProbesAdapter adapter = new MethodProbesAdapter(
    							EMPTY_METHOD_PROBES_VISITOR, probeCounter);
    					// We do not use the accept() method as ASM resets labels
    					// after every call to accept()
    					instructions.accept(adapter);
    					cv.visitTotalProbeCount(probeCounter.count);
    				}
    				this.accept(new MethodProbesAdapter(methodProbes,
    						ClassProbesAdapter.this));
    			}
    		};
    	}

    	@Override
    	public void visitEnd() {
    		if (!interfaceType) {
    			cv.visitTotalProbeCount(counter);
    		}
    		super.visitEnd();
    	}

    	// === IProbeIdGenerator ===

    	public int nextId() {
    		return counter++;
    	}

    }