001    /**
002     * Copyright 2005-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.uif.view;
017    
018    import com.google.common.collect.Lists;
019    import org.apache.commons.lang.StringUtils;
020    import org.kuali.rice.core.api.mo.common.active.Inactivatable;
021    import org.kuali.rice.krad.datadictionary.AttributeDefinition;
022    import org.kuali.rice.krad.datadictionary.parse.BeanTag;
023    import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
024    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
025    import org.kuali.rice.krad.uif.UifConstants.ViewType;
026    import org.kuali.rice.krad.uif.UifPropertyPaths;
027    import org.kuali.rice.krad.uif.container.CollectionGroup;
028    import org.kuali.rice.krad.uif.container.Group;
029    import org.kuali.rice.krad.uif.component.Component;
030    import org.kuali.rice.krad.uif.component.RequestParameter;
031    import org.kuali.rice.krad.uif.control.Control;
032    import org.kuali.rice.krad.uif.control.TextAreaControl;
033    import org.kuali.rice.krad.uif.control.TextControl;
034    import org.kuali.rice.krad.uif.element.Action;
035    import org.kuali.rice.krad.uif.element.Message;
036    import org.kuali.rice.krad.uif.field.FieldGroup;
037    import org.kuali.rice.krad.uif.field.InputField;
038    import org.kuali.rice.krad.uif.field.LookupInputField;
039    import org.kuali.rice.krad.uif.util.ComponentFactory;
040    import org.kuali.rice.krad.uif.util.ComponentUtils;
041    import org.kuali.rice.krad.util.KRADConstants;
042    import org.kuali.rice.krad.web.form.LookupForm;
043    
044    import java.util.ArrayList;
045    import java.util.Arrays;
046    import java.util.HashMap;
047    import java.util.List;
048    
049    /**
050     * View type for lookups
051     *
052     * <p>
053     * Supports doing a search against a data object class or performing a more advanced query. The view
054     * type is primarily made up of two groups, the search (or criteria) group and the results group. Many
055     * options are supported on the view to enable/disable certain features, like what actions are available
056     * on the search results.
057     * </p>
058     *
059     * <p>
060     * Works in conjunction with <code>LookupableImpl</code> which customizes the view and carries out the
061     * business functionality
062     * </p>
063     *
064     * @author Kuali Rice Team (rice.collab@kuali.org)
065     */
066    @BeanTag(name = "lookupView-bean", parent = "Uif-LookupView")
067    public class LookupView extends FormView {
068        private static final long serialVersionUID = 716926008488403616L;
069    
070        private Class<?> dataObjectClassName;
071    
072        private Group criteriaGroup;
073        private CollectionGroup resultsGroup;
074    
075        private List<Component> criteriaFields;
076        private List<Component> resultFields;
077        private List<String> defaultSortAttributeNames;
078    
079        protected boolean defaultSortAscending = true;
080    
081        @RequestParameter
082        private boolean hideReturnLinks = false;
083        @RequestParameter
084        private boolean suppressActions = false;
085        @RequestParameter
086        private boolean showMaintenanceLinks = false;
087        @RequestParameter
088        private boolean multipleValuesSelect = false;
089        @RequestParameter
090        private boolean renderLookupCriteria = true;
091        @RequestParameter
092        private boolean renderSearchButtons = true;
093        @RequestParameter
094        private boolean renderHeader = true;
095    
096        @RequestParameter
097        private String returnTarget;
098    
099        @RequestParameter
100        private boolean returnByScript;
101    
102        private boolean triggerOnChange;
103    
104        private Integer resultSetLimit = null;
105        private Integer multipleValuesSelectResultSetLimit = null;
106    
107        private String maintenanceUrlMapping;
108    
109        private FieldGroup rangeFieldGroupPrototype;
110    
111        private Message rangedToMessage;
112    
113        private boolean autoAddActiveCriteria;
114    
115        public LookupView() {
116            super();
117    
118            setViewTypeName(ViewType.LOOKUP);
119            setApplyDirtyCheck(false);
120            setTriggerOnChange(false);
121            setAutoAddActiveCriteria(true);
122        }
123    
124        /**
125         * The following initialization is performed:
126         *
127         * <ul>
128         * <li>Set the abstractTypeClasses map for the lookup object path</li>
129         * </ul>
130         *
131         * @see org.kuali.rice.krad.uif.container.ContainerBase#performInitialization(org.kuali.rice.krad.uif.view.View,
132         *      java.lang.Object)
133         */
134        @Override
135        public void performInitialization(View view, Object model) {
136    
137            boolean isInactivatableClass = Inactivatable.class.isAssignableFrom(dataObjectClassName);
138    
139            if (autoAddActiveCriteria && isInactivatableClass) {
140                autoAddActiveCriteria();
141            }
142    
143            initializeGroups();
144    
145            // since we don't have these as prototypes need to assign ids here
146            view.assignComponentIds(getCriteriaGroup());
147            view.assignComponentIds(getResultsGroup());
148    
149            if (getItems().isEmpty()) {
150                setItems(Arrays.asList(getCriteriaGroup(), getResultsGroup()));
151            }
152    
153            super.performInitialization(view, model);
154    
155            // if this is a multi-value lookup, don't show return column
156            if (multipleValuesSelect) {
157                hideReturnLinks = true;
158            }
159    
160            getObjectPathToConcreteClassMapping().put(UifPropertyPaths.LOOKUP_CRITERIA, getDataObjectClassName());
161            if (StringUtils.isNotBlank(getDefaultBindingObjectPath())) {
162                getObjectPathToConcreteClassMapping().put(getDefaultBindingObjectPath(), getDataObjectClassName());
163            }
164        }
165    
166        /**
167         * Adds the 'active' property criteria to the criteria fields if the BO is inactivatable
168         */
169        private void autoAddActiveCriteria() {
170            boolean hasActiveCriteria = false;
171    
172            for (Component field : getCriteriaFields()) {
173                if (((InputField)field).getPropertyName().equals("active")) {
174                    hasActiveCriteria = true;
175                }
176            }
177    
178            if (!hasActiveCriteria) {
179                AttributeDefinition attributeDefinition = KRADServiceLocatorWeb.getDataDictionaryService().getAttributeDefinition(
180                        dataObjectClassName.getName(), "active");
181                LookupInputField activeField = new LookupInputField();
182    
183                if (attributeDefinition == null) {
184                    activeField = (LookupInputField)ComponentFactory.getNewComponentInstance("Uif-LookupActiveInputField");
185                }else{
186                    activeField = (LookupInputField)ComponentFactory.getNewComponentInstance("Uif-LookupCriteriaInputField");
187                    activeField.setPropertyName("active");
188                    activeField.copyFromAttributeDefinition(this, attributeDefinition);
189                }
190    
191                getCriteriaFields().add(activeField);
192            }
193        }
194    
195        protected void initializeGroups() {
196            if (renderLookupCriteria && (getCriteriaGroup() != null) && (getCriteriaGroup().getItems().isEmpty())) {
197                getCriteriaGroup().setItems(getCriteriaFields());
198            }
199    
200            if (getResultsGroup() != null) {
201                if ((getResultsGroup().getItems().isEmpty()) && (getResultFields() != null)) {
202                    getResultsGroup().setItems(getResultFields());
203                }
204                if (getResultsGroup().getCollectionObjectClass() == null) {
205                    getResultsGroup().setCollectionObjectClass(getDataObjectClassName());
206                }
207            }
208        }
209    
210        /**
211         * @see org.kuali.rice.krad.uif.container.ContainerBase#performApplyModel(View, Object,
212         * org.kuali.rice.krad.uif.component.Component)
213         */
214        @Override
215        public void performApplyModel(View view, Object model, Component parent) {
216            LookupForm lookupForm = (LookupForm) model;
217    
218            if (!renderSearchButtons) {
219                criteriaGroup.getFooter().setRender(false);
220            }
221    
222            if (!renderLookupCriteria) {
223                criteriaGroup.setRender(false);
224            }
225    
226            if (!renderHeader) {
227                getHeader().setRender(false);
228            }
229    
230            setupLookupCriteriaFields(view, model);
231    
232            // Get the search action button for trigger on change and trigger on enter
233            Group actionGroup = criteriaGroup.getFooter();
234            Action searchButton = findSearchButton(actionGroup.getItems());
235    
236            // Only add trigger on script if an action with methodToCall search exists
237            if (searchButton != null) {
238                String searchButtonId = searchButton.getId();
239    
240                for (Component criteriaField : criteriaGroup.getItems()) {
241                    addTriggerScripts(searchButtonId, criteriaField);
242                }
243            }
244    
245            super.performApplyModel(view, model, parent);
246        }
247    
248        /**
249         * @see org.kuali.rice.krad.uif.container.ContainerBase#performFinalize(org.kuali.rice.krad.uif.view.View,
250         *      Object, org.kuali.rice.krad.uif.component.Component)
251         */
252        @Override
253        public void performFinalize(View view, Object model, Component parent) {
254            super.performFinalize(view, model, parent);
255    
256            // force session persistence of criteria fields so we can validate the search input
257            List<InputField> fields = ComponentUtils.getComponentsOfTypeDeep(criteriaGroup, InputField.class);
258            for (InputField field : fields) {
259                field.setForceSessionPersistence(true);
260            }
261        }
262    
263        /**
264         * Adds an on change script to fields with the isTriggerOnChange set to true. Also prevents adds script to execute
265         * search on enter when focus is in a criteris field
266         *
267         * @param searchButtonId the id of the search button
268         * @param criteriaField that the script will be added to
269         */
270        private void addTriggerScripts(String searchButtonId, Component criteriaField) {
271            if (criteriaField instanceof LookupInputField) {
272    
273                criteriaField.setOnKeyPressScript("if(e.which == 13) { e.preventDefault();jQuery('#" + searchButtonId + "' ).click();}");
274    
275                if (isTriggerOnChange() || ((LookupInputField)criteriaField).isTriggerOnChange()) {
276                    criteriaField.setOnChangeScript("jQuery('#" + searchButtonId + "' ).click();");
277                }
278            }
279        }
280    
281        /**
282         * Finds an Action with the search methodToCall from a list of Actions
283         *
284         * @param componentList list of components
285         * @return the Action component with methodToCall of search
286         */
287        private Action findSearchButton(List<? extends Component> componentList) {
288            List<? extends Action> actionList = ComponentUtils.getComponentsOfType(componentList, Action.class);
289            for (Action action : actionList) {
290                String methodToCall = action.getMethodToCall();
291                if (methodToCall != null && methodToCall.equals("search")) {
292                    return action;
293                }
294            }
295            return null;
296        }
297    
298        /**
299         * Helper method to do any lookup specific changes to the criteria fields
300         */
301        private void setupLookupCriteriaFields(View view, Object model) {
302            HashMap<Integer, Component> dateRangeFieldMap = new HashMap<Integer, Component>();
303    
304            ExpressionEvaluator expressionEvaluator =
305                    view.getViewHelperService().getExpressionEvaluator();
306    
307            int rangeIndex = 0;
308            for (Component criteriaField : criteriaGroup.getItems()) {
309                // Set the max length on the controls to allow for wildcards
310                Control control = ((InputField)criteriaField).getControl();
311                if (control instanceof TextControl) {
312                    ((TextControl) control).setMaxLength(null);
313                } else if (control instanceof TextAreaControl) {
314                    ((TextAreaControl) control).setMaxLength(null);
315                }
316    
317                if (((LookupInputField)criteriaField).isRanged()) {
318                    // Create field group
319                    FieldGroup rangeFieldGroup = ComponentUtils.copy(rangeFieldGroupPrototype, criteriaField.getId());
320                    rangeFieldGroup.setLabel(((LookupInputField)criteriaField).getLabel());
321    
322                    // Evaluate and set the required property and reset the required message on the 'to' label
323                    expressionEvaluator.evaluatePropertyExpression(view, criteriaField.getContext(), criteriaField,
324                            "required", true);
325                    rangeFieldGroup.setRequired(criteriaField.getRequired());
326                    ((LookupInputField) criteriaField).getFieldLabel().setRequiredMessage(new Message());
327    
328                    // Evaluate and set the render property
329                    expressionEvaluator.evaluatePropertyExpression(view, criteriaField.getContext(), criteriaField,
330                            UifPropertyPaths.RENDER, true);
331                    rangeFieldGroup.setRender(criteriaField.isRender());
332    
333                    List<Component> fieldGroupItems = new ArrayList<Component>();
334    
335                    // Create a new from date field
336                    LookupInputField fromDate = (LookupInputField) ComponentUtils.copy(criteriaField,
337                            KRADConstants.LOOKUP_DEFAULT_RANGE_SEARCH_LOWER_BOUND_LABEL);
338                    fromDate.getBindingInfo().setBindingName(
339                            KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + fromDate.getPropertyName());
340                    fromDate.setPropertyName(
341                            KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + fromDate.getPropertyName());
342    
343                    // Set the criteria fields labels
344                    fromDate.setLabel("");
345                    fromDate.getFieldLabel().setRenderColon(false);
346                    ((LookupInputField)criteriaField).getFieldLabel().setRender(false);
347    
348                    // Add the cirteria fields to the field group
349                    fieldGroupItems.add(fromDate);
350                    fieldGroupItems.add(rangedToMessage);
351                    fieldGroupItems.add(criteriaField);
352                    rangeFieldGroup.setItems(fieldGroupItems);
353    
354                    // Add fieldgroup to map with index as key
355                    dateRangeFieldMap.put(rangeIndex, rangeFieldGroup);
356                }
357    
358                rangeIndex++;
359            }
360    
361            // Replace original fields with range fieldgroups
362            List<Component> itemList = (List<Component>)criteriaGroup.getItems();
363            for (Integer index : dateRangeFieldMap.keySet()) {
364                    itemList.set(index, dateRangeFieldMap.get(index));
365            }
366    
367            criteriaGroup.setItems(itemList);
368        }
369    
370        /**
371         * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes()
372         */
373        @Override
374        public List<Component> getComponentPrototypes() {
375            List<Component> components = super.getComponentPrototypes();
376    
377            components.add(rangeFieldGroupPrototype);
378            components.add(rangedToMessage);
379    
380            return components;
381        }
382    
383        public void applyConditionalLogicForFieldDisplay() {
384            // TODO: work into view lifecycle
385            //          LookupViewHelperService lookupViewHelperService = (LookupViewHelperService) getViewHelperService();
386            //              Set<String> readOnlyFields = lookupViewHelperService.getConditionallyReadOnlyPropertyNames();
387            //              Set<String> requiredFields = lookupViewHelperService.getConditionallyRequiredPropertyNames();
388            //              Set<String> hiddenFields = lookupViewHelperService.getConditionallyHiddenPropertyNames();
389            //              if ( (readOnlyFields != null && !readOnlyFields.isEmpty()) ||
390            //                       (requiredFields != null && !requiredFields.isEmpty()) ||
391            //                       (hiddenFields != null && !hiddenFields.isEmpty())
392            //                      ) {
393            //                      for (Field field : getResultsGroup().getItems()) {
394            //                              if (InputField.class.isAssignableFrom(field.getClass())) {
395            //                                      InputField attributeField = (InputField) field;
396            //                                      if (readOnlyFields != null && readOnlyFields.contains(attributeField.getBindingInfo().getBindingName())) {
397            //                                              attributeField.setReadOnly(true);
398            //                                      }
399            //                                      if (requiredFields != null && requiredFields.contains(attributeField.getBindingInfo().getBindingName())) {
400            //                                              attributeField.setRequired(Boolean.TRUE);
401            //                                      }
402            //                                      if (hiddenFields != null && hiddenFields.contains(attributeField.getBindingInfo().getBindingName())) {
403            //                                              attributeField.setControl(LookupInquiryUtils.generateCustomLookupControlFromExisting(HiddenControl.class, null));
404            //                                      }
405            //                              }
406            //              }
407            //              }
408        }
409    
410        /**
411         * Class name for the object the lookup applies to
412         *
413         * <p>
414         * The object class name is used to pick up a dictionary entry which will
415         * feed the attribute field definitions and other configuration. In addition
416         * it is to configure the <code>Lookupable</code> which will carry out the
417         * lookup action
418         * </p>
419         *
420         * @return lookup data object class
421         */
422        @BeanTagAttribute(name="dataObjectClassName")
423        public Class<?> getDataObjectClassName() {
424            return this.dataObjectClassName;
425        }
426    
427        /**
428         * Setter for the object class name
429         *
430         * @param dataObjectClassName
431         */
432        public void setDataObjectClassName(Class<?> dataObjectClassName) {
433            this.dataObjectClassName = dataObjectClassName;
434        }
435    
436        /**
437         * @return the hideReturnLinks
438         */
439        @BeanTagAttribute(name="hideReturnLinks")
440        public boolean isHideReturnLinks() {
441            return this.hideReturnLinks;
442        }
443    
444        /**
445         * @param hideReturnLinks the hideReturnLinks to set
446         */
447        public void setHideReturnLinks(boolean hideReturnLinks) {
448            this.hideReturnLinks = hideReturnLinks;
449        }
450    
451        /**
452         * @return the suppressActions
453         */
454        @BeanTagAttribute(name="isSuppressActions")
455        public boolean isSuppressActions() {
456            return this.suppressActions;
457        }
458    
459        /**
460         * @param suppressActions the suppressActions to set
461         */
462        public void setSuppressActions(boolean suppressActions) {
463            this.suppressActions = suppressActions;
464        }
465    
466        /**
467         * @return the showMaintenanceLinks
468         */
469        @BeanTagAttribute(name="showMaintenanceLinks")
470        public boolean isShowMaintenanceLinks() {
471            return this.showMaintenanceLinks;
472        }
473    
474        /**
475         * @param showMaintenanceLinks the showMaintenanceLinks to set
476         */
477        public void setShowMaintenanceLinks(boolean showMaintenanceLinks) {
478            this.showMaintenanceLinks = showMaintenanceLinks;
479        }
480    
481        /**
482         * Indicates whether multiple values select should be enabled for the lookup
483         *
484         * <p>
485         * When set to true, the select field is enabled for the lookup results group that allows the user
486         * to select one or more rows for returning
487         * </p>
488         *
489         * @return true if multiple values should be enabled, false otherwise
490         */
491        @BeanTagAttribute(name="multipleValueSelect")
492        public boolean isMultipleValuesSelect() {
493            return multipleValuesSelect;
494        }
495    
496        /**
497         * Setter for the multiple values select indicator
498         *
499         * @param multipleValuesSelect
500         */
501        public void setMultipleValuesSelect(boolean multipleValuesSelect) {
502            this.multipleValuesSelect = multipleValuesSelect;
503        }
504    
505        @BeanTagAttribute(name="criteriaGroup",type = BeanTagAttribute.AttributeType.SINGLEBEAN)
506        public Group getCriteriaGroup() {
507            return this.criteriaGroup;
508        }
509    
510        public void setCriteriaGroup(Group criteriaGroup) {
511            this.criteriaGroup = criteriaGroup;
512        }
513    
514        @BeanTagAttribute(name="resultsGroup",type= BeanTagAttribute.AttributeType.SINGLEBEAN)
515        public CollectionGroup getResultsGroup() {
516            return this.resultsGroup;
517        }
518    
519        public void setResultsGroup(CollectionGroup resultsGroup) {
520            this.resultsGroup = resultsGroup;
521        }
522    
523        @BeanTagAttribute(name="criteriaFields",type= BeanTagAttribute.AttributeType.LISTBEAN)
524        public List<Component> getCriteriaFields() {
525            return this.criteriaFields;
526        }
527    
528        public void setCriteriaFields(List<Component> criteriaFields) {
529            this.criteriaFields = criteriaFields;
530        }
531    
532        @BeanTagAttribute(name="resultFields",type= BeanTagAttribute.AttributeType.LISTBEAN)
533        public List<Component> getResultFields() {
534            return this.resultFields;
535        }
536    
537        public void setResultFields(List<Component> resultFields) {
538            this.resultFields = resultFields;
539        }
540    
541        @BeanTagAttribute(name="defaultSortAttributeNames",type= BeanTagAttribute.AttributeType.LISTVALUE)
542        public List<String> getDefaultSortAttributeNames() {
543            return this.defaultSortAttributeNames;
544        }
545    
546        public void setDefaultSortAttributeNames(List<String> defaultSortAttributeNames) {
547            this.defaultSortAttributeNames = defaultSortAttributeNames;
548        }
549    
550        @BeanTagAttribute(name="defaultSortAscending")
551        public boolean isDefaultSortAscending() {
552            return this.defaultSortAscending;
553        }
554    
555        public void setDefaultSortAscending(boolean defaultSortAscending) {
556            this.defaultSortAscending = defaultSortAscending;
557        }
558    
559        /**
560         * Retrieves the maximum number of records that will be listed
561         * as a result of the lookup search
562         *
563         * @return Integer result set limit
564         */
565        @BeanTagAttribute(name="resultSetLimit")
566        public Integer getResultSetLimit() {
567            return resultSetLimit;
568        }
569    
570        /**
571         * Setter for the result list limit
572         *
573         * @param resultSetLimit Integer specifying limit
574         */
575        public void setResultSetLimit(Integer resultSetLimit) {
576            this.resultSetLimit = resultSetLimit;
577        }
578    
579        /**
580         * Indicates whether a result set limit has been specified for the
581         * view
582         *
583         * @return true if this instance has a result set limit
584         */
585        public boolean hasResultSetLimit() {
586            return (resultSetLimit != null);
587        }
588    
589        /**
590         * Retrieves the maximum number of records that will be listed
591         * as a result of the multiple values select lookup search
592         *
593         * @return multiple values select result set limit
594         */
595        @BeanTagAttribute(name="multipleValuesSelectResultSetLimit")
596        public Integer getMultipleValuesSelectResultSetLimit() {
597            return multipleValuesSelectResultSetLimit;
598        }
599    
600        /**
601         * Setter for the multiple values select result set limit
602         *
603         * @param multipleValuesSelectResultSetLimit Integer specifying limit
604         */
605        public void setMultipleValuesSelectResultSetLimit(Integer multipleValuesSelectResultSetLimit) {
606            this.multipleValuesSelectResultSetLimit = multipleValuesSelectResultSetLimit;
607        }
608    
609        /**
610         * Indicates whether a multiple values select result
611         * set limit has been specified for the view
612         *
613         * @return true if this instance has a multiple values select result set limit
614         */
615        public boolean hasMultipleValuesSelectResultSetLimit() {
616            return (multipleValuesSelectResultSetLimit != null);
617        }
618    
619        /**
620         * @param returnTarget the returnTarget to set
621         */
622        public void setReturnTarget(String returnTarget) {
623            this.returnTarget = returnTarget;
624        }
625    
626        /**
627         * @return the returnTarget
628         */
629        @BeanTagAttribute(name="returnTarget")
630        public String getReturnTarget() {
631            return returnTarget;
632        }
633    
634        /**
635         * @return the returnByScript
636         */
637        @BeanTagAttribute(name="returnByScript")
638        public boolean isReturnByScript() {
639            return returnByScript;
640        }
641    
642        /**
643         * Setter for the flag to indicate that lookups will return the value
644         * by script and not a post
645         *
646         * @param returnByScript the returnByScript flag
647         */
648        public void setReturnByScript(boolean returnByScript) {
649            this.returnByScript = returnByScript;
650        }
651    
652        /**
653         * String that maps to the maintenance controller for the maintenance document (if any) associated with the
654         * lookup data object class
655         *
656         * <p>
657         * Mapping will be used to build the maintenance action links (such as edit, copy, and new). If not given, the
658         * default maintenance mapping will be used
659         * </p>
660         *
661         * @return mapping string
662         */
663        @BeanTagAttribute(name="maintenanceUrlMapping")
664        public String getMaintenanceUrlMapping() {
665            return maintenanceUrlMapping;
666        }
667    
668        /**
669         * Setter for the URL mapping string that will be used to build up maintenance action URLs
670         *
671         * @param maintenanceUrlMapping
672         */
673        public void setMaintenanceUrlMapping(String maintenanceUrlMapping) {
674            this.maintenanceUrlMapping = maintenanceUrlMapping;
675        }
676    
677        /**
678         * Indicates that the action buttons like search in the criteria section should be rendered
679         *
680         * @return boolean
681         */
682        public boolean isRenderSearchButtons() {
683            return renderSearchButtons;
684        }
685    
686        /**
687         * Setter for the render search buttons flag
688         *
689         * @param renderSearchButtons
690         */
691        public void setRenderSearchButtons(boolean renderSearchButtons) {
692            this.renderSearchButtons = renderSearchButtons;
693        }
694    
695        /**
696         * Indicates whether the lookup criteria group should be rendered
697         *
698         * <p>
699         * Defaults to true. Can be set as bean property or passed as a request parameter in the lookup url.
700         * </p>
701         *
702         * @return boolean
703         */
704        public boolean isRenderLookupCriteria() {
705            return renderLookupCriteria;
706        }
707    
708        /**
709         * Setter for the lookup criteria group render flag
710         *
711         * @param renderLookupCriteria
712         */
713        public void setRenderLookupCriteria(boolean renderLookupCriteria) {
714            this.renderLookupCriteria = renderLookupCriteria;
715        }
716    
717        /**
718         * Indicates whether the lookup header should be rendered
719         *
720         * <p>
721         * Defaults to true. Can be set as bean property or passed as a request parameter in the lookup url.
722         * </p>
723         *
724         * @return boolean
725         */
726        public boolean isRenderHeader() {
727            return renderHeader;
728        }
729    
730        /**
731         * Setter for the header render flag
732         *
733         * @param renderHeader
734         */
735        public void setRenderHeader(boolean renderHeader) {
736            this.renderHeader = renderHeader;
737        }
738    
739        /**
740         * Indicates that the search must execute on changing of a value in all lookup input fields
741         *
742         * @return boolean
743         */
744        public boolean isTriggerOnChange() {
745            return triggerOnChange;
746        }
747    
748        /**
749         * Setter for the trigger search on change flag
750         *
751         * @param triggerOnChange
752         */
753        public void setTriggerOnChange(boolean triggerOnChange) {
754            this.triggerOnChange = triggerOnChange;
755        }
756    
757        /**
758         * The field group prototype that will be copied and used for range fields
759         *
760         * @return FieldGroup
761         */
762        public FieldGroup getRangeFieldGroupPrototype() {
763            return rangeFieldGroupPrototype;
764        }
765    
766        /**
767         * Setter for the range FieldGroup prototype
768         *
769         * @param rangeFieldGroupPrototype
770         */
771        public void setRangeFieldGroupPrototype(FieldGroup rangeFieldGroupPrototype) {
772            this.rangeFieldGroupPrototype = rangeFieldGroupPrototype;
773        }
774    
775        /**
776         * Indicates whether the 'active' criteria field must be added automatically for Inactivatable BO's
777         *
778         * @return boolean
779         */
780        public boolean isAutoAddActiveCriteria() {
781            return autoAddActiveCriteria;
782        }
783    
784        /**
785         * Setter for the flag that indicates whether the 'active' criteria field must be added automatically for
786         * Inactivatable BO's
787         *
788         * @param autoAddActiveCriteria
789         */
790        public void setAutoAddActiveCriteria(boolean autoAddActiveCriteria) {
791            this.autoAddActiveCriteria = autoAddActiveCriteria;
792        }
793    
794        /**
795         * The Message to render between the two range fields for ranged criteria fields
796         *
797         * @return
798         */
799        public Message getRangedToMessage() {
800            return rangedToMessage;
801        }
802    
803        /**
804         * Setter for the Message rendered between the two range fields for ranged criteria fields
805         *
806         * @param rangedToMessage
807         */
808        public void setRangedToMessage(Message rangedToMessage) {
809            this.rangedToMessage = rangedToMessage;
810        }
811    
812        /**
813         * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
814         */
815        @Override
816        protected <T> void copyProperties(T component) {
817            super.copyProperties(component);
818    
819            LookupView lookupViewCopy = (LookupView) component;
820    
821            if (this.dataObjectClassName != null) {
822                lookupViewCopy.setDataObjectClassName(this.getDataObjectClassName());
823            }
824    
825            if (this.criteriaGroup != null) {
826                lookupViewCopy.setCriteriaGroup((Group) this.getCriteriaGroup().copy());
827            }
828    
829            if (this.resultsGroup != null) {
830                lookupViewCopy.setResultsGroup((CollectionGroup) this.getResultsGroup().copy());
831            }
832    
833            if (this.criteriaFields != null) {
834                List<Component> criteriaFieldsCopy = Lists.newArrayListWithExpectedSize(criteriaFields.size());
835                for (Component criteriaField : criteriaFields) {
836                    criteriaFieldsCopy.add((Component) criteriaField.copy());
837                }
838                lookupViewCopy.setCriteriaFields(criteriaFieldsCopy);
839            }
840    
841            if (this.resultFields != null) {
842                List<Component> resultFieldsCopy = Lists.newArrayListWithExpectedSize(resultFields.size());
843                for (Component resultField : resultFields) {
844                    resultFieldsCopy.add((Component) resultField.copy());
845                }
846                lookupViewCopy.setResultFields(resultFieldsCopy);
847            }
848    
849            if (this.defaultSortAttributeNames != null) {
850                lookupViewCopy.setDefaultSortAttributeNames(new ArrayList<String>(defaultSortAttributeNames));
851            }
852    
853            lookupViewCopy.setDefaultSortAscending(this.isDefaultSortAscending());
854            lookupViewCopy.setHideReturnLinks(this.hideReturnLinks);
855            lookupViewCopy.setSuppressActions(this.suppressActions);
856            lookupViewCopy.setShowMaintenanceLinks(this.showMaintenanceLinks);
857            lookupViewCopy.setMaintenanceUrlMapping(this.maintenanceUrlMapping);
858            lookupViewCopy.setMultipleValuesSelect(this.multipleValuesSelect);
859            lookupViewCopy.setRenderLookupCriteria(this.renderLookupCriteria);
860            lookupViewCopy.setRenderSearchButtons(this.renderSearchButtons);
861            lookupViewCopy.setRenderHeader(this.renderHeader);
862            lookupViewCopy.setResultSetLimit(this.resultSetLimit);
863            lookupViewCopy.setReturnTarget(this.returnTarget);
864            lookupViewCopy.setTriggerOnChange(this.triggerOnChange);
865            lookupViewCopy.setResultSetLimit(this.resultSetLimit);
866            lookupViewCopy.setMultipleValuesSelectResultSetLimit(this.multipleValuesSelectResultSetLimit);
867            lookupViewCopy.setMaintenanceUrlMapping(this.maintenanceUrlMapping);
868    
869            if (this.rangeFieldGroupPrototype != null) {
870                lookupViewCopy.setRangeFieldGroupPrototype((FieldGroup) this.rangeFieldGroupPrototype.copy());
871            }
872    
873            if (this.rangedToMessage != null) {
874                lookupViewCopy.setRangedToMessage((Message) this.rangedToMessage.copy());
875            }
876    
877            lookupViewCopy.setAutoAddActiveCriteria(this.autoAddActiveCriteria);
878        }
879    
880    }