001/* 002 * Cobertura - http://cobertura.sourceforge.net/ 003 * 004 * Copyright (C) 2011 Piotr Tabor 005 * 006 * Note: This file is dual licensed under the GPL and the Apache 007 * Source License (so that it can be used from both the main 008 * Cobertura classes and the ant tasks). 009 * 010 * Cobertura is free software; you can redistribute it and/or modify 011 * it under the terms of the GNU General Public License as published 012 * by the Free Software Foundation; either version 2 of the License, 013 * or (at your option) any later version. 014 * 015 * Cobertura is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 018 * General Public License for more details. 019 * 020 * You should have received a copy of the GNU General Public License 021 * along with Cobertura; if not, write to the Free Software 022 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 023 * USA 024 */ 025 026package net.sourceforge.cobertura.instrument.tp; 027 028import java.util.HashMap; 029import java.util.HashSet; 030import java.util.Iterator; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035import java.util.SortedMap; 036import java.util.TreeMap; 037import java.util.concurrent.atomic.AtomicInteger; 038 039 040import net.sourceforge.cobertura.coveragedata.ClassData; 041import net.sourceforge.cobertura.coveragedata.ProjectData; 042import net.sourceforge.cobertura.instrument.pass2.BuildClassMapClassVisitor; 043 044import org.apache.log4j.Logger; 045import org.objectweb.asm.Label; 046 047/** 048 * <p>This class is a container for informations gathered during class analyzing done by {@link BuildClassMapClassVisitor}.</p> 049 * 050 * 051 * @author piotr.tabor@gmail.com 052 */ 053public class ClassMap { 054 private static final Logger logger=Logger.getLogger(ClassMap.class); 055 /** 056 * Simple name of source-file that was used to generate that value 057 */ 058 private String source; 059 060 /** 061 * We map every eventId that is connected to instruction that created touch-point to the touch-point 062 */ 063 private final Map<Integer,TouchPointDescriptor> eventId2touchPointDescriptor=new HashMap<Integer, TouchPointDescriptor>(); 064 065 /** 066 * Contains map of label into set of {@link JumpTouchPointDescriptor} or {@link SwitchTouchPointDescriptor} that the label could be destination of 067 * 068 * <p>The labels used here are {@link Label} created during {@link BuildClassMapClassVisitor} pass. Don't try to compare it with labels created by other instrumentation passes. 069 * Instead you should use eventId and {@link #eventId2label} to get the label created in the first pass and lookup using the label.</p> 070 */ 071 private final Map<Label, Set<TouchPointDescriptor>> label2sourcePoints=new HashMap<Label, Set<TouchPointDescriptor>>(); 072 073 /** 074 * Maps eventId to code label from BuildClassMapClassInstrumenter pass 075 */ 076 private final Map<Integer,Label> eventId2label = new HashMap<Integer, Label>(); 077 078 /** 079 * List of line numbers (not lineIds) of lines that are not allowed to contain touch-point. This 080 * lines was probably excluded from coverage using 'ignore' stuff. 081 */ 082 private final Set<Integer> blockedLines = new HashSet<Integer>(); 083 084 /** 085 * List of touch-points stored in given line. 086 */ 087 private final SortedMap<Integer, List<TouchPointDescriptor>> line2touchPoints=new TreeMap<Integer, List<TouchPointDescriptor>>(); 088 089 /** 090 * Set of eventIds that has bean already registered. 091 */ 092 private final Set<Integer> alreadyRegisteredEvents = new HashSet<Integer>(); 093 094 /*from duplicate to origin*/ 095 private final Map<Label,Label> labelDuplicates2orginMap=new HashMap<Label, Label>(); 096 private final Map<Label,Set<Label>> labelDuplicates2duplicateMap=new HashMap<Label, Set<Label>>(); 097 098 private String className; 099 100 private int maxCounterId=0; 101 102 public void setSource(String source) { 103 this.source = source; 104 } 105 106 public void registerNewJump(int eventId,int currentLine, Label destinationLabel) { 107 if (alreadyRegisteredEvents.add(eventId)){ 108 logger.debug(className+":"+currentLine+": Registering JUMP ("+eventId+") to "+destinationLabel); 109 JumpTouchPointDescriptor descriptor=new JumpTouchPointDescriptor(eventId,currentLine/*,destinationLabel*/); 110 eventId2touchPointDescriptor.put(eventId, descriptor); 111 getOrCreateSourcePoints(destinationLabel).add(descriptor); 112 getOrCreateLineTouchPoints(currentLine).add(descriptor); 113 }else{ 114 logger.debug(className+":"+currentLine+": NOT registering (already done) JUMP ("+eventId+") to "+destinationLabel); 115 } 116 } 117 118 private List<TouchPointDescriptor> getOrCreateLineTouchPoints(int currentLine) { 119 List<TouchPointDescriptor> res=line2touchPoints.get(currentLine); 120 if (res==null){ 121 res=new LinkedList<TouchPointDescriptor>(); 122 line2touchPoints.put(currentLine,res); 123 } 124 return res; 125 } 126 127 private Set<TouchPointDescriptor> getOrCreateSourcePoints(Label label) { 128 Set<TouchPointDescriptor> res=label2sourcePoints.get(label); 129 if (res==null){ 130 res=new HashSet<TouchPointDescriptor>(); 131 label2sourcePoints.put(label,res); 132 } 133 return res; 134 } 135 136 public void registerNewLabel(int eventId,int currentLine, Label label) { 137 logger.debug(className+":"+currentLine+": Registering label ("+eventId+") "+label); 138 if (alreadyRegisteredEvents.add(eventId)){ 139 eventId2label.put(eventId,label); 140 putIntoDuplicatesMaps(label,label); 141 }else{ 142 putIntoDuplicatesMaps(label, eventId2label.get(eventId)); 143 } 144 } 145 146 public void putIntoDuplicatesMaps(Label label,Label orgin){ 147 labelDuplicates2orginMap.put(label, orgin); //For coherancy 148 Set<Label> list=labelDuplicates2duplicateMap.get(orgin); 149 if(list==null){ 150 list=new HashSet<Label>(); 151 labelDuplicates2duplicateMap.put(orgin,list); 152 } 153 list.add(label); 154 } 155 156 public void registerLineNumber(int eventId,int currentLine, Label label,String methodName, String methodSignature) { 157 logger.debug(className+":"+currentLine+": Registering line ("+eventId+") "+label); 158 if (alreadyRegisteredEvents.add(eventId)){ 159 if (!blockedLines.contains(currentLine)){ 160 LineTouchPointDescriptor line=new LineTouchPointDescriptor(eventId, currentLine,methodName,methodSignature); 161 eventId2label.put(eventId, label); 162 eventId2touchPointDescriptor.put(eventId, line); 163 getOrCreateLineTouchPoints(currentLine).add(line); 164 } 165 } 166 } 167 168 public void unregisterLine(int eventId,int currentLine) { 169 if (alreadyRegisteredEvents.add(eventId)){ 170 blockedLines.add(currentLine); 171 List<TouchPointDescriptor> res=line2touchPoints.get(currentLine); 172 if(res!=null){ 173 Iterator<TouchPointDescriptor> iter=res.iterator(); 174 while(iter.hasNext()){ 175 TouchPointDescriptor desc=iter.next(); 176 if(desc instanceof LineTouchPointDescriptor){ 177 iter.remove(); 178 eventId2touchPointDescriptor.remove(desc.getEventId()); 179 eventId2label.remove(desc.getEventId()); 180 } 181 } 182 } 183 } 184 } 185 186 public void registerSwitch(int eventId,int currentLine, Label def, Label[] labels, String conditionType) { 187 if (alreadyRegisteredEvents.add(eventId)){ 188 SwitchTouchPointDescriptor swi=new SwitchTouchPointDescriptor(eventId, currentLine,def, labels, conditionType); 189 eventId2touchPointDescriptor.put(eventId, swi); 190 getOrCreateLineTouchPoints(currentLine).add(swi); 191 getOrCreateSourcePoints(def).add(swi); 192 for(Label l:labels){ 193// System.out.println("Registering label to switch:"+l); 194 getOrCreateSourcePoints(l).add(swi); 195 } 196 } 197 } 198 199//======================= data retrieval ===================================================== 200 201 202 public Integer getCounterIdForJumpTrue(int eventId) { 203 JumpTouchPointDescriptor jumpTouchPointDescriptor=(JumpTouchPointDescriptor) eventId2touchPointDescriptor.get(eventId); 204 if (jumpTouchPointDescriptor!=null){ 205 return jumpTouchPointDescriptor.getCounterIdForTrue(); 206 } 207 return null; 208 } 209 210 public Integer getCounterIdForJumpFalse(int eventId) { 211 JumpTouchPointDescriptor jumpTouchPointDescriptor=(JumpTouchPointDescriptor) eventId2touchPointDescriptor.get(eventId); 212 if (jumpTouchPointDescriptor!=null){ 213 return jumpTouchPointDescriptor.getCounterIdForFalse(); 214 } 215 return null; 216 } 217 218 public boolean isJumpDestinationLabel(int eventId) { 219 Label label_local=eventId2label.get(eventId); 220 logger.debug("Label found for eventId:"+eventId+":"+label_local); 221 if (labelDuplicates2duplicateMap.containsKey(label_local)) { 222 for (Label label:labelDuplicates2duplicateMap.get(label_local)){ 223 if (label!=null){ 224 Set<TouchPointDescriptor> res=label2sourcePoints.get(label); 225 logger.debug("label2sourcePoints.get("+label+"):"+res); 226 if(res!=null){ 227 for(TouchPointDescriptor r:res){ 228 if(r instanceof JumpTouchPointDescriptor){ 229 return true; 230 } 231 } 232 } 233 } 234 } 235 } 236 return false; 237 } 238 239 public Integer getCounterIdForSwitch(int eventId) { 240 SwitchTouchPointDescriptor point=(SwitchTouchPointDescriptor)eventId2touchPointDescriptor.get(eventId); 241 if (point!=null){ 242 return point.getCounterId(); 243 } 244 return null; 245 } 246 247 public Integer getCounterIdForLineEventId(int eventId) { 248 LineTouchPointDescriptor point=(LineTouchPointDescriptor)eventId2touchPointDescriptor.get(eventId); 249 if (point!=null){ 250 return point.getCounterId(); 251 } 252 return null; 253 } 254 255 /** 256 * Returns map: switchCounterId --> counterId 257 * @param labelEventId 258 * @return 259 */ 260 public Map<Integer, Integer> getBranchLabelDescriptorsForLabelEvent(int labelEventId) { 261 Label label_local=eventId2label.get(labelEventId); 262 if (label_local!=null){ 263 if (labelDuplicates2duplicateMap.containsKey(label_local)) { 264 for (Label label:labelDuplicates2duplicateMap.get(label_local)){ 265 Set<TouchPointDescriptor> list=label2sourcePoints.get(label); 266 if(list!=null){ 267 Map<Integer,Integer> res=new HashMap<Integer, Integer>(); 268 for(TouchPointDescriptor r:list){ 269 if(r instanceof SwitchTouchPointDescriptor){ 270 SwitchTouchPointDescriptor swi=(SwitchTouchPointDescriptor)r; 271 res.put(swi.getCounterId(), swi.getCounterIdForLabel(label)); 272 } 273 } 274 return res; 275 } 276 } 277 } 278 } 279 return null; 280 } 281 282 /** 283 * Iterates over all touch-points created during class analysis and assigns 284 * hit-counter identifiers to each of the touchpoint (some of them needs mode then one 285 * hit-counter). 286 * 287 * <p>This class assign hit-counter ids to each touch-point and upgrades maxCounterId to 288 * reflect the greatest assigned Id. 289 */ 290 public void assignCounterIds(){ 291 AtomicInteger idGenerator=new AtomicInteger(0); 292 for(List<TouchPointDescriptor> tpd:line2touchPoints.values()){ 293 for(TouchPointDescriptor t:tpd){ 294 t.assignCounters(idGenerator); 295 } 296 } 297 maxCounterId=idGenerator.get(); 298 } 299 300 public int getMaxCounterId() { 301 return maxCounterId; 302 } 303 304 public String getClassName() { 305 return className; 306 } 307 308 public void setClassName(String className) { 309 this.className = className; 310 } 311 312 public String getSource() { 313 return source; 314 } 315 316 public List<TouchPointDescriptor> getTouchPointsInLineOrder() { 317 LinkedList<TouchPointDescriptor> res=new LinkedList<TouchPointDescriptor>(); 318 for(List<TouchPointDescriptor> tpd:line2touchPoints.values()){ 319 for(TouchPointDescriptor t:tpd){ 320 if(tpd instanceof LineTouchPointDescriptor){ 321 res.add(t); 322 } 323 } 324 for(TouchPointDescriptor t:tpd){ 325 if(!(tpd instanceof LineTouchPointDescriptor)){ 326 res.add(t); 327 } 328 } 329 } 330 return res; 331 } 332 333 /** 334 * Upgrades {@link ProjectData} to contain all information fount in class during class instrumentation. 335 * 336 * <p>I don't like the idea o creating sar file during the instrumentation, but we need to do it, 337 * to be compatible with tools that expact that (such a cobertura-maven-plugin)</p> 338 * @param projectData 339 */ 340 public ClassData applyOnProjectData(ProjectData projectData, boolean instrumented){ 341 ClassData classData=projectData.getOrCreateClassData(className.replace('/','.')); 342 if(source!=null){ 343 classData.setSourceFileName(source); 344 } 345 if (instrumented){ 346 classData.setContainsInstrumentationInfo(); 347 int lastLine=0; 348 int jumpsInLine=0; 349 int toucesInLine=0; 350 351 for(TouchPointDescriptor tpd:getTouchPointsInLineOrder()){ 352 if(tpd.getLineNumber()!=lastLine){ 353 jumpsInLine=0; 354 toucesInLine=0; 355 lastLine=tpd.getLineNumber(); 356 } 357 if(tpd instanceof LineTouchPointDescriptor){ 358 classData.addLine(tpd.getLineNumber(), ((LineTouchPointDescriptor) tpd).getMethodName(), ((LineTouchPointDescriptor) tpd).getMethodSignature()); 359 }else if(tpd instanceof JumpTouchPointDescriptor){ 360 classData.addLineJump(tpd.getLineNumber(), jumpsInLine++); 361 }else if(tpd instanceof SwitchTouchPointDescriptor){ 362 int countersCnt=((SwitchTouchPointDescriptor)tpd).getCountersForLabelsCnt(); 363 //TODO(ptab): instead of Integer.MAX_VALUE should be length of Enum. 364 classData.addLineSwitch(tpd.getLineNumber(), toucesInLine++,0, countersCnt-2, Integer.MAX_VALUE); 365 } 366 } 367 } 368 return classData; 369 } 370 371}