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 }