package org.marketcetera.photon.commons;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.marketcetera.util.log.I18NLoggerProxy;
import org.marketcetera.util.log.I18NMessage;
import org.marketcetera.util.log.I18NMessage1P;
import org.marketcetera.util.log.I18NMessage2P;
import org.marketcetera.util.log.I18NMessage3P;
import org.marketcetera.util.log.I18NMessageProvider;
import org.marketcetera.util.misc.ClassVersion;
/* $License$ */
/**
* Experimental utility to aid construction of message constants. A typical
* usage would be
*
* <pre>
* /**
* * Messages for this package.
* */
* @ClassVersion("$Id$")
* final class Messages {
*
* static I18NMessage0P MY_MESSAGE;
* static I18NMessage1P MY_OTHER_MESSAGE;
*
* static {
* ReflectiveMessages.init(Messages.class);
* }
* }
* </pre>
*
* @author <a href="mailto:will@marketcetera.com">Will Horn</a>
* @version $Id: ReflectiveMessages.java 16154 2012-07-14 16:34:05Z colin $
* @since 2.0.0
*/
@ClassVersion("$Id: ReflectiveMessages.java 16154 2012-07-14 16:34:05Z colin $")
public final class ReflectiveMessages {
private static final String MESSAGE_ENTRY_SEPARATOR = "__"; //$NON-NLS-1$
private static final String EXTENSION_METHOD_NAME = "initReflectiveMessages"; //$NON-NLS-1$
/**
* Initializes message constants for a class. This assumes a properties file
* named "_messages.properties" in the same package as clazz. For each
* static {@link I18NMessage} field in clazz, this method instantiates a
* suitable instance. The message id and entry id is determined by the
* following procedure:
* <ul>
* <li>convert the field name to lower case</li>
* <li>if the result ends with "__xyz", use the string before it as the
* message id, and "xyz" as the entry id</li>
* <li>otherwise use the entire string as the messageId and
* {@link I18NMessage#UNKNOWN_ENTRY_ID} as the entry id</li>
* </ul>
* <p>
* This method also supports extension. If a field has a type Type that is
* not assignable to {@link I18NMessage}, this method will attempt to
* reflect on Type for a static "initReflectiveMessages" method with the
* signature:
*
* <pre>
* Type initReflectiveMessages(String, I18NLoggerProxy)
* </pre>
*
* If found, it will invoke this method and set the field to the returned
* Type object. Otherwise, the field will be ignored. An example of
* extension can be found in
* org.marketcetera.photon.commons.ui.LocalizedLabel.
*
* @param clazz
* the class to initialize
*/
public static void init(Class<?> clazz) {
final String className = clazz.getName();
try {
String id = clazz.getPackage().getName().replaceAll("\\.", "/") + "/"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
final I18NMessageProvider provider = new I18NMessageProvider(id,
clazz.getClassLoader());
final I18NLoggerProxy logger = new I18NLoggerProxy(provider);
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
final String fieldName = field.getName();
if ((field.getModifiers() & Modifier.STATIC) == 0) {
Messages.NONSTATIC_FIELD_IGNORED.info(
ReflectiveMessages.class, fieldName, className);
continue;
}
Class<?> type = field.getType();
if (I18NMessage.class.isAssignableFrom(type)) {
String entryId = I18NMessage.UNKNOWN_ENTRY_ID;
String messageId = fieldName.toLowerCase();
String[] split = messageId.split(MESSAGE_ENTRY_SEPARATOR);
if (split.length > 1) {
String suffix = split[split.length - 1];
if (!suffix.isEmpty()) {
entryId = suffix;
messageId = messageId.substring(0, messageId
.lastIndexOf(MESSAGE_ENTRY_SEPARATOR));
}
}
try {
Constructor<?> constructor = type.getConstructor(
I18NLoggerProxy.class, String.class,
String.class);
Object instance = constructor.newInstance(logger,
messageId, entryId);
field.set(null, instance);
} catch (Exception e) {
Messages.FAILED_TO_INITIALIZE_FIELD.error(
ReflectiveMessages.class, e, fieldName,
className);
} catch (ExceptionInInitializerError e) {
Messages.FAILED_TO_INITIALIZE_FIELD.error(
ReflectiveMessages.class, e, fieldName,
className);
}
} else {
try {
Method initMethod = type.getDeclaredMethod(
EXTENSION_METHOD_NAME, String.class,
I18NLoggerProxy.class);
initMethod.setAccessible(true);
try {
Object instance = initMethod.invoke(null,
fieldName, logger);
field.set(null, instance);
} catch (Exception e) {
Messages.FAILED_TO_INITIALIZE_FIELD.error(
ReflectiveMessages.class, e, fieldName,
className);
} catch (ExceptionInInitializerError e) {
Messages.FAILED_TO_INITIALIZE_FIELD.error(
ReflectiveMessages.class, e, fieldName,
className);
}
} catch (NoSuchMethodException e) {
Messages.UNSUPPORTED_FIELD_IGNORED.info(
ReflectiveMessages.class, e, type.getName(),
fieldName, className);
}
}
}
} catch (Exception e) {
Messages.FAILED_TO_INITIALIZE_CLASS.error(ReflectiveMessages.class,
e, className);
}
}
private ReflectiveMessages() {
throw new AssertionError("non-instantiable"); //$NON-NLS-1$
}
/**
* Message constants used by {@link ReflectiveMessages}.
*/
@ClassVersion("$Id: ReflectiveMessages.java 16154 2012-07-14 16:34:05Z colin $")
private static class Messages {
/**
* The message provider
*/
static final I18NMessageProvider PROVIDER = new I18NMessageProvider(
"reflective_messages", Messages.class.getClassLoader()); //$NON-NLS-1$
/**
* The message logger.
*/
static final I18NLoggerProxy LOGGER = new I18NLoggerProxy(PROVIDER);
/**
* The messages.
*/
static final I18NMessage2P NONSTATIC_FIELD_IGNORED = new I18NMessage2P(
LOGGER, "nonstatic_field_ignored"); //$NON-NLS-1$
static final I18NMessage3P UNSUPPORTED_FIELD_IGNORED = new I18NMessage3P(
LOGGER, "unsupported_field_ignored"); //$NON-NLS-1$
static final I18NMessage2P FAILED_TO_INITIALIZE_FIELD = new I18NMessage2P(
LOGGER, "failed_to_initialize_field"); //$NON-NLS-1$
static final I18NMessage1P FAILED_TO_INITIALIZE_CLASS = new I18NMessage1P(
LOGGER, "failed_to_initialize_class"); //$NON-NLS-1$
}
}