1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.surefire.report;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.net.MalformedURLException;
24 import java.net.URL;
25 import java.net.URLClassLoader;
26 import java.text.MessageFormat;
27 import java.util.ArrayList;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.MissingResourceException;
32 import java.util.ResourceBundle;
33
34 import org.apache.maven.model.ReportPlugin;
35 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
36 import org.apache.maven.plugins.annotations.Component;
37 import org.apache.maven.plugins.annotations.Parameter;
38 import org.apache.maven.project.MavenProject;
39 import org.apache.maven.reporting.AbstractMavenReport;
40 import org.apache.maven.reporting.MavenReportException;
41 import org.apache.maven.settings.Settings;
42 import org.apache.maven.shared.utils.PathTool;
43 import org.codehaus.plexus.i18n.I18N;
44 import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
45 import org.codehaus.plexus.interpolation.InterpolationException;
46 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
47 import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
48 import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
49
50 import static java.util.Collections.addAll;
51 import static org.apache.maven.plugins.surefire.report.SurefireReportParser.hasReportFiles;
52 import static org.apache.maven.shared.utils.StringUtils.isEmpty;
53
54
55
56
57
58
59 public abstract class AbstractSurefireReportMojo extends AbstractMavenReport {
60
61
62
63
64 @Parameter(defaultValue = "true", required = true, property = "showSuccess")
65 private boolean showSuccess;
66
67
68
69
70 @Parameter
71 private File[] reportsDirectories;
72
73
74
75
76
77 @Deprecated
78 @Parameter
79 private File reportsDirectory;
80
81
82
83
84 @Parameter(defaultValue = "${reactorProjects}", readonly = true)
85 private List<MavenProject> reactorProjects;
86
87
88
89
90 @Parameter(defaultValue = "${project.reporting.outputDirectory}/xref-test")
91 private File xrefLocation;
92
93
94
95
96 @Parameter(defaultValue = "true", property = "linkXRef")
97 private boolean linkXRef;
98
99
100
101
102 @Parameter(defaultValue = "false", property = "aggregate")
103 private boolean aggregate;
104
105
106
107
108 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
109 private Settings settings;
110
111
112
113
114
115
116
117 @Parameter(defaultValue = "src/site/custom/surefire-report.properties")
118 private String customBundle;
119
120
121
122
123 @Component
124 private I18N i18n;
125
126 private List<File> resolvedReportsDirectories;
127
128
129
130
131
132
133
134 protected boolean isSkipped() {
135 return false;
136 }
137
138
139
140
141
142
143
144 protected boolean isGeneratedWhenNoResults() {
145 return false;
146 }
147
148
149
150
151 @Override
152 public void executeReport(Locale locale) throws MavenReportException {
153 if (!hasReportDirectories()) {
154 return;
155 }
156
157 SurefireReportRenderer r = new SurefireReportRenderer(
158 getSink(),
159 getI18N(locale),
160 getI18Nsection(),
161 locale,
162 getConsoleLogger(),
163 showSuccess,
164 getReportsDirectories(),
165 determineXrefLocation());
166 r.render();
167 }
168
169 @Override
170 public boolean canGenerateReport() {
171 return hasReportDirectories() && super.canGenerateReport();
172 }
173
174 private boolean hasReportDirectories() {
175 if (isSkipped()) {
176 return false;
177 }
178
179 final List<File> reportsDirectories = getReportsDirectories();
180
181 if (reportsDirectories == null) {
182 return false;
183 }
184
185 if (!isGeneratedWhenNoResults()) {
186 boolean atLeastOneDirectoryExists = false;
187 for (Iterator<File> i = reportsDirectories.iterator(); i.hasNext() && !atLeastOneDirectoryExists; ) {
188 atLeastOneDirectoryExists = hasReportFiles(i.next());
189 }
190 if (!atLeastOneDirectoryExists) {
191 return false;
192 }
193 }
194 return true;
195 }
196
197 private List<File> getReportsDirectories() {
198 if (resolvedReportsDirectories != null) {
199 return resolvedReportsDirectories;
200 }
201
202 resolvedReportsDirectories = new ArrayList<>();
203
204 if (this.reportsDirectories != null) {
205 addAll(resolvedReportsDirectories, this.reportsDirectories);
206 }
207
208 if (reportsDirectory != null) {
209
210 resolvedReportsDirectories.add(reportsDirectory);
211 }
212 if (aggregate) {
213 if (!project.isExecutionRoot()) {
214 return null;
215 }
216 if (this.reportsDirectories == null) {
217 if (reactorProjects.size() > 1) {
218 for (MavenProject mavenProject : getProjectsWithoutRoot()) {
219 resolvedReportsDirectories.add(getSurefireReportsDirectory(mavenProject));
220 }
221 } else {
222 resolvedReportsDirectories.add(getSurefireReportsDirectory(project));
223 }
224 } else {
225
226
227 String parentBaseDir = getProject().getBasedir().getAbsolutePath();
228 for (MavenProject subProject : getProjectsWithoutRoot()) {
229 String moduleBaseDir = subProject.getBasedir().getAbsolutePath();
230 for (File reportsDirectory1 : this.reportsDirectories) {
231 String reportDir = reportsDirectory1.getPath();
232 if (reportDir.startsWith(parentBaseDir)) {
233 reportDir = reportDir.substring(parentBaseDir.length());
234 }
235 File reportsDirectory = new File(moduleBaseDir, reportDir);
236 if (reportsDirectory.exists() && reportsDirectory.isDirectory()) {
237 getConsoleLogger().debug("Adding report dir : " + moduleBaseDir + reportDir);
238 resolvedReportsDirectories.add(reportsDirectory);
239 }
240 }
241 }
242 }
243 } else {
244 if (resolvedReportsDirectories.isEmpty()) {
245
246 resolvedReportsDirectories.add(getSurefireReportsDirectory(project));
247 }
248 }
249 return resolvedReportsDirectories;
250 }
251
252
253
254
255
256
257
258 protected abstract File getSurefireReportsDirectory(MavenProject subProject);
259
260 private List<MavenProject> getProjectsWithoutRoot() {
261 List<MavenProject> result = new ArrayList<>();
262 for (MavenProject subProject : reactorProjects) {
263 if (!project.equals(subProject)) {
264 result.add(subProject);
265 }
266 }
267 return result;
268 }
269
270 private String determineXrefLocation() {
271 String location = null;
272
273 if (linkXRef) {
274 String relativePath = PathTool.getRelativePath(getOutputDirectory(), xrefLocation.getAbsolutePath());
275 if (isEmpty(relativePath)) {
276 relativePath = ".";
277 }
278 relativePath = relativePath + "/" + xrefLocation.getName();
279 if (xrefLocation.exists()) {
280
281 location = relativePath;
282 } else {
283
284 for (Object o : project.getReportPlugins()) {
285 ReportPlugin report = (ReportPlugin) o;
286
287 String artifactId = report.getArtifactId();
288 if ("maven-jxr-plugin".equals(artifactId) || "jxr-maven-plugin".equals(artifactId)) {
289 location = relativePath;
290 }
291 }
292 }
293
294 if (location == null) {
295 getConsoleLogger().warning("Unable to locate Test Source XRef to link to - DISABLED");
296 }
297 }
298 return location;
299 }
300
301
302
303
304
305
306 protected String getI18nString(Locale locale, String key) {
307 return getI18N(locale).getString("surefire-report", locale, "report." + getI18Nsection() + '.' + key);
308 }
309
310
311
312
313 protected I18N getI18N(Locale locale) {
314 if (customBundle != null) {
315 File customBundleFile = new File(customBundle);
316 if (customBundleFile.isFile() && customBundleFile.getName().endsWith(".properties")) {
317 if (!i18n.getClass().isAssignableFrom(CustomI18N.class)
318 || !i18n.getDefaultLanguage().equals(locale.getLanguage())) {
319
320 i18n = new CustomI18N(project, settings, customBundleFile, locale, i18n);
321 }
322 }
323 }
324
325 return i18n;
326 }
327
328
329
330 protected abstract String getI18Nsection();
331
332
333 public String getName(Locale locale) {
334 return getI18nString(locale, "name");
335 }
336
337
338 public String getDescription(Locale locale) {
339 return getI18nString(locale, "description");
340 }
341
342
343
344
345 @Override
346 public abstract String getOutputName();
347
348 protected final ConsoleLogger getConsoleLogger() {
349 return new PluginConsoleLogger(getLog());
350 }
351
352 @Override
353 protected MavenProject getProject() {
354 return project;
355 }
356
357
358 private static class CustomI18N implements I18N {
359 private final MavenProject project;
360
361 private final Settings settings;
362
363 private final String bundleName;
364
365 private final Locale locale;
366
367 private final I18N i18nOriginal;
368
369 private ResourceBundle bundle;
370
371 private static final Object[] NO_ARGS = new Object[0];
372
373 CustomI18N(MavenProject project, Settings settings, File customBundleFile, Locale locale, I18N i18nOriginal) {
374 super();
375 this.project = project;
376 this.settings = settings;
377 this.locale = locale;
378 this.i18nOriginal = i18nOriginal;
379 this.bundleName = customBundleFile
380 .getName()
381 .substring(0, customBundleFile.getName().indexOf(".properties"));
382
383 URLClassLoader classLoader = null;
384 try {
385 classLoader = new URLClassLoader(
386 new URL[] {customBundleFile.getParentFile().toURI().toURL()}, null);
387 } catch (MalformedURLException e) {
388
389 }
390
391 this.bundle = ResourceBundle.getBundle(this.bundleName, locale, classLoader);
392 if (!this.bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
393 this.bundle = ResourceBundle.getBundle(this.bundleName, Locale.getDefault(), classLoader);
394 }
395 }
396
397
398 public String getDefaultLanguage() {
399 return locale.getLanguage();
400 }
401
402
403 public String getDefaultCountry() {
404 return locale.getCountry();
405 }
406
407
408 public String getDefaultBundleName() {
409 return bundleName;
410 }
411
412
413 public String[] getBundleNames() {
414 return new String[] {bundleName};
415 }
416
417
418 public ResourceBundle getBundle() {
419 return bundle;
420 }
421
422
423 public ResourceBundle getBundle(String bundleName) {
424 return bundle;
425 }
426
427
428 public ResourceBundle getBundle(String bundleName, String languageHeader) {
429 return bundle;
430 }
431
432
433 public ResourceBundle getBundle(String bundleName, Locale locale) {
434 return bundle;
435 }
436
437
438 public Locale getLocale(String languageHeader) {
439 return new Locale(languageHeader);
440 }
441
442
443 public String getString(String key) {
444 return getString(bundleName, locale, key);
445 }
446
447
448 public String getString(String key, Locale locale) {
449 return getString(bundleName, locale, key);
450 }
451
452
453 public String getString(String bundleName, Locale locale, String key) {
454 String value;
455
456 if (locale == null) {
457 locale = getLocale(null);
458 }
459
460 ResourceBundle rb = getBundle(bundleName, locale);
461 value = getStringOrNull(rb, key);
462
463 if (value == null) {
464
465 value = i18nOriginal.getString(bundleName, locale, key);
466 }
467
468 if (!value.contains("${")) {
469 return value;
470 }
471
472 final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
473 try {
474 interpolator.addValueSource(new EnvarBasedValueSource());
475 } catch (final IOException e) {
476
477 }
478
479 interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties()));
480 interpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties()));
481 interpolator.addValueSource(new PrefixedObjectValueSource("project", project));
482 interpolator.addValueSource(new PrefixedObjectValueSource("pom", project));
483 interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings));
484
485 try {
486 value = interpolator.interpolate(value);
487 } catch (final InterpolationException e) {
488
489 }
490
491 return value;
492 }
493
494
495 public String format(String key, Object arg1) {
496 return format(bundleName, locale, key, new Object[] {arg1});
497 }
498
499
500 public String format(String key, Object arg1, Object arg2) {
501 return format(bundleName, locale, key, new Object[] {arg1, arg2});
502 }
503
504
505 public String format(String bundleName, Locale locale, String key, Object arg1) {
506 return format(bundleName, locale, key, new Object[] {arg1});
507 }
508
509
510 public String format(String bundleName, Locale locale, String key, Object arg1, Object arg2) {
511 return format(bundleName, locale, key, new Object[] {arg1, arg2});
512 }
513
514
515 public String format(String bundleName, Locale locale, String key, Object[] args) {
516 if (locale == null) {
517 locale = getLocale(null);
518 }
519
520 String value = getString(bundleName, locale, key);
521 if (args == null) {
522 args = NO_ARGS;
523 }
524
525 MessageFormat messageFormat = new MessageFormat("");
526 messageFormat.setLocale(locale);
527 messageFormat.applyPattern(value);
528
529 return messageFormat.format(args);
530 }
531
532 private String getStringOrNull(ResourceBundle rb, String key) {
533 if (rb != null) {
534 try {
535 return rb.getString(key);
536 } catch (MissingResourceException ignored) {
537
538 }
539 }
540 return null;
541 }
542 }
543 }