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