001 /*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * Copyright (C) 2005 Mark Doliner <thekingant@users.sourceforge.net>
005 *
006 * Cobertura is free software; you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published
008 * by the Free Software Foundation; either version 2 of the License,
009 * or (at your option) any later version.
010 *
011 * Cobertura is distributed in the hope that it will be useful, but
012 * WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with Cobertura; if not, write to the Free Software
018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019 * USA
020 */
021
022 package net.sourceforge.cobertura.coverage;
023
024 import java.util.regex.Pattern;
025
026 import org.apache.log4j.Logger;
027 import org.objectweb.asm.ClassAdapter;
028 import org.objectweb.asm.ClassVisitor;
029 import org.objectweb.asm.MethodVisitor;
030 import org.objectweb.asm.Opcodes;
031
032 class ClassInstrumenter extends ClassAdapter implements Opcodes
033 {
034
035 private static final Logger logger = Logger.getLogger(Main.class);
036
037 private final static String hasBeenInstrumented = "net/sourceforge/cobertura/coverage/HasBeenInstrumented";
038 private Pattern ignoreRegexp;
039 private CoverageData coverageData;
040 private String myName;
041 private boolean instrument = false;
042
043 public String getClassName()
044 {
045 return this.myName;
046 }
047
048 public boolean isInstrumented()
049 {
050 return instrument;
051 }
052
053 public ClassInstrumenter(final ClassVisitor cv, Pattern ignoreRegexp)
054 {
055 super(cv);
056 this.ignoreRegexp = ignoreRegexp;
057 }
058
059 private boolean arrayContains(Object[] array, Object key)
060 {
061 for (int i = 0; i < array.length; i++)
062 {
063 if (array[i].equals(key))
064 return true;
065 }
066
067 return false;
068 }
069
070 /**
071 * @param name In the format
072 * "net/sourceforge/cobertura/coverage/ClassInstrumenter"
073 */
074 public void visit(int version, int access, String name, String signature,
075 String superName, String[] interfaces)
076 {
077 this.myName = name.replace('/', '.');
078 coverageData = CoverageDataFactory.getInstance().newInstrumentation(
079 this.myName);
080
081 // Do not attempt to instrument interfaces or classes that
082 // have already been instrumented
083 if (((access & ACC_INTERFACE) != 0)
084 || arrayContains(interfaces, hasBeenInstrumented))
085 {
086 super.visit(version, access, name, signature, superName,
087 interfaces);
088 }
089 else
090 {
091 instrument = true;
092
093 // Flag this class as having been instrumented
094 String[] newInterfaces = new String[interfaces.length + 1];
095 System.arraycopy(interfaces, 0, newInterfaces, 0,
096 interfaces.length);
097 newInterfaces[newInterfaces.length - 1] = hasBeenInstrumented;
098
099 super.visit(version, access, name, signature, superName,
100 newInterfaces);
101 }
102 }
103
104 public void visitSource(String source, String debug)
105 {
106 super.visitSource(source, debug);
107 coverageData.setSourceFileName(source);
108 }
109
110 public MethodVisitor visitMethod(final int access, final String name,
111 final String desc, final String signature,
112 final String[] exceptions)
113 {
114 MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
115 exceptions);
116
117 if (!instrument)
118 return mv;
119
120 return mv == null ? null : new MethodInstrumenter(mv, coverageData,
121 this.myName, name, desc, ignoreRegexp);
122 }
123
124 public void visitEnd()
125 {
126 if (instrument && coverageData.getValidLineNumbers().size() == 0)
127 logger.warn("No line number information found for class "
128 + this.myName
129 + ". Perhaps you need to compile with debug=true?");
130 }
131
132 }