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.HashMap;
20  import java.util.LinkedHashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.kuali.student.common.assembly.data.LookupMetadata;
25  import org.kuali.student.common.assembly.data.LookupParamMetadata;
26  import org.kuali.student.common.assembly.data.Metadata;
27  import org.kuali.student.common.assembly.data.LookupMetadata.Usage;
28  import org.kuali.student.common.assembly.data.Metadata.WriteAccess;
29  import org.kuali.student.common.search.dto.SearchParam;
30  import org.kuali.student.common.search.dto.SearchRequest;
31  import org.kuali.student.common.ui.client.application.Application;
32  import org.kuali.student.common.ui.client.configurable.mvc.DefaultWidgetFactory;
33  import org.kuali.student.common.ui.client.mvc.Callback;
34  import org.kuali.student.common.ui.client.util.UtilConstants;
35  import org.kuali.student.common.ui.client.widgets.KSButton;
36  import org.kuali.student.common.ui.client.widgets.KSDropDown;
37  import org.kuali.student.common.ui.client.widgets.KSLabel;
38  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
39  import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
40  import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations.ButtonEnum;
41  import org.kuali.student.common.ui.client.widgets.field.layout.button.ActionCancelGroup;
42  import org.kuali.student.common.ui.client.widgets.field.layout.button.ButtonGroup;
43  import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
44  import org.kuali.student.common.ui.client.widgets.layout.HorizontalBlockFlowPanel;
45  import org.kuali.student.common.ui.client.widgets.layout.VerticalFlowPanel;
46  import org.kuali.student.common.ui.client.widgets.list.KSSelectItemWidgetAbstract;
47  import org.kuali.student.common.ui.client.widgets.list.ListItems;
48  import org.kuali.student.common.ui.client.widgets.list.SelectionChangeEvent;
49  import org.kuali.student.common.ui.client.widgets.list.SelectionChangeHandler;
50  import org.kuali.student.common.ui.client.widgets.searchtable.ResultRow;
51  
52  import com.google.gwt.core.client.GWT;
53  import com.google.gwt.event.dom.client.ClickEvent;
54  import com.google.gwt.event.dom.client.ClickHandler;
55  import com.google.gwt.event.dom.client.KeyCodes;
56  import com.google.gwt.event.dom.client.KeyDownEvent;
57  import com.google.gwt.event.dom.client.KeyDownHandler;
58  import com.google.gwt.event.shared.HandlerRegistration;
59  import com.google.gwt.user.client.Window;
60  import com.google.gwt.user.client.ui.Composite;
61  import com.google.gwt.user.client.ui.HTMLPanel;
62  import com.google.gwt.user.client.ui.HasText;
63  import com.google.gwt.user.client.ui.HasValue;
64  import com.google.gwt.user.client.ui.SimplePanel;
65  import com.google.gwt.user.client.ui.SuggestBox;
66  import com.google.gwt.user.client.ui.VerticalPanel;
67  import com.google.gwt.user.client.ui.Widget;
68  
69  public class SearchPanel extends Composite{
70  
71      //Layout configuration
72      private VerticalFlowPanel layout = new VerticalFlowPanel();
73      private SimplePanel searchSelectorPanel = new SimplePanel();
74      private VerticalFlowPanel resultsTablePanel = new VerticalFlowPanel();
75      private HorizontalBlockFlowPanel enteredCriteriaString = new HorizontalBlockFlowPanel();
76      private CollapsablePanel modifySearchPanel;
77      private String criteriaInstructions = getMessage("searchPanelEnterFields");
78      private KSLabel enteredCriteriaHeading = new KSLabel(getMessage("searchPanelCriteria"));
79      private SearchResultsTable table;
80      private boolean isMultiSelect = true;
81  
82  	public static enum SearchStyle{ADVANCED, CUSTOM}; 
83      private ActionCancelGroup actionCancelButtons;
84  
85      private String actionLabel = getMessage("search");  //set default action label
86      private boolean resultsSelected = false;
87      private boolean hasSearchParams = false;			//indicates if there are any user supplied search parameters
88      													//if false will auto default search and not display modify search link.
89      
90  	//Search data
91      private List<LookupMetadata> lookups = new ArrayList<LookupMetadata>();
92      private boolean multiSelect = false;
93      private boolean resultsShown = false;    
94      private SearchParametersWidget activeSearchParametersWidget = null;
95      // uses "name" of the lookup metadata to lookup the widget that layouts the search UI
96      private Map<String, SearchParametersWidget> searchParameterWidgetMap = new HashMap<String, SearchParametersWidget>();
97      private List<SearchField> searchFields = new ArrayList<SearchField>();
98      private List<Callback<LookupMetadata>> lookupChangedCallbacks = new ArrayList<Callback<LookupMetadata>>();    
99      private String selectedLookupName;
100     private List<Callback<List<SelectedResults>>> selectedCompleteCallbacks = new ArrayList<Callback<List<SelectedResults>>>();  
101     private List<Callback<Boolean>> actionCompletedCallbacks = new ArrayList<Callback<Boolean>>();    
102 
103 	private Callback<ButtonEnumerations.ButtonEnum> actionCancelCallback = new Callback<ButtonEnumerations.ButtonEnum>(){
104 
105 		@Override
106         public void exec(ButtonEnum result) {
107             if (result == ButtonEnumerations.SearchCancelEnum.SEARCH) {
108             	table.getContentTable().removeContent();
109                 getActionCompleteCallback().exec(true);                                 
110             }
111        }
112 	};
113    
114     interface SearchParametersWidget {
115         public SearchRequest getSearchRequest();
116         public LookupMetadata getLookupMetadata();
117         public List<HasSearchParam> getSearchParams();
118     }
119 
120     public SearchPanel(LookupMetadata meta){
121         lookups.add(meta);
122         this.initWidget(layout);
123     }
124 
125     public SearchPanel(List<LookupMetadata> metas){
126         lookups = metas;       
127         this.initWidget(layout);
128     }
129 
130     @SuppressWarnings("unchecked")
131 	public ButtonGroup getButtons(){
132     	return actionCancelButtons;
133     }
134     
135     public void setMutipleSelect(boolean isMultiSelect){
136     	this.isMultiSelect = isMultiSelect;
137     }
138     
139     public void setupButtons() {
140         if (actionCancelButtons != null) {
141             actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
142             actionCancelButtons.addCallback(actionCancelCallback);            
143         }    	
144     }
145     
146     public void setupSearch() {                
147         resultsTablePanel.clear();
148         layout.clear();
149         resultsShown = false;
150     	hasSearchParams = false;
151 
152         //create search panel
153         Widget searchParamPanel;        
154         if (lookups.size() == 1) {
155             searchParamPanel = createSearchParamPanel(lookups.get(0));
156             selectedLookupName = lookups.get(0).getName();
157             activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookupName);
158         } else {
159             LinkedHashMap<String, Widget> searches = new LinkedHashMap<String, Widget>();
160             LinkedHashMap<String, LookupMetadata> searchLookups = new LinkedHashMap<String, LookupMetadata>();
161             for(LookupMetadata lookup: lookups){
162                 searches.put(lookup.getName(), createSearchParamPanel(lookup));
163                 searchLookups.put(lookup.getName(), lookup);
164             }
165             selectedLookupName = lookups.get(0).getName();
166             // Sets the activeSearchParametersWidget to be the first search
167             activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookupName);
168             String actionLabel = (lookups.get(0) == null)? null : lookups.get(0)
169                     .getWidgetOptionValue(LookupMetadata.WidgetOption.ADVANCED_LIGHTBOX_ACTION_LABEL);
170             setActionLabel(actionLabel);
171             searchParamPanel = new SwappablePanel(searches);
172             ((SwappablePanel)searchParamPanel).setSearchLookups(searchLookups);
173             ((SwappablePanel)searchParamPanel).addLookupChangedCallback(new Callback<LookupMetadata>() {
174                 @Override
175                 public void exec(LookupMetadata selectedLookup) {
176                     activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookup.getName());
177                     selectedLookupName = selectedLookup.getName();
178                     if (lookupChangedCallbacks != null) {
179                         for (Callback<LookupMetadata> callback : lookupChangedCallbacks) {
180                             callback.exec(selectedLookup);
181                         }
182                     }
183                 }
184             });
185         }
186         searchSelectorPanel.setWidget(searchParamPanel);
187         layout.add(searchSelectorPanel);
188         
189         //Create layout for results screen
190         
191         //Entered criteria section
192         if (hasSearchParams){
193 	        enteredCriteriaHeading.addStyleName("ks-form-module-single-line-margin");
194 	        enteredCriteriaHeading.addStyleName("KS-Advanced-Search-Search-Criteria-Label");
195 	        resultsTablePanel.add(enteredCriteriaHeading);
196 	        resultsTablePanel.add(enteredCriteriaString);
197 	        resultsTablePanel.setVisible(false);
198         }
199         
200         //Search Results table
201         table = new SearchResultsTable();
202         table.setMutipleSelect(isMultiSelect);
203         table.addStyleName("KS-Advanced-Search-Results-Table");
204         resultsTablePanel.add(table);
205         layout.add(resultsTablePanel); 
206         
207         table.getMslabel().addClickHandler(new ClickHandler(){
208 
209 			@Override
210 			public void onClick(ClickEvent event) {
211 				if(modifySearchPanel.isOpen()){
212 					modifySearchPanel.close();
213 				}
214 				else{
215 					modifySearchPanel.open();
216 				}
217 				
218 				resultsTablePanel.setVisible(false);
219 				resultsSelected = false;
220 				actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
221 			}
222 		});
223         
224         resultsSelected = false;
225         actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
226         
227         //Execute the search and display the results of there are no user supplied search parameters defined
228         if (!hasSearchParams){
229         	getActionCompleteCallback().exec(true);
230         }
231     }
232 
233     private Widget createSearchParamPanel(LookupMetadata meta){
234         ParamListItems listItems = new ParamListItems(meta);
235         final AdvancedSearch advancedSearch = new AdvancedSearch(meta);
236         LinkPanel panel = new LinkPanel(SearchStyle.ADVANCED, advancedSearch);
237         searchParameterWidgetMap.put(meta.getName(), advancedSearch);
238 
239         //check whether we need custom tab i.e. whether we have at least one parameter that should appear on custom tab
240         for(LookupParamMetadata metaParam: meta.getParams()){
241         	if (metaParam.getUsage() != null && metaParam.getUsage() != Usage.DEFAULT){
242         		hasSearchParams = true;   //Only set to true if this only has user supplied params (i.e. params without default values)
243         	}
244         	if ((metaParam.getUsage() == Usage.CUSTOM) || (metaParam.getUsage() == Usage.ADVANCED_CUSTOM)) {
245             	
246                 final CustomizedSearch customizedSearch = new CustomizedSearch(meta, listItems);
247                 KSButton button = panel.addLinkToPanel(SearchStyle.ADVANCED, getMessage("searchPanelCustomizeSearch"), SearchStyle.CUSTOM);
248                 button.addClickHandler(new ClickHandler(){
249 
250                     @Override
251                     public void onClick(ClickEvent event) {
252                         resultsTablePanel.setVisible(false);
253                         activeSearchParametersWidget = customizedSearch;
254                     }});
255                 button.addStyleName("KS-Advanced-Search-Link");
256                 button.getParent().addStyleName("clearfix");
257                 panel.addPanel(SearchStyle.CUSTOM, customizedSearch);
258                 button = panel.addLinkToPanel(SearchStyle.CUSTOM, getMessage("searchPanelReturnToAdvancedSearch"), SearchStyle.ADVANCED);
259                 button.addClickHandler(new ClickHandler(){
260 
261                     @Override
262                     public void onClick(ClickEvent event) {
263                         resultsTablePanel.setVisible(false);
264                         activeSearchParametersWidget = advancedSearch;
265                     }});
266                 button.addStyleName("KS-Advanced-Search-Link");
267                 button.getParent().addStyleName("clearfix");
268                 break;
269             }
270         }
271 
272         return panel;
273     }
274 
275     private class CustomizedSearch extends Composite implements SearchParametersWidget {
276 
277         private List<CustomLine> lines = new ArrayList<CustomLine>();
278         private List<HasSearchParam> searchParams = new ArrayList<HasSearchParam>();
279         private VerticalPanel layout = new VerticalPanel();
280         private VerticalPanel linePanel = new VerticalPanel();
281         private LookupMetadata meta;
282 
283         public CustomizedSearch(final LookupMetadata meta, final ParamListItems listItems){
284 
285             KSLabel instrLabel = new KSLabel(criteriaInstructions);
286             layout.add(instrLabel);
287 
288             layout.add(linePanel);
289             CustomLine line = new CustomLine(meta, listItems);
290             line.addStyleName("ks-form-module-single-line-margin");
291             linePanel.add(line);
292             lines.add(line);
293             searchParams.add(line);
294             this.meta = meta;
295 
296             KSButton addCriteria = new KSButton(getMessage("searchPanelAddCriteria"), ButtonStyle.SECONDARY);
297             addCriteria.addClickHandler(new ClickHandler(){
298 
299                 @Override
300                 public void onClick(ClickEvent event) {
301                     CustomLine line = new CustomLine(meta, listItems);
302                     line.addStyleName("ks-form-module-single-line-margin");
303                     linePanel.add(line);
304                     lines.add(line);
305                     searchParams.add(line);
306                 }
307             });
308 
309             addCriteria.addStyleName("ks-form-module-single-line-margin");
310             layout.add(addCriteria);    
311             
312             this.initWidget(layout);
313         }
314         
315         public LookupMetadata getLookupMetadata() {
316             return meta;
317         }
318 
319         @Override
320         public SearchRequest getSearchRequest() {
321             //Create search request and then pass it to the table
322             //TODO pass search to the table
323             SearchRequest sr = new SearchRequest();
324             List<SearchParam> params = new ArrayList<SearchParam>();
325             for(CustomLine field: lines){
326                 SearchParam param = field.getSearchParam();
327                 //TODO is this check needed here? probably. assuming string here
328                 if((param.getValue() != null)){
329                     params.add(param);
330                 }
331             }
332 
333             //add search criteria widgets to the custom tab
334             for(LookupParamMetadata metaParam: meta.getParams()){
335 
336                 //select only parameters shown on custom search tab
337                 if ((metaParam.getUsage() != Usage.CUSTOM) && (metaParam.getUsage() != Usage.ADVANCED_CUSTOM)) {
338                     continue;
339                 }
340 
341                 if(metaParam.getWriteAccess() == WriteAccess.NEVER){
342                     SearchParam param = new SearchParam();
343                     param.setKey(metaParam.getKey());
344                     if(metaParam.getDefaultValueList()==null){
345                         param.setValue(metaParam.getDefaultValueString());
346                     }else{
347                         param.setValue(metaParam.getDefaultValueList());
348                     }
349                     params.add(param);
350                 }
351                 else if(metaParam.getWriteAccess() == WriteAccess.WHEN_NULL){
352                     if((metaParam.getDefaultValueString() != null && !metaParam.getDefaultValueString().isEmpty())||
353                        (metaParam.getDefaultValueList() != null && !metaParam.getDefaultValueList().isEmpty())){
354                         SearchParam param = new SearchParam();
355                         param.setKey(metaParam.getKey());
356                         if(metaParam.getDefaultValueList()==null){
357                             param.setValue(metaParam.getDefaultValueString());
358                         }else{
359                             param.setValue(metaParam.getDefaultValueList());
360                         }
361                         params.add(param);
362                     }
363                 }
364             }
365 
366             sr.setParams(params);
367             sr.setSearchKey(meta.getSearchTypeId());
368             return sr;
369         }
370 
371         @Override
372         public List<HasSearchParam> getSearchParams() {
373             return searchParams;
374         }
375 
376     }
377 
378     private interface HasSearchParam{
379         public SearchParam getSearchParam();
380         public String getFieldName();
381         public String getSearchText();
382     }
383 
384     private static class CustomLine extends Composite implements HasSearchParam{
385         private KSDropDown paramSelector = new KSDropDown();
386         private SimplePanel widgetPanel = new SimplePanel();
387         private Widget widget = null;
388         private String key;
389         private HorizontalBlockFlowPanel layout = new HorizontalBlockFlowPanel();
390         private ParamListItems listItems;
391 
392         public CustomLine(LookupMetadata meta, final ParamListItems listItems){
393             
394             List<LookupParamMetadata> customParams = new ArrayList<LookupParamMetadata>();
395 
396             for (LookupParamMetadata lookupParamMetadata : listItems.getParams()) {
397                 if (lookupParamMetadata.getWriteAccess() != WriteAccess.NEVER){
398                     if (lookupParamMetadata.getUsage() == Usage.CUSTOM || lookupParamMetadata.getUsage() == Usage.ADVANCED_CUSTOM ) {
399                         customParams.add(lookupParamMetadata);
400                     }                   
401                 } 
402             }
403             
404             ParamListItems customParamList = new ParamListItems(customParams);
405             
406             this.listItems = customParamList;
407             paramSelector.setBlankFirstItem(false);
408             paramSelector.setListItems(customParamList);
409 
410             String id = meta.getParams().get(0).getKey();
411             paramSelector.selectItem(id);
412             widget = listItems.getWidget(id);
413             key = id;
414             widgetPanel.setWidget(widget);
415             paramSelector.addSelectionChangeHandler(new SelectionChangeHandler(){
416 
417                 @Override
418                 public void onSelectionChange(SelectionChangeEvent event) {
419                     String id = ((KSSelectItemWidgetAbstract)event.getWidget()).getSelectedItem();
420                     widget = listItems.getWidget(id);
421                     widgetPanel.setWidget(widget);
422                     key = id;
423 
424                 }
425             });
426             layout.add(paramSelector);
427             layout.add(widgetPanel);
428             this.initWidget(layout);
429         }
430 
431         public SearchParam getSearchParam(){
432             return SearchPanel.getSearchParam(widget, key);
433         }
434 
435         public String getKey(){
436             return key;
437         }
438 
439         public String getFieldName(){
440             String id = paramSelector.getSelectedItem();
441             return listItems.getItemText(id);
442         }
443 
444         @Override
445         public String getSearchText() {
446             return SearchPanel.getSearchText(widget);
447         }
448     }
449 
450     private class AdvancedSearch extends Composite implements SearchParametersWidget {
451         private LookupMetadata meta;
452         private List<HasSearchParam> searchParams = new ArrayList<HasSearchParam>();
453 
454         public AdvancedSearch(final LookupMetadata meta){
455             VerticalPanel panel = new VerticalPanel();
456 
457             KSLabel instrLabel = new KSLabel();
458             panel.add(instrLabel);
459             this.meta = meta;            
460 
461             //add widget for each search criteria to the advanced tab
462             boolean allFieldsRequired = true;
463             for(LookupParamMetadata param: meta.getParams()){
464 
465                 //select only parameters shown on advanced search tab
466                 if ((param.getUsage() != Usage.ADVANCED) && (param.getUsage() != Usage.ADVANCED_CUSTOM)) {
467                     continue;
468                 }
469 
470                 if ((param.getWriteAccess() == WriteAccess.ALWAYS) || (param.getWriteAccess() == WriteAccess.REQUIRED)){
471                     SearchField paramField = new SearchField(param);
472                     searchFields.add(paramField);
473                     panel.add(paramField);
474                     searchParams.add(paramField);
475                 }
476                 else if (param.getWriteAccess() == WriteAccess.WHEN_NULL){
477                     if(param.getDefaultValueString() == null && param.getDefaultValueList() == null){
478                         SearchField paramField = new SearchField(param);
479                         searchFields.add(paramField);
480                         panel.add(paramField);
481                         searchParams.add(paramField); 
482                     }
483                 }
484 
485                 if (param.getWriteAccess() != Metadata.WriteAccess.REQUIRED) {
486                     allFieldsRequired = false;
487                 }
488             }
489 
490             //do not show criteria instructions if we have only one criteria field or in case all fields are required
491             if ((searchFields.size() > 1) || (allFieldsRequired == false)) {
492                 instrLabel.setText(criteriaInstructions);
493             }    
494             this.addKeyDownHandler(downHandler);
495             this.initWidget(panel);
496         }
497         
498         public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
499     	    return addDomHandler(handler, KeyDownEvent.getType());
500     	}
501         
502         public LookupMetadata getLookupMetadata() {
503             return meta;
504         }        
505         
506         private KeyDownHandler downHandler = new KeyDownHandler(){
507 
508     		@Override
509     		public void onKeyDown(KeyDownEvent event) {
510     			if(event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) 	// Enter button
511     				actionCancelCallback.exec(ButtonEnumerations.SearchCancelEnum.SEARCH);
512     		}
513     		
514     	};
515 
516         @Override
517         public SearchRequest getSearchRequest() {
518             SearchRequest sr = new SearchRequest();
519             List<SearchParam> params = new ArrayList<SearchParam>();
520             List<HasSearchParam> searchParams = getSearchParams();
521 
522             //initialize search parameters if user entered values into search criteria fields in UI
523             
524             for(HasSearchParam field: searchParams){
525                 SearchParam param = field.getSearchParam();
526                 //TODO is this null check needed here? probably. assuming string here
527                 //TODO make check more robust here/inserting params more robust
528                 //do not pass to the search parameters that are empty
529                 //FIXME hack - comparison to 'optional' - replace with check against 'optional' field and update related lookup metadata
530                 if ((param.getValue() != null) && ((param.getValue().toString().trim().isEmpty() == false) || (param.getKey().toLowerCase().indexOf("optional") == -1))) {
531                     params.add(param);
532                 }
533             }
534 
535             //initialize search parameters that are hidden from the UI because they are set to default context specific values
536             for(LookupParamMetadata metaParam: meta.getParams()){
537                 if(metaParam.getWriteAccess() == WriteAccess.NEVER){
538                     if ((metaParam.getDefaultValueString() == null || metaParam.getDefaultValueString().isEmpty())&&
539                         (metaParam.getDefaultValueList() == null || metaParam.getDefaultValueList().isEmpty())) {
540                         //FIXME throw an exception?
541                         GWT.log("Key = " + metaParam.getKey() + " has write access NEVER but has no default value!", null);
542                         continue;
543                     }
544                     SearchParam param = new SearchParam();
545                     param.setKey(metaParam.getKey());
546                     if(metaParam.getDefaultValueList()==null){
547                         param.setValue(metaParam.getDefaultValueString());
548                     }else{
549                         param.setValue(metaParam.getDefaultValueList());
550                     }
551                     params.add(param);
552                 }
553                 else if(metaParam.getWriteAccess() == WriteAccess.WHEN_NULL){
554                     if((metaParam.getDefaultValueString() != null && !metaParam.getDefaultValueString().isEmpty())||
555                        (metaParam.getDefaultValueList() != null && !metaParam.getDefaultValueList().isEmpty())){
556                         SearchParam param = new SearchParam();
557                         param.setKey(metaParam.getKey());
558                         if(metaParam.getDefaultValueList()==null){
559                             param.setValue(metaParam.getDefaultValueString());
560                         }else{
561                             param.setValue(metaParam.getDefaultValueList());
562                         }
563                         params.add(param);
564                     }
565                 }
566             }
567             sr.setParams(params);
568             if (meta.getResultSortKey() != null) {
569                 sr.setSortColumn(meta.getResultSortKey());
570             }
571             sr.setSearchKey(meta.getSearchTypeId());
572             return sr;
573         }
574 
575         @Override
576         public List<HasSearchParam> getSearchParams() {
577             return searchParams;
578         }
579     }
580 
581     private class SearchField extends Composite implements HasSearchParam{
582 
583         private Widget widget = null;
584         private LookupParamMetadata meta = null;
585         private VerticalFlowPanel panel = new VerticalFlowPanel();
586         private String fieldName;    	
587 
588         public SearchParam getSearchParam(){
589             return SearchPanel.getSearchParam(widget, meta.getKey());
590         }
591 
592         public SearchField(LookupParamMetadata param){
593             meta = param;
594             //TODO use message call here
595             fieldName = param.getName();
596             widget = DefaultWidgetFactory.getInstance().getWidget(param);
597             if(param.getDefaultValueString() != null){
598                 //TODO Add handling of default value lists here
599                 if(widget instanceof HasText){
600                     ((HasText) widget).setText(param.getDefaultValueString().toString());
601                 }
602                 else if(widget instanceof HasValue){
603                     ((HasValue) widget).setValue(param.getDefaultValueString());
604                 }
605             }
606 
607             //FIXME: remove because required field '*' indication will be part of FieldElement class
608             if (param.getWriteAccess() == Metadata.WriteAccess.REQUIRED) {
609                 fieldName += " *";
610             }            
611 
612             FieldElement fieldElement = new FieldElement(fieldName, widget);
613             fieldElement.getTitleWidget().addStyleName("KS-Picker-Criteria-Text");
614             panel.add(fieldElement);
615             panel.addStyleName("clearfix");
616                                     
617             this.initWidget(panel);
618         }
619 
620         public Widget getFieldPanel(){
621             return panel;
622         }
623 
624         public String getFieldName() {
625             return fieldName;
626         }
627 
628         @Override
629         public String getSearchText() {
630             return SearchPanel.getSearchText(widget);
631         }
632     }
633 
634     private static SearchParam getSearchParam(final Widget widget, String key){
635         SearchParam param = new SearchParam();
636         param.setKey(key);
637         if(widget instanceof HasText){
638             param.setValue(((HasText) widget).getText());
639         }
640         else if(widget instanceof HasValue){
641             Object value = ((HasValue) widget).getValue();
642             if(value != null){
643             //TODO need to handle date and other types here, how they are converted for search, etc
644                 if(value instanceof String){
645                     param.setValue((String)value);
646                 }
647                 else{
648                     param.setValue(value.toString());
649                     GWT.log("Fields in search probably(?) shouldnt have values other than string", null);
650                 }
651             }
652         }
653         else if (widget instanceof KSPicker){
654         	KSPicker pickerWidget = (KSPicker)widget; 
655         	String pickerValue = pickerWidget.getValue().toString();
656         	if (UtilConstants.IMPOSSIBLE_CHARACTERS.equals(pickerValue)){
657         		SuggestBox suggestBox = (SuggestBox)pickerWidget.getInputWidget();
658         		pickerValue = suggestBox.getText();
659         	}
660         	
661             param.setValue(pickerValue);
662         }
663         else {
664             param.setValue("");
665         }
666 
667         return param;
668     }
669     
670     private static String getSearchText(final Widget widget){
671         if(widget instanceof HasText){
672             return ((HasText) widget).getText();
673         }
674         else if(widget instanceof HasValue){
675             Object value = ((HasValue) widget).getValue();
676             if(value != null){
677             //TODO need to handle date and other types here, how they are converted for search, etc
678                 if(value instanceof String){
679                     return (String)value;
680                 }
681                 else{
682                     GWT.log("Fields in search probably(?) shouldnt have values other than string", null);
683                     return value.toString();
684                 }
685             }
686         }
687         else if (widget instanceof KSPicker){
688             return ((KSPicker)widget).getDisplayValue();
689         }
690         return "";
691     }
692 
693     private void showCriteriaChosen(List<HasSearchParam> fields){
694         enteredCriteriaString.clear();
695         boolean first = true;;
696         for(HasSearchParam field: fields){
697             String name = field.getFieldName();
698             String value = field.getSearchText();
699           if(!value.isEmpty()&&value.equals("$$##@@"))
700           	value = field.getSearchParam().getValue().toString().toUpperCase();
701             if(!value.isEmpty()){
702                 HTMLPanel label = new HTMLPanel(name + ": <b>" + value + "</b>&nbsp;");
703                 if (!first) {
704                     label.addStyleName("KS-Advanced-Search-Search-Criteria-Text");
705                 }
706                 enteredCriteriaString.add(label);
707                 first = false;
708             }
709         }
710     }
711 
712     public List<String> getSelectedIds(){
713         List<String> ids = new ArrayList<String>();
714         if(table != null){
715             ids = table.getSelectedIds();
716         }
717         return ids;
718     }
719 
720     public List<SelectedResults> getSelectedValues() {
721 
722         List<SelectedResults> selectedValues = new ArrayList<SelectedResults>();
723         if (table != null) {
724             List<ResultRow> selectedRows = table.getSelectedRows();
725             for (ResultRow row : selectedRows) {
726                 String displayKey = row.getValue(activeSearchParametersWidget.getLookupMetadata().getResultDisplayKey());
727                 String returnKey = row.getValue(activeSearchParametersWidget.getLookupMetadata().getResultReturnKey());
728                 selectedValues.add(new SelectedResults(displayKey, returnKey, row));
729                 if (multiSelect == false) {
730                     break;
731                 }
732             }
733         }
734 
735         return selectedValues;
736     }
737 
738     public boolean isMultiSelect() {
739         return multiSelect;
740     }
741 
742     public void setMultiSelect(boolean multiSelect) {
743         this.multiSelect = multiSelect;
744     }
745 
746     private static class ParamListItems implements ListItems{
747 
748         private List<LookupParamMetadata> params = new ArrayList<LookupParamMetadata>();
749 
750         public ParamListItems(LookupMetadata meta){
751             params = meta.getParams();
752         }
753 
754         public ParamListItems(List<LookupParamMetadata> params){
755             this.params = params;
756         }
757         
758         @Override
759         public List<String> getAttrKeys() {
760             return new ArrayList<String>();
761         }
762 
763         @Override
764         public String getItemAttribute(String id, String attrkey) {
765             return "";
766         }
767 
768         @Override
769         public int getItemCount() {
770             return params.size();
771         }
772 
773         @Override
774         public List<String> getItemIds() {
775             List<String> ids = new ArrayList<String>();
776             for(LookupParamMetadata param: params){
777                 ids.add(param.getKey());
778             }
779             return ids;
780         }
781 
782         @Override
783         public String getItemText(String id) {
784             String itemText = id;
785             for(LookupParamMetadata param: params){
786                 if(param.getKey().equals(id)){
787                     //TODO this should be a message key
788                     itemText = param.getName();
789                     break;
790                 }
791             }
792             return itemText;
793         }
794 
795         public Widget getWidget(String id){
796             Widget w = null;
797             for(LookupParamMetadata param: params){
798                 if(param.getKey().equals(id)){
799                     w = DefaultWidgetFactory.getInstance().getWidget(param);
800                     break;
801                 }
802             }
803             return w;
804         }
805 
806         public List<LookupParamMetadata> getParams() {
807             return params;
808         }
809     }
810 
811     private String getMessage(final String msgKey) {
812         return Application.getApplicationContext().getMessage(msgKey);
813     }
814 
815     public void addLookupChangedCallback(Callback<LookupMetadata> callback) {
816         lookupChangedCallbacks.add(callback);
817     }
818     
819     //FIXME: Is an action complete callback really necessary here, couldn't this simply be a method to perform the seach action.
820     public Callback<Boolean> getActionCompleteCallback() {
821         return new Callback<Boolean>() {
822             
823             @Override
824             public void exec(Boolean result) {                               
825                 
826                 if (resultsSelected == true) {
827                 	List<SelectedResults> selectedItems = getSelectedValues();
828                 	if (selectedItems.isEmpty())
829                 		Window.alert("Please, select a value");
830                 	else
831                 	{	
832                 		for(Callback<List<SelectedResults>> callback: selectedCompleteCallbacks){
833                 			callback.exec(selectedItems);
834                 		}  
835                 		return;
836                 	}	
837                 }
838                 
839                 actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getMessage("select"));
840                 resultsSelected = true;
841                 
842                 SearchRequest sr = getSearchRequest();
843                 table.performSearch(sr, activeSearchParametersWidget.getLookupMetadata().getResults(), activeSearchParametersWidget.getLookupMetadata().getResultReturnKey());
844                 resultsTablePanel.setVisible(true);
845                 List<HasSearchParam> userCriteria = new ArrayList<HasSearchParam>();
846                 List<HasSearchParam> searchParams = activeSearchParametersWidget.getSearchParams();
847 
848                 //initialize search parameters if user entered values into search criteria fields in UI
849                 for(HasSearchParam field: searchParams){
850                     SearchParam param = field.getSearchParam();
851                     //TODO is this null check needed here? probably. assuming string here
852                     //TODO make check more robust here/inserting params more robust
853                     //do not pass to the search parameters that are empty
854                     //FIXME hack - comparison to 'optional' - replace with check against 'optional' field and update related lookup metadata
855                     if ((param.getValue() != null) && ((param.getValue().toString().trim().isEmpty() == false) || (param.getKey().toLowerCase().indexOf("optional") == -1))) {
856                         userCriteria.add(field);
857                     }
858                 }
859                 showCriteriaChosen(userCriteria);
860 
861                 if (hasSearchParams){
862                 	//Only display modify link if there are search parametes available.
863 	                if(!resultsShown){
864 	                    searchSelectorPanel.removeFromParent();
865 	                    modifySearchPanel = new CollapsablePanel(getMessage("searchPanelModifySearch"), searchSelectorPanel, false);
866 	                    modifySearchPanel.getLabel().addClickHandler(new ClickHandler(){
867 	                        @Override
868 	                        public void onClick(ClickEvent event) {
869 	                            resultsTablePanel.setVisible(false);
870 	                            actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
871 	                            resultsSelected = false;
872 	                        }});
873 	                    SearchPanel.this.layout.insert(modifySearchPanel, 0);
874 	                    
875 	                }
876 	                else{
877 	                    modifySearchPanel.close();
878 	                }
879                 }
880                 resultsShown = true; 
881                 
882                 for(Callback<Boolean> callback: actionCompletedCallbacks){
883                     callback.exec( Boolean.valueOf(true));
884                 }                
885             }
886         };
887     }
888     
889     public SearchRequest getSearchRequest() {
890         if (activeSearchParametersWidget != null) {
891             return activeSearchParametersWidget.getSearchRequest();
892         }
893         return null;
894     }    
895     
896     public void setActionCancelButtonGroup(ActionCancelGroup actionCancelButtons) {
897         this.actionCancelButtons = actionCancelButtons;
898     }
899 	
900     public String getSelectedLookupName() {
901         return selectedLookupName;
902     }
903 
904     public void setSelectedLookupName(String selectedLookupName) {
905         this.selectedLookupName = selectedLookupName;
906     }	
907     
908     public void addSelectionCompleteCallback(Callback<List<SelectedResults>> callback){
909         selectedCompleteCallbacks.add(callback);
910     }   
911     
912     public void addActionCompleteCallback(Callback<Boolean> callback){
913         actionCompletedCallbacks.add(callback);
914     }
915 
916     public String getActionLabel() {
917         return actionLabel;
918     }
919 
920     public void setActionLabel(String actionLabel) {
921         if ((actionLabel != null) && (actionLabel.trim().length() > 0)) {
922             this.actionLabel = actionLabel;
923         }
924     }
925 
926 }