net.sourceforge.cobertura.instrument.pass2.BuildClassMapClassVisitor.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.cobertura.instrument.pass2.BuildClassMapClassVisitor.java

Source

/*
 * Cobertura - http://cobertura.sourceforge.net/
 *
 * Copyright (C) 2011 Piotr Tabor
 *
 * Note: This file is dual licensed under the GPL and the Apache
 * Source License (so that it can be used from both the main
 * Cobertura classes and the ant tasks).
 *
 * Cobertura is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 *
 * Cobertura is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cobertura; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

package net.sourceforge.cobertura.instrument.pass2;

import net.sourceforge.cobertura.CoverageIgnore;
import net.sourceforge.cobertura.instrument.AbstractFindTouchPointsClassInstrumenter;
import net.sourceforge.cobertura.instrument.FindTouchPointsMethodAdapter;
import net.sourceforge.cobertura.instrument.HistoryMethodAdapter;
import net.sourceforge.cobertura.instrument.pass3.CodeProvider;
import net.sourceforge.cobertura.instrument.tp.ClassMap;
import org.objectweb.asm.*;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * <p>Analyzes given class. Builds {@link ClassMap} that represents any touch-points and other important information
 * for instrumentation.</p>
 * <p/>
 * This instrumenter ({@link ClassVisitor}) does not change the bytecode of the class. It makes only analyzys and fills {@link ClassMap}.
 *
 * @author piotr.tabor@gmail.com
 */
public class BuildClassMapClassVisitor extends AbstractFindTouchPointsClassInstrumenter {
    /**
     * {@link ClassMap} for the currently analyzed class.
     */
    private final ClassMap classMap = new ClassMap();

    /**
     * Information about important 'events' (instructions) are sent into the listener that is internally
     * responsible for modifying the {@link #classMap} content.
     */
    private final BuildClassMapTouchPointListener touchPointListener = new BuildClassMapTouchPointListener(
            classMap);

    /**
     * It's flag that signals if the class should be instrumented by cobertura.
     * After analyzing the class you can check the field using {@link #shouldBeInstrumented()}.
     */
    private boolean toInstrument = true;

    private final Set<String> ignoredMethods;
    private final Set<String> ignoredClassAnnotations;

    /**
     * @param cv                 - a listener for code-instrumentation events
     * @param ignoreRegexes       - list of patters of method calls that should be ignored from line-coverage-measurement
     * @param ignoreClassAnnotations - list of class annotations to exclude them from instrumentation at all
      * @param duplicatedLinesMap - map of found duplicates in the class. You should use {@link DetectDuplicatedCodeClassVisitor} to find the duplicated lines.
     */
    public BuildClassMapClassVisitor(ClassVisitor cv, Collection<Pattern> ignoreRegexes,
            Set<String> ignoreClassAnnotations, Map<Integer, Map<Integer, Integer>> duplicatedLinesMap,
            Set<String> ignoredMethods) {
        super(cv, ignoreRegexes, duplicatedLinesMap);
        this.ignoredMethods = ignoredMethods;
        this.ignoredClassAnnotations = ignoreClassAnnotations;
    }

    @Override
    public AnnotationVisitor visitAnnotation(String name, boolean arg1) {
        if (Type.getDescriptor(CoverageIgnore.class).equals(name)) {
            toInstrument = false;
        } else if (ignoredClassAnnotations != null) {
            String className = Type.getObjectType(name).getClassName();
            // Class name contains artifacts anyway so trimming them out before
            // matching
            String normalizedClassName = className.replaceAll("[L;]", "");
            if (ignoredClassAnnotations.contains(normalizedClassName)) {
                toInstrument = false;
            }
        }

        return super.visitAnnotation(name, arg1);
    }

    /**
     * Stores in {@link #classMap} information of className and if the class should be instrumented ({@link #shouldBeInstrumented()})
     */
    @Override
    public void visit(int version, int access, String name, String signature, String parent, String[] interfaces) {
        classMap.setClassName(name);

        if ((access & Opcodes.ACC_INTERFACE) != 0) {
            toInstrument = false;
        }
        super.visit(version, access, name, signature, parent, interfaces);
    }

    /**
     * Stores in {@link #classMap} information of source filename
     */
    @Override
    public void visitSource(String file, String debug) {
        classMap.setSource(file);
        super.visitSource(file, debug);
    }

    /**
     * Analyzes given method and stores  information about all found important places into {@link #classMap}
     */
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (((access & Opcodes.ACC_STATIC) != 0) && CodeProvider.COBERTURA_INIT_METHOD_NAME.equals(name)) {
            toInstrument = false; //  The class has bean already instrumented.
        }

        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if (ignoredMethods.contains(name + desc)) {
            return mv;
        }
        FindTouchPointsMethodAdapter instrumenter = new FindTouchPointsMethodAdapter(
                new HistoryMethodAdapter(mv, 4), classMap.getClassName(), name, desc, eventIdGenerator,
                duplicatedLinesMap, lineIdGenerator);
        instrumenter.setTouchPointListener(touchPointListener);
        instrumenter.setIgnoreRegexp(getIgnoreRegexp());
        return instrumenter;
    }

    /**
     * Returns classMap build for the analyzed map. The classmap is filled after running the analyzer ({@link ClassReader#accept(ClassVisitor, int)}).
     *
     * @return the classmap.
     */
    public ClassMap getClassMap() {
        return classMap;
    }

    /**
     * It's flag that signals if the class should be instrumented by Cobertura.
     */
    public boolean shouldBeInstrumented() {
        return toInstrument;
    }
}