1 /*******************************************************************************
2  * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *    John Keeping - initial implementation
10  *    Marc R. Hoffmann - rework
11  *
12  *******************************************************************************/
13 package org.jacoco.cli.internal.commands;
14 
15 import java.io.File;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.io.PrintWriter;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.List;
22 
23 import org.jacoco.cli.internal.Command;
24 import org.jacoco.core.analysis.Analyzer;
25 import org.jacoco.core.analysis.CoverageBuilder;
26 import org.jacoco.core.analysis.IBundleCoverage;
27 import org.jacoco.core.analysis.IClassCoverage;
28 import org.jacoco.core.data.ExecutionDataStore;
29 import org.jacoco.core.tools.ExecFileLoader;
30 import org.jacoco.report.DirectorySourceFileLocator;
31 import org.jacoco.report.FileMultiReportOutput;
32 import org.jacoco.report.IReportVisitor;
33 import org.jacoco.report.ISourceFileLocator;
34 import org.jacoco.report.MultiReportVisitor;
35 import org.jacoco.report.MultiSourceFileLocator;
36 import org.jacoco.report.csv.CSVFormatter;
37 import org.jacoco.report.html.HTMLFormatter;
38 import org.jacoco.report.xml.XMLFormatter;
39 import org.kohsuke.args4j.Argument;
40 import org.kohsuke.args4j.Option;
41 
42 /**
43  * The <code>report</code> command.
44  */
45 public class Report extends Command {
46 
47 	@Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>")
48 	List<File> execfiles = new ArrayList<File>();
49 
50 	@Option(name = "--classfiles", usage = "location of Java class files", metaVar = "<path>", required = true)
51 	List<File> classfiles = new ArrayList<File>();
52 
53 	@Option(name = "--sourcefiles", usage = "location of the source files", metaVar = "<path>")
54 	List<File> sourcefiles = new ArrayList<File>();
55 
56 	@Option(name = "--tabwith", usage = "tab stop width for the source pages (default 4)", metaVar = "<n>")
57 	int tabwidth = 4;
58 
59 	@Option(name = "--name", usage = "name used for this report", metaVar = "<name>")
60 	String name = "JaCoCo Coverage Report";
61 
62 	@Option(name = "--encoding", usage = "source file encoding (by default platform encoding is used)", metaVar = "<charset>")
63 	String encoding;
64 
65 	@Option(name = "--xml", usage = "output file for the XML report", metaVar = "<file>")
66 	File xml;
67 
68 	@Option(name = "--csv", usage = "output file for the CSV report", metaVar = "<file>")
69 	File csv;
70 
71 	@Option(name = "--html", usage = "output directory for the HTML report", metaVar = "<dir>")
72 	File html;
73 
74 	@Override
description()75 	public String description() {
76 		return "Generate reports in different formats by reading exec and Java class files.";
77 	}
78 
79 	@Override
execute(final PrintWriter out, final PrintWriter err)80 	public int execute(final PrintWriter out, final PrintWriter err)
81 			throws IOException {
82 		final ExecFileLoader loader = loadExecutionData(out);
83 		final IBundleCoverage bundle = analyze(loader.getExecutionDataStore(),
84 				out);
85 		writeReports(bundle, loader, out);
86 		return 0;
87 	}
88 
loadExecutionData(final PrintWriter out)89 	private ExecFileLoader loadExecutionData(final PrintWriter out)
90 			throws IOException {
91 		final ExecFileLoader loader = new ExecFileLoader();
92 		if (execfiles.isEmpty()) {
93 			out.println("[WARN] No execution data files provided.");
94 		} else {
95 			for (final File file : execfiles) {
96 				out.printf("[INFO] Loading execution data file %s.%n",
97 						file.getAbsolutePath());
98 				loader.load(file);
99 			}
100 		}
101 		return loader;
102 	}
103 
analyze(final ExecutionDataStore data, final PrintWriter out)104 	private IBundleCoverage analyze(final ExecutionDataStore data,
105 			final PrintWriter out) throws IOException {
106 		final CoverageBuilder builder = new CoverageBuilder();
107 		final Analyzer analyzer = new Analyzer(data, builder);
108 		for (final File f : classfiles) {
109 			analyzer.analyzeAll(f);
110 		}
111 		printNoMatchWarning(builder.getNoMatchClasses(), out);
112 		return builder.getBundle(name);
113 	}
114 
printNoMatchWarning(final Collection<IClassCoverage> nomatch, final PrintWriter out)115 	private void printNoMatchWarning(final Collection<IClassCoverage> nomatch,
116 			final PrintWriter out) {
117 		if (!nomatch.isEmpty()) {
118 			out.println(
119 					"[WARN] Some classes do not match with execution data.");
120 			out.println(
121 					"[WARN] For report generation the same class files must be used as at runtime.");
122 			for (final IClassCoverage c : nomatch) {
123 				out.printf(
124 						"[WARN] Execution data for class %s does not match.%n",
125 						c.getName());
126 			}
127 		}
128 	}
129 
writeReports(final IBundleCoverage bundle, final ExecFileLoader loader, final PrintWriter out)130 	private void writeReports(final IBundleCoverage bundle,
131 			final ExecFileLoader loader, final PrintWriter out)
132 			throws IOException {
133 		out.printf("[INFO] Analyzing %s classes.%n",
134 				Integer.valueOf(bundle.getClassCounter().getTotalCount()));
135 		final IReportVisitor visitor = createReportVisitor();
136 		visitor.visitInfo(loader.getSessionInfoStore().getInfos(),
137 				loader.getExecutionDataStore().getContents());
138 		visitor.visitBundle(bundle, getSourceLocator());
139 		visitor.visitEnd();
140 	}
141 
createReportVisitor()142 	private IReportVisitor createReportVisitor() throws IOException {
143 		final List<IReportVisitor> visitors = new ArrayList<IReportVisitor>();
144 
145 		if (xml != null) {
146 			final XMLFormatter formatter = new XMLFormatter();
147 			visitors.add(formatter.createVisitor(new FileOutputStream(xml)));
148 		}
149 
150 		if (csv != null) {
151 			final CSVFormatter formatter = new CSVFormatter();
152 			visitors.add(formatter.createVisitor(new FileOutputStream(csv)));
153 		}
154 
155 		if (html != null) {
156 			final HTMLFormatter formatter = new HTMLFormatter();
157 			visitors.add(
158 					formatter.createVisitor(new FileMultiReportOutput(html)));
159 		}
160 
161 		return new MultiReportVisitor(visitors);
162 	}
163 
getSourceLocator()164 	private ISourceFileLocator getSourceLocator() {
165 		final MultiSourceFileLocator multi = new MultiSourceFileLocator(
166 				tabwidth);
167 		for (final File f : sourcefiles) {
168 			multi.add(new DirectorySourceFileLocator(f, encoding, tabwidth));
169 		}
170 		return multi;
171 	}
172 
173 }
174