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.search;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.kuali.student.common.assembly.data.Data.DataType;
22  import org.kuali.student.common.assembly.data.LookupResultMetadata;
23  import org.kuali.student.common.search.dto.SearchRequest;
24  import org.kuali.student.common.search.dto.SearchResult;
25  import org.kuali.student.common.search.dto.SearchResultCell;
26  import org.kuali.student.common.search.dto.SearchResultRow;
27  import org.kuali.student.common.ui.client.application.Application;
28  import org.kuali.student.common.ui.client.application.KSAsyncCallback;
29  import org.kuali.student.common.ui.client.mvc.Callback;
30  import org.kuali.student.common.ui.client.service.SearchRpcServiceAsync;
31  import org.kuali.student.common.ui.client.service.SearchServiceFactory;
32  import org.kuali.student.common.ui.client.widgets.KSButton;
33  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
34  import org.kuali.student.common.ui.client.widgets.KSLabel;
35  import org.kuali.student.common.ui.client.widgets.field.layout.layouts.FieldLayoutComponent;
36  import org.kuali.student.common.ui.client.widgets.layout.VerticalFlowPanel;
37  import org.kuali.student.common.ui.client.widgets.searchtable.ResultRow;
38  import org.kuali.student.common.ui.client.widgets.table.scroll.Column;
39  import org.kuali.student.common.ui.client.widgets.table.scroll.DefaultTableModel;
40  import org.kuali.student.common.ui.client.widgets.table.scroll.RetrieveAdditionalDataHandler;
41  import org.kuali.student.common.ui.client.widgets.table.scroll.Row;
42  import org.kuali.student.common.ui.client.widgets.table.scroll.RowComparator;
43  import org.kuali.student.common.ui.client.widgets.table.scroll.Table;
44  
45  import com.google.gwt.core.client.GWT;
46  import com.google.gwt.user.client.Window;
47  import com.google.gwt.user.client.ui.Composite;
48  import com.google.gwt.user.client.ui.VerticalPanel;
49  
50  public class SearchResultsTable extends Composite{
51  
52      protected final int PAGE_SIZE = 10;
53      
54      protected SearchRpcServiceAsync searchRpcServiceAsync = SearchServiceFactory.getSearchService();
55      
56      protected VerticalPanel layout = new VerticalPanel();
57      
58      private DefaultTableModel tableModel;
59      protected String resultIdColumnKey;
60      protected String resultDisplayKey;  
61      protected SearchRequest searchRequest;
62      private Table table = new Table();
63      protected boolean isMultiSelect = true;
64      protected boolean withMslable = true;
65      protected KSButton mslabel = new KSButton("Modify your search?", ButtonStyle.DEFAULT_ANCHOR);
66      
67      protected List<Callback<List<SelectedResults>>> selectedCompleteCallbacks = new ArrayList<Callback<List<SelectedResults>>>();
68  	
69      public KSButton getMslabel() {
70  		return mslabel;
71  	}
72  
73  	public void setMslabel(KSButton mslabel) {
74  		this.mslabel = mslabel;
75  	}
76  
77      public void removeContent() {
78          table.removeContent();
79  	}
80  	public SearchResultsTable(){
81          super();
82          redraw();
83          layout.setWidth("100%");
84          initWidget(layout);
85      }
86      
87      public void redraw(){
88          layout.clear();      
89      }
90      
91      public void setMutipleSelect(boolean isMultiSelect){
92      	this.isMultiSelect = isMultiSelect;
93      }
94   
95  	public void setWithMslable(boolean withMslable) {
96  		this.withMslable = withMslable;
97  	}
98  
99  
100 	
101 	public void initializeTable(List<LookupResultMetadata> listResultMetadata, String resultIdKey, String resultDisplayKey){
102 			 initializeTable("", listResultMetadata, resultIdKey, resultDisplayKey);
103 	}
104 	    
105 	//FIXME do we really need to recreate the table for every refresh?	
106 	public void initializeTable(String searchId, List<LookupResultMetadata> listResultMetadata, String resultIdKey, String resultDisplayKey){ 
107 	    	
108     	//creating a new table because stale data was corrupting new searches
109     	table = new Table();
110     	table.removeAllRows();
111         this.resultIdColumnKey = resultIdKey;
112         this.resultDisplayKey = resultDisplayKey;
113         
114         tableModel = new DefaultTableModel();
115         tableModel.setMultipleSelectable(isMultiSelect);
116 
117         //create table heading
118         for (LookupResultMetadata r: listResultMetadata){
119             if(!r.isHidden()){
120                 Column col1 = new Column();
121                 col1.setId(r.getKey());                
122                 String header = "";                
123                 // KSLAB2571 KSCM1326 - adds SerachID to message override hierarchy
124                 if (Application.getApplicationContext().getMessage(searchId + ":"+ r.getKey() + FieldLayoutComponent.NAME_MESSAGE_KEY) != null) {
125                     header = Application.getApplicationContext().getMessage(searchId + ":"+ r.getKey() + FieldLayoutComponent.NAME_MESSAGE_KEY);
126                 } else if (Application.getApplicationContext().getMessage(r.getKey() + FieldLayoutComponent.NAME_MESSAGE_KEY) != null) {
127                     header = Application.getApplicationContext().getMessage(r.getKey() + FieldLayoutComponent.NAME_MESSAGE_KEY);
128                 } else {
129                     header = Application.getApplicationContext().getUILabel("", null, null, r.getName());
130                 }     
131                 
132                 col1.setName(header);
133                 col1.setId(r.getKey());
134                 col1.setWidth("100px");                    
135                 col1.setAscendingRowComparator(new FieldAscendingRowComparator(r.getKey(), r.getDataType()));
136                 col1.setDescendingRowComparator(new FieldDescendingRowComparator(r.getKey(), r.getDataType()));                
137                 
138                 tableModel.addColumn(col1);
139             }
140         }      
141                      
142      // TODO - there's a better way to do this
143         if (this.searchRequest.getSearchKey().toLowerCase().contains("cross")) {
144         	tableModel.setMoreData(false);
145         }
146         if(isMultiSelect){
147         	tableModel.installCheckBoxRowHeaderColumn();
148         }
149         
150         table.getScrollPanel().setHeight("300px");
151         table.setTableModel(tableModel);
152         
153         table.addRetrieveAdditionalDataHandler(new RetrieveAdditionalDataHandler(){
154 			@Override
155 			public void onAdditionalDataRequest() {
156 				 performOnDemandSearch(tableModel.getRowCount(), PAGE_SIZE);
157                  //tableModel.fireTableDataChanged();
158 			}
159 		});
160 
161         redraw();
162         layout.add(table);
163   }   
164     
165     public void performSearch(SearchRequest searchRequest, List<LookupResultMetadata> listResultMetadata, String resultIdKey, String resultDisplayKey, boolean pagedResults) {
166         this.searchRequest = searchRequest;
167         initializeTable(listResultMetadata, resultIdKey, resultDisplayKey);
168         if (this.searchRequest.getSearchKey().toLowerCase().contains("cross")) {
169             //FIXME Do we still need this if condition?
170             // Added an else to the if(pagedResults) line to prevent searches being executed
171             // twice if the search name includes cross
172             performOnDemandSearch(0, 0);
173         }
174         else if(pagedResults){
175         	performOnDemandSearch(0, PAGE_SIZE);
176         }
177         else{
178         	performOnDemandSearch(0, 0);
179         }
180     }
181     
182     // KSLAB2571 KSCM1326 - Overloaded method to add SerachID to message override hierarchy 
183     public void performSearch(String searchId, SearchRequest searchRequest, List<LookupResultMetadata> listResultMetadata, String resultIdKey, String resultDisplayKey, boolean pagedResults) {
184         this.searchRequest = searchRequest;
185         initializeTable(searchId, listResultMetadata, resultIdKey, resultDisplayKey);
186         if (this.searchRequest.getSearchKey().toLowerCase().contains("cross")) {
187             //FIXME Do we still need this if condition?
188             // Added an else to the if(pagedResults) line to prevent searches being executed
189             // twice if the search name includes cross
190             performOnDemandSearch(0, 0);
191         }
192         else if(pagedResults){
193         	performOnDemandSearch(0, PAGE_SIZE);
194         }
195         else{
196         	performOnDemandSearch(0, 0);
197         }
198     }
199     
200     public void performSearch(SearchRequest searchRequest, List<LookupResultMetadata> listResultMetadata, String resultIdKey, boolean pagedResults){
201         this.performSearch(searchRequest, listResultMetadata, resultIdKey, null, true);
202     }    
203     
204     public void performSearch(SearchRequest searchRequest, List<LookupResultMetadata> listResultMetadata, String resultIdKey){
205         this.performSearch(searchRequest, listResultMetadata, resultIdKey, true);
206     }    
207     
208     protected void performOnDemandSearch(int startAt, int size) {
209                 
210     	table.displayLoading(true);
211         searchRequest.setStartAt(startAt);
212         if (size != 0) {
213         	searchRequest.setNeededTotalResults(false);
214         	searchRequest.setMaxResults(size);
215         } else {
216         	searchRequest.setNeededTotalResults(true);
217         }
218 
219         searchRpcServiceAsync.search(searchRequest, new KSAsyncCallback<SearchResult>(){
220 
221             @Override
222             public void handleFailure(Throwable cause) {
223                 GWT.log("Failed to perform search", cause); //FIXME more detail info here
224                 Window.alert("Failed to perform search");
225                 table.displayLoading(false);
226             }
227 
228             @Override
229             public void onSuccess(SearchResult results) {
230             	table.addContent();
231             	
232                 if(results != null && results.getRows() != null && results.getRows().size() != 0){
233                     for (SearchResultRow r: results.getRows()){
234                         ResultRow theRow = new ResultRow();
235                         for(SearchResultCell c: r.getCells()){
236                             if(c.getKey().equals(resultIdColumnKey)){
237                                 theRow.setId(c.getValue());
238                             }
239                             theRow.setValue(c.getKey(), c.getValue());
240                         }
241                        tableModel.addRow(new SearchResultsRow(theRow));
242                     }
243                 } else {
244                 	tableModel.setMoreData(false);
245                 	
246                 	//add no matches found if no search results
247                 	if(searchRequest.getStartAt() == 0){
248 	                	table.removeContent();
249 	                	VerticalFlowPanel noResultsPanel = new VerticalFlowPanel();
250 	                	noResultsPanel.add(new KSLabel("No matches found"));
251 	                	if(withMslable) noResultsPanel.add(mslabel);
252 	                	noResultsPanel.addStyleName("ks-no-results-message");
253 	                	table.getScrollPanel().add(noResultsPanel);
254                 	}
255                 }
256 //                tableModel.selectFirstRow();
257                 tableModel.fireTableDataChanged();
258                 table.displayLoading(false);
259             }
260         });
261     }
262     
263     public List<ResultRow> getSelectedRows(){
264         List<ResultRow> rows = new ArrayList<ResultRow>();
265         for(Row row : tableModel.getSelectedRows()){
266             rows.add(((SearchResultsRow)row).getResultRow());
267         }
268         return rows;
269     }
270 
271     public List<String> getSelectedIds(){
272         List<String> ids = new ArrayList<String>();
273         for(Row row : tableModel.getSelectedRows()){
274             ids.add(((SearchResultsRow)row).getResultRow().getId());
275         }                
276         return ids;
277     }
278     
279     public void addSelectionCompleteCallback(Callback<List<SelectedResults>> callback){
280         selectedCompleteCallbacks.add(callback);
281     }
282 }
283 
284 class SearchResultsRow extends Row {
285 
286     ResultRow row;
287 
288     public SearchResultsRow(ResultRow row) {
289         this.row = row;
290     }
291 
292     @Override
293     public Object getCellData(String columnId) {
294         return row.getValue(columnId);
295     }
296 
297     @Override
298     public void setCellData(String columnId, Object newValue) {
299         row.setValue(columnId, newValue.toString());
300     }
301 
302     @Override
303     public String toString() {
304         return row.toString();
305     }
306 
307     public ResultRow getResultRow() {
308         return row;
309     }
310 }
311 
312 class FieldAscendingRowComparator extends RowComparator{
313     
314     String columnId;
315     DataType type;
316     
317     FieldAscendingRowComparator(String columnId, DataType type) {
318         this.columnId = columnId;
319         this.type = type;
320     }
321     
322     @Override
323     public int compare(Row row0, Row row1) {
324         String id0, id1;
325         
326         if (type.equals(DataType.STRING)) {
327             id0 = (String)row0.getCellData(columnId);
328             id1 = (String)row1.getCellData(columnId);
329         } else {
330             id0 = (String)row0.getCellData(columnId);
331             id1 = (String)row1.getCellData(columnId);            
332         }
333         return id0.compareTo(id1);
334     }    
335 }
336 
337 class FieldDescendingRowComparator extends RowComparator{
338     
339     String columnId;
340     DataType type;    
341     
342     FieldDescendingRowComparator(String columnId, DataType type) {
343         this.columnId = columnId;
344         this.type = type;        
345     }    
346     
347     @Override
348     public int compare(Row row0, Row row1) {
349         String id0, id1;
350         
351         if (type.equals(DataType.STRING)) {
352             id0 = (String)row0.getCellData(columnId);
353             id1 = (String)row1.getCellData(columnId);
354         } else {
355             id0 = (String)row0.getCellData(columnId);
356             id1 = (String)row1.getCellData(columnId);            
357         }
358         return id1.compareTo(id0);
359     }    
360 }