001    /**
002     * Copyright 2010 The Kuali Foundation Licensed under the
003     * Educational Community License, Version 2.0 (the "License"); you may
004     * not use this file except in compliance with the License. You may
005     * obtain a copy of the License at
006     *
007     * http://www.osedu.org/licenses/ECL-2.0
008     *
009     * Unless required by applicable law or agreed to in writing,
010     * software distributed under the License is distributed on an "AS IS"
011     * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012     * or implied. See the License for the specific language governing
013     * permissions and limitations under the License.
014     */
015    
016    package org.kuali.student.common.ui.client.widgets.search;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.LinkedHashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import org.kuali.student.common.ui.client.application.Application;
025    import org.kuali.student.common.ui.client.configurable.mvc.DefaultWidgetFactory;
026    import org.kuali.student.common.ui.client.mvc.Callback;
027    import org.kuali.student.common.ui.client.widgets.KSButton;
028    import org.kuali.student.common.ui.client.widgets.KSDropDown;
029    import org.kuali.student.common.ui.client.widgets.KSLabel;
030    import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
031    import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
032    import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations.ButtonEnum;
033    import org.kuali.student.common.ui.client.widgets.field.layout.button.ActionCancelGroup;
034    import org.kuali.student.common.ui.client.widgets.field.layout.button.ButtonGroup;
035    import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
036    import org.kuali.student.common.ui.client.widgets.layout.HorizontalBlockFlowPanel;
037    import org.kuali.student.common.ui.client.widgets.layout.VerticalFlowPanel;
038    import org.kuali.student.common.ui.client.widgets.list.KSSelectItemWidgetAbstract;
039    import org.kuali.student.common.ui.client.widgets.list.ListItems;
040    import org.kuali.student.common.ui.client.widgets.list.SelectionChangeEvent;
041    import org.kuali.student.common.ui.client.widgets.list.SelectionChangeHandler;
042    import org.kuali.student.common.ui.client.widgets.searchtable.ResultRow;
043    import org.kuali.student.core.assembly.data.LookupMetadata;
044    import org.kuali.student.core.assembly.data.LookupParamMetadata;
045    import org.kuali.student.core.assembly.data.Metadata;
046    import org.kuali.student.core.assembly.data.LookupMetadata.Usage;
047    import org.kuali.student.core.assembly.data.Metadata.WriteAccess;
048    import org.kuali.student.core.search.dto.SearchParam;
049    import org.kuali.student.core.search.dto.SearchRequest;
050    
051    import com.google.gwt.core.client.GWT;
052    import com.google.gwt.event.dom.client.ClickEvent;
053    import com.google.gwt.event.dom.client.ClickHandler;
054    import com.google.gwt.user.client.ui.Composite;
055    import com.google.gwt.user.client.ui.HTMLPanel;
056    import com.google.gwt.user.client.ui.HasText;
057    import com.google.gwt.user.client.ui.HasValue;
058    import com.google.gwt.user.client.ui.SimplePanel;
059    import com.google.gwt.user.client.ui.VerticalPanel;
060    import com.google.gwt.user.client.ui.Widget;
061    
062    public class SearchPanel extends Composite{
063    
064        //Layout configuration
065        private VerticalFlowPanel layout = new VerticalFlowPanel();
066        private SimplePanel searchSelectorPanel = new SimplePanel();
067        private VerticalFlowPanel resultsTablePanel = new VerticalFlowPanel();
068        private HorizontalBlockFlowPanel enteredCriteriaString = new HorizontalBlockFlowPanel();
069        private CollapsablePanel modifySearchPanel;
070        private String criteriaInstructions = getMessage("searchPanelEnterFields");
071        private KSLabel enteredCriteriaHeading = new KSLabel(getMessage("searchPanelCriteria"));
072        private SearchResultsTable table;
073        private boolean isMultiSelect = true;
074    
075            public static enum SearchStyle{ADVANCED, CUSTOM}; 
076        private ActionCancelGroup actionCancelButtons;
077    
078        private String actionLabel = getMessage("search");  //set default action label
079        private boolean resultsSelected = false;
080        
081        //Search data
082        private List<LookupMetadata> lookups = new ArrayList<LookupMetadata>();
083        private boolean multiSelect = false;
084        private boolean resultsShown = false;    
085        private SearchParametersWidget activeSearchParametersWidget = null;
086        // uses "name" of the lookup metadata to lookup the widget that layouts the search UI
087        private Map<String, SearchParametersWidget> searchParameterWidgetMap = new HashMap<String, SearchParametersWidget>();
088        private List<SearchField> searchFields = new ArrayList<SearchField>();
089        private List<Callback<LookupMetadata>> lookupChangedCallbacks = new ArrayList<Callback<LookupMetadata>>();    
090        private String selectedLookupName;
091        private List<Callback<List<SelectedResults>>> selectedCompleteCallbacks = new ArrayList<Callback<List<SelectedResults>>>();  
092        private List<Callback<Boolean>> actionCompletedCallbacks = new ArrayList<Callback<Boolean>>();    
093    
094        
095        interface SearchParametersWidget {
096            public SearchRequest getSearchRequest();
097            public LookupMetadata getLookupMetadata();
098            public List<HasSearchParam> getSearchParams();
099        }
100    
101        public SearchPanel(LookupMetadata meta){
102            lookups.add(meta);
103       //     setupSearch();
104            this.initWidget(layout);
105        }
106    
107        public SearchPanel(List<LookupMetadata> metas){
108            lookups = metas;       
109       //     setupSearch();
110            this.initWidget(layout);
111        }
112    
113        @SuppressWarnings("unchecked")
114            public ButtonGroup getButtons(){
115            return actionCancelButtons;
116        }
117        
118        public void setMutipleSelect(boolean isMultiSelect){
119            this.isMultiSelect = isMultiSelect;
120        }
121        
122        public void setupButtons() {
123            if (actionCancelButtons != null) {
124                actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
125                actionCancelButtons.addCallback(new Callback<ButtonEnumerations.ButtonEnum>(){
126                    @Override
127                   public void exec(ButtonEnum result) {
128                        if (result == ButtonEnumerations.SearchCancelEnum.SEARCH) {
129                            table.getContentTable().removeContent();
130                            getActionCompleteCallback().exec(true);                                 
131                        }
132                   }
133               });            
134            }       
135        }
136        
137        public void setupSearch() {
138                    
139            resultsTablePanel.clear();
140            layout.clear();
141            resultsShown = false;
142    
143            //create search panel
144            Widget searchParamPanel;        
145            if (lookups.size() == 1) {
146                searchParamPanel = createSearchParamPanel(lookups.get(0));
147                selectedLookupName = lookups.get(0).getName();
148                activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookupName);
149            } else {
150                LinkedHashMap<String, Widget> searches = new LinkedHashMap<String, Widget>();
151                LinkedHashMap<String, LookupMetadata> searchLookups = new LinkedHashMap<String, LookupMetadata>();
152                for(LookupMetadata lookup: lookups){
153                    searches.put(lookup.getName(), createSearchParamPanel(lookup));
154                    searchLookups.put(lookup.getName(), lookup);
155                }
156                selectedLookupName = lookups.get(0).getName();
157                // Sets the activeSearchParametersWidget to be the first search
158                activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookupName);
159                String actionLabel = (lookups.get(0) == null)? null : lookups.get(0)
160                        .getWidgetOptionValue(LookupMetadata.WidgetOption.ADVANCED_LIGHTBOX_ACTION_LABEL);
161                setActionLabel(actionLabel);
162                searchParamPanel = new SwappablePanel(searches);
163                ((SwappablePanel)searchParamPanel).setSearchLookups(searchLookups);
164                ((SwappablePanel)searchParamPanel).addLookupChangedCallback(new Callback<LookupMetadata>() {
165                    @Override
166                    public void exec(LookupMetadata selectedLookup) {
167                        activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookup.getName());
168                        selectedLookupName = selectedLookup.getName();
169                        if (lookupChangedCallbacks != null) {
170                            for (Callback<LookupMetadata> callback : lookupChangedCallbacks) {
171                                callback.exec(selectedLookup);
172                            }
173                        }
174                    }
175                });
176            }
177            searchSelectorPanel.setWidget(searchParamPanel);
178            layout.add(searchSelectorPanel);
179            
180            //create layout for results screen
181            enteredCriteriaHeading.addStyleName("ks-form-module-single-line-margin");
182            enteredCriteriaHeading.addStyleName("KS-Advanced-Search-Search-Criteria-Label");
183            resultsTablePanel.add(enteredCriteriaHeading);
184            resultsTablePanel.add(enteredCriteriaString);
185            resultsTablePanel.setVisible(false);        
186            table = new SearchResultsTable();
187            table.setMutipleSelect(isMultiSelect);
188            table.addStyleName("KS-Advanced-Search-Results-Table");
189            resultsTablePanel.add(table);
190            layout.add(resultsTablePanel); 
191            
192            table.getMslabel().addClickHandler(new ClickHandler(){
193    
194                            @Override
195                            public void onClick(ClickEvent event) {
196                                    if(modifySearchPanel.isOpen()){
197                                            modifySearchPanel.close();
198                                    }
199                                    else{
200                                            modifySearchPanel.open();
201                                    }
202                                    
203                                    resultsTablePanel.setVisible(false);
204                                    resultsSelected = false;
205                                    actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
206                            }
207                    });
208            
209            resultsSelected = false;
210            actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
211        }
212    
213        private Widget createSearchParamPanel(LookupMetadata meta){
214            ParamListItems listItems = new ParamListItems(meta);
215            final AdvancedSearch advancedSearch = new AdvancedSearch(meta);
216            LinkPanel panel = new LinkPanel(SearchStyle.ADVANCED, advancedSearch);
217            searchParameterWidgetMap.put(meta.getName(), advancedSearch);
218    
219            //check whether we need custom tab i.e. whether we have at least one parameter that should appear on custom tab
220            for(LookupParamMetadata metaParam: meta.getParams()){
221                if ((metaParam.getUsage() == Usage.CUSTOM) || (metaParam.getUsage() == Usage.ADVANCED_CUSTOM)) {
222                    final CustomizedSearch customizedSearch = new CustomizedSearch(meta, listItems);
223                    KSButton button = panel.addLinkToPanel(SearchStyle.ADVANCED, getMessage("searchPanelCustomizeSearch"), SearchStyle.CUSTOM);
224                    button.addClickHandler(new ClickHandler(){
225    
226                        @Override
227                        public void onClick(ClickEvent event) {
228                            resultsTablePanel.setVisible(false);
229                            activeSearchParametersWidget = customizedSearch;
230                        }});
231                    button.addStyleName("KS-Advanced-Search-Link");
232                    button.getParent().addStyleName("clearfix");
233                    panel.addPanel(SearchStyle.CUSTOM, customizedSearch);
234                    button = panel.addLinkToPanel(SearchStyle.CUSTOM, getMessage("searchPanelReturnToAdvancedSearch"), SearchStyle.ADVANCED);
235                    button.addClickHandler(new ClickHandler(){
236    
237                        @Override
238                        public void onClick(ClickEvent event) {
239                            resultsTablePanel.setVisible(false);
240                            activeSearchParametersWidget = advancedSearch;
241                        }});
242                    button.addStyleName("KS-Advanced-Search-Link");
243                    button.getParent().addStyleName("clearfix");
244                    break;
245                }
246            }
247    
248            return panel;
249        }
250    
251        private class CustomizedSearch extends Composite implements SearchParametersWidget {
252    
253            private List<CustomLine> lines = new ArrayList<CustomLine>();
254            private List<HasSearchParam> searchParams = new ArrayList<HasSearchParam>();
255            private VerticalPanel layout = new VerticalPanel();
256            private VerticalPanel linePanel = new VerticalPanel();
257            private LookupMetadata meta;
258    
259            public CustomizedSearch(final LookupMetadata meta, final ParamListItems listItems){
260    
261                KSLabel instrLabel = new KSLabel(criteriaInstructions);
262                layout.add(instrLabel);
263    
264                layout.add(linePanel);
265                CustomLine line = new CustomLine(meta, listItems);
266                line.addStyleName("ks-form-module-single-line-margin");
267                linePanel.add(line);
268                lines.add(line);
269                searchParams.add(line);
270                this.meta = meta;
271    
272                KSButton addCriteria = new KSButton(getMessage("searchPanelAddCriteria"), ButtonStyle.SECONDARY);
273                addCriteria.addClickHandler(new ClickHandler(){
274    
275                    @Override
276                    public void onClick(ClickEvent event) {
277                        CustomLine line = new CustomLine(meta, listItems);
278                        line.addStyleName("ks-form-module-single-line-margin");
279                        linePanel.add(line);
280                        lines.add(line);
281                        searchParams.add(line);
282                    }
283                });
284    
285                addCriteria.addStyleName("ks-form-module-single-line-margin");
286                layout.add(addCriteria);    
287                
288                this.initWidget(layout);
289            }
290            
291            public LookupMetadata getLookupMetadata() {
292                return meta;
293            }
294    
295            @Override
296            public SearchRequest getSearchRequest() {
297                //Create search request and then pass it to the table
298                //TODO pass search to the table
299                SearchRequest sr = new SearchRequest();
300                List<SearchParam> params = new ArrayList<SearchParam>();
301                for(CustomLine field: lines){
302                    SearchParam param = field.getSearchParam();
303                    //TODO is this check needed here? probably. assuming string here
304                    if((param.getValue() != null)){
305                        params.add(param);
306                    }
307                }
308    
309                //add search criteria widgets to the custom tab
310                for(LookupParamMetadata metaParam: meta.getParams()){
311    
312                    //select only parameters shown on custom search tab
313                    if ((metaParam.getUsage() != Usage.CUSTOM) && (metaParam.getUsage() != Usage.ADVANCED_CUSTOM)) {
314                        continue;
315                    }
316    
317                    if(metaParam.getWriteAccess() == WriteAccess.NEVER){
318                        SearchParam param = new SearchParam();
319                        param.setKey(metaParam.getKey());
320                        if(metaParam.getDefaultValueList()==null){
321                            param.setValue(metaParam.getDefaultValueString());
322                        }else{
323                            param.setValue(metaParam.getDefaultValueList());
324                        }
325                        params.add(param);
326                    }
327                    else if(metaParam.getWriteAccess() == WriteAccess.WHEN_NULL){
328                        if((metaParam.getDefaultValueString() != null && !metaParam.getDefaultValueString().isEmpty())||
329                           (metaParam.getDefaultValueList() != null && !metaParam.getDefaultValueList().isEmpty())){
330                            SearchParam param = new SearchParam();
331                            param.setKey(metaParam.getKey());
332                            if(metaParam.getDefaultValueList()==null){
333                                param.setValue(metaParam.getDefaultValueString());
334                            }else{
335                                param.setValue(metaParam.getDefaultValueList());
336                            }
337                            params.add(param);
338                        }
339                    }
340                }
341    
342                sr.setParams(params);
343                sr.setSearchKey(meta.getSearchTypeId());
344                return sr;
345            }
346    
347            @Override
348            public List<HasSearchParam> getSearchParams() {
349                return searchParams;
350            }
351    
352        }
353    
354        private interface HasSearchParam{
355            public SearchParam getSearchParam();
356            public String getFieldName();
357        }
358    
359        private static class CustomLine extends Composite implements HasSearchParam{
360            private KSDropDown paramSelector = new KSDropDown();
361            private SimplePanel widgetPanel = new SimplePanel();
362            private Widget widget = null;
363            private String key;
364            private HorizontalBlockFlowPanel layout = new HorizontalBlockFlowPanel();
365            private ParamListItems listItems;
366    
367            public CustomLine(LookupMetadata meta, final ParamListItems listItems){
368                
369                List<LookupParamMetadata> customParams = new ArrayList<LookupParamMetadata>();
370    
371                for (LookupParamMetadata lookupParamMetadata : listItems.getParams()) {
372                    if (lookupParamMetadata.getWriteAccess() != WriteAccess.NEVER){
373                        if (lookupParamMetadata.getUsage() == Usage.CUSTOM || lookupParamMetadata.getUsage() == Usage.ADVANCED_CUSTOM ) {
374                            customParams.add(lookupParamMetadata);
375                        }                   
376                    } 
377                }
378                
379                ParamListItems customParamList = new ParamListItems(customParams);
380                
381                this.listItems = customParamList;
382                paramSelector.setBlankFirstItem(false);
383                paramSelector.setListItems(customParamList);
384    
385                String id = meta.getParams().get(0).getKey();
386                paramSelector.selectItem(id);
387                widget = listItems.getWidget(id);
388                key = id;
389                widgetPanel.setWidget(widget);
390                paramSelector.addSelectionChangeHandler(new SelectionChangeHandler(){
391    
392                    @Override
393                    public void onSelectionChange(SelectionChangeEvent event) {
394                        String id = ((KSSelectItemWidgetAbstract)event.getWidget()).getSelectedItem();
395                        widget = listItems.getWidget(id);
396                        widgetPanel.setWidget(widget);
397                        key = id;
398    
399                    }
400                });
401                layout.add(paramSelector);
402                layout.add(widgetPanel);
403                this.initWidget(layout);
404            }
405    
406            public SearchParam getSearchParam(){
407                return SearchPanel.getSearchParam(widget, key);
408            }
409    
410            public String getKey(){
411                return key;
412            }
413    
414            public String getFieldName(){
415                String id = paramSelector.getSelectedItem();
416                return listItems.getItemText(id);
417            }
418        }
419    
420        private class AdvancedSearch extends Composite implements SearchParametersWidget {
421            private LookupMetadata meta;
422            private List<HasSearchParam> searchParams = new ArrayList<HasSearchParam>();
423    
424            public AdvancedSearch(final LookupMetadata meta){
425                VerticalPanel panel = new VerticalPanel();
426    
427                KSLabel instrLabel = new KSLabel();
428                panel.add(instrLabel);
429                this.meta = meta;            
430    
431                //add widget for each search criteria to the advanced tab
432                boolean allFieldsRequired = true;
433                for(LookupParamMetadata param: meta.getParams()){
434    
435                    //select only parameters shown on advanced search tab
436                    if ((param.getUsage() != Usage.ADVANCED) && (param.getUsage() != Usage.ADVANCED_CUSTOM)) {
437                        continue;
438                    }
439    
440                    if ((param.getWriteAccess() == WriteAccess.ALWAYS) || (param.getWriteAccess() == WriteAccess.REQUIRED)){
441                        SearchField paramField = new SearchField(param);
442                        searchFields.add(paramField);
443                        panel.add(paramField);
444                        searchParams.add(paramField);
445                    }
446                    else if (param.getWriteAccess() == WriteAccess.WHEN_NULL){
447                        if(param.getDefaultValueString() == null && param.getDefaultValueList() == null){
448                            SearchField paramField = new SearchField(param);
449                            searchFields.add(paramField);
450                            panel.add(paramField);
451                            searchParams.add(paramField);
452                        }
453                    }
454    
455                    if (param.getWriteAccess() != Metadata.WriteAccess.REQUIRED) {
456                        allFieldsRequired = false;
457                    }
458                }
459    
460                //do not show criteria instructions if we have only one criteria field or in case all fields are required
461                if ((searchFields.size() > 1) || (allFieldsRequired == false)) {
462                    instrLabel.setText(criteriaInstructions);
463                }    
464                
465                this.initWidget(panel);
466            }
467            
468            public LookupMetadata getLookupMetadata() {
469                return meta;
470            }        
471    
472            @Override
473            public SearchRequest getSearchRequest() {
474                SearchRequest sr = new SearchRequest();
475                List<SearchParam> params = new ArrayList<SearchParam>();
476                List<HasSearchParam> searchParams = getSearchParams();
477    
478                //initialize search parameters if user entered values into search criteria fields in UI
479                
480                for(HasSearchParam field: searchParams){
481                    SearchParam param = field.getSearchParam();
482                    //TODO is this null check needed here? probably. assuming string here
483                    //TODO make check more robust here/inserting params more robust
484                    //do not pass to the search parameters that are empty
485                    //FIXME hack - comparison to 'optional' - replace with check against 'optional' field and update related lookup metadata
486                    if ((param.getValue() != null) && ((param.getValue().toString().trim().isEmpty() == false) || (param.getKey().toLowerCase().indexOf("optional") == -1))) {
487                        params.add(param);
488                    }
489                }
490    
491                //initialize search parameters that are hidden from the UI because they are set to default context specific values
492                for(LookupParamMetadata metaParam: meta.getParams()){
493                    if(metaParam.getWriteAccess() == WriteAccess.NEVER){
494                        if ((metaParam.getDefaultValueString() == null || metaParam.getDefaultValueString().isEmpty())&&
495                            (metaParam.getDefaultValueList() == null || metaParam.getDefaultValueList().isEmpty())) {
496                            //FIXME throw an exception?
497                            GWT.log("Key = " + metaParam.getKey() + " has write access NEVER but has no default value!", null);
498                            continue;
499                        }
500                        SearchParam param = new SearchParam();
501                        param.setKey(metaParam.getKey());
502                        if(metaParam.getDefaultValueList()==null){
503                            param.setValue(metaParam.getDefaultValueString());
504                        }else{
505                            param.setValue(metaParam.getDefaultValueList());
506                        }
507                        params.add(param);
508                    }
509                    else if(metaParam.getWriteAccess() == WriteAccess.WHEN_NULL){
510                        if((metaParam.getDefaultValueString() != null && !metaParam.getDefaultValueString().isEmpty())||
511                           (metaParam.getDefaultValueList() != null && !metaParam.getDefaultValueList().isEmpty())){
512                            SearchParam param = new SearchParam();
513                            param.setKey(metaParam.getKey());
514                            if(metaParam.getDefaultValueList()==null){
515                                param.setValue(metaParam.getDefaultValueString());
516                            }else{
517                                param.setValue(metaParam.getDefaultValueList());
518                            }
519                            params.add(param);
520                        }
521                    }
522                }
523                sr.setParams(params);
524                if (meta.getResultSortKey() != null) {
525                    sr.setSortColumn(meta.getResultSortKey());
526                }
527                sr.setSearchKey(meta.getSearchTypeId());
528                return sr;
529            }
530    
531            @Override
532            public List<HasSearchParam> getSearchParams() {
533                return searchParams;
534            }
535        }
536    
537        private static class SearchField extends Composite implements HasSearchParam{
538    
539            private Widget widget = null;
540            private LookupParamMetadata meta = null;
541            private VerticalFlowPanel panel = new VerticalFlowPanel();
542            private String fieldName;
543    
544            public SearchParam getSearchParam(){
545                return SearchPanel.getSearchParam(widget, meta.getKey());
546            }
547    
548            public SearchField(LookupParamMetadata param){
549                meta = param;
550                //TODO use message call here
551                fieldName = param.getName();
552                widget = DefaultWidgetFactory.getInstance().getWidget(param);
553                if(param.getDefaultValueString() != null){
554                    //TODO Add handling of default value lists here
555                    if(widget instanceof HasText){
556                        ((HasText) widget).setText(param.getDefaultValueString().toString());
557                    }
558                    else if(widget instanceof HasValue){
559                        ((HasValue) widget).setValue(param.getDefaultValueString());
560                    }
561                }
562    
563                //FIXME: remove because required field '*' indication will be part of FieldElement class
564                if (param.getWriteAccess() == Metadata.WriteAccess.REQUIRED) {
565                    fieldName += " *";
566                }
567    
568                FieldElement fieldElement = new FieldElement(fieldName, widget);
569                fieldElement.getTitleWidget().addStyleName("KS-Picker-Criteria-Text");
570                panel.add(fieldElement);
571                panel.addStyleName("clear");
572    
573                this.initWidget(panel);
574            }
575    
576            public Widget getFieldPanel(){
577                return panel;
578            }
579    
580            public String getFieldName() {
581                return fieldName;
582            }
583        }
584    
585        private static SearchParam getSearchParam(final Widget widget, String key){
586            SearchParam param = new SearchParam();
587            param.setKey(key);
588            if(widget instanceof HasText){
589                param.setValue(((HasText) widget).getText());
590            }
591            else if(widget instanceof HasValue){
592                Object value = ((HasValue) widget).getValue();
593                if(value != null){
594                //TODO need to handle date and other types here, how they are converted for search, etc
595                    if(value instanceof String){
596                        param.setValue((String)value);
597                    }
598                    else{
599                        param.setValue(value.toString());
600                        GWT.log("Fields in search probably(?) shouldnt have values other than string", null);
601                    }
602                }
603            }
604            else if (widget instanceof KSPicker){
605                param.setValue(((KSPicker)widget).getDisplayValue());
606            }
607            else {
608                param.setValue("");
609            }
610    
611            return param;
612        }
613    
614        private void showCriteriaChosen(List<HasSearchParam> fields){
615            enteredCriteriaString.clear();
616            boolean first = true;;
617            for(HasSearchParam field: fields){
618                String name = field.getFieldName();
619                //TODO Should be string only, needs type safety
620                String value = field.getSearchParam().getValue().toString();
621                if(!value.isEmpty()){
622                    HTMLPanel label = new HTMLPanel(name + ": <b>" + value + "</b> ");
623                    if (!first) {
624                        label.addStyleName("KS-Advanced-Search-Search-Criteria-Text");
625                    }
626                    enteredCriteriaString.add(label);
627                    first = false;
628                }
629            }
630        }
631    
632        public List<String> getSelectedIds(){
633            List<String> ids = new ArrayList<String>();
634            if(table != null){
635                ids = table.getSelectedIds();
636            }
637            return ids;
638        }
639    
640        public List<SelectedResults> getSelectedValues() {
641    
642            List<SelectedResults> selectedValues = new ArrayList<SelectedResults>();
643            if (table != null) {
644                List<ResultRow> selectedRows = table.getSelectedRows();
645                for (ResultRow row : selectedRows) {
646                    String displayKey = row.getValue(activeSearchParametersWidget.getLookupMetadata().getResultDisplayKey());
647                    String returnKey = row.getValue(activeSearchParametersWidget.getLookupMetadata().getResultReturnKey());
648                    selectedValues.add(new SelectedResults(displayKey, returnKey, row));
649                    if (multiSelect == false) {
650                        break;
651                    }
652                }
653            }
654    
655            return selectedValues;
656        }
657    
658        public boolean isMultiSelect() {
659            return multiSelect;
660        }
661    
662        public void setMultiSelect(boolean multiSelect) {
663            this.multiSelect = multiSelect;
664        }
665    
666        private static class ParamListItems implements ListItems{
667    
668            private List<LookupParamMetadata> params = new ArrayList<LookupParamMetadata>();
669    
670            public ParamListItems(LookupMetadata meta){
671                params = meta.getParams();
672            }
673    
674            public ParamListItems(List<LookupParamMetadata> params){
675                this.params = params;
676            }
677            
678            @Override
679            public List<String> getAttrKeys() {
680                return new ArrayList<String>();
681            }
682    
683            @Override
684            public String getItemAttribute(String id, String attrkey) {
685                return "";
686            }
687    
688            @Override
689            public int getItemCount() {
690                return params.size();
691            }
692    
693            @Override
694            public List<String> getItemIds() {
695                List<String> ids = new ArrayList<String>();
696                for(LookupParamMetadata param: params){
697                    ids.add(param.getKey());
698                }
699                return ids;
700            }
701    
702            @Override
703            public String getItemText(String id) {
704                String itemText = id;
705                for(LookupParamMetadata param: params){
706                    if(param.getKey().equals(id)){
707                        //TODO this should be a message key
708                        itemText = param.getName();
709                        break;
710                    }
711                }
712                return itemText;
713            }
714    
715            public Widget getWidget(String id){
716                Widget w = null;
717                for(LookupParamMetadata param: params){
718                    if(param.getKey().equals(id)){
719                        w = DefaultWidgetFactory.getInstance().getWidget(param);
720                        break;
721                    }
722                }
723                return w;
724            }
725    
726            public List<LookupParamMetadata> getParams() {
727                return params;
728            }
729        }
730    
731        private String getMessage(final String msgKey) {
732            return Application.getApplicationContext().getMessage(msgKey);
733        }
734    
735        public void addLookupChangedCallback(Callback<LookupMetadata> callback) {
736            lookupChangedCallbacks.add(callback);
737        }
738        
739        public Callback<Boolean> getActionCompleteCallback() {
740            return new Callback<Boolean>() {
741                
742                @Override
743                public void exec(Boolean result) {                               
744                    
745                    if (resultsSelected == true) {
746                        List<SelectedResults> selectedItems = getSelectedValues();
747                        for(Callback<List<SelectedResults>> callback: selectedCompleteCallbacks){
748                            callback.exec(selectedItems);
749                        }  
750                        return;
751                    }
752                    
753                    actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getMessage("select"));
754                    resultsSelected = true;
755                    
756                    SearchRequest sr = getSearchRequest();
757                    table.performSearch(sr, activeSearchParametersWidget.getLookupMetadata().getResults(), activeSearchParametersWidget.getLookupMetadata().getResultReturnKey());
758                    resultsTablePanel.setVisible(true);
759                    List<HasSearchParam> userCriteria = new ArrayList<HasSearchParam>();
760                    List<HasSearchParam> searchParams = activeSearchParametersWidget.getSearchParams();
761    
762                    //initialize search parameters if user entered values into search criteria fields in UI
763                    for(HasSearchParam field: searchParams){
764                        SearchParam param = field.getSearchParam();
765                        //TODO is this null check needed here? probably. assuming string here
766                        //TODO make check more robust here/inserting params more robust
767                        //do not pass to the search parameters that are empty
768                        //FIXME hack - comparison to 'optional' - replace with check against 'optional' field and update related lookup metadata
769                        if ((param.getValue() != null) && ((param.getValue().toString().trim().isEmpty() == false) || (param.getKey().toLowerCase().indexOf("optional") == -1))) {
770                            userCriteria.add(field);
771                        }
772                    }
773                    showCriteriaChosen(userCriteria);
774    
775                    if(!resultsShown){
776                        searchSelectorPanel.removeFromParent();
777                        modifySearchPanel = new CollapsablePanel(getMessage("searchPanelModifySearch"), searchSelectorPanel, false);
778                        modifySearchPanel.getLabel().addClickHandler(new ClickHandler(){
779                            @Override
780                            public void onClick(ClickEvent event) {
781                                resultsTablePanel.setVisible(false);
782                                actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
783                                resultsSelected = false;
784                            }});
785                        SearchPanel.this.layout.insert(modifySearchPanel, 0);
786                        
787                    }
788                    else{
789                        modifySearchPanel.close();
790                    }
791                    resultsShown = true; 
792                    
793                    for(Callback<Boolean> callback: actionCompletedCallbacks){
794                        callback.exec( Boolean.valueOf(true));
795                    }                
796                }
797            };
798        }
799        
800        public SearchRequest getSearchRequest() {
801            if (activeSearchParametersWidget != null) {
802                return activeSearchParametersWidget.getSearchRequest();
803            }
804            return null;
805        }    
806        
807        public void setActionCancelButtonGroup(ActionCancelGroup actionCancelButtons) {
808            this.actionCancelButtons = actionCancelButtons;
809        }
810            
811        public String getSelectedLookupName() {
812            return selectedLookupName;
813        }
814    
815        public void setSelectedLookupName(String selectedLookupName) {
816            this.selectedLookupName = selectedLookupName;
817        }   
818        
819        public void addSelectionCompleteCallback(Callback<List<SelectedResults>> callback){
820            selectedCompleteCallbacks.add(callback);
821        }   
822        
823        public void addActionCompleteCallback(Callback<Boolean> callback){
824            actionCompletedCallbacks.add(callback);
825        }
826    
827        public String getActionLabel() {
828            return actionLabel;
829        }
830    
831        public void setActionLabel(String actionLabel) {
832            if ((actionLabel != null) && (actionLabel.trim().length() > 0)) {
833                this.actionLabel = actionLabel;
834            }
835        }
836    
837    
838    }