Coverage Report - org.kuali.student.common.search.service.impl.CrossSearchManager
 
Classes in this File Line Coverage Branch Coverage Complexity
CrossSearchManager
21%
38/177
10%
17/170
10.143
CrossSearchManager$1
0%
0/1
N/A
10.143
CrossSearchManager$DataType
0%
0/1
N/A
10.143
CrossSearchManager$SearchResultRowComparator
0%
0/40
0%
0/28
10.143
 
 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.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  
  * This still needs a few things
 45  
  * 1 - no search meta(sort, pagination) is implemented
 46  
  * 2 - a way to do subselects should be implemented to reduce the processing and sheer size of the unions
 47  
  * (for example if searching for LO and the related CLU by the LO description, we need to match ALL CLUs 
 48  
  * with just the LOs that match, meaning if we had 1000 clus, and 10 LOs we would be comparing 10000 results)
 49  
  * 
 50  
  *
 51  
  */
 52  
 /**
 53  
  * @author Daniel Epstein
 54  
  *
 55  
  */
 56  1
 public class CrossSearchManager {
 57  
         private SearchDispatcher searchDispatcher;
 58  
 
 59  
         public SearchResult doCrossSearch(SearchRequest searchRequest, CrossSearchTypeInfo crossSearchType) {
 60  1
                 SearchResult searchResult = new SearchResult();
 61  
                 
 62  1
                 Map<String,SearchResult> subSearchResults = new HashMap<String,SearchResult>();
 63  
                 
 64  
                 //First perform all the subsearches
 65  1
                 for(SubSearchInfo subSearch:crossSearchType.getSubSearches()){
 66  
                         //Map the parameters to the subsearch
 67  2
                         SearchRequest subSearchRequest = new SearchRequest();
 68  
                         
 69  2
                         subSearchRequest.setSearchKey(subSearch.getSearchkey());
 70  2
                         subSearchRequest.setParams(new ArrayList<SearchParam>());
 71  
                         
 72  
                         //For each param mapping, map the paramvalue from the cross search to the sub search
 73  2
                         for(SubSearchParamMappingInfo paramMapping:subSearch.getSubSearchParamMappings()){
 74  0
                                 for(SearchParam crossSearchParam:searchRequest.getParams()){
 75  0
                                         if(paramMapping.getCrossSearchParam().equals(crossSearchParam.getKey())){
 76  0
                                                 SearchParam subSearchParam = new SearchParam();
 77  0
                                                 subSearchParam.setKey(paramMapping.getSubSearchParam());
 78  0
                                                 Object paramValue = crossSearchParam.getValue();
 79  0
                                                 if(paramValue instanceof String){
 80  0
                                                         subSearchParam.setValue((String)paramValue);
 81  0
                                                 }else if(paramValue instanceof List<?>){
 82  0
                                                         subSearchParam.setValue((List<String>)paramValue);
 83  
                                                 }
 84  0
                                                 subSearchRequest.getParams().add(subSearchParam);
 85  0
                                         }
 86  
                                 }
 87  
                         }
 88  2
                         SearchResult subSearchResult = searchDispatcher.dispatchSearch(subSearchRequest);
 89  2
                         subSearchResults.put(subSearch.getKey(), subSearchResult);
 90  2
                 }
 91  
                 
 92  
                 //merge the subsearches together using the join rules
 93  1
                 if(crossSearchType.getJoinCriteria().getComparisons().isEmpty()){
 94  
                         //If the root join has no criteria then do a simple union of rows
 95  1
                         for(Map.Entry<String,SearchResult> subSearchResult:subSearchResults.entrySet()){
 96  2
                                 for(SearchResultRow row:subSearchResult.getValue().getRows()){
 97  4
                                         SearchResultRow mappedResult = mapResultRow(subSearchResult.getKey(),row,crossSearchType);
 98  4
                                         searchResult.getRows().add(mappedResult);
 99  4
                                 }
 100  
                         }
 101  
                 }else{
 102  
                         //merge the subsearches together using the join rules (this is in o^2 time which is bad)
 103  0
                         List <Map<String,SearchResultRow>> allPermutations = unionOfAllRows(subSearchResults);
 104  
         
 105  0
                         for(Map<String,SearchResultRow> permutation:allPermutations){
 106  0
                                 if(meetsCriteria(permutation,crossSearchType,crossSearchType.getJoinCriteria())){
 107  0
                                         SearchResultRow mappedResult = mapResultRow(permutation,crossSearchType);
 108  0
                                         searchResult.getRows().add(mappedResult);
 109  0
                                 }
 110  
                         }
 111  
                 }
 112  1
                 return metaFilter(searchResult,searchRequest);
 113  
         }
 114  
         
 115  
         
 116  
         
 117  
         
 118  
         /**
 119  
          * @param searchResult
 120  
          * @param searchRequest
 121  
          * @return a sorted and paginated result
 122  
          */
 123  
         private SearchResult metaFilter(SearchResult searchResult,
 124  
                 SearchRequest searchRequest) {
 125  
                 
 126  1
                 searchResult.setTotalResults(searchResult.getRows().size());
 127  1
                 final String sortColumn = searchRequest.getSortColumn();
 128  1
                 final SortDirection sortDirection = searchRequest.getSortDirection();
 129  
                 
 130  
                 //Sort if we need to
 131  1
                 if(sortColumn!=null){
 132  0
                         Collections.sort(searchResult.getRows(), new SearchResultRowComparator(sortColumn,sortDirection));
 133  
                 }
 134  
                 
 135  
                 
 136  
                 
 137  
                 //Paginate if we need to
 138  1
                 if(searchRequest.getMaxResults()!=null){
 139  0
                         int fromIndex=0;
 140  0
                         if(searchRequest.getStartAt()!=null){
 141  0
                                 fromIndex=searchRequest.getStartAt();
 142  
                         }
 143  0
                         int toIndex = fromIndex+searchRequest.getMaxResults();
 144  0
                         SearchResult pagedResult = new SearchResult();
 145  0
                         for (int i=fromIndex; i <= toIndex; i++) {
 146  0
                                 if (!(searchResult.getRows().size() < i+1)) {
 147  0
                                         pagedResult.getRows().add(searchResult.getRows().get(i));
 148  
                                 }
 149  
                         }
 150  
                         
 151  0
                         searchResult = pagedResult;
 152  
                 }
 153  1
                 return searchResult;
 154  
         }
 155  
 
 156  
         
 157  
         /**
 158  
          * Compares two SearchResultRow rows with a given sort direction and column
 159  
          *
 160  
          */
 161  0
         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  0
                         super();
 168  0
                         this.sortColumn = sortColumn;
 169  0
                         this.sortDirection = sortDirection;
 170  0
                 }
 171  
                 
 172  
                 @Override
 173  
                 public int compare(SearchResultRow r1, SearchResultRow r2) {
 174  0
                         int compareResult = 0;
 175  
                         
 176  
                         //Pares out the cell values to compare
 177  0
                         String v1=null;
 178  0
                         String v2=null;
 179  0
                         for(SearchResultCell c:r1.getCells()){
 180  0
                                 if(sortColumn.equals(c.getKey())){
 181  0
                                         v1=c.getValue();
 182  0
                                         break;
 183  
                                 }
 184  
                         }
 185  0
                         for(SearchResultCell c:r2.getCells()){
 186  0
                                 if(sortColumn.equals(c.getKey())){
 187  0
                                         v2=c.getValue();
 188  0
                                         break;
 189  
                                 }
 190  
                         }
 191  
                         
 192  
                         //Compare the values wiuth the right type (SHould be done more efficiently
 193  
                         try{
 194  0
                                 Integer v1Integer = Integer.parseInt(v1);
 195  0
                                 Integer v2Integer = Integer.parseInt(v2);
 196  0
                                 compareResult = v1Integer.compareTo(v2Integer);
 197  0
                         }catch(Exception e1){
 198  0
                                 if(v1!=null&&v2!=null&&("true".equals(v1.toLowerCase())||"false".equals(v1.toLowerCase()))&&
 199  
                                    ("true".equals(v2.toLowerCase())||"false".equals(v2.toLowerCase()))){
 200  0
                                         Boolean v1Boolean = Boolean.parseBoolean(v1);
 201  0
                                         Boolean v2Boolean = Boolean.parseBoolean(v2);
 202  0
                                         compareResult = v1Boolean.compareTo(v2Boolean);
 203  0
                                 }else{
 204  
                                         try{
 205  0
                                                 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
 206  0
                                                 Date v1Date = df.parse(v1);
 207  0
                                                 Date v2Date = df.parse(v2);
 208  0
                                                 compareResult = v1Date.compareTo(v2Date);
 209  0
                                         }catch(Exception e){
 210  0
                                                 if(v1!=null && v2!=null){
 211  0
                                                         compareResult = v1.compareTo(v2);
 212  0
                                                 }else if(v2==null){
 213  0
                                                         compareResult = 0;
 214  
                                                 }else{
 215  0
                                                         compareResult = -1; 
 216  
                                                 }
 217  0
                                         }
 218  
                                 }
 219  0
                         }
 220  
                         
 221  
                         //Sort reverse if order is descending
 222  0
                         if(SortDirection.DESC.equals(sortDirection)){
 223  0
                                 return -1 * compareResult;
 224  
                         }
 225  0
                         return compareResult;
 226  
                 }
 227  
                 
 228  
         }
 229  
 
 230  
 
 231  
 
 232  
         /**
 233  
          * Maps results from multiple searches into a single result row
 234  
          *
 235  
          * @param permutation
 236  
          * @param crossSearchType
 237  
          * @return a mapped SearchResultRow
 238  
          */
 239  
         private SearchResultRow mapResultRow(
 240  
                         Map<String, SearchResultRow> permutation,
 241  
                         CrossSearchTypeInfo crossSearchType) {
 242  
                 //FIXME this is pretty inefficient to loop through everything... a map structure for the cells might be better
 243  0
                 SearchResultRow resultRow = new SearchResultRow();
 244  0
                 for(JoinResultMappingInfo resultMapping: crossSearchType.getJoinResultMappings()){
 245  0
                         for(SearchResultCell cell: permutation.get(resultMapping.getSubSearchKey()).getCells()){
 246  0
                                 if(resultMapping.getSubSearchResultParam().equals(cell.getKey())){
 247  0
                                         SearchResultCell mappedCell = new SearchResultCell();
 248  0
                                         mappedCell.setKey(resultMapping.getResultParam());
 249  0
                                         mappedCell.setValue(cell.getValue());
 250  0
                                         resultRow.getCells().add(mappedCell);
 251  0
                                         break;//FIXME breaks are bad... but there is no map in the cells
 252  
                                 }
 253  
                         }
 254  
                 }
 255  0
                 return resultRow;
 256  
                 
 257  
         }
 258  
 
 259  
         private SearchResultRow mapResultRow(
 260  
                         String subSearchKey, SearchResultRow row,
 261  
                         CrossSearchTypeInfo crossSearchType) {
 262  4
                 SearchResultRow resultRow = new SearchResultRow();
 263  
                 
 264  4
                 for(JoinResultMappingInfo resultMapping: crossSearchType.getJoinResultMappings()){
 265  16
                         if(subSearchKey.equals(resultMapping.getSubSearchKey())){
 266  8
                                 for(SearchResultCell cell: row.getCells()){
 267  12
                                         if(resultMapping.getSubSearchResultParam().equals(cell.getKey())){
 268  8
                                                 SearchResultCell mappedCell = new SearchResultCell();
 269  8
                                                 mappedCell.setKey(resultMapping.getResultParam());
 270  8
                                                 mappedCell.setValue(cell.getValue());
 271  8
                                                 resultRow.getCells().add(mappedCell);
 272  8
                                                 break;//FIXME breaks are bad... but there is no map in the cells
 273  
                                         }
 274  
                                 }
 275  
                         }
 276  
                 }
 277  4
                 return resultRow;
 278  
         }
 279  
         /**
 280  
          * Checks each comparison of the join criteria and recursively checks through nested criteria.  
 281  
          * Short circuits for false 'AND' joins and true 'OR' joins
 282  
          * @param permutation
 283  
          * @param crossSearchType
 284  
          * @param joinCriteria
 285  
          * @return whether the criteria is met
 286  
          */
 287  
         private boolean meetsCriteria(Map<String, SearchResultRow> permutation,
 288  
                         CrossSearchTypeInfo crossSearchType, JoinCriteriaInfo joinCriteria){
 289  
 
 290  0
                 JoinType joinType = joinCriteria.getJoinType();
 291  
                 
 292  
                 //Check actual comparisons
 293  0
                 for(JoinComparisonInfo comparison:joinCriteria.getComparisons()){
 294  0
                         SearchResultRow leftResultRow =  permutation.get(comparison.getLeftHandSide().getSubSearchKey());
 295  0
                         String leftResultValue = null;
 296  0
                         if(leftResultRow!=null){
 297  0
                                 for(SearchResultCell cell: leftResultRow.getCells()){
 298  0
                                         if(comparison.getLeftHandSide().getParam().equals(cell.getKey())){
 299  0
                                                 leftResultValue = cell.getValue();
 300  0
                                                 break;//FIXME breaks are bad... but there is no map in the cells
 301  
                                         }
 302  
                                 }
 303  
                         }
 304  
                         
 305  0
                         SearchResultRow rightResultRow =  permutation.get(comparison.getRightHandSide().getSubSearchKey());
 306  0
                         String rightResultValue = null;
 307  0
                         if(rightResultRow!=null){
 308  0
                                 for(SearchResultCell cell: rightResultRow.getCells()){
 309  0
                                         if(comparison.getRightHandSide().getParam().equals(cell.getKey())){
 310  0
                                                 rightResultValue = cell.getValue();
 311  0
                                                 break;//FIXME breaks are bad... but there is no map in the cells
 312  
                                         }
 313  
                                 }
 314  
                         }                        
 315  
                         
 316  
                         //Get the compare type for the 
 317  
                         //TODO get the types for the params!
 318  0
                         if(leftResultValue==null||rightResultValue==null){
 319  0
                                 int i=0;i++;
 320  
                         }
 321  0
                         if(compare(null, leftResultValue,rightResultValue,comparison.getType())){
 322  0
                                 if(JoinType.OR.equals(joinType)){
 323  0
                                         return true;
 324  
                                 }
 325  
                         }else{
 326  0
                                 if(JoinType.AND.equals(joinType)){
 327  0
                                         return false;
 328  
                                 }
 329  
                         }
 330  0
                 }
 331  
                 
 332  
                 //Check all subcriteria next
 333  0
                 for(JoinCriteriaInfo subCriteria: joinCriteria.getJoinCriteria()){
 334  0
                         if(meetsCriteria(permutation, crossSearchType, subCriteria)){
 335  0
                                 if(JoinType.OR.equals(joinType)){
 336  0
                                         return true;
 337  
                                 }
 338  
                         }else{
 339  0
                                 if(JoinType.AND.equals(joinType)){
 340  0
                                         return false;
 341  
                                 }
 342  
                         }
 343  
                 }
 344  
                 
 345  0
                 if(JoinType.AND.equals(joinType)){
 346  0
                         return true;
 347  
                 }
 348  0
                 if(JoinType.OR.equals(joinType)){
 349  0
                         return false;
 350  
                 }
 351  
                 
 352  0
                 return false;
 353  
         }
 354  
 
 355  
         /**
 356  
          * @param searchResults
 357  
          * @return a list of all possible combinations of rows
 358  
          */
 359  
         private List <Map<String,SearchResultRow>> unionOfAllRows(Map<String, SearchResult> searchResults){
 360  0
                 List <Map<String,SearchResultRow>> r = new ArrayList<Map<String,SearchResultRow>>();
 361  0
                 for(Map.Entry<String,SearchResult> x:searchResults.entrySet()){
 362  0
                         List<Map<String,SearchResultRow>> t = new ArrayList<Map<String,SearchResultRow>>();
 363  0
                         if(x.getValue()!=null&&x.getValue().getRows()!=null){
 364  0
                                 for(SearchResultRow y:x.getValue().getRows()){
 365  0
                                         for(Map<String,SearchResultRow> i:r){
 366  0
                                                 Map<String,SearchResultRow> unions =  new HashMap<String,SearchResultRow>();
 367  0
                                                 unions.putAll(i);
 368  0
                                                 unions.put(x.getKey(), y);
 369  0
                                                 t.add(unions);
 370  0
                                         }
 371  0
                                         if(r.size()==0){
 372  0
                                                 Map<String,SearchResultRow> unions  =  new HashMap<String,SearchResultRow>();
 373  0
                                                 unions.put(x.getKey(), y);
 374  0
                                                 t.add(unions);
 375  0
                                         }
 376  
                                 }
 377  
                         }
 378  0
                         r = t;
 379  0
                 }
 380  0
                 return r;
 381  
         }        
 382  
         
 383  0
         private enum DataType{STRING,INT,BOOLEAN,DATE}
 384  
         
 385  
 
 386  
 
 387  
         private boolean compare(DataType dataType, String left, String right,
 388  
                         ComparisonType type ){
 389  
                 //FIXME needs a handle to the result params data types here
 390  
                 try{
 391  0
                         Integer leftInteger = Integer.parseInt(left);
 392  0
                         Integer rightInteger = Integer.parseInt(right);
 393  0
                         return compareInt(leftInteger,rightInteger,type);
 394  0
                 }catch(Exception e){
 395  
                 }
 396  
                 try{
 397  0
                         if(("true".equals(left.toLowerCase())||"false".equals(left.toLowerCase()))&&
 398  
                            ("true".equals(right.toLowerCase())||"false".equals(right.toLowerCase()))){
 399  0
                                 Boolean leftBoolean = Boolean.parseBoolean(left);
 400  0
                                 Boolean rightBoolean = Boolean.parseBoolean(right);
 401  0
                                 return compareBoolean(leftBoolean,rightBoolean,type);
 402  
                         }
 403  0
                 }catch(Exception e){
 404  0
                 }
 405  
                 try{
 406  0
                         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
 407  0
                         Date leftDate = df.parse(left);
 408  0
                         Date rightDate = df.parse(right);
 409  0
                         return compareDate(leftDate,rightDate,type);
 410  0
                 }catch(Exception e){
 411  
                 }
 412  0
                 return compareString(left,right,type);
 413  
 //                switch(dataType){
 414  
 //                        case BOOLEAN:
 415  
 //                                Boolean leftBoolean = new Boolean(left);
 416  
 //                                Boolean rightBoolean = new Boolean(right);
 417  
 //                                return compareBoolean(leftBoolean,rightBoolean,type);
 418  
 //                        case DATE:
 419  
 //                                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
 420  
 //                                Date leftDate = df.parse(left);
 421  
 //                                Date rightDate = df.parse(right);
 422  
 //                                return compareDate(leftDate,rightDate,type);
 423  
 //                        case INT:
 424  
 //                                Integer leftInteger = Integer.getInteger(left);
 425  
 //                                Integer rightInteger = Integer.getInteger(right);
 426  
 //                                return compareInt(leftInteger,rightInteger,type);
 427  
 //                        case STRING:
 428  
 //                                return compareString(left,right,type);
 429  
 //                }
 430  
 //                return false;
 431  
         }
 432  
         
 433  
         private boolean compareString(String left, String right, ComparisonType type) {
 434  0
                 switch(type){
 435  
                 case EQUALS:
 436  0
                         return left.equals(right);
 437  
                 case GREATERTHAN:
 438  0
                         return left.compareTo(right) > 0;
 439  
                 case GREATERTHANEQUALS:
 440  0
                         return left.compareTo(right) >= 0;
 441  
                 case LESSTHAN:
 442  0
                         return left.compareTo(right) < 0;
 443  
                 case LESSTHANEQUALS:
 444  0
                         return left.compareTo(right) <= 0;
 445  
                 case NOTEQUALS:
 446  0
                         return !left.equals(right);
 447  
                 }
 448  0
                 return false;
 449  
         }
 450  
 
 451  
         private boolean compareInt(Integer left, Integer right, ComparisonType type) {
 452  0
                 switch(type){
 453  
                 case EQUALS:
 454  0
                         return left.equals(right);
 455  
                 case GREATERTHAN:
 456  0
                         return left.compareTo(right) > 0;
 457  
                 case GREATERTHANEQUALS:
 458  0
                         return left.compareTo(right) >= 0;
 459  
                 case LESSTHAN:
 460  0
                         return left.compareTo(right) < 0;
 461  
                 case LESSTHANEQUALS:
 462  0
                         return left.compareTo(right) <= 0;
 463  
                 case NOTEQUALS:
 464  0
                         return !left.equals(right);
 465  
                 }
 466  0
                 return false;
 467  
         }
 468  
 
 469  
         private boolean compareDate(Date left, Date right, ComparisonType type) {
 470  0
                 switch(type){
 471  
                 case EQUALS:
 472  0
                         return left.equals(right);
 473  
                 case GREATERTHAN:
 474  0
                         return left.compareTo(right) > 0;
 475  
                 case GREATERTHANEQUALS:
 476  0
                         return left.compareTo(right) >= 0;
 477  
                 case LESSTHAN:
 478  0
                         return left.compareTo(right) < 0;
 479  
                 case LESSTHANEQUALS:
 480  0
                         return left.compareTo(right) <= 0;
 481  
                 case NOTEQUALS:
 482  0
                         return !left.equals(right);
 483  
                 }
 484  0
                 return false;
 485  
         }
 486  
 
 487  
         private boolean compareBoolean(Boolean left, Boolean right,
 488  
                         ComparisonType type) {
 489  0
                 switch(type){
 490  
                 case EQUALS:
 491  0
                         return left.equals(right);
 492  
                 case GREATERTHAN:
 493  0
                         return left.compareTo(right) > 0;
 494  
                 case GREATERTHANEQUALS:
 495  0
                         return left.compareTo(right) >= 0;
 496  
                 case LESSTHAN:
 497  0
                         return left.compareTo(right) < 0;
 498  
                 case LESSTHANEQUALS:
 499  0
                         return left.compareTo(right) <= 0;
 500  
                 case NOTEQUALS:
 501  0
                         return !left.equals(right);
 502  
                 }
 503  0
                 return false;
 504  
         }
 505  
         public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
 506  1
                 this.searchDispatcher = searchDispatcher;
 507  1
         }
 508  
 
 509  
 
 510  
 }