package xapi.gwt.ui.autoui.client;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import xapi.annotation.inject.InstanceOverride;
import xapi.log.X_Log;
import xapi.log.api.LogLevel;
import xapi.platform.GwtPlatform;
import xapi.ui.autoui.api.BeanValueProvider;
import xapi.ui.autoui.api.DoNotIndex;
import xapi.ui.autoui.api.UiOptions;
import xapi.ui.autoui.api.UiRendererOptions;
import xapi.ui.autoui.api.UiRenderingContext;
import xapi.ui.autoui.api.UserInterfaceFactory;
import xapi.ui.autoui.impl.AbstractUserInterfaceFactory;
import xapi.util.X_Debug;
import xapi.util.api.ConvertsValue;
@GwtPlatform
@InstanceOverride(implFor=UserInterfaceFactory.class)
public class UserInterfaceFactoryGwt extends AbstractUserInterfaceFactory{
private static final int MAX_DEPTH = 10;
@Override
protected UiRenderingContext[] getOptions(Class<?> type) {
List<UiRenderingContext> options = new ArrayList<UiRenderingContext>();
BeanValueProvider values = getBeanProvider(type);
// Check the package for options
Package pkg = type.getPackage();
if (pkg != null && pkg.isAnnotationPresent(UiOptions.class)) {
options.addAll(extractRenderingContext(pkg.getAnnotation(UiOptions.class), values));
}
if (type.isAnnotationPresent(UiOptions.class)) {
UiOptions opts = type.getAnnotation(UiOptions.class);
if (opts.fields().length > 0) {
values.setChildKeys(opts.fields());
}
}
// check for enclosing types/methods?
Class<?> check = type;
while (check != null) {
// Check the type for options
addAllRendererContexts(options, check, values);
check = check.getEnclosingClass();
}
for (Method m : type.getMethods()) {
addAllRendererContexts(options, m, values);
}
return options.toArray(new UiRenderingContext[options.size()]);
}
protected void addAllRendererContexts(List<UiRenderingContext> options, AnnotatedElement element, BeanValueProvider values) {
if (element.isAnnotationPresent(UiOptions.class)) {
options.addAll(extractRenderingContext(element.getAnnotation(UiOptions.class), values));
}
if (element.isAnnotationPresent(UiRendererOptions.class)) {
options.addAll(extractRenderingContext(element.getAnnotation(UiRendererOptions.class), values,
element instanceof Method ? getNameFromMethod((Method)element) : null ));
}
}
@Override
protected void recursiveAddBeanValues(BeanValueProvider bean, Class<?> cls,
final ConvertsValue<Object, Object> converter, String prefix, int depth) {
if (depth > MAX_DEPTH) {
X_Log.warn(getClass(), "Recursion sickness detected in "+cls+" from "+prefix+"; depth reached "+MAX_DEPTH);
if (X_Log.loggable(LogLevel.TRACE)) {
X_Log.trace("Consider using the @DoNotIndex annotation in the recursion chain produced by "+prefix);
}
return;
}
// Add all method getter
for (Method m : cls.getMethods()) {
if (
m.getParameterTypes().length == 0
&& m.getDeclaringClass() != Object.class
&& m.getReturnType() != void.class
) {
DoNotIndex noIndex = m.getAnnotation(DoNotIndex.class);
if (noIndex != null && noIndex.unlessDepthLessThan() >= depth) {
continue;
}
final String name = getNameFromMethod(m);
String key = prefix.length()==0 ? name : prefix+"."+name;
// bean.addProvider(key+".name()", new ConvertsValue<Object, Object>() {
// @Override
// public Object convert(Object from) {
// return name;
// }
// });
final Method rootMethod = getRootMethod(m);
ConvertsValue<Object, Object> valueConverter = new ConvertsValue<Object, Object>() {
@Override
public Object convert(Object from) {
Object parent = converter.convert(from);
try {
return rootMethod.invoke(parent);
} catch (Exception e) {
throw X_Debug.rethrow(e);
}
}
};
bean.addProvider(key, name, valueConverter);
if (isNotPrimitive(m.getReturnType())) {
recursiveAddBeanValues(bean, m.getReturnType(), valueConverter, key, depth+1);
}
}
}
}
protected Method getRootMethod(Method m) {
if (m.getDeclaringClass().isInterface()) {
// When using interfaces, search for the deepest ancestor interface with the given method.
Method winner = m;
for (Class<?> c : m.getDeclaringClass().getInterfaces()) {
try {
Method method = c.getMethod(m.getName(), m.getParameterTypes());
if (method.getDeclaringClass().isAssignableFrom(winner.getDeclaringClass())) {
winner = method;
}
} catch (Throwable ignored){}
}
return winner;
}
return m;
}
protected boolean isNotPrimitive(Class<?> cls) {
return
!cls.isPrimitive()
&& cls != String.class
&& cls != Boolean.class
&& cls != Byte.class
&& cls != Character.class
&& cls != Short.class
&& cls != Integer.class
&& cls != Long.class
&& cls != Float.class
&& cls != Double.class
;
}
}