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