1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.nuiton.decorator;
23
24 import org.apache.commons.jxpath.JXPathContext;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import java.io.Serializable;
29 import java.util.Arrays;
30 import java.util.Comparator;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class JXPathDecorator<O> extends Decorator<O> {
60
61 private static final long serialVersionUID = 1L;
62
63
64 private static final Log log = LogFactory.getLog(JXPathDecorator.class);
65
66
67 protected Context<O> context;
68
69
70 protected int nbToken;
71
72
73 protected String initialExpression;
74
75 protected JXPathDecorator(Class<O> internalClass,
76 String expression,
77 Context<O> context)
78 throws IllegalArgumentException, NullPointerException {
79 super(internalClass);
80 initialExpression = expression;
81 if (context != null) {
82 setContext(context);
83 }
84 }
85
86 @Override
87 public String toString(Object bean) {
88 if (bean == null) {
89 return null;
90 }
91 JXPathContext jxcontext = JXPathContext.newContext(bean);
92 Object[] args = new Object[nbToken];
93
94 for (int i = 0; i < nbToken; i++) {
95 try {
96 args[i] = getTokenValue(jxcontext, context.tokens[i]);
97 } catch (Exception e) {
98 if (log.isErrorEnabled()) {
99 log.error("can not obtain token " + context.tokens[i]
100 + " on object of type " +
101 bean.getClass().getName() +
102 " for reason " + e.getMessage(), e);
103 }
104
105 }
106 }
107
108 String result;
109 try {
110 result = String.format(context.expression, args);
111 } catch (Exception eee) {
112 if (log.isErrorEnabled()) {
113 log.error("Could not format " + context.expression + "" +
114 " with args : " + Arrays.toString(args), eee);
115 }
116 result = "";
117 }
118 return result;
119 }
120
121 public String getProperty(int pos) {
122 return getTokens()[pos];
123 }
124
125 public String getExpression() {
126 return context.expression;
127 }
128
129 public String[] getTokens() {
130 return context.tokens;
131 }
132
133 public int getNbToken() {
134 return nbToken;
135 }
136
137 public String getInitialExpression() {
138 return initialExpression;
139 }
140
141 @Override
142 public String toString() {
143 return super.toString() + '<' + context + '>';
144 }
145
146 public void setContext(Context<O> context) {
147 this.context = context;
148 nbToken = context.tokens.length;
149
150
151 if (log.isDebugEnabled()) {
152 log.debug(context);
153 }
154 }
155
156 @SuppressWarnings({"unchecked"})
157 protected Comparator<O> getComparator(int pos) {
158 ensureTokenIndex(this, pos);
159 return context.getComparator(pos);
160 }
161
162 @SuppressWarnings({"unchecked"})
163 protected Comparable<Comparable<?>> getTokenValue(JXPathContext jxcontext,
164 String token) {
165
166 return (Comparable<Comparable<?>>) jxcontext.getValue(token);
167 }
168
169 public static class JXPathComparator<O> implements Comparator<O> {
170
171 protected Map<O, Comparable<Comparable<?>>> valueCache;
172
173 private final String expression;
174
175 public JXPathComparator(String expression) {
176 this.expression = expression;
177 valueCache = new HashMap<O, Comparable<Comparable<?>>>();
178 }
179
180 @Override
181 public int compare(O o1, O o2) {
182 Comparable<Comparable<?>> c1 = valueCache.get(o1);
183 Comparable<Comparable<?>> c2 = valueCache.get(o2);
184 if (c1 == null) {
185 if (c2 == null) {
186 return 0;
187 }
188 return 1;
189 }
190 if (c2 == null) {
191 return -1;
192 }
193 return c1.compareTo(c2);
194 }
195
196 public void clear() {
197 valueCache.clear();
198 }
199
200 public void init(JXPathDecorator<O> decorator, List<O> datas) {
201 clear();
202 for (O data : datas) {
203 JXPathContext jxcontext = JXPathContext.newContext(data);
204 Comparable<Comparable<?>> key;
205 key = decorator.getTokenValue(jxcontext, expression);
206 valueCache.put(data, key);
207 }
208 }
209 }
210
211 public static class Context<O> implements Serializable {
212
213
214
215
216
217 protected String expression;
218
219
220 protected String[] tokens;
221
222 protected transient Comparator<O> comparator;
223
224 private static final long serialVersionUID = 1L;
225
226 public Context(String expression, String[] tokens) {
227 this.expression = expression;
228 this.tokens = tokens;
229 }
230
231 public String getFirstProperty() {
232 return tokens[0];
233 }
234
235 public Comparator<O> getComparator(int pos) {
236 if (comparator == null) {
237 comparator = new JXPathComparator<O>(tokens[pos]);
238 }
239 return comparator;
240 }
241
242 public void setComparator(Comparator<O> comparator) {
243 this.comparator = comparator;
244 }
245
246 @Override
247 public String toString() {
248 return "<expression:" + expression + ", tokens:" +
249 Arrays.toString(tokens) + '>';
250 }
251 }
252
253 protected static void ensureTokenIndex(JXPathDecorator<?> decorator, int pos) {
254 if (pos < -1 || pos > decorator.getNbToken()) {
255 throw new ArrayIndexOutOfBoundsException(
256 "token index " + pos + " is out of bound, can be inside [" +
257 0 + ',' + decorator.nbToken + ']');
258 }
259 }
260 }