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 }