001/**
002 * Copyright 2005-2016 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.krad.service.impl;
017
018import org.kuali.rice.core.api.CoreApiServiceLocator;
019import org.kuali.rice.core.api.config.property.ConfigurationService;
020import org.kuali.rice.core.api.data.DataType;
021import org.kuali.rice.core.api.exception.RiceRuntimeException;
022import org.kuali.rice.core.api.uif.RemotableAbstractControl;
023import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
024import org.kuali.rice.core.api.uif.RemotableAttributeField;
025import org.kuali.rice.core.api.uif.RemotableCheckbox;
026import org.kuali.rice.core.api.uif.RemotableCheckboxGroup;
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.RemotableTextInput;
032import org.kuali.rice.core.api.uif.RemotableTextarea;
033import org.kuali.rice.core.api.util.KeyValue;
034import org.kuali.rice.krad.bo.BusinessObject;
035import org.kuali.rice.krad.datadictionary.AttributeDefinition;
036import org.kuali.rice.krad.service.DataDictionaryRemoteFieldService;
037import org.kuali.rice.krad.service.DataDictionaryService;
038import org.kuali.rice.krad.service.DataObjectMetaDataService;
039import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
040import org.kuali.rice.krad.service.LegacyDataAdapter;
041import org.kuali.rice.krad.uif.control.CheckboxControl;
042import org.kuali.rice.krad.uif.control.CheckboxGroupControl;
043import org.kuali.rice.krad.uif.control.Control;
044import org.kuali.rice.krad.uif.control.GroupControl;
045import org.kuali.rice.krad.uif.control.HiddenControl;
046import org.kuali.rice.krad.uif.control.MultiValueControl;
047import org.kuali.rice.krad.uif.control.RadioGroupControl;
048import org.kuali.rice.krad.uif.control.SelectControl;
049import org.kuali.rice.krad.uif.control.TextAreaControl;
050import org.kuali.rice.krad.uif.control.TextControl;
051import org.kuali.rice.krad.uif.control.UserControl;
052import org.kuali.rice.krad.util.DataTypeUtil;
053
054import java.util.Collections;
055import java.util.HashMap;
056import java.util.List;
057import java.util.Map;
058
059/**
060 * Implementation of the {@link DataDictionaryRemoteFieldService} service
061 *
062 * @author Kuali Rice Team (rice.collab@kuali.org)
063 */
064public class DataDictionaryRemoteFieldServiceImpl implements DataDictionaryRemoteFieldService {
065
066    @Override
067    public RemotableAttributeField buildRemotableFieldFromAttributeDefinition(String componentClassName,
068            String attributeName) {
069        AttributeDefinition baseDefinition;
070        Class<?> componentClass;
071        // try to resolve the component name - if not possible - try to pull the definition from the app mediation service
072        try {
073            componentClass = Class.forName(componentClassName);
074            baseDefinition = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(componentClassName)
075                    .getAttributeDefinition(attributeName);
076        } catch (ClassNotFoundException ex) {
077            throw new RiceRuntimeException("Unable to find attribute definition for attribute : " + attributeName);
078        }
079
080        RemotableAttributeField.Builder definition = RemotableAttributeField.Builder.create(baseDefinition.getName());
081
082        definition.setLongLabel(baseDefinition.getLabel());
083        definition.setShortLabel(baseDefinition.getShortLabel());
084        definition.setMaxLength(baseDefinition.getMaxLength());
085
086        if (baseDefinition.isRequired() != null) {
087            definition.setRequired(baseDefinition.isRequired().booleanValue());
088        }
089
090        definition.setForceUpperCase(baseDefinition.getForceUppercase().booleanValue());
091
092        //set the datatype - needed for successful custom doc searches
093        String dataType = DataTypeUtil.determineFieldDataType((Class<? extends BusinessObject>) componentClass,
094                attributeName);
095        definition.setDataType(DataType.valueOf(dataType.toUpperCase()));
096
097        RemotableAbstractControl.Builder control = createControl(baseDefinition);
098        if (control != null) {
099            definition.setControl(control);
100        }
101
102        RemotableQuickFinder.Builder qf = createQuickFinder(componentClass, attributeName);
103        if (qf != null) {
104            definition.setWidgets(Collections.<RemotableAbstractWidget.Builder>singletonList(qf));
105        }
106
107        return definition.build();
108    }
109
110    /**
111     * Creates a {@link RemotableAbstractControl} instance based on the control definition within the given
112     * attribute definition
113     *
114     * @param attr - attribute definition instance to pull control from
115     * @return RemotableAbstractControl instance or null if one could not be built
116     */
117    protected RemotableAbstractControl.Builder createControl(AttributeDefinition attr) {
118        Control control = attr.getControlField();
119
120        if (control != null) {
121            if (control instanceof CheckboxControl) {
122                return RemotableCheckbox.Builder.create();
123            } else if (control instanceof CheckboxGroupControl) {
124                return RemotableCheckboxGroup.Builder.create(getValues(attr));
125            } else if (control instanceof HiddenControl) {
126                return RemotableHiddenInput.Builder.create();
127            } else if (control instanceof SelectControl) {
128                RemotableSelect.Builder b = RemotableSelect.Builder.create(getValues(attr));
129                b.setMultiple(((SelectControl) control).isMultiple());
130                b.setSize(((SelectControl) control).getSize());
131            } else if (control instanceof RadioGroupControl) {
132                return RemotableRadioButtonGroup.Builder.create(getValues(attr));
133            } else if (control instanceof TextControl) {
134                final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
135                b.setSize(((TextControl) control).getSize());
136                return b;
137            } else if (control instanceof UserControl) {
138                final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
139                b.setSize(((UserControl) control).getSize());
140                return b;
141            } else if (control instanceof GroupControl) {
142                final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
143                b.setSize(((GroupControl) control).getSize());
144                return b;
145            } else if (control instanceof TextAreaControl) {
146                final RemotableTextarea.Builder b = RemotableTextarea.Builder.create();
147                b.setCols(((TextAreaControl) control).getCols());
148                b.setRows(((TextAreaControl) control).getRows());
149                return b;
150            }
151        }
152        
153        return null;
154    }
155
156    /**
157     * Will first try to retrieve options configured on the control.  If that doesn't return any values then will
158     * try to use the optionfinder on the AttributeDefinition.
159     *
160     * @param attr - AttributeDefinition
161     * @return Map of key value pairs
162     */
163    protected Map<String, String> getValues(AttributeDefinition attr) {
164        Control control = attr.getControlField();
165
166        if ((control instanceof MultiValueControl)
167                && (((MultiValueControl) control).getOptions() != null)
168                && !((MultiValueControl) control).getOptions().isEmpty()) {
169            List<KeyValue> keyValues = ((MultiValueControl) control).getOptions();
170                    Map<String, String> options = new HashMap<String, String> ();
171                    for (KeyValue keyValue : keyValues) {
172                        options.put(keyValue.getKey(), keyValue.getValue());
173                    }
174                    return options;
175        } else if (attr.getOptionsFinder() != null) {
176            return attr.getOptionsFinder().getKeyLabelMap();
177        }
178
179        return Collections.emptyMap();
180    }
181
182    /**
183     * Builds a {@link RemotableQuickFinder} instance for the given attribute based on determined relationships
184     *
185     * <p>
186     * Uses the {@link DataObjectMetaDataService} to find relationships the given attribute participates in within the
187     * given class. If a relationship is not found, the title attribute is also checked to determine if a lookup should
188     * be rendered back to the component class itself. If a relationship suitable for lookup is found, the associated
189     * field conversions and lookup parameters are built
190     * </p>
191     *
192     * @param componentClass - class that attribute belongs to and should be checked for relationships
193     * @param attributeName - name of the attribute to determine quickfinder for
194     * @return RemotableQuickFinder.Builder instance for the configured lookup, or null if one could not be found
195     */
196    protected RemotableQuickFinder.Builder createQuickFinder(Class<?> componentClass, String attributeName) {
197        return getLegacyDataAdapter().createQuickFinder(componentClass, attributeName);
198    }
199
200    protected DataDictionaryService getDataDictionaryService() {
201        return KRADServiceLocatorWeb.getDataDictionaryService();
202    }
203
204    protected ConfigurationService getKualiConfigurationService() {
205        return CoreApiServiceLocator.getKualiConfigurationService();
206    }
207
208    protected LegacyDataAdapter getLegacyDataAdapter() {
209        return KRADServiceLocatorWeb.getLegacyDataAdapter();
210    }
211}