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