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