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}