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 }