]> source.dussan.org Git - sonarqube.git/blob
4a4acb20b9c05cafa30d5fa5140fdd0724b019e2
[sonarqube.git] /
1 package org.sonar.plugins.cobertura.api;
2
3 import static java.util.Locale.ENGLISH;
4 import static org.sonar.api.utils.ParsingUtils.parseNumber;
5 import static org.sonar.api.utils.ParsingUtils.scaleValue;
6
7 import org.apache.commons.io.FilenameUtils;
8 import org.apache.commons.lang.StringUtils;
9 import org.codehaus.staxmate.in.SMHierarchicCursor;
10 import org.codehaus.staxmate.in.SMInputCursor;
11 import org.sonar.api.batch.SensorContext;
12 import org.sonar.api.measures.CoreMetrics;
13 import org.sonar.api.measures.Measure;
14 import org.sonar.api.measures.PersistenceMode;
15 import org.sonar.api.measures.PropertiesBuilder;
16 import org.sonar.api.resources.Resource;
17 import org.sonar.api.utils.StaxParser;
18 import org.sonar.api.utils.XmlParserException;
19
20 import java.io.File;
21 import java.text.ParseException;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.xml.stream.XMLStreamException;
28
29 /**
30  * @since 2.4
31  */
32 public abstract class AbstractCoberturaParser {
33   public void parseReport(File xmlFile, final SensorContext context) {
34     try {
35       StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() {
36
37         public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
38           try {
39             rootCursor.advance();
40             collectPackageMeasures(rootCursor.descendantElementCursor("package"), context);
41           } catch (ParseException e) {
42             throw new XMLStreamException(e);
43           }
44         }
45       });
46       parser.parse(xmlFile);
47     } catch (XMLStreamException e) {
48       throw new XmlParserException(e);
49     }
50   }
51
52   private void collectPackageMeasures(SMInputCursor pack, SensorContext context) throws ParseException, XMLStreamException {
53     while (pack.getNext() != null) {
54       Map<String, FileData> fileDataPerFilename = new HashMap<String, FileData>();
55       collectFileMeasures(pack.descendantElementCursor("class"), fileDataPerFilename);
56       for (FileData cci : fileDataPerFilename.values()) {
57         if (isFileExist(context, cci.getFile())) {
58           for (Measure measure : cci.getMeasures()) {
59             context.saveMeasure(cci.getFile(), measure);
60           }
61         }
62       }
63     }
64   }
65
66   private boolean isFileExist(SensorContext context, Resource<?> file) {
67     return context.getResource(file) != null;
68   }
69
70   private void collectFileMeasures(SMInputCursor clazz, Map<String, FileData> dataPerFilename) throws ParseException, XMLStreamException {
71     while (clazz.getNext() != null) {
72       String fileName = FilenameUtils.removeExtension(clazz.getAttrValue("filename"));
73       fileName = fileName.replace('/', '.').replace('\\', '.');
74       FileData data = dataPerFilename.get(fileName);
75       if (data == null) {
76         data = new FileData(getResource(fileName));
77         dataPerFilename.put(fileName, data);
78       }
79       collectFileData(clazz, data);
80     }
81   }
82
83   private void collectFileData(SMInputCursor clazz, FileData data) throws ParseException, XMLStreamException {
84     SMInputCursor line = clazz.childElementCursor("lines").advance().childElementCursor("line");
85     while (line.getNext() != null) {
86       String lineId = line.getAttrValue("number");
87       data.addLine(lineId, (int) parseNumber(line.getAttrValue("hits"), ENGLISH));
88
89       String isBranch = line.getAttrValue("branch");
90       String text = line.getAttrValue("condition-coverage");
91       if (StringUtils.equals(isBranch, "true") && StringUtils.isNotBlank(text)) {
92         String[] conditions = StringUtils.split(StringUtils.substringBetween(text, "(", ")"), "/");
93         data.addConditionLine(lineId, Integer.parseInt(conditions[0]), Integer.parseInt(conditions[1]), StringUtils.substringBefore(text, " "));
94       }
95     }
96   }
97
98   private class FileData {
99
100     private int lines = 0;
101     private int conditions = 0;
102     private int coveredLines = 0;
103     private int coveredConditions = 0;
104
105     private Resource<?> file;
106     private PropertiesBuilder<String, Integer> lineHitsBuilder = new PropertiesBuilder<String, Integer>(CoreMetrics.COVERAGE_LINE_HITS_DATA);
107     private PropertiesBuilder<String, String> branchHitsBuilder = new PropertiesBuilder<String, String>(CoreMetrics.BRANCH_COVERAGE_HITS_DATA);
108
109     public void addLine(String lineId, int lineHits) {
110       lines++;
111       if (lineHits > 0) {
112         coveredLines++;
113       }
114       lineHitsBuilder.add(lineId, lineHits);
115     }
116
117     public void addConditionLine(String lineId, int coveredConditions, int conditions, String label) {
118       this.conditions += conditions;
119       this.coveredConditions += coveredConditions;
120       branchHitsBuilder.add(lineId, label);
121     }
122
123     public FileData(Resource<?> file) {
124       this.file = file;
125     }
126
127     public List<Measure> getMeasures() {
128       List<Measure> measures = new ArrayList<Measure>();
129       if (lines > 0) {
130         measures.add(new Measure(CoreMetrics.COVERAGE, calculateCoverage(coveredLines + coveredConditions, lines + conditions)));
131
132         measures.add(new Measure(CoreMetrics.LINE_COVERAGE, calculateCoverage(coveredLines, lines)));
133         measures.add(new Measure(CoreMetrics.LINES_TO_COVER, (double) lines));
134         measures.add(new Measure(CoreMetrics.UNCOVERED_LINES, (double) lines - coveredLines));
135         measures.add(lineHitsBuilder.build().setPersistenceMode(PersistenceMode.DATABASE));
136
137         if (conditions > 0) {
138           measures.add(new Measure(CoreMetrics.BRANCH_COVERAGE, calculateCoverage(coveredConditions, conditions)));
139           measures.add(new Measure(CoreMetrics.CONDITIONS_TO_COVER, (double) conditions));
140           measures.add(new Measure(CoreMetrics.UNCOVERED_CONDITIONS, (double) conditions - coveredConditions));
141           measures.add(branchHitsBuilder.build().setPersistenceMode(PersistenceMode.DATABASE));
142         }
143       }
144       return measures;
145     }
146
147     public Resource<?> getFile() {
148       return file;
149     }
150   }
151
152   private double calculateCoverage(int coveredElements, int elements) {
153     if (elements > 0) {
154       return scaleValue(100.0 * ((double) coveredElements / (double) elements));
155     }
156     return 0.0;
157   }
158
159   protected abstract Resource<?> getResource(String fileName);
160 }