001package org.kuali.rice.krad.uif.field;
002
003import org.apache.commons.lang.StringUtils;
004import org.kuali.rice.core.api.util.ConcreteKeyValue;
005import org.kuali.rice.krad.datadictionary.AttributeDefinition;
006import org.kuali.rice.krad.datadictionary.parse.BeanTag;
007import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
008import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
009import org.kuali.rice.krad.uif.UifConstants;
010import org.kuali.rice.krad.uif.component.Component;
011import org.kuali.rice.krad.uif.control.*;
012import org.kuali.rice.krad.uif.element.Message;
013import org.kuali.rice.krad.uif.util.ComponentFactory;
014import org.kuali.rice.krad.uif.util.ComponentUtils;
015import org.kuali.rice.krad.uif.util.KeyMessage;
016import org.kuali.rice.krad.uif.view.View;
017import org.kuali.rice.krad.util.KRADConstants;
018import org.kuali.rice.krad.util.KRADPropertyConstants;
019
020import java.util.Map;
021
022/**
023 * Created by angelind on 8/3/15.
024 *
025 * Overridden for fixing the issue with setting maxLength for the lookup fields.
026 * Modified method name: copyFromAttributeDefinition.
027 * Changes description : Getting the maxLength from attributeDefinition if the attribute definition is having maxLength.
028 */
029@BeanTag(name = "lookupCriteriaInputField-bean", parent = "Uif-LookupCriteriaInputField")
030public class LookupInputField extends InputField {
031    private static final long serialVersionUID = -8294275596836322699L;
032
033    public static final String CHECKBOX_CONVERTED_RADIO_CONTROL = "Uif-CheckboxConvertedRadioControl";
034
035    private boolean disableWildcardsAndOperators;
036    private boolean addControlSelectAllOption;
037    private boolean triggerOnChange;
038    private boolean ranged;
039
040    public LookupInputField() {
041        super();
042
043        disableWildcardsAndOperators = false;
044        addControlSelectAllOption = false;
045        setTriggerOnChange(false);
046    }
047
048    /**
049     * The following actions are performed:
050     *
051     * <ul>
052     * <li>Add all option if enabled and control is multi-value</li>
053     * </ul>
054     *
055     * @see org.kuali.rice.krad.uif.component.ComponentBase#performFinalize(org.kuali.rice.krad.uif.view.View,
056     *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
057     */
058    @Override
059    public void performFinalize(View view, Object model, Component parent) {
060        super.performFinalize(view, model, parent);
061
062        // if enabled add option to select all values
063        if (addControlSelectAllOption && (getControl() != null) && getControl() instanceof MultiValueControl) {
064            String allOptionText = KRADServiceLocatorWeb.getMessageService().getMessageText(
065                    UifConstants.MessageKeys.OPTION_ALL);
066
067            MultiValueControl multiValueControl = (MultiValueControl) getControl();
068            if (multiValueControl.getOptions() != null) {
069                multiValueControl.getOptions().add(0, new ConcreteKeyValue("", allOptionText));
070            }
071
072            if (multiValueControl.getRichOptions() != null) {
073                Message message = ComponentFactory.getMessage();
074
075                view.assignComponentIds(message);
076                message.setMessageText(allOptionText);
077                message.setGenerateSpan(false);
078
079                multiValueControl.getRichOptions().add(0, new KeyMessage("", allOptionText, message));
080            }
081        }
082    }
083
084    /**
085     * Override of InputField copy to setup properties necessary to make the field usable for inputting
086     * search criteria
087     *
088     * @param attributeDefinition AttributeDefinition instance the property values should be copied from
089     * @see DataField#copyFromAttributeDefinition(org.kuali.rice.krad.uif.view.View,
090     *      org.kuali.rice.krad.datadictionary.AttributeDefinition)
091     */
092    @Override
093    public void copyFromAttributeDefinition(View view, AttributeDefinition attributeDefinition) {
094        // label
095        if (StringUtils.isEmpty(getLabel())) {
096            setLabel(attributeDefinition.getLabel());
097        }
098
099        // short label
100        if (StringUtils.isEmpty(getShortLabel())) {
101            setShortLabel(attributeDefinition.getShortLabel());
102        }
103
104        // security
105        if (getDataFieldSecurity().getAttributeSecurity() == null) {
106            getDataFieldSecurity().setAttributeSecurity(attributeDefinition.getAttributeSecurity());
107        }
108
109        // options
110        if (getOptionsFinder() == null) {
111            setOptionsFinder(attributeDefinition.getOptionsFinder());
112        }
113
114        // TODO: what about formatter?
115
116        // use control from dictionary if not specified and convert for searching
117        if (getControl() == null) {
118            Control control = convertControlToLookupControl(attributeDefinition);
119            view.assignComponentIds(control);
120
121            setControl(control);
122        }
123
124        // overwrite maxLength to allow for wildcards and ranges
125        setMaxLength(100);
126        /* Modified by 'Angelin Dayana' for fixing the issue with setting maxLength for the lookup fields.
127        * Getting the maxLength from attributeDefinition if the attribute definition is having maxLength. */
128        if ( attributeDefinition.getMaxLength()!=null && (attributeDefinition.getMaxLength() > 100)) {
129            setMaxLength(attributeDefinition.getMaxLength());
130        }
131
132        // set default value for active field to true
133        if (StringUtils.isEmpty(getDefaultValue())) {
134            if ((StringUtils.equals(getPropertyName(), KRADPropertyConstants.ACTIVE))) {
135                setDefaultValue(KRADConstants.YES_INDICATOR_VALUE);
136            }
137        }
138
139        /*
140           * TODO delyea: FieldUtils.createAndPopulateFieldsForLookup used to allow for a set of property names to be passed in via the URL
141           * parameters of the lookup url to set fields as 'read only'
142           */
143
144    }
145
146    /**
147     * If control definition is defined on the given attribute definition, converts to an appropriate control for
148     * searching (if necessary) and returns a copy for setting on the field
149     *
150     * @param attributeDefinition attribute definition instance to retrieve control from
151     * @return Control instance or null if not found
152     */
153    protected static Control convertControlToLookupControl(AttributeDefinition attributeDefinition) {
154        if (attributeDefinition.getControlField() == null) {
155            return null;
156        }
157
158        Control newControl = null;
159
160        // convert checkbox to radio with yes/no/both options
161        if (CheckboxControl.class.isAssignableFrom(attributeDefinition.getControlField().getClass())) {
162            newControl = getCheckboxConvertedRadioControl();
163        }
164        // text areas get converted to simple text inputs
165        else if (TextAreaControl.class.isAssignableFrom(attributeDefinition.getControlField().getClass())) {
166            newControl = ComponentFactory.getTextControl();
167        } else {
168            newControl = ComponentUtils.copy(attributeDefinition.getControlField(), "");
169        }
170
171        return newControl;
172    }
173
174    /**
175     * @return the treatWildcardsAndOperatorsAsLiteral
176     */
177    @BeanTagAttribute(name = "disableWildcardsAndOperators")
178    public boolean isDisableWildcardsAndOperators() {
179        return this.disableWildcardsAndOperators;
180    }
181
182    /**
183     * @param disableWildcardsAndOperators the treatWildcardsAndOperatorsAsLiteral to set
184     */
185    public void setDisableWildcardsAndOperators(boolean disableWildcardsAndOperators) {
186        this.disableWildcardsAndOperators = disableWildcardsAndOperators;
187    }
188
189    /**
190     * Indicates whether the option for all values (blank key, 'All' label) should be added to the lookup
191     * field, note this is only supported for {@link org.kuali.rice.krad.uif.control.MultiValueControl} instance
192     *
193     * @return boolean true if all option should be added, false if not
194     */
195    @BeanTagAttribute(name = "addControlSelectAllOption")
196    public boolean isAddControlSelectAllOption() {
197        return addControlSelectAllOption;
198    }
199
200    /**
201     * Setter for the add all option indicator
202     *
203     * @param addControlSelectAllOption
204     */
205    public void setAddControlSelectAllOption(boolean addControlSelectAllOption) {
206        this.addControlSelectAllOption = addControlSelectAllOption;
207    }
208
209    /**
210     * Indicates that the search must execute on changing of a value in the lookup input field
211     *
212     * @return boolean
213     */
214    public boolean isTriggerOnChange() {
215        return triggerOnChange;
216    }
217
218    /**
219     * Setter for the trigger search on change flag
220     *
221     * @param triggerOnChange
222     */
223    public void setTriggerOnChange(boolean triggerOnChange) {
224        this.triggerOnChange = triggerOnChange;
225    }
226
227    /**
228     * Indicates that a field must be rendered as a from and to value
229     *
230     * @return
231     */
232    public boolean isRanged() {
233        return ranged;
234    }
235
236    /**
237     * Setter for ranged flag to indicate this is a range field
238     *
239     * @param ranged
240     */
241    public void setRanged(boolean ranged) {
242        this.ranged = ranged;
243    }
244
245    /**
246     * Remove any search criteria that are not part of the database lookup
247     *
248     * @param searchCriteria the search criteria to be filtered
249     * @return the filteredSearchCriteria
250     */
251    public Map<String, String> filterSearchCriteria(Map<String, String> searchCriteria) {
252        if (getControl() instanceof FilterableLookupCriteriaControl) {
253            return ((FilterableLookupCriteriaControl) getControl()).filterSearchCriteria(getPropertyName(),
254                    searchCriteria);
255        } else {
256            return searchCriteria;
257        }
258    }
259
260    /**
261     * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
262     */
263    @Override
264    protected <T> void copyProperties(T component) {
265        super.copyProperties(component);
266        LookupInputField lookupInputFieldCopy = (LookupInputField) component;
267        lookupInputFieldCopy.setDisableWildcardsAndOperators(this.disableWildcardsAndOperators);
268        lookupInputFieldCopy.setAddControlSelectAllOption(this.addControlSelectAllOption);
269        lookupInputFieldCopy.setTriggerOnChange(this.triggerOnChange);
270        lookupInputFieldCopy.setRanged(this.ranged);
271    }
272
273    /**
274     * Retrieves a new radio group control instance for converted lookup criteria checkboxes from Spring
275     * (initialized by the bean definition with the given id)
276     *
277     * @return RadioGroupControl
278     */
279    private static RadioGroupControl getCheckboxConvertedRadioControl() {
280        return (RadioGroupControl) ComponentFactory.getNewComponentInstance(CHECKBOX_CONVERTED_RADIO_CONTROL);
281    }
282}
283