1 package fr.ifremer.tutti.service;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import com.google.common.base.Preconditions;
26 import com.google.common.cache.CacheBuilder;
27 import com.google.common.cache.CacheLoader;
28 import com.google.common.cache.LoadingCache;
29 import com.google.common.collect.Maps;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.nuiton.decorator.DecoratorUtil;
33 import org.nuiton.decorator.JXPathDecorator;
34 import org.nuiton.decorator.MultiJXPathDecorator;
35 import org.nuiton.util.beans.BeanUtil;
36
37 import java.beans.PropertyDescriptor;
38 import java.io.Serializable;
39 import java.lang.reflect.Method;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.concurrent.ExecutionException;
45
46
47
48
49
50
51
52 public class TuttiDecorator<O> extends MultiJXPathDecorator<O> implements Cloneable {
53
54 private static final long serialVersionUID = 1L;
55
56
57 private static final Log log = LogFactory.getLog(TuttiDecorator.class);
58
59 protected final Map<String, Method> tokenMethods;
60
61 protected final LoadingCache<O, String> cache;
62
63 protected boolean useCache;
64
65
66
67
68
69
70 protected boolean sortOnlyOnSelectedContext;
71
72
73
74
75
76
77
78
79 protected Set<String> sortOnlyOnSelectedContextTokens;
80
81 public static <O> TuttiDecorator<O> newDecorator(Class<O> internalClass,
82 String expression,
83 String separator,
84 String separatorReplacement) {
85 return new TuttiDecorator<>(internalClass, expression, separator, separatorReplacement);
86 }
87
88 protected TuttiDecorator(Class<O> internalClass,
89 String expression,
90 String separator,
91 String separatorReplacement) throws IllegalArgumentException, NullPointerException {
92 super(internalClass,
93 expression,
94 separator,
95 separatorReplacement,
96 DecoratorUtil.<O>createMultiJXPathContextKeepingOrder(expression,
97 separator,
98 separatorReplacement));
99 tokenMethods = Maps.newHashMap();
100 Set<PropertyDescriptor> descriptors =
101 BeanUtil.getDescriptors(type, BeanUtil.IS_READ_DESCRIPTOR);
102
103 for (String token : getTokens()) {
104 Method m = null;
105 for (PropertyDescriptor propertyDescriptor : descriptors) {
106 if (propertyDescriptor.getName().equals(token)) {
107 m = propertyDescriptor.getReadMethod();
108 break;
109 }
110 }
111 if (m == null) {
112 throw new IllegalArgumentException(
113 "could not find the property " + token + " in " + type);
114 }
115 tokenMethods.put(token, m);
116 }
117
118 int i = 0;
119 for (Context<O> OContext : contexts) {
120 OContext.setComparator(new TuttiDecoratorComparator<>(getProperty(i++)));
121 }
122
123 this.cache = CacheBuilder.newBuilder().build(new CacheLoader<O, String>() {
124 @Override
125 public String load(O key) throws Exception {
126 String result;
127 if (key == null) {
128 result = "";
129 } else {
130 result = TuttiDecorator.this.toString(key);
131 }
132 return result;
133 }
134 });
135 }
136
137 public boolean isUseCache() {
138 return useCache;
139 }
140
141 public void setUseCache(boolean useCache) {
142 this.useCache = useCache;
143 cache.invalidateAll();
144 }
145
146 public boolean isSortOnlyOnSelectedContext() {
147 return sortOnlyOnSelectedContext;
148 }
149
150 public void setSortOnlyOnSelectedContext(boolean sortOnlyOnSelectedContext) {
151 this.sortOnlyOnSelectedContext = sortOnlyOnSelectedContext;
152 }
153
154 public Set<String> getSortOnlyOnSelectedContextTokens() {
155 return sortOnlyOnSelectedContextTokens;
156 }
157
158 public void setSortOnlyOnSelectedContextTokens(Set<String> sortOnlyOnSelectedContextTokens) {
159 this.sortOnlyOnSelectedContextTokens = sortOnlyOnSelectedContextTokens;
160 }
161
162 public TuttiDecoratorComparator<O> getCurrentComparator() {
163 return (TuttiDecoratorComparator<O>) context.getComparator(0);
164 }
165
166 @Override
167 public String toString(Object bean) {
168 O bean1 = (O) bean;
169 String result = null;
170
171 if (useCache) {
172
173
174 try {
175 result = cache.get(bean1);
176 } catch (ExecutionException e) {
177 if (log.isErrorEnabled()) {
178 log.error("Could not obtain from cache", e);
179 }
180 }
181 }
182 if (result == null) {
183
184 if (bean != null) {
185 Object[] args = new Object[nbToken];
186
187 String[] tokens = getTokens();
188 for (int i = 0; i < nbToken; i++) {
189 String token = tokens[i];
190 Object value = getValue(bean1, token);
191 if (value == null) {
192 value = onNullValue(bean1, token);
193 }
194 args[i] = value;
195 }
196
197 try {
198 result = String.format(getExpression(), args);
199 } catch (Exception eee) {
200 if (log.isErrorEnabled()) {
201 log.error("Could not format " + getExpression() + "" +
202 " with args : " + Arrays.toString(args), eee);
203 }
204 result = "";
205 }
206
207 if (useCache) {
208 cache.put(bean1, result);
209 }
210 }
211 }
212
213 return result;
214 }
215
216 protected Object getValue(O bean, String token) {
217 Method method = tokenMethods.get(token);
218 Preconditions.checkNotNull(method,
219 "Could not find method for token " + token);
220 Object result;
221 try {
222 result = method.invoke(bean);
223 } catch (Exception e) {
224 if (log.isErrorEnabled()) {
225 log.error("Could not obtain token [" + token + "] value", e);
226 }
227 result = "";
228 }
229 return result;
230 }
231
232 protected Object onNullValue(O bean, String token) {
233 return null;
234 }
235
236 @Override
237 public Object clone() throws CloneNotSupportedException {
238 return super.clone();
239 }
240
241 public static class TuttiDecoratorComparator<O> extends JXPathComparator<O> implements Serializable, Cloneable {
242
243 private static final long serialVersionUID = 1L;
244
245 protected String expression;
246
247 public TuttiDecoratorComparator(String expression) {
248 super(expression);
249 this.expression = expression;
250 }
251
252 @Override
253 public void init(JXPathDecorator<O> decorator, List<O> datas) {
254 clear();
255 TuttiDecorator<O> tuttiDecorator = (TuttiDecorator<O>) decorator;
256 String token = decorator.getTokens()[0];
257 boolean sortOnlyOnSelectedContext =
258 tuttiDecorator.isSortOnlyOnSelectedContext() &&
259 tuttiDecorator.getSortOnlyOnSelectedContextTokens() != null &&
260 tuttiDecorator.getSortOnlyOnSelectedContextTokens().contains(token);
261
262 for (O data : datas) {
263 if (sortOnlyOnSelectedContext) {
264 Object tokenValue = tuttiDecorator.getValue(data, token);
265
266 valueCache.put(data, (Comparable) tokenValue);
267 } else if (data instanceof Number) {
268 valueCache.put(data, (Comparable) data);
269 } else {
270 Comparable key = tuttiDecorator.toString(data);
271 valueCache.put(data, key);
272 }
273 }
274 }
275
276 @Override
277 public TuttiDecoratorComparator<O> clone() {
278 return new TuttiDecoratorComparator<>(expression);
279 }
280
281 public String getExpression() {
282 return expression;
283 }
284 }
285 }