Skip to content

Commit

Permalink
[SUREFIRE-2160] Replace LocalizedProperties with (Custom)I18N approac…
Browse files Browse the repository at this point in the history
…h from MPIR
  • Loading branch information
michael-o committed May 1, 2023
1 parent 33d30c6 commit 6718f14
Show file tree
Hide file tree
Showing 21 changed files with 530 additions and 321 deletions.
Expand Up @@ -19,18 +19,33 @@
package org.apache.maven.plugins.surefire.report;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.apache.maven.model.ReportPlugin;
import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.AbstractMavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.apache.maven.settings.Settings;
import org.apache.maven.shared.utils.PathTool;
import org.codehaus.plexus.i18n.I18N;
import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;

import static java.util.Collections.addAll;
import static org.apache.maven.plugins.surefire.report.SurefireReportParser.hasReportFiles;
Expand Down Expand Up @@ -87,6 +102,27 @@ public abstract class AbstractSurefireReportMojo extends AbstractMavenReport {
@Parameter(defaultValue = "false", property = "aggregate")
private boolean aggregate;

/**
* The current user system settings for use in Maven.
*/
@Parameter(defaultValue = "${settings}", readonly = true, required = true)
private Settings settings;

/**
* Path for a custom bundle instead of using the default one. <br>
* Using this field, you could change the texts in the generated reports.
*
* @since 3.1.0
*/
@Parameter(defaultValue = "src/site/custom/surefire-report.properties")
private String customBundle;

/**
* Internationalization component
*/
@Component
private I18N i18n;

private List<File> resolvedReportsDirectories;

/**
Expand All @@ -109,14 +145,6 @@ protected boolean isGeneratedWhenNoResults() {
return false;
}

public abstract void setTitle(String title);

public abstract String getTitle();

public abstract void setDescription(String description);

public abstract String getDescription();

/**
* {@inheritDoc}
*/
Expand All @@ -128,8 +156,9 @@ public void executeReport(Locale locale) throws MavenReportException {

SurefireReportRenderer r = new SurefireReportRenderer(
getSink(),
getI18N(locale),
getI18Nsection(),
locale,
getBundle(locale),
getConsoleLogger(),
showSuccess,
getReportsDirectories(),
Expand Down Expand Up @@ -270,19 +299,44 @@ private String determineXrefLocation() {
}

/**
* {@inheritDoc}
* @param locale The locale
* @param key The key to search for
* @return The text appropriate for the locale.
*/
@Override
public String getName(Locale locale) {
return getBundle(locale).getReportName();
protected String getI18nString(Locale locale, String key) {
return getI18N(locale).getString("surefire-report", locale, "report." + getI18Nsection() + '.' + key);
}
/**
* @param locale The local.
* @return I18N for the locale
*/
protected I18N getI18N(Locale locale) {
if (customBundle != null) {
File customBundleFile = new File(customBundle);
if (customBundleFile.isFile() && customBundleFile.getName().endsWith(".properties")) {
if (!i18n.getClass().isAssignableFrom(CustomI18N.class)
|| !i18n.getDefaultLanguage().equals(locale.getLanguage())) {
// first load
i18n = new CustomI18N(project, settings, customBundleFile, locale, i18n);
}
}
}

return i18n;
}
/**
* {@inheritDoc}
* @return The according string for the section.
*/
@Override
protected abstract String getI18Nsection();

/** {@inheritDoc} */
public String getName(Locale locale) {
return getI18nString(locale, "name");
}

/** {@inheritDoc} */
public String getDescription(Locale locale) {
return getBundle(locale).getReportDescription();
return getI18nString(locale, "description");
}

/**
Expand All @@ -291,18 +345,199 @@ public String getDescription(Locale locale) {
@Override
public abstract String getOutputName();

protected abstract LocalizedProperties getBundle(Locale locale, ClassLoader resourceBundleClassLoader);

protected final ConsoleLogger getConsoleLogger() {
return new PluginConsoleLogger(getLog());
}

final LocalizedProperties getBundle(Locale locale) {
return getBundle(locale, getClass().getClassLoader());
}

@Override
protected MavenProject getProject() {
return project;
}

// TODO Review, especially Locale.getDefault()
private static class CustomI18N implements I18N {
private final MavenProject project;

private final Settings settings;

private final String bundleName;

private final Locale locale;

private final I18N i18nOriginal;

private ResourceBundle bundle;

private static final Object[] NO_ARGS = new Object[0];

CustomI18N(MavenProject project, Settings settings, File customBundleFile, Locale locale, I18N i18nOriginal) {
super();
this.project = project;
this.settings = settings;
this.locale = locale;
this.i18nOriginal = i18nOriginal;
this.bundleName = customBundleFile
.getName()
.substring(0, customBundleFile.getName().indexOf(".properties"));

URLClassLoader classLoader = null;
try {
classLoader = new URLClassLoader(
new URL[] {customBundleFile.getParentFile().toURI().toURL()}, null);
} catch (MalformedURLException e) {
// could not happen.
}

this.bundle = ResourceBundle.getBundle(this.bundleName, locale, classLoader);
if (!this.bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
this.bundle = ResourceBundle.getBundle(this.bundleName, Locale.getDefault(), classLoader);
}
}

/** {@inheritDoc} */
public String getDefaultLanguage() {
return locale.getLanguage();
}

/** {@inheritDoc} */
public String getDefaultCountry() {
return locale.getCountry();
}

/** {@inheritDoc} */
public String getDefaultBundleName() {
return bundleName;
}

/** {@inheritDoc} */
public String[] getBundleNames() {
return new String[] {bundleName};
}

/** {@inheritDoc} */
public ResourceBundle getBundle() {
return bundle;
}

/** {@inheritDoc} */
public ResourceBundle getBundle(String bundleName) {
return bundle;
}

/** {@inheritDoc} */
public ResourceBundle getBundle(String bundleName, String languageHeader) {
return bundle;
}

/** {@inheritDoc} */
public ResourceBundle getBundle(String bundleName, Locale locale) {
return bundle;
}

/** {@inheritDoc} */
public Locale getLocale(String languageHeader) {
return new Locale(languageHeader);
}

/** {@inheritDoc} */
public String getString(String key) {
return getString(bundleName, locale, key);
}

/** {@inheritDoc} */
public String getString(String key, Locale locale) {
return getString(bundleName, locale, key);
}

/** {@inheritDoc} */
public String getString(String bundleName, Locale locale, String key) {
String value;

if (locale == null) {
locale = getLocale(null);
}

ResourceBundle rb = getBundle(bundleName, locale);
value = getStringOrNull(rb, key);

if (value == null) {
// try to load default
value = i18nOriginal.getString(bundleName, locale, key);
}

if (!value.contains("${")) {
return value;
}

final RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
try {
interpolator.addValueSource(new EnvarBasedValueSource());
} catch (final IOException e) {
// In which cases could this happen? And what should we do?
}

interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties()));
interpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties()));
interpolator.addValueSource(new PrefixedObjectValueSource("project", project));
interpolator.addValueSource(new PrefixedObjectValueSource("pom", project));
interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings));

try {
value = interpolator.interpolate(value);
} catch (final InterpolationException e) {
// What does this exception mean?
}

return value;
}

/** {@inheritDoc} */
public String format(String key, Object arg1) {
return format(bundleName, locale, key, new Object[] {arg1});
}

/** {@inheritDoc} */
public String format(String key, Object arg1, Object arg2) {
return format(bundleName, locale, key, new Object[] {arg1, arg2});
}

/** {@inheritDoc} */
public String format(String bundleName, Locale locale, String key, Object arg1) {
return format(bundleName, locale, key, new Object[] {arg1});
}

/** {@inheritDoc} */
public String format(String bundleName, Locale locale, String key, Object arg1, Object arg2) {
return format(bundleName, locale, key, new Object[] {arg1, arg2});
}

/** {@inheritDoc} */
public String format(String bundleName, Locale locale, String key, Object[] args) {
if (locale == null) {
locale = getLocale(null);
}

String value = getString(bundleName, locale, key);
if (args == null) {
args = NO_ARGS;
}

MessageFormat messageFormat = new MessageFormat("");
messageFormat.setLocale(locale);
messageFormat.applyPattern(value);

return messageFormat.format(args);
}

private String getStringOrNull(ResourceBundle rb, String key) {
if (rb != null) {
try {
return rb.getString(key);
} catch (MissingResourceException ignored) {
// intentional
}
}
return null;
}
}
}

0 comments on commit 6718f14

Please sign in to comment.