001    /**
002     * Copyright 2005-2014 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.util;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
027    import org.kuali.rice.core.api.uif.RemotableAttributeField;
028    import org.kuali.rice.core.api.uif.RemotableCheckbox;
029    import org.kuali.rice.core.api.uif.RemotableCheckboxGroup;
030    import org.kuali.rice.core.api.uif.RemotableControlContract;
031    import org.kuali.rice.core.api.uif.RemotableDatepicker;
032    import org.kuali.rice.core.api.uif.RemotableHiddenInput;
033    import org.kuali.rice.core.api.uif.RemotableQuickFinder;
034    import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup;
035    import org.kuali.rice.core.api.uif.RemotableSelect;
036    import org.kuali.rice.core.api.uif.RemotableSelectGroup;
037    import org.kuali.rice.core.api.uif.RemotableTextExpand;
038    import org.kuali.rice.core.api.uif.RemotableTextInput;
039    import org.kuali.rice.core.api.uif.RemotableTextarea;
040    import org.kuali.rice.core.api.util.ConcreteKeyValue;
041    import org.kuali.rice.core.api.util.KeyValue;
042    import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
043    import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
044    import org.kuali.rice.krad.lookup.LookupInputField;
045    import org.kuali.rice.krad.lookup.LookupView;
046    import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
047    import org.kuali.rice.krad.uif.UifConstants;
048    import org.kuali.rice.krad.uif.component.Component;
049    import org.kuali.rice.krad.uif.container.CollectionGroup;
050    import org.kuali.rice.krad.uif.container.DialogGroup;
051    import org.kuali.rice.krad.uif.container.Group;
052    import org.kuali.rice.krad.uif.container.LinkGroup;
053    import org.kuali.rice.krad.uif.container.NavigationGroup;
054    import org.kuali.rice.krad.uif.container.PageGroup;
055    import org.kuali.rice.krad.uif.container.TabGroup;
056    import org.kuali.rice.krad.uif.container.TreeGroup;
057    import org.kuali.rice.krad.uif.control.CheckboxControl;
058    import org.kuali.rice.krad.uif.control.CheckboxGroupControl;
059    import org.kuali.rice.krad.uif.control.Control;
060    import org.kuali.rice.krad.uif.control.FileControl;
061    import org.kuali.rice.krad.uif.control.GroupControl;
062    import org.kuali.rice.krad.uif.control.HiddenControl;
063    import org.kuali.rice.krad.uif.control.MultiValueControl;
064    import org.kuali.rice.krad.uif.control.RadioGroupControl;
065    import org.kuali.rice.krad.uif.control.SelectControl;
066    import org.kuali.rice.krad.uif.control.SizedControl;
067    import org.kuali.rice.krad.uif.control.TextAreaControl;
068    import org.kuali.rice.krad.uif.control.TextControl;
069    import org.kuali.rice.krad.uif.control.UserControl;
070    import org.kuali.rice.krad.uif.element.Action;
071    import org.kuali.rice.krad.uif.element.Header;
072    import org.kuali.rice.krad.uif.element.Iframe;
073    import org.kuali.rice.krad.uif.element.Image;
074    import org.kuali.rice.krad.uif.element.Label;
075    import org.kuali.rice.krad.uif.element.Message;
076    import org.kuali.rice.krad.uif.element.ValidationMessages;
077    import org.kuali.rice.krad.uif.field.DataField;
078    import org.kuali.rice.krad.uif.field.FieldGroup;
079    import org.kuali.rice.krad.uif.field.GenericField;
080    import org.kuali.rice.krad.uif.field.ImageField;
081    import org.kuali.rice.krad.uif.field.InputField;
082    import org.kuali.rice.krad.uif.field.LinkField;
083    import org.kuali.rice.krad.uif.field.MessageField;
084    import org.kuali.rice.krad.uif.field.SpaceField;
085    import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
086    import org.kuali.rice.krad.uif.view.InquiryView;
087    import org.kuali.rice.krad.uif.widget.Inquiry;
088    import org.kuali.rice.krad.uif.widget.LightBox;
089    import org.kuali.rice.krad.uif.widget.QuickFinder;
090    
091    /**
092     * Factory class for creating new UIF components from their base definitions in the dictionary.
093     *
094     * @author Kuali Rice Team (rice.collab@kuali.org)
095     */
096    public class ComponentFactory {
097        private static Log LOG = LogFactory.getLog(ComponentFactory.class);
098    
099        public static final String TEXT_CONTROL = "Uif-TextControl";
100        public static final String CHECKBOX_CONTROL = "Uif-CheckboxControl";
101        public static final String HIDDEN_CONTROL = "Uif-HiddenControl";
102        public static final String TEXTAREA_CONTROL = "Uif-TextAreaControl";
103        public static final String SELECT_CONTROL = "Uif-DropdownControl";
104        public static final String CHECKBOX_CONVERTED_RADIO_CONTROL = "Uif-CheckboxConvertedRadioControl";
105        public static final String CHECKBOX_GROUP_CONTROL = "Uif-VerticalCheckboxesControl";
106        public static final String CHECKBOX_GROUP_CONTROL_HORIZONTAL = "Uif-HorizontalCheckboxesControl";
107        public static final String RADIO_GROUP_CONTROL = "Uif-VerticalRadioControl";
108        public static final String RADIO_GROUP_CONTROL_HORIZONTAL = "Uif-HorizontalRadioControl";
109        public static final String FILE_CONTROL = "Uif-FileControl";
110        public static final String DATE_CONTROL = "Uif-DateControl";
111        public static final String USER_CONTROL = "Uif-KimPersonControl";
112        public static final String GROUP_CONTROL = "Uif-KimGroupControl";
113    
114        public static final String DATA_FIELD = "Uif-DataField";
115        public static final String INPUT_FIELD = "Uif-InputField";
116        public static final String LOOKUP_ACTIVE_INPUT_FIELD = "Uif-LookupActiveInputField";
117        public static final String LOOKUP_INPUT_FIELD = "Uif-LookupCriteriaInputField";
118        public static final String ERRORS_FIELD = "Uif-FieldValidationMessages";
119        public static final String ACTION = "Uif-PrimaryActionButton";
120        public static final String ACTION_LINK = "Uif-ActionLink";
121        public static final String LINK_FIELD = "Uif-LinkField";
122        public static final String IFRAME = "Uif-Iframe";
123        public static final String IMAGE_FIELD = "Uif-ImageField";
124        public static final String SPACE_FIELD = "Uif-SpaceField";
125        public static final String GENERIC_FIELD = "Uif-CustomTemplateField";
126        public static final String IMAGE = "Uif-Image";
127        public static final String LABEL = "Uif-Label";
128        public static final String MESSAGE = "Uif-Message";
129        public static final String MESSAGE_FIELD = "Uif-MessageField";
130        public static final String COLLECTION_GROUPING_FIELD = "Uif-ColGroupingField";
131        public static final String FIELD_GROUP = "Uif-VerticalFieldGroup";
132        public static final String HORIZONTAL_FIELD_GROUP = "Uif-HorizontalFieldGroup";
133    
134        public static final String GROUP = "Uif-GroupBase";
135        public static final String VERTICAL_BOX_GROUP = "Uif-VerticalBoxGroup";
136        public static final String HORIZONTAL_BOX_GROUP = "Uif-HorizontalBoxGroup";
137        public static final String VERTICAL_BOX_SECTION = "Uif-VerticalBoxSection";
138        public static final String HORIZONTAL_BOX_SECTION = "Uif-HorizontalBoxSection";
139        public static final String PAGE_GROUP = "Uif-Page";
140        public static final String GROUP_GRID_LAYOUT = "Uif-GridSection";
141        public static final String GROUP_WITH_DISCLOSURE_GRID_LAYOUT = "Uif-Disclosure-GridSection";
142        public static final String GROUP_BODY_ONLY = "Uif-BoxGroupBase";
143        public static final String GROUP_GRID_BODY_ONLY = "Uif-GridGroup";
144        public static final String TAB_GROUP = "Uif-TabSection";
145        public static final String NAVIGATION_GROUP = "Uif-NavigationGroupBase";
146        public static final String TREE_GROUP = "Uif-TreeSection";
147        public static final String LINK_GROUP = "Uif-LinkGroup";
148        public static final String COLLECTION_GROUP = "Uif-StackedCollectionSection";
149        public static final String COLLECTION_WITH_DISCLOSURE_GROUP = "Uif-Disclosure-StackedCollectionSection";
150        public static final String COLLECTION_GROUP_TABLE_LAYOUT = "Uif-TableCollectionSection";
151        public static final String COLLECTION_WITH_DISCLOSURE_GROUP_TABLE_LAYOUT = "Uif-Disclosure-TableCollectionSection";
152    
153        public static final String LIST_GROUP = "Uif-ListCollectionSection";
154    
155        public static final String HEADER = "Uif-HeaderFieldBase";
156        public static final String FOOTER = "Uif-FooterBase";
157        public static final String FOOTER_SAVECLOSECANCEL = "Uif-FormPageFooter";
158    
159        public static final String CONSTRAINT_MESSAGE = "Uif-ConstraintMessage";
160        public static final String INSTRUCTIONAL_MESSAGE = "Uif-InstructionalMessage";
161        public static final String HELP_ACTION = "Uif-HelpAction";
162        public static final String IMAGE_CAPTION_HEADER = "Uif-ImageCaptionHeader";
163        public static final String IMAGE_CUTLINE_MESSAGE = "Uif-ImageCutineMessage";
164    
165        public static final String LIGHTBOX = "Uif-LightBox";
166        public static final String QUICKFINDER = "Uif-QuickFinder";
167        public static final String INQUIRY = "Uif-Inquiry";
168    
169        public static final String ADD_BLANK_LINE_ACTION = "Uif-AddBlankLineAction";
170        public static final String ADD_VIA_LIGHTBOX_ACTION = "Uif-AddViaLightBoxAction";
171    
172        public static final String SESSION_TIMEOUT_WARNING_DIALOG = "Uif-SessionTimeoutWarning-DialogGroup";
173        public static final String SESSION_TIMEOUT_DIALOG = "Uif-SessionTimeout-DialogGroup";
174    
175        public static final String INQUIRY_VIEW = "Uif-InquiryView";
176        public static final String LOOKUP_VIEW = "Uif-LookupView";
177        public static final String LOOKUP_CRITERIA_FIELD = "Uif-LookupCriteriaInputField";
178        public static final String LOOKUP_CRITERIA_ACTIVE_INDICATOR_FIELD = "Uif-LookupActiveInputField";
179    
180        public static final String URL_INFO = "Uif-Url";
181    
182        private static Map<String, Component> cache = new HashMap<String, Component>();
183    
184        /**
185         * Returns a new {@link Component} instance for the given bean id from the spring factory.
186         *
187         * @param beanId id of the bean definition
188         * @return new component instance or null if no such component definition was found
189         */
190        public static Component getNewComponentInstance(String beanId) {
191            Component component;
192    
193            if (cache.containsKey(beanId)) {
194                component = cache.get(beanId);
195            } else {
196                component = (Component) KRADServiceLocatorWeb.getDataDictionaryService().getDictionaryBean(beanId);
197    
198                // clear id before returning so duplicates do not occur
199                component.setId(null);
200    
201                // populate property expressions from expression graph
202                ViewLifecycle.getExpressionEvaluator().populatePropertyExpressionsFromGraph(component, true);
203    
204                // TODO: preprocess?
205                // CopyUtils.preventModification(component);
206    
207                synchronized (cache) {
208                    cache.put(beanId, component);
209                }
210            }
211    
212            component = ComponentUtils.copy(component);
213    
214            return component;
215        }
216    
217        /**
218         * Retrieves a new Text control instance from Spring (initialized by the bean definition
219         * with the given id)
220         *
221         * @return TextControl
222         */
223        public static TextControl getTextControl() {
224            return (TextControl) getNewComponentInstance(TEXT_CONTROL);
225        }
226    
227        /**
228         * Retrieves a new Text area control instance from Spring (initialized by the bean definition
229         * with the given id)
230         *
231         * @return TextAreaControl
232         */
233        public static TextAreaControl getTextAreaControl() {
234            return (TextAreaControl) getNewComponentInstance(TEXTAREA_CONTROL);
235        }
236    
237        /**
238         * Retrieves a new checkbox control instance from Spring (initialized by the bean definition
239         * with the given id)
240         *
241         * @return CheckboxControl
242         */
243        public static CheckboxControl getCheckboxControl() {
244            return (CheckboxControl) getNewComponentInstance(CHECKBOX_CONTROL);
245        }
246    
247        /**
248         * Retrieves a new hidden control instance from Spring (initialized by the bean definition
249         * with the given id)
250         *
251         * @return HiddenControl
252         */
253        public static HiddenControl getHiddenControl() {
254            return (HiddenControl) getNewComponentInstance(HIDDEN_CONTROL);
255        }
256    
257        /**
258         * Retrieves a new select control instance from Spring (initialized by the bean definition
259         * with the given id)
260         *
261         * @return SelectControl
262         */
263        public static SelectControl getSelectControl() {
264            return (SelectControl) getNewComponentInstance(SELECT_CONTROL);
265        }
266    
267        /**
268         * Retrieves a new checkbox group control instance from Spring (initialized by the bean definition
269         * with the given id)
270         *
271         * <p>
272         * Return checkbox group set for vertical orientation
273         * </p>
274         *
275         * @return CheckboxGroupControl
276         */
277        public static CheckboxGroupControl getCheckboxGroupControl() {
278            return (CheckboxGroupControl) getNewComponentInstance(CHECKBOX_GROUP_CONTROL);
279        }
280    
281        /**
282         * Retrieves a new checkbox group control instance from Spring (initialized by the bean definition
283         * with the given id)
284         *
285         * <p>
286         * Return checkbox group set for horizontal orientation
287         * </p>
288         *
289         * @return CheckboxGroupControl
290         */
291        public static CheckboxGroupControl getCheckboxGroupControlHorizontal() {
292            return (CheckboxGroupControl) getNewComponentInstance(CHECKBOX_GROUP_CONTROL_HORIZONTAL);
293        }
294    
295        /**
296         * Retrieves a new radio group control instance from Spring (initialized by the bean definition
297         * with the given id)
298         *
299         * <p>
300         * Return radio group set for vertical orientation
301         * </p>
302         *
303         * @return RadioGroupControl
304         */
305        public static RadioGroupControl getRadioGroupControl() {
306            return (RadioGroupControl) getNewComponentInstance(RADIO_GROUP_CONTROL);
307        }
308    
309        /**
310         * Retrieves a new radio group control instance from Spring (initialized by the bean definition
311         * with the given id)
312         *
313         * <p>
314         * Return radio group set for horizontal orientation
315         * </p>
316         *
317         * @return RadioGroupControl
318         */
319        public static RadioGroupControl getRadioGroupControlHorizontal() {
320            return (RadioGroupControl) getNewComponentInstance(RADIO_GROUP_CONTROL_HORIZONTAL);
321        }
322    
323        /**
324         * Retrieves a new file control instance from Spring (initialized by the bean definition
325         * with the given id)
326         *
327         * @return FileControl
328         */
329        public static FileControl getFileControl() {
330            return (FileControl) getNewComponentInstance(FILE_CONTROL);
331        }
332    
333        /**
334         * Retrieves a new text control instance from Spring (initialized by the bean definition
335         * with the given id) configured for a date (enabled data picker)
336         *
337         * @return TextControl
338         */
339        public static TextControl getDateControl() {
340            return (TextControl) getNewComponentInstance(DATE_CONTROL);
341        }
342    
343        /**
344         * Retrieves a new text control instance from Spring (initialized by the bean definition
345         * with the given id) configured for KIM user input
346         *
347         * @return TextControl
348         */
349        public static UserControl getUserControl() {
350            return (UserControl) getNewComponentInstance(USER_CONTROL);
351        }
352    
353        /**
354         * Retrieves a new text control instance from Spring (initialized by the bean definition
355         * with the given id) configured for KIM group input
356         *
357         * @return TextControl
358         */
359        public static GroupControl getGroupControl() {
360            return (GroupControl) getNewComponentInstance(GROUP_CONTROL);
361        }
362    
363        /**
364         * Retrieves a new data field instance from Spring (initialized by the bean definition
365         * with the given id)
366         *
367         * @return DataField
368         */
369        public static DataField getDataField() {
370            return (DataField) getNewComponentInstance(DATA_FIELD);
371        }
372    
373        /**
374         * Retrieves a new data field instance from Spring (initialized by the bean definition
375         * with the given id) and sets the property name and label to the given parameters
376         *
377         * @param propertyName name of the property the data field should bind to
378         * @param label label for the field
379         * @return DataField
380         */
381        public static DataField getDataField(String propertyName, String label) {
382            DataField field = (DataField) getNewComponentInstance(DATA_FIELD);
383    
384            field.setPropertyName(propertyName);
385            field.setLabel(label);
386    
387            return field;
388        }
389    
390        /**
391         * Retrieves a new input field instance from Spring (initialized by the bean definition
392         * with the given id)
393         *
394         * @return InputField
395         */
396        public static InputField getInputField() {
397            return (InputField) getNewComponentInstance(INPUT_FIELD);
398        }
399    
400        /**
401         * Retrieves a new input field instance from Spring (initialized by the bean definition
402         * with the given id) and sets the property name and label to the given parameters
403         *
404         * @param propertyName name of the property the input field should bind to
405         * @param label label for the field
406         * @return InputField
407         */
408        public static InputField getInputField(String propertyName, String label) {
409            InputField field = (InputField) getNewComponentInstance(INPUT_FIELD);
410    
411            field.setPropertyName(propertyName);
412            field.setLabel(label);
413    
414            return field;
415        }
416    
417        /**
418         * Retrieves a new input field instance from Spring (initialized by the bean definition
419         * with the given id) and sets the property name, control, and label to the given parameters
420         *
421         * @param propertyName name of the property the input field should bind to
422         * @param label label for the field
423         * @param controlType enum that identifies the type of control to create for the input field
424         * @return InputField
425         */
426        public static InputField getInputField(String propertyName, String label, UifConstants.ControlType controlType) {
427            InputField field = (InputField) getNewComponentInstance(INPUT_FIELD);
428    
429            field.setPropertyName(propertyName);
430            field.setLabel(label);
431            field.setControl(getControl(controlType));
432    
433            return field;
434        }
435    
436        /**
437         * Retrieves a new input field instance from Spring (initialized by the bean definition
438         * with the given id) and sets the property name, control, defaultValue, and label to the given parameters
439         *
440         * @param propertyName name of the property the input field should bind to
441         * @param label label for the field
442         * @param controlType enum that identifies the type of control to create for the input field
443         * @param defaultValue default value for the property backing the input field
444         * @return InputField
445         */
446        public static InputField getInputField(String propertyName, String label, UifConstants.ControlType controlType,
447                String defaultValue) {
448            InputField field = (InputField) getNewComponentInstance(INPUT_FIELD);
449    
450            field.setPropertyName(propertyName);
451            field.setLabel(label);
452            field.setControl(getControl(controlType));
453            field.setDefaultValue(defaultValue);
454    
455            return field;
456        }
457    
458        /**
459         * Retrieves a new input field instance from Spring (initialized by the bean definition
460         * with the given id) and sets the property name, control, options finder, and label to the given parameters
461         *
462         * @param propertyName name of the property the input field should bind to
463         * @param label label for the field
464         * @param controlType enum that identifies the type of control to create for the input field
465         * @param optionsFinderClass class that will provide options for the control (assume control type is multi-value)
466         * @return InputField
467         */
468        public static InputField getInputField(String propertyName, String label, UifConstants.ControlType controlType,
469                Class<? extends KeyValuesFinder> optionsFinderClass) {
470            InputField field = (InputField) getNewComponentInstance(INPUT_FIELD);
471    
472            field.setPropertyName(propertyName);
473            field.setLabel(label);
474            field.setControl(getControl(controlType));
475            field.setOptionsFinderClass(optionsFinderClass);
476    
477            return field;
478        }
479    
480        /**
481         * Retrieves a new input field instance from Spring (initialized by the bean definition
482         * with the given id) and sets the property name, control, options, and label to the given parameters
483         *
484         * @param propertyName name of the property the input field should bind to
485         * @param label label for the field
486         * @param controlType enum that identifies the type of control to create for the input field
487         * @param options list of key value objects to set as the controls options
488         * @return InputField
489         */
490        public static InputField getInputField(String propertyName, String label, UifConstants.ControlType controlType,
491                List<KeyValue> options) {
492            InputField field = (InputField) getNewComponentInstance(INPUT_FIELD);
493    
494            field.setPropertyName(propertyName);
495            field.setLabel(label);
496    
497            Control control = getControl(controlType);
498            if (control instanceof MultiValueControl) {
499                ((MultiValueControl) control).setOptions(options);
500            } else {
501                throw new RuntimeException("Control is not instance of multi-value control, cannot set options");
502            }
503    
504            return field;
505        }
506    
507        /**
508         * Retrieves a new input field instance from Spring (initialized by the bean definition
509         * with the given id) and sets the property name, control, size, min and max length,
510         * and label to the given parameters
511         *
512         * @param propertyName name of the property the input field should bind to
513         * @param label label for the field
514         * @param controlType enum that identifies the type of control to create for the input field
515         * @param size size for the control
516         * @param maxLength max length for the field's value (also used for the control)
517         * @param minLength min length for the field's value (also used for the control)
518         * @return InputField
519         */
520        public static InputField getInputField(String propertyName, String label, UifConstants.ControlType controlType,
521                int size, int maxLength, int minLength) {
522            InputField field = (InputField) getNewComponentInstance(INPUT_FIELD);
523    
524            field.setPropertyName(propertyName);
525            field.setLabel(label);
526    
527            Control control = getControl(controlType);
528            if (control instanceof SizedControl) {
529                ((SizedControl) control).setSize(size);
530            } else {
531                throw new RuntimeException("Control does not support the size property");
532            }
533    
534            field.setMaxLength(maxLength);
535            field.setMinLength(minLength);
536    
537            return field;
538        }
539    
540        /**
541         * Builds a new <code>InputField</code> from the properties set on the
542         * given <code>RemotableAttributeField</code>
543         *
544         * <p>
545         * Note the returned InputField will not be initialized yet. Its state will be that of the initial
546         * object returned from the UIF dictionary with the properties set from the remotable attribute field, thus it
547         * is really just a more configuration complete field
548         * </p>
549         *
550         * @param remotableField field defined by a remove attribute
551         * 
552         * @return AttributeField instance built from remotable field
553         */
554        public static InputField translateRemotableField(RemotableAttributeField remotableField) {
555            InputField inputField = getInputField();
556    
557            inputField.setPropertyName(remotableField.getName());
558            inputField.setShortLabel(remotableField.getShortLabel());
559            inputField.setLabel(remotableField.getLongLabel());
560            inputField.setConstraintText(remotableField.getConstraintText());
561            inputField.setUppercaseValue(remotableField.isForceUpperCase());
562            inputField.setMinLength(remotableField.getMinLength());
563            inputField.setMaxLength(remotableField.getMaxLength());
564    
565            // why are exclusive min and max strings?
566            if (remotableField.getMinValue() != null) {
567                inputField.setExclusiveMin(remotableField.getMinValue().toString());
568            }
569            if (remotableField.getMaxValue() != null) {
570                inputField.setInclusiveMax(remotableField.getMaxValue().toString());
571            }
572            inputField.setRequired(remotableField.isRequired());
573    
574            if ((remotableField.getDefaultValues() != null) && !remotableField.getDefaultValues().isEmpty()) {
575                inputField.setDefaultValue(remotableField.getDefaultValues().iterator().next());
576            }
577    
578            if (StringUtils.isNotBlank(remotableField.getRegexConstraint())) {
579                ValidCharactersConstraint constraint = new ValidCharactersConstraint();
580                constraint.setValue(remotableField.getRegexConstraint());
581                inputField.setValidCharactersConstraint(constraint);
582                // TODO: how to deal with remotable field regexContraintMsg?
583            }
584    
585            RemotableDatepicker remotableDatepicker = null;
586            RemotableTextExpand remotableTextExpand = null;
587            RemotableQuickFinder remotableQuickFinder = null;
588            for (RemotableAbstractWidget remoteWidget : remotableField.getWidgets()) {
589                if (remoteWidget instanceof RemotableDatepicker) {
590                    remotableDatepicker = (RemotableDatepicker) remoteWidget;
591                } else if (remoteWidget instanceof RemotableTextExpand) {
592                    remotableTextExpand = (RemotableTextExpand) remoteWidget;
593                } else if (remoteWidget instanceof RemotableQuickFinder) {
594                    remotableQuickFinder = (RemotableQuickFinder) remoteWidget;
595                }
596            }
597    
598            if (remotableQuickFinder != null) {
599                if (inputField.getQuickfinder() == null) {
600                    inputField.setQuickfinder(ComponentFactory.getQuickFinder());
601                }
602    
603                inputField.getQuickfinder().setBaseLookupUrl(remotableQuickFinder.getBaseLookupUrl());
604                inputField.getQuickfinder().setDataObjectClassName(remotableQuickFinder.getDataObjectClass());
605                inputField.getQuickfinder().setLookupParameters(remotableQuickFinder.getLookupParameters());
606                inputField.getQuickfinder().setFieldConversions(remotableQuickFinder.getFieldConversions());
607            }
608    
609            if (remotableField.getControl() != null) {
610                Control control = null;
611    
612                RemotableControlContract remotableControl = remotableField.getControl();
613                if (remotableControl instanceof RemotableHiddenInput) {
614                    control = getHiddenControl();
615                } else if (remotableControl instanceof RemotableRadioButtonGroup) {
616                    RemotableRadioButtonGroup remotableRadioButtonGroup = (RemotableRadioButtonGroup) remotableControl;
617                    control = getRadioGroupControl();
618                    ((RadioGroupControl) control).setOptions(buildKeyValuePairs(remotableRadioButtonGroup.getKeyLabels()));
619                } else if (remotableControl instanceof RemotableSelect) {
620                    RemotableSelect remotableSelect = (RemotableSelect) remotableControl;
621                    control = getSelectControl();
622    
623                    Map<String, String> keyLabels = new HashMap<String, String>();
624                    if ((remotableSelect.getGroups() != null) && (!remotableSelect.getGroups().isEmpty())) {
625                        for (RemotableSelectGroup remotableSelectGroup : remotableSelect.getGroups()) {
626                            keyLabels.putAll(remotableSelectGroup.getKeyLabels());
627                        }
628                    } else {
629                        keyLabels = remotableSelect.getKeyLabels();
630                    }
631    
632                    ((SelectControl) control).setOptions(buildKeyValuePairs(keyLabels));
633                    if (remotableSelect.getSize() != null) {
634                        ((SelectControl) control).setSize(remotableSelect.getSize());
635                    }
636                    ((SelectControl) control).setMultiple(remotableSelect.isMultiple());
637                } else if (remotableControl instanceof RemotableCheckboxGroup) {
638                    RemotableCheckboxGroup remotableCheckboxGroup = (RemotableCheckboxGroup) remotableControl;
639                    control = getCheckboxGroupControl();
640                    ((CheckboxGroupControl) control).setOptions(buildKeyValuePairs(remotableCheckboxGroup.getKeyLabels()));
641                } else if (remotableControl instanceof RemotableCheckbox) {
642                    control = getCheckboxControl();
643                } else if (remotableControl instanceof RemotableTextarea) {
644                    RemotableTextarea remotableTextarea = (RemotableTextarea) remotableControl;
645                    control = getTextAreaControl();
646    
647                    if (remotableTextExpand != null) {
648                        ((TextAreaControl) control).setTextExpand(true);
649                    }
650                    ((TextAreaControl) control).setRows(remotableTextarea.getRows());
651                    ((TextAreaControl) control).setCols(remotableTextarea.getCols());
652                    ((TextAreaControl) control).setWatermarkText(remotableTextarea.getWatermark());
653    
654                } else if (remotableControl instanceof RemotableTextInput) {
655                    RemotableTextInput remotableTextInput = (RemotableTextInput) remotableControl;
656    
657                    if (remotableDatepicker != null) {
658                        control = getDateControl();
659                    } else {
660                        control = getTextControl();
661                    }
662    
663                    if (remotableTextExpand != null) {
664                        ((TextAreaControl) control).setTextExpand(true);
665                    }
666                    ((TextControl) control).setSize(remotableTextInput.getSize());
667                    ((TextControl) control).setWatermarkText(remotableTextInput.getWatermark());
668                }
669    
670                inputField.setControl(control);
671            }
672    
673            return inputField;
674        }
675    
676        /**
677         * For each remotable field in the given list creates a new {@link org.kuali.rice.krad.uif.field.InputField}
678         * instance and sets the
679         * corresponding properties from the remotable instance
680         *
681         * @param remotableFields list of remotable fields to translate
682         * @return List<AttributeField> list of attribute fields built from the remotable field properties
683         */
684        public static List<InputField> translateRemotableFields(List<RemotableAttributeField> remotableFields) {
685            List<InputField> inputFields = new ArrayList<InputField>();
686    
687            for (RemotableAttributeField remotableField : remotableFields) {
688                inputFields.add(translateRemotableField(remotableField));
689            }
690    
691            return inputFields;
692        }
693    
694        /**
695         * For each option in the given list, create a new {@link org.kuali.rice.core.api.util.KeyValue} instance
696         *
697         * @param optionsMap list of options
698         * @return List<KeyValue> list of key values built from the list of options
699         */
700        protected static List<KeyValue> buildKeyValuePairs(Map<String, String> optionsMap) {
701            List<KeyValue> options = new ArrayList<KeyValue>();
702    
703            for (Map.Entry<String, String> optionEntry : optionsMap.entrySet()) {
704                KeyValue keyValue = new ConcreteKeyValue(optionEntry.getKey(), optionEntry.getValue());
705                options.add(keyValue);
706            }
707    
708            return options;
709        }
710    
711        /**
712         * Gets the control
713         *
714         * @param controlType
715         * @return the control based on the control type
716         */
717        protected static Control getControl(UifConstants.ControlType controlType) {
718            Control control = null;
719            switch (controlType) {
720                case CHECKBOX:
721                    control = getCheckboxControl();
722                    break;
723                case CHECKBOXGROUP:
724                    control = getCheckboxGroupControl();
725                    break;
726                case FILE:
727                    control = getFileControl();
728                    break;
729                case GROUP:
730                    control = getGroupControl();
731                    break;
732                case HIDDEN:
733                    control = getHiddenControl();
734                    break;
735                case RADIOGROUP:
736                    control = getRadioGroupControl();
737                    break;
738                case SELECT:
739                    control = getSelectControl();
740                    break;
741                case TEXTAREA:
742                    control = getTextAreaControl();
743                    break;
744                case TEXT:
745                    control = getTextControl();
746                    break;
747                case USER:
748                    control = getUserControl();
749                    break;
750            }
751    
752            return control;
753        }
754    
755        /**
756         * Gets the errors field
757         *
758         * @return ValidationMessages errors field
759         */
760        public static ValidationMessages getErrorsField() {
761            return (ValidationMessages) getNewComponentInstance(ERRORS_FIELD);
762        }
763    
764        /**
765         * Gets the action
766         *
767         * @return action
768         */
769        public static Action getAction() {
770            return (Action) getNewComponentInstance(ACTION);
771        }
772    
773        /**
774         * Gets the action link
775         *
776         * @return action link
777         */
778        public static Action getActionLink() {
779            return (Action) getNewComponentInstance(ACTION_LINK);
780        }
781    
782        /**
783         * Gets the link field
784         *
785         * @return link field
786         */
787        public static LinkField getLinkField() {
788            return (LinkField) getNewComponentInstance(LINK_FIELD);
789        }
790    
791        /**
792         * Gets the iframe
793         *
794         * @return iframe
795         */
796        public static Iframe getIframe() {
797            return (Iframe) getNewComponentInstance(IFRAME);
798        }
799    
800        /**
801         * Gets the image field
802         *
803         * @return image field
804         */
805        public static ImageField getImageField() {
806            return (ImageField) getNewComponentInstance(IMAGE_FIELD);
807        }
808    
809        /**
810         * Gets the image component
811         *
812         * @return image field
813         */
814        public static Image getImage() {
815            return (Image) getNewComponentInstance(IMAGE);
816        }
817    
818        /**
819         * Gets the space field
820         *
821         * @return space field
822         */
823        public static SpaceField getSpaceField() {
824            return (SpaceField) getNewComponentInstance(SPACE_FIELD);
825        }
826    
827        /**
828         * Gets the generic field
829         *
830         * @return generic field
831         */
832        public static GenericField getGenericField() {
833            return (GenericField) getNewComponentInstance(GENERIC_FIELD);
834        }
835    
836        /**
837         * Gets the label
838         *
839         * @return label
840         */
841        public static Label getLabel() {
842            return (Label) getNewComponentInstance(LABEL);
843        }
844    
845        /**
846         * Gets the message
847         *
848         * @return message
849         */
850        public static Message getMessage() {
851            return (Message) getNewComponentInstance(MESSAGE);
852        }
853    
854        /**
855         * Gets the message field
856         *
857         * @return message field
858         */
859        public static MessageField getMessageField() {
860            return (MessageField) getNewComponentInstance(MESSAGE_FIELD);
861        }
862    
863        /**
864         * Gets the collection grouping field
865         *
866         * @return message field
867         */
868        public static MessageField getColGroupingField() {
869            return (MessageField) getNewComponentInstance(COLLECTION_GROUPING_FIELD);
870        }
871    
872        /**
873         * Gets the field group
874         *
875         * @return field group
876         */
877        public static FieldGroup getFieldGroup() {
878            return (FieldGroup) getNewComponentInstance(FIELD_GROUP);
879        }
880    
881        /**
882         * Gets the horizontal field group
883         *
884         * @return horizontal field group
885         */
886        public static FieldGroup getHorizontalFieldGroup() {
887            return (FieldGroup) getNewComponentInstance(HORIZONTAL_FIELD_GROUP);
888        }
889    
890        /**
891         * Gets the group
892         *
893         * @return group
894         */
895        public static Group getGroup() {
896            return (Group) getNewComponentInstance(GROUP);
897        }
898    
899        /**
900         * Gets the vertical box group
901         *
902         * @return group
903         */
904        public static Group getVerticalBoxGroup() {
905            return (Group) getNewComponentInstance(VERTICAL_BOX_GROUP);
906        }
907    
908        /**
909         * Gets the horizontal box group
910         *
911         * @return group
912         */
913        public static Group getHorizontalBoxGroup() {
914            return (Group) getNewComponentInstance(HORIZONTAL_BOX_GROUP);
915        }
916    
917        /**
918         * Gets the vertical box section
919         *
920         * @return group
921         */
922        public static Group getVerticalBoxSection() {
923            return (Group) getNewComponentInstance(VERTICAL_BOX_SECTION);
924        }
925    
926        /**
927         * Gets the horizontal box section
928         *
929         * @return group
930         */
931        public static Group getHorizontalBoxSection() {
932            return (Group) getNewComponentInstance(HORIZONTAL_BOX_SECTION);
933        }
934    
935        /**
936         * Gets the page group
937         *
938         * @return page group
939         */
940        public static PageGroup getPageGroup() {
941            return (PageGroup) getNewComponentInstance(PAGE_GROUP);
942        }
943    
944        /**
945         * Gets the group grid layout
946         *
947         * @return group grid layout
948         */
949        public static Group getGroupGridLayout() {
950            return (Group) getNewComponentInstance(GROUP_GRID_LAYOUT);
951        }
952    
953        public static Group getGroupWithDisclosureGridLayout() {
954            return (Group) getNewComponentInstance(GROUP_WITH_DISCLOSURE_GRID_LAYOUT);
955        }
956    
957        /**
958         * Gets the group body only
959         *
960         * @return group body only
961         */
962        public static Group getGroupBodyOnly() {
963            return (Group) getNewComponentInstance(GROUP_BODY_ONLY);
964        }
965    
966        /**
967         * Gets the group grid body only
968         *
969         * @return group grid body only
970         */
971        public static Group getGroupGridBodyOnly() {
972            return (Group) getNewComponentInstance(GROUP_GRID_BODY_ONLY);
973        }
974    
975        /**
976         * Gets the tab group
977         *
978         * @return tab group
979         */
980        public static TabGroup getTabGroup() {
981            return (TabGroup) getNewComponentInstance(TAB_GROUP);
982        }
983    
984        /**
985         * Gets the navigation group
986         *
987         * @return navigation group
988         */
989        public static NavigationGroup getNavigationGroup() {
990            return (NavigationGroup) getNewComponentInstance(NAVIGATION_GROUP);
991        }
992    
993        /**
994         * Gets the tree group
995         *
996         * @return tree group
997         */
998        public static TreeGroup getTreeGroup() {
999            return (TreeGroup) getNewComponentInstance(TREE_GROUP);
1000        }
1001    
1002        /**
1003         * Gets the link group
1004         *
1005         * @return link group
1006         */
1007        public static LinkGroup getLinkGroup() {
1008            return (LinkGroup) getNewComponentInstance(LINK_GROUP);
1009        }
1010    
1011        /**
1012         * Gets the collection group
1013         *
1014         * @return collection group
1015         */
1016        public static CollectionGroup getCollectionGroup() {
1017            return (CollectionGroup) getNewComponentInstance(COLLECTION_GROUP);
1018        }
1019    
1020        /**
1021         * Gets the collection group with disclosure behavior
1022         *
1023         * @return collection group
1024         */
1025        public static CollectionGroup getCollectionWithDisclosureGroup() {
1026            return (CollectionGroup) getNewComponentInstance(COLLECTION_WITH_DISCLOSURE_GROUP);
1027        }
1028    
1029        /**
1030         * Gets the collection group table layout
1031         *
1032         * @return collection group table layout
1033         */
1034        public static CollectionGroup getCollectionGroupTableLayout() {
1035            return (CollectionGroup) getNewComponentInstance(COLLECTION_GROUP_TABLE_LAYOUT);
1036        }
1037    
1038        /**
1039         * Gets the collection group table layout with disclosure behavior
1040         *
1041         * @return collection group table layout
1042         */
1043        public static CollectionGroup getCollectionWithDisclosureGroupTableLayout() {
1044            return (CollectionGroup) getNewComponentInstance(COLLECTION_WITH_DISCLOSURE_GROUP_TABLE_LAYOUT);
1045        }
1046    
1047        /**
1048         * Gets the list group
1049         *
1050         * @return list group
1051         */
1052        public static CollectionGroup getListGroup() {
1053            return (CollectionGroup) getNewComponentInstance(LIST_GROUP);
1054        }
1055    
1056        /**
1057         * Gets the header
1058         *
1059         * @return header
1060         */
1061        public static Header getHeader() {
1062            return (Header) getNewComponentInstance(HEADER);
1063        }
1064    
1065        /**
1066         * Gets the footer
1067         *
1068         * @return footer
1069         */
1070        public static Group getFooter() {
1071            return (Group) getNewComponentInstance(FOOTER);
1072        }
1073    
1074        /**
1075         * Gets the footer save/close/cancel
1076         *
1077         * @return footer save/close/cancel
1078         */
1079        public static Group getFooterSaveCloseCancel() {
1080            return (Group) getNewComponentInstance(FOOTER_SAVECLOSECANCEL);
1081        }
1082    
1083        /**
1084         * Gets the default action component configured for help
1085         *
1086         * @return Action for help display
1087         */
1088        public static Action getHelpAction() {
1089            return (Action) getNewComponentInstance(HELP_ACTION);
1090        }
1091    
1092        /**
1093         * Gets the default constraint message configuration
1094         *
1095         * @return Message component for constraint messages
1096         */
1097        public static Message getConstraintMessage() {
1098            return (Message) getNewComponentInstance(CONSTRAINT_MESSAGE);
1099        }
1100    
1101        /**
1102         * Gets the default instructional message configuration
1103         *
1104         * @return Message component for instructional messages
1105         */
1106        public static Message getInstructionalMessage() {
1107            return (Message) getNewComponentInstance(INSTRUCTIONAL_MESSAGE);
1108        }
1109    
1110        /**
1111         * Gets the default image caption header configuration
1112         *
1113         * @return Header component for image caption headers
1114         */
1115        public static Header getImageCaptionHeader() {
1116            return (Header) getNewComponentInstance(IMAGE_CAPTION_HEADER);
1117        }
1118    
1119        /**
1120         * Gets the default image cutline message configuration
1121         *
1122         * @return Message component for image cutlines messages
1123         */
1124        public static Message getImageCutlineMessage() {
1125            return (Message) getNewComponentInstance(IMAGE_CUTLINE_MESSAGE);
1126        }
1127    
1128        /**
1129         * Gets the default lightbox configuration
1130         *
1131         * @return Lightbox component
1132         */
1133        public static LightBox getLightBox() {
1134            return (LightBox) getNewComponentInstance(LIGHTBOX);
1135        }
1136    
1137        /**
1138         * Gets the default quickfinder configuration
1139         *
1140         * @return QuickFinder component
1141         */
1142        public static QuickFinder getQuickFinder() {
1143            return (QuickFinder) getNewComponentInstance(QUICKFINDER);
1144        }
1145    
1146        /**
1147         * Gets the default inquiry configuration
1148         *
1149         * @return Inquiry component
1150         */
1151        public static Inquiry getInquiry() {
1152            return (Inquiry) getNewComponentInstance(INQUIRY);
1153        }
1154    
1155        /**
1156         * Gets an instance of the session timeout warning dialog
1157         *
1158         * @return instance of session timeout warning dialog
1159         */
1160        public static DialogGroup getSessionTimeoutWarningDialog() {
1161            return (DialogGroup) getNewComponentInstance(SESSION_TIMEOUT_WARNING_DIALOG);
1162        }
1163    
1164        /**
1165         * Gets an instance of the session timeout dialog
1166         *
1167         * @return instance of session timeout dialog
1168         */
1169        public static DialogGroup getSessionTimeoutDialog() {
1170            return (DialogGroup) getNewComponentInstance(SESSION_TIMEOUT_DIALOG);
1171        }
1172    
1173        /**
1174         * Gets an instance of an UrlInfo
1175         *
1176         * @return instance of UrlInfo
1177         */
1178        public static UrlInfo getUrlInfo() {
1179            return (UrlInfo) getNewComponentInstance(URL_INFO);
1180        }
1181    
1182        /**
1183         * Gets an empty inquiry view configuration for population.
1184         *
1185         * @return InquiryView component
1186         */
1187        public static InquiryView getInquiryView() {
1188            return (InquiryView) getNewComponentInstance(INQUIRY_VIEW);
1189        }
1190    
1191        /**
1192         * Gets an empty lookup view configuration for population.
1193         *
1194         * @return LookupView component
1195         */
1196        public static LookupView getLookupView() {
1197            return (LookupView) getNewComponentInstance(LOOKUP_VIEW);
1198        }
1199    
1200        /**
1201         * Gets a component instance for an input field in the lookup criteria section
1202         * @return lookup input field instance
1203         */
1204        public static LookupInputField getLookupCriteriaInputField() {
1205            return (LookupInputField) getNewComponentInstance(LOOKUP_CRITERIA_FIELD);
1206        }
1207    
1208        /**
1209         * Gets a component instance for an input field for the active indicator
1210         * in the lookup criteria section
1211         * @return lookup input field instance
1212         */
1213        public static LookupInputField getLookupCriteriaActiveIndicatorInputField() {
1214            return (LookupInputField) getNewComponentInstance(LOOKUP_CRITERIA_ACTIVE_INDICATOR_FIELD);
1215        }
1216    }