src/main/java/au/id/djc/stringtemplate/AnnotationAttributeRendererGenerator.java (6078B) - raw
1 package au.id.djc.stringtemplate;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.logging.Logger;
10
11 import org.springframework.beans.BeansException;
12 import org.springframework.context.ApplicationContext;
13 import org.springframework.context.ConfigurableApplicationContext;
14 import org.springframework.context.support.ApplicationObjectSupport;
15 import org.springframework.core.annotation.AnnotationUtils;
16 import org.springframework.util.LinkedMultiValueMap;
17 import org.springframework.util.MultiValueMap;
18 import org.springframework.util.ReflectionUtils;
19
20 /**
21 * This bean will automatically populate your {@link ApplicationContext} with an
22 * {@link AttributeRenderer} implementation for each method on any (singleton)
23 * beans which is annotated with {@link AttributeRendererMethod}.
24 */
25 public class AnnotationAttributeRendererGenerator extends ApplicationObjectSupport {
26
27 private static final Logger LOG = Logger.getLogger(AnnotationAttributeRendererGenerator.class.getName());
28
29 private static final class MethodWrapper {
30
31 private final String format;
32 private final String beanName;
33 private final Method method;
34
35 public MethodWrapper(String format, String beanName, Method method) {
36 this.format = format;
37 this.beanName = beanName;
38 this.method = method;
39 }
40
41 @Override
42 public String toString() {
43 return "MethodWrapper[" + beanName + "," + method.getName() + "]";
44 }
45
46 }
47
48 private static final class MethodWrappingAttributeRenderer implements AttributeRenderer {
49
50 private final ApplicationContext applicationContext;
51 private final Class<?> targetClass;
52 private final Map<String, MethodWrapper> methodsByFormat = new HashMap<String, MethodWrapper>();
53
54 public MethodWrappingAttributeRenderer(ApplicationContext applicationContext,
55 Class<?> targetClass, List<MethodWrapper> methodWrappers) {
56 this.applicationContext = applicationContext;
57 this.targetClass = targetClass;
58 for (MethodWrapper methodWrapper : methodWrappers) {
59 methodsByFormat.put(methodWrapper.format, methodWrapper);
60 }
61 LOG.info("Registering generated AttributeRenderer targeting " + targetClass.getName() + " with methods " + methodsByFormat);
62 }
63
64 @Override
65 public Class<?> getTargetClass() {
66 return targetClass;
67 }
68
69 @Override
70 public String toString(Object o) {
71 return toString(o, "");
72 }
73
74 @Override
75 public String toString(Object o, String formatName) {
76 MethodWrapper methodWrapper = methodsByFormat.get(formatName);
77 if (methodWrapper == null) {
78 if (!formatName.isEmpty()) {
79 return toString(o, "");
80 } else {
81 return o.toString();
82 }
83 }
84 Object bean = applicationContext.getBean(methodWrapper.beanName);
85 try {
86 return (String) methodWrapper.method.invoke(bean, o);
87 } catch (IllegalArgumentException e) {
88 throw new RuntimeException(e);
89 } catch (IllegalAccessException e) {
90 throw new RuntimeException(e);
91 } catch (InvocationTargetException e) {
92 throw new RuntimeException(e);
93 }
94 }
95
96 }
97
98 private final MultiValueMap<Class<?>, MethodWrapper> methodsByTargetClass = new LinkedMultiValueMap<Class<?>, MethodWrapper>();
99
100 @Override
101 protected void initApplicationContext() throws BeansException {
102 String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class, false, true);
103 for (final String beanName : beanNames) {
104 Object bean = getApplicationContext().getBean(beanName);
105 ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
106 @Override
107 public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
108 AttributeRendererMethod annotation = AnnotationUtils.findAnnotation(method, AttributeRendererMethod.class);
109 if (annotation != null) {
110 if (method.getParameterTypes().length != 1) {
111 throw new IllegalArgumentException("AttributeRenderer method does not take exactly one argument: "
112 + method.getName() + " of bean " + beanName);
113 }
114 if (!String.class.isAssignableFrom(method.getReturnType())) {
115 throw new IllegalArgumentException("AttributeRenderer method does not return String: "
116 + method.getName() + " of bean " + beanName);
117 }
118 methodsByTargetClass.add(method.getParameterTypes()[0],
119 new MethodWrapper(annotation.format(), beanName, method));
120 }
121 }
122 });
123 }
124 registerAttributeRendererBeans();
125 }
126
127 private void registerAttributeRendererBeans() {
128 int i = 1;
129 for (Entry<Class<?>, List<MethodWrapper>> entry : methodsByTargetClass.entrySet()) {
130 MethodWrappingAttributeRenderer attributeRenderer =
131 new MethodWrappingAttributeRenderer(getApplicationContext(), entry.getKey(), entry.getValue());
132 ((ConfigurableApplicationContext) getApplicationContext()).getBeanFactory().registerSingleton(
133 String.format("generatedAttributeRenderer#%d-targeting-%s", i, entry.getKey().getName()),
134 attributeRenderer);
135 i ++;
136 }
137 }
138
139 }