001/**
002 * Copyright 2005-2018 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 */
016package org.kuali.rice.kns.kim.type;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.CoreApiServiceLocator;
020import org.kuali.rice.core.api.uif.RemotableControlContract;
021import org.kuali.rice.core.api.uif.RemotableAbstractControl;
022import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
023import org.kuali.rice.core.api.uif.RemotableAttributeField;
024import org.kuali.rice.core.api.uif.RemotableCheckbox;
025import org.kuali.rice.core.api.uif.RemotableCheckboxGroup;
026import org.kuali.rice.core.api.uif.RemotableDatepicker;
027import org.kuali.rice.core.api.uif.RemotableHiddenInput;
028import org.kuali.rice.core.api.uif.RemotableQuickFinder;
029import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup;
030import org.kuali.rice.core.api.uif.RemotableSelect;
031import org.kuali.rice.core.api.uif.RemotableTextExpand;
032import org.kuali.rice.core.api.uif.RemotableTextInput;
033import org.kuali.rice.core.api.uif.RemotableTextarea;
034import org.kuali.rice.kim.api.KimConstants;
035import org.kuali.rice.kim.api.type.KimAttributeField;
036import org.kuali.rice.kns.datadictionary.control.CheckboxControlDefinition;
037import org.kuali.rice.kns.datadictionary.control.HiddenControlDefinition;
038import org.kuali.rice.kns.datadictionary.control.MultiselectControlDefinition;
039import org.kuali.rice.kns.datadictionary.control.RadioControlDefinition;
040import org.kuali.rice.kns.datadictionary.control.SelectControlDefinition;
041import org.kuali.rice.kns.datadictionary.control.TextControlDefinition;
042import org.kuali.rice.kns.datadictionary.control.TextareaControlDefinition;
043import org.kuali.rice.krad.datadictionary.AttributeDefinition;
044import org.kuali.rice.krad.datadictionary.control.ControlDefinition;
045import org.kuali.rice.krad.datadictionary.exporter.ExportMap;
046import org.kuali.rice.krad.datadictionary.validation.ValidationPattern;
047import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
048import org.kuali.rice.krad.keyvalues.KeyValuesFinderFactory;
049import org.kuali.rice.krad.util.ObjectUtils;
050
051import java.util.ArrayList;
052import java.util.Collection;
053import java.util.Collections;
054import java.util.List;
055import java.util.Map;
056import java.util.regex.Pattern;
057
058/**
059 * @deprecated A krad integrated type service base class will be provided in the future.
060 * This is only used for the legacy {@link DataDictionaryTypeServiceBase}.
061 */
062@Deprecated
063public final class DataDictionaryTypeServiceHelper {
064
065    private DataDictionaryTypeServiceHelper() {
066        throw new UnsupportedOperationException("do not call");
067    }
068
069    public static String getKimBasePath(){
070            String kimBaseUrl = CoreApiServiceLocator.getKualiConfigurationService().getPropertyValueAsString(KimConstants.KimUIConstants.KIM_URL_KEY);
071            if (!kimBaseUrl.endsWith(KimConstants.KimUIConstants.URL_SEPARATOR)) {
072                    kimBaseUrl = kimBaseUrl + KimConstants.KimUIConstants.URL_SEPARATOR;
073            }
074            return kimBaseUrl;
075        }
076
077    public static RemotableAbstractControl.Builder toRemotableAbstractControlBuilder(AttributeDefinition attr) {
078        ControlDefinition control = attr.getControl();
079        if (ObjectUtils.isNotNull(control)) {
080            if (control.isCheckbox()) {
081                return RemotableCheckbox.Builder.create();
082            } else if (control.isHidden()) {
083                return RemotableHiddenInput.Builder.create();
084            } else if (control.isMultiselect()) {
085                RemotableSelect.Builder b = RemotableSelect.Builder.create(getValues(attr));
086                b.setMultiple(true);
087                b.setSize(control.getSize());
088            } else if (control.isRadio()) {
089                return RemotableRadioButtonGroup.Builder.create(getValues(attr));
090            } else if (control.isSelect()) {
091                RemotableSelect.Builder b = RemotableSelect.Builder.create(getValues(attr));
092                b.setMultiple(false);
093                b.setSize(control.getSize());
094                return b;
095            } else if (control.isText() || control.isCurrency()) {
096                final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
097                b.setSize(control.getSize());
098                return b;
099            } else if (control.isTextarea()) {
100                final RemotableTextarea.Builder b = RemotableTextarea.Builder.create();
101                b.setCols(control.getCols());
102                b.setRows(control.getRows());
103                return b;
104            }
105        }
106        return null;
107    }
108
109    /**
110     * will first try to execute the values finder.  If that doesn't return any values then will try to use the optionfinder
111     * on the AttributeDefinition.
112     *
113     * @param attr AttributeDefinition
114     * @return a Map of key value pairs
115     */
116    private static Map<String, String> getValues(AttributeDefinition attr) {
117        ControlDefinition control = attr.getControl();
118
119        try {
120            final Class<KeyValuesFinder> clazz = (Class<KeyValuesFinder>) Class.forName(control.getValuesFinderClass());
121            final KeyValuesFinder finder = clazz.newInstance();
122            final Map<String, String> values = finder.getKeyLabelMap();
123            if ((values != null) && !values.isEmpty()) {
124               return values;
125            }
126            else if (attr.getOptionsFinder() != null) {
127                return attr.getOptionsFinder().getKeyLabelMap();
128            }
129        } catch (ClassNotFoundException e) {
130            throw new RuntimeException(e);
131        } catch (InstantiationException e) {
132            throw new RuntimeException(e);
133        } catch (IllegalAccessException e) {
134            throw new RuntimeException(e);
135        }
136        return Collections.emptyMap();
137    }
138
139    public static List<KimAttributeDefinition> toKimAttributeDefinitions(List<KimAttributeField> fields) {
140        if (fields == null) {
141            throw new IllegalArgumentException("fields was null");
142        }
143
144        final List<KimAttributeDefinition> defns = new ArrayList<KimAttributeDefinition>();
145        for (KimAttributeField field : fields) {
146            defns.add(toKimAttributeDefinition(field));
147        }
148
149        return Collections.unmodifiableList(defns);
150    }
151
152    public static KimAttributeDefinition toKimAttributeDefinition(KimAttributeField field) {
153        if (field == null) {
154            throw new IllegalArgumentException("field is null");
155        }
156
157        KimAttributeDefinition ad = new KimAttributeDefinition();
158        ad.setKimAttrDefnId(field.getId());
159        ad.setUnique(field.isUnique());
160
161        final RemotableAttributeField attr = field.getAttributeField();
162        ad.setName(attr.getName());
163        ad.setDataType(attr.getDataType());
164        ad.setShortLabel(attr.getShortLabel());
165        ad.setLabel(attr.getLongLabel());
166        ad.setSummary(attr.getHelpSummary());
167        ad.setConstraintText(attr.getConstraintText());
168        ad.setDescription(attr.getHelpDescription());
169        ad.setForceUppercase(attr.isForceUpperCase());
170        ad.setMinLength(attr.getMinLength());
171        ad.setMaxLength(attr.getMaxLength());
172        ad.setExclusiveMin(attr.getMinValue() != null ? attr.getMinValue().toString() : null);
173        ad.setInclusiveMax(attr.getMaxValue() != null ? attr.getMaxValue().toString() : null);
174        if (StringUtils.isNotBlank(attr.getRegexConstraint())) {
175            ValidationPattern pattern = new ValidationPattern() {
176
177                @Override
178                public Pattern getRegexPattern() {
179                    return Pattern.compile(getRegexString());
180                }
181
182                @Override
183                protected String getRegexString() {
184                    return attr.getRegexConstraint();
185                }
186
187                @Override
188                public ExportMap buildExportMap(String exportKey) {
189                    ExportMap exportMap = new ExportMap(exportKey);
190                    exportMap.set("type", "regex");
191                    exportMap.set("pattern", getRegexString());
192
193                    return exportMap;
194                }
195
196                @Override
197                public String getValidationErrorMessageKey() {
198                    return attr.getRegexContraintMsg();
199                }
200            };
201            ad.setValidationPattern(pattern);
202        }
203        ad.setRequired(attr.isRequired());
204
205        final RemotableControlContract control = field.getAttributeField().getControl();
206
207        if (control != null) {
208            ControlDefinition d = toControlDefinition(control, ad);
209            for (RemotableAbstractWidget widget : field.getAttributeField().getWidgets()) {
210                if(widget instanceof RemotableQuickFinder) {
211                    ad.setLookupBoClass(((RemotableQuickFinder) widget).getDataObjectClass());
212                    ad.setLookupInputPropertyConversions(((RemotableQuickFinder) widget).getLookupParameters());
213                    ad.setLookupReturnPropertyConversions(((RemotableQuickFinder) widget).getFieldConversions());
214                } else if (widget instanceof RemotableDatepicker && d != null) {
215                    d.setDatePicker(true);
216                } else if (widget instanceof RemotableTextExpand && d != null) {
217                    d.setExpandedTextArea(true);
218                }
219            }
220            ad.setControl(d);
221        }
222
223        return ad;
224    }
225
226    /** WARNING HACK! this may set the OptionsFinder instance on the passed in KimAttributeDefinition! */
227    private static ControlDefinition toControlDefinition(RemotableControlContract control, KimAttributeDefinition containingAttribute) {
228        if (control instanceof RemotableCheckboxGroup) {
229            containingAttribute.setOptionsFinder(KeyValuesFinderFactory.fromMap(((RemotableCheckboxGroup) control).getKeyLabels()));
230            CheckboxControlDefinition checkbox = new CheckboxControlDefinition();
231            return checkbox;
232        } else if (control instanceof RemotableCheckbox) {
233            CheckboxControlDefinition checkbox = new CheckboxControlDefinition();
234            return checkbox;
235        } else if (control instanceof RemotableHiddenInput) {
236            HiddenControlDefinition hidden = new HiddenControlDefinition();
237            return hidden;
238        } else if (control instanceof RemotableRadioButtonGroup) {
239            containingAttribute.setOptionsFinder(KeyValuesFinderFactory.fromMap(((RemotableRadioButtonGroup) control).getKeyLabels()));
240            RadioControlDefinition radio = new RadioControlDefinition();
241            return radio;
242
243        } else if (control instanceof RemotableSelect) {
244            containingAttribute.setOptionsFinder(KeyValuesFinderFactory.fromMap(((RemotableSelect) control).getKeyLabels()));
245            if (((RemotableSelect) control).isMultiple()) {
246                MultiselectControlDefinition multiSelect = new MultiselectControlDefinition();
247                multiSelect.setSize(((RemotableSelect) control).getSize());
248                return multiSelect;
249            } else {
250                SelectControlDefinition select = new SelectControlDefinition();
251                select.setSize(((RemotableSelect) control).getSize());
252                return select;
253            }
254        } else if (control instanceof RemotableTextarea) {
255            TextareaControlDefinition textarea = new TextareaControlDefinition();
256            textarea.setRows(((RemotableTextarea) control).getRows());
257            textarea.setCols(((RemotableTextarea) control).getCols());
258            return textarea;
259        } else if (control instanceof RemotableTextInput) {
260            TextControlDefinition text = new TextControlDefinition();
261            text.setSize(((RemotableTextInput) control).getSize());
262            return text;
263        }
264        return null;
265    }
266
267        /**
268     * Utility method to search a collection of attribute fields and returns
269     * a field for a give attribute name.
270     *
271     * @param attributeName the name of the attribute to search for.  Cannot be blank or null.
272     * @param fields cannot be null.
273     *
274     * @return the attribute field or null if not found.
275     */
276    public static <T extends KimAttributeField> T findAttributeField(String attributeName, Collection<? extends T> fields) {
277        if (StringUtils.isBlank(attributeName)) {
278            throw new IllegalArgumentException("attributeName is blank");
279        }
280
281        if (fields == null) {
282            throw new IllegalArgumentException("fields is null");
283        }
284
285        for (T field : fields) {
286            if (attributeName.equals(field.getAttributeField().getName())) {
287                return field;
288            }
289        }
290        return null;
291    }
292
293    public static String createErrorString(KimAttributeField definition) {
294        return definition.getAttributeField().getRegexContraintMsg();
295    }
296
297     /** will create a string like the following:
298     * errorKey:param1;param2;param3;
299     *
300     * @param errorKey the errorKey
301     * @param params the error params
302     * @return error string
303     */
304    public static String createErrorString(String errorKey, String... params) {
305        final StringBuilder s = new StringBuilder(errorKey).append(':');
306        if (params != null) {
307            for (String p : params) {
308                if (p != null) {
309                    s.append(p);
310                    s.append(';');
311                }
312            }
313        }
314        return s.toString();
315    }
316
317    public static String getAttributeErrorLabel(KimAttributeField definition) {
318        String longAttributeLabel = definition.getAttributeField().getLongLabel();
319        String shortAttributeLabel = definition.getAttributeField().getShortLabel();
320        return longAttributeLabel + " (" + shortAttributeLabel + ")";
321    }
322}