org.sonar.plugins.clover.CloverXmlReportParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sonar-clover-plugin Show documentation
Show all versions of sonar-clover-plugin Show documentation
Get code coverage with <a href="http://www.atlassian.com/software/clover/">Atlassian Clover</a>.
The newest version!
/*
* Sonar Clover Plugin
* Copyright (C) 2008 SonarSource
* [email protected]
*
* 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.clover;
import org.apache.commons.lang.StringUtils;
import org.codehaus.staxmate.in.SMEvent;
import org.codehaus.staxmate.in.SMFilterFactory;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
import org.codehaus.staxmate.in.SimpleFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.measures.CoverageMeasuresBuilder;
import org.sonar.api.measures.Measure;
import org.sonar.api.resources.Resource;
import org.sonar.api.utils.ParsingUtils;
import org.sonar.api.utils.StaxParser;
import org.sonar.api.utils.XmlParserException;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.text.ParseException;
public class CloverXmlReportParser {
private static final Logger LOG = LoggerFactory.getLogger(CloverXmlReportParser.class);
private final FileProvider fileProvider;
private SensorContext context;
private int files;
private int unmatchedFile;
private String unmatchedFiles;
final CoverageMeasuresBuilder fileMeasuresBuilder = CoverageMeasuresBuilder.create();
CloverXmlReportParser(FileProvider fileProvider, SensorContext context) {
this.context = context;
this.fileProvider = fileProvider;
}
private boolean reportExists(File report) {
return report != null && report.exists() && report.isFile();
}
protected void collect(File xmlFile) {
try {
if (reportExists(xmlFile)) {
files = 0;
unmatchedFile = 0;
unmatchedFiles = "";
LOG.info("Parsing " + xmlFile.getCanonicalPath());
createStaxParser().parse(xmlFile);
LOG.info("Matched files in report : {}", getMatchedPercentage());
if (!unmatchedFiles.isEmpty()) {
LOG.warn("{} files in clover report did not match any file in SonarQube Index : {}", unmatchedFile, unmatchedFiles);
}
}
} catch (IllegalStateException e) {
LOG.error("Format of clover report file is unexpected ", e);
throw new XmlParserException(e);
} catch (Exception e) {
LOG.error("An error occured while parsing clover xml report : ", e);
throw new XmlParserException(e);
}
}
private StaxParser createStaxParser() {
return new StaxParser(new StaxParser.XmlStreamHandler() {
public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
try {
collectProjectMeasures(rootCursor.advance());
} catch (ParseException e) {
throw new XMLStreamException(e);
}
}
});
}
private String getMatchedPercentage() {
if (files == 0) {
return "No files found in section of report";
}
return (files - unmatchedFile) * 100 / files+"%";
}
private void collectProjectMeasures(SMInputCursor rootCursor) throws ParseException, XMLStreamException {
SMInputCursor projectCursor = rootCursor.descendantElementCursor("project");
SMInputCursor projectChildrenCursor = projectCursor.advance().childElementCursor();
projectChildrenCursor.setFilter(new SimpleFilter(SMEvent.START_ELEMENT));
//Skip the metrics tag.
projectChildrenCursor.advance();
collectPackageMeasures(projectChildrenCursor);
}
private void collectPackageMeasures(SMInputCursor packCursor) throws ParseException, XMLStreamException {
while (packCursor.getNext() != null) {
SMInputCursor packChildrenCursor = packCursor.descendantElementCursor();
packChildrenCursor.setFilter(new SimpleFilter(SMEvent.START_ELEMENT));
//Skip the metrics tag.
packChildrenCursor.advance();
collectFileMeasures(packChildrenCursor);
}
}
private void collectFileMeasures(SMInputCursor fileCursor) throws ParseException, XMLStreamException {
fileCursor.setFilter(SMFilterFactory.getElementOnlyFilter("file"));
while (fileCursor.getNext() != null) {
if (fileCursor.asEvent().isStartElement()) {
String absoluteFilePath = fileCursor.getAttrValue("path");
if (absoluteFilePath != null) {
SMInputCursor fileChildrenCursor = fileCursor.childCursor(new SimpleFilter(SMEvent.START_ELEMENT));
// cursor should be on the metrics element
if (canBeIncludedInFileMetrics(fileChildrenCursor)) {
// cursor should be now on the line cursor
saveHitsData(getResource(absoluteFilePath), fileChildrenCursor);
}
}
}
}
}
private org.sonar.api.resources.File getResource(String absoluteFilePath) {
files++;
org.sonar.api.resources.File resource = fileProvider.fromIOFile(absoluteFilePath);
if (resource == null) {
unmatchedFile++;
LOG.warn("Resource " + absoluteFilePath + " was not found.");
unmatchedFiles += absoluteFilePath + ", ";
}
return resource;
}
private void saveHitsData(Resource resource, SMInputCursor lineCursor) throws ParseException, XMLStreamException {
fileMeasuresBuilder.reset();
while (lineCursor.getNext() != null) {
// skip class elements on format 2_3_2
if (isClass(lineCursor)) {
continue;
}
final int lineId = Integer.parseInt(lineCursor.getAttrValue("num"));
String count = lineCursor.getAttrValue("count");
if (StringUtils.isNotBlank(count)) {
fileMeasuresBuilder.setHits(lineId, Integer.parseInt(count));
} else {
int trueCount = (int) ParsingUtils.parseNumber(lineCursor.getAttrValue("truecount"));
int falseCount = (int) ParsingUtils.parseNumber(lineCursor.getAttrValue("falsecount"));
int coveredConditions = 0;
if (trueCount > 0) {
coveredConditions++;
}
if (falseCount > 0) {
coveredConditions++;
}
fileMeasuresBuilder.setConditions(lineId, 2, coveredConditions);
}
}
if (resource != null) {
for (Measure measure : fileMeasuresBuilder.createMeasures()) {
context.saveMeasure(resource, measure);
}
}
}
private boolean canBeIncludedInFileMetrics(SMInputCursor metricsCursor) throws ParseException, XMLStreamException {
while (metricsCursor.getNext() != null && isClass(metricsCursor)) {
// skip class elements on 1.x xml format
}
return ParsingUtils.parseNumber(metricsCursor.getAttrValue("elements")) > 0;
}
private boolean isClass(SMInputCursor cursor) throws XMLStreamException {
return "class".equals(cursor.getLocalName());
}
}