001/* 002 * Cobertura - http://cobertura.sourceforge.net/ 003 * 004 * Copyright (C) 2011 Piotr Tabor 005 * 006 * Note: This file is dual licensed under the GPL and the Apache 007 * Source License (so that it can be used from both the main 008 * Cobertura classes and the ant tasks). 009 * 010 * Cobertura is free software; you can redistribute it and/or modify 011 * it under the terms of the GNU General Public License as published 012 * by the Free Software Foundation; either version 2 of the License, 013 * or (at your option) any later version. 014 * 015 * Cobertura is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 018 * General Public License for more details. 019 * 020 * You should have received a copy of the GNU General Public License 021 * along with Cobertura; if not, write to the Free Software 022 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 023 * USA 024 */ 025 026package net.sourceforge.cobertura.instrument.pass2; 027 028import java.util.Collection; 029import java.util.Map; 030import java.util.Set; 031import java.util.regex.Pattern; 032 033import net.sourceforge.cobertura.CoverageIgnore; 034import net.sourceforge.cobertura.instrument.AbstractFindTouchPointsClassInstrumenter; 035import net.sourceforge.cobertura.instrument.FindTouchPointsMethodAdapter; 036import net.sourceforge.cobertura.instrument.HistoryMethodAdapter; 037import net.sourceforge.cobertura.instrument.pass3.CodeProvider; 038import net.sourceforge.cobertura.instrument.tp.ClassMap; 039 040import org.objectweb.asm.AnnotationVisitor; 041import org.objectweb.asm.ClassReader; 042import org.objectweb.asm.ClassVisitor; 043import org.objectweb.asm.MethodVisitor; 044import org.objectweb.asm.Opcodes; 045import org.objectweb.asm.Type; 046 047/** 048 * <p>Analyzes given class. Builds {@link ClassMap} that represents any touch-points and other important information 049 * for instrumentation.</p> 050 * 051 * This instrumenter ({@link ClassVisitor}) does not change the bytecode of the class. It makes only analyzys and fills {@link ClassMap}. 052 * 053 * @author piotr.tabor@gmail.com 054 */ 055public class BuildClassMapClassVisitor extends AbstractFindTouchPointsClassInstrumenter{ 056 /** 057 * {@link ClassMap} for the currently analyzed class. 058 */ 059 private final ClassMap classMap = new ClassMap(); 060 061 /** 062 * Information about important 'events' (instructions) are sent into the listener that is internally 063 * responsible for modifying the {@link #classMap} content. 064 */ 065 private final BuildClassMapTouchPointListener touchPointListener = new BuildClassMapTouchPointListener(classMap); 066 067 /** 068 * It's flag that signals if the class should be instrumented by cobertura. 069 * After analyzing the class you can check the field using {@link #shouldBeInstrumented()}. 070 */ 071 private boolean toInstrument = true; 072 073 private final Set<String> ignoredMethods; 074 075 /** 076 * @param cv - a listener for code-instrumentation events 077 * @param ignoreRegexp - list of patters of method calls that should be ignored from line-coverage-measurement 078 * @param duplicatedLinesMap - map of found duplicates in the class. You should use {@link DetectDuplicatedCodeClassVisitor} to find the duplicated lines. 079 */ 080 public BuildClassMapClassVisitor(ClassVisitor cv, Collection<Pattern> ignoreRegexes,Map<Integer, Map<Integer, Integer>> duplicatedLinesMap, 081 Set<String> ignoredMethods) { 082 super(cv,ignoreRegexes,duplicatedLinesMap); 083 this.ignoredMethods = ignoredMethods; 084 } 085 086 @Override 087 public AnnotationVisitor visitAnnotation(String name, boolean arg1) { 088 if (Type.getDescriptor(CoverageIgnore.class).equals(name)) { 089 toInstrument = false; 090 } 091 return super.visitAnnotation(name, arg1); 092 } 093 094 /** 095 * Stores in {@link #classMap} information of className and if the class should be instrumented ({@link #shouldBeInstrumented()}) 096 */ 097 @Override 098 public void visit(int version, int access, String name, String signature, 099 String parent, String[] interfaces) { 100 classMap.setClassName(name); 101 102 if ((access & Opcodes.ACC_INTERFACE) != 0) { 103 toInstrument = false; 104 } 105 super.visit(version, access, name, signature, parent, interfaces); 106 } 107 108 /** 109 * Stores in {@link #classMap} information of source filename 110 */ 111 @Override 112 public void visitSource(String file, String debug) { 113 classMap.setSource(file); 114 super.visitSource(file, debug); 115 } 116 117 /** 118 * Analyzes given method and stores information about all found important places into {@link #classMap} 119 */ 120 @Override 121 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 122 if (((access & Opcodes.ACC_STATIC) != 0) 123 && CodeProvider.COBERTURA_INIT_METHOD_NAME.equals(name)) { 124 toInstrument = false; // The class has bean already instrumented. 125 } 126 127 MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 128 if (ignoredMethods.contains(name + desc)) { 129 return mv; 130 } 131 FindTouchPointsMethodAdapter instrumenter = new FindTouchPointsMethodAdapter( 132 new HistoryMethodAdapter(mv, 4), 133 classMap.getClassName(),name,desc,eventIdGenerator,duplicatedLinesMap,lineIdGenerator); 134 instrumenter.setTouchPointListener(touchPointListener); 135 instrumenter.setIgnoreRegexp(getIgnoreRegexp()); 136 return instrumenter; 137 } 138 139 /** 140 * Returns classMap build for the analyzed map. The classmap is filled after running the analyzer ({@link ClassReader#accept(ClassVisitor, int)}). 141 * 142 * @return the classmap. 143 */ 144 public ClassMap getClassMap(){ 145 return classMap; 146 } 147 148 /** 149 * It's flag that signals if the class should be instrumented by Cobertura. 150 */ 151 public boolean shouldBeInstrumented() { 152 return toInstrument; 153 } 154}