mirror of
https://github.com/apache/struts.git
synced 2026-08-05 22:56:59 +00:00
WW-5481 Extracts duplicated code
This commit is contained in:
@@ -67,24 +67,18 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
private final Set<String> missingBundles = ConcurrentHashMap.newKeySet();
|
||||
private final ConcurrentMap<Integer, ClassLoader> delegatedClassLoaderMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Adds the bundle to the internal list of default bundles.
|
||||
* If the bundle already exists in the list it will be re-added.
|
||||
*
|
||||
* @param resourceBundleName the name of the bundle to add.
|
||||
*/
|
||||
@Override
|
||||
public void addDefaultResourceBundle(String resourceBundleName) {
|
||||
public void addDefaultResourceBundle(String bundleName) {
|
||||
//make sure this doesn't get added more than once
|
||||
final ClassLoader ccl = getCurrentThreadContextClassLoader();
|
||||
synchronized (XWORK_MESSAGES_BUNDLE) {
|
||||
List<String> bundles = classLoaderMap.computeIfAbsent(ccl.hashCode(), k -> new CopyOnWriteArrayList<>());
|
||||
bundles.remove(resourceBundleName);
|
||||
bundles.add(0, resourceBundleName);
|
||||
bundles.remove(bundleName);
|
||||
bundles.add(0, bundleName);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Added default resource bundle '{}' to default resource bundles for the following classloader '{}'", resourceBundleName, ccl.toString());
|
||||
LOG.debug("Added default resource bundle '{}' to default resource bundles for the following classloader '{}'", bundleName, ccl.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,16 +107,8 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized message for the specified key, aTextName. Neither the key nor the
|
||||
* message is evaluated.
|
||||
*
|
||||
* @param aTextName the message key
|
||||
* @param locale the locale the message should be for
|
||||
* @return a localized message based on the specified key, or null if no localized message can be found for it
|
||||
*/
|
||||
@Override
|
||||
public String findDefaultText(String aTextName, Locale locale) {
|
||||
public String findDefaultText(String textKey, Locale locale) {
|
||||
List<String> localList = getCurrentBundleNames();
|
||||
|
||||
for (String bundleName : localList) {
|
||||
@@ -130,7 +116,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
if (bundle != null) {
|
||||
reloadBundles();
|
||||
try {
|
||||
return bundle.getString(aTextName);
|
||||
return bundle.getString(textKey);
|
||||
} catch (MissingResourceException e) {
|
||||
// will be logged when not found in any bundle
|
||||
}
|
||||
@@ -138,26 +124,17 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
}
|
||||
|
||||
if (devMode) {
|
||||
LOG.warn("Missing key [{}] in bundles [{}]!", aTextName, localList);
|
||||
LOG.warn("Missing key [{}] in bundles [{}]!", textKey, localList);
|
||||
} else {
|
||||
LOG.debug("Missing key [{}] in bundles [{}]!", aTextName, localList);
|
||||
LOG.debug("Missing key [{}] in bundles [{}]!", textKey, localList);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized message for the specified key, aTextName, substituting variables from the
|
||||
* array of params into the message. Neither the key nor the message is evaluated.
|
||||
*
|
||||
* @param aTextName the message key
|
||||
* @param locale the locale the message should be for
|
||||
* @param params an array of objects to be substituted into the message text
|
||||
* @return A formatted message based on the specified key, or null if no localized message can be found for it
|
||||
*/
|
||||
@Override
|
||||
public String findDefaultText(String aTextName, Locale locale, Object[] params) {
|
||||
String defaultText = findDefaultText(aTextName, locale);
|
||||
public String findDefaultText(String textKey, Locale locale, Object[] params) {
|
||||
String defaultText = findDefaultText(textKey, locale);
|
||||
if (defaultText != null) {
|
||||
MessageFormat mf = buildMessageFormat(defaultText, locale);
|
||||
return formatWithNullDetection(mf, params);
|
||||
@@ -165,51 +142,27 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param aTextName the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @param valueStack the OGNL value stack.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args,
|
||||
public String findText(ResourceBundle bundle, String textKey, Locale locale, String defaultMessage, Object[] args,
|
||||
ValueStack valueStack) {
|
||||
try {
|
||||
reloadBundles(valueStack.getContext());
|
||||
|
||||
String message = TextParseUtil.translateVariables(bundle.getString(aTextName), valueStack);
|
||||
String message = TextParseUtil.translateVariables(bundle.getString(textKey), valueStack);
|
||||
MessageFormat mf = buildMessageFormat(message, locale);
|
||||
|
||||
return formatWithNullDetection(mf, args);
|
||||
} catch (MissingResourceException ex) {
|
||||
if (devMode) {
|
||||
LOG.warn("Missing key [{}] in bundle [{}]!", aTextName, bundle);
|
||||
LOG.warn("Missing key [{}] in bundle [{}]!", textKey, bundle);
|
||||
} else {
|
||||
LOG.debug("Missing key [{}] in bundle [{}]!", aTextName, bundle);
|
||||
LOG.debug("Missing key [{}] in bundle [{}]!", textKey, bundle);
|
||||
}
|
||||
}
|
||||
|
||||
GetDefaultMessageReturnArg result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
|
||||
GetDefaultMessageReturnArg result = getDefaultMessage(textKey, locale, valueStack, args, defaultMessage);
|
||||
if (unableToFindTextForKey(result)) {
|
||||
LOG.warn("Unable to find text for key '{}' in ResourceBundles for locale '{}'", aTextName, locale);
|
||||
LOG.warn("Unable to find text for key '{}' in ResourceBundles for locale '{}'", textKey, locale);
|
||||
}
|
||||
return result != null ? result.message : null;
|
||||
}
|
||||
@@ -425,20 +378,10 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
this.searchDefaultBundlesFirst = Boolean.parseBoolean(searchDefaultBundlesFirst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the given resource bundle by it's name.
|
||||
* <p>
|
||||
* Will use <code>Thread.currentThread().getContextClassLoader()</code> as the classloader.
|
||||
* </p>
|
||||
*
|
||||
* @param aBundleName the name of the bundle (usually it's FQN classname).
|
||||
* @param locale the locale.
|
||||
* @return the bundle, <tt>null</tt> if not found.
|
||||
*/
|
||||
@Override
|
||||
public ResourceBundle findResourceBundle(String aBundleName, Locale locale) {
|
||||
public ResourceBundle findResourceBundle(String bundleName, Locale locale) {
|
||||
ClassLoader classLoader = getCurrentThreadContextClassLoader();
|
||||
String key = createMissesKey(String.valueOf(classLoader.hashCode()), aBundleName, locale);
|
||||
String key = createMissesKey(String.valueOf(classLoader.hashCode()), bundleName, locale);
|
||||
|
||||
if (missingBundles.contains(key)) {
|
||||
return null;
|
||||
@@ -449,7 +392,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
if (bundlesMap.containsKey(key)) {
|
||||
bundle = bundlesMap.get(key);
|
||||
} else {
|
||||
bundle = ResourceBundle.getBundle(aBundleName, locale, classLoader);
|
||||
bundle = ResourceBundle.getBundle(bundleName, locale, classLoader);
|
||||
bundlesMap.putIfAbsent(key, bundle);
|
||||
}
|
||||
} catch (MissingResourceException ex) {
|
||||
@@ -458,15 +401,15 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
if (bundlesMap.containsKey(key)) {
|
||||
bundle = bundlesMap.get(key);
|
||||
} else {
|
||||
bundle = ResourceBundle.getBundle(aBundleName, locale, delegatedClassLoaderMap.get(classLoader.hashCode()));
|
||||
bundle = ResourceBundle.getBundle(bundleName, locale, delegatedClassLoaderMap.get(classLoader.hashCode()));
|
||||
bundlesMap.putIfAbsent(key, bundle);
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName, e);
|
||||
LOG.debug("Missing resource bundle [{}]!", bundleName, e);
|
||||
missingBundles.add(key);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Missing resource bundle [{}]!", aBundleName);
|
||||
LOG.debug("Missing resource bundle [{}]!", bundleName);
|
||||
missingBundles.add(key);
|
||||
}
|
||||
}
|
||||
@@ -656,6 +599,36 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String extractIndexedName(String textKey) {
|
||||
String indexedTextName = null;
|
||||
// calculate indexedTextName (collection[*]) if applicable
|
||||
if (textKey.contains("[")) {
|
||||
int i = -1;
|
||||
|
||||
indexedTextName = textKey;
|
||||
|
||||
while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
|
||||
int j = indexedTextName.indexOf(']', i);
|
||||
String a = indexedTextName.substring(0, i);
|
||||
String b = indexedTextName.substring(j);
|
||||
indexedTextName = a + "[*" + b;
|
||||
}
|
||||
}
|
||||
return indexedTextName;
|
||||
}
|
||||
|
||||
protected void logMissingText(Class<?> startClazz, String textKey, Locale locale, GetDefaultMessageReturnArg result, String indexedTextName) {
|
||||
// could we find the text, if not log a WARN
|
||||
if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
|
||||
String warn = "Unable to find text for key '" + textKey + "' ";
|
||||
if (indexedTextName != null) {
|
||||
warn += " or indexed key '" + indexedTextName + "' ";
|
||||
}
|
||||
warn += "in class '" + startClazz.getName() + "' and locale '" + locale + "'";
|
||||
LOG.debug(warn);
|
||||
}
|
||||
}
|
||||
|
||||
static class MessageFormatKey {
|
||||
String pattern;
|
||||
Locale locale;
|
||||
|
||||
@@ -41,182 +41,42 @@ public class GlobalLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
|
||||
* with aTextName as the default message.
|
||||
*
|
||||
* @param aClass class name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class<?> aClass, String aTextName, Locale locale) {
|
||||
return findText(aClass, aTextName, locale, aTextName, new Object[0]);
|
||||
public String findText(Class<?> startClazz, String textKey, Locale locale) {
|
||||
return findText(startClazz, textKey, locale, textKey, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for the message in the default resource bundles.</li>
|
||||
* <li>If not found, return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* resource bundle
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class<?> aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
public String findText(Class<?> startClazz, String textKey, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
|
||||
|
||||
return findText(startClazz, textKey, locale, defaultMessage, args, valueStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for the message in the default resource bundles.</li>
|
||||
* <li>If not found, return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a DEBUG level log warning will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* @param valueStack the value stack to use to evaluate expressions instead of the
|
||||
* one in the ActionContext ThreadLocal
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class<?> aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack) {
|
||||
String indexedTextName = null;
|
||||
if (aTextName == null) {
|
||||
LOG.warn("Trying to find text with null key!");
|
||||
aTextName = "";
|
||||
}
|
||||
// calculate indexedTextName (collection[*]) if applicable
|
||||
if (aTextName.contains("[")) {
|
||||
int i = -1;
|
||||
|
||||
indexedTextName = aTextName;
|
||||
|
||||
while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
|
||||
int j = indexedTextName.indexOf(']', i);
|
||||
String a = indexedTextName.substring(0, i);
|
||||
String b = indexedTextName.substring(j);
|
||||
indexedTextName = a + "[*" + b;
|
||||
}
|
||||
public String findText(Class<?> startClazz, String textKey, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack) {
|
||||
if (textKey == null) {
|
||||
LOG.debug("Key is null, short-circuit to default message");
|
||||
return defaultMessage;
|
||||
}
|
||||
String indexedTextName = extractIndexedName(textKey);
|
||||
|
||||
// get default
|
||||
GetDefaultMessageReturnArg result = getDefaultMessageWithAlternateKey(aTextName, indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
GetDefaultMessageReturnArg result = getDefaultMessageWithAlternateKey(textKey, indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
|
||||
// could we find the text, if not log a warn
|
||||
if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
|
||||
String warn = "Unable to find text for key '" + aTextName + "' ";
|
||||
if (indexedTextName != null) {
|
||||
warn += " or indexed key '" + indexedTextName + "' ";
|
||||
}
|
||||
warn += "in class '" + aClass.getName() + "' and locale '" + locale + "'";
|
||||
LOG.debug(warn);
|
||||
}
|
||||
logMissingText(startClazz, textKey, locale, result, indexedTextName);
|
||||
|
||||
return result != null ? result.message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource bundle
|
||||
* with aTextName as the default message.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle a resource bundle name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(ResourceBundle, String, Locale, String, Object[])
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale) {
|
||||
return findText(bundle, aTextName, locale, aTextName, new Object[0]);
|
||||
public String findText(ResourceBundle bundle, String textKey, Locale locale) {
|
||||
return findText(bundle, textKey, locale, textKey, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param aTextName the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
public String findText(ResourceBundle bundle, String textKey, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
|
||||
return findText(bundle, textKey, locale, defaultMessage, args, valueStack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,24 +26,168 @@ import java.util.ResourceBundle;
|
||||
|
||||
public interface LocalizedTextProvider extends Serializable {
|
||||
|
||||
String findDefaultText(String aTextName, Locale locale);
|
||||
/**
|
||||
* Returns a localized message for the specified key, aTextName. Neither the key nor the
|
||||
* message is evaluated.
|
||||
*
|
||||
* @param textKey the message key
|
||||
* @param locale the locale the message should be for
|
||||
* @return a localized message based on the specified key, or null if no localized message can be found for it
|
||||
*/
|
||||
String findDefaultText(String textKey, Locale locale);
|
||||
|
||||
String findDefaultText(String aTextName, Locale locale, Object[] params);
|
||||
/**
|
||||
* Returns a localized message for the specified key, aTextName, substituting variables from the
|
||||
* array of params into the message. Neither the key nor the message is evaluated.
|
||||
*
|
||||
* @param textKey the message key
|
||||
* @param locale the locale the message should be for
|
||||
* @param params an array of objects to be substituted into the message text
|
||||
* @return A formatted message based on the specified key, or null if no localized message can be found for it
|
||||
*/
|
||||
String findDefaultText(String textKey, Locale locale, Object[] params);
|
||||
|
||||
ResourceBundle findResourceBundle(String aBundleName, Locale locale);
|
||||
/**
|
||||
* Finds the given resource bundle by it's name.
|
||||
* <p>
|
||||
* Will use <code>Thread.currentThread().getContextClassLoader()</code> as the classloader.
|
||||
* </p>
|
||||
*
|
||||
* @param bundleName the name of the bundle (usually it's FQN classname).
|
||||
* @param locale the locale.
|
||||
* @return the bundle, <tt>null</tt> if not found.
|
||||
*/
|
||||
ResourceBundle findResourceBundle(String bundleName, Locale locale);
|
||||
|
||||
String findText(Class<?> aClass, String aTextName, Locale locale);
|
||||
/**
|
||||
* Calls {@link #findText(Class startClazz, String textKey, Locale locale, String defaultMessage, Object[] args)}
|
||||
* with textKey as the default message.
|
||||
*
|
||||
* @param startClazz class name
|
||||
* @param textKey text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(Class startClazz, String textKey, Locale locale, String defaultMessage, Object[] args)
|
||||
*/
|
||||
String findText(Class<?> startClazz, String textKey, Locale locale);
|
||||
|
||||
String findText(Class<?> aClass, String aTextName, Locale locale, String defaultMessage, Object[] args);
|
||||
/**
|
||||
* Finds a localized text message for the given key, textKey. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for the message in the default resource bundles.</li>
|
||||
* <li>If not found, return defaultMessage</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
*
|
||||
* @param startClazz the class whose name to use as the start point for the search
|
||||
* @param textKey the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* resource bundle
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
String findText(Class<?> startClazz, String textKey, Locale locale, String defaultMessage, Object[] args);
|
||||
|
||||
String findText(Class<?> aClass, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
|
||||
/**
|
||||
* Finds a localized text message for the given key, textKey. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Look for the message in the default resource bundles.</li>
|
||||
* <li>If not found, return defaultMessage</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a DEBUG level log warning will be logged.
|
||||
*
|
||||
* @param startClazz the class whose name to use as the start point for the search
|
||||
* @param textKey the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* @param valueStack the value stack to use to evaluate expressions instead of the
|
||||
* one in the ActionContext ThreadLocal
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
String findText(Class<?> startClazz, String textKey, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
|
||||
|
||||
String findText(ResourceBundle bundle, String aTextName, Locale locale);
|
||||
/**
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource bundle
|
||||
* with aTextName as the default message.
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
*
|
||||
* @param bundle a resource bundle name
|
||||
* @param textKey text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(ResourceBundle, String, Locale, String, Object[])
|
||||
*/
|
||||
String findText(ResourceBundle bundle, String textKey, Locale locale);
|
||||
|
||||
String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args);
|
||||
/**
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param textKey the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
String findText(ResourceBundle bundle, String textKey, Locale locale, String defaultMessage, Object[] args);
|
||||
|
||||
String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
|
||||
/**
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param textKey the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @param valueStack the OGNL value stack.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
String findText(ResourceBundle bundle, String textKey, Locale locale, String defaultMessage, Object[] args, ValueStack valueStack);
|
||||
|
||||
void addDefaultResourceBundle(String resourceBundleName);
|
||||
/**
|
||||
* Adds the bundle to the internal list of default bundles.
|
||||
* If the bundle already exists in the list it will be re-added.
|
||||
*
|
||||
* @param bundleName the name of the bundle to add.
|
||||
*/
|
||||
void addDefaultResourceBundle(String bundleName);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.struts2.text;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.ActionContext;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
import org.apache.struts2.ModelDriven;
|
||||
@@ -25,8 +27,6 @@ import org.apache.struts2.conversion.impl.XWorkConverter;
|
||||
import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.util.reflection.ReflectionProvider;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.Locale;
|
||||
@@ -46,150 +46,26 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
addDefaultResourceBundle(STRUTS_MESSAGES_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
|
||||
* with aTextName as the default message.
|
||||
*
|
||||
* @param aClass class name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class<?> aClass, String aTextName, Locale locale) {
|
||||
return findText(aClass, aTextName, locale, aTextName, new Object[0]);
|
||||
public String findText(Class<?> startClazz, String textKey, Locale locale) {
|
||||
return findText(startClazz, textKey, locale, textKey, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>If {@link #searchDefaultBundlesFirst} is <code>true</code>, look for the message in the default resource bundles first.</li>
|
||||
* <li>Look for message in aClass' class hierarchy.
|
||||
* <ol>
|
||||
* <li>Look for the message in a resource bundle for aClass</li>
|
||||
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
|
||||
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
|
||||
* </ol></li>
|
||||
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
|
||||
* the model's class hierarchy (repeat sub-steps listed above).</li>
|
||||
* <li>If not found, look for message in child property. This is determined by evaluating
|
||||
* the message key as an OGNL expression. For example, if the key is
|
||||
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
|
||||
* object. If so, repeat the entire process from the beginning with the object's class as
|
||||
* aClass and "address.state" as the message key.</li>
|
||||
* <li>If not found, look for the message in aClass' package hierarchy.</li>
|
||||
* <li>If still not found, look for the message in the default resource bundles
|
||||
* (Note: the lookup is not repeated again if {@link #searchDefaultBundlesFirst} was <code>true</code>).</li>
|
||||
* <li>Return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* resource bundle
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class<?> aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
public String findText(Class<?> startClazz, String textKey, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
|
||||
return findText(startClazz, textKey, locale, defaultMessage, args, valueStack);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName. Both the key and the message
|
||||
* itself is evaluated as required. The following algorithm is used to find the requested
|
||||
* message:
|
||||
* </p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>If {@link #searchDefaultBundlesFirst} is <code>true</code>, look for the message in the default resource bundles first.</li>
|
||||
* <li>Look for message in aClass' class hierarchy.
|
||||
* <ol>
|
||||
* <li>Look for the message in a resource bundle for aClass</li>
|
||||
* <li>If not found, look for the message in a resource bundle for any implemented interface</li>
|
||||
* <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
|
||||
* </ol></li>
|
||||
* <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
|
||||
* the model's class hierarchy (repeat sub-steps listed above).</li>
|
||||
* <li>If not found, look for message in child property. This is determined by evaluating
|
||||
* the message key as an OGNL expression. For example, if the key is
|
||||
* <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
|
||||
* object. If so, repeat the entire process from the beginning with the object's class as
|
||||
* aClass and "address.state" as the message key.</li>
|
||||
* <li>If not found, look for the message in aClass' package hierarchy.</li>
|
||||
* <li>If still not found, look for the message in the default resource bundles
|
||||
* (Note: the lookup is not repeated again if {@link #searchDefaultBundlesFirst} was <code>true</code>).</li>
|
||||
* <li>Return defaultMessage</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
|
||||
* message for that specific key cannot be found, the general form will also be looked up
|
||||
* (i.e. user.phone[*]).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a DEBUG level log warning will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param aClass the class whose name to use as the start point for the search
|
||||
* @param aTextName the key to find the text message for
|
||||
* @param locale the locale the message should be for
|
||||
* @param defaultMessage the message to be returned if no text message can be found in any
|
||||
* resource bundle
|
||||
* @param args arguments
|
||||
* @param valueStack the value stack to use to evaluate expressions instead of the
|
||||
* one in the ActionContext ThreadLocal
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(Class<?> aClass, String aTextName, Locale locale, String defaultMessage, Object[] args,
|
||||
public String findText(Class<?> startClazz, String textKey, Locale locale, String defaultMessage, Object[] args,
|
||||
ValueStack valueStack) {
|
||||
String indexedTextName = null;
|
||||
if (aTextName == null) {
|
||||
LOG.warn("Trying to find text with null key!");
|
||||
aTextName = "";
|
||||
}
|
||||
// calculate indexedTextName (collection[*]) if applicable
|
||||
if (aTextName.contains("[")) {
|
||||
int i = -1;
|
||||
|
||||
indexedTextName = aTextName;
|
||||
|
||||
while ((i = indexedTextName.indexOf('[', i + 1)) != -1) {
|
||||
int j = indexedTextName.indexOf(']', i);
|
||||
String a = indexedTextName.substring(0, i);
|
||||
String b = indexedTextName.substring(j);
|
||||
indexedTextName = a + "[*" + b;
|
||||
}
|
||||
if (textKey == null) {
|
||||
LOG.debug("Key is null, short-circuit to default message");
|
||||
return defaultMessage;
|
||||
}
|
||||
String indexedTextName = extractIndexedName(textKey);
|
||||
|
||||
// Allow for and track an early lookup for the message in the default resource bundles first, before searching the class hierarchy.
|
||||
// The early lookup is only performed when the text provider has been configured to do so, otherwise follow the standard processing order.
|
||||
@@ -198,21 +74,21 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
|
||||
// If search default bundles first is set true, call alternative logic first.
|
||||
if (searchDefaultBundlesFirst) {
|
||||
result = getDefaultMessageWithAlternateKey(aTextName, indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
result = getDefaultMessageWithAlternateKey(textKey, indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
performedInitialDefaultBundlesMessageLookup = true;
|
||||
if (!unableToFindTextForKey(result)) {
|
||||
return result.message; // Found a message in the default resource bundles for aTextName or indexedTextName.
|
||||
return result.message; // Found a message in the default resource bundles for textKey or indexedTextName.
|
||||
}
|
||||
}
|
||||
|
||||
// search up class hierarchy
|
||||
String msg = findMessage(aClass, aTextName, indexedTextName, locale, args, null, valueStack);
|
||||
String msg = findMessage(startClazz, textKey, indexedTextName, locale, args, null, valueStack);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (ModelDriven.class.isAssignableFrom(aClass)) {
|
||||
if (ModelDriven.class.isAssignableFrom(startClazz)) {
|
||||
ActionContext context = ActionContext.getContext();
|
||||
// search up model's class hierarchy
|
||||
ActionInvocation actionInvocation = context.getActionInvocation();
|
||||
@@ -223,7 +99,7 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
if (action instanceof ModelDriven) {
|
||||
Object model = ((ModelDriven<?>) action).getModel();
|
||||
if (model != null) {
|
||||
msg = findMessage(model.getClass(), aTextName, indexedTextName, locale, args, null, valueStack);
|
||||
msg = findMessage(model.getClass(), textKey, indexedTextName, locale, args, null, valueStack);
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
}
|
||||
@@ -233,7 +109,7 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
}
|
||||
|
||||
// nothing still? alright, search the package hierarchy now
|
||||
for (Class<?> clazz = aClass;
|
||||
for (Class<?> clazz = startClazz;
|
||||
(clazz != null) && !clazz.equals(Object.class);
|
||||
clazz = clazz.getSuperclass()) {
|
||||
|
||||
@@ -241,7 +117,7 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
while (basePackageName.lastIndexOf('.') != -1) {
|
||||
basePackageName = basePackageName.substring(0, basePackageName.lastIndexOf('.'));
|
||||
String packageName = basePackageName + ".package";
|
||||
msg = getMessage(packageName, locale, aTextName, valueStack, args);
|
||||
msg = getMessage(packageName, locale, textKey, valueStack, args);
|
||||
|
||||
if (msg != null) {
|
||||
return msg;
|
||||
@@ -258,22 +134,22 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
}
|
||||
|
||||
// see if it's a child property
|
||||
int idx = aTextName.indexOf('.');
|
||||
int idx = textKey.indexOf('.');
|
||||
|
||||
if (idx != -1) {
|
||||
String newKey = null;
|
||||
String prop = null;
|
||||
|
||||
if (aTextName.startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
|
||||
idx = aTextName.indexOf('.', XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
|
||||
if (textKey.startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
|
||||
idx = textKey.indexOf('.', XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length());
|
||||
|
||||
if (idx != -1) {
|
||||
prop = aTextName.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), idx);
|
||||
newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX + aTextName.substring(idx + 1);
|
||||
prop = textKey.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), idx);
|
||||
newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX + textKey.substring(idx + 1);
|
||||
}
|
||||
} else {
|
||||
prop = aTextName.substring(0, idx);
|
||||
newKey = aTextName.substring(idx + 1);
|
||||
prop = textKey.substring(0, idx);
|
||||
newKey = textKey.substring(idx + 1);
|
||||
}
|
||||
|
||||
if (prop != null) {
|
||||
@@ -310,74 +186,28 @@ public class StrutsLocalizedTextProvider extends AbstractLocalizedTextProvider {
|
||||
// Note: The default bundles lookup may already have been performed (via alternate early lookup),
|
||||
// so we check first to avoid repeating the same operation twice.
|
||||
if (!performedInitialDefaultBundlesMessageLookup) {
|
||||
result = getDefaultMessageWithAlternateKey(aTextName, indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
result = getDefaultMessageWithAlternateKey(textKey, indexedTextName, locale, valueStack, args, defaultMessage);
|
||||
}
|
||||
|
||||
// could we find the text, if not log a warn
|
||||
if (unableToFindTextForKey(result) && LOG.isDebugEnabled()) {
|
||||
String warn = "Unable to find text for key '" + aTextName + "' ";
|
||||
if (indexedTextName != null) {
|
||||
warn += " or indexed key '" + indexedTextName + "' ";
|
||||
}
|
||||
warn += "in class '" + aClass.getName() + "' and locale '" + locale + "'";
|
||||
LOG.debug(warn);
|
||||
}
|
||||
logMissingText(startClazz, textKey, locale, result, indexedTextName);
|
||||
|
||||
return result != null ? result.message : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource bundle
|
||||
* with aTextName as the default message.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle a resource bundle name
|
||||
* @param aTextName text name
|
||||
* @param locale the locale
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
* @see #findText(java.util.ResourceBundle, String, java.util.Locale, String, Object[])
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale) {
|
||||
return findText(bundle, aTextName, locale, aTextName, new Object[0]);
|
||||
public String findText(ResourceBundle bundle, String textKey, Locale locale) {
|
||||
return findText(bundle, textKey, locale, textKey, new Object[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Finds a localized text message for the given key, aTextName, in the specified resource
|
||||
* bundle.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is found, it will also be interpolated. Anything within <code>${...}</code>
|
||||
* will be treated as an OGNL expression and evaluated as such.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a message is <b>not</b> found a WARN log will be logged.
|
||||
* </p>
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @param aTextName the key
|
||||
* @param locale the locale
|
||||
* @param defaultMessage the default message to use if no message was found in the bundle
|
||||
* @param args arguments for the message formatter.
|
||||
* @return the localized text, or null if none can be found and no defaultMessage is provided
|
||||
*/
|
||||
@Override
|
||||
public String findText(ResourceBundle bundle, String aTextName, Locale locale, String defaultMessage, Object[] args) {
|
||||
public String findText(ResourceBundle bundle, String textKey, Locale locale, String defaultMessage, Object[] args) {
|
||||
ValueStack valueStack = ActionContext.getContext().getValueStack();
|
||||
return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
|
||||
return findText(bundle, textKey, locale, defaultMessage, args, valueStack);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setReflectionProvider(ReflectionProvider reflectionProvider) {
|
||||
this.reflectionProvider = reflectionProvider;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user