org.sonar.plugins.cxx.cppncss.CxxCppNcssSensor.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.cxx.cppncss.CxxCppNcssSensor.java

Source

/*
 * Sonar Cxx Plugin, open source software quality management tool.
 * Copyright (C) 2010 - 2011, Neticoa SAS France - Tous droits rservs.
 * Author(s) : Franck Bonin, Neticoa SAS France.
 *
 * Sonar Cxx Plugin is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * Sonar Cxx Plugin 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sonar Cxx Plugin; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.plugins.cxx.cppncss;

import java.io.File;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.stream.XMLStreamException;

import org.apache.commons.lang.StringUtils;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.measures.RangeDistributionBuilder;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.StaxParser;
import org.sonar.api.utils.XmlParserException;
import org.sonar.plugins.cxx.CxxFile;
import org.sonar.plugins.cxx.CxxPlugin;
import org.sonar.plugins.cxx.utils.ReportsHelper;

public class CxxCppNcssSensor extends ReportsHelper implements Sensor {

    private static Logger logger = LoggerFactory.getLogger(CxxCppNcssSensor.class);

    public boolean shouldExecuteOnProject(Project project) {
        return CxxPlugin.KEY.equals(project.getLanguageKey());
    }

    public static final String GROUP_ID = "org.codehaus.mojo";
    public static final String ARTIFACT_ID = "cxx-maven-plugin";
    public static final String SENSOR_ID = "cppncss";
    public static final String DEFAULT_GCOVR_REPORTS_DIR = "cppncss-reports";
    public static final String DEFAULT_REPORTS_FILE_PATTERN = "**/cppncss-result-*.xml";

    @Override
    protected String getArtifactId() {
        return ARTIFACT_ID;
    }

    @Override
    protected String getSensorId() {
        return SENSOR_ID;
    }

    @Override
    protected String getDefaultReportsDir() {
        return DEFAULT_GCOVR_REPORTS_DIR;
    }

    @Override
    protected String getDefaultReportsFilePattern() {
        return DEFAULT_REPORTS_FILE_PATTERN;
    }

    @Override
    protected String getGroupId() {
        return GROUP_ID;
    }

    @Override
    protected Logger getLogger() {
        return logger;
    }

    public void analyse(Project project, SensorContext context) {
        File reportDirectory = getReportsDirectory(project);
        if (reportDirectory != null) {
            File reports[] = getReports(project, reportDirectory);
            for (File report : reports) {
                parseReport(project, report, context);
            }
        }
    }

    private final static Number[] METHODS_DISTRIB_BOTTOM_LIMITS = { 1, 2, 4, 6, 8, 10, 12 };
    private final static Number[] FILE_DISTRIB_BOTTOM_LIMITS = { 0, 5, 10, 20, 30, 60, 90 };
    private final static Number[] CLASS_DISTRIB_BOTTOM_LIMITS = { 0, 5, 10, 20, 30, 60, 90 };

    private class FileData {
        FileData(String name) {
            FileName = name;
        }

        private class classData {
            private Map<String, Integer> ComplexityPerMethod = new HashMap<String, Integer>();
            private Integer ClassComplexity = new Integer(0);
            private Integer NbClassMethod = new Integer(0);

            private void putComplexityPerMethod(String name, Integer Complexity) {
                NbClassMethod++;
                ClassComplexity += Complexity;
                Integer c = ComplexityPerMethod.get(name);
                if (c == null) {
                    c = new Integer(Complexity);
                    ComplexityPerMethod.put(name, c);
                } else {
                    // alert !
                }
            }

            public Integer getClassComplexity() {
                return ClassComplexity;
            }

            public Collection<Integer> complexityPerMethodValues() {
                return ComplexityPerMethod.values();
            }
        }

        public void addMethod(String clazz, String name, Integer Complexity) {
            NbMethod++;
            FileComplexity += Complexity;

            classData ComplexityPerMethod = ComplexityPerClassPerMethod.get(clazz);
            if (ComplexityPerMethod == null) {
                ComplexityPerMethod = new classData();
                ComplexityPerClassPerMethod.put(clazz, ComplexityPerMethod);
            }
            ComplexityPerMethod.putComplexityPerMethod(name, Complexity);
        }

        public void saveMetric(Project project, SensorContext context) {
            CxxFile file = CxxFile.fromFileName(project, FileName, getReportsIncludeSourcePath(project), false);
            if (context.getResource(file) != null) {

                RangeDistributionBuilder complexityMethodsDistribution = new RangeDistributionBuilder(
                        CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, METHODS_DISTRIB_BOTTOM_LIMITS);
                RangeDistributionBuilder complexityFileDistribution = new RangeDistributionBuilder(
                        CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, FILE_DISTRIB_BOTTOM_LIMITS);
                RangeDistributionBuilder complexityClassDistribution = new RangeDistributionBuilder(
                        CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION, CLASS_DISTRIB_BOTTOM_LIMITS);

                complexityFileDistribution.add(FileComplexity);
                for (classData cd : ComplexityPerClassPerMethod.values()) {
                    complexityClassDistribution.add(cd.getClassComplexity());
                    for (Integer c : cd.complexityPerMethodValues()) {
                        complexityMethodsDistribution.add(c);
                    }
                }
                context.saveMeasure(file, CoreMetrics.FUNCTIONS, NbMethod.doubleValue());
                context.saveMeasure(file, CoreMetrics.COMPLEXITY, FileComplexity.doubleValue());
                //logger.info("File saveMeasure NbMethod={}, FileComplexity={}", NbMethod, FileComplexity);

                context.saveMeasure(file,
                        complexityMethodsDistribution.build().setPersistenceMode(PersistenceMode.MEMORY));
                //logger.info("File complexityMethodsDistribution={}", complexityMethodsDistribution.build());
                context.saveMeasure(file,
                        complexityClassDistribution.build().setPersistenceMode(PersistenceMode.MEMORY));
                //logger.info("File complexityClassDistribution={}", complexityClassDistribution.build());
                context.saveMeasure(file,
                        complexityFileDistribution.build().setPersistenceMode(PersistenceMode.MEMORY));
                //logger.info("File complexityFileDistribution={}", complexityFileDistribution.build());
            }
        }

        private String FileName;
        private Integer NbMethod = new Integer(0);
        private Map<String, classData> ComplexityPerClassPerMethod = new HashMap<String, classData>();
        private Integer FileComplexity = new Integer(0);
    }

    private void parseReport(final Project project, File xmlFile, final SensorContext context) {
        try {
            logger.info("parsing {}", xmlFile);
            StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() {

                public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
                    try {
                        Map<String, FileData> fileDataPerFilename = new HashMap<String, FileData>();
                        rootCursor.advance();
                        collectMeasure(project, rootCursor.childElementCursor("measure"), fileDataPerFilename,
                                context);
                        for (FileData d : fileDataPerFilename.values()) {
                            d.saveMetric(project, context);
                        }
                    } catch (ParseException e) {
                        throw new XMLStreamException(e);
                    }
                }
            });
            parser.parse(xmlFile);
        } catch (XMLStreamException e) {
            throw new XmlParserException(e);
        }
    }

    private void collectMeasure(Project project, SMInputCursor mesure, Map<String, FileData> fileDataPerFilename,
            SensorContext context) throws ParseException, XMLStreamException {
        while (mesure.getNext() != null) {
            String type = mesure.getAttrValue("type");
            logger.debug("collect Mesure type = {}", type);
            if (!StringUtils.isEmpty(type)) {

                SMInputCursor mesureChild = mesure.childElementCursor().advance();

                // collect labels   
                List<String> valueLabels = new ArrayList<String>();
                if (mesureChild.getLocalName().equalsIgnoreCase("labels")) {
                    logger.debug("collect labels");
                    SMInputCursor itemValueLabels = mesureChild.childElementCursor("label");
                    while (itemValueLabels.getNext() != null) {
                        String label = itemValueLabels.getElemStringValue();
                        logger.debug("new label = {}", label);
                        valueLabels.add(label);
                    }
                }

                // collect only function metrics
                if (type.equalsIgnoreCase("Function")) {
                    collectFunctionItems(project, fileDataPerFilename, valueLabels, mesureChild, context);
                } else if (type.equalsIgnoreCase("File")) {
                    // nothing
                }
            }
        }
    }

    private void collectFunctionItems(Project project, Map<String, FileData> fileDataPerFilename,
            List<String> valueLabels, SMInputCursor items, SensorContext context)
            throws ParseException, XMLStreamException {

        while (items.getNext() != null) {

            String name = items.getAttrValue("name");
            //logger.info("collect Funtion name = {}", name);
            if (!StringUtils.isEmpty(name)) {
                String loc[] = name.split(" at ");
                String fullFuncName = loc[0];
                String fullFileName = loc[1];

                loc = fullFuncName.split("::");
                String className = (loc.length > 1) ? loc[0] : "GLOBAL";
                String funcName = (loc.length > 1) ? loc[1] : loc[0];
                loc = fullFileName.split(":");
                String fileName = loc[0];

                Object tab[] = { fileName, className, funcName };
                logger.debug("collect Funtion : fileName = {}, className = {}, funcName = {}", tab);
                FileData data = fileDataPerFilename.get(fileName);
                if (data == null) {
                    data = new FileData(fileName);
                    fileDataPerFilename.put(fileName, data);
                }

                SMInputCursor value = items.childElementCursor("value");
                int i = 0;
                while (value.getNext() != null) {
                    if (valueLabels.get(i).equalsIgnoreCase("CCN")) {
                        String MethodeComplexity = value.getElemStringValue();
                        if (!StringUtils.isEmpty(MethodeComplexity)) {
                            //logger.info("Found Funtion CCN = {}", MethodeComplexity);
                            data.addMethod(className, funcName, Integer.parseInt(MethodeComplexity.trim()));
                        }
                    }
                    i++;
                }
            }
        }
    }
}