View Javadoc

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.ui.client.widgets.suggestbox;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.kuali.student.common.ui.client.application.KSAsyncCallback;
22  import org.kuali.student.common.ui.client.service.CachingSearchService;
23  import org.kuali.student.common.ui.client.service.SearchRpcServiceAsync;
24  import org.kuali.student.common.ui.client.service.SearchServiceFactory;
25  import org.kuali.student.common.ui.client.widgets.KSErrorDialog;
26  import org.kuali.student.common.ui.client.widgets.notification.LoadingDiv;
27  import org.kuali.student.r1.common.assembly.data.LookupMetadata;
28  import org.kuali.student.r1.common.assembly.data.LookupParamMetadata;
29  import org.kuali.student.r1.common.assembly.data.Metadata.WriteAccess;
30  import org.kuali.student.r2.core.search.dto.*;
31  import org.kuali.student.r2.core.search.dto.SearchParamInfo;
32  
33  import com.google.gwt.user.client.ui.HasText;
34  import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
35  import com.google.gwt.user.client.ui.Widget;
36  
37  public class SearchSuggestOracle extends IdableSuggestOracle{
38      
39      private String searchTypeKey;
40      private String searchIdKey;
41      private String searchTextKey;
42      private String resultIdKey;
43      private Callback currentCallback;
44      private Request pendingRequest;
45      private Callback pendingCallback;
46      private HasText textWidget;
47      private String resultDisplayKey;
48      private String resultSortKey;
49      private SortDirection sortDirection;
50      private List<SearchParamInfo> additionalParams = new ArrayList<SearchParamInfo>();
51      private List<IdableSuggestion> lastSuggestions = new ArrayList<IdableSuggestion>();
52      
53      private LookupMetadata lookupMetaData;
54      private CachingSearchService cachingSearchService = CachingSearchService.getSearchService();
55      private SearchRpcServiceAsync searchService = SearchServiceFactory.getSearchService();
56  
57      private List<org.kuali.student.common.ui.client.mvc.Callback<IdableSuggestion>> searchCompletedCallbacks = new ArrayList<org.kuali.student.common.ui.client.mvc.Callback<IdableSuggestion>>();
58      
59      private LoadingDiv loading = new LoadingDiv();
60      
61      /**
62       * @deprecated
63       * @param searchTypeKey the type to be search on
64       * @param searchTextKey the column/key that to search on
65       * @param idKey the column/key that is the primary key for this type
66       */
67      public SearchSuggestOracle(String searchTypeKey, String searchTextKey, String searchIdKey, String resultIdKey, String resultDisplayKey){
68          this.searchTypeKey = searchTypeKey;
69          this.searchTextKey = searchTextKey;
70          this.searchIdKey = searchIdKey;
71          this.resultIdKey = resultIdKey;
72          this.resultDisplayKey = resultDisplayKey;
73      }
74      
75      /*
76       * 
77       */
78      public SearchSuggestOracle(LookupMetadata lookupMetadata) {
79      	this.lookupMetaData = lookupMetadata;
80          this.searchTypeKey = lookupMetaData.getSearchTypeId();
81          
82          for (LookupParamMetadata param : lookupMetadata.getParams()) {
83          	if ((param.getUsage() != null) && param.getUsage().name().equals("DEFAULT")) {
84          		this.searchTextKey = param.getKey();
85          	}
86          	//Add in any writeaccess never default values to the additional params
87          	if(WriteAccess.NEVER.equals(param.getWriteAccess())||param.getDefaultValueString()!=null||param.getDefaultValueList()!=null){
88          		SearchParamInfo searchParam = new SearchParamInfo();
89          		searchParam.setKey(param.getKey());
90  				if(param.getDefaultValueList()==null){
91  					searchParam.getValues().add(param.getDefaultValueString());
92  				}else{
93  					searchParam.setValues(param.getDefaultValueList());
94  				}
95  				additionalParams.add(searchParam);
96          	}
97          }
98          if (this.searchTextKey == null) {
99          	KSErrorDialog.show(new Throwable("Cannot find searchTextKey for " + searchTypeKey) );
100         }
101         
102         this.searchIdKey = lookupMetadata.getSearchParamIdKey();
103         this.resultIdKey = lookupMetadata.getResultReturnKey();
104         this.resultDisplayKey = lookupMetadata.getResultDisplayKey();
105         this.resultSortKey = lookupMetadata.getResultSortKey();
106         this.sortDirection = lookupMetadata.getSortDirection();
107     }
108 
109     public void setAdditionalSearchParams(List<SearchParamInfo> params){
110         additionalParams = params;
111     }
112     
113     private Callback wrappedCallback = new Callback() {
114 
115         public void onSuggestionsReady(Request request, Response response) {
116           if (textWidget.getText().equals(request.getQuery())) {
117             currentCallback.onSuggestionsReady(request, response);
118             pendingRequest = null;
119             pendingCallback = null;
120           }
121           currentCallback = null;
122           if (pendingCallback != null) {
123             requestSuggestions(pendingRequest, pendingCallback);
124             pendingRequest = null;
125             pendingCallback = null;
126           }
127         }
128 
129     };
130     
131     @Override
132     public void requestSuggestions(Request request, Callback callback) {
133         // Check if the request query is smaller than the minimum size allowed
134         String query = request.getQuery().trim();
135         int minQuerySize = 0;
136         
137         //[KSCOR-225] LO's currently use the depricated constructor that does not pass in the 
138         // lookupMetaData so we need to do a null check until that is fixed
139         if (lookupMetaData != null && lookupMetaData.getMinQuerySize() != null){
140             minQuerySize = lookupMetaData.getMinQuerySize().intValue();
141         }
142         if ((currentCallback == null) && (query.length() >= minQuerySize)){
143           final int x = ((Widget)this.textWidget).getAbsoluteLeft() + ((Widget)this.textWidget).getOffsetWidth();
144   		  final int y = ((Widget)this.textWidget).getAbsoluteTop() + ((Widget)this.textWidget).getOffsetHeight();
145   		  loading.setPopupPositionAndShow(new PositionCallback(){
146 
147   				@Override
148   				public void setPosition(int offsetWidth, int offsetHeight) {
149   					loading.setPopupPosition(x - offsetWidth, y + 1);
150   				}
151   			});
152           currentCallback = callback;
153           sendRequest(request, wrappedCallback);
154         } else {
155           pendingRequest = request;
156           pendingCallback = callback;
157         }        
158     }
159     
160     private SearchRequestInfo buildSearchRequest(String query, String searchId) {
161     	SearchRequestInfo sr = new SearchRequestInfo();
162     	sr.setNeededTotalResults(false);
163     	sr.setSearchKey(this.searchTypeKey);
164     	sr.setSortColumn(this.resultSortKey);
165         sr.setSortDirection(this.sortDirection);
166 
167 		List<SearchParamInfo> searchParams = new ArrayList<SearchParamInfo>();
168 		SearchParamInfo param1 = createParam(this.searchTextKey, query);
169 		searchParams.add(param1);
170 		
171     	sr.setParams(searchParams);
172     	
173     	sr.getParams().addAll(additionalParams);
174 
175         return sr;
176     }
177     
178     private SearchRequestInfo buildSearchRequestById(String query, String searchId) {
179     	SearchRequestInfo sr = new SearchRequestInfo();
180     	sr.setNeededTotalResults(false);
181     	sr.setSearchKey(this.searchTypeKey);
182     	sr.setSortColumn(this.resultSortKey);
183         sr.setSortDirection(this.sortDirection);
184 
185 		List<SearchParamInfo> searchParams = new ArrayList<SearchParamInfo>();
186 		SearchParamInfo param2 = createParam(this.searchIdKey, searchId);
187 		searchParams.add(param2);
188 		
189     	sr.setParams(searchParams);
190     	
191     	sr.getParams().addAll(additionalParams);
192 
193         return sr;
194     }
195     
196     private SearchParamInfo createParam(String key, String value) {
197     	SearchParamInfo param = new SearchParamInfo();
198     	
199     	if(key == null) {
200 			param.setKey("");
201 		} else {
202 			param.setKey(key);
203 		}
204 
205     	if(value == null) {
206 			param.getValues().add("");
207 		} else {
208 			param.getValues().add(value);
209 		}
210     	
211     	return param;
212     }
213 
214     public void sendRequest(final Request request, final Callback callback){
215         String query = request.getQuery().trim();
216         SearchRequestInfo searchRequest = buildSearchRequest(query, null);
217         
218         //case-sensitive?
219         if(query.length() > 0){
220         	searchService.search(searchRequest, new KSAsyncCallback<SearchResultInfo>(){
221     
222                 @Override
223                 public void onSuccess(SearchResultInfo results) {
224                     lastSuggestions = createSuggestions(results, request.getLimit());
225                     Response response = new Response(lastSuggestions);
226                     loading.hide();
227                     callback.onSuggestionsReady(request, response);
228                     if (searchCompletedCallbacks != null &&
229                             lastSuggestions != null && lastSuggestions.size() == 1) {
230                         //Code Changed for JIRA-9075 - SONAR Critical issues - Use get(0) with caution - 5
231                         int firstSuggestion = 0;
232                         for (org.kuali.student.common.ui.client.mvc.Callback<IdableSuggestion> callback : searchCompletedCallbacks) {
233                             callback.exec(lastSuggestions.get(firstSuggestion));
234                         }
235                     }
236                 }
237                 
238                 @Override
239                 public void onFailure(Throwable caught) {
240                 	loading.hide();
241                 	super.onFailure(caught);
242                 }
243                 
244                 private List<IdableSuggestion> createSuggestions(SearchResultInfo results, int limit){
245                     List<IdableSuggestion> suggestionsList = new ArrayList<IdableSuggestion>();
246                     String query = request.getQuery();
247                     query = query.trim();
248                     int count = 0;
249                     if(results != null){
250                         for (SearchResultRowInfo r: results.getRows()){
251                             if(count == limit){
252                                 break;
253                             }
254 
255                             IdableSuggestion theSuggestion = new IdableSuggestion();
256                             for(SearchResultCellInfo c: r.getCells()){
257                                 if(c.getKey().equals(resultDisplayKey)){
258                                     String itemText = c.getValue();
259                                     theSuggestion.addAttr(c.getKey(), c.getValue());
260                                     int index = (" " + itemText).toLowerCase().indexOf(" " + query.toLowerCase().trim());
261                                     
262                                     if (index < 0) {
263                                         //temporary fix to stop index out of bound exception in hosted mode
264 //                                        continue;
265                                         //Including fuzzy search results in the display list.
266                                         theSuggestion.setDisplayString(itemText);
267                                         theSuggestion.setReplacementString(itemText);
268                                     	//FIXME handle case when search for text is not appearing within search result - should not happen (misconfiguration)
269                                         continue;
270                                     }
271                                     
272                                     String htmlString = itemText.substring(0,index) + "<b>" + itemText.substring(index, index + query.length()) + "</b>" + itemText.substring(index + query.length(), itemText.length());
273                                     theSuggestion.setDisplayString(htmlString);
274                                     theSuggestion.setReplacementString(itemText);
275                                     if (c.getKey().equals(resultIdKey)){
276                                         theSuggestion.setId(c.getValue());
277                                     }
278                                 } else if(c.getKey().equals(resultIdKey)){
279                                      theSuggestion.setId(c.getValue());
280                                      theSuggestion.addAttr(c.getKey(), c.getValue());
281                                 } else{
282                                     theSuggestion.addAttr(c.getKey(), c.getValue());
283                                 }
284                             }
285                             suggestionsList.add(theSuggestion);
286                             count++;
287                         }
288                     }
289                     return suggestionsList;
290                 }
291             });
292         }
293     }
294 
295     @Override
296     public boolean isDisplayStringHTML() {
297         return true;
298     }
299 
300 /*    
301     public IdableSuggestion getSuggestionById(String id) {
302         IdableSuggestion suggestion = null;
303         if(!(lastSuggestions.isEmpty())){
304             for(IdableSuggestion is: lastSuggestions){
305                 if(is.getId().equals(id)){
306                     suggestion = is;
307                     break;
308                 }
309             }
310         }
311         if(suggestion == null){
312             searchOnId(id);
313         }
314         return suggestion;
315     }*/
316 
317     @Override
318     public IdableSuggestion getSuggestionByText(String text){
319         IdableSuggestion suggestion = null;
320         for(IdableSuggestion is: lastSuggestions){
321             if(is.getReplacementString().trim().equalsIgnoreCase(text.trim())){
322                 suggestion = is;
323                 break;
324             }
325         }
326         return suggestion;
327     }
328     
329     public void setTextWidget(HasText widget){
330         textWidget = widget;
331     }
332 
333     @Override
334     public void getSuggestionByIdSearch(String id, final org.kuali.student.common.ui.client.mvc.Callback<IdableSuggestion> callback) {
335         SearchRequestInfo searchRequest = buildSearchRequestById(null, id);
336         cachingSearchService.search(searchRequest, new KSAsyncCallback<SearchResultInfo>(){
337             @Override
338             public void onSuccess(SearchResultInfo results) {
339                 IdableSuggestion theSuggestion = null;
340                 if(results != null && !results.getRows().isEmpty()){
341                 	SearchResultRowInfo r = results.getRows().get(0);
342                     theSuggestion = new IdableSuggestion();
343                     for(SearchResultCellInfo c: r.getCells()){
344                         if(c.getKey().equals(resultDisplayKey)){
345                             String itemText = c.getValue();
346                             theSuggestion.addAttr(c.getKey(), c.getValue());
347                             theSuggestion.setDisplayString(itemText);
348                             theSuggestion.setReplacementString(itemText);
349                         } else if(c.getKey().equals(resultIdKey)){
350                              theSuggestion.setId(c.getValue());
351                              theSuggestion.addAttr(c.getKey(), c.getValue());
352                         } else {
353                             theSuggestion.addAttr(c.getKey(), c.getValue());
354                         }
355                     }
356                 }
357                 callback.exec(theSuggestion);
358             }
359         });
360     }
361 
362     @Override
363     public void addSearchCompletedCallback(org.kuali.student.common.ui.client.mvc.Callback<IdableSuggestion> callback) {
364         searchCompletedCallbacks.add(callback);
365     }
366     
367 }