001/*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * Copyright (C) 2010 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.coveragedata;
027
028import java.lang.reflect.Method;
029import java.util.Map;
030import java.util.concurrent.ConcurrentHashMap;
031import java.util.logging.Level;
032import java.util.logging.Logger;
033
034
035import net.sourceforge.cobertura.CoverageIgnore;
036import net.sourceforge.cobertura.instrument.pass3.AbstractCodeProvider;
037
038@CoverageIgnore
039public class TouchCollector {
040        private static final Logger logger = Logger.getLogger(TouchCollector.class.getCanonicalName());
041        /*In fact - concurrentHashset*/
042        private static Map<Class<?>,Integer> registeredClasses = new ConcurrentHashMap<Class<?>,Integer>();
043
044        static {
045                ProjectData.getGlobalProjectData(); // To call ProjectData.initialize();
046        }
047        
048        public static synchronized void registerClass(Class<?> classa) {
049                registeredClasses.put(classa,0);
050        }
051
052        public static synchronized void applyTouchesOnProjectData(
053                        ProjectData projectData) {
054                logger.fine("=================== START OF REPORT ======================== ");
055                for (Class<?> c : registeredClasses.keySet()) {
056                        logger.fine("Report: "+c.getName());
057                        ClassData cd=projectData.getOrCreateClassData(c.getName());
058                        applyTouchesToSingleClassOnProjectData(cd, c);
059                }
060                logger.fine("===================  END OF REPORT  ======================== ");
061        }
062
063        private static void applyTouchesToSingleClassOnProjectData(final ClassData classData,final Class<?> c) {
064                logger.finer("----------- "+ c.getCanonicalName() + " ---------------- ");
065                try {
066                        Method m0 = c.getDeclaredMethod(AbstractCodeProvider.COBERTURA_GET_AND_RESET_COUNTERS_METHOD_NAME);
067                        m0.setAccessible(true);
068                        final int[] res=(int[])m0.invoke(null, new Object[]{});
069                        
070                        LightClassmapListener lightClassmap=new ApplyToClassDataLightClassmapListener(classData,res);                   
071                        Method m = c.getDeclaredMethod(AbstractCodeProvider.COBERTURA_CLASSMAP_METHOD_NAME,LightClassmapListener.class);
072                        m.setAccessible(true);
073                        m.invoke(null, lightClassmap);
074                } catch (Exception e) {
075                        logger.log(Level.SEVERE, "Cannot apply touches", e);
076                }
077        }
078        
079        @CoverageIgnore
080        private static class ApplyToClassDataLightClassmapListener implements LightClassmapListener {
081                //private AtomicInteger idProvider=new AtomicInteger(0);
082                private final ClassData classData;
083                private final int[] res;
084                
085                private int currentLine=0;
086                private int jumpsInLine=0;
087                private int switchesInLine=0;
088                
089                private void updateLine(int new_line){
090                        if(new_line!=currentLine){
091                                currentLine=new_line;
092                                jumpsInLine=0;
093                                switchesInLine=0;
094                        }
095                }               
096                
097                public ApplyToClassDataLightClassmapListener(ClassData cd,int[] res) {
098                        classData=cd;
099                        this.res=res;
100                }
101                
102                public void setSource(String source) {
103                        logger.fine("source: "+source);
104                        classData.setSourceFileName(source);
105                        
106                }
107                
108                public void setClazz(Class<?> clazz) {                                  
109                }
110                
111                public void putLineTouchPoint(int classLine, int counterId, String methodName, String methodDescription) {
112                        updateLine(classLine);                          
113                        LineData ld=classData.addLine(classLine, methodName,methodDescription);
114                        ld.touch(res[counterId]);
115                }
116                
117                public void putSwitchTouchPoint(int classLine, int maxBranches, int... counterIds) {
118                        updateLine(classLine);                  
119                        LineData ld=getOrCreateLine(classLine);
120                        int switchId = switchesInLine++;
121                        classData.addLineSwitch(classLine,switchId , 0, counterIds.length-2, maxBranches);
122                        for(int i=0; i < counterIds.length; i++){
123                                ld.touchSwitch(switchId, i-1, res[counterIds[i]]);
124                        }
125                }               
126                
127                public void putJumpTouchPoint(int classLine, int trueCounterId, int falseCounterId) {
128                        updateLine(classLine);                                  
129                        LineData ld=getOrCreateLine(classLine);
130                        int branchId=jumpsInLine++;
131                        classData.addLineJump(classLine, branchId);
132                        ld.touchJump(branchId, true, res[trueCounterId]);
133                        ld.touchJump(branchId, false, res[falseCounterId]);
134                }
135
136                private LineData getOrCreateLine(int classLine) {
137                        LineData ld=classData.getLineData(classLine);
138                        if(ld==null){
139                                ld=classData.addLine(classLine, null, null);
140                        }
141                        return ld;
142                }
143        };
144}