package xapi.ui.autoui.api; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import xapi.collect.api.StringTo; import xapi.collect.impl.ArrayIterable; import xapi.source.write.MappedTemplate; import xapi.util.api.ConvertsValue; public class BeanValueProvider { private static Object getValue(String name, Object object, Map<String, ConvertsValue<Object, Object>> map) { if (!map.containsKey(name)){ illegalArg(name); } return map.get(name).convert(object); } protected static Object illegalArg(String name) { throw new IllegalArgumentException("Value for field named "+name+" not found"); } private class Converter { protected Object valueOf(String name, Object object) { return BeanValueProvider.this.valueOf(name, object); } } private class RebasedBeanValueProvider extends BeanValueProvider { private final String name; private final Converter converter; public RebasedBeanValueProvider(String name, Converter converter) { this.name = name; this.converter = converter; } @Override public Object getValue(String name, String context, Object object) { // assert context.equals(this.name); if ("$name".equals(name) || "this.name()".equals(name) || (this.name+".name()").equals(name)) return this.name; if ("$value".equals(name) || "this".equals(name) || this.name.equals(name)) return converter.valueOf(this.name, object); return converter.valueOf(this.name+"."+name, object); } } private class RebaseAllBeanValueProvider extends BeanValueProvider { private final Converter converter; private RebaseAllBeanValueProvider(Converter converter) { this.converter = converter; } @Override public Object getValue(String name, String context, Object object) { if ("$name".equals(name) || "this.name()".equals(name)) return context; if ("$value".equals(name) || "this".equals(name)) return object; if (context.length()>0) { context += "."; } return converter.valueOf(context+name, object); } } private Map<String, ConvertsValue<Object, Object>> map = new LinkedHashMap<String, ConvertsValue<Object, Object>>(); private String[] childKeys; public void addProvider(String key, final String name, ConvertsValue<Object, Object> provider) { map.put(key, provider); ConvertsValue<Object, Object> namer = new ConvertsValue<Object, Object>() { @Override public Object convert(Object from) { return from instanceof HasName ? ((HasName)from).getName() : name; } }; map.put(key+".name()", namer ); if ("this".equals(key)) { map.put("$name", namer); map.put("$value", provider); } } protected Object valueOf(String name, Object object) { return getValue(name, object, map); } protected String[] getKeys(Map<String, ConvertsValue<Object, Object>> map) { String[] keys = map.keySet().toArray(new String[map.size()]); for (int i = keys.length; i-->0;) { keys[i] = "${"+keys[i]+"}"; } return keys; } protected String[] childKeys() { if (childKeys == null) { List<String> keys = new ArrayList<String>(); for (Entry<String, ConvertsValue<Object, Object>> entry : map.entrySet()) { String key = entry.getKey(); if (key.indexOf('.') == -1 && !"this".equals(key)) { keys.add(key); } } childKeys = keys.toArray(new String[keys.size()]); } return childKeys; } public void setChildKeys(String[] keys) { this.childKeys = keys; } public Object getValue(String name, String context, Object object) { return valueOf(name, object); } public BeanValueProvider rebase(String methodName) { BeanValueProvider bean = new RebasedBeanValueProvider(methodName, new Converter()); bean.childKeys = childKeys; bean.map = map; return bean; } public BeanValueProvider rebaseAll() { BeanValueProvider bean = new RebaseAllBeanValueProvider(new Converter()); bean.childKeys = childKeys; bean.map = map; return bean; } public Iterable<String> getChildKeys() { return new ArrayIterable<String>(childKeys()); } @SuppressWarnings({ "rawtypes", "unchecked" }) public void fillMap(String name, MappedTemplate template, StringTo map, Object val) { if ("".equals(name)) { for (String key : childKeys()) { String k = key.startsWith("$") ? key : "${"+key+"}"; if (template.hasKey(k)) { map.put(k, getValue(key, "", val)); } k = "${"+key+".name()}"; if (template.hasKey(k)) { map.put(k, key); } } } else { val = getValue(name, "", val); map.put("$value", val); map.put("$name", name); map.put("${"+name+"}", val); map.put("${"+name+".name()}", name); // We should also have a means to do deep name resolving on rebased bean items... } } }