/* * Cobertura - http://cobertura.sourceforge.net/ * * Copyright (C) 2011 Piotr Tabor * * Note: This file is dual licensed under the GPL and the Apache * Source License (so that it can be used from both the main * Cobertura classes and the ant tasks). * * Cobertura is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * Cobertura is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Cobertura; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ package net.sourceforge.cobertura.instrument.tp; import net.sourceforge.cobertura.coveragedata.ClassData; import net.sourceforge.cobertura.coveragedata.ProjectData; import net.sourceforge.cobertura.instrument.pass2.BuildClassMapClassVisitor; import org.apache.log4j.Logger; import org.objectweb.asm.Label; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * <p>This class is a container for informations gathered during class analyzing done by {@link BuildClassMapClassVisitor}.</p> * * @author piotr.tabor@gmail.com */ public class ClassMap { private static final Logger logger = Logger.getLogger(ClassMap.class); /** * Simple name of source-file that was used to generate that value */ private String source; /** * We map every eventId that is connected to instruction that created touch-point to the touch-point */ private final Map<Integer, TouchPointDescriptor> eventId2touchPointDescriptor = new HashMap<Integer, TouchPointDescriptor>(); /** * Contains map of label into set of {@link JumpTouchPointDescriptor} or {@link SwitchTouchPointDescriptor} that the label could be destination of * <p/> * <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. * Instead you should use eventId and {@link #eventId2label} to get the label created in the first pass and lookup using the label.</p> */ private final Map<Label, Set<TouchPointDescriptor>> label2sourcePoints = new HashMap<Label, Set<TouchPointDescriptor>>(); /** * Maps eventId to code label from BuildClassMapClassInstrumenter pass */ private final Map<Integer, Label> eventId2label = new HashMap<Integer, Label>(); /** * List of line numbers (not lineIds) of lines that are not allowed to contain touch-point. This * lines was probably excluded from coverage using 'ignore' stuff. */ private final Set<Integer> blockedLines = new HashSet<Integer>(); /** * List of touch-points stored in given line. */ private final SortedMap<Integer, List<TouchPointDescriptor>> line2touchPoints = new TreeMap<Integer, List<TouchPointDescriptor>>(); /** * Set of eventIds that has bean already registered. */ private final Set<Integer> alreadyRegisteredEvents = new HashSet<Integer>(); /*from duplicate to origin*/ private final Map<Label, Label> labelDuplicates2orginMap = new HashMap<Label, Label>(); private final Map<Label, Set<Label>> labelDuplicates2duplicateMap = new HashMap<Label, Set<Label>>(); private String className; private int maxCounterId = 0; public void setSource(String source) { this.source = source; } public void registerNewJump(int eventId, int currentLine, Label destinationLabel) { if (alreadyRegisteredEvents.add(eventId)) { logger.debug(className + ":" + currentLine + ": Registering JUMP (" + eventId + ") to " + destinationLabel); JumpTouchPointDescriptor descriptor = new JumpTouchPointDescriptor( eventId, currentLine/*,destinationLabel*/); eventId2touchPointDescriptor.put(eventId, descriptor); getOrCreateSourcePoints(destinationLabel).add(descriptor); getOrCreateLineTouchPoints(currentLine).add(descriptor); } else { logger.debug(className + ":" + currentLine + ": NOT registering (already done) JUMP (" + eventId + ") to " + destinationLabel); } } private List<TouchPointDescriptor> getOrCreateLineTouchPoints( int currentLine) { List<TouchPointDescriptor> res = line2touchPoints.get(currentLine); if (res == null) { res = new LinkedList<TouchPointDescriptor>(); line2touchPoints.put(currentLine, res); } return res; } private Set<TouchPointDescriptor> getOrCreateSourcePoints(Label label) { Set<TouchPointDescriptor> res = label2sourcePoints.get(label); if (res == null) { res = new HashSet<TouchPointDescriptor>(); label2sourcePoints.put(label, res); } return res; } public void registerNewLabel(int eventId, int currentLine, Label label) { logger.debug(className + ":" + currentLine + ": Registering label (" + eventId + ") " + label); if (alreadyRegisteredEvents.add(eventId)) { eventId2label.put(eventId, label); putIntoDuplicatesMaps(label, label); } else { putIntoDuplicatesMaps(label, eventId2label.get(eventId)); } } public void putIntoDuplicatesMaps(Label label, Label orgin) { labelDuplicates2orginMap.put(label, orgin); //For coherancy Set<Label> list = labelDuplicates2duplicateMap.get(orgin); if (list == null) { list = new HashSet<Label>(); labelDuplicates2duplicateMap.put(orgin, list); } list.add(label); } public void registerLineNumber(int eventId, int currentLine, Label label, String methodName, String methodSignature) { logger.debug(className + ":" + currentLine + ": Registering line (" + eventId + ") " + label); if (alreadyRegisteredEvents.add(eventId)) { if (!blockedLines.contains(currentLine)) { LineTouchPointDescriptor line = new LineTouchPointDescriptor( eventId, currentLine, methodName, methodSignature); eventId2label.put(eventId, label); eventId2touchPointDescriptor.put(eventId, line); getOrCreateLineTouchPoints(currentLine).add(line); } } } public void unregisterLine(int eventId, int currentLine) { if (alreadyRegisteredEvents.add(eventId)) { blockedLines.add(currentLine); List<TouchPointDescriptor> res = line2touchPoints.get(currentLine); if (res != null) { Iterator<TouchPointDescriptor> iter = res.iterator(); while (iter.hasNext()) { TouchPointDescriptor desc = iter.next(); if (desc instanceof LineTouchPointDescriptor) { iter.remove(); eventId2touchPointDescriptor.remove(desc.getEventId()); eventId2label.remove(desc.getEventId()); } } } } } public void registerSwitch(int eventId, int currentLine, Label def, Label[] labels, String conditionType) { if (alreadyRegisteredEvents.add(eventId)) { SwitchTouchPointDescriptor swi = new SwitchTouchPointDescriptor( eventId, currentLine, def, labels, conditionType); eventId2touchPointDescriptor.put(eventId, swi); getOrCreateLineTouchPoints(currentLine).add(swi); getOrCreateSourcePoints(def).add(swi); for (Label l : labels) { // System.out.println("Registering label to switch:"+l); getOrCreateSourcePoints(l).add(swi); } } } //======================= data retrieval ===================================================== public Integer getCounterIdForJumpTrue(int eventId) { if (eventId2touchPointDescriptor.get(eventId) instanceof JumpTouchPointDescriptor) { JumpTouchPointDescriptor jumpTouchPointDescriptor = (JumpTouchPointDescriptor) eventId2touchPointDescriptor .get(eventId); if (jumpTouchPointDescriptor != null) { return jumpTouchPointDescriptor.getCounterIdForTrue(); } } return null; } public Integer getCounterIdForJumpFalse(int eventId) { if (eventId2touchPointDescriptor.get(eventId) instanceof JumpTouchPointDescriptor) { JumpTouchPointDescriptor jumpTouchPointDescriptor = (JumpTouchPointDescriptor) eventId2touchPointDescriptor .get(eventId); if (jumpTouchPointDescriptor != null) { return jumpTouchPointDescriptor.getCounterIdForFalse(); } } return null; } public boolean isJumpDestinationLabel(int eventId) { Label label_local = eventId2label.get(eventId); logger.debug("Label found for eventId:" + eventId + ":" + label_local); if (labelDuplicates2duplicateMap.containsKey(label_local)) { for (Label label : labelDuplicates2duplicateMap.get(label_local)) { if (label != null) { Set<TouchPointDescriptor> res = label2sourcePoints .get(label); logger .debug("label2sourcePoints.get(" + label + "):" + res); if (res != null) { for (TouchPointDescriptor r : res) { if (r instanceof JumpTouchPointDescriptor) { return true; } } } } } } return false; } public Integer getCounterIdForSwitch(int eventId) { if (eventId2touchPointDescriptor.get(eventId) instanceof SwitchTouchPointDescriptor) { SwitchTouchPointDescriptor point = (SwitchTouchPointDescriptor) eventId2touchPointDescriptor .get(eventId); if (point != null) { return point.getCounterId(); } } return null; } public Integer getCounterIdForLineEventId(int eventId) { if (eventId2touchPointDescriptor.get(eventId) instanceof LineTouchPointDescriptor) { LineTouchPointDescriptor point = (LineTouchPointDescriptor) eventId2touchPointDescriptor .get(eventId); if (point != null) { return point.getCounterId(); } } return null; } /** * Returns map: switchCounterId --> counterId * * @param labelEventId * * @return */ public Map<Integer, Integer> getBranchLabelDescriptorsForLabelEvent( int labelEventId) { Label label_local = eventId2label.get(labelEventId); if (label_local != null) { if (labelDuplicates2duplicateMap.containsKey(label_local)) { for (Label label : labelDuplicates2duplicateMap .get(label_local)) { Set<TouchPointDescriptor> list = label2sourcePoints .get(label); if (list != null) { Map<Integer, Integer> res = new HashMap<Integer, Integer>(); for (TouchPointDescriptor r : list) { if (r instanceof SwitchTouchPointDescriptor) { SwitchTouchPointDescriptor swi = (SwitchTouchPointDescriptor) r; res.put(swi.getCounterId(), swi .getCounterIdForLabel(label)); } } return res; } } } } return null; } /** * Iterates over all touch-points created during class analysis and assigns * hit-counter identifiers to each of the touchpoint (some of them needs mode then one * hit-counter). * <p/> * <p>This class assign hit-counter ids to each touch-point and upgrades maxCounterId to * reflect the greatest assigned Id. */ public void assignCounterIds() { AtomicInteger idGenerator = new AtomicInteger(0); for (List<TouchPointDescriptor> tpd : line2touchPoints.values()) { for (TouchPointDescriptor t : tpd) { t.assignCounters(idGenerator); } } maxCounterId = idGenerator.get(); } public int getMaxCounterId() { return maxCounterId; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getSource() { return source; } public List<TouchPointDescriptor> getTouchPointsInLineOrder() { LinkedList<TouchPointDescriptor> res = new LinkedList<TouchPointDescriptor>(); for (List<TouchPointDescriptor> tpd : line2touchPoints.values()) { for (TouchPointDescriptor t : tpd) { if (tpd instanceof LineTouchPointDescriptor) { res.add(t); } } for (TouchPointDescriptor t : tpd) { if (!(tpd instanceof LineTouchPointDescriptor)) { res.add(t); } } } return res; } /** * Upgrades {@link ProjectData} to contain all information fount in class during class instrumentation. * <p/> * <p>I don't like the idea o creating sar file during the instrumentation, but we need to do it, * to be compatible with tools that expact that (such a cobertura-maven-plugin)</p> * * @param projectData */ public ClassData applyOnProjectData(ProjectData projectData, boolean instrumented) { ClassData classData = projectData.getOrCreateClassData(className .replace('/', '.')); if (source != null) { classData.setSourceFileName(source); } if (instrumented) { classData.setContainsInstrumentationInfo(); int lastLine = 0; int jumpsInLine = 0; int toucesInLine = 0; for (TouchPointDescriptor tpd : getTouchPointsInLineOrder()) { if (tpd.getLineNumber() != lastLine) { jumpsInLine = 0; toucesInLine = 0; lastLine = tpd.getLineNumber(); } if (tpd instanceof LineTouchPointDescriptor) { classData.addLine(tpd.getLineNumber(), ((LineTouchPointDescriptor) tpd).getMethodName(), ((LineTouchPointDescriptor) tpd) .getMethodSignature()); } else if (tpd instanceof JumpTouchPointDescriptor) { classData.addLineJump(tpd.getLineNumber(), jumpsInLine++); } else if (tpd instanceof SwitchTouchPointDescriptor) { int countersCnt = ((SwitchTouchPointDescriptor) tpd) .getCountersForLabelsCnt(); //TODO(ptab): instead of Integer.MAX_VALUE should be length of Enum. classData.addLineSwitch(tpd.getLineNumber(), toucesInLine++, 0, countersCnt - 2, Integer.MAX_VALUE); } } } return classData; } }