Provides support for localization in XWork.
To clarify #5, while traversing the package hierarchy, Struts 2 will look for a file package.properties:
If FooAction.properties does not exist, com/acme/action/package.properties will be searched for, if
not found com/acme/package.properties, if not found com/package.properties, etc.
A global resource bundle could be specified programatically, as well as the locale.
Method from com.opensymphony.xwork2.util.LocalizedTextUtil Detail: |
public static void addDefaultResourceBundle(String resourceBundleName) {
//make sure this doesn't get added more than once
synchronized (DEFAULT_RESOURCE_BUNDLES) {
DEFAULT_RESOURCE_BUNDLES.remove(resourceBundleName);
DEFAULT_RESOURCE_BUNDLES.add(0, resourceBundleName);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Added default resource bundle '" + resourceBundleName + "' to default resource bundles = " + DEFAULT_RESOURCE_BUNDLES);
}
}
Add's the bundle to the internal list of default bundles.
If the bundle already exists in the list it will be readded. |
public static void clearBundle(String bundleName) {
synchronized (bundlesMap) {
bundlesMap.remove(bundleName);
}
}
Removes the bundle from any cached "misses" |
public static void clearDefaultResourceBundles() {
if (DEFAULT_RESOURCE_BUNDLES != null) {
synchronized (DEFAULT_RESOURCE_BUNDLES) {
DEFAULT_RESOURCE_BUNDLES.clear();
DEFAULT_RESOURCE_BUNDLES.add("com/opensymphony/xwork2/xwork-messages");
}
} else {
synchronized (DEFAULT_RESOURCE_BUNDLES) {
DEFAULT_RESOURCE_BUNDLES.add("com/opensymphony/xwork2/xwork-messages");
}
}
}
Clears the internal list of resource bundles. |
public static String findDefaultText(String aTextName,
Locale locale) {
List< String > localList = DEFAULT_RESOURCE_BUNDLES;
for (String bundleName : localList) {
ResourceBundle bundle = findResourceBundle(bundleName, locale);
if (bundle != null) {
reloadBundles();
try {
return bundle.getString(aTextName);
} catch (MissingResourceException e) {
// ignore and try others
}
}
}
return null;
}
Returns a localized message for the specified key, aTextName. Neither the key nor the
message is evaluated. |
public static String findDefaultText(String aTextName,
Locale locale,
Object[] params) {
String defaultText = findDefaultText(aTextName, locale);
if (defaultText != null) {
MessageFormat mf = buildMessageFormat(defaultText, locale);
return mf.format(params);
}
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. |
public static ResourceBundle findResourceBundle(String aBundleName,
Locale locale) {
String key = createMissesKey(aBundleName, locale);
ResourceBundle bundle;
try {
if (!bundlesMap.containsKey(key)) {
bundle = ResourceBundle.getBundle(
aBundleName,
locale,
Thread.currentThread().getContextClassLoader());
bundlesMap.put(key, bundle);
}
bundle = bundlesMap.get(key);
} catch (MissingResourceException ex) {
if ( delegatedClassLoader != null) {
try {
if (!bundlesMap.containsKey(key)) {
bundle = ResourceBundle.getBundle(
aBundleName,
locale,
delegatedClassLoader);
bundlesMap.put(key, bundle);
}
bundle = bundlesMap.get(key);
} catch (MissingResourceException e) {
bundle = EMPTY_BUNDLE;
bundlesMap.put(key, bundle);
}
} else {
bundle = EMPTY_BUNDLE;
bundlesMap.put(key, bundle);
}
}
return (bundle == EMPTY_BUNDLE) ? null : bundle;
}
Finds the given resorce bundle by it's name.
Will use Thread.currentThread().getContextClassLoader() as the classloader.
If #delegatedClassLoader is defined and the bundle cannot be found the current
classloader it will delegate to that. |
public static String findText(Class aClass,
String aTextName,
Locale locale) {
return findText(aClass, aTextName, locale, aTextName, new Object[0]);
}
|
public static String findText(ResourceBundle bundle,
String aTextName,
Locale locale) {
return findText(bundle, aTextName, locale, aTextName, new Object[0]);
}
Finds a localized text message for the given key, aTextName, in the specified resource bundle
with aTextName as the default message.
If a message is found, it will also be interpolated. Anything within ${...}
will be treated as an OGNL expression and evaluated as such. |
public static String findText(Class aClass,
String aTextName,
Locale locale,
String defaultMessage,
Object[] args) {
ValueStack valueStack = ActionContext.getContext().getValueStack();
return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
}
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:
- Look for message in aClass' class hierarchy.
- Look for the message in a resource bundle for aClass
- If not found, look for the message in a resource bundle for any implemented interface
- If not found, traverse up the Class' hierarchy and repeat from the first sub-step
- If not found and aClass is a ModelDriven Action, then look for message in
the model's class hierarchy (repeat sub-steps listed above).
- 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
user.address.state, then it will attempt to see if "user" can be resolved into an
object. If so, repeat the entire process fromthe beginning with the object's class as
aClass and "address.state" as the message key.
- If not found, look for the message in aClass' package hierarchy.
- If still not found, look for the message in the default resource bundles.
- Return defaultMessage
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[*]).
If a message is found, it will also be interpolated. Anything within ${...}
will be treated as an OGNL expression and evaluated as such. |
public static String findText(ResourceBundle bundle,
String aTextName,
Locale locale,
String defaultMessage,
Object[] args) {
ValueStack valueStack = ActionContext.getContext().getValueStack();
return findText(bundle, aTextName, locale, defaultMessage, args, valueStack);
}
Finds a localized text message for the given key, aTextName, in the specified resource
bundle.
If a message is found, it will also be interpolated. Anything within ${...}
will be treated as an OGNL expression and evaluated as such.
If a message is not found a WARN log will be logged. |
public static 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;
}
}
// search up class hierarchy
String msg = findMessage(aClass, aTextName, indexedTextName, locale, args, null, valueStack);
if (msg != null) {
return msg;
}
if (ModelDriven.class.isAssignableFrom(aClass)) {
ActionContext context = ActionContext.getContext();
// search up model's class hierarchy
ActionInvocation actionInvocation = context.getActionInvocation();
// ActionInvocation may be null if we're being run from a Sitemesh filter, so we won't get model texts if this is null
if (actionInvocation != null) {
Object action = actionInvocation.getAction();
if (action instanceof ModelDriven) {
Object model = ((ModelDriven) action).getModel();
if (model != null) {
msg = findMessage(model.getClass(), aTextName, indexedTextName, locale, args, null, valueStack);
if (msg != null) {
return msg;
}
}
}
}
}
// nothing still? alright, search the package hierarchy now
for (Class clazz = aClass;
(clazz != null) && !clazz.equals(Object.class);
clazz = clazz.getSuperclass()) {
String basePackageName = clazz.getName();
while (basePackageName.lastIndexOf('.') != -1) {
basePackageName = basePackageName.substring(0, basePackageName.lastIndexOf('.'));
String packageName = basePackageName + ".package";
msg = getMessage(packageName, locale, aTextName, valueStack, args);
if (msg != null) {
return msg;
}
if (indexedTextName != null) {
msg = getMessage(packageName, locale, indexedTextName, valueStack, args);
if (msg != null) {
return msg;
}
}
}
}
// see if it's a child property
int idx = aTextName.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 (idx != -1) {
prop = aTextName.substring(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX.length(), idx);
newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX + aTextName.substring(idx + 1);
}
} else {
prop = aTextName.substring(0, idx);
newKey = aTextName.substring(idx + 1);
}
if (prop != null) {
Object obj = valueStack.findValue(prop);
try {
Object actionObj = ReflectionProviderFactory.getInstance().getRealTarget(prop, valueStack.getContext(), valueStack.getRoot());
if (actionObj != null) {
PropertyDescriptor propertyDescriptor = ReflectionProviderFactory.getInstance().getPropertyDescriptor(actionObj.getClass(), prop);
if (propertyDescriptor != null) {
Class clazz = propertyDescriptor.getPropertyType();
if (clazz != null) {
if (obj != null)
valueStack.push(obj);
msg = findText(clazz, newKey, locale, null, args);
if (obj != null)
valueStack.pop();
if (msg != null) {
return msg;
}
}
}
}
}
catch (Exception e) {
LOG.debug("unable to find property " + prop, e);
}
}
}
// get default
GetDefaultMessageReturnArg result = null;
if (indexedTextName == null) {
result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
} else {
result = getDefaultMessage(aTextName, locale, valueStack, args, null);
if (result != null && result.message != null) {
return result.message;
}
result = getDefaultMessage(indexedTextName, locale, valueStack, args, defaultMessage);
}
// could we find the text, if not log a warn
if (unableToFindTextForKey(result)) {
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);
}
return result != null ? result.message : null;
}
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:
- Look for message in aClass' class hierarchy.
- Look for the message in a resource bundle for aClass
- If not found, look for the message in a resource bundle for any implemented interface
- If not found, traverse up the Class' hierarchy and repeat from the first sub-step
- If not found and aClass is a ModelDriven Action, then look for message in
the model's class hierarchy (repeat sub-steps listed above).
- 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
user.address.state, then it will attempt to see if "user" can be resolved into an
object. If so, repeat the entire process fromthe beginning with the object's class as
aClass and "address.state" as the message key.
- If not found, look for the message in aClass' package hierarchy.
- If still not found, look for the message in the default resource bundles.
- Return defaultMessage
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[*]).
If a message is found, it will also be interpolated. Anything within ${...}
will be treated as an OGNL expression and evaluated as such.
If a message is not found a WARN log will be logged. |
public static String findText(ResourceBundle bundle,
String aTextName,
Locale locale,
String defaultMessage,
Object[] args,
ValueStack valueStack) {
try {
reloadBundles();
String message = TextParseUtil.translateVariables(bundle.getString(aTextName), valueStack);
MessageFormat mf = buildMessageFormat(message, locale);
return mf.format(args);
} catch (MissingResourceException ex) {
// ignore
}
GetDefaultMessageReturnArg result = getDefaultMessage(aTextName, locale, valueStack, args, defaultMessage);
if (unableToFindTextForKey(result)) {
LOG.warn("Unable to find text for key '" + aTextName + "' in ResourceBundles for locale '" + locale + "'");
}
return result != null ? result.message : null;
}
Finds a localized text message for the given key, aTextName, in the specified resource
bundle.
If a message is found, it will also be interpolated. Anything within ${...}
will be treated as an OGNL expression and evaluated as such.
If a message is not found a WARN log will be logged. |
public static Locale localeFromString(String localeStr,
Locale defaultLocale) {
if ((localeStr == null) || (localeStr.trim().length() == 0) || ("_".equals(localeStr))) {
if (defaultLocale != null) {
return defaultLocale;
}
return Locale.getDefault();
}
int index = localeStr.indexOf('_');
if (index < 0) {
return new Locale(localeStr);
}
String language = localeStr.substring(0, index);
if (index == localeStr.length()) {
return new Locale(language);
}
localeStr = localeStr.substring(index + 1);
index = localeStr.indexOf('_');
if (index < 0) {
return new Locale(language, localeStr);
}
String country = localeStr.substring(0, index);
if (index == localeStr.length()) {
return new Locale(language, country);
}
localeStr = localeStr.substring(index + 1);
return new Locale(language, country, localeStr);
}
|
public static void reset() {
clearDefaultResourceBundles();
bundlesMap.clear();
synchronized (messageFormats) {
messageFormats.clear();
}
}
Clears all the internal lists. |
public static void setDelegatedClassLoader(ClassLoader classLoader) {
synchronized (bundlesMap) {
delegatedClassLoader = classLoader;
}
}
Sets a ClassLoader to look up the bundle from if none can be found on the current thread's classloader |
public static void setReloadBundles(boolean reloadBundles) {
LocalizedTextUtil.reloadBundles = reloadBundles;
}
Should resorce bundles be reloaded. |