com.pablissimo.sonar.LCOVParserImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.pablissimo.sonar.LCOVParserImpl.java

Source

/*
 * SonarQube JavaScript Plugin
 * Copyright (C) 2011 SonarSource and Eriks Nukis
 * 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 com.pablissimo.sonar;

import com.google.common.base.Objects;
import com.google.common.collect.Maps;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.measures.CoverageMeasuresBuilder;
import org.sonar.api.utils.SonarException;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
 */
public final class LCOVParserImpl implements LCOVParser {

    private static final Logger LOG = LoggerFactory.getLogger(LCOVParserImpl.class);

    private static final String SF = "SF:";
    private static final String DA = "DA:";
    private static final String BRDA = "BRDA:";

    private final File moduleBaseDir;

    public LCOVParserImpl(File moduleBaseDir) {
        this.moduleBaseDir = moduleBaseDir;
    }

    public Map<String, CoverageMeasuresBuilder> parseFile(File file) {
        final List<String> lines;
        try {
            lines = FileUtils.readLines(file);
        } catch (IOException e) {
            throw new SonarException("Could not read content from file: " + file, e);
        }
        return parse(lines);
    }

    public Map<String, CoverageMeasuresBuilder> parse(List<String> lines) {
        final Map<String, FileData> files = Maps.newHashMap();
        FileData fileData = new FileData();

        for (String line : lines) {
            if (line.startsWith(SF)) {
                // SF:<absolute path to the source file>
                String filePath = line.substring(SF.length());

                // some tools (like Istanbul, Karma) provide relative paths, so let's consider them relative to project directory
                try {
                    filePath = this.getIOFile(moduleBaseDir, filePath).getCanonicalPath();
                } catch (IOException e) {
                    filePath = "";
                    LOG.error("Unable to retreive coverage info for file {}, because: {}", filePath, e);
                }

                fileData = files.get(filePath);
                if (fileData == null) {
                    fileData = new FileData();
                    files.put(filePath, fileData);
                }

            } else if (line.startsWith(DA)) {
                // DA:<line number>,<execution count>[,<checksum>]
                String execution = line.substring(DA.length());
                String executionCount = execution.substring(execution.indexOf(',') + 1);
                String lineNumber = execution.substring(0, execution.indexOf(','));

                fileData.addLine(Integer.valueOf(lineNumber), Integer.valueOf(executionCount));

            } else if (line.startsWith(BRDA)) {
                // BRDA:<line number>,<block number>,<branch number>,<taken>
                String[] tokens = line.substring(BRDA.length()).trim().split(",");
                String lineNumber = tokens[0];
                String branchNumber = tokens[1] + tokens[2];
                String taken = tokens[3];

                fileData.addBranch(Integer.valueOf(lineNumber), branchNumber,
                        "-".equals(taken) ? 0 : Integer.valueOf(taken));
            }
        }

        Map<String, CoverageMeasuresBuilder> coveredFiles = Maps.newHashMap();
        for (Map.Entry<String, FileData> e : files.entrySet()) {
            coveredFiles.put(e.getKey(), e.getValue().convert());
        }
        return coveredFiles;
    }

    /**
     * Returns a java.io.File for the given path.
     * If path is not absolute, returns a File with module base directory as parent path.
     */
    protected File getIOFile(File baseDir, String path) {
        File file = new File(path);
        if (!file.isAbsolute()) {
            file = new File(baseDir, path);
        }

        return file;
    }

    private static class FileData {
        /**
         * line number -> branch number -> taken
         */
        private Map<Integer, Map<String, Integer>> branches = Maps.newHashMap();

        /**
         * line number -> execution count
         */
        private Map<Integer, Integer> hits = Maps.newHashMap();

        public void addBranch(Integer lineNumber, String branchNumber, Integer taken) {
            Map<String, Integer> branchesForLine = branches.get(lineNumber);
            if (branchesForLine == null) {
                branchesForLine = Maps.newHashMap();
                branches.put(lineNumber, branchesForLine);
            }
            Integer currentValue = branchesForLine.get(branchNumber);
            branchesForLine.put(branchNumber, Objects.firstNonNull(currentValue, 0) + taken);
        }

        public void addLine(Integer lineNumber, Integer executionCount) {
            Integer currentValue = hits.get(lineNumber);
            hits.put(lineNumber, Objects.firstNonNull(currentValue, 0) + executionCount);
        }

        public CoverageMeasuresBuilder convert() {
            CoverageMeasuresBuilder result = CoverageMeasuresBuilder.create();
            for (Map.Entry<Integer, Integer> e : hits.entrySet()) {
                result.setHits(e.getKey(), e.getValue());
            }
            for (Map.Entry<Integer, Map<String, Integer>> e : branches.entrySet()) {
                int conditions = e.getValue().size();
                int covered = 0;
                for (Integer taken : e.getValue().values()) {
                    if (taken > 0) {
                        covered++;
                    }
                }
                result.setConditions(e.getKey(), conditions, covered);
            }
            return result;
        }
    }

}