/* * 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.module.ar.businessobject.lookup; import java.text.ParseException; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.kuali.kfs.module.ar.ArKeyConstants; import org.kuali.kfs.module.ar.report.service.ContractsGrantsReportHelperService; import org.kuali.kfs.sys.KFSKeyConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.core.api.search.SearchOperator; import org.kuali.rice.kim.api.identity.Person; import org.kuali.rice.kns.document.authorization.BusinessObjectRestrictions; import org.kuali.rice.kns.lookup.HtmlData; import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData; import org.kuali.rice.kns.web.comparator.CellComparatorHelper; import org.kuali.rice.kns.web.struts.form.LookupForm; import org.kuali.rice.kns.web.ui.Column; import org.kuali.rice.kns.web.ui.ResultRow; import org.kuali.rice.krad.bo.BusinessObject; import org.kuali.rice.krad.exception.ValidationException; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.ObjectUtils; /** * Customized Lookupable Helper class for Contracts & Grants Reports. */ public abstract class ContractsGrantsReportLookupableHelperServiceImplBase extends AccountsReceivableLookupableHelperServiceImplBase { protected ContractsGrantsReportHelperService contractsGrantsReportHelperService; protected Pattern numericPattern = Pattern.compile("[-+]?\\d+\\.?\\d*"); protected void buildResultTable(LookupForm lookupForm, Collection displayList, Collection resultTable) { Person user = GlobalVariables.getUserSession().getPerson(); boolean hasReturnableRow = false; // Iterate through result list and wrap rows with return url and action url for (Iterator iter = displayList.iterator(); iter.hasNext();) { BusinessObject element = (BusinessObject) iter.next(); BusinessObjectRestrictions businessObjectRestrictions = getBusinessObjectAuthorizationService().getLookupResultRestrictions(element, user); List<Column> columns = getColumns(); for (Iterator iterator = columns.iterator(); iterator.hasNext();) { Column col = (Column) iterator.next(); String propValue = ObjectUtils.getFormattedPropertyValue(element, col.getPropertyName(), col.getFormatter()); Class propClass = getPropertyClass(element, col.getPropertyName()); col.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(propClass)); col.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(propClass)); String propValueBeforePotientalMasking = propValue; propValue = maskValueIfNecessary(element.getClass(), col.getPropertyName(), propValue, businessObjectRestrictions); col.setPropertyValue(propValue); // Add url when property is documentNumber and paymentNumber (paymentNumber is a document number.) if (col.getPropertyName().equals(KFSPropertyConstants.DOCUMENT_NUMBER) || col.getPropertyName().equals("paymentNumber")) { String url = contractsGrantsReportHelperService.getDocSearchUrl(propValue); Map<String, String> fieldList = new HashMap<String, String>(); fieldList.put(KFSPropertyConstants.DOCUMENT_NUMBER, propValue); AnchorHtmlData a = new AnchorHtmlData(url, KRADConstants.EMPTY_STRING); a.setTitle(HtmlData.getTitleText(getContractsGrantsReportHelperService().createTitleText(getBusinessObjectClass()), getBusinessObjectClass(), fieldList)); col.setColumnAnchor(a); } } ResultRow row = new ResultRow(columns, "", ACTION_URLS_EMPTY); if (getBusinessObjectDictionaryService().isExportable(getBusinessObjectClass())) { row.setBusinessObject(element); } boolean isRowReturnable = isResultReturnable(element); row.setRowReturnable(isRowReturnable); if (isRowReturnable) { hasReturnableRow = true; } resultTable.add(row); } lookupForm.setHasReturnableRow(hasReturnableRow); } /** * Inner class to capture an operator */ protected class OperatorAndValue { protected SearchOperator operator; protected Double value; //we should be able to safely coerce all values to double private OperatorAndValue(SearchOperator operator, String valueAsString) { this.operator = operator; this.value = new Double(valueAsString); } protected boolean applyComparison(Number otherValue) { if (value == null) { return otherValue == null; } if (otherValue == null) { return false; // value is not null, so they're not equal } final Double otherValueDouble = new Double(otherValue.doubleValue()); final int compareResult = otherValueDouble.compareTo(value); switch (this.operator) { case EQUAL: return compareResult == 0; case LESS_THAN: return compareResult < 0; case GREATER_THAN: return compareResult > 0; case LESS_THAN_EQUAL: return compareResult <= 0; case GREATER_THAN_EQUAL: return compareResult >= 0; } throw new IllegalStateException("The operator did not catch "); } } /** * Validates whether a given String can be parsed into an operator and a value * @param propertyName the name of the property that this operatorAndValue was entered into * @param operatorAndValue String which should represent both an operator and a value */ protected boolean validateOperatorAndValue(String propertyName, String operatorAndValue) { if (StringUtils.isBlank(operatorAndValue)) { return true; // nothing to validate } String numericValue = operatorAndValue; if (operatorAndValue.charAt(0) == '>' || operatorAndValue.charAt(0) == '<') { if (operatorAndValue.charAt(1) == '=') { numericValue = operatorAndValue.substring(2); } else { numericValue = operatorAndValue.substring(1); } } final Matcher matcher = numericPattern.matcher(numericValue); if (!matcher.matches()) { GlobalVariables.getMessageMap().putError(propertyName, ArKeyConstants.ERROR_REPORT_INVALID_CALCULATED_PATTERN, operatorAndValue); return false; } return true; } /** * Performs a {@link #validateSearchParameters(Map)}-friendly validation of the given property name to make sure it is * a valid operator and value * @param fieldValues the fieldValues from the lookup form * @param propertyName the property to validate as an operator and value */ protected void validateSearchParametersForOperatorAndValue(Map<String, String> fieldValues, String propertyName) { if (!StringUtils.isBlank(fieldValues.get(propertyName))) { if (!validateOperatorAndValue(propertyName, fieldValues.get(propertyName))) { throw new ValidationException("Error in criteria for "+propertyName); } } } /** * Parses a given String into a record with an Operator and a numeric value * @param operatorAndValue the String to parse into an operator and value * @return a build OperatorAndValue record */ protected OperatorAndValue parseOperatorAndValue(String operatorAndValue) { if (StringUtils.isBlank(operatorAndValue)) { return null; // nothing to parse } if (operatorAndValue.startsWith(SearchOperator.LESS_THAN_EQUAL.op())) { final String valueOnly = operatorAndValue.substring(2); return new OperatorAndValue(SearchOperator.LESS_THAN_EQUAL, valueOnly); } else if (operatorAndValue.startsWith(SearchOperator.GREATER_THAN_EQUAL.toString())) { final String valueOnly = operatorAndValue.substring(2); return new OperatorAndValue(SearchOperator.GREATER_THAN_EQUAL, valueOnly); } else if (operatorAndValue.startsWith(SearchOperator.LESS_THAN.op())) { final String valueOnly = operatorAndValue.substring(1); return new OperatorAndValue(SearchOperator.LESS_THAN, valueOnly); } else if (operatorAndValue.startsWith(SearchOperator.GREATER_THAN.op())) { final String valueOnly = operatorAndValue.substring(1); return new OperatorAndValue(SearchOperator.GREATER_THAN, valueOnly); } return new OperatorAndValue(SearchOperator.EQUAL, operatorAndValue); } /** * Convenience method which removes a field value from the given lookup field map and turns it into an OperatorAndValue, or null if the field was not filled in * @param lookupFields the fields from the lookup form * @param propertyName the property name to lookup * @return the OperatorAndValue representing that field, or null if no value was entered */ protected OperatorAndValue buildOperatorAndValueFromField(Map lookupFields, String propertyName) { OperatorAndValue operator = null; final String fieldFromLookup = (String)lookupFields.get(propertyName); if (!StringUtils.isBlank(fieldFromLookup)) { operator = parseOperatorAndValue(fieldFromLookup); } return operator; } /** * Convenience method to validate a date from the lookup criteria * @param dateFieldValue the value of the field from the lookup criteria * @param dateFieldClass the class being looked up * @param dateFieldPropertyName the property name representing the date * @param dateTimeService an implementation of DateTimeService */ protected void validateDateField(String dateFieldValue, String dateFieldPropertyName, DateTimeService dateTimeService) { if (!StringUtils.isBlank(dateFieldValue)) { try { dateTimeService.convertToDate(dateFieldValue); } catch (ParseException pe) { addDateTimeError(dateFieldPropertyName); } } } /** * Convenience method to validate a timestamp from the lookup criteria * @param dateFieldValue the value of the field from the lookup criteria * @param dateFieldClass the class being looked up * @param dateFieldPropertyName the property name representing the date * @param dateTimeService an implementation of DateTimeService */ protected void validateTimestampField(String dateFieldValue, String dateFieldPropertyName, DateTimeService dateTimeService) { if (!StringUtils.isBlank(dateFieldValue)) { try { dateTimeService.convertToSqlTimestamp(dateFieldValue); } catch (ParseException pe) { addDateTimeError(dateFieldPropertyName); } } } /** * Adds an appropriate error about the date or date time field, with the property name given by dateFieldPropertyName, being unparsable * @param dateFieldPropertyName the property name which has the error */ protected void addDateTimeError(String dateFieldPropertyName) { final String attributeProperty = dateFieldPropertyName.startsWith(KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX) ? dateFieldPropertyName.substring(KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX.length()) : dateFieldPropertyName; final String label = getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), attributeProperty); GlobalVariables.getMessageMap().putError(dateFieldPropertyName, KFSKeyConstants.ERROR_DATE_TIME, label); } public ContractsGrantsReportHelperService getContractsGrantsReportHelperService() { return contractsGrantsReportHelperService; } public void setContractsGrantsReportHelperService(ContractsGrantsReportHelperService contractsGrantsReportHelperService) { this.contractsGrantsReportHelperService = contractsGrantsReportHelperService; } }