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