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 */
023package com.jcoverage.coverage;
024
025import com.jcoverage.util.MethodHelper;
026
027import java.util.HashMap;
028import java.util.Map;
029import java.util.Set;
030import java.util.TreeSet;
031
032import org.apache.bcel.classfile.JavaClass;
033import org.apache.bcel.classfile.Method;
034
035import org.apache.bcel.generic.ClassGen;
036
037import 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 */
044class 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}