001    /*
002     * Copyright 2011 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.kfs.sys.document.service.impl;
017    
018    import java.lang.reflect.InvocationTargetException;
019    import java.util.ArrayList;
020    import java.util.List;
021    import java.util.Map;
022    
023    import javax.servlet.jsp.PageContext;
024    
025    import org.apache.commons.beanutils.PropertyUtils;
026    import org.kuali.kfs.coa.service.AccountService;
027    import org.kuali.kfs.sys.KFSConstants;
028    import org.kuali.kfs.sys.businessobject.AccountingLine;
029    import org.kuali.kfs.sys.context.SpringContext;
030    import org.kuali.kfs.sys.document.AccountingDocument;
031    import org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentAuthorizerBase;
032    import org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationController;
033    import org.kuali.kfs.sys.document.datadictionary.AccountingLineGroupDefinition;
034    import org.kuali.kfs.sys.document.datadictionary.AccountingLineViewFieldDefinition;
035    import org.kuali.kfs.sys.document.service.AccountingLineAuthorizationTransformer;
036    import org.kuali.kfs.sys.document.service.AccountingLineFieldRenderingTransformation;
037    import org.kuali.kfs.sys.document.service.AccountingLineRenderingService;
038    import org.kuali.kfs.sys.document.service.AccountingLineRenderingTransformation;
039    import org.kuali.kfs.sys.document.service.AccountingLineTableTransformation;
040    import org.kuali.kfs.sys.document.web.AccountingLineTableRow;
041    import org.kuali.kfs.sys.document.web.TableJoining;
042    import org.kuali.kfs.sys.document.web.renderers.CheckboxRenderer;
043    import org.kuali.kfs.sys.document.web.renderers.CurrencyRenderer;
044    import org.kuali.kfs.sys.document.web.renderers.DateRenderer;
045    import org.kuali.kfs.sys.document.web.renderers.DropDownRenderer;
046    import org.kuali.kfs.sys.document.web.renderers.DynamicReadOnlyRender;
047    import org.kuali.kfs.sys.document.web.renderers.FieldRenderer;
048    import org.kuali.kfs.sys.document.web.renderers.HiddenRenderer;
049    import org.kuali.kfs.sys.document.web.renderers.RadioButtonGroupRenderer;
050    import org.kuali.kfs.sys.document.web.renderers.ReadOnlyRenderer;
051    import org.kuali.kfs.sys.document.web.renderers.TextAreaRenderer;
052    import org.kuali.kfs.sys.document.web.renderers.TextRenderer;
053    import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentFormBase;
054    import org.kuali.rice.kns.datadictionary.AttributeDefinition;
055    import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
056    import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition;
057    import org.kuali.rice.kns.datadictionary.validation.ValidationPattern;
058    import org.kuali.rice.kns.datadictionary.validation.fieldlevel.DateValidationPattern;
059    import org.kuali.rice.kns.service.DataDictionaryService;
060    import org.kuali.rice.kns.service.DocumentHelperService;
061    import org.kuali.rice.kns.util.GlobalVariables;
062    import org.kuali.rice.kns.web.ui.Field;
063    
064    /**
065     * The default implementation of the AccountingLineRenderingService
066     */
067    public class AccountingLineRenderingServiceImpl implements AccountingLineRenderingService {
068        protected final String KUALI_FORM_NAME = "KualiForm";
069        
070        private List<AccountingLineFieldRenderingTransformation> fieldTransformations;
071        private DataDictionaryService dataDictionaryService;
072        private AccountingLineAuthorizationTransformer accountingLineAuthorizationTransformer;
073        private List<AccountingLineRenderingTransformation> preTablificationTransformations;
074        private List<AccountingLineTableTransformation> postTablificationTransformations;
075        private DocumentHelperService documentHelperService;
076    
077        /**
078         * @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)
079         */
080        public void performPreTablificationTransformations(List<TableJoining> elements, AccountingLineGroupDefinition groupDefinition, AccountingDocument accountingDocument, AccountingLine accountingLine, boolean newLine, Map unconvertedValues, String accountingLinePropertyName) {
081            performAuthorizationTransformations(elements, groupDefinition, accountingDocument, accountingLine, newLine, accountingLinePropertyName);
082            performFieldTransformations(elements, accountingDocument, accountingLine, unconvertedValues);
083            for (AccountingLineRenderingTransformation transformation : preTablificationTransformations) {
084                transformation.transformElements(elements, accountingLine);
085            }
086        }
087        
088        /**
089         * @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)
090         */
091        public void performPostTablificationTransformations(List<AccountingLineTableRow> rows, AccountingLineGroupDefinition groupDefinition, AccountingDocument document, AccountingLine accountingLine, boolean newLine) {
092            for (AccountingLineTableTransformation transformation : postTablificationTransformations) {
093                transformation.transformRows(rows);
094            }
095        }
096    
097    
098        /**
099         * Performs the authorization transformations
100         * @param elements the layout elements which we are authorizing
101         * @param accountingLineGroupDefinition the data dictionary definition of the accounting line group
102         * @param accountingDocument the accounting line document we're rendering accounting lines for
103         * @param accountingLine the accounting line we're rendering
104         * @param newLine true if the accounting line is not yet on the form yet, false otherwise
105         */
106        protected void performAuthorizationTransformations(List<TableJoining> elements, AccountingLineGroupDefinition accountingLineGroupDefinition, AccountingDocument accountingDocument, AccountingLine accountingLine, boolean newLine, String accountingLinePropertyName) {
107            accountingLineAuthorizationTransformer.transformElements(elements, accountingLine, accountingDocument, accountingLineGroupDefinition.getAccountingLineAuthorizer(), newLine, accountingLinePropertyName);
108        }
109        
110        /**
111         * Performs field transformations for pre-rendering
112         * @param elements the layout elements that hold fields to transform
113         * @param accountingDocument the accounting document with the line we are rendering
114         * @param accountingLine the accounting line we are rendering
115         * @param unconvertedValues any unconverted values
116         */
117        protected void performFieldTransformations(List<TableJoining> elements, AccountingDocument accountingDocument, AccountingLine accountingLine, Map unconvertedValues) {
118            for (TableJoining layoutElement : elements) {
119                layoutElement.performFieldTransformations(fieldTransformations, accountingLine, unconvertedValues);
120            }
121        }
122     
123        /**
124         * Creates an accounting document authorizer for the given accounting document
125         * @param document the document to get an authorizer for
126         * @return an authorizer for the document
127         */
128        protected FinancialSystemTransactionalDocumentAuthorizerBase getDocumentAuthorizer(AccountingDocument document) {
129            final FinancialSystemTransactionalDocumentAuthorizerBase authorizer = (FinancialSystemTransactionalDocumentAuthorizerBase) getDocumentHelperService().getDocumentAuthorizer(document);
130            return authorizer;
131        }
132        
133        /**
134         * @param document the document to get the presentation controller for
135         * @return the proper presentation controller
136         */
137        protected FinancialSystemTransactionalDocumentPresentationController getPresentationController(AccountingDocument document) {
138            final FinancialSystemTransactionalDocumentPresentationController presentationController = (FinancialSystemTransactionalDocumentPresentationController) getDocumentHelperService().getDocumentPresentationController(document);
139            return presentationController;
140        }
141    
142        /**
143         * Simplify the tree so that it is made up of only table elements and fields
144         * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#tablify(java.util.List)
145         */
146        public List<AccountingLineTableRow> tablify(List<TableJoining> elements) {
147            List<AccountingLineTableRow> rows = createBlankTableRows(getMaxRowCount(elements));
148            tablifyElements(elements, rows);
149            return rows;
150        }
151        
152        /**
153         * Gets the maximum number of rows needed by any child element
154         * @param elements the elements to turn into table rows
155         * @return the maximum number of rows requested
156         */
157        protected int getMaxRowCount(List<TableJoining> elements) {
158            int maxRowCount = 0;
159            for (TableJoining element : elements) {
160                int rowCount = element.getRequestedRowCount();
161                if (rowCount > maxRowCount) {
162                    maxRowCount = rowCount;
163                }
164            }
165            return maxRowCount;
166        }
167        
168        /**
169         * This method creates a List of blank table rows, based on the requested count
170         * @param count the count of table rows
171         * @return a List of table rows ready for population
172         */
173        protected List<AccountingLineTableRow> createBlankTableRows(int count) {
174            List<AccountingLineTableRow> rows = new ArrayList<AccountingLineTableRow>();
175            for (int i = 0; i < count; i++) {
176                rows.add(new AccountingLineTableRow());
177            }
178            return rows;
179        }
180    
181        /**
182         * Requests each of the given elements to join the table
183         * @param elements the elements to join to the table
184         * @param rows the table rows to join to
185         */
186        protected void tablifyElements(List<TableJoining> elements, List<AccountingLineTableRow> rows) {
187            for (TableJoining element : elements) {
188                element.joinTable(rows);
189            }
190        }
191    
192        /**
193         * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#getFieldRendererForField(org.kuali.rice.kns.web.ui.Field, org.kuali.kfs.sys.businessobject.AccountingLine)
194         */
195        public FieldRenderer getFieldRendererForField(Field field, AccountingLine accountingLineToRender) {
196            FieldRenderer renderer = null;
197    
198            if (field.isReadOnly() || field.getFieldType().equals(Field.READONLY)) {
199                renderer = new ReadOnlyRenderer();
200            } /* 
201            else if (field.getPropertyName().equals(KFSConstants.CHART_OF_ACCOUNTS_CODE_PROPERTY_NAME) && !SpringContext.getBean(AccountService.class).accountsCanCrossCharts()) {
202                // the special case for rendering chart of accounts code when accounts can't cross charts
203                renderer = new DynamicReadOnlyRender();   
204            } */
205            else if (field.getFieldType().equals(Field.TEXT)) {
206                if (field.isDatePicker() || usesDateValidation(field.getPropertyName(), accountingLineToRender)) { // are we a date?
207                    renderer = new DateRenderer();
208                } else {
209                    renderer = new TextRenderer();
210                }
211            } else if (field.getFieldType().equals(Field.TEXT_AREA)) {
212                renderer = new TextAreaRenderer();
213            } else if (field.getFieldType().equals(Field.HIDDEN)) {
214                renderer = new HiddenRenderer();
215            } else if (field.getFieldType().equals(Field.CURRENCY)) {
216                renderer = new CurrencyRenderer();
217            } else if (field.getFieldType().equals(Field.DROPDOWN)) {
218                renderer = new DropDownRenderer();
219            } else if (field.getFieldType().equals(Field.RADIO)) {
220                renderer = new RadioButtonGroupRenderer();
221            } else if (field.getFieldType().equals(Field.CHECKBOX)) {
222                renderer = new CheckboxRenderer();
223            }
224            
225            return renderer;
226        }
227        
228        /**
229         * Determines if this method uses a date validation pattern, in which case, a date picker should be rendered
230         * @param propertyName the property of the field being checked from the command line
231         * @param accountingLineToRender the accounting line which is being rendered
232         * @return true if the property does use date validation, false otherwise
233         */
234        protected boolean usesDateValidation(String propertyName, Object businessObject) {
235            final BusinessObjectEntry entry = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(businessObject.getClass().getName());
236            AttributeDefinition attributeDefinition = entry.getAttributeDefinition(propertyName);
237            
238            if (attributeDefinition == null) {
239                if (!propertyName.contains(".")) return false;
240                final int firstNestingPoint = propertyName.indexOf(".");
241                final String toNestingPoint = propertyName.substring(0, firstNestingPoint);
242                final String fromNestingPoint = propertyName.substring(firstNestingPoint+1);
243                Object childObject = null;
244                try {
245                    final Class childClass = PropertyUtils.getPropertyType(businessObject, toNestingPoint);
246                    childObject = childClass.newInstance();
247                }
248                catch (IllegalAccessException iae) {
249                    new UnsupportedOperationException(iae);
250                }
251                catch (InvocationTargetException ite) {
252                    new UnsupportedOperationException(ite);
253                }
254                catch (NoSuchMethodException nsme) {
255                    new UnsupportedOperationException(nsme);
256                }
257                catch (InstantiationException ie) {
258                    throw new UnsupportedOperationException(ie);
259                }
260                return usesDateValidation(fromNestingPoint, childObject);
261            }
262            
263            final ValidationPattern validationPattern = attributeDefinition.getValidationPattern();
264            if (validationPattern == null) return false; // no validation for sure means we ain't using date validation
265            return validationPattern instanceof DateValidationPattern;
266        }
267    
268        /**
269         * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#createGenericAccountingLineViewFieldDefinition(org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition)
270         */
271        public AccountingLineViewFieldDefinition createGenericAccountingLineViewFieldDefinition(MaintainableFieldDefinition currentDefinition) {
272            AccountingLineViewFieldDefinition fieldDefinition = new AccountingLineViewFieldDefinition();
273            
274            fieldDefinition.setRequired(currentDefinition.isRequired());
275            fieldDefinition.setUnconditionallyReadOnly(currentDefinition.isUnconditionallyReadOnly());
276            fieldDefinition.setReadOnlyAfterAdd(currentDefinition.isReadOnlyAfterAdd());
277            fieldDefinition.setNoLookup(currentDefinition.isNoLookup());
278            
279            fieldDefinition.setDefaultValue(currentDefinition.getDefaultValue());
280            fieldDefinition.setTemplate(currentDefinition.getTemplate());
281            fieldDefinition.setDefaultValueFinderClass(currentDefinition.getDefaultValueFinderClass());
282            
283            fieldDefinition.setOverrideLookupClass(currentDefinition.getOverrideLookupClass());
284            fieldDefinition.setOverrideFieldConversions(currentDefinition.getOverrideFieldConversions());
285            
286            return fieldDefinition;
287        }
288    
289        /**
290         * Gets the fieldTransformations attribute. 
291         * @return Returns the fieldTransformations.
292         */
293        public List<AccountingLineFieldRenderingTransformation> getFieldTransformations() {
294            return fieldTransformations;
295        }
296    
297        /**
298         * Sets the fieldTransformations attribute value.
299         * @param fieldTransformations The fieldTransformations to set.
300         */
301        public void setFieldTransformations(List<AccountingLineFieldRenderingTransformation> fieldTransformations) {
302            this.fieldTransformations = fieldTransformations;
303        }
304    
305        /**
306         * Gets the accountingLineAuthorizationTransformer attribute. 
307         * @return Returns the accountingLineAuthorizationTransformer.
308         */
309        public AccountingLineAuthorizationTransformer getAccountingLineAuthorizationTransformer() {
310            return accountingLineAuthorizationTransformer;
311        }
312    
313        /**
314         * Sets the accountingLineAuthorizationTransformer attribute value.
315         * @param accountingLineAuthorizationTransformer The accountingLineAuthorizationTransformer to set.
316         */
317        public void setAccountingLineAuthorizationTransformer(AccountingLineAuthorizationTransformer accountingLineAuthorizationTransformer) {
318            this.accountingLineAuthorizationTransformer = accountingLineAuthorizationTransformer;
319        }
320    
321        /**
322         * Gets the dataDictionaryService attribute. 
323         * @return Returns the dataDictionaryService.
324         */
325        public DataDictionaryService getDataDictionaryService() {
326            return dataDictionaryService;
327        }
328    
329        /**
330         * Sets the dataDictionaryService attribute value.
331         * @param dataDictionaryService The dataDictionaryService to set.
332         */
333        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
334            this.dataDictionaryService = dataDictionaryService;
335        }
336    
337        /**
338         * Gets the postTablificationTransformations attribute. 
339         * @return Returns the postTablificationTransformations.
340         */
341        public List<AccountingLineTableTransformation> getPostTablificationTransformations() {
342            return postTablificationTransformations;
343        }
344    
345        /**
346         * Sets the postTablificationTransformations attribute value.
347         * @param postTablificationTransformations The postTablificationTransformations to set.
348         */
349        public void setPostTablificationTransformations(List<AccountingLineTableTransformation> postTablificationTransformations) {
350            this.postTablificationTransformations = postTablificationTransformations;
351        }
352    
353        /**
354         * Gets the preTablificationTransformations attribute. 
355         * @return Returns the preTablificationTransformations.
356         */
357        public List<AccountingLineRenderingTransformation> getPreTablificationTransformations() {
358            return preTablificationTransformations;
359        }
360    
361        /**
362         * Sets the preTablificationTransformations attribute value.
363         * @param preTablificationTransformations The preTablificationTransformations to set.
364         */
365        public void setPreTablificationTransformations(List<AccountingLineRenderingTransformation> preTablificationTransformations) {
366            this.preTablificationTransformations = preTablificationTransformations;
367        }
368    
369        /**
370         * @see org.kuali.kfs.sys.document.service.AccountingLineRenderingService#findForm(javax.servlet.jsp.PageContext)
371         */
372        public KualiAccountingDocumentFormBase findForm(PageContext pageContext) {
373            if (pageContext.getRequest().getAttribute(KUALI_FORM_NAME) != null) return (KualiAccountingDocumentFormBase)pageContext.getRequest().getAttribute(KUALI_FORM_NAME);
374            
375            if (pageContext.getSession().getAttribute(KUALI_FORM_NAME) != null) return (KualiAccountingDocumentFormBase)pageContext.getSession().getAttribute(KUALI_FORM_NAME);
376            
377            return (KualiAccountingDocumentFormBase)GlobalVariables.getKualiForm();
378        }
379        
380        protected DocumentHelperService getDocumentHelperService() {
381            if (documentHelperService == null) {
382                documentHelperService = SpringContext.getBean(DocumentHelperService.class);
383            }
384            return documentHelperService;
385        }
386    }
387