Java tutorial
/*$Id: AnswerHelper.java 9029 2008-03-13 23:11:51Z jens $*/ /* **************************************************************************** * * * (c) Copyright 2005 ABM-utvikling * * * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 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 General * * Public License for more details. http://www.gnu.org/licenses/gpl.html * * * **************************************************************************** */ package no.abmu.abmstatistikk.annualstatistic.service; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import no.abmu.abmstatistikk.annualstatistic.domain.Answer; import no.abmu.abmstatistikk.annualstatistic.domain.Field; import no.abmu.abmstatistikk.annualstatistic.domain.FieldAnnualChangeConstraint; import no.abmu.abmstatistikk.annualstatistic.domain.FieldGroupConstraint; import no.abmu.abmstatistikk.annualstatistic.domain.FieldGroupConstraintMember; import no.abmu.abmstatistikk.annualstatistic.domain.FieldType; import no.abmu.abmstatistikk.annualstatistic.domain.FieldTypeConstraint; import no.abmu.abmstatistikk.annualstatistic.domain.Report; import no.abmu.abmstatistikk.annualstatistic.domain.Schema; /** * AnswerHelper. * * This class is a helper-class for the auto-generated Answer class. * Use this helper to add functionallity and ease the use of Answer objects. * * @author: Henning Kulander hennikul@linpro.no * @author $Author: jens $ * @version $Rev: 9029 $ * @date $Date: 2008-03-14 00:11:51 +0100 (Fri, 14 Mar 2008) $ * @copyright ABM-Utvikling * @since 2005-01-07 (Rev 459) */ public class AnswerHelper { private static final Log logger = (Log) LogFactory.getLog(AnswerHelper.class); private Answer answer; public AnswerHelper(Answer answer) { this.answer = answer; } /** * validate. * * @return Errormessage from validation or null if no errors */ public String validate() { String error = null; error = validateFieldType(); if (error != null || answer.getValue() == null) { return error; } error = validateFieldGroupConstraint(); if (error != null) { return error; } return validateFieldAnnualChangeConstraint(); } /** * validateFieldType. * * @return The errormessage property */ public String validateFieldType() { Field field = answer.getField(); if (field == null) { return "Answer not associated with a field in database!"; } FieldType fieldType = field.getFieldType(); if (fieldType == null) { return "Field has no type, can't validate..."; } String dataType = fieldType.getDatatype(); if (dataType == null) { return "Field has undefined dataType, can't validate..."; } if (dataType.equals("TEXT")) { return validateText(); } else if (dataType.equals("POSTNUMBER")) { return validatePostNumber(); } else if (dataType.equals("PHONENUMBER")) { return validatePhoneNumber(); } else if (dataType.equals("YEAR")) { return validateYear(); } else if (dataType.equals("E-MAIL")) { return validateEmail(); } else if (dataType.equals("URL")) { return validateURL(); } else if (dataType.equals("YES_NO_CHOICE")) { return validateYesNoChoice(); } else if (dataType.equals("YES_NO_PARTLY_CHOICE")) { return validateYesNoPartyChoice(); } else if (dataType.equals("CHECKBOX")) { return validateCheckBox(); } else if (dataType.equals("DIGIT_12")) { return validateDigit(12); } else if (dataType.equals("DIGIT_10")) { return validateDigit(10); } else if (dataType.equals("DIGIT_9")) { return validateDigit(9); } else if (dataType.equals("DIGIT_4")) { return validateDigit(4); } else if (dataType.equals("DIGIT_5")) { return validateDigit(5); } else if (dataType.equals("DIGIT_3")) { return validateDigit(3); } else if (dataType.equals("DIGIT_12_0")) { return validateDigit(12); } else if (dataType.equals("DIGIT_4_0")) { return validateDigit(4); } else if (dataType.equals("DIGIT_3_0")) { return validateDigit(3); } else if (dataType.equals("RADIO_GROUP")) { return validateRadioGroup(); } else if (dataType.equals("FLOAT")) { return validateFloat(10, 2); } else if (dataType.equals("FLOAT_5_2")) { return validateFloat(5, 2); } else if (dataType.equals("FLOAT_4_2")) { return validateFloat(4, 2); } else if (dataType.equals("WEEKHOURS")) { return validateWeekHours(); } else if (dataType.equals("YEARDAYS")) { return validateYearDays(); } else if (dataType.equals("YEARDAYSINT")) { return validateYearDaysInteger(); } else if (dataType.equals("YEARHOURS")) { return validateYearHours(); } else if (dataType.equals("SELECTBOX")) { return validateSelectBox(); } else if (dataType.equals("AUTOCALCULATE")) { return validateAutoCalculate(); } else if (dataType.equals("DATE")) { return validateDate(); } else { return "Don't know how to validate " + dataType; } /* If we get this far, everything is OK. Returning no error string */ } /** * validateDate. * * @return */ private String validateDate() { String validateValue = answer.getValue(); if (validateValue == null || validateValue.equals("")) { return null; /* Value not required */ } Pattern floatPattern = Pattern.compile("(\\d\\d?)/(\\d\\d?)"); Matcher floatMatch = floatPattern.matcher(validateValue); if (!floatMatch.matches()) { return "schema.error.date_unmatched"; } String dayString = floatMatch.group(1); String monthString = floatMatch.group(2); int day = 0, month = 0; try { day = Integer.parseInt(dayString); month = Integer.parseInt(monthString); } catch (NumberFormatException e) { return "schema.error.date_unmatched"; } if (month < 1 || month > 12) { return "schema.error.date_bad_month"; } if (day < 1 || day > 31) { return "schema.error.date_bad_day"; } return null; } /** * validateSelectBox. * * @return */ private String validateSelectBox() { String validateValue = answer.getValue(); if (validateValue == null || validateValue.equals("")) { // No choice made return null; } Field field = answer.getField(); if (field == null) { logger.error("No defined field for " + answer); return "No field defined here... Should not happen"; } FieldType fieldType = field.getFieldType(); String fieldDatatype = null; if (fieldType != null) { fieldDatatype = fieldType.getDatatype(); } if (fieldDatatype == null) { logger.error("No fieldtype for field: " + field); return "Field has no type, this should never happen."; } if (!fieldDatatype.equals("SELECTBOX")) { logger.error("Trying to validate " + fieldDatatype + " as SELECTBOX"); return "Trying to validate " + fieldDatatype + " as SELECTBOX"; } Set fieldTypeConstraints = fieldType.getFieldTypeConstraints(); Iterator fieldTypeConstraintsIterator = fieldTypeConstraints.iterator(); while (fieldTypeConstraintsIterator.hasNext()) { FieldTypeConstraint fieldTypeConstraint = (FieldTypeConstraint) fieldTypeConstraintsIterator.next(); String parameterValue = fieldTypeConstraint.getValue(); if (parameterValue != null && parameterValue.equals(validateValue)) { return null; } } return "schema.error.selectbox_illegal_value"; } /** * validateFloat. * * @param beforeComma : Maximum amounts of digit before comma * @param afterComma : Maximum number of digits after comma * @return */ private String validateFloat(int beforeComma, int afterComma) { String validateValue = answer.getValue(); if (validateValue == null || validateValue.equals("")) { // Input is not required return null; } Pattern floatPattern = Pattern .compile("(\\d{1," + beforeComma + "})([,.](\\d{" + afterComma + "," + afterComma + "}))?"); Matcher floatMatch = floatPattern.matcher(validateValue); if (!floatMatch.matches()) { return "schema.error.float_" + beforeComma + "_" + afterComma + "_unmatched"; } String beforeCommaString = floatMatch.group(1); String afterCommaString = floatMatch.group(3); if (afterCommaString == null) { afterCommaString = ""; for (int i = 0; i < afterComma; i++) { afterCommaString = afterCommaString + "0"; } } answer.setValue(beforeCommaString + "," + afterCommaString); return validateFloat(); } /** * validateYearHours. * * @return */ private String validateYearHours() { String error = validateDigit(0); if (error != null) { return error; } String valueString = answer.getValue(); if (valueString == null || valueString.equals("")) { return null; /* Allow for no input */ } /* Find year of schema and use calendar to find number of days of * that year. */ Report report = answer.getReport(); if (report == null) { logger.error("Answer not bound to a report: " + answer); return "No report for answer! This should never happen."; } Schema schema = report.getSchema(); if (schema == null) { logger.error("Report not bound to a schema: " + report); return "No schema for report! This should never happen."; } SchemaHelper schemaHelper = new SchemaHelper(schema); int year = schemaHelper.getYear(); Calendar calendar = new GregorianCalendar(year, 1, 1); int daysOfYear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR); int value = Integer.parseInt(valueString); if (value > daysOfYear * 24 || value < 0) { return "schema.error.invalid_number_of_hours_pr_year"; } return null; } private String validateYearDaysInteger() { String error = validateDigit(10); if (error != null) { return error; } return validateYearDaysCommon(); } /** * validateYearDays. * * @return error if day not between 0 and maximum number of days for * the year given by schema. null if validation passes */ private String validateYearDays() { String error = validateFloat(3, 1); if (error != null) { return error; } return validateYearDaysCommon(); } private String validateYearDaysCommon() { String valueString = answer.getValue(); if (valueString == null || valueString.equals("")) { return null; /* Allow for no input */ } /* Find year of schema and use calendar to find number of days of * that year. */ Report report = answer.getReport(); if (report == null) { logger.error("Answer not bound to a report: " + answer); return "No report for answer! This should never happen."; } Schema schema = report.getSchema(); if (schema == null) { logger.error("Report not bound to a schema: " + report); return "No schema for report! This should never happen."; } SchemaHelper schemaHelper = new SchemaHelper(schema); int year = schemaHelper.getYear(); Calendar calendar = new GregorianCalendar(year, 1, 1); int daysOfYyear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR); valueString = valueString.replace(',', '.'); float value = Float.parseFloat(valueString); if (value > daysOfYyear || value < 0) { return "schema.error.invalid_number_of_days_pr_year"; } return null; } /** * validateWeekHours. * * @return error if hours not between 0 and 7*24, null if validation passes */ private String validateWeekHours() { String error = validateFloat(3, 1); if (error != null) { return error; } String valueString = answer.getValue(); if (valueString == null || valueString.equals("")) { return null; /* Allow for no input */ } valueString = valueString.replace(',', '.'); float value = Float.parseFloat(valueString); if (value > 7 * 24 || value < 0) { return "schema.error.invalid_number_of_hours_pr_week"; } return null; } /** * validateAutoCalculate. * * @return */ private String validateAutoCalculate() { // No need to validate automatic values, they are not editable. return null; } /** * validateFloat. * * @return */ private String validateFloat() { String validateValue = answer.getValue(); if (validateValue == null || validateValue.equals("")) { // Input is not required return null; } validateValue = validateValue.replace(',', '.'); float value; try { value = Float.parseFloat(validateValue); } catch (NumberFormatException e) { return "schema.error.float_invalid"; } logger.debug("Validation of answer with value " + value + " succeded."); return null; } /** * validateRadioGroup. * * @return */ private String validateRadioGroup() { // TODO Auto-generated method stub return "Validation of Radio Groups not implemented yet"; } /** * validateDigit. * * @param max_digits : Maximum number of digits * @return error string or null if validation succedes */ private String validateDigit(int maxDigits) { String validateValue = answer.getValue(); if (validateValue == null || validateValue.equals("")) { // It is not mandatory to enter a value, return success return null; } // Check if length is longer than allowed: if (maxDigits > 0 && validateValue.length() > maxDigits) { return "schema.error.digit_too_long_" + maxDigits; } // Check if input is a valid digit: long value = 0; try { value = Long.parseLong(validateValue); } catch (NumberFormatException e) { return "schema.error.digit_invalid"; } logger.debug("Validation of answer with value " + value + " succeded."); return null; } /** * validateCheckBox. * * @return */ private String validateCheckBox() { String validateValue = answer.getValue(); if (validateValue == null || validateValue.equals("Unchecked") // Unchecked || validateValue.equals("true")) { // Checked return null; } logger.error("Validation of a checkbox with value " + validateValue + " failed. This should never happen!"); return "schema.error.bad_checkbox_value"; } /** * validateYesNoParty_Choice. * * @return */ private String validateYesNoPartyChoice() { String validateValue = answer.getValue(); if (validateValue == null) { return "schema.error.choice_required"; } if (validateValue.equals("partly")) { return null; } return validateYesNoChoice(); } /** * validateYesNo_Choice. * * @return */ private String validateYesNoChoice() { String validateValue = answer.getValue(); if (validateValue == null) { return "schema.error.choice_required"; } if (validateValue.equals("yes") || validateValue.equals("true") // Yes || validateValue.equals("no") || validateValue.equals("false")) { // No return null; } logger.error( "Validation of a yes/no input failed with value " + validateValue + " this should never happen!"); return "schema.error.bad_yes_no_value"; } /** * validateURL. * * @return */ private String validateURL() { // TODO Auto-generated method stub return "Validation of URLs not implemented yet"; } /** * validateEmail. * * @return */ private String validateEmail() { // TODO Auto-generated method stub return "Validation of e-mail addresses not implemented yet"; } /** * validateYear. * * @return */ private String validateYear() { // TODO Auto-generated method stub return "Validation of years not implemented yet"; } /** * validatePhoneNumber. * * @return */ private String validatePhoneNumber() { // TODO Auto-generated method stub return "Validation of phone-numbers not implemented yet"; } /** * validatePostNumber. * * @return */ private String validatePostNumber() { // TODO Auto-generated method stub return "Validation of Post-numbers not implemented yet"; } /** * validateText. * * @return */ private String validateText() { /* Free text input, check length... */ String stringValue = answer.getValue(); if (stringValue != null) { /* Remove leading spaces */ stringValue = stringValue.replaceAll("^(\\s|\\n)+", ""); /* Remove trailing spaces */ stringValue = stringValue.replaceAll("(\\s|\\n)+$", ""); answer.setValue(stringValue); } if (stringValue == null || stringValue.length() < 4096) { return null; } else { return "schema.error.text_too_long"; } } public String validateSortedGroupConstraint(FieldGroupConstraint groupConstraint, FieldGroupConstraintMember groupMembership, Field field, ReportHelper reportHelper, Report report) { logger.debug("Checking sorted constraint for " + field.getName()); String stringValue = answer.getValue(); logger.debug("Field has value " + stringValue); FieldGroupConstraintHelper constraintHelper = new FieldGroupConstraintHelper(groupConstraint); int orderInGroup = groupMembership.getComp_id().getOrderingroupconstraint().intValue(); int lastMemberNumber = constraintHelper.lastMemberNumber(); if (orderInGroup > lastMemberNumber) { logger.error("Group member out of bounds! " + "Should not happen."); return "Group member out of bounds! Should not happen!"; } else if (orderInGroup == lastMemberNumber) { /* If any of the other fields has a value, this field must * also have a value. Check for other values if this field * is empty */ if (stringValue != null && !stringValue.equals("")) { return null; /* Value is not empty, no check needed */ } String previousStringValue = null; int previousOrderInGroup = orderInGroup - 1; while (previousOrderInGroup >= 0 && (previousStringValue == null || previousStringValue.equals(""))) { String previousFieldName = constraintHelper.getFieldNameByOrderInGroup(previousOrderInGroup); if (previousFieldName == null) { logger.error("No field with " + "order " + previousOrderInGroup + " in FieldGroupConstraint" + groupConstraint); return "Previous field in group constraint not " + "found. This should never happen."; } Answer previousAnswer = reportHelper.getAnswerByFieldName(previousFieldName); if (previousAnswer == null) { logger.error( "Report does not containt answer for " + "field " + previousFieldName + ". :" + report); return "No answer object for next member in group " + "this should never happen"; } previousStringValue = previousAnswer.getValue(); previousOrderInGroup--; } if (previousStringValue != null && !previousStringValue.equals("")) { /* Last required since some other field has value */ return "schema.error.sorted_field_required"; } else { return null; } } else { /* If the field has a value, check if all fields bigger also * has a value, and that the value is higher */ if (stringValue == null || stringValue.equals("")) { return null; /* Don't compare empty values */ } int nextOrderInGroup = orderInGroup; String nextStringValue; do { nextOrderInGroup++; String nextFieldName = constraintHelper.getFieldNameByOrderInGroup(nextOrderInGroup); if (nextFieldName == null) { logger.error("No field with order " + nextOrderInGroup + " in FieldGroupConstraint" + groupConstraint); return "Next field in group constraint not found. " + "This should never happen."; } Answer nextAnswer = reportHelper.getAnswerByFieldName(nextFieldName); if (nextAnswer == null) { logger.error( "Report does not containt answer for " + "field " + nextFieldName + ". :" + report); return "No answer object for next member in group " + "this should never happen"; } nextStringValue = nextAnswer.getValue(); } while (nextOrderInGroup < lastMemberNumber && (nextStringValue == null || nextStringValue.equals(""))); if (nextStringValue == null || nextStringValue.equals("")) { /* All fields above were empty */ return "schema.error.sorted_missing_field_to_compare_with"; } logger.debug("Compare with " + nextStringValue); float value; try { stringValue = stringValue.replace(',', '.'); value = Float.parseFloat(stringValue); } catch (NumberFormatException e) { /* Should have been caught in digit validation, but * we have the extra test here just in case */ return "schema.error.float_invalid"; } float nextValue; try { nextStringValue = nextStringValue.replace(',', '.'); nextValue = Float.parseFloat(nextStringValue); } catch (NumberFormatException e) { /* Other field fails validation. The validation for this * field shoul also reflect this. */ return "schema.error.sorted_unable_to_compare"; } if (value <= nextValue) { return null; } else { return "schema.error.sorted_too_big"; } } } public String validateExcludesGroupConstraint(FieldGroupConstraint groupConstraint, FieldGroupConstraintMember groupMembership, Field field, ReportHelper reportHelper, Report report) { /* Check if this answer is the first in the group */ FieldGroupConstraintHelper constraintHelper = new FieldGroupConstraintHelper(groupConstraint); int orderInGroup = groupMembership.getComp_id().getOrderingroupconstraint().intValue(); /* If it is, allways return null */ if (orderInGroup == 0) { return null; } /* If not return true if empty or null */ String stringValue = answer.getValue(); if (stringValue == null || stringValue.equals("")) { return null; } /* If not empty or null find the first answer */ String firstFieldName = constraintHelper.getFieldNameByOrderInGroup(0); Answer firstAnswer = reportHelper.getAnswerByFieldName(firstFieldName); String firstAnswerValue = firstAnswer.getValue(); /* Compare first answer to FieldGroupConstraint.parameter */ /* If not equal, return true */ if (firstAnswerValue == null || !firstAnswerValue.equals(groupConstraint.getParameter())) { return null; } /* Check if this is a Yes/No or Yes/No/Partly field */ FieldType fieldType = field.getFieldType(); if (fieldType == null) { logger.error("Field " + field.getName() + " does not containt " + "a FieldType:" + report); return "No FieldType for field, this should never happen"; } String dataType = fieldType.getDatatype(); if (dataType == null) { logger.error("Field " + field.getName() + " does not containt " + "a DataType:" + report); return "No DataType for field, this should never happen"; } if (dataType.equals("YES_NO_CHOICE") || dataType.equals("YES_NO_PARTLY_CHOICE")) { /* If it is, return true if choice is no */ if (stringValue.equals("no") || stringValue.equals("false")) { return null; } } return "schema.error." + report.getSchema().getShortname() + ".excluded_by_field_" + firstFieldName; } public String validateMinMaxGroupConstraint(FieldGroupConstraint groupConstraint, FieldGroupConstraintMember groupMembership, Field field, ReportHelper reportHelper, Report report) { /* Find the parameters for this MINMAX group */ logger.debug("Checking MINMAX requirements for field " + answer.getField().getName()); String parameter = groupConstraint.getParameter(); Pattern pattern = Pattern.compile("\\((\\d+),(\\d+),(\\w+)\\)"); Matcher matcher = pattern.matcher(parameter); if (!matcher.matches()) { logger.error("Bad MINMAX parameter " + parameter); return "Bad MINMAX parameter for this field. " + "This should never happen!"; } String maxString = matcher.group(1); int max = Integer.parseInt(maxString); String minString = matcher.group(2); int min = Integer.parseInt(minString); String value = matcher.group(3); String stringValue = answer.getValue(); /* Count how many has this value. */ int count = 0; Set members = groupConstraint.getFieldGroupConstraintMembers(); Iterator memberIterator = members.iterator(); while (memberIterator.hasNext()) { FieldGroupConstraintMember member = (FieldGroupConstraintMember) memberIterator.next(); Field memberField = member.getField(); String memberFieldName = memberField.getName(); Answer memberAnswer = reportHelper.getAnswerByFieldName(memberFieldName); String memberValue = memberAnswer.getValue(); if (memberValue != null && memberValue.equals(value)) { count++; } } /* Check if this fields value is the value of the group, */ if (stringValue == null || !stringValue.equals(value)) { /* If it is not, return error if count less than min */ if (count < min) { return "schema.error.maxmin_" + value + "_lower_than_" + minString; } else { return null; } } else { /* If count is more than max, return error */ if (count > max) { return "schema.error.maxmin_" + value + "_higher_than_" + maxString; } else { return null; } } } /** * validateFieldGroupConstraint. * * @return null if no errors, error if sorting is wrong. */ public String validateFieldGroupConstraint() { Field field = answer.getField(); if (field == null) { logger.error("No defined field for " + answer); return "No field defined here... Should not happen"; } Report report = answer.getReport(); if (report == null) { logger.error("Answer not bound to a report: " + answer); return "No report for answer! This should never happen."; } ReportHelper reportHelper = new ReportHelper(report); Set fieldGroupMemberships = field.getFieldGroupConstraintMembers(); if (fieldGroupMemberships.isEmpty()) { return null; /* Not in any FieldGroupConstraints, nothing to check*/ } /* Walk through all fieldGroupConstraintMemberships and validate * according to type. */ Iterator fieldGroupMembershipsIterator = fieldGroupMemberships.iterator(); String error; while (fieldGroupMembershipsIterator.hasNext()) { FieldGroupConstraintMember groupMembership = (FieldGroupConstraintMember) fieldGroupMembershipsIterator .next(); FieldGroupConstraint groupConstraint = groupMembership.getFieldGroupConstraint(); String groupConstraintType = groupConstraint.getType(); /* Validate SORTED Group constraints * Member with orderInGroup == 0 should have highest value. * Other members are in ascending order. * If a field has a value, all values after (ie. higher * orderInGroup) should also have values. Other members may be * absent. */ if (groupConstraintType.equals("SORTED")) { error = validateSortedGroupConstraint(groupConstraint, groupMembership, field, reportHelper, report); } else if (groupConstraintType.equals("EXCLUDES")) { error = validateExcludesGroupConstraint(groupConstraint, groupMembership, field, reportHelper, report); } else if (groupConstraintType.equals("MINMAX")) { error = validateMinMaxGroupConstraint(groupConstraint, groupMembership, field, reportHelper, report); /* AUTOCALCULATE-groups should not be editable and is not validated */ } else if (groupConstraintType.equals("CALCULATION")) { error = null; } else { return "validation not implemented for GroupConstraintType: " + groupConstraintType; } if (error != null) { return error; } } return null; } /** * validateFieldAnnualChangeConstraint. * * @return null if no errors, error if difference between this years * value and last years value is to big. */ public String validateFieldAnnualChangeConstraint() { Field field = answer.getField(); if (field == null) { logger.error("No defined field for " + answer); return "No field defined here... Should not happen"; } FieldAnnualChangeConstraint annualChangeConstraint = field.getFieldAnnualChangeConstraintByFieldid(); if (annualChangeConstraint == null) { if (logger.isDebugEnabled()) { logger.debug("No annual constraing on field " + field.getName()); } return null; } else { if (logger.isDebugEnabled()) { logger.info("Start schecking annualConstraing for field " + field.getName()); } FieldType fieldType = field.getFieldType(); String fieldDatatype = null; if (fieldType != null) { fieldDatatype = fieldType.getDatatype(); } if (fieldDatatype == null) { logger.error("No fieldtype for field: " + field); return "Field has no type, this should never happen."; } if (!fieldDatatype.startsWith("DIGIT") && !fieldDatatype.startsWith("FLOAT") && !fieldDatatype.startsWith("WEEKHOURS")) { logger.error("Cannot validate annual change constraint for " + "datatype " + fieldDatatype); return "Unhandled dataType: " + fieldDatatype; } Field lastYearField = annualChangeConstraint.getFieldByLastyearfieldid(); if (lastYearField == null) { logger.error("No last year field defined in Change Constraint" + annualChangeConstraint); return "No last year field defined in annual change constraint" + ". This should never happen."; } String lastYearFieldName = lastYearField.getName(); Report report = answer.getReport(); if (report == null) { logger.error("Answer not bound to a report: " + answer); return "No report for answer! This should never happen."; } Report lastYearReport = report.getReport(); if (lastYearReport == null) { if (logger.isDebugEnabled()) { logger.debug("No report to check values against"); } return null; /* No report to check values against */ } ReportHelper lastYearReportHelper = new ReportHelper(lastYearReport); Answer lastYearAnswer = lastYearReportHelper.getAnswerByFieldName(lastYearFieldName); String lastYearValueString = null; if (lastYearAnswer != null) { lastYearValueString = lastYearAnswer.getValue(); } if (lastYearValueString == null || lastYearValueString.equals("")) { /* If last year value does not exist, no error has occured */ return null; } String thisYearValueString = answer.getValue(); if (thisYearValueString == null || thisYearValueString.equals("")) { /* If this years value is not present, don't perform any tests*/ return null; } float thisYearValue; try { thisYearValueString = thisYearValueString.replace(',', '.'); thisYearValue = Float.valueOf(thisYearValueString).floatValue(); } catch (NumberFormatException e) { return "schema.error.float_invalid"; } float lastYearValue; try { lastYearValueString = lastYearValueString.replace(',', '.'); lastYearValue = Float.valueOf(lastYearValueString).floatValue(); } catch (NumberFormatException e) { logger.error("Bad value in last years report: " + lastYearReport); logger.error("Value was: " + lastYearValueString); return "Bad value in last years report: " + lastYearValueString; } float change = java.lang.Math.abs(lastYearValue - thisYearValue); int minChange = annualChangeConstraint.getLowchangelimit(); int highChange = annualChangeConstraint.getHighchangelimit(); int percent = annualChangeConstraint.getPercentchangelimit(); if (logger.isDebugEnabled()) { logger.debug("Last year value '" + lastYearValue + "' this year value '" + thisYearValue + "' change '" + change + "' min change '" + minChange + "' max change '" + highChange + "' percent '" + percent + "%"); } /* Now we are ready to do the tests... */ String comment = answer.getComment(); if (comment != null && !comment.equals("")) { if (logger.isDebugEnabled()) { logger.debug("User has commented the change"); } return null; /* User has commented the change */ } if (change > highChange) { /* If change is more than maxChange, warn user */ //TODO: Find a way to give user a warning... if (logger.isDebugEnabled()) { logger.debug( "Change from " + lastYearValue + " to " + thisYearValue + " more than " + highChange); } return "schema.error.change_too_big"; } else if (change > minChange && change / lastYearValue > (float) percent / 100) { /* If change is more than minChange and less than highChange * and the relative change is more than percent, issue a warning */ //TODO: Find a way to give user a warning... if (logger.isDebugEnabled()) { logger.debug("Change from " + lastYearValue + " to " + thisYearValue + " more than " + percent + "%. (" + (change / lastYearValue) * 100 + "%)."); } return "schema.error.change_too_big_percent"; } /* If change is less than minChange, no error has occured */ /* If change is more than minChange, less than highChange and * relative change is less than percent, no error has occured */ return null; } } public void autoCalculate() { float sum = 0; Field field = answer.getField(); if (field == null) { logger.error("No field for answer: " + answer); } if (logger.isDebugEnabled()) { logger.debug("Calculating sum for field " + field.getName()); } FieldType fieldType = field.getFieldType(); if (!fieldType.getDatatype().equals("AUTOCALCULATE")) { logger.error("Trying to autocalculate an answer of type " + fieldType.getDatatype() + " - " + answer); return; } String oldValue = answer.getValue(); if (oldValue != null && (!oldValue.equals("") || oldValue.equals("CALCULATING"))) { /* This sum has allready been calculated */ return; /* Early cutoff in recursive sums */ } answer.setValue("CALCULATING"); Report report = answer.getReport(); if (report == null) { logger.error("Answer not bound to a report: " + answer); } ReportHelper reportHelper = new ReportHelper(report); Set fieldGroupMemberships = field.getFieldGroupConstraintMembers(); if (fieldGroupMemberships.isEmpty()) { logger.error("Trying to autocalculate answer for field " + field.getName() + " with no Fieldgroup " + "membership " + answer); } Iterator fieldGroupMembershipsIterator = fieldGroupMemberships.iterator(); while (fieldGroupMembershipsIterator.hasNext()) { FieldGroupConstraintMember groupMembership = (FieldGroupConstraintMember) fieldGroupMembershipsIterator .next(); FieldGroupConstraint groupConstraint = groupMembership.getFieldGroupConstraint(); String groupConstraintType = groupConstraint.getType(); if (groupConstraintType.equals("CALCULATION")) { FieldGroupConstraintHelper constraintHelper = new FieldGroupConstraintHelper(groupConstraint); int orderInGroup = groupMembership.getComp_id().getOrderingroupconstraint().intValue(); if (orderInGroup == 0) { int lastMemberNumber = constraintHelper.lastMemberNumber(); String sign = groupConstraint.getParameter(); if (sign == null || (!sign.equals("+") && !sign.equals("-") && !sign.equals("*") && !sign.equals("/"))) { logger.error("Don't know how to calculate with '" + sign + "'."); return; } for (int i = 1; i <= lastMemberNumber; i++) { String sumMemberFieldName = constraintHelper.getFieldNameByOrderInGroup(i); Answer sumMember = reportHelper.getAnswerByFieldName(sumMemberFieldName); if (sumMember == null) { logger.error("Report for schema " + report.getSchema().getShortname() + " has no field with name " + sumMemberFieldName + "."); logger.error("Cannot calculate sum for field " + field.getName()); return; } /* If field is autocalculated, calculate it first */ Field sumMemberField = sumMember.getField(); if (sumMemberField == null) { logger.error("Member in sum has no field: " + sumMember); return; } FieldType sumMemberFieldType = sumMemberField.getFieldType(); if (sumMemberFieldType == null) { logger.error("Member in sum has noe fieldtype: " + answer); return; } if (sumMemberFieldType.getDatatype().equals("AUTOCALCULATE")) { AnswerHelper sumMemberHelper = new AnswerHelper(sumMember); String calculateValue = sumMember.getValue(); if (calculateValue != null && calculateValue.equals("CALCULATING")) { logger.error("Calculation loop detected when " + "calculating sum for field " + field.getName() + " in report for schema " + report.getSchema().getShortname()); return; } sumMemberHelper.autoCalculate(); } float sumMemberValue = 0; String value = sumMember.getValue(); if (value == null || value.equals("")) { continue; } try { sumMemberValue = Float.parseFloat(value); } catch (NumberFormatException e) { /* Badly formated number. Should not happen, return * with answer.value == null. */ answer.setValue("ERROR"); return; } if (i == 1) { /* Start with sum equals to first member*/ sum = sumMemberValue; } else if (sign.equals("+")) { sum += sumMemberValue; } else if (sign.equals("-")) { sum -= sumMemberValue; } else if (sign.equals("*")) { sum *= sumMemberValue; } else if (sign.equals("/")) { sum /= sumMemberValue; } else { logger.error("Bad calculation! " + "Should never reach this point"); return; } } if (logger.isDebugEnabled()) { logger.debug("Calculated sum for field " + answer.getField().getName() + " with value " + Float.toString(sum)); } answer.setValue(Float.toString(sum)); return; } } } logger.error("Trying to autocalculate answer with no CALCULATION " + "fieldgroups where orderInGroup is 0: " + answer); } }