org.sonar.plugins.csharp.gallio.results.coverage.CoverageResultParser.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.csharp.gallio.results.coverage.CoverageResultParser.java

Source

/*
 * Sonar C# Plugin :: Gallio
 * Copyright (C) 2010 Jose Chillan, Alexandre Victoor and 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.csharp.gallio.results.coverage;

import static org.sonar.plugins.csharp.gallio.helper.StaxHelper.descendantElements;
import static org.sonar.plugins.csharp.gallio.helper.StaxHelper.findAttributeValue;
import static org.sonar.plugins.csharp.gallio.helper.StaxHelper.findElementName;
import static org.sonar.plugins.csharp.gallio.helper.StaxHelper.findXMLEvent;
import static org.sonar.plugins.csharp.gallio.helper.StaxHelper.nextPosition;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;

import org.codehaus.staxmate.SMInputFactory;
import org.codehaus.staxmate.in.SMFilterFactory;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.SonarException;
import org.sonar.plugins.csharp.gallio.results.coverage.model.FileCoverage;
import org.sonar.plugins.csharp.gallio.results.coverage.model.ParserResult;
import org.sonar.plugins.csharp.gallio.results.coverage.model.ProjectCoverage;

import com.google.common.base.Predicate;
import com.google.common.collect.Maps;

/**
 * Parses a coverage report using Stax
 * 
 * @author Maxime SCHNEIDER-DUFEUTRELLE January 26, 2011
 */
public class CoverageResultParser implements PointParserCallback, BatchExtension {

    /**
     * Generates the logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(CoverageResultParser.class);

    private SensorContext context;
    private Map<Integer, FileCoverage> sourceFilesById;
    private final Map<String, ProjectCoverage> projectsByAssemblyName;
    private final List<AbstractParsingStrategy> parsingStrategies;
    private AbstractParsingStrategy currentStrategy;

    /**
     * Constructs a @link{CoverageResultStaxParser}.
     */
    public CoverageResultParser(SensorContext context) {
        this.context = context;
        sourceFilesById = new HashMap<Integer, FileCoverage>();
        projectsByAssemblyName = new HashMap<String, ProjectCoverage>();
        parsingStrategies = new ArrayList<AbstractParsingStrategy>();
        parsingStrategies.add(new PartCover23ParsingStrategy());
        parsingStrategies.add(new PartCover22ParsingStrategy());
        parsingStrategies.add(new PartCover4ParsingStrategy());
        parsingStrategies.add(new NCover3ParsingStrategy());
    }

    /**
     * Parses a file
     * 
     * @param file
     *          : the file to parse
     * 
     */
    public ParserResult parse(final Project sonarProject, final File file) {

        try {
            SMInputFactory inf = new SMInputFactory(XMLInputFactory.newInstance());
            SMHierarchicCursor rootCursor = inf.rootElementCursor(file);
            SMInputCursor root = rootCursor.advance();

            LOG.debug("\nrootCursor is at : {}", findElementName(rootCursor));
            // First define the version
            chooseParsingStrategy(root);

            SMInputCursor rootChildCursor = descendantElements(root);

            // Then all the indexed files are extracted
            sourceFilesById = currentStrategy.findFiles(rootChildCursor);

            if (sourceFilesById.isEmpty()) {
                // no source, ther is no point to parse further
                return new ParserResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
            }

            // filter files according to the exclusion patterns
            sourceFilesById = Maps.filterValues(sourceFilesById, new Predicate<FileCoverage>() {

                public boolean apply(FileCoverage input) {
                    return context.isIndexed(org.sonar.api.resources.File.fromIOFile(input.getFile(), sonarProject),
                            false);
                }
            });

            // We finally process the coverage details
            fillProjects(rootChildCursor);

            // We summarize the files
            for (FileCoverage fileCoverage : sourceFilesById.values()) {
                fileCoverage.summarize();
            }
            for (ProjectCoverage project : projectsByAssemblyName.values()) {
                LOG.debug("Summarize project: {}", project);
                project.summarize();
            }
        } catch (XMLStreamException e) {
            throw new SonarException("Could not parse the result file", e);
        }
        List<ProjectCoverage> projects = new ArrayList<ProjectCoverage>(projectsByAssemblyName.values());
        List<FileCoverage> sourceFiles = new ArrayList<FileCoverage>(sourceFilesById.values());
        return new ParserResult(projects, sourceFiles);
    }

    /**
     * Processes the details of the coverage
     * 
     * @param rootChildCursor
     *          cursor positioned to get the method elements
     */
    private void fillProjects(SMInputCursor rootChildCursor) {

        // Because of a different structure in PartCover 4, we need to get the assemblies first
        // if the report is from PartCover 4
        currentStrategy.saveAssemblyNamesById(rootChildCursor);

        // Sets the cursor to the tags "Type" for PartCover and "Module" for NCover
        rootChildCursor.setFilter(SMFilterFactory.getElementOnlyFilter(currentStrategy.getModuleTag()));
        do {
            if (findXMLEvent(rootChildCursor) != null) {

                currentStrategy.saveId(findAttributeValue(rootChildCursor, currentStrategy.getAssemblyReference()));

                String assemblyName = currentStrategy.findAssemblyName(rootChildCursor);
                LOG.debug("AssemblyName: {}", assemblyName);

                currentStrategy.findPoints(assemblyName, rootChildCursor, this);
            }
        } while (nextPosition(rootChildCursor) != null);
    }

    public void createProjects(String assemblyName, SMInputCursor classElements) {
        FileCoverage fileCoverage = null;
        SMInputCursor methodElements = descendantElements(classElements);
        while (nextPosition(methodElements) != null) {
            fileCoverage = currentStrategy.parseMethod(methodElements, assemblyName, sourceFilesById);
            if (fileCoverage != null) {
                final ProjectCoverage project;
                if (projectsByAssemblyName.containsKey(assemblyName)) {
                    project = projectsByAssemblyName.get(assemblyName);
                } else {
                    project = new ProjectCoverage();
                    project.setAssemblyName(assemblyName);
                    projectsByAssemblyName.put(assemblyName, project);
                }
                project.addFile(fileCoverage);
            }
        }
    }

    /**
     * This method is necessary due to a modification of the schema between partcover 2.2 and 2.3, for which elements start now with an
     * uppercase letter. Format is a little bit different with partcover4, and NCover use a different format too.
     * 
     * @param root
     *          : root cursor
     */
    private void chooseParsingStrategy(SMInputCursor root) {

        Iterator<AbstractParsingStrategy> strategyIterator = parsingStrategies.iterator();
        while (strategyIterator.hasNext()) {
            AbstractParsingStrategy strategy = (AbstractParsingStrategy) strategyIterator.next();
            if (strategy.isCompatible(root)) {
                this.currentStrategy = strategy;
            }
        }
        if (currentStrategy == null) {
            LOG.warn("XML coverage format unknown, using default strategy");
            this.currentStrategy = parsingStrategies.get(0);
        }
    }

}