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