1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krms.impl.repository.mock;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.util.ArrayList;
22 import java.util.Calendar;
23 import java.util.Collection;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.atomic.AtomicInteger;
29 import java.util.concurrent.atomic.AtomicLong;
30 import java.util.regex.Pattern;
31 import org.apache.commons.beanutils.NestedNullException;
32 import org.apache.commons.beanutils.PropertyUtils;
33 import org.joda.time.DateTime;
34 import org.kuali.rice.core.api.criteria.AndPredicate;
35 import org.kuali.rice.core.api.criteria.EqualPredicate;
36 import org.kuali.rice.core.api.criteria.GreaterThanOrEqualPredicate;
37 import org.kuali.rice.core.api.criteria.GreaterThanPredicate;
38 import org.kuali.rice.core.api.criteria.LessThanOrEqualPredicate;
39 import org.kuali.rice.core.api.criteria.LessThanPredicate;
40 import org.kuali.rice.core.api.criteria.LikePredicate;
41 import org.kuali.rice.core.api.criteria.OrPredicate;
42 import org.kuali.rice.core.api.criteria.Predicate;
43 import org.kuali.rice.core.api.criteria.QueryByCriteria;
44
45
46
47
48
49
50
51 public class CriteriaMatcherInMemory<T> {
52
53 public CriteriaMatcherInMemory() {
54 super();
55 }
56 private QueryByCriteria criteria;
57
58 public QueryByCriteria getCriteria() {
59 return criteria;
60 }
61
62 public void setCriteria(QueryByCriteria criteria) {
63 this.criteria = criteria;
64 }
65
66
67
68
69
70
71
72 public Collection<T> findMatching(Collection<T> all) {
73
74 if (criteria == null) {
75 return all;
76 }
77 int count = -1;
78 int startAt = 0;
79 if (this.criteria.getStartAtIndex() != null) {
80 startAt = this.criteria.getStartAtIndex();
81 }
82 int maxResults = Integer.MAX_VALUE;
83 if (this.criteria.getMaxResults() != null) {
84 maxResults = this.criteria.getMaxResults();
85 }
86 List<T> selected = new ArrayList<T>();
87 for (T obj : all) {
88 if (matches(obj)) {
89 count++;
90 if (count < startAt) {
91 continue;
92 }
93 selected.add(obj);
94 if (count > maxResults) {
95 break;
96 }
97 }
98 }
99 return selected;
100 }
101
102
103
104
105
106
107
108 public boolean matches(T infoObject) {
109 return matches(infoObject, this.criteria.getPredicate());
110 }
111
112
113
114
115 protected boolean matches(T infoObject, Predicate predicate) {
116
117 if (predicate == null) {
118 return true;
119 }
120 if (predicate instanceof OrPredicate) {
121 return matchesOr(infoObject, (OrPredicate) predicate);
122 }
123 if (predicate instanceof AndPredicate) {
124 return matchesAnd(infoObject, (AndPredicate) predicate);
125 }
126 if (predicate instanceof EqualPredicate) {
127 return matchesEqual(infoObject, (EqualPredicate) predicate);
128 }
129 if (predicate instanceof LessThanPredicate) {
130 return matchesLessThan(infoObject, (LessThanPredicate) predicate);
131 }
132 if (predicate instanceof LessThanOrEqualPredicate) {
133 return matchesLessThanOrEqual(infoObject, (LessThanOrEqualPredicate) predicate);
134 }
135 if (predicate instanceof GreaterThanPredicate) {
136 return matchesGreaterThan(infoObject, (GreaterThanPredicate) predicate);
137 }
138 if (predicate instanceof GreaterThanOrEqualPredicate) {
139 return matchesGreaterThanOrEqual(infoObject, (GreaterThanOrEqualPredicate) predicate);
140 }
141 if (predicate instanceof LikePredicate) {
142 return matchesLike(infoObject, (LikePredicate) predicate);
143 }
144 throw new UnsupportedOperationException("predicate type not supported yet in in-memory mathcer" + predicate.getClass().getName());
145 }
146
147 private boolean matchesOr(T infoObject, OrPredicate predicate) {
148 for (Predicate subPred : predicate.getPredicates()) {
149 if (matches(infoObject, subPred)) {
150 return true;
151 }
152 }
153 return false;
154 }
155
156 private boolean matchesAnd(T infoObject, AndPredicate predicate) {
157 for (Predicate subPred : predicate.getPredicates()) {
158 if (!matches(infoObject, subPred)) {
159 return false;
160 }
161 }
162 return true;
163 }
164
165 private boolean matchesEqual(T infoObject, EqualPredicate predicate) {
166 Object dataValue = extractValue(predicate.getPropertyPath(), infoObject);
167 return matchesEqual(dataValue, predicate.getValue().getValue());
168 }
169
170 private boolean matchesLessThan(T infoObject, LessThanPredicate predicate) {
171 Object dataValue = extractValue(predicate.getPropertyPath(), infoObject);
172 return matchesLessThan(dataValue, predicate.getValue().getValue());
173 }
174
175 private boolean matchesLessThanOrEqual(T infoObject, LessThanOrEqualPredicate predicate) {
176 Object dataValue = extractValue(predicate.getPropertyPath(), infoObject);
177 if (matchesLessThan(dataValue, predicate.getValue().getValue())) {
178 return true;
179 }
180 return matchesEqual(dataValue, predicate.getValue().getValue());
181 }
182
183 private boolean matchesGreaterThan(T infoObject, GreaterThanPredicate predicate) {
184 Object dataValue = extractValue(predicate.getPropertyPath(), infoObject);
185 return matchesGreaterThan(dataValue, predicate.getValue().getValue());
186 }
187
188 private boolean matchesGreaterThanOrEqual(T infoObject, GreaterThanOrEqualPredicate predicate) {
189 Object dataValue = extractValue(predicate.getPropertyPath(), infoObject);
190 if (matchesGreaterThan(dataValue, predicate.getValue().getValue())) {
191 return true;
192 }
193 return matchesEqual(dataValue, predicate.getValue().getValue());
194 }
195
196 private boolean matchesLike(T infoObject, LikePredicate predicate) {
197 Object dataValue = extractValue(predicate.getPropertyPath(), infoObject);
198 return matchesLike(dataValue, predicate.getValue().getValue());
199 }
200
201 protected static Object extractValue(String fieldPath, Object infoObject) {
202
203 try {
204 if (infoObject == null) {
205 return null;
206 }
207 Object value = PropertyUtils.getNestedProperty(infoObject, fieldPath);
208
209
210
211 if (value instanceof Boolean) {
212 return value.toString();
213 }
214
215
216
217 if (value instanceof Date) {
218 return new DateTime ((Date) value);
219 }
220 if (value instanceof Calendar) {
221 return new DateTime ((Calendar) value);
222 }
223 if (value instanceof Short) {
224 return BigInteger.valueOf(((Short) value).longValue());
225 }
226 if (value instanceof AtomicLong) {
227 return BigInteger.valueOf(((AtomicLong) value).longValue());
228 }
229 if (value instanceof AtomicInteger) {
230 return BigInteger.valueOf(((AtomicInteger) value).longValue());
231 }
232 if (value instanceof Integer) {
233 return BigInteger.valueOf(((Integer)value).longValue());
234 }
235 if (value instanceof Long) {
236 return BigInteger.valueOf(((Long)value).longValue());
237 }
238 if (value instanceof Float) {
239 return BigDecimal.valueOf(((Float)value).doubleValue());
240 }
241 if (value instanceof Double) {
242 return BigDecimal.valueOf(((Double)value).doubleValue());
243 }
244 return value;
245 } catch (NestedNullException ex) {
246 return null;
247 } catch (IllegalAccessException ex) {
248 throw new IllegalArgumentException(fieldPath, ex);
249 } catch (InvocationTargetException ex) {
250 throw new IllegalArgumentException(fieldPath, ex);
251 } catch (NoSuchMethodException ex) {
252 throw new IllegalArgumentException(fieldPath, ex);
253 }
254
255
256 }
257
258 public static boolean matchesEqual(Object dataValue, Object criteriaValue) {
259 if (dataValue == criteriaValue) {
260 return true;
261 }
262 if (dataValue == null && criteriaValue == null) {
263 return true;
264 }
265 if (dataValue == null) {
266 return false;
267 }
268 return dataValue.equals(criteriaValue);
269 }
270
271 public static boolean matchesLessThan(Object dataValue, Object criteriaValue) {
272 if (dataValue == criteriaValue) {
273 return false;
274 }
275 if (dataValue == null && criteriaValue == null) {
276 return false;
277 }
278 if (dataValue == null) {
279 return false;
280 }
281 if (criteriaValue instanceof Comparable) {
282 Comparable comp1 = (Comparable) dataValue;
283 Comparable comp2 = (Comparable) criteriaValue;
284 if (comp1.compareTo(comp2) < 0) {
285 return true;
286 }
287 return false;
288 }
289 throw new IllegalArgumentException("The values are not comparable " + criteriaValue);
290 }
291
292 public static boolean matchesGreaterThan(Object dataValue, Object criteriaValue) {
293 if (dataValue == criteriaValue) {
294 return false;
295 }
296 if (dataValue == null && criteriaValue == null) {
297 return false;
298 }
299 if (dataValue == null) {
300 return false;
301 }
302 if (criteriaValue instanceof Comparable) {
303 Comparable comp1 = (Comparable) dataValue;
304 Comparable comp2 = (Comparable) criteriaValue;
305 if (comp1.compareTo(comp2) > 0) {
306 return true;
307 }
308 return false;
309 }
310 throw new IllegalArgumentException("The values are not comparable " + criteriaValue);
311 }
312
313 private transient Map<String, Pattern> patternCache = new HashMap<String, Pattern>();
314
315 private Pattern getPattern(String expr) {
316 Pattern p = patternCache.get(expr);
317 if (p == null) {
318 p = compilePattern(expr);
319 patternCache.put(expr, p);
320 }
321 return p;
322 }
323
324 public boolean matchesLike(Object dataValue, Object criteriaValue) {
325 if (dataValue == criteriaValue) {
326 return false;
327 }
328 if (dataValue == null && criteriaValue == null) {
329 return false;
330 }
331 if (dataValue == null) {
332 return false;
333 }
334 return matchesLikeCachingPattern(dataValue.toString(), criteriaValue.toString());
335 }
336
337
338
339
340
341 public boolean matchesLikeCachingPattern(final String str, final String expr) {
342 return matchesLike(str, getPattern(expr));
343 }
344
345 private static Pattern compilePattern(final String expr) {
346 String regex = quotemeta(expr);
347 regex = regex.replace("_", ".").replace("%", ".*?");
348 Pattern p = Pattern.compile(regex, Pattern.DOTALL);
349 return p;
350 }
351
352
353
354
355
356
357 public static boolean matchesLike(final String str, final String expr) {
358 Pattern p = compilePattern(expr);
359 return matchesLike(str, p);
360 }
361
362 private static boolean matchesLike(final String str, final Pattern p) {
363 return p.matcher(str).matches();
364 }
365
366 private static String quotemeta(String s) {
367 if (s == null) {
368 throw new IllegalArgumentException("String cannot be null");
369 }
370
371 int len = s.length();
372 if (len == 0) {
373 return "";
374 }
375
376 StringBuilder sb = new StringBuilder(len * 2);
377 for (int i = 0; i < len; i++) {
378 char c = s.charAt(i);
379 if ("[](){}.*+?$^|#\\".indexOf(c) != -1) {
380 sb.append("\\");
381 }
382 sb.append(c);
383 }
384 return sb.toString();
385 }
386 }