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