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.service.impl;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.kuali.rice.core.api.config.property.ConfigurationService;
020 import org.kuali.rice.core.api.exception.RiceRuntimeException;
021 import org.kuali.rice.core.api.uif.DataType;
022 import org.kuali.rice.core.api.uif.RemotableAbstractControl;
023 import org.kuali.rice.core.api.uif.RemotableAbstractWidget;
024 import org.kuali.rice.core.api.uif.RemotableAttributeField;
025 import org.kuali.rice.core.api.uif.RemotableCheckbox;
026 import org.kuali.rice.core.api.uif.RemotableCheckboxGroup;
027 import org.kuali.rice.core.api.uif.RemotableHiddenInput;
028 import org.kuali.rice.core.api.uif.RemotableQuickFinder;
029 import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup;
030 import org.kuali.rice.core.api.uif.RemotableSelect;
031 import org.kuali.rice.core.api.uif.RemotableTextInput;
032 import org.kuali.rice.core.api.uif.RemotableTextarea;
033 import org.kuali.rice.core.api.util.KeyValue;
034 import org.kuali.rice.krad.bo.BusinessObject;
035 import org.kuali.rice.krad.bo.DataObjectRelationship;
036 import org.kuali.rice.krad.datadictionary.AttributeDefinition;
037 import org.kuali.rice.krad.service.DataDictionaryRemoteFieldService;
038 import org.kuali.rice.krad.service.DataDictionaryService;
039 import org.kuali.rice.krad.service.DataObjectMetaDataService;
040 import org.kuali.rice.krad.service.KRADServiceLocator;
041 import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
042 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
043 import org.kuali.rice.krad.uif.control.CheckboxControl;
044 import org.kuali.rice.krad.uif.control.CheckboxGroupControl;
045 import org.kuali.rice.krad.uif.control.Control;
046 import org.kuali.rice.krad.uif.control.GroupControl;
047 import org.kuali.rice.krad.uif.control.HiddenControl;
048 import org.kuali.rice.krad.uif.control.MultiValueControl;
049 import org.kuali.rice.krad.uif.control.RadioGroupControl;
050 import org.kuali.rice.krad.uif.control.SelectControl;
051 import org.kuali.rice.krad.uif.control.TextAreaControl;
052 import org.kuali.rice.krad.uif.control.TextControl;
053 import org.kuali.rice.krad.uif.control.UserControl;
054 import org.kuali.rice.krad.util.KRADConstants;
055 import org.kuali.rice.krad.workflow.service.WorkflowAttributePropertyResolutionService;
056
057 import java.util.Collections;
058 import java.util.HashMap;
059 import java.util.List;
060 import java.util.Map;
061
062 /**
063 * Implementation of the {@link DataDictionaryRemoteFieldService} service
064 *
065 * @author Kuali Rice Team (rice.collab@kuali.org)
066 */
067 public class DataDictionaryRemoteFieldServiceImpl implements DataDictionaryRemoteFieldService {
068
069 /**
070 * @see org.kuali.rice.krad.service.DataDictionaryRemoteFieldService#buildRemotableFieldFromAttributeDefinition(java.lang.String,
071 * java.lang.String)
072 */
073 public RemotableAttributeField buildRemotableFieldFromAttributeDefinition(String componentClassName,
074 String attributeName) {
075 AttributeDefinition baseDefinition;
076 Class<?> componentClass;
077 // try to resolve the component name - if not possible - try to pull the definition from the app mediation service
078 try {
079 componentClass = (Class<? extends BusinessObject>) Class.forName(componentClassName);
080 baseDefinition = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(componentClassName)
081 .getAttributeDefinition(attributeName);
082 } catch (ClassNotFoundException ex) {
083 throw new RiceRuntimeException("Unable to find attribute definition for attribute : " + attributeName);
084 }
085
086 RemotableAttributeField.Builder definition = RemotableAttributeField.Builder.create(baseDefinition.getName());
087
088 definition.setLongLabel(baseDefinition.getLabel());
089 definition.setShortLabel(baseDefinition.getShortLabel());
090 definition.setMaxLength(baseDefinition.getMaxLength());
091
092 if (baseDefinition.isRequired() != null) {
093 definition.setRequired(baseDefinition.isRequired());
094 }
095
096 definition.setForceUpperCase(baseDefinition.getForceUppercase());
097 //set the datatype - needed for successful custom doc searches
098 WorkflowAttributePropertyResolutionService propertyResolutionService = KRADServiceLocatorInternal
099 .getWorkflowAttributePropertyResolutionService();
100 String dataType = propertyResolutionService.determineFieldDataType(
101 (Class<? extends BusinessObject>) componentClass, attributeName);
102 definition.setDataType(DataType.valueOf(dataType.toUpperCase()));
103 RemotableAbstractControl.Builder control = createControl(baseDefinition);
104 if (control != null) {
105 definition.setControl(control);
106 }
107
108 RemotableQuickFinder.Builder qf = createQuickFinder(componentClass, attributeName);
109 if (qf != null) {
110 definition.setWidgets(Collections.<RemotableAbstractWidget.Builder>singletonList(qf));
111 }
112
113 return definition.build();
114 }
115
116 /**
117 * Creates a {@link RemotableAbstractControl} instance based on the control definition within the given
118 * attribute definition
119 *
120 * @param attr - attribute definition instance to pull control from
121 * @return RemotableAbstractControl instance or null if one could not be built
122 */
123 protected RemotableAbstractControl.Builder createControl(AttributeDefinition attr) {
124 Control control = attr.getControlField();
125
126 if (control != null) {
127 if (control instanceof CheckboxControl) {
128 return RemotableCheckbox.Builder.create();
129 } else if (control instanceof CheckboxGroupControl) {
130 return RemotableCheckboxGroup.Builder.create(getValues(attr));
131 } else if (control instanceof HiddenControl) {
132 return RemotableHiddenInput.Builder.create();
133 } else if (control instanceof SelectControl) {
134 RemotableSelect.Builder b = RemotableSelect.Builder.create(getValues(attr));
135 b.setMultiple(((SelectControl) control).isMultiple());
136 b.setSize(((SelectControl) control).getSize());
137 } else if (control instanceof RadioGroupControl) {
138 return RemotableRadioButtonGroup.Builder.create(getValues(attr));
139 } else if (control instanceof TextControl) {
140 final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
141 b.setSize(((TextControl) control).getSize());
142 return b;
143 } else if (control instanceof UserControl) {
144 final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
145 b.setSize(((UserControl) control).getSize());
146 return b;
147 } else if (control instanceof GroupControl) {
148 final RemotableTextInput.Builder b = RemotableTextInput.Builder.create();
149 b.setSize(((GroupControl) control).getSize());
150 return b;
151 } else if (control instanceof TextAreaControl) {
152 final RemotableTextarea.Builder b = RemotableTextarea.Builder.create();
153 b.setCols(((TextAreaControl) control).getCols());
154 b.setRows(((TextAreaControl) control).getRows());
155 return b;
156 }
157 }
158
159 return null;
160 }
161
162 /**
163 * Will first try to retrieve options configured on the control. If that doesn't return any values then will
164 * try to use the optionfinder on the AttributeDefinition.
165 *
166 * @param attr - AttributeDefinition
167 * @return Map of key value pairs
168 */
169 protected Map<String, String> getValues(AttributeDefinition attr) {
170 Control control = attr.getControlField();
171
172 if ((control instanceof MultiValueControl)
173 && (((MultiValueControl) control).getOptions() != null)
174 && !((MultiValueControl) control).getOptions().isEmpty()) {
175 List<KeyValue> keyValues = ((MultiValueControl) control).getOptions();
176 Map<String, String> options = new HashMap<String, String> ();
177 for (KeyValue keyValue : keyValues) {
178 options.put(keyValue.getKey(), keyValue.getValue());
179 }
180 return options;
181 } else if (attr.getOptionsFinder() != null) {
182 return attr.getOptionsFinder().getKeyLabelMap();
183 }
184
185 return Collections.emptyMap();
186 }
187
188 /**
189 * Builds a {@link RemotableQuickFinder} instance for the given attribute based on determined relationships
190 *
191 * <p>
192 * Uses the {@link DataObjectMetaDataService} to find relationships the given attribute participates in within the
193 * given class. If a relationship is not found, the title attribute is also checked to determine if a lookup should
194 * be rendered back to the component class itself. If a relationship suitable for lookup is found, the associated
195 * field conversions and lookup parameters are built
196 * </p>
197 *
198 * @param componentClass - class that attribute belongs to and should be checked for relationships
199 * @param attributeName - name of the attribute to determine quickfinder for
200 * @return RemotableQuickFinder.Builder instance for the configured lookup, or null if one could not be found
201 */
202 protected RemotableQuickFinder.Builder createQuickFinder(Class<?> componentClass, String attributeName) {
203 Object sampleComponent;
204 try {
205 sampleComponent = componentClass.newInstance();
206 } catch (InstantiationException e) {
207 throw new RiceRuntimeException(e);
208 } catch (IllegalAccessException e) {
209 throw new RiceRuntimeException(e);
210 }
211
212 String lookupClassName = null;
213 Map<String, String> fieldConversions = new HashMap<String, String>();
214 Map<String, String> lookupParameters = new HashMap<String, String>();
215
216 DataObjectRelationship relationship = getDataObjectMetaDataService().getDataObjectRelationship(sampleComponent,
217 componentClass, attributeName, "", true, true, false);
218 if (relationship != null) {
219 lookupClassName = relationship.getRelatedClass().getName();
220
221 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
222 String fromField = entry.getValue();
223 String toField = entry.getKey();
224 fieldConversions.put(fromField, toField);
225 }
226
227 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
228 String fromField = entry.getKey();
229 String toField = entry.getValue();
230
231 if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey()
232 .equals(fromField)) {
233 lookupParameters.put(fromField, toField);
234 }
235 }
236 } else {
237 // check for title attribute and if match build lookup to component class using pk fields
238 String titleAttribute = getDataObjectMetaDataService().getTitleAttribute(componentClass);
239 if (StringUtils.equals(titleAttribute, attributeName)) {
240 lookupClassName = componentClass.getName();
241
242 List<String> pkAttributes = getDataObjectMetaDataService().listPrimaryKeyFieldNames(componentClass);
243 for (String pkAttribute : pkAttributes) {
244 fieldConversions.put(pkAttribute, pkAttribute);
245 if (!StringUtils.equals(pkAttribute, attributeName)) {
246 lookupParameters.put(pkAttribute, pkAttribute);
247 }
248 }
249 }
250 }
251
252 if (StringUtils.isNotBlank(lookupClassName)) {
253 String baseUrl = getKualiConfigurationService().getPropertyValueAsString(KRADConstants.KRAD_LOOKUP_URL_KEY);
254 RemotableQuickFinder.Builder builder = RemotableQuickFinder.Builder.create(baseUrl, lookupClassName);
255 builder.setLookupParameters(lookupParameters);
256 builder.setFieldConversions(fieldConversions);
257
258 return builder;
259 }
260
261 return null;
262 }
263
264 protected DataDictionaryService getDataDictionaryService() {
265 return KRADServiceLocatorWeb.getDataDictionaryService();
266 }
267
268 protected DataObjectMetaDataService getDataObjectMetaDataService() {
269 return KRADServiceLocatorWeb.getDataObjectMetaDataService();
270 }
271
272 protected ConfigurationService getKualiConfigurationService() {
273 return KRADServiceLocator.getKualiConfigurationService();
274 }
275 }