001 /** 002 * www.jcoverage.com 003 * Copyright (C)2003 jcoverage ltd. 004 * 005 * This file is part of jcoverage. 006 * 007 * jcoverage is free software; you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published 009 * by the Free Software Foundation; either version 2 of the License, 010 * or (at your option) any later version. 011 * 012 * jcoverage is distributed in the hope that it will be useful, but 013 * WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with jcoverage; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 020 * USA 021 * 022 */ 023 package com.jcoverage.coverage; 024 025 import com.jcoverage.util.MethodHelper; 026 027 import java.util.HashMap; 028 import java.util.Map; 029 import java.util.Set; 030 import java.util.TreeSet; 031 032 import org.apache.bcel.classfile.JavaClass; 033 import org.apache.bcel.classfile.Method; 034 035 import org.apache.bcel.generic.ClassGen; 036 037 import org.apache.log4j.Logger; 038 039 /** 040 * Add coverage instrumentation to an existing class. Instances of 041 * this class are normally created by @see Instrument, as part of the 042 * instrumentation process. 043 */ 044 class InstrumentClassGen { 045 static final Logger logger=Logger.getLogger(InstrumentClassGen.class); 046 047 final ClassGen cg; 048 049 /** 050 * The set of "real" source line numbers that are present in this 051 * class. That is, those lines of Java source code that do not 052 * represent comments, or other syntax "fluff" (e.g., "} else {"), 053 * or those lines that have been ignored because they match the 054 * ignore regex. 055 */ 056 final Set sourceLineNumbers=new TreeSet(); 057 058 /** 059 * The set of method names concatenated with their signature. 060 */ 061 final Set methodNamesAndSignatures=new TreeSet(); 062 063 /** 064 * A mapping from method name and signature to the set of line 065 * numbers for that method. 066 */ 067 final Map methodLineNumbers=new HashMap(); 068 069 /** 070 * A mapping from method name and signature to the set of 071 * conditionals for that method. 072 * @see Conditional 073 */ 074 final Map methodConditionals=new HashMap(); 075 076 final String ignoreRegex; 077 078 InstrumentClassGen(JavaClass jc,String ignoreRegex) { 079 this.cg=new ClassGen(jc); 080 this.ignoreRegex=ignoreRegex; 081 } 082 083 /** 084 * Add instrumentation collected by <code>instrument</code> to this 085 * class 086 */ 087 private void add(Method method,InstrumentMethodGen instrument) { 088 methodNamesAndSignatures.add(MethodHelper.getMethodNameAndSignature(method)); 089 methodLineNumbers.put(MethodHelper.getMethodNameAndSignature(method),instrument.getSourceLineNumbers()); 090 methodConditionals.put(MethodHelper.getMethodNameAndSignature(method),instrument.getConditionals()); 091 addSourceLineNumbers(instrument.getSourceLineNumbers()); 092 } 093 094 /** 095 * Add instrumentation to a method found in this class. 096 * @param method a method present in the class 097 */ 098 void addInstrumentation(Method method) { 099 if(logger.isDebugEnabled()) { 100 logger.debug("adding instrumentation to: "+cg.getClassName()+'.'+method.getName()); 101 } 102 InstrumentMethodGen instrument=new InstrumentMethodGen(method,cg,ignoreRegex); 103 instrument.addInstrumentation(); 104 add(method,instrument); 105 } 106 107 /** 108 * Add instrument to all the supplied methods. 109 */ 110 void addInstrumentation(Method[] methods) { 111 for(int i=0;i<methods.length;i++) { 112 addInstrumentation(methods[i]); 113 } 114 } 115 116 /** 117 * Add coverage instrumentation to the class. Once instrumented, the 118 * instrumented class is tagged with a marker interface @see 119 * HasBeenInstrumented to prevent it from being instrumented again. 120 */ 121 void addInstrumentation() { 122 if(logger.isDebugEnabled()) { 123 logger.debug("adding instrumentation to: "+getClassGen().getClassName()); 124 } 125 126 addInstrumentation(getClassGen().getMethods()); 127 getClassGen().addInterface(HasBeenInstrumented.class.getName()); 128 } 129 130 ClassGen getClassGen() { 131 return cg; 132 } 133 134 private void addSourceLineNumbers(Set sourceLineNumbers) { 135 this.sourceLineNumbers.addAll(sourceLineNumbers); 136 } 137 138 /** 139 * @return the set of source line numbers for this class 140 */ 141 Set getSourceLineNumbers() { 142 return sourceLineNumbers; 143 } 144 145 /** 146 * @return a mapping from method name and signature to the set of 147 * line numbers for that method. 148 */ 149 Map getMethodLineNumbers() { 150 return methodLineNumbers; 151 } 152 153 /** 154 * @return a mapping from method name and signature to the set of 155 * conditionals for that method. 156 * @see Conditional 157 */ 158 Map getMethodConditionals() { 159 return methodConditionals; 160 } 161 162 /** 163 * @return the set of method names and signatures that can be found 164 * in this class. 165 */ 166 Set getMethodNamesAndSignatures() { 167 return methodNamesAndSignatures; 168 } 169 }