org.sonar.plugins.flex.flexmetrics.FlexMetricsParser.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.flex.flexmetrics.FlexMetricsParser.java

Source

/*
 * Sonar Flex Plugin
 * Copyright (C) 2010 SonarSource
 * dev@sonar.codehaus.org
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */

package org.sonar.plugins.flex.flexmetrics;

import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
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.Resource;
import org.sonar.api.utils.ParsingUtils;
import org.sonar.api.utils.StaxParser;
import org.sonar.plugins.flex.FlexFile;
import org.sonar.plugins.flex.FlexPackage;
import org.sonar.plugins.flex.FlexUtils;

import java.io.File;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;

import javax.xml.stream.XMLStreamException;

public class FlexMetricsParser {

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

    private SensorContext context;

    public FlexMetricsParser(SensorContext context) {
        this.context = context;
    }

    public void parse(File xmlFile) {
        StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() {
            public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
                rootCursor.advance(); // <javancss>
                SMInputCursor cursor = rootCursor.childElementCursor();
                while (cursor.getNext() != null) {
                    String nodeName = cursor.getLocalName();
                    if (StringUtils.equals("packages", nodeName)) {
                        SMInputCursor packagesCursor = cursor.childElementCursor("package");
                        processPackages(packagesCursor);
                    } else if (StringUtils.equals("objects", nodeName)) {
                        SMInputCursor objectsCursor = cursor.childElementCursor("object");
                        processObjects(objectsCursor);
                    } else if (StringUtils.equals("functions", nodeName)) {
                        SMInputCursor functionsCursor = cursor.childElementCursor("function");
                        processFunctions(functionsCursor);
                    }
                }
            }
        });

        try {
            parser.parse(xmlFile);
        } catch (XMLStreamException e) {
            FlexUtils.LOG.error("Error parsing file : " + xmlFile, e);
        }
    }

    private void processPackages(SMInputCursor packagesCursor) throws XMLStreamException {
        while (packagesCursor.getNext() != null) {
            Map<String, String> values = processChildren(packagesCursor);

            String packageName = values.get("name");
            Resource flexPackage = new FlexPackage(packageName);

            context.saveMeasure(flexPackage, CoreMetrics.PACKAGES, 1.0);
        }
    }

    private void processObjects(SMInputCursor objectsCursor) throws XMLStreamException {
        while (objectsCursor.getNext() != null) {
            Map<String, String> values = processChildren(objectsCursor);

            String fileName = values.get("name");
            Resource flexFile = new FlexFile(fileName);

            double lines = parseDouble(values.get("javadocs")) + parseDouble(values.get("single_comment_lines"))
                    + parseDouble(values.get("multi_comment_lines"));

            context.saveMeasure(flexFile, CoreMetrics.COMMENT_LINES, lines);
            context.saveMeasure(flexFile, CoreMetrics.CLASSES, 1.0);
            context.saveMeasure(flexFile, CoreMetrics.FILES, 1.0);
            context.saveMeasure(flexFile, CoreMetrics.NCLOC, parseDouble(values.get("ncss")));
            context.saveMeasure(flexFile, CoreMetrics.FUNCTIONS, parseDouble(values.get("functions")));
        }
    }

    private void processFunctions(SMInputCursor functionsCursor) throws XMLStreamException {
        Map<String, Integer> ccnCountPerClass = new HashMap<String, Integer>();
        Map<String, RangeDistributionBuilder> ccnDistributionPerClass = new HashMap<String, RangeDistributionBuilder>();

        while (functionsCursor.getNext() != null) {
            Map<String, String> values = processChildren(functionsCursor);
            String fullFunctionName = values.get("name");
            Integer ccnForFunction = Integer.valueOf(values.get("ccn"));
            String packageAndClassName = getPackageAndClassFromFunction(fullFunctionName);
            addUpComplexityToClass(ccnCountPerClass, ccnForFunction, packageAndClassName);
            addUpComplexityToClassDistribution(ccnDistributionPerClass, ccnForFunction, packageAndClassName);
        }

        for (Map.Entry<String, Integer> entry : ccnCountPerClass.entrySet()) {
            String fullname = entry.getKey();
            Integer ccnForClass = entry.getValue();
            context.saveMeasure(new FlexFile(fullname), CoreMetrics.COMPLEXITY, ccnForClass.doubleValue());
            RangeDistributionBuilder ccnDistribution = new RangeDistributionBuilder(
                    CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION, CLASSES_DISTRIB_BOTTOM_LIMITS);
            ccnDistribution.add(ccnForClass.doubleValue());
            context.saveMeasure(new FlexFile(fullname),
                    ccnDistribution.build().setPersistenceMode(PersistenceMode.MEMORY));
        }
        for (Map.Entry<String, RangeDistributionBuilder> entry : ccnDistributionPerClass.entrySet()) {
            String fullname = entry.getKey();
            RangeDistributionBuilder ccnDistributionForClass = entry.getValue();
            context.saveMeasure(new FlexFile(fullname),
                    ccnDistributionForClass.build().setPersistenceMode(PersistenceMode.MEMORY));
        }
    }

    private static void addUpComplexityToClass(Map<String, Integer> ccnCountperClass, Integer ccnForFunction,
            String packageAndClassName) {
        Integer ccnSum = ccnCountperClass.get(packageAndClassName);
        if (ccnSum == null) {
            ccnSum = 0;
        }
        ccnSum += ccnForFunction;
        ccnCountperClass.put(packageAndClassName, ccnSum);
    }

    private static void addUpComplexityToClassDistribution(
            Map<String, RangeDistributionBuilder> ccnDistributionperClass, Integer ccnForFunction,
            String packageAndClassName) {
        RangeDistributionBuilder ccnDistribution = ccnDistributionperClass.get(packageAndClassName);
        if (ccnDistribution == null) {
            ccnDistribution = new RangeDistributionBuilder(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION,
                    FUNCTIONS_DISTRIB_BOTTOM_LIMITS);
            ccnDistributionperClass.put(packageAndClassName, ccnDistribution);
        }
        ccnDistribution.add(ccnForFunction);
    }

    static String getPackageAndClassFromFunction(String functionName) {
        return StringUtils.substringBefore(functionName, "::");
    }

    private static Map<String, String> processChildren(SMInputCursor cursor) throws XMLStreamException {
        SMInputCursor objectCursor = cursor.childElementCursor();
        Map<String, String> values = Maps.newHashMap();
        while (objectCursor.getNext() != null) {
            String nodeName = objectCursor.getLocalName();
            values.put(nodeName, objectCursor.collectDescendantText());
        }
        return values;
    }

    private static double parseDouble(String value) {
        if (StringUtils.isBlank(value)) {
            return 0.0;
        }
        try {
            return ParsingUtils.parseNumber(value);
        } catch (ParseException e) {
            return 0.0;
        }
    }
}