/* * 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.service.impl; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.servlet.jsp.PageContext; import org.apache.commons.beanutils.PropertyUtils; import org.kuali.kfs.sys.businessobject.AccountingLine; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.document.AccountingDocument; import org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentAuthorizerBase; import org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationController; import org.kuali.kfs.sys.document.datadictionary.AccountingLineGroupDefinition; import org.kuali.kfs.sys.document.datadictionary.AccountingLineViewFieldDefinition; import org.kuali.kfs.sys.document.service.AccountingLineAuthorizationTransformer; import org.kuali.kfs.sys.document.service.AccountingLineFieldRenderingTransformation; import org.kuali.kfs.sys.document.service.AccountingLineRenderingService; import org.kuali.kfs.sys.document.service.AccountingLineRenderingTransformation; import org.kuali.kfs.sys.document.service.AccountingLineTableTransformation; import org.kuali.kfs.sys.document.web.AccountingLineTableRow; import org.kuali.kfs.sys.document.web.TableJoining; import org.kuali.kfs.sys.document.web.renderers.CheckboxRenderer; import org.kuali.kfs.sys.document.web.renderers.CurrencyRenderer; import org.kuali.kfs.sys.document.web.renderers.DateRenderer; import org.kuali.kfs.sys.document.web.renderers.DropDownRenderer; import org.kuali.kfs.sys.document.web.renderers.FieldRenderer; import org.kuali.kfs.sys.document.web.renderers.HiddenRenderer; import org.kuali.kfs.sys.document.web.renderers.RadioButtonGroupRenderer; import org.kuali.kfs.sys.document.web.renderers.ReadOnlyRenderer; import org.kuali.kfs.sys.document.web.renderers.TextAreaRenderer; import org.kuali.kfs.sys.document.web.renderers.TextRenderer; import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentFormBase; import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition; import org.kuali.rice.kns.datadictionary.validation.fieldlevel.DateValidationPattern; import org.kuali.rice.kns.service.DataDictionaryService; import org.kuali.rice.kns.service.DocumentHelperService; import org.kuali.rice.kns.util.KNSGlobalVariables; import org.kuali.rice.kns.web.ui.Field; import org.kuali.rice.krad.datadictionary.AttributeDefinition; import org.kuali.rice.krad.datadictionary.validation.ValidationPattern; /** * The default implementation of the AccountingLineRenderingService */ public class AccountingLineRenderingServiceImpl implements AccountingLineRenderingService { protected final String KUALI_FORM_NAME = "KualiForm"; private List<AccountingLineFieldRenderingTransformation> fieldTransformations; private DataDictionaryService dataDictionaryService; private AccountingLineAuthorizationTransformer accountingLineAuthorizationTransformer; private List<AccountingLineRenderingTransformation> preTablificationTransformations; private List<AccountingLineTableTransformation> postTablificationTransformations; private DocumentHelperService documentHelperService; /** * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#performPreTablificationTransformations(java.util.List, org.kuali.kfs.sys.document.datadictionary.AccountingLineGroupDefinition, org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine, boolean, java.util.Map, java.lang.String) */ public void performPreTablificationTransformations(List<TableJoining> elements, AccountingLineGroupDefinition groupDefinition, AccountingDocument accountingDocument, AccountingLine accountingLine, boolean newLine, Map unconvertedValues, String accountingLinePropertyName) { performAuthorizationTransformations(elements, groupDefinition, accountingDocument, accountingLine, newLine, accountingLinePropertyName); performFieldTransformations(elements, accountingDocument, accountingLine, unconvertedValues); for (AccountingLineRenderingTransformation transformation : preTablificationTransformations) { transformation.transformElements(elements, accountingLine); } } /** * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#performPostTablificationTransformations(java.util.List, org.kuali.kfs.sys.document.datadictionary.AccountingLineGroupDefinition, org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine, boolean) */ public void performPostTablificationTransformations(List<AccountingLineTableRow> rows, AccountingLineGroupDefinition groupDefinition, AccountingDocument document, AccountingLine accountingLine, boolean newLine) { for (AccountingLineTableTransformation transformation : postTablificationTransformations) { transformation.transformRows(rows); } } /** * Performs the authorization transformations * @param elements the layout elements which we are authorizing * @param accountingLineGroupDefinition the data dictionary definition of the accounting line group * @param accountingDocument the accounting line document we're rendering accounting lines for * @param accountingLine the accounting line we're rendering * @param newLine true if the accounting line is not yet on the form yet, false otherwise */ protected void performAuthorizationTransformations(List<TableJoining> elements, AccountingLineGroupDefinition accountingLineGroupDefinition, AccountingDocument accountingDocument, AccountingLine accountingLine, boolean newLine, String accountingLinePropertyName) { accountingLineAuthorizationTransformer.transformElements(elements, accountingLine, accountingDocument, accountingLineGroupDefinition.getAccountingLineAuthorizer(), newLine, accountingLinePropertyName); } /** * Performs field transformations for pre-rendering * @param elements the layout elements that hold fields to transform * @param accountingDocument the accounting document with the line we are rendering * @param accountingLine the accounting line we are rendering * @param unconvertedValues any unconverted values */ protected void performFieldTransformations(List<TableJoining> elements, AccountingDocument accountingDocument, AccountingLine accountingLine, Map unconvertedValues) { for (TableJoining layoutElement : elements) { layoutElement.performFieldTransformations(fieldTransformations, accountingLine, unconvertedValues); } } /** * Creates an accounting document authorizer for the given accounting document * @param document the document to get an authorizer for * @return an authorizer for the document */ protected FinancialSystemTransactionalDocumentAuthorizerBase getDocumentAuthorizer(AccountingDocument document) { final FinancialSystemTransactionalDocumentAuthorizerBase authorizer = (FinancialSystemTransactionalDocumentAuthorizerBase) getDocumentHelperService().getDocumentAuthorizer(document); return authorizer; } /** * @param document the document to get the presentation controller for * @return the proper presentation controller */ protected FinancialSystemTransactionalDocumentPresentationController getPresentationController(AccountingDocument document) { final FinancialSystemTransactionalDocumentPresentationController presentationController = (FinancialSystemTransactionalDocumentPresentationController) getDocumentHelperService().getDocumentPresentationController(document); return presentationController; } /** * Simplify the tree so that it is made up of only table elements and fields * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#tablify(java.util.List) */ public List<AccountingLineTableRow> tablify(List<TableJoining> elements) { List<AccountingLineTableRow> rows = createBlankTableRows(getMaxRowCount(elements)); tablifyElements(elements, rows); return rows; } /** * Gets the maximum number of rows needed by any child element * @param elements the elements to turn into table rows * @return the maximum number of rows requested */ protected int getMaxRowCount(List<TableJoining> elements) { int maxRowCount = 0; for (TableJoining element : elements) { int rowCount = element.getRequestedRowCount(); if (rowCount > maxRowCount) { maxRowCount = rowCount; } } return maxRowCount; } /** * This method creates a List of blank table rows, based on the requested count * @param count the count of table rows * @return a List of table rows ready for population */ protected List<AccountingLineTableRow> createBlankTableRows(int count) { List<AccountingLineTableRow> rows = new ArrayList<AccountingLineTableRow>(); for (int i = 0; i < count; i++) { rows.add(new AccountingLineTableRow()); } return rows; } /** * Requests each of the given elements to join the table * @param elements the elements to join to the table * @param rows the table rows to join to */ protected void tablifyElements(List<TableJoining> elements, List<AccountingLineTableRow> rows) { for (TableJoining element : elements) { element.joinTable(rows); } } /** * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#getFieldRendererForField(org.kuali.rice.kns.web.ui.Field, org.kuali.kfs.sys.businessobject.AccountingLine) * * KRAD Conversion: Performs customization of the renderer based on the properties of the fields. */ public FieldRenderer getFieldRendererForField(Field field, AccountingLine accountingLineToRender) { FieldRenderer renderer = null; if (field.isReadOnly() || field.getFieldType().equals(Field.READONLY)) { renderer = new ReadOnlyRenderer(); } /* else if (field.getPropertyName().equals(KFSConstants.CHART_OF_ACCOUNTS_CODE_PROPERTY_NAME) && !SpringContext.getBean(AccountService.class).accountsCanCrossCharts()) { // the special case for rendering chart of accounts code when accounts can't cross charts renderer = new DynamicReadOnlyRender(); } */ else if (field.getFieldType().equals(Field.TEXT)) { if (field.isDatePicker() || usesDateValidation(field.getPropertyName(), accountingLineToRender)) { // are we a date? renderer = new DateRenderer(); } else { renderer = new TextRenderer(); } } else if (field.getFieldType().equals(Field.TEXT_AREA)) { renderer = new TextAreaRenderer(); } else if (field.getFieldType().equals(Field.HIDDEN)) { renderer = new HiddenRenderer(); } else if (field.getFieldType().equals(Field.CURRENCY)) { renderer = new CurrencyRenderer(); } else if (field.getFieldType().equals(Field.DROPDOWN)) { renderer = new DropDownRenderer(); } else if (field.getFieldType().equals(Field.RADIO)) { renderer = new RadioButtonGroupRenderer(); } else if (field.getFieldType().equals(Field.CHECKBOX)) { renderer = new CheckboxRenderer(); } return renderer; } /** * Determines if this method uses a date validation pattern, in which case, a date picker should be rendered * @param propertyName the property of the field being checked from the command line * @param accountingLineToRender the accounting line which is being rendered * @return true if the property does use date validation, false otherwise */ protected boolean usesDateValidation(String propertyName, Object businessObject) { final org.kuali.rice.krad.datadictionary.BusinessObjectEntry entry = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(businessObject.getClass().getName()); AttributeDefinition attributeDefinition = entry.getAttributeDefinition(propertyName); if (attributeDefinition == null) { if (!propertyName.contains(".")) return false; final int firstNestingPoint = propertyName.indexOf("."); final String toNestingPoint = propertyName.substring(0, firstNestingPoint); final String fromNestingPoint = propertyName.substring(firstNestingPoint+1); Object childObject = null; try { final Class childClass = PropertyUtils.getPropertyType(businessObject, toNestingPoint); childObject = childClass.newInstance(); } catch (IllegalAccessException iae) { new UnsupportedOperationException(iae); } catch (InvocationTargetException ite) { new UnsupportedOperationException(ite); } catch (NoSuchMethodException nsme) { new UnsupportedOperationException(nsme); } catch (InstantiationException ie) { throw new UnsupportedOperationException(ie); } return usesDateValidation(fromNestingPoint, childObject); } final ValidationPattern validationPattern = attributeDefinition.getValidationPattern(); if (validationPattern == null) return false; // no validation for sure means we ain't using date validation return validationPattern instanceof DateValidationPattern; } /** * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#createGenericAccountingLineViewFieldDefinition(org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition) */ public AccountingLineViewFieldDefinition createGenericAccountingLineViewFieldDefinition(MaintainableFieldDefinition currentDefinition) { AccountingLineViewFieldDefinition fieldDefinition = new AccountingLineViewFieldDefinition(); fieldDefinition.setRequired(currentDefinition.isRequired()); fieldDefinition.setUnconditionallyReadOnly(currentDefinition.isUnconditionallyReadOnly()); fieldDefinition.setReadOnlyAfterAdd(currentDefinition.isReadOnlyAfterAdd()); fieldDefinition.setNoLookup(currentDefinition.isNoLookup()); fieldDefinition.setDefaultValue(currentDefinition.getDefaultValue()); fieldDefinition.setTemplate(currentDefinition.getTemplate()); fieldDefinition.setDefaultValueFinderClass(currentDefinition.getDefaultValueFinderClass()); fieldDefinition.setOverrideLookupClass(currentDefinition.getOverrideLookupClass()); fieldDefinition.setOverrideFieldConversions(currentDefinition.getOverrideFieldConversions()); return fieldDefinition; } /** * Gets the fieldTransformations attribute. * @return Returns the fieldTransformations. */ public List<AccountingLineFieldRenderingTransformation> getFieldTransformations() { return fieldTransformations; } /** * Sets the fieldTransformations attribute value. * @param fieldTransformations The fieldTransformations to set. */ public void setFieldTransformations(List<AccountingLineFieldRenderingTransformation> fieldTransformations) { this.fieldTransformations = fieldTransformations; } /** * Gets the accountingLineAuthorizationTransformer attribute. * @return Returns the accountingLineAuthorizationTransformer. */ public AccountingLineAuthorizationTransformer getAccountingLineAuthorizationTransformer() { return accountingLineAuthorizationTransformer; } /** * Sets the accountingLineAuthorizationTransformer attribute value. * @param accountingLineAuthorizationTransformer The accountingLineAuthorizationTransformer to set. */ public void setAccountingLineAuthorizationTransformer(AccountingLineAuthorizationTransformer accountingLineAuthorizationTransformer) { this.accountingLineAuthorizationTransformer = accountingLineAuthorizationTransformer; } /** * Gets the dataDictionaryService attribute. * @return Returns the dataDictionaryService. */ public DataDictionaryService getDataDictionaryService() { return dataDictionaryService; } /** * Sets the dataDictionaryService attribute value. * @param dataDictionaryService The dataDictionaryService to set. */ public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { this.dataDictionaryService = dataDictionaryService; } /** * Gets the postTablificationTransformations attribute. * @return Returns the postTablificationTransformations. */ public List<AccountingLineTableTransformation> getPostTablificationTransformations() { return postTablificationTransformations; } /** * Sets the postTablificationTransformations attribute value. * @param postTablificationTransformations The postTablificationTransformations to set. */ public void setPostTablificationTransformations(List<AccountingLineTableTransformation> postTablificationTransformations) { this.postTablificationTransformations = postTablificationTransformations; } /** * Gets the preTablificationTransformations attribute. * @return Returns the preTablificationTransformations. */ public List<AccountingLineRenderingTransformation> getPreTablificationTransformations() { return preTablificationTransformations; } /** * Sets the preTablificationTransformations attribute value. * @param preTablificationTransformations The preTablificationTransformations to set. */ public void setPreTablificationTransformations(List<AccountingLineRenderingTransformation> preTablificationTransformations) { this.preTablificationTransformations = preTablificationTransformations; } /** * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#findForm(javax.servlet.jsp.PageContext) */ public KualiAccountingDocumentFormBase findForm(PageContext pageContext) { if (pageContext.getRequest().getAttribute(KUALI_FORM_NAME) != null) return (KualiAccountingDocumentFormBase)pageContext.getRequest().getAttribute(KUALI_FORM_NAME); if (pageContext.getSession().getAttribute(KUALI_FORM_NAME) != null) return (KualiAccountingDocumentFormBase)pageContext.getSession().getAttribute(KUALI_FORM_NAME); return (KualiAccountingDocumentFormBase)KNSGlobalVariables.getKualiForm(); } protected DocumentHelperService getDocumentHelperService() { if (documentHelperService == null) { documentHelperService = SpringContext.getBean(DocumentHelperService.class); } return documentHelperService; } }