001: /*
002: * Cobertura - http://cobertura.sourceforge.net/
003: *
004: * Copyright (C) 2003 jcoverage ltd.
005: * Copyright (C) 2005 Mark Doliner
006: * Copyright (C) 2005 Jeremy Thomerson
007: * Copyright (C) 2006 Jiri Mares
008: *
009: * Cobertura is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published
011: * by the Free Software Foundation; either version 2 of the License,
012: * or (at your option) any later version.
013: *
014: * Cobertura is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * General Public License for more details.
018: *
019: * You should have received a copy of the GNU General Public License
020: * along with Cobertura; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
022: * USA
023: */
024:
025: package net.sourceforge.cobertura.reporting.xml;
026:
027: import java.io.File;
028: import java.io.IOException;
029: import java.io.PrintWriter;
030: import java.util.Collection;
031: import java.util.Date;
032: import java.util.Iterator;
033: import java.util.SortedSet;
034: import java.util.TreeSet;
035:
036: import net.sourceforge.cobertura.coveragedata.ClassData;
037: import net.sourceforge.cobertura.coveragedata.JumpData;
038: import net.sourceforge.cobertura.coveragedata.LineData;
039: import net.sourceforge.cobertura.coveragedata.PackageData;
040: import net.sourceforge.cobertura.coveragedata.ProjectData;
041: import net.sourceforge.cobertura.coveragedata.SourceFileData;
042: import net.sourceforge.cobertura.coveragedata.SwitchData;
043: import net.sourceforge.cobertura.reporting.ComplexityCalculator;
044: import net.sourceforge.cobertura.util.FileFinder;
045: import net.sourceforge.cobertura.util.Header;
046: import net.sourceforge.cobertura.util.IOUtil;
047: import net.sourceforge.cobertura.util.StringUtil;
048:
049: import org.apache.log4j.Logger;
050:
051: public class XMLReport {
052:
053: private static final Logger logger = Logger
054: .getLogger(XMLReport.class);
055:
056: protected final static String coverageDTD = "coverage-03.dtd";
057:
058: private final PrintWriter pw;
059: private final FileFinder finder;
060: private final ComplexityCalculator complexity;
061: private int indent = 0;
062:
063: public XMLReport(ProjectData projectData, File destinationDir,
064: FileFinder finder, ComplexityCalculator complexity)
065: throws IOException {
066: this .complexity = complexity;
067: this .finder = finder;
068:
069: File file = new File(destinationDir, "coverage.xml");
070: pw = IOUtil.getPrintWriter(file);
071:
072: try {
073: println("<?xml version=\"1.0\"?>");
074: println("<!DOCTYPE coverage SYSTEM \"http://cobertura.sourceforge.net/xml/"
075: + coverageDTD + "\">");
076: println("");
077:
078: // TODO: Set a schema?
079: //println("<coverage " + sourceDirectories.toString() + " xmlns=\"http://cobertura.sourceforge.net\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://cobertura.sourceforge.net/xml/coverage.xsd\">");
080: println("<coverage line-rate=\""
081: + projectData.getLineCoverageRate()
082: + "\" branch-rate=\""
083: + projectData.getBranchCoverageRate()
084: + "\" version=\"" + Header.version()
085: + "\" timestamp=\"" + new Date().getTime() + "\">");
086:
087: increaseIndentation();
088: dumpSources();
089: dumpPackages(projectData);
090: decreaseIndentation();
091: println("</coverage>");
092: } finally {
093: pw.close();
094: }
095: }
096:
097: void increaseIndentation() {
098: indent++;
099: }
100:
101: void decreaseIndentation() {
102: if (indent > 0)
103: indent--;
104: }
105:
106: void indent() {
107: for (int i = 0; i < indent; i++) {
108: pw.print("\t");
109: }
110: }
111:
112: void println(String ln) {
113: indent();
114: pw.println(ln);
115: }
116:
117: private void dumpSources() {
118: println("<sources>");
119: increaseIndentation();
120: for (Iterator it = finder.getSourceDirectoryList().iterator(); it
121: .hasNext();) {
122: String dir = (String) it.next();
123: dumpSource(dir);
124: }
125: decreaseIndentation();
126: println("</sources>");
127: }
128:
129: private void dumpSource(String sourceDirectory) {
130: println("<source>" + sourceDirectory + "</source>");
131: }
132:
133: private void dumpPackages(ProjectData projectData) {
134: println("<packages>");
135: increaseIndentation();
136:
137: Iterator it = projectData.getPackages().iterator();
138: while (it.hasNext()) {
139: dumpPackage((PackageData) it.next());
140: }
141:
142: decreaseIndentation();
143: println("</packages>");
144: }
145:
146: private void dumpPackage(PackageData packageData) {
147: logger.debug("Dumping package " + packageData.getName());
148:
149: println("<package name=\"" + packageData.getName()
150: + "\" line-rate=\"" + packageData.getLineCoverageRate()
151: + "\" branch-rate=\""
152: + packageData.getBranchCoverageRate()
153: + "\" complexity=\""
154: + complexity.getCCNForPackage(packageData) + "\"" + ">");
155: increaseIndentation();
156: dumpSourceFiles(packageData);
157: decreaseIndentation();
158: println("</package>");
159: }
160:
161: private void dumpSourceFiles(PackageData packageData) {
162: println("<classes>");
163: increaseIndentation();
164:
165: Iterator it = packageData.getSourceFiles().iterator();
166: while (it.hasNext()) {
167: dumpClasses((SourceFileData) it.next());
168: }
169:
170: decreaseIndentation();
171: println("</classes>");
172: }
173:
174: private void dumpClasses(SourceFileData sourceFileData) {
175: Iterator it = sourceFileData.getClasses().iterator();
176: while (it.hasNext()) {
177: dumpClass((ClassData) it.next());
178: }
179: }
180:
181: private void dumpClass(ClassData classData) {
182: logger.debug("Dumping class " + classData.getName());
183:
184: println("<class name=\"" + classData.getName()
185: + "\" filename=\"" + classData.getSourceFileName()
186: + "\" line-rate=\"" + classData.getLineCoverageRate()
187: + "\" branch-rate=\""
188: + classData.getBranchCoverageRate()
189: + "\" complexity=\""
190: + complexity.getCCNForClass(classData) + "\"" + ">");
191: increaseIndentation();
192:
193: dumpMethods(classData);
194: dumpLines(classData);
195:
196: decreaseIndentation();
197: println("</class>");
198: }
199:
200: private void dumpMethods(ClassData classData) {
201: println("<methods>");
202: increaseIndentation();
203:
204: SortedSet sortedMethods = new TreeSet();
205: sortedMethods.addAll(classData.getMethodNamesAndDescriptors());
206: Iterator iter = sortedMethods.iterator();
207: while (iter.hasNext()) {
208: dumpMethod(classData, (String) iter.next());
209: }
210:
211: decreaseIndentation();
212: println("</methods>");
213: }
214:
215: private void dumpMethod(ClassData classData, String nameAndSig) {
216: String name = nameAndSig.substring(0, nameAndSig.indexOf('('));
217: String signature = nameAndSig
218: .substring(nameAndSig.indexOf('('));
219: double lineRate = classData.getLineCoverageRate(nameAndSig);
220: double branchRate = classData.getBranchCoverageRate(nameAndSig);
221:
222: println("<method name=\"" + xmlEscape(name) + "\" signature=\""
223: + xmlEscape(signature) + "\" line-rate=\"" + lineRate
224: + "\" branch-rate=\"" + branchRate + "\">");
225: increaseIndentation();
226: dumpLines(classData, nameAndSig);
227: decreaseIndentation();
228: println("</method>");
229: }
230:
231: private static String xmlEscape(String str) {
232: str = StringUtil.replaceAll(str, "<", "<");
233: str = StringUtil.replaceAll(str, ">", ">");
234: return str;
235: }
236:
237: private void dumpLines(ClassData classData) {
238: dumpLines(classData.getLines());
239: }
240:
241: private void dumpLines(ClassData classData, String methodNameAndSig) {
242: dumpLines(classData.getLines(methodNameAndSig));
243: }
244:
245: private void dumpLines(Collection lines) {
246: println("<lines>");
247: increaseIndentation();
248:
249: SortedSet sortedLines = new TreeSet();
250: sortedLines.addAll(lines);
251: Iterator iter = sortedLines.iterator();
252: while (iter.hasNext()) {
253: dumpLine((LineData) iter.next());
254: }
255:
256: decreaseIndentation();
257: println("</lines>");
258: }
259:
260: private void dumpLine(LineData lineData) {
261: int lineNumber = lineData.getLineNumber();
262: long hitCount = lineData.getHits();
263: boolean hasBranch = lineData.hasBranch();
264: String conditionCoverage = lineData.getConditionCoverage();
265:
266: String lineInfo = "<line number=\"" + lineNumber + "\" hits=\""
267: + hitCount + "\" branch=\"" + hasBranch + "\"";
268: if (hasBranch) {
269: println(lineInfo + " condition-coverage=\""
270: + conditionCoverage + "\">");
271: dumpConditions(lineData);
272: println("</line>");
273: } else {
274: println(lineInfo + "/>");
275: }
276: }
277:
278: private void dumpConditions(LineData lineData) {
279: increaseIndentation();
280: println("<conditions>");
281:
282: for (int i = 0; i < lineData.getConditionSize(); i++) {
283: Object conditionData = lineData.getConditionData(i);
284: String coverage = lineData.getConditionCoverage(i);
285: dumpCondition(conditionData, coverage);
286: }
287:
288: println("</conditions>");
289: decreaseIndentation();
290: }
291:
292: private void dumpCondition(Object conditionData, String coverage) {
293: increaseIndentation();
294: StringBuffer buffer = new StringBuffer("<condition");
295: if (conditionData instanceof JumpData) {
296: JumpData jumpData = (JumpData) conditionData;
297: buffer.append(" number=\"").append(
298: jumpData.getConditionNumber()).append("\"");
299: buffer.append(" type=\"").append("jump").append("\"");
300: buffer.append(" coverage=\"").append(coverage).append("\"");
301: } else {
302: SwitchData switchData = (SwitchData) conditionData;
303: buffer.append(" number=\"").append(
304: switchData.getSwitchNumber()).append("\"");
305: buffer.append(" type=\"").append("switch").append("\"");
306: buffer.append(" coverage=\"").append(coverage).append("\"");
307: }
308: buffer.append("/>");
309: println(buffer.toString());
310: decreaseIndentation();
311: }
312:
313: }
|