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.validation.impl;
017    
018    import java.lang.reflect.InvocationTargetException;
019    import java.util.LinkedList;
020    import java.util.Queue;
021    
022    import org.apache.commons.beanutils.PropertyUtils;
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.kfs.sys.businessobject.AccountingLine;
025    import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
026    import org.kuali.kfs.sys.document.AccountingDocument;
027    import org.kuali.kfs.sys.document.validation.GenericValidation;
028    import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
029    import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
030    import org.kuali.rice.kns.bo.PersistableBusinessObject;
031    import org.kuali.rice.kns.service.ParameterService;
032    import org.kuali.rice.kns.util.ObjectUtils;
033    
034    /**
035     * A validation which uses parameters to determine if a value on an accounting line is valid.
036     */
037    public class AccountingLineValueAllowedValidation extends GenericValidation {
038        protected String propertyPath;
039        protected String parameterToCheckAgainst;
040        protected ParameterService parameterService;
041        protected String responsibleProperty;
042        protected AccountingDocument accountingDocumentForValidation;
043        protected AccountingLine accountingLineForValidation;
044    
045        /**
046         * Checks if a value in a given accounting line is allowed, based on system parameters.
047         * <strong>Expects an accounting document as the first parameter and accounting line as the second</strong>
048         * @see org.kuali.kfs.sys.document.validation.GenericValidation#validate(java.lang.Object[])
049         */
050        public boolean validate(AttributedDocumentEvent event) {
051            
052            if (!StringUtils.isBlank(propertyPath)) {
053                refreshByPath(accountingLineForValidation);
054            }
055            
056            return isAccountingLineValueAllowed(accountingDocumentForValidation.getDocumentClassForAccountingLineValueAllowedValidation(), accountingLineForValidation, parameterToCheckAgainst, propertyPath, (responsibleProperty != null ? responsibleProperty : propertyPath));
057        }
058        
059        /**
060         * Checks that a value on an accounting line is valid, based on parameters, for a document of the given class
061         * @param documentClass the class of the document to check
062         * @param accountingLine the accounting line to check
063         * @param parameterName the name of the parameter to check
064         * @param propertyName the name of the property to check
065         * @param userEnteredPropertyName the value the user entered on the line
066         * @return true if this passes validation, false otherwise
067         */
068        protected boolean isAccountingLineValueAllowed(Class documentClass, AccountingLine accountingLine, String parameterName, String propertyName, String userEnteredPropertyName) {
069            boolean isAllowed = true;
070            String exceptionMessage = "Invalue property name provided to AccountingDocumentRuleBase isAccountingLineValueAllowed method: " + propertyName;
071            try {
072                String propertyValue = (String) PropertyUtils.getProperty(accountingLine, propertyName);
073                if (getParameterService().parameterExists(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, parameterName)) {
074                    isAllowed = getParameterService().getParameterEvaluator(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, parameterName, propertyValue).evaluateAndAddError(SourceAccountingLine.class, propertyName, userEnteredPropertyName);
075                }
076                if (getParameterService().parameterExists(documentClass, parameterName)) {
077                    isAllowed = getParameterService().getParameterEvaluator(documentClass, parameterName, propertyValue).evaluateAndAddError(SourceAccountingLine.class, propertyName, userEnteredPropertyName);
078                }
079            }
080            catch (IllegalAccessException e) {
081                throw new RuntimeException(exceptionMessage, e);
082            }
083            catch (InvocationTargetException e) {
084                throw new RuntimeException(exceptionMessage, e);
085            }
086            catch (NoSuchMethodException e) {
087                throw new RuntimeException(exceptionMessage, e);
088            }
089            return isAllowed;
090        }
091        
092        /**
093         * Refreshes a value on the accounting line, using the propertyPath to decided what to refresh
094         * @param line the accounting line to refresh a property on
095         */
096        public void refreshByPath(AccountingLine line) {
097            refreshByQueue(line, convertPathToQueue(propertyPath));
098        }
099        
100        /**
101         * Creates a Queue which represents a FIFO path of what properties to visit, based on the given property path
102         * @param path the path to convert to a Queue
103         * @return a Queue representing the path
104         */
105        protected Queue<String> convertPathToQueue(String path) {
106            Queue<String> pathQueue = new LinkedList<String>();
107            for (String property: path.split("\\.")) {
108                pathQueue.add(property);
109            }
110            return pathQueue;
111        }
112        
113        /**
114         * Recursively refreshes a property given by the queue path
115         * @param bo the business object to refresh
116         * @param path the path, in Queue form, of properties to refresh
117         */
118        protected void refreshByQueue(PersistableBusinessObject bo, Queue<String> path) {
119            if (path.size() > 1) { // we know that the last thing on our list is a code. why refresh that?
120                String currentProperty = path.remove();
121                bo.refreshReferenceObject(currentProperty);
122                PersistableBusinessObject childBO = (PersistableBusinessObject)ObjectUtils.getPropertyValue(bo, currentProperty);
123                if (!ObjectUtils.isNull(childBO)) {       
124                    refreshByQueue(childBO, path);
125                }
126            }
127        }
128    
129        /**
130         * Gets the propertyPath attribute. This is the path to the value to check, e. g. "accountNumber.subFundGroup.fundGroupCode"
131         * @return Returns the propertyPath.
132         */
133        public String getPropertyPath() {
134            return propertyPath;
135        }
136    
137        /**
138         * Sets the propertyPath attribute value. This is the path to the value to check, e. g. "accountNumber.subFundGroup.fundGroupCode"
139         * @param propertyPath The propertyPath to set.
140         */
141        public void setPropertyPath(String refreshPath) {
142            this.propertyPath = refreshPath;
143        }
144    
145        /**
146         * Gets the parameterService attribute. 
147         * @return Returns the parameterService.
148         */
149        public ParameterService getParameterService() {
150            return parameterService;
151        }
152    
153        /**
154         * Sets the parameterService attribute value.
155         * @param parameterService The parameterService to set.
156         */
157        public void setParameterService(ParameterService parameterService) {
158            this.parameterService = parameterService;
159        }
160    
161        /**
162         * Gets the parameterToCheckAgainst attribute. This is the name of the parameter which has the values to validate against.
163         * @return Returns the parameterToCheckAgainst.
164         */
165        public String getParameterToCheckAgainst() {
166            return parameterToCheckAgainst;
167        }
168    
169        /**
170         * Sets the parameterToCheckAgainst attribute value.  This is the name of the parameter which has the values to validate against.
171         * @param parameterToCheckAgainst The parameterToCheckAgainst to set.
172         */
173        public void setParameterToCheckAgainst(String parameterToCheckAgainst) {
174            this.parameterToCheckAgainst = parameterToCheckAgainst;
175        }
176    
177        /**
178         * Gets the responsibleProperty attribute. This is the property on the accounting line to show the error on.
179         * @return Returns the responsibleProperty.
180         */
181        public String getResponsibleProperty() {
182            return responsibleProperty;
183        }
184    
185        /**
186         * Sets the responsibleProperty attribute value. This is the property on the accounting line to show the error on.
187         * @param responsibleProperty The responsibleProperty to set.
188         */
189        public void setResponsibleProperty(String responsibleProperty) {
190            this.responsibleProperty = responsibleProperty;
191        }
192    
193        /**
194         * Gets the accountingDocumentForValidation attribute. 
195         * @return Returns the accountingDocumentForValidation.
196         */
197        public AccountingDocument getAccountingDocumentForValidation() {
198            return accountingDocumentForValidation;
199        }
200    
201        /**
202         * Sets the accountingDocumentForValidation attribute value.
203         * @param accountingDocumentForValidation The accountingDocumentForValidation to set.
204         */
205        public void setAccountingDocumentForValidation(AccountingDocument accountingDocumentForValidation) {
206            this.accountingDocumentForValidation = accountingDocumentForValidation;
207        }
208    
209        /**
210         * Gets the accountingLineForValidation attribute. 
211         * @return Returns the accountingLineForValidation.
212         */
213        public AccountingLine getAccountingLineForValidation() {
214            return accountingLineForValidation;
215        }
216    
217        /**
218         * Sets the accountingLineForValidation attribute value.
219         * @param accountingLineForValidation The accountingLineForValidation to set.
220         */
221        public void setAccountingLineForValidation(AccountingLine accountingLineForValidation) {
222            this.accountingLineForValidation = accountingLineForValidation;
223        }
224    }