/* Messages.java
Purpose:
Description:
History:
2001/9/, Tom M. Yeh: Created.
Copyright (C) 2001 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.mesg;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.lang.Objects;
import org.zkoss.text.MessageFormats;
import org.zkoss.util.Locales;
import org.zkoss.util.resource.Labels;
import org.zkoss.util.resource.PropertyBundle;
/**
* The message manager.
* This class manages how an message is retrieved based on the message code
* and the locale.
*
* <p>Note: unlike MessageFormat's default behavior, all null objects
* are treated as an empty string rather than "null".
*
* @author tomyeh
*/
public class Messages implements MessageConst {
private static final Logger log = LoggerFactory.getLogger(Messages.class);
private static Formatter _formatter;
protected Messages() {//prohibit from inited
}
/**
* Gets a message based on the specified code without formating arguments.
* <p>Equivalent to get(code, null).
*/
public static final String get(int code) {
return get(code, null, getLocale());
}
/**
* Gets a message based on the locale of current user with
* ONE format-argument.
*/
public static final String get(int code, Object fmtArg) {
return get(code, new Object[] {fmtArg}, getLocale());
}
/**
* Gets a message based on the locale of current user.
* <p>Equivalent to get(code, fmtArgs, current_locale).
* The current_locale argument depends on the implementation.
*/
public static final String get(int code, Object[] fmtArgs) {
return get(code, fmtArgs, getLocale());
}
private static final Locale getLocale() {
return Locales.getCurrent();
}
/**
* Gets a message from the resource bundle.
*
* @return null if no found
*/
private static final String getFromBundle(int code, Locale locale) {
final BundleInfo bi = Aide.getBundleInfo(code);
final String label = Labels.getLabel(getLabelKey(bi, code));
if (label != null)
return label;
final PropertyBundle rb = //case insensitive
PropertyBundle.getBundle(bi.filename, locale, true);
if (rb != null)
return rb.getProperty(Integer.toHexString(code - getType(code)));
throw new IllegalStateException("Missing resource: " + bi + " locale=" + locale);
}
private static final String getLabelKey(BundleInfo bi, int code) {
final String nm = bi.klass.getName();
final int j = nm.lastIndexOf('.');
return (j >= 0 ? nm.substring(j+1): nm) + '.' + Integer.toHexString(code & 0xffff);
}
private static final String getNotFound(int code, Locale locale) {
if (code == NULL_CODE)
return ""; //special code
try {
log.error("Message code not found: " +
Integer.toHexString(code) + " not in " + locale + ":" + Aide.getBundleInfo(code));
final String hexcode = Integer.toHexString(code);
final String s = getFromBundle(
MCommon.MESSAGE_CODE_NOT_FOUND, locale);
return s != null ?
MessageFormats.format(s, new Object[] {hexcode}, locale):
"Unknown message code: " + hexcode;
}catch(Exception ex) {
log.error("", ex);
return "Unknown message code: " + Integer.toHexString(code);
}
}
/**
* Gets a message based on the specified code. If not found, returns
* an error message to denote it.
*
* <p>If fmtArgs is not null,
* {@link org.zkoss.text.MessageFormats#format} is called to format
* the message. However, unlike MessageFormat's default behavior,
* all null objects are treated as an empty string rather than "null".
*
* <p>It also recognizes {@link org.zkoss.lang.Objects#UNKNOWN}.
*
* @param code the code
* @param fmtArgs the argument lists to format the message
* @param locale the locale of the message to load
* @return the message; never be null
*/
public static String get(int code, Object[] fmtArgs, Locale locale) {
try {
String s = getFromBundle(code, locale);
if (s == null)
return getNotFound(code, locale);
if (fmtArgs != null && fmtArgs.length > 0) {
final Object[] args = new Object[fmtArgs.length];
final Formatter formatter = _formatter;
for (int j = 0; j < fmtArgs.length; ++j) {
final Object arg = fmtArgs[j];
if (formatter != null)
args[j] = formatter.format(arg);
else if (arg == null || arg == Objects.UNKNOWN)
args[j] = "";
else if (arg instanceof Object[])
args[j] = Objects.toString(arg);
else
args[j] = arg;
}
s = MessageFormats.format(s, args, locale);
}
return s;
}catch(Exception ex) {
log.error("", ex);
return getNotFound(code, locale);
}
}
/** Returns the formatter used by {@link #get(int, Object[], Locale)},
* or null if not set.
*/
public static Formatter getFormatter() {
return _formatter;
}
/** Sets the formatter used by {@link #get(int, Object[], Locale)}.
* <p>Default: null.
*/
public static void setFormatter(Formatter fmt) {
_formatter = fmt;
}
/**
* Gets the message type of the specified code.
*/
public static final int getType(int code) {
return code & 0xffff0000;
}
/** The formatter used by {@link #get(int, Object[], Locale)} to
* format the specified object.
*/
public static interface Formatter {
/** Formats the specified object into a string.
*/
public Object format(Object o);
}
}