View Javadoc
1   package fr.ifremer.tutti.service;
2   
3   /*
4    * #%L
5    * Tutti :: Service
6    * %%
7    * Copyright (C) 2012 - 2014 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
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   * TODO
48   *
49   * @author Tony Chemit - chemit@codelutin.com
50   * @since TODO
51   */
52  public class TuttiDecorator<O> extends MultiJXPathDecorator<O> implements Cloneable {
53  
54      private static final long serialVersionUID = 1L;
55  
56      /** Logger. */
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       * To sort always on the selected context.
67       *
68       * @since 2.8
69       */
70      protected boolean sortOnlyOnSelectedContext;
71  
72      /**
73       * List of tokens to sort using the {@link #sortOnlyOnSelectedContext} mode.
74       *
75       * If the mode is not set, then won't use this.
76       *
77       * @since 2.8
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             // try first in cache
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 }