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 @Override
292 public String getName(Locale locale) {
293 return getI18nString(locale, "name");
294 }
295
296
297 @Override
298 public String getDescription(Locale locale) {
299 return getI18nString(locale, "description");
300 }
301
302
303
304
305 @Override
306 public abstract String getOutputName();
307
308 protected final ConsoleLogger getConsoleLogger() {
309 return new PluginConsoleLogger(getLog());
310 }
311
312 @Override
313 protected MavenProject getProject() {
314 return project;
315 }
316
317 protected List<MavenProject> getReactorProjects() {
318 return reactorProjects;
319 }
320
321
322 private static class CustomI18N implements I18N {
323 private final MavenProject project;
324
325 private final Settings settings;
326
327 private final String bundleName;
328
329 private final Locale locale;
330
331 private final I18N i18nOriginal;
332
333 private ResourceBundle bundle;
334
335 private static final Object[] NO_ARGS = new Object[0];
336
337 CustomI18N(MavenProject project, Settings settings, File customBundleFile, Locale locale, I18N i18nOriginal) {
338 super();
339 this.project = project;
340 this.settings = settings;
341 this.locale = locale;
342 this.i18nOriginal = i18nOriginal;
343 this.bundleName = customBundleFile
344 .getName()
345 .substring(0, customBundleFile.getName().indexOf(".properties"));
346
347 URLClassLoader classLoader = null;
348 try {
349 classLoader = new URLClassLoader(
350 new URL[] {customBundleFile.getParentFile().toURI().toURL()}, null);
351 } catch (MalformedURLException e) {
352
353 }
354
355 this.bundle = ResourceBundle.getBundle(this.bundleName, locale, classLoader);
356 if (!this.bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
357 this.bundle = ResourceBundle.getBundle(this.bundleName, Locale.getDefault(), classLoader);
358 }
359 }
360
361
362 @Override
363 public String getDefaultLanguage() {
364 return locale.getLanguage();
365 }
366
367
368 @Override
369 public String getDefaultCountry() {
370 return locale.getCountry();
371 }
372
373
374 @Override
375 public String getDefaultBundleName() {
376 return bundleName;
377 }
378
379
380 @Override
381 public String[] getBundleNames() {
382 return new String[] {bundleName};
383 }
384
385
386 @Override
387 public ResourceBundle getBundle() {
388 return bundle;
389 }
390
391
392 @Override
393 public ResourceBundle getBundle(String bundleName) {
394 return bundle;
395 }
396
397
398 @Override
399 public ResourceBundle getBundle(String bundleName, String languageHeader) {
400 return bundle;
401 }
402
403
404 @Override
405 public ResourceBundle getBundle(String bundleName, Locale locale) {
406 return bundle;
407 }
408
409
410 @Override
411 public Locale getLocale(String languageHeader) {
412 return new Locale(languageHeader);
413 }
414
415
416 @Override
417 public String getString(String key) {
418 return getString(bundleName, locale, key);
419 }
420
421
422 @Override
423 public String getString(String key, Locale locale) {
424 return getString(bundleName, locale, key);
425 }
426
427
428 @Override
429 public String getString(String bundleName, Locale locale, String key) {
430 String value;
431
432 if (locale == null) {
433 locale = getLocale(null);
434 }
435
436 ResourceBundle rb = getBundle(bundleName, locale);
437 value = getStringOrNull(rb, key);
438
439 if (value == null) {
440
441 value = i18nOriginal.getString(bundleName, locale, key);
442 }
443
444 if (!value.contains("${")) {
445 return value;
446 }
447
448 final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
449 try {
450 interpolator.addValueSource(new EnvarBasedValueSource());
451 } catch (final IOException e) {
452
453 }
454
455 interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties()));
456 interpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties()));
457 interpolator.addValueSource(new PrefixedObjectValueSource("project", project));
458 interpolator.addValueSource(new PrefixedObjectValueSource("pom", project));
459 interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings));
460
461 try {
462 value = interpolator.interpolate(value);
463 } catch (final InterpolationException e) {
464
465 }
466
467 return value;
468 }
469
470
471 @Override
472 public String format(String key, Object arg1) {
473 return format(bundleName, locale, key, new Object[] {arg1});
474 }
475
476
477 @Override
478 public String format(String key, Object arg1, Object arg2) {
479 return format(bundleName, locale, key, new Object[] {arg1, arg2});
480 }
481
482
483 @Override
484 public String format(String bundleName, Locale locale, String key, Object arg1) {
485 return format(bundleName, locale, key, new Object[] {arg1});
486 }
487
488
489 @Override
490 public String format(String bundleName, Locale locale, String key, Object arg1, Object arg2) {
491 return format(bundleName, locale, key, new Object[] {arg1, arg2});
492 }
493
494
495 @Override
496 public String format(String bundleName, Locale locale, String key, Object[] args) {
497 if (locale == null) {
498 locale = getLocale(null);
499 }
500
501 String value = getString(bundleName, locale, key);
502 if (args == null) {
503 args = NO_ARGS;
504 }
505
506 MessageFormat messageFormat = new MessageFormat("");
507 messageFormat.setLocale(locale);
508 messageFormat.applyPattern(value);
509
510 return messageFormat.format(args);
511 }
512
513 private String getStringOrNull(ResourceBundle rb, String key) {
514 if (rb != null) {
515 try {
516 return rb.getString(key);
517 } catch (MissingResourceException ignored) {
518
519 }
520 }
521 return null;
522 }
523 }
524 }