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