Clover Coverage Report - KS Common 1.2.1-SNAPSHOT (Aggregated)
Coverage timestamp: Wed Nov 2 2011 04:55:08 EST
../../../../../../../img/srcFileCovDistChart2.png 27% of files have more coverage
191   429   73   14.69
56   307   0.38   6.5
13     5.62  
2    
 
  CrossSearchManager       Line # 53 191 0% 73 217 16.5% 0.16538462
  CrossSearchManager.DataType       Line # 299 0 - 0 0 - -1.0
 
  (1)
 
1    /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10    * software distributed under the License is distributed on an "AS IS"
11    * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12    * or implied. See the License for the specific language governing
13    * permissions and limitations under the License.
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.Date;
21    import java.util.HashMap;
22    import java.util.List;
23    import java.util.Map;
24   
25    import org.kuali.student.common.search.dto.CrossSearchTypeInfo;
26    import org.kuali.student.common.search.dto.JoinComparisonInfo;
27    import org.kuali.student.common.search.dto.JoinComparisonInfo.ComparisonType;
28    import org.kuali.student.common.search.dto.JoinCriteriaInfo;
29    import org.kuali.student.common.search.dto.JoinCriteriaInfo.JoinType;
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.SubSearchInfo;
37    import org.kuali.student.common.search.dto.SubSearchParamMappingInfo;
38    import org.kuali.student.common.search.service.SearchDispatcher;
39   
40    /**
41    * This still needs a few things
42    * 1 - no search meta(sort, pagination) is implemented
43    * 2 - a way to do subselects should be implemented to reduce the processing and sheer size of the unions
44    * (for example if searching for LO and the related CLU by the LO description, we need to match ALL CLUs
45    * with just the LOs that match, meaning if we had 1000 clus, and 10 LOs we would be comparing 10000 results)
46    *
47    *
48    */
49    /**
50    * @author Daniel Epstein
51    *
52    */
 
53    public class CrossSearchManager {
54    private SearchDispatcher searchDispatcher;
55   
 
56  1 toggle public SearchResult doCrossSearch(SearchRequest searchRequest, CrossSearchTypeInfo crossSearchType) {
57  1 SearchResult searchResult = new SearchResult();
58   
59  1 Map<String,SearchResult> subSearchResults = new HashMap<String,SearchResult>();
60   
61    //First perform all the subsearches
62  1 for(SubSearchInfo subSearch:crossSearchType.getSubSearches()){
63    //Map the parameters to the subsearch
64  2 SearchRequest subSearchRequest = new SearchRequest();
65   
66  2 subSearchRequest.setSearchKey(subSearch.getSearchkey());
67  2 subSearchRequest.setParams(new ArrayList<SearchParam>());
68  2 subSearchRequest.setSortColumn(searchRequest.getSortColumn());
69  2 subSearchRequest.setSortDirection(searchRequest.getSortDirection());
70   
71    //For each param mapping, map the paramvalue from the cross search to the sub search
72  2 for(SubSearchParamMappingInfo paramMapping:subSearch.getSubSearchParamMappings()){
73  0 for(SearchParam crossSearchParam:searchRequest.getParams()){
74  0 if(paramMapping.getCrossSearchParam().equals(crossSearchParam.getKey())){
75  0 SearchParam subSearchParam = new SearchParam();
76  0 subSearchParam.setKey(paramMapping.getSubSearchParam());
77  0 Object paramValue = crossSearchParam.getValue();
78  0 if(paramValue instanceof String){
79  0 subSearchParam.setValue((String)paramValue);
80  0 }else if(paramValue instanceof List<?>){
81  0 subSearchParam.setValue((List<String>)paramValue);
82    }
83  0 subSearchRequest.getParams().add(subSearchParam);
84    }
85    }
86    }
87  2 SearchResult subSearchResult = searchDispatcher.dispatchSearch(subSearchRequest);
88  2 subSearchResults.put(subSearch.getKey(), subSearchResult);
89    }
90   
91    //merge the subsearches together using the join rules
92  1 if(crossSearchType.getJoinCriteria().getComparisons().isEmpty()){
93    //If the root join has no criteria then do a simple union of rows
94  1 for(Map.Entry<String,SearchResult> subSearchResult:subSearchResults.entrySet()){
95  2 for(SearchResultRow row:subSearchResult.getValue().getRows()){
96  4 SearchResultRow mappedResult = mapResultRow(subSearchResult.getKey(),row,crossSearchType);
97  4 searchResult.getRows().add(mappedResult);
98    }
99    }
100    }else{
101    //merge the subsearches together using the join rules (this is in o^2 time which is bad)
102  0 List <Map<String,SearchResultRow>> allPermutations = unionOfAllRows(subSearchResults);
103   
104  0 for(Map<String,SearchResultRow> permutation:allPermutations){
105  0 if(meetsCriteria(permutation,crossSearchType,crossSearchType.getJoinCriteria())){
106  0 SearchResultRow mappedResult = mapResultRow(permutation,crossSearchType);
107  0 searchResult.getRows().add(mappedResult);
108    }
109    }
110    }
111  1 return metaFilter(searchResult,searchRequest);
112    }
113   
114   
115   
116   
117    /**
118    * @param searchResult
119    * @param searchRequest
120    * @return a sorted and paginated result
121    */
 
122  1 toggle private SearchResult metaFilter(SearchResult searchResult,
123    SearchRequest searchRequest) {
124   
125  1 searchResult.setTotalResults(searchResult.getRows().size());
126   
127  1 searchResult.sortRows();
128   
129    //Paginate if we need to
130  1 if(searchRequest.getMaxResults()!=null){
131  0 int fromIndex=0;
132  0 if(searchRequest.getStartAt()!=null){
133  0 fromIndex=searchRequest.getStartAt();
134    }
135  0 int toIndex = fromIndex+searchRequest.getMaxResults();
136  0 SearchResult pagedResult = new SearchResult();
137  0 for (int i=fromIndex; i <= toIndex; i++) {
138  0 if (!(searchResult.getRows().size() < i+1)) {
139  0 pagedResult.getRows().add(searchResult.getRows().get(i));
140    }
141    }
142  0 pagedResult.setTotalResults(searchResult.getRows().size());
143  0 searchResult = pagedResult;
144    }
145  1 return searchResult;
146    }
147   
148    /**
149    * Maps results from multiple searches into a single result row
150    *
151    * @param permutation
152    * @param crossSearchType
153    * @return a mapped SearchResultRow
154    */
 
155  0 toggle private SearchResultRow mapResultRow(
156    Map<String, SearchResultRow> permutation,
157    CrossSearchTypeInfo crossSearchType) {
158    //FIXME this is pretty inefficient to loop through everything... a map structure for the cells might be better
159  0 SearchResultRow resultRow = new SearchResultRow();
160  0 for(JoinResultMappingInfo resultMapping: crossSearchType.getJoinResultMappings()){
161  0 for(SearchResultCell cell: permutation.get(resultMapping.getSubSearchKey()).getCells()){
162  0 if(resultMapping.getSubSearchResultParam().equals(cell.getKey())){
163  0 SearchResultCell mappedCell = new SearchResultCell();
164  0 mappedCell.setKey(resultMapping.getResultParam());
165  0 mappedCell.setValue(cell.getValue());
166  0 resultRow.getCells().add(mappedCell);
167  0 break;//FIXME breaks are bad... but there is no map in the cells
168    }
169    }
170    }
171  0 return resultRow;
172   
173    }
174   
 
175  4 toggle private SearchResultRow mapResultRow(
176    String subSearchKey, SearchResultRow row,
177    CrossSearchTypeInfo crossSearchType) {
178  4 SearchResultRow resultRow = new SearchResultRow();
179   
180  4 for(JoinResultMappingInfo resultMapping: crossSearchType.getJoinResultMappings()){
181  16 if(subSearchKey.equals(resultMapping.getSubSearchKey())){
182  8 for(SearchResultCell cell: row.getCells()){
183  12 if(resultMapping.getSubSearchResultParam().equals(cell.getKey())){
184  8 SearchResultCell mappedCell = new SearchResultCell();
185  8 mappedCell.setKey(resultMapping.getResultParam());
186  8 mappedCell.setValue(cell.getValue());
187  8 resultRow.getCells().add(mappedCell);
188  8 break;//FIXME breaks are bad... but there is no map in the cells
189    }
190    }
191    }
192    }
193  4 return resultRow;
194    }
195    /**
196    * Checks each comparison of the join criteria and recursively checks through nested criteria.
197    * Short circuits for false 'AND' joins and true 'OR' joins
198    * @param permutation
199    * @param crossSearchType
200    * @param joinCriteria
201    * @return whether the criteria is met
202    */
 
203  0 toggle private boolean meetsCriteria(Map<String, SearchResultRow> permutation,
204    CrossSearchTypeInfo crossSearchType, JoinCriteriaInfo joinCriteria){
205   
206  0 JoinType joinType = joinCriteria.getJoinType();
207   
208    //Check actual comparisons
209  0 for(JoinComparisonInfo comparison:joinCriteria.getComparisons()){
210  0 SearchResultRow leftResultRow = permutation.get(comparison.getLeftHandSide().getSubSearchKey());
211  0 String leftResultValue = null;
212  0 if(leftResultRow!=null){
213  0 for(SearchResultCell cell: leftResultRow.getCells()){
214  0 if(comparison.getLeftHandSide().getParam().equals(cell.getKey())){
215  0 leftResultValue = cell.getValue();
216  0 break;//FIXME breaks are bad... but there is no map in the cells
217    }
218    }
219    }
220   
221  0 SearchResultRow rightResultRow = permutation.get(comparison.getRightHandSide().getSubSearchKey());
222  0 String rightResultValue = null;
223  0 if(rightResultRow!=null){
224  0 for(SearchResultCell cell: rightResultRow.getCells()){
225  0 if(comparison.getRightHandSide().getParam().equals(cell.getKey())){
226  0 rightResultValue = cell.getValue();
227  0 break;//FIXME breaks are bad... but there is no map in the cells
228    }
229    }
230    }
231   
232    //Get the compare type for the
233    //TODO get the types for the params!
234  0 if(leftResultValue==null||rightResultValue==null){
235  0 int i=0;i++;
236    }
237  0 if(compare(null, leftResultValue,rightResultValue,comparison.getType())){
238  0 if(JoinType.OR.equals(joinType)){
239  0 return true;
240    }
241    }else{
242  0 if(JoinType.AND.equals(joinType)){
243  0 return false;
244    }
245    }
246    }
247   
248    //Check all subcriteria next
249  0 for(JoinCriteriaInfo subCriteria: joinCriteria.getJoinCriteria()){
250  0 if(meetsCriteria(permutation, crossSearchType, subCriteria)){
251  0 if(JoinType.OR.equals(joinType)){
252  0 return true;
253    }
254    }else{
255  0 if(JoinType.AND.equals(joinType)){
256  0 return false;
257    }
258    }
259    }
260   
261  0 if(JoinType.AND.equals(joinType)){
262  0 return true;
263    }
264  0 if(JoinType.OR.equals(joinType)){
265  0 return false;
266    }
267   
268  0 return false;
269    }
270   
271    /**
272    * @param searchResults
273    * @return a list of all possible combinations of rows
274    */
 
275  0 toggle private List <Map<String,SearchResultRow>> unionOfAllRows(Map<String, SearchResult> searchResults){
276  0 List <Map<String,SearchResultRow>> r = new ArrayList<Map<String,SearchResultRow>>();
277  0 for(Map.Entry<String,SearchResult> x:searchResults.entrySet()){
278  0 List<Map<String,SearchResultRow>> t = new ArrayList<Map<String,SearchResultRow>>();
279  0 if(x.getValue()!=null&&x.getValue().getRows()!=null){
280  0 for(SearchResultRow y:x.getValue().getRows()){
281  0 for(Map<String,SearchResultRow> i:r){
282  0 Map<String,SearchResultRow> unions = new HashMap<String,SearchResultRow>();
283  0 unions.putAll(i);
284  0 unions.put(x.getKey(), y);
285  0 t.add(unions);
286    }
287  0 if(r.size()==0){
288  0 Map<String,SearchResultRow> unions = new HashMap<String,SearchResultRow>();
289  0 unions.put(x.getKey(), y);
290  0 t.add(unions);
291    }
292    }
293    }
294  0 r = t;
295    }
296  0 return r;
297    }
298   
 
299    private enum DataType{STRING,INT,BOOLEAN,DATE}
300   
301   
302   
 
303  0 toggle private boolean compare(DataType dataType, String left, String right,
304    ComparisonType type ){
305    //FIXME needs a handle to the result params data types here
306  0 try{
307  0 Integer leftInteger = Integer.parseInt(left);
308  0 Integer rightInteger = Integer.parseInt(right);
309  0 return compareInt(leftInteger,rightInteger,type);
310    }catch(Exception e){
311    }
312  0 try{
313  0 if(("true".equals(left.toLowerCase())||"false".equals(left.toLowerCase()))&&
314    ("true".equals(right.toLowerCase())||"false".equals(right.toLowerCase()))){
315  0 Boolean leftBoolean = Boolean.parseBoolean(left);
316  0 Boolean rightBoolean = Boolean.parseBoolean(right);
317  0 return compareBoolean(leftBoolean,rightBoolean,type);
318    }
319    }catch(Exception e){
320    }
321  0 try{
322  0 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
323  0 Date leftDate = df.parse(left);
324  0 Date rightDate = df.parse(right);
325  0 return compareDate(leftDate,rightDate,type);
326    }catch(Exception e){
327    }
328  0 return compareString(left,right,type);
329    // switch(dataType){
330    // case BOOLEAN:
331    // Boolean leftBoolean = new Boolean(left);
332    // Boolean rightBoolean = new Boolean(right);
333    // return compareBoolean(leftBoolean,rightBoolean,type);
334    // case DATE:
335    // SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
336    // Date leftDate = df.parse(left);
337    // Date rightDate = df.parse(right);
338    // return compareDate(leftDate,rightDate,type);
339    // case INT:
340    // Integer leftInteger = Integer.getInteger(left);
341    // Integer rightInteger = Integer.getInteger(right);
342    // return compareInt(leftInteger,rightInteger,type);
343    // case STRING:
344    // return compareString(left,right,type);
345    // }
346    // return false;
347    }
348   
 
349  0 toggle private boolean compareString(String left, String right, ComparisonType type) {
350  0 switch(type){
351  0 case EQUALS:
352  0 return left.equals(right);
353  0 case GREATERTHAN:
354  0 return left.compareTo(right) > 0;
355  0 case GREATERTHANEQUALS:
356  0 return left.compareTo(right) >= 0;
357  0 case LESSTHAN:
358  0 return left.compareTo(right) < 0;
359  0 case LESSTHANEQUALS:
360  0 return left.compareTo(right) <= 0;
361  0 case NOTEQUALS:
362  0 return !left.equals(right);
363    }
364  0 return false;
365    }
366   
 
367  0 toggle private boolean compareInt(Integer left, Integer right, ComparisonType type) {
368  0 switch(type){
369  0 case EQUALS:
370  0 return left.equals(right);
371  0 case GREATERTHAN:
372  0 return left.compareTo(right) > 0;
373  0 case GREATERTHANEQUALS:
374  0 return left.compareTo(right) >= 0;
375  0 case LESSTHAN:
376  0 return left.compareTo(right) < 0;
377  0 case LESSTHANEQUALS:
378  0 return left.compareTo(right) <= 0;
379  0 case NOTEQUALS:
380  0 return !left.equals(right);
381    }
382  0 return false;
383    }
384   
 
385  0 toggle private boolean compareDate(Date left, Date right, ComparisonType type) {
386  0 switch(type){
387  0 case EQUALS:
388  0 return left.equals(right);
389  0 case GREATERTHAN:
390  0 return left.compareTo(right) > 0;
391  0 case GREATERTHANEQUALS:
392  0 return left.compareTo(right) >= 0;
393  0 case LESSTHAN:
394  0 return left.compareTo(right) < 0;
395  0 case LESSTHANEQUALS:
396  0 return left.compareTo(right) <= 0;
397  0 case NOTEQUALS:
398  0 return !left.equals(right);
399    }
400  0 return false;
401    }
402   
 
403  0 toggle private boolean compareBoolean(Boolean left, Boolean right,
404    ComparisonType type) {
405  0 switch(type){
406  0 case EQUALS:
407  0 return left.equals(right);
408  0 case GREATERTHAN:
409  0 return left.compareTo(right) > 0;
410  0 case GREATERTHANEQUALS:
411  0 return left.compareTo(right) >= 0;
412  0 case LESSTHAN:
413  0 return left.compareTo(right) < 0;
414  0 case LESSTHANEQUALS:
415  0 return left.compareTo(right) <= 0;
416  0 case NOTEQUALS:
417  0 return !left.equals(right);
418    }
419  0 return false;
420    }
 
421  2 toggle public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
422  2 this.searchDispatcher = searchDispatcher;
423    }
424   
 
425  0 toggle public SearchDispatcher getSearchDispatcher() {
426  0 return searchDispatcher;
427    }
428   
429    }