1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.i18nsystem.proxytoys;
35
36 import info.magnolia.i18nsystem.I18nKeyGenerator;
37 import info.magnolia.i18nsystem.I18nKeyGeneratorFactory;
38 import info.magnolia.i18nsystem.I18nParentable;
39 import info.magnolia.i18nsystem.I18nText;
40 import info.magnolia.i18nsystem.I18nable;
41 import info.magnolia.i18nsystem.I18nizer;
42 import info.magnolia.i18nsystem.LocaleProvider;
43 import info.magnolia.i18nsystem.TranslationService;
44
45 import javax.inject.Inject;
46
47 import com.thoughtworks.proxy.ProxyFactory;
48 import com.thoughtworks.proxy.factory.CglibProxyFactory;
49 import com.thoughtworks.proxy.toys.decorate.Decorating;
50 import com.thoughtworks.proxy.toys.decorate.Decorator;
51 import com.thoughtworks.proxy.toys.dispatch.Dispatching;
52
53 import net.sf.cglib.proxy.Enhancer;
54
55
56
57
58
59 public class ProxytoysI18nizer implements I18nizer {
60 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ProxytoysI18nizer.class);
61
62 private final TranslationService translationService;
63 private final LocaleProvider localeProvider;
64 private final ProxyFactory proxyFactory;
65
66 @Inject
67 public ProxytoysI18nizer(TranslationService translationService, LocaleProvider localeProvider) {
68 this.translationService = translationService;
69 this.localeProvider = localeProvider;
70 this.proxyFactory = new CglibProxyFactory(false);
71 }
72
73 @Override
74 public <C> C decorate(C obj) {
75 if (obj == null) {
76 return null;
77 }
78
79 if (isProxied(obj.getClass()) && obj instanceof I18nParentable) {
80 throw new IllegalStateException(obj + " has already been enhanced by " + getClass().getSimpleName());
81 }
82
83 return decorateChild(obj, null);
84 }
85
86 protected <P, C> C decorateChild(C child, P parent) {
87 log.debug("About to decorate {} (parent: {})", child, parent);
88 if (child == null) {
89 return null;
90 }
91 if (child instanceof I18nParentable) {
92 log.debug(" - {} is already an instance of I18nParentable - probably because we're in a list. Assuming it's also already decorated with i18n.", child);
93 return child;
94 }
95
96
97 Class<C> originalClass = (Class<C>) child.getClass();
98 while (isProxied(originalClass)) {
99
100 originalClass = (Class<C>) originalClass.getSuperclass();
101 }
102 final C parentedChild = injectParentableInterface(originalClass, child, parent);
103
104 final C i18nd = decorateI18nTextMethods(parentedChild, originalClass, I18nParentable.class);
105 return interceptGetters(i18nd, originalClass);
106 }
107
108 protected <T, P> T injectParentableInterface(Class<? extends T> ct, T object, P parent) {
109 final I18nParentableImpl<P> parentable = new I18nParentableImpl<P>(parent);
110 return Dispatching.proxy(ct, I18nParentable.class)
111 .with(object, parentable)
112 .build(proxyFactory);
113 }
114
115 protected <T> T decorateI18nTextMethods(T obj, Class<T> proxyPrimaryType, Class... proxyMoreTypes) {
116 final I18nKeyGenerator<T> keyGen = I18nKeyGeneratorFactory.newKeyGeneratorFor(obj);
117 final I18nTextMethodDecorator<T> decorator = newI18nTextMethodDecorator(keyGen);
118
119 return decorateI18nTextMethods(obj, proxyPrimaryType, proxyMoreTypes, decorator);
120 }
121
122 protected <T> I18nTextMethodDecorator<T> newI18nTextMethodDecorator(I18nKeyGenerator<T> keyGen) {
123 return new I18nTextMethodDecorator<T>(translationService, localeProvider, keyGen);
124 }
125
126 protected <T> T decorateI18nTextMethods(T obj, Class<T> proxyPrimaryType, Class[] proxyMoreTypes, Decorator<T> decorator) {
127 return Decorating.proxy(proxyPrimaryType, proxyMoreTypes)
128 .with(obj)
129 .visiting(new FilteringMethodDecorator<T>(decorator, new MethodsAnnotatedWith(I18nText.class)))
130 .build(proxyFactory);
131 }
132
133 protected <T> T interceptGetters(T o, Class<T> originalClass) {
134 final T proxy = Decorating.proxy(originalClass, new Class[] { I18nParentable.class })
135 .with(o)
136 .visiting(new FilteringMethodDecorator<T>(new ChildDecorator<T>(this), new ReturnsAnnotatedTypeArgument(I18nable.class)))
137 .build(proxyFactory);
138 log.debug("Proxied {} (and returning {})", o, proxy);
139 return proxy;
140 }
141
142 protected boolean isProxied(Class<?> aClass) {
143
144 return proxyFactory.isProxyClass(aClass) || Enhancer.isEnhanced(aClass);
145 }
146
147 }