/** * The contents of this file are subject to the OpenMRS Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.web.controller.patient; import java.util.Calendar; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.PatientIdentifier; import org.openmrs.PersonAddress; import org.openmrs.PersonName; import org.openmrs.api.context.Context; import org.openmrs.util.OpenmrsUtil; import org.openmrs.validator.PatientIdentifierValidator; import org.openmrs.validator.PersonAddressValidator; import org.openmrs.validator.PersonNameValidator; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.validation.ObjectError; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; /** * This class validates a Short Patient Model object in the {@link NewPatientFormController} */ public class ShortPatientFormValidator implements Validator { private static Log log = LogFactory.getLog(PersonNameValidator.class); /** * Returns whether or not this validator supports validating a given class. * * @param c The class to check for support. * @see org.springframework.validation.Validator#supports(java.lang.Class) */ public boolean supports(Class c) { return ShortPatientModel.class.isAssignableFrom(c); } /** * Validates the given Patient. * * @param obj The patient to validate. * @param errors The patient to validate. * @see org.springframework.validation.Validator#validate(java.lang.Object, * org.springframework.validation.Errors) * @should pass if the minimum required fields are provided and are valid * @should fail validation if gender is blank * @should fail validation if birthdate is blank * @should fail validation if birthdate makes patient 120 years old or older * @should fail validation if birthdate is a future date * @should fail validation if causeOfDeath is blank when patient is dead * @should fail if all name fields are empty or white space characters * @should fail if no identifiers are added * @should fail if all identifiers have been voided * @should fail if any name has more than 50 characters * @should fail validation if deathdate is a future date * @should fail if the deathdate is before the birthdate incase the patient is dead * @should reject a duplicate name * @should reject a duplicate address */ public void validate(Object obj, Errors errors) { if (log.isDebugEnabled()) log.debug(this.getClass().getName() + ": Validating patient data from the short patient form...."); ShortPatientModel shortPatientModel = (ShortPatientModel) obj; PersonName personName = shortPatientModel.getPersonName(); //TODO We should be able to let developers and implementations to specify which person name // fields should be used to determine uniqueness //check if this name has a unique givenName, middleName and familyName combination for (PersonName possibleDuplicate : shortPatientModel.getPatient().getNames()) { //don't compare the name to itself if (OpenmrsUtil.nullSafeEquals(possibleDuplicate.getId(), personName.getId())) continue; if (OpenmrsUtil.nullSafeEqualsIgnoreCase(possibleDuplicate.getGivenName(), personName.getGivenName()) && OpenmrsUtil.nullSafeEqualsIgnoreCase(possibleDuplicate.getMiddleName(), personName.getMiddleName()) && OpenmrsUtil.nullSafeEqualsIgnoreCase(possibleDuplicate.getFamilyName(), personName.getFamilyName())) { errors.reject("Patient.duplicateName", new Object[] { personName.toString() }, personName.toString() + " is a duplicate name for the same patient"); } } Errors nameErrors = new BindException(personName, "personName"); new PersonNameValidator().validatePersonName(personName, nameErrors, false, true); if (nameErrors.hasErrors()) { // pick all the personName errors and bind them to the formObject Iterator<ObjectError> it = nameErrors.getAllErrors().iterator(); Set<String> errorCodesWithNoArguments = new HashSet<String>(); while (it.hasNext()) { ObjectError error = it.next(); // don't show similar error message multiple times in the view // unless they take in arguments which will make them atleast different if (error.getCode() != null && (!errorCodesWithNoArguments.contains(error.getCode()) || (error.getArguments() != null && error .getArguments().length > 0))) { errors.reject(error.getCode(), error.getArguments(), ""); if (error.getArguments() == null || error.getArguments().length == 0) errorCodesWithNoArguments.add(error.getCode()); } } // drop the collection errorCodesWithNoArguments = null; } //TODO We should be able to let developers and implementations to specify which // person address fields should be used to determine uniqueness //check if this address is unique PersonAddress personAddress = shortPatientModel.getPersonAddress(); for (PersonAddress possibleDuplicate : shortPatientModel.getPatient().getAddresses()) { //don't compare the address to itself if (OpenmrsUtil.nullSafeEquals(possibleDuplicate.getId(), personAddress.getId())) continue; if (!possibleDuplicate.isBlank() && !personAddress.isBlank() && possibleDuplicate.toString().equalsIgnoreCase(personAddress.toString())) { errors.reject("Patient.duplicateAddress", new Object[] { personAddress.toString() }, personAddress .toString() + " is a duplicate address for the same patient"); } } if (CollectionUtils.isEmpty(shortPatientModel.getIdentifiers())) errors.reject("PatientIdentifier.error.insufficientIdentifiers"); else { boolean nonVoidedIdentifierFound = false; for (PatientIdentifier pId : shortPatientModel.getIdentifiers()) { if (!pId.isVoided()) nonVoidedIdentifierFound = true; new PatientIdentifierValidator().validate(pId, errors); } // if all the names are voided if (!nonVoidedIdentifierFound) errors.reject("PatientIdentifier.error.insufficientIdentifiers"); } // Make sure they chose a gender if (StringUtils.isBlank(shortPatientModel.getPatient().getGender())) errors.rejectValue("patient.gender", "Person.gender.required"); // check patients birthdate against future dates and really old dates if (shortPatientModel.getPatient().getBirthdate() != null) { if (shortPatientModel.getPatient().getBirthdate().after(new Date())) errors.rejectValue("patient.birthdate", "error.date.future"); else { Calendar c = Calendar.getInstance(); c.setTime(new Date()); c.add(Calendar.YEAR, -120); // patient cannot be older than 120 // years old if (shortPatientModel.getPatient().getBirthdate().before(c.getTime())) { errors.rejectValue("patient.birthdate", "error.date.nonsensical"); } } } else { errors.rejectValue("patient.birthdate", "error.required", new Object[] { Context.getMessageSourceService() .getMessage("Person.birthdate") }, ""); } //validate the personAddress if (shortPatientModel.getPersonAddress() != null) { try { errors.pushNestedPath("personAddress"); ValidationUtils.invokeValidator(new PersonAddressValidator(), shortPatientModel.getPersonAddress(), errors); } finally { errors.popNestedPath(); } } if (shortPatientModel.getPatient().getDead()) { if (shortPatientModel.getPatient().getCauseOfDeath() == null) errors.rejectValue("patient.causeOfDeath", "Person.dead.causeOfDeathNull"); if (shortPatientModel.getPatient().getDeathDate() != null) { if (shortPatientModel.getPatient().getDeathDate().after(new Date())) errors.rejectValue("patient.deathDate", "error.date.future"); // death date has to be after birthdate if both are specified if (shortPatientModel.getPatient().getBirthdate() != null && shortPatientModel.getPatient().getDeathDate().before( shortPatientModel.getPatient().getBirthdate())) errors.rejectValue("patient.deathDate", "error.deathdate.before.birthdate"); } } } }