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