/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.sys.document.workflow; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.core.api.exception.RiceRuntimeException; import org.kuali.rice.core.api.uif.DataType; import org.kuali.rice.core.api.uif.RemotableAbstractControl; import org.kuali.rice.core.api.uif.RemotableAbstractWidget; import org.kuali.rice.core.api.uif.RemotableAttributeField; import org.kuali.rice.core.api.uif.RemotableCheckbox; import org.kuali.rice.core.api.uif.RemotableHiddenInput; import org.kuali.rice.core.api.uif.RemotableQuickFinder; import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup; import org.kuali.rice.core.api.uif.RemotableSelect; import org.kuali.rice.core.api.uif.RemotableTextInput; import org.kuali.rice.core.api.uif.RemotableTextarea; import org.kuali.rice.kns.datadictionary.control.CheckboxControlDefinition; import org.kuali.rice.kns.datadictionary.control.HiddenControlDefinition; import org.kuali.rice.kns.datadictionary.control.MultivalueControlDefinitionBase; import org.kuali.rice.kns.datadictionary.control.RadioControlDefinition; import org.kuali.rice.kns.datadictionary.control.SelectControlDefinition; import org.kuali.rice.kns.datadictionary.control.TextControlDefinition; import org.kuali.rice.kns.datadictionary.control.TextareaControlDefinition; import org.kuali.rice.krad.bo.BusinessObject; import org.kuali.rice.krad.bo.DataObjectRelationship; import org.kuali.rice.krad.datadictionary.AttributeDefinition; import org.kuali.rice.krad.datadictionary.control.ControlDefinition; import org.kuali.rice.krad.keyvalues.KeyValuesFinder; import org.kuali.rice.krad.service.DataDictionaryService; import org.kuali.rice.krad.service.DataObjectMetaDataService; import org.kuali.rice.krad.service.KRADServiceLocator; import org.kuali.rice.krad.service.KRADServiceLocatorInternal; import org.kuali.rice.krad.service.KRADServiceLocatorWeb; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.workflow.service.WorkflowAttributePropertyResolutionService; //RICE20 This class is a temporary fix to support KNS attribute definitions. Should be deleted when rice2.0 adds support. public class DataDictionaryRemoteFieldBuilder { private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DataDictionaryRemoteFieldBuilder.class); /** * @see org.kuali.rice.krad.service.DataDictionaryRemoteFieldService#buildRemotableFieldFromAttributeDefinition(java.lang.String, * java.lang.String) */ public RemotableAttributeField buildRemotableFieldFromAttributeDefinition(String componentClassName, String attributeName) { AttributeDefinition baseDefinition; Class<?> componentClass; // try to resolve the component name - if not possible - try to pull the definition from the app mediation service try { componentClass = (Class<? extends BusinessObject>) Class.forName(componentClassName); baseDefinition = getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(componentClassName).getAttributeDefinition(attributeName); } catch (ClassNotFoundException ex) { throw new RiceRuntimeException("Unable to find attribute definition for attribute : " + attributeName); } RemotableAttributeField.Builder definition = RemotableAttributeField.Builder.create(baseDefinition.getName()); definition.setLongLabel(baseDefinition.getLabel()); definition.setShortLabel(baseDefinition.getShortLabel()); definition.setMaxLength(baseDefinition.getMaxLength()); definition.setRequired(baseDefinition.isRequired()); definition.setForceUpperCase(baseDefinition.getForceUppercase()); // set the datatype - needed for successful custom doc searches WorkflowAttributePropertyResolutionService propertyResolutionService = KRADServiceLocatorInternal.getWorkflowAttributePropertyResolutionService(); String dataType = propertyResolutionService.determineFieldDataType((Class<? extends BusinessObject>) componentClass, attributeName); definition.setDataType(DataType.valueOf(dataType.toUpperCase())); RemotableAbstractControl.Builder control = createControl(baseDefinition); if (control != null) { definition.setControl(control); } try { RemotableQuickFinder.Builder qf = createQuickFinder(componentClass, attributeName); if (qf != null) { definition.setWidgets(Collections.<RemotableAbstractWidget.Builder> singletonList(qf)); } } catch ( Exception ex ) { LOG.warn(ex); } return definition.build(); } /** * Creates a {@link RemotableAbstractControl} instance based on the control definition within the given attribute definition * * @param attr - attribute definition instance to pull control from * @return RemotableAbstractControl instance or null if one could not be built */ protected RemotableAbstractControl.Builder createControl(AttributeDefinition attr) { ControlDefinition control = attr.getControl(); if (control != null) { if (control instanceof CheckboxControlDefinition) { return RemotableCheckbox.Builder.create(); } // else if (control instanceof CheckboxGroupControl) { // return RemotableCheckboxGroup.Builder.create(getValues(attr)); // } else if (control instanceof HiddenControlDefinition) { return RemotableHiddenInput.Builder.create(); } else if (control instanceof SelectControlDefinition) { RemotableSelect.Builder b = RemotableSelect.Builder.create(getValues(attr)); b.setMultiple(((SelectControlDefinition) control).isMultiselect()); b.setSize(((SelectControlDefinition) control).getSize()); } else if (control instanceof RadioControlDefinition) { return RemotableRadioButtonGroup.Builder.create(getValues(attr)); } else if (control instanceof TextControlDefinition) { final RemotableTextInput.Builder b = RemotableTextInput.Builder.create(); b.setSize(((TextControlDefinition) control).getSize()); return b; } // else if (control instanceof UserControl) { // final RemotableTextInput.Builder b = RemotableTextInput.Builder.create(); // b.setSize(((UserControl) control).getSize()); // return b; // } // else if (control instanceof GroupControl) { // final RemotableTextInput.Builder b = RemotableTextInput.Builder.create(); // b.setSize(((GroupControl) control).getSize()); // return b; // } else if (control instanceof TextareaControlDefinition) { final RemotableTextarea.Builder b = RemotableTextarea.Builder.create(); b.setCols(((TextareaControlDefinition) control).getCols()); b.setRows(((TextareaControlDefinition) control).getRows()); return b; } } return null; } /** * Will first try to retrieve options configured on the control. If that doesn't return any values then will try to use the * optionfinder on the AttributeDefinition. * * @param attr - AttributeDefinition * @return Map of key value pairs */ protected Map<String, String> getValues(AttributeDefinition attr) { ControlDefinition control = attr.getControl(); if (MultivalueControlDefinitionBase.class.isAssignableFrom(control.getClass())) { String valuesFinderClass = ((MultivalueControlDefinitionBase) control).getValuesFinderClass(); KeyValuesFinder finder; try { finder = (KeyValuesFinder) Class.forName(valuesFinderClass).newInstance(); } catch (Exception ex) { throw new RuntimeException("", ex); } Map<String, String> options = finder.getKeyLabelMap(); return options; } else if (attr.getOptionsFinder() != null) { return attr.getOptionsFinder().getKeyLabelMap(); } return Collections.emptyMap(); } /** * Builds a {@link RemotableQuickFinder} instance for the given attribute based on determined relationships * <p> * Uses the {@link DataObjectMetaDataService} to find relationships the given attribute participates in within the given class. * If a relationship is not found, the title attribute is also checked to determine if a lookup should be rendered back to the * component class itself. If a relationship suitable for lookup is found, the associated field conversions and lookup * parameters are built * </p> * * @param componentClass - class that attribute belongs to and should be checked for relationships * @param attributeName - name of the attribute to determine quickfinder for * @return RemotableQuickFinder.Builder instance for the configured lookup, or null if one could not be found */ protected RemotableQuickFinder.Builder createQuickFinder(Class<?> componentClass, String attributeName) { Object sampleComponent; try { sampleComponent = componentClass.newInstance(); } catch (InstantiationException e) { throw new RiceRuntimeException(e); } catch (IllegalAccessException e) { throw new RiceRuntimeException(e); } String lookupClassName = null; Map<String, String> fieldConversions = new HashMap<String, String>(); Map<String, String> lookupParameters = new HashMap<String, String>(); DataObjectRelationship relationship = getDataObjectMetaDataService().getDataObjectRelationship(sampleComponent, componentClass, attributeName, "", true, true, false); if (relationship != null) { lookupClassName = relationship.getRelatedClass().getName(); for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) { String fromField = entry.getValue(); String toField = entry.getKey(); fieldConversions.put(fromField, toField); } for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) { String fromField = entry.getKey(); String toField = entry.getValue(); if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey().equals(fromField)) { lookupParameters.put(fromField, toField); } } } else { Map foreignKeysForReference = KRADServiceLocator.getPersistenceStructureService().getForeignKeysForReference(componentClass, attributeName); // check for title attribute and if match build lookup to component class using pk fields String titleAttribute = getDataObjectMetaDataService().getTitleAttribute(componentClass); if (StringUtils.equals(titleAttribute, attributeName)) { lookupClassName = componentClass.getName(); List<String> pkAttributes = getDataObjectMetaDataService().listPrimaryKeyFieldNames(componentClass); for (String pkAttribute : pkAttributes) { fieldConversions.put(pkAttribute, pkAttribute); if (!StringUtils.equals(pkAttribute, attributeName)) { lookupParameters.put(pkAttribute, pkAttribute); } } } } if (StringUtils.isNotBlank(lookupClassName)) { String baseUrl = getKualiConfigurationService().getPropertyValueAsString(KRADConstants.KRAD_LOOKUP_URL_KEY); RemotableQuickFinder.Builder builder = RemotableQuickFinder.Builder.create(baseUrl, lookupClassName); builder.setLookupParameters(lookupParameters); builder.setFieldConversions(fieldConversions); return builder; } return null; } protected DataDictionaryService getDataDictionaryService() { return KRADServiceLocatorWeb.getDataDictionaryService(); } protected DataObjectMetaDataService getDataObjectMetaDataService() { return KRADServiceLocatorWeb.getDataObjectMetaDataService(); } protected ConfigurationService getKualiConfigurationService() { return KRADServiceLocator.getKualiConfigurationService(); } }