1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.student.common.search.service.impl;
17
18 import java.text.SimpleDateFormat;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.kuali.student.common.search.dto.CrossSearchTypeInfo;
28 import org.kuali.student.common.search.dto.JoinComparisonInfo;
29 import org.kuali.student.common.search.dto.JoinCriteriaInfo;
30 import org.kuali.student.common.search.dto.JoinResultMappingInfo;
31 import org.kuali.student.common.search.dto.SearchParam;
32 import org.kuali.student.common.search.dto.SearchRequest;
33 import org.kuali.student.common.search.dto.SearchResult;
34 import org.kuali.student.common.search.dto.SearchResultCell;
35 import org.kuali.student.common.search.dto.SearchResultRow;
36 import org.kuali.student.common.search.dto.SortDirection;
37 import org.kuali.student.common.search.dto.SubSearchInfo;
38 import org.kuali.student.common.search.dto.SubSearchParamMappingInfo;
39 import org.kuali.student.common.search.dto.JoinComparisonInfo.ComparisonType;
40 import org.kuali.student.common.search.dto.JoinCriteriaInfo.JoinType;
41 import org.kuali.student.common.search.service.SearchDispatcher;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class CrossSearchManager {
57 private SearchDispatcher searchDispatcher;
58
59 public SearchResult doCrossSearch(SearchRequest searchRequest, CrossSearchTypeInfo crossSearchType) {
60 SearchResult searchResult = new SearchResult();
61
62 Map<String,SearchResult> subSearchResults = new HashMap<String,SearchResult>();
63
64
65 for(SubSearchInfo subSearch:crossSearchType.getSubSearches()){
66
67 SearchRequest subSearchRequest = new SearchRequest();
68
69 subSearchRequest.setSearchKey(subSearch.getSearchkey());
70 subSearchRequest.setParams(new ArrayList<SearchParam>());
71
72
73 for(SubSearchParamMappingInfo paramMapping:subSearch.getSubSearchParamMappings()){
74 for(SearchParam crossSearchParam:searchRequest.getParams()){
75 if(paramMapping.getCrossSearchParam().equals(crossSearchParam.getKey())){
76 SearchParam subSearchParam = new SearchParam();
77 subSearchParam.setKey(paramMapping.getSubSearchParam());
78 Object paramValue = crossSearchParam.getValue();
79 if(paramValue instanceof String){
80 subSearchParam.setValue((String)paramValue);
81 }else if(paramValue instanceof List<?>){
82 subSearchParam.setValue((List<String>)paramValue);
83 }
84 subSearchRequest.getParams().add(subSearchParam);
85 }
86 }
87 }
88 SearchResult subSearchResult = searchDispatcher.dispatchSearch(subSearchRequest);
89 subSearchResults.put(subSearch.getKey(), subSearchResult);
90 }
91
92
93 if(crossSearchType.getJoinCriteria().getComparisons().isEmpty()){
94
95 for(Map.Entry<String,SearchResult> subSearchResult:subSearchResults.entrySet()){
96 for(SearchResultRow row:subSearchResult.getValue().getRows()){
97 SearchResultRow mappedResult = mapResultRow(subSearchResult.getKey(),row,crossSearchType);
98 searchResult.getRows().add(mappedResult);
99 }
100 }
101 }else{
102
103 List <Map<String,SearchResultRow>> allPermutations = unionOfAllRows(subSearchResults);
104
105 for(Map<String,SearchResultRow> permutation:allPermutations){
106 if(meetsCriteria(permutation,crossSearchType,crossSearchType.getJoinCriteria())){
107 SearchResultRow mappedResult = mapResultRow(permutation,crossSearchType);
108 searchResult.getRows().add(mappedResult);
109 }
110 }
111 }
112 return metaFilter(searchResult,searchRequest);
113 }
114
115
116
117
118
119
120
121
122
123 private SearchResult metaFilter(SearchResult searchResult,
124 SearchRequest searchRequest) {
125
126 searchResult.setTotalResults(searchResult.getRows().size());
127 final String sortColumn = searchRequest.getSortColumn();
128 final SortDirection sortDirection = searchRequest.getSortDirection();
129
130
131 if(sortColumn!=null){
132 Collections.sort(searchResult.getRows(), new SearchResultRowComparator(sortColumn,sortDirection));
133 }
134
135
136
137
138 if(searchRequest.getMaxResults()!=null){
139 int fromIndex=0;
140 if(searchRequest.getStartAt()!=null){
141 fromIndex=searchRequest.getStartAt();
142 }
143 int toIndex = fromIndex+searchRequest.getMaxResults();
144 SearchResult pagedResult = new SearchResult();
145 for (int i=fromIndex; i <= toIndex; i++) {
146 if (!(searchResult.getRows().size() < i+1)) {
147 pagedResult.getRows().add(searchResult.getRows().get(i));
148 }
149 }
150
151 searchResult = pagedResult;
152 }
153 return searchResult;
154 }
155
156
157
158
159
160
161 private static class SearchResultRowComparator implements Comparator<SearchResultRow> {
162 private String sortColumn;
163 private SortDirection sortDirection;
164
165 public SearchResultRowComparator(String sortColumn,
166 SortDirection sortDirection) {
167 super();
168 this.sortColumn = sortColumn;
169 this.sortDirection = sortDirection;
170 }
171
172 @Override
173 public int compare(SearchResultRow r1, SearchResultRow r2) {
174 int compareResult = 0;
175
176
177 String v1=null;
178 String v2=null;
179 for(SearchResultCell c:r1.getCells()){
180 if(sortColumn.equals(c.getKey())){
181 v1=c.getValue();
182 break;
183 }
184 }
185 for(SearchResultCell c:r2.getCells()){
186 if(sortColumn.equals(c.getKey())){
187 v2=c.getValue();
188 break;
189 }
190 }
191
192
193 try{
194 Integer v1Integer = Integer.parseInt(v1);
195 Integer v2Integer = Integer.parseInt(v2);
196 compareResult = v1Integer.compareTo(v2Integer);
197 }catch(Exception e1){
198 if(v1!=null&&v2!=null&&("true".equals(v1.toLowerCase())||"false".equals(v1.toLowerCase()))&&
199 ("true".equals(v2.toLowerCase())||"false".equals(v2.toLowerCase()))){
200 Boolean v1Boolean = Boolean.parseBoolean(v1);
201 Boolean v2Boolean = Boolean.parseBoolean(v2);
202 compareResult = v1Boolean.compareTo(v2Boolean);
203 }else{
204 try{
205 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
206 Date v1Date = df.parse(v1);
207 Date v2Date = df.parse(v2);
208 compareResult = v1Date.compareTo(v2Date);
209 }catch(Exception e){
210 if(v1!=null && v2!=null){
211 compareResult = v1.compareTo(v2);
212 }else if(v2==null){
213 compareResult = 0;
214 }else{
215 compareResult = -1;
216 }
217 }
218 }
219 }
220
221
222 if(SortDirection.DESC.equals(sortDirection)){
223 return -1 * compareResult;
224 }
225 return compareResult;
226 }
227
228 }
229
230
231
232
233
234
235
236
237
238
239 private SearchResultRow mapResultRow(
240 Map<String, SearchResultRow> permutation,
241 CrossSearchTypeInfo crossSearchType) {
242
243 SearchResultRow resultRow = new SearchResultRow();
244 for(JoinResultMappingInfo resultMapping: crossSearchType.getJoinResultMappings()){
245 for(SearchResultCell cell: permutation.get(resultMapping.getSubSearchKey()).getCells()){
246 if(resultMapping.getSubSearchResultParam().equals(cell.getKey())){
247 SearchResultCell mappedCell = new SearchResultCell();
248 mappedCell.setKey(resultMapping.getResultParam());
249 mappedCell.setValue(cell.getValue());
250 resultRow.getCells().add(mappedCell);
251 break;
252 }
253 }
254 }
255 return resultRow;
256
257 }
258
259 private SearchResultRow mapResultRow(
260 String subSearchKey, SearchResultRow row,
261 CrossSearchTypeInfo crossSearchType) {
262 SearchResultRow resultRow = new SearchResultRow();
263
264 for(JoinResultMappingInfo resultMapping: crossSearchType.getJoinResultMappings()){
265 if(subSearchKey.equals(resultMapping.getSubSearchKey())){
266 for(SearchResultCell cell: row.getCells()){
267 if(resultMapping.getSubSearchResultParam().equals(cell.getKey())){
268 SearchResultCell mappedCell = new SearchResultCell();
269 mappedCell.setKey(resultMapping.getResultParam());
270 mappedCell.setValue(cell.getValue());
271 resultRow.getCells().add(mappedCell);
272 break;
273 }
274 }
275 }
276 }
277 return resultRow;
278 }
279
280
281
282
283
284
285
286
287 private boolean meetsCriteria(Map<String, SearchResultRow> permutation,
288 CrossSearchTypeInfo crossSearchType, JoinCriteriaInfo joinCriteria){
289
290 JoinType joinType = joinCriteria.getJoinType();
291
292
293 for(JoinComparisonInfo comparison:joinCriteria.getComparisons()){
294 SearchResultRow leftResultRow = permutation.get(comparison.getLeftHandSide().getSubSearchKey());
295 String leftResultValue = null;
296 if(leftResultRow!=null){
297 for(SearchResultCell cell: leftResultRow.getCells()){
298 if(comparison.getLeftHandSide().getParam().equals(cell.getKey())){
299 leftResultValue = cell.getValue();
300 break;
301 }
302 }
303 }
304
305 SearchResultRow rightResultRow = permutation.get(comparison.getRightHandSide().getSubSearchKey());
306 String rightResultValue = null;
307 if(rightResultRow!=null){
308 for(SearchResultCell cell: rightResultRow.getCells()){
309 if(comparison.getRightHandSide().getParam().equals(cell.getKey())){
310 rightResultValue = cell.getValue();
311 break;
312 }
313 }
314 }
315
316
317
318 if(leftResultValue==null||rightResultValue==null){
319 int i=0;i++;
320 }
321 if(compare(null, leftResultValue,rightResultValue,comparison.getType())){
322 if(JoinType.OR.equals(joinType)){
323 return true;
324 }
325 }else{
326 if(JoinType.AND.equals(joinType)){
327 return false;
328 }
329 }
330 }
331
332
333 for(JoinCriteriaInfo subCriteria: joinCriteria.getJoinCriteria()){
334 if(meetsCriteria(permutation, crossSearchType, subCriteria)){
335 if(JoinType.OR.equals(joinType)){
336 return true;
337 }
338 }else{
339 if(JoinType.AND.equals(joinType)){
340 return false;
341 }
342 }
343 }
344
345 if(JoinType.AND.equals(joinType)){
346 return true;
347 }
348 if(JoinType.OR.equals(joinType)){
349 return false;
350 }
351
352 return false;
353 }
354
355
356
357
358
359 private List <Map<String,SearchResultRow>> unionOfAllRows(Map<String, SearchResult> searchResults){
360 List <Map<String,SearchResultRow>> r = new ArrayList<Map<String,SearchResultRow>>();
361 for(Map.Entry<String,SearchResult> x:searchResults.entrySet()){
362 List<Map<String,SearchResultRow>> t = new ArrayList<Map<String,SearchResultRow>>();
363 if(x.getValue()!=null&&x.getValue().getRows()!=null){
364 for(SearchResultRow y:x.getValue().getRows()){
365 for(Map<String,SearchResultRow> i:r){
366 Map<String,SearchResultRow> unions = new HashMap<String,SearchResultRow>();
367 unions.putAll(i);
368 unions.put(x.getKey(), y);
369 t.add(unions);
370 }
371 if(r.size()==0){
372 Map<String,SearchResultRow> unions = new HashMap<String,SearchResultRow>();
373 unions.put(x.getKey(), y);
374 t.add(unions);
375 }
376 }
377 }
378 r = t;
379 }
380 return r;
381 }
382
383 private enum DataType{STRING,INT,BOOLEAN,DATE}
384
385
386
387 private boolean compare(DataType dataType, String left, String right,
388 ComparisonType type ){
389
390 try{
391 Integer leftInteger = Integer.parseInt(left);
392 Integer rightInteger = Integer.parseInt(right);
393 return compareInt(leftInteger,rightInteger,type);
394 }catch(Exception e){
395 }
396 try{
397 if(("true".equals(left.toLowerCase())||"false".equals(left.toLowerCase()))&&
398 ("true".equals(right.toLowerCase())||"false".equals(right.toLowerCase()))){
399 Boolean leftBoolean = Boolean.parseBoolean(left);
400 Boolean rightBoolean = Boolean.parseBoolean(right);
401 return compareBoolean(leftBoolean,rightBoolean,type);
402 }
403 }catch(Exception e){
404 }
405 try{
406 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
407 Date leftDate = df.parse(left);
408 Date rightDate = df.parse(right);
409 return compareDate(leftDate,rightDate,type);
410 }catch(Exception e){
411 }
412 return compareString(left,right,type);
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431 }
432
433 private boolean compareString(String left, String right, ComparisonType type) {
434 switch(type){
435 case EQUALS:
436 return left.equals(right);
437 case GREATERTHAN:
438 return left.compareTo(right) > 0;
439 case GREATERTHANEQUALS:
440 return left.compareTo(right) >= 0;
441 case LESSTHAN:
442 return left.compareTo(right) < 0;
443 case LESSTHANEQUALS:
444 return left.compareTo(right) <= 0;
445 case NOTEQUALS:
446 return !left.equals(right);
447 }
448 return false;
449 }
450
451 private boolean compareInt(Integer left, Integer right, ComparisonType type) {
452 switch(type){
453 case EQUALS:
454 return left.equals(right);
455 case GREATERTHAN:
456 return left.compareTo(right) > 0;
457 case GREATERTHANEQUALS:
458 return left.compareTo(right) >= 0;
459 case LESSTHAN:
460 return left.compareTo(right) < 0;
461 case LESSTHANEQUALS:
462 return left.compareTo(right) <= 0;
463 case NOTEQUALS:
464 return !left.equals(right);
465 }
466 return false;
467 }
468
469 private boolean compareDate(Date left, Date right, ComparisonType type) {
470 switch(type){
471 case EQUALS:
472 return left.equals(right);
473 case GREATERTHAN:
474 return left.compareTo(right) > 0;
475 case GREATERTHANEQUALS:
476 return left.compareTo(right) >= 0;
477 case LESSTHAN:
478 return left.compareTo(right) < 0;
479 case LESSTHANEQUALS:
480 return left.compareTo(right) <= 0;
481 case NOTEQUALS:
482 return !left.equals(right);
483 }
484 return false;
485 }
486
487 private boolean compareBoolean(Boolean left, Boolean right,
488 ComparisonType type) {
489 switch(type){
490 case EQUALS:
491 return left.equals(right);
492 case GREATERTHAN:
493 return left.compareTo(right) > 0;
494 case GREATERTHANEQUALS:
495 return left.compareTo(right) >= 0;
496 case LESSTHAN:
497 return left.compareTo(right) < 0;
498 case LESSTHANEQUALS:
499 return left.compareTo(right) <= 0;
500 case NOTEQUALS:
501 return !left.equals(right);
502 }
503 return false;
504 }
505 public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
506 this.searchDispatcher = searchDispatcher;
507 }
508
509 public SearchDispatcher getSearchDispatcher() {
510 return searchDispatcher;
511 }
512
513 }