1919package org .apache .maven .plugins .surefire .report ;
2020
2121import 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 ;
2227import java .util .ArrayList ;
2328import java .util .Iterator ;
2429import java .util .List ;
2530import java .util .Locale ;
31+ import java .util .MissingResourceException ;
32+ import java .util .ResourceBundle ;
2633
2734import org .apache .maven .model .ReportPlugin ;
2835import org .apache .maven .plugin .surefire .log .api .ConsoleLogger ;
36+ import org .apache .maven .plugins .annotations .Component ;
2937import org .apache .maven .plugins .annotations .Parameter ;
3038import org .apache .maven .project .MavenProject ;
3139import org .apache .maven .reporting .AbstractMavenReport ;
3240import org .apache .maven .reporting .MavenReportException ;
41+ import org .apache .maven .settings .Settings ;
3342import 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 ;
3449
3550import static java .util .Collections .addAll ;
3651import static org .apache .maven .plugins .surefire .report .SurefireReportParser .hasReportFiles ;
@@ -87,6 +102,27 @@ public abstract class AbstractSurefireReportMojo extends AbstractMavenReport {
87102 @ Parameter (defaultValue = "false" , property = "aggregate" )
88103 private boolean aggregate ;
89104
105+ /**
106+ * The current user system settings for use in Maven.
107+ */
108+ @ Parameter (defaultValue = "${settings}" , readonly = true , required = true )
109+ private Settings settings ;
110+
111+ /**
112+ * Path for a custom bundle instead of using the default one. <br>
113+ * Using this field, you could change the texts in the generated reports.
114+ *
115+ * @since 3.1.0
116+ */
117+ @ Parameter (defaultValue = "src/site/custom/surefire-report.properties" )
118+ private String customBundle ;
119+
120+ /**
121+ * Internationalization component
122+ */
123+ @ Component
124+ private I18N i18n ;
125+
90126 private List <File > resolvedReportsDirectories ;
91127
92128 /**
@@ -109,14 +145,6 @@ protected boolean isGeneratedWhenNoResults() {
109145 return false ;
110146 }
111147
112- public abstract void setTitle (String title );
113-
114- public abstract String getTitle ();
115-
116- public abstract void setDescription (String description );
117-
118- public abstract String getDescription ();
119-
120148 /**
121149 * {@inheritDoc}
122150 */
@@ -128,8 +156,9 @@ public void executeReport(Locale locale) throws MavenReportException {
128156
129157 SurefireReportRenderer r = new SurefireReportRenderer (
130158 getSink (),
159+ getI18N (locale ),
160+ getI18Nsection (),
131161 locale ,
132- getBundle (locale ),
133162 getConsoleLogger (),
134163 showSuccess ,
135164 getReportsDirectories (),
@@ -270,19 +299,44 @@ private String determineXrefLocation() {
270299 }
271300
272301 /**
273- * {@inheritDoc}
302+ * @param locale The locale
303+ * @param key The key to search for
304+ * @return The text appropriate for the locale.
274305 */
275- @ Override
276- public String getName (Locale locale ) {
277- return getBundle (locale ).getReportName ();
306+ protected String getI18nString (Locale locale , String key ) {
307+ return getI18N (locale ).getString ("surefire-report" , locale , "report." + getI18Nsection () + '.' + key );
278308 }
309+ /**
310+ * @param locale The local.
311+ * @return I18N for the locale
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+ // first load
320+ i18n = new CustomI18N (project , settings , customBundleFile , locale , i18n );
321+ }
322+ }
323+ }
279324
325+ return i18n ;
326+ }
280327 /**
281- * {@inheritDoc}
328+ * @return The according string for the section.
282329 */
283- @ Override
330+ protected abstract String getI18Nsection ();
331+
332+ /** {@inheritDoc} */
333+ public String getName (Locale locale ) {
334+ return getI18nString (locale , "name" );
335+ }
336+
337+ /** {@inheritDoc} */
284338 public String getDescription (Locale locale ) {
285- return getBundle (locale ). getReportDescription ( );
339+ return getI18nString (locale , "description" );
286340 }
287341
288342 /**
@@ -291,18 +345,199 @@ public String getDescription(Locale locale) {
291345 @ Override
292346 public abstract String getOutputName ();
293347
294- protected abstract LocalizedProperties getBundle (Locale locale , ClassLoader resourceBundleClassLoader );
295-
296348 protected final ConsoleLogger getConsoleLogger () {
297349 return new PluginConsoleLogger (getLog ());
298350 }
299351
300- final LocalizedProperties getBundle (Locale locale ) {
301- return getBundle (locale , getClass ().getClassLoader ());
302- }
303-
304352 @ Override
305353 protected MavenProject getProject () {
306354 return project ;
307355 }
356+
357+ // TODO Review, especially Locale.getDefault()
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+ // could not happen.
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+ /** {@inheritDoc} */
398+ public String getDefaultLanguage () {
399+ return locale .getLanguage ();
400+ }
401+
402+ /** {@inheritDoc} */
403+ public String getDefaultCountry () {
404+ return locale .getCountry ();
405+ }
406+
407+ /** {@inheritDoc} */
408+ public String getDefaultBundleName () {
409+ return bundleName ;
410+ }
411+
412+ /** {@inheritDoc} */
413+ public String [] getBundleNames () {
414+ return new String [] {bundleName };
415+ }
416+
417+ /** {@inheritDoc} */
418+ public ResourceBundle getBundle () {
419+ return bundle ;
420+ }
421+
422+ /** {@inheritDoc} */
423+ public ResourceBundle getBundle (String bundleName ) {
424+ return bundle ;
425+ }
426+
427+ /** {@inheritDoc} */
428+ public ResourceBundle getBundle (String bundleName , String languageHeader ) {
429+ return bundle ;
430+ }
431+
432+ /** {@inheritDoc} */
433+ public ResourceBundle getBundle (String bundleName , Locale locale ) {
434+ return bundle ;
435+ }
436+
437+ /** {@inheritDoc} */
438+ public Locale getLocale (String languageHeader ) {
439+ return new Locale (languageHeader );
440+ }
441+
442+ /** {@inheritDoc} */
443+ public String getString (String key ) {
444+ return getString (bundleName , locale , key );
445+ }
446+
447+ /** {@inheritDoc} */
448+ public String getString (String key , Locale locale ) {
449+ return getString (bundleName , locale , key );
450+ }
451+
452+ /** {@inheritDoc} */
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+ // try to load default
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+ // In which cases could this happen? And what should we do?
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+ // What does this exception mean?
489+ }
490+
491+ return value ;
492+ }
493+
494+ /** {@inheritDoc} */
495+ public String format (String key , Object arg1 ) {
496+ return format (bundleName , locale , key , new Object [] {arg1 });
497+ }
498+
499+ /** {@inheritDoc} */
500+ public String format (String key , Object arg1 , Object arg2 ) {
501+ return format (bundleName , locale , key , new Object [] {arg1 , arg2 });
502+ }
503+
504+ /** {@inheritDoc} */
505+ public String format (String bundleName , Locale locale , String key , Object arg1 ) {
506+ return format (bundleName , locale , key , new Object [] {arg1 });
507+ }
508+
509+ /** {@inheritDoc} */
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+ /** {@inheritDoc} */
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+ // intentional
538+ }
539+ }
540+ return null ;
541+ }
542+ }
308543}
0 commit comments