gov.nih.nci.cabig.caaers.api.impl.SAEEvaluationServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.cabig.caaers.api.impl.SAEEvaluationServiceImpl.java

Source

/*******************************************************************************
 * Copyright SemanticBits, Northwestern University and Akaza Research
 * 
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.com/caaers/LICENSE.txt for details.
 ******************************************************************************/
package gov.nih.nci.cabig.caaers.api.impl;

import static gov.nih.nci.cabig.caaers.domain.dto.ReportDefinitionWrapper.ActionType.AMEND;
import static gov.nih.nci.cabig.caaers.domain.dto.ReportDefinitionWrapper.ActionType.WITHDRAW;
import gov.nih.nci.cabig.caaers.CaaersSystemException;
import gov.nih.nci.cabig.caaers.dao.AdverseEventRecommendedReportDao;
import gov.nih.nci.cabig.caaers.dao.AdverseEventReportingPeriodDao;
import gov.nih.nci.cabig.caaers.dao.ExpeditedAdverseEventReportDao;
import gov.nih.nci.cabig.caaers.dao.ParticipantDao;
import gov.nih.nci.cabig.caaers.dao.StudyDao;
import gov.nih.nci.cabig.caaers.dao.TreatmentAssignmentDao;
import gov.nih.nci.cabig.caaers.domain.AdverseEvent;
import gov.nih.nci.cabig.caaers.domain.AdverseEventCtcTerm;
import gov.nih.nci.cabig.caaers.domain.AdverseEventMeddraLowLevelTerm;
import gov.nih.nci.cabig.caaers.domain.AdverseEventRecommendedReport;
import gov.nih.nci.cabig.caaers.domain.AdverseEventReportingPeriod;
import gov.nih.nci.cabig.caaers.domain.AeTerminology;
import gov.nih.nci.cabig.caaers.domain.Epoch;
import gov.nih.nci.cabig.caaers.domain.ExpeditedAdverseEventReport;
import gov.nih.nci.cabig.caaers.domain.Identifier;
import gov.nih.nci.cabig.caaers.domain.LocalOrganization;
import gov.nih.nci.cabig.caaers.domain.Organization;
import gov.nih.nci.cabig.caaers.domain.ReportTableRow;
import gov.nih.nci.cabig.caaers.domain.Study;
import gov.nih.nci.cabig.caaers.domain.StudyParticipantAssignment;
import gov.nih.nci.cabig.caaers.domain.StudySite;
import gov.nih.nci.cabig.caaers.domain.TreatmentAssignment;
import gov.nih.nci.cabig.caaers.domain.dto.ApplicableReportDefinitionsDTO;
import gov.nih.nci.cabig.caaers.domain.dto.EvaluationResultDTO;
import gov.nih.nci.cabig.caaers.domain.dto.ReportDefinitionWrapper;
import gov.nih.nci.cabig.caaers.domain.meddra.LowLevelTerm;
import gov.nih.nci.cabig.caaers.domain.report.ReportDefinition;
import gov.nih.nci.cabig.caaers.domain.repository.AdverseEventRoutingAndReviewRepository;
import gov.nih.nci.cabig.caaers.integration.schema.adverseevent.AdverseEventType;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.AEsOutputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.AdverseEventResult;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.AdverseEvents;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.EvaluateAEsInputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.EvaluateAEsOutputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.EvaluateAndInitiateInputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.EvaluateAndInitiateOutputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.EvaluatedAdverseEventResults;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.RecommendedActions;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.SaveAndEvaluateAEsInputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.SaveAndEvaluateAEsOutputMessage;
import gov.nih.nci.cabig.caaers.integration.schema.saerules.SaveAndEvaluateAEsOutputMessageType;
import gov.nih.nci.cabig.caaers.service.DomainObjectImportOutcome;
import gov.nih.nci.cabig.caaers.service.EvaluationService;
import gov.nih.nci.cabig.caaers.service.RecommendedActionService;
import gov.nih.nci.cabig.caaers.service.migrator.adverseevent.AdverseEventConverter;
import gov.nih.nci.cabig.caaers.service.migrator.adverseevent.AdverseEventReportingPeriodMigrator;
import gov.nih.nci.cabig.caaers.service.migrator.adverseevent.SAEAdverseEventReportingPeriodConverter;
import gov.nih.nci.cabig.caaers.service.migrator.adverseevent.SAEServiceMessageConverter;
import gov.nih.nci.cabig.caaers.service.synchronizer.adverseevent.AdverseEventReportingPeriodSynchronizer;
import gov.nih.nci.cabig.caaers.tools.configuration.Configuration;
import gov.nih.nci.cabig.caaers.utils.DateUtils;
import gov.nih.nci.cabig.caaers.validation.AdverseEventGroup;
import gov.nih.nci.cabig.caaers.validation.CourseCycleGroup;
import gov.nih.nci.cabig.caaers.validation.ValidationErrors;
import gov.nih.nci.cabig.caaers.validation.validator.AdverseEventValidatior;
import gov.nih.nci.cabig.caaers.ws.faults.CaaersFault;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.groups.Default;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
import org.springframework.transaction.annotation.Transactional;

/**
 * EvaluationService evaluate rules on the given AE's submitted by Web service.
 * 
 * @author MedaV
 * 
 */

public class SAEEvaluationServiceImpl {

    private AdverseEventRecommendedReportDao adverseEventRecommendedReportDao;
    private StudyDao studyDao;
    private TreatmentAssignmentDao treatmentAssignmentDao;
    private ParticipantDao participantDao;
    private SAEAdverseEventReportingPeriodConverter reportingPeriodConverter;
    private EvaluationService evaluationService;
    private Configuration configuration;
    private MessageSource messageSource;
    private AdverseEventConverter converter;
    private RecommendedActionService recommendedActionService;
    private AdverseEventReportingPeriodDao adverseEventReportingPeriodDao;
    private AdverseEventReportingPeriodSynchronizer reportingPeriodSynchronizer;
    private AdverseEventReportingPeriodMigrator reportingPeriodMigrator;
    private AdverseEventValidatior adverseEventValidatior;
    private AdverseEventRoutingAndReviewRepository adverseEventRoutingAndReviewRepository;
    private Validator validator;
    private SAEServiceMessageConverter xmlConverter = new SAEServiceMessageConverter();

    private enum RequestType {
        SaveEvaluate, Evaluate, EvaluateInitiate
    };

    private static String DEF_ERR_MSG = "Error evaluating adverse events with SAE rules";
    private ExpeditedAdverseEventReportDao expeditedAdverseEventReportDao;

    public void setExpeditedAdverseEventReportDao(ExpeditedAdverseEventReportDao expeditedAdverseEventReportDao) {
        this.expeditedAdverseEventReportDao = expeditedAdverseEventReportDao;
    }

    private static final Log logger = LogFactory.getLog(SAEEvaluationServiceImpl.class);

    private SafetyReportServiceImpl safetySvcImpl;

    public SafetyReportServiceImpl getSafetySvcImpl() {
        return safetySvcImpl;
    }

    public void setSafetySvcImpl(SafetyReportServiceImpl safetySvcImpl) {
        this.safetySvcImpl = safetySvcImpl;
    }

    public SAEServiceMessageConverter getSAEServiceMessageConverter() {
        return xmlConverter;
    }

    public void setSAEServiceMessageConverter(SAEServiceMessageConverter xmlConverter) {
        this.xmlConverter = xmlConverter;
    }

    private AEsOutputMessage saveAndProcessAdverseEvents(Study study, AdverseEventReportingPeriod reportingPeriod,
            Map<AdverseEvent, AdverseEventResult> mapAE2DTO, RequestType type) throws CaaersFault {
        SaveAndEvaluateAEsOutputMessageType output;
        switch (type) {
        case Evaluate:
            throw new IllegalArgumentException("Can't take evaluate as an input.");
        case EvaluateInitiate:
            output = new EvaluateAndInitiateOutputMessage();
            break;
        case SaveEvaluate:
            output = new SaveAndEvaluateAEsOutputMessage();
            break;
        default:
            output = null;

        }
        try {
            // 2. Persist AdverseEvents.
            ValidationErrors errors = new ValidationErrors();
            reportingPeriod = createOrUpdateAdverseEvents(reportingPeriod, errors, true);

            if (errors.hasErrors()) {
                logger.error(
                        "Adverse Event Management Service create or update call failed :" + String.valueOf(errors));
                if (errors.getErrorAt(0).getCode().equals("NO-CODE"))
                    throw Helper.createCaaersFault(DEF_ERR_MSG, errors.getErrorAt(0).getCode(),
                            errors.getErrorAt(0).getMessage() + " "
                                    + errors.getErrorAt(0).getReplacementVariables()[0]);
                throw Helper.createCaaersFault(DEF_ERR_MSG, errors.getErrorAt(0).getCode(),
                        errors.getErrorAt(0).getMessage());
            }

            //initialize requires reporting flag
            for (AdverseEvent ae : reportingPeriod.getAdverseEvents()) {
                AdverseEventResult aeResult = findAdverseEvent(ae, mapAE2DTO);
                if (aeResult != null)
                    aeResult.setRequiresReporting(ae.getRequiresReporting());
            }

            output.setLinkToReport(constructLinkToReport(study.getId(), reportingPeriod.getParticipant().getId(),
                    reportingPeriod.getId()));
            // 3. fire Evaluation Service to identify SAE or not ?
            output = (SaveAndEvaluateAEsOutputMessageType) fireSAERules(reportingPeriod, study, mapAE2DTO, type,
                    output);

            for (AdverseEvent ae : reportingPeriod.getAdverseEvents()) {
                AdverseEventResult aeResult = findAdverseEvent(ae, mapAE2DTO);
                if (aeResult == null) {
                    continue;
                }
                ae.setRequiresReporting(aeResult.isRequiresReporting());
            }

            // save the updated reporting period
            saveReportingPeriod(reportingPeriod);
        } catch (CaaersSystemException ex) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, ex.getErrorCode(), ex.getMessage());
        }

        return output;
    }

    /**
     * Process and Save the Adverse Events.
     *
     * @param saveAndEvaluateAEsInputMessage
     * @return
     * @throws CaaersFault
     */
    public SaveAndProcessOutput saveAndProcessAdverseEvents(
            SaveAndEvaluateAEsInputMessage saveAndEvaluateAEsInputMessage) throws CaaersFault {
        Map<AdverseEvent, AdverseEventResult> mapAE2DTO = new HashMap<AdverseEvent, AdverseEventResult>();

        if (saveAndEvaluateAEsInputMessage == null) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, "WS_SAE_007",
                    messageSource.getMessage("WS_SAE_007", new String[] {}, "", Locale.getDefault()));
        }
        SaveAndEvaluateAEsOutputMessage saveAndEvaluateAEsOutputMessage = (SaveAndEvaluateAEsOutputMessage) createResponseObject(
                RequestType.SaveEvaluate);

        AdverseEventReportingPeriod reportingPeriod = null;
        try {
            // 0. Load the study required.
            String studyIdentifier = saveAndEvaluateAEsInputMessage.getCriteria().getStudyIdentifier();
            Study study = fetchStudy(studyIdentifier);

            // 1. Call the converter and make the required object.
            reportingPeriod = reportingPeriodConverter.convert(saveAndEvaluateAEsInputMessage, mapAE2DTO);

            // 2. Persist AdverseEvents.
            ValidationErrors errors = new ValidationErrors();
            reportingPeriod = createOrUpdateAdverseEvents(reportingPeriod, errors, true);

            if (errors.hasErrors()) {
                logger.error(
                        "Adverse Event Management Service create or update call failed :" + String.valueOf(errors));
                if (errors.getErrorAt(0).getCode().equals("NO-CODE"))
                    throw Helper.createCaaersFault(DEF_ERR_MSG, errors.getErrorAt(0).getCode(),
                            errors.getErrorAt(0).getMessage() + " "
                                    + errors.getErrorAt(0).getReplacementVariables()[0]);
                throw Helper.createCaaersFault(DEF_ERR_MSG, errors.getErrorAt(0).getCode(),
                        errors.getErrorAt(0).getMessage());
            }

            //initialize requires reporting flag
            for (AdverseEvent ae : reportingPeriod.getAdverseEvents()) {
                AdverseEventResult aeResult = findAdverseEvent(ae, mapAE2DTO);
                if (aeResult != null)
                    aeResult.setRequiresReporting(ae.getRequiresReporting());
            }

            saveAndEvaluateAEsOutputMessage.setLinkToReport(constructLinkToReport(study.getId(),
                    reportingPeriod.getParticipant().getId(), reportingPeriod.getId()));
            // 3. fire Evaluation Service to identify SAE or not ?
            saveAndEvaluateAEsOutputMessage = (SaveAndEvaluateAEsOutputMessage) fireSAERules(reportingPeriod, study,
                    mapAE2DTO, RequestType.SaveEvaluate, saveAndEvaluateAEsOutputMessage);

            for (AdverseEvent ae : reportingPeriod.getAdverseEvents()) {
                AdverseEventResult aeResult = findAdverseEvent(ae, mapAE2DTO);
                if (aeResult == null) {
                    continue;
                }
                ae.setRequiresReporting(aeResult.isRequiresReporting());
            }

            // save the updated reporting period
            saveReportingPeriod(reportingPeriod);
        } catch (CaaersSystemException ex) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, ex.getErrorCode(), ex.getMessage());
        }

        studyDao.flush();

        return new SaveAndProcessOutput(saveAndEvaluateAEsOutputMessage, reportingPeriod);
    }

    @Transactional(readOnly = false)
    public EvaluateAndInitiateOutputMessage processAndInitiate(EvaluateAndInitiateInputMessage evaluateInputMessage)
            throws CaaersFault {

        SaveAndEvaluateAEsInputMessage sae = xmlConverter.SAEInputMessage(evaluateInputMessage);

        // CAAERS-7414 if reportId is passed check if the reporting period attributes are consistent with the one already saved.
        String reportId = evaluateInputMessage.getReportId();
        if (!StringUtils.isBlank(reportId)) {
            validateReportIdAndAdverseEventReportingPeriodAttributes(sae, reportId);
        }

        SaveAndProcessOutput data = saveAndProcessAdverseEvents(sae);

        SaveAndEvaluateAEsOutputMessage response = data.getMsg();

        EvaluateAndInitiateOutputMessage retVal = xmlConverter.EvaluateAndInitiateOutput(response);

        if (response.getRecommendedActions() != null && data.getPeriod() != null) {
            safetySvcImpl.initiateSafetyReportAction(evaluateInputMessage, response, retVal, data.getPeriod());
        }
        return retVal;
    }

    public void validateReportIdAndAdverseEventReportingPeriodAttributes(SaveAndEvaluateAEsInputMessage sae,
            String reportId) throws CaaersFault {

        if (StringUtils.isBlank(reportId)) {
            return;
        }

        if (sae == null) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, "WS_SAE_007",
                    messageSource.getMessage("WS_SAE_007", new String[] {}, "", Locale.getDefault()));
        }

        // find reporting period attributes from input

        AdverseEventReportingPeriod rpDest = new AdverseEventReportingPeriod();
        gov.nih.nci.cabig.caaers.integration.schema.adverseevent.CourseType course = null;

        if (sae.getCriteria() != null && sae.getCriteria().getCourse() != null
                && sae.getCriteria().getCourse() != null) {
            course = sae.getCriteria().getCourse();
        }
        if (course != null && course.getStartDateOfThisCourse() != null) {
            rpDest.setStartDate(course.getStartDateOfThisCourse().toGregorianCalendar().getTime());
        }

        if (course != null && sae.getCriteria().getCourse().getCycleNumber() != null) {
            rpDest.setCycleNumber(course.getCycleNumber().intValue());
        }

        if (course != null && course.getTreatmentAssignmentCode() != null) {
            TreatmentAssignment treatmentAssignmentSrc = new TreatmentAssignment();
            treatmentAssignmentSrc.setCode(course.getTreatmentAssignmentCode());
            rpDest.setTreatmentAssignment(treatmentAssignmentSrc);
        }

        ExpeditedAdverseEventReport dbReport = expeditedAdverseEventReportDao.getByExternalId(reportId);
        if (dbReport != null) {
            String tac = dbReport.getReportingPeriod().getTreatmentAssignment() != null
                    ? dbReport.getReportingPeriod().getTreatmentAssignment().getCode()
                    : null;
            if (!rpDest.hasSameCoreAttributes(dbReport.getReportingPeriod().getCycleNumber(),
                    dbReport.getReportingPeriod().getStartDate(), tac)) {
                throw Helper.createCaaersFault(DEF_ERR_MSG, "WS_SAE_008",
                        messageSource.getMessage("WS_SAE_008", new String[] { reportId }, "", Locale.getDefault()));
            }
        }
    }

    /**
      * Process the adverse Events.
      * @param evaluateAEsInputMessage
      * @return
      * @throws CaaersFault
      */
    public EvaluateAEsOutputMessage processAdverseEvents(EvaluateAEsInputMessage evaluateAEsInputMessage)
            throws CaaersFault {
        if (evaluateAEsInputMessage == null) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, "WS_SAE_007",
                    messageSource.getMessage("WS_SAE_007", new String[] {}, "", Locale.getDefault()));
        }

        gov.nih.nci.cabig.caaers.integration.schema.saerules.Study study = evaluateAEsInputMessage.getStudy();

        if (study == null) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, "WS_GEN_001",
                    messageSource.getMessage("WS_GEN_001", new String[] {}, "", Locale.getDefault()));
        }

        if (evaluateAEsInputMessage.getAdverseEvents() == null
                || evaluateAEsInputMessage.getAdverseEvents().getAdverseEvent() == null
                || evaluateAEsInputMessage.getAdverseEvents().getAdverseEvent().size() == 0) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, "WS_SAE_006",
                    messageSource.getMessage("WS_SAE_006", new String[] {}, "", Locale.getDefault()));
        }

        StudyParticipantAssignment spa = new StudyParticipantAssignment();
        StudySite studySite = new StudySite();
        Organization siteOrg = new LocalOrganization();
        siteOrg.setNciInstituteCode(study.getParticipantSiteIdentifier());
        studySite.setOrganization(siteOrg);
        spa.setStudySite(studySite);

        return processAdverseEvents(evaluateAEsInputMessage.getStudy().getStudyIdentifier(),
                evaluateAEsInputMessage.getAdverseEvents(), spa,
                evaluateAEsInputMessage.getStudy().getTreatmentAssignmentCode());
    }

    public EvaluateAEsOutputMessage processAdverseEvents(String studyId, AdverseEvents adverseEvents,
            StudyParticipantAssignment assignment, String tacCode) throws CaaersFault {

        // Construct from the input Message.

        List<AdverseEvent> aes = new ArrayList<AdverseEvent>();
        Map<AdverseEvent, AdverseEventResult> mapAE2DTO = new HashMap<AdverseEvent, AdverseEventResult>();

        Study study = null;
        TreatmentAssignment tas = null;

        try {

            // Fetch the study from Database
            study = fetchStudy(studyId);

            AeTerminology terminology = study.getAeTerminology();

            for (AdverseEventType adverseEventDto : adverseEvents.getAdverseEvent()) {
                AdverseEvent ae = new AdverseEvent();
                converter.convertAdverseEventDtoToAdverseEventDomain(adverseEventDto, ae, terminology, null, null);
                aes.add(ae);

                AdverseEventResult result = new AdverseEventResult();
                result.setAdverseEvent(adverseEventDto);
                result.setRequiresReporting(false);

                mapAE2DTO.put(ae, result);
            }

            if (tacCode != null) {
                tas = resolveTreatmentAssignment(tacCode, study);
            }

        } catch (CaaersSystemException e) {
            throw Helper.createCaaersFault(DEF_ERR_MSG, e.getErrorCode(), e.getMessage());
        }

        // Populate AdverseEventReporting Period
        AdverseEventReportingPeriod period = new AdverseEventReportingPeriod();

        period.setAdverseEvents(aes);
        period.setAssignment(assignment);
        period.setTreatmentAssignment(tas);

        if (assignment.getStudySite() != null) {
            assignment.getStudySite().setStudy(study);
        }
        /**
         * Fill the reporting period into ae's.
         */
        for (AdverseEvent ae : aes) {
            ae.setReportingPeriod(period);
        }

        // Create the response Object.
        EvaluateAEsOutputMessage response = (EvaluateAEsOutputMessage) createResponseObject(RequestType.Evaluate);

        return (EvaluateAEsOutputMessage) fireSAERules(period, study, mapAE2DTO, RequestType.Evaluate, response);
    }

    /**
     * Retrieve TreatmentAssignment from the Study for the
     * TreatmentAssignmentCode
     * 
     * @param tacCode
     *            - String representing TreatmentAssignmentCode
     * @param study
     *            - instance of Study
     * @return instance of TreatmentAssignment
     */
    private TreatmentAssignment resolveTreatmentAssignment(String tacCode, Study study) {
        TreatmentAssignment ta = null;
        try {
            ta = treatmentAssignmentDao.getAssignmentsByStudyIdExactMatch(tacCode, study.getId());
        } catch (Exception e) {
            throw new CaaersSystemException("WS_SAE_002", messageSource.getMessage("WS_SAE_002",
                    new String[] { String.valueOf(study.getId()) }, "", Locale.getDefault()));
        }

        return ta;
    }

    /**
     * Fire the SAE on the Reporting Period Adverse Events.
     * @param reportingPeriod
     * @param study
     * @param mapAE2DTO
     * @return
     * @throws CaaersFault
     */

    private AEsOutputMessage fireSAERules(AdverseEventReportingPeriod reportingPeriod, Study study,
            Map<AdverseEvent, AdverseEventResult> mapAE2DTO, RequestType requestType, AEsOutputMessage response)
            throws CaaersFault {
        try {
            EvaluatedAdverseEventResults results = new EvaluatedAdverseEventResults();
            response.setEvaluatedAdverseEventResults(results);
            List<AdverseEventResult> aeResultList = results.getAdverseEventResult();

            //populate the output list with the AdverseEventResult objects created for each AdverseEventType
            aeResultList.addAll(mapAE2DTO.values());

            EvaluationResultDTO dto = evaluationService.evaluateSAERules(reportingPeriod);

            if (requestType.equals(RequestType.SaveEvaluate)) {
                findRecommendedActions(dto, reportingPeriod, (SaveAndEvaluateAEsOutputMessage) response);
                populateActionTextAndDueDate((SaveAndEvaluateAEsOutputMessage) response);
            }
            // create/update/delete AE recommended reports
            manageAdverseEventRecommendedReports(requestType, dto);

            //CAAERS-6316 If there are no recommended actions or if there is only 1 recommended action and is Withdraw, 
            // hasSae = false, otherwise true
            if (requestType.equals(RequestType.SaveEvaluate)) {
                boolean sae = (((SaveAndEvaluateAEsOutputMessage) response).getRecommendedActions() != null
                        && ((SaveAndEvaluateAEsOutputMessage) response).getRecommendedActions().size() > 1);
                if (!sae && ((SaveAndEvaluateAEsOutputMessage) response).getRecommendedActions() != null
                        && !((SaveAndEvaluateAEsOutputMessage) response).getRecommendedActions().isEmpty()) {
                    sae = !StringUtils.equals(
                            ((SaveAndEvaluateAEsOutputMessage) response).getRecommendedActions().get(0).getAction(),
                            WITHDRAW.getDisplayName());
                }
                ((SaveAndEvaluateAEsOutputMessage) response).setHasSAE(sae);
            }

            //retrieve all the SAEs identified by rules engine.
            Set<AdverseEvent> evaluatedAdverseEvents = dto.getAllEvaluatedAdverseEvents();

            //Mark the Requires reporting flag on AE
            for (AdverseEvent ae : evaluatedAdverseEvents) {

                // find DTO object corresponding to Adverse Event.
                AdverseEventResult aeDTO = null;
                if (requestType.equals(RequestType.SaveEvaluate)) {
                    aeDTO = findAdverseEvent(ae, mapAE2DTO);
                } else {
                    aeDTO = mapAE2DTO.get(ae);
                }
                if (aeDTO != null) {
                    aeDTO.setRequiresReporting(ae.getRequiresReporting());
                }
            }

        } catch (Exception e) {
            logger.error("Exception Occured when processing rules; " + e.toString(), e);
            throw Helper.createCaaersFault(DEF_ERR_MSG, "WS_SAE_001",
                    messageSource.getMessage("WS_SAE_001", new String[] {}, "", Locale.getDefault()));
        }

        return response;
    }

    private void populateActionTextAndDueDate(SaveAndEvaluateAEsOutputMessage response) {
        for (RecommendedActions recActions : response.getRecommendedActions()) {
            recActions.setActionText(recActions.getAction().substring(0, 1).toUpperCase()
                    + recActions.getAction().substring(1, recActions.getAction().length()).toLowerCase() + " the "
                    + recActions.getReport());
        }
    }

    private void refreshReportIndexMap(Map<Integer, ExpeditedAdverseEventReport> aeReportIndexMap) {
        Integer ZERO = new Integer(0);
        aeReportIndexMap.put(ZERO, null);
    }

    /**
     * Find the Actions to be recommended to the client
     * @param evaluationResult
     * @param reportingPeriod
     * @param response
     */
    private void findRecommendedActions(EvaluationResultDTO evaluationResult,
            AdverseEventReportingPeriod reportingPeriod, SaveAndEvaluateAEsOutputMessage response) {

        List<RecommendedActions> recommendedActions = new ArrayList<RecommendedActions>();

        response.setRecommendedActions(recommendedActions);

        Map<Integer, ExpeditedAdverseEventReport> aeReportIndexMap = reportingPeriod.populateAeReportIndexMap();

        refreshReportIndexMap(aeReportIndexMap);

        Map<Integer, List<ReportTableRow>> recommendedReportTableMap = new LinkedHashMap<Integer, List<ReportTableRow>>();

        Map<Integer, List<ReportTableRow>> applicableReportTableMap = new LinkedHashMap<Integer, List<ReportTableRow>>();

        recommendedActionService.generateRecommendedReportTable(evaluationResult, aeReportIndexMap,
                recommendedReportTableMap);

        ApplicableReportDefinitionsDTO applicableReportDefinitions = evaluationService
                .applicableReportDefinitions(reportingPeriod.getStudy(), reportingPeriod.getAssignment());

        recommendedActionService.refreshApplicableReportTable(evaluationResult, aeReportIndexMap,
                applicableReportTableMap, applicableReportDefinitions);

        // This data structure is used for handling Create/Edit Scneario.
        List<ReportTableRow> ignoredRows = getListOfIgnoredRows(recommendedReportTableMap);

        for (Integer aeReportId : recommendedReportTableMap.keySet()) {

            List<ReportTableRow> applicableRows = applicableReportTableMap.get(aeReportId);

            if (applicableRows != null) {
                findMatchingRecommendations(applicableRows, recommendedReportTableMap.get(aeReportId),
                        recommendedActions, ignoredRows);
            }
        }
    }

    /**
     * Find the corresponding applicable report table value of the recommended table value.
     * @param applicableRows
     * @param reportName
     * @return
     */

    private ReportTableRow findApplicableRow(List<ReportTableRow> applicableRows, String reportName) {

        for (ReportTableRow row : applicableRows) {
            if (row.getReportDefinition().getName().equals(reportName)) {
                return row;
            }
        }

        return null;
    }

    /**
     * Maintain the list of rows with preSelectedFlag is true.
     * @param applicableRows
     * @return
     */
    private List<ReportTableRow> findPreSelectedRows(List<ReportTableRow> applicableRows) {
        List<ReportTableRow> preSelectedRows = new ArrayList<ReportTableRow>();
        for (ReportTableRow row : applicableRows) {
            if (row.isPreSelected()) {
                preSelectedRows.add(row);
            }
        }

        return preSelectedRows;
    }

    /**
     * Return true ifAny of the report of the Group is checked.
     * @param row
     * @param preselectedRows
     * @return
     */

    private boolean isAnyInGroupChecked(ReportTableRow row, List<ReportTableRow> preselectedRows) {
        boolean isfound = false;
        for (ReportTableRow preSelectedRow : preselectedRows) {
            if (preSelectedRow.getReportDefinition() != null && row.getReportDefinition() != null
                    && preSelectedRow.getReportDefinition().getGroup() != null
                    && row.getReportDefinition().getGroup() != null && preSelectedRow.getReportDefinition()
                            .getGroup().getName().equals(row.getReportDefinition().getGroup().getName())) {
                isfound = true;
                break;
            }
        }

        return isfound;

    }

    /**
     * Convert the Report Table Row to Action object for webservice to send the output.
     * @param row
     * @param preselectedRows
     * @return
     */
    private RecommendedActions returnActionFromRow(ReportTableRow row, List<ReportTableRow> preselectedRows) {
        RecommendedActions action = new RecommendedActions();

        action.setReport(row.getReportDefinition().getName());
        if (row.isPreSelected()) { // If the row is pre-selected.

            action.setAction(row.getAction().getDisplayName());
            action.setDue(row.getDue());
            action.setStatus(row.getStatus());
            action.setDueDate(generateDueDate(row.getReportDefinition(), row.getBaseDate()));

        } else {
            if (isAnyInGroupChecked(row, preselectedRows)) { // If the any one of the Report in the group is selected.

                action.setAction(row.getGrpAction().getDisplayName());
                action.setDue(row.getGrpDue());
                action.setDueDate(generateDueDate(row.getReportDefinition(), row.getBaseDate()));
                action.setStatus(row.getGrpStatus());

            } else { // Other Actions.

                action.setAction(row.getOtherAction().getDisplayName());
                action.setDue(row.getOtherDue());
                action.setDueDate(generateDueDate(row.getReportDefinition(), row.getBaseDate()));
                action.setStatus(row.getOtherStatus());
            }

        }

        return action;
    }

    /**
     * Return the matched is true only when the Action is Edit.
     * @param recommendedReportTableMap
     * @param ignoredRow
     * @return
     */
    private boolean findMatchingRecommendedRow(Map<Integer, List<ReportTableRow>> recommendedReportTableMap,
            ReportTableRow ignoredRow) {
        boolean isFound = false;

        for (Integer aeReportId : recommendedReportTableMap.keySet()) {
            if (aeReportId == 0)
                continue;

            for (ReportTableRow row : recommendedReportTableMap.get(aeReportId)) {
                if ((recommendedReportTableMap.size() == 2) && row.getAction().getDisplayName().equals("Edit")) { // Condition will Evaluate 
                    isFound = true;
                    break;
                }
            }

            if (isFound)
                break;

        }

        return isFound;
    }

    /**
     * This is to handle the special case of Create-Edit Scenario.
     *
     */
    private List<ReportTableRow> getListOfIgnoredRows(
            Map<Integer, List<ReportTableRow>> recommendedReportTableMap) {
        List<ReportTableRow> ignoredRows = new ArrayList<ReportTableRow>();
        Integer Zero = new Integer(0);
        List<ReportTableRow> rows = recommendedReportTableMap.get(Zero);

        if (rows == null)
            return ignoredRows;

        for (ReportTableRow row : rows) {
            boolean isFound = findMatchingRecommendedRow(recommendedReportTableMap, row);
            if (isFound)
                ignoredRows.add(row);
        }

        return ignoredRows;

    }

    private boolean isMatchedIgnoredRow(List<ReportTableRow> ignoredRows, ReportTableRow recommRow) {
        boolean isFound = false;

        for (ReportTableRow row : ignoredRows) {
            if (row.getAction().equals(recommRow.getAction())
                    && row.getReportDefinition().getName().equals(recommRow.getReportDefinition().getName())) {
                isFound = true;
                break;
            }
        }

        return isFound;
    }

    /**
     * Return the Recommendations required report group. ( AMend/WithDraw/Create/Edit).
     *
     * @param applicableRows
     * @param recommRows
     * @param recommendedActions
     */

    private void findMatchingRecommendations(List<ReportTableRow> applicableRows, List<ReportTableRow> recommRows,
            List<RecommendedActions> recommendedActions, List<ReportTableRow> ignoredRows) {
        // Find the report group of the pre-selected row.
        List<ReportTableRow> preselectedRows = findPreSelectedRows(applicableRows);

        for (ReportTableRow recommRow : recommRows) {
            ReportTableRow applicableRow = findApplicableRow(applicableRows,
                    recommRow.getReportDefinition().getName());

            if (applicableRow == null || isMatchedIgnoredRow(ignoredRows, recommRow))
                continue;

            RecommendedActions action = returnActionFromRow(applicableRow, preselectedRows);
            recommendedActions.add(action);

            boolean withdrawOrAmend = StringUtils.equals(action.getAction(), WITHDRAW.getDisplayName())
                    || StringUtils.equals(action.getAction(), AMEND.getDisplayName());

            if (withdrawOrAmend) {
                for (ReportTableRow preselectedRow : preselectedRows) {
                    if (preselectedRow != null) {

                        // Update the Group Due.
                        action.setDue(applicableRow.getGrpDue());

                        // find if the report already exists.
                        RecommendedActions preSelectedAction = null;
                        for (RecommendedActions actionIter : recommendedActions) {

                            if (actionIter.getReport().equals(preselectedRow.getReportDefinition().getName())
                                    && actionIter.getAction().equals("Create")) {
                                preSelectedAction = actionIter;
                                break;
                            }
                        }
                        if (preSelectedAction == null) { // If the Create Action is not occured before, Create one manually.
                            preSelectedAction = new RecommendedActions();
                            preSelectedAction.setAction(ReportDefinitionWrapper.ActionType.CREATE.toString()); // Make it Create.
                            preSelectedAction.setStatus("Not Started");
                            preSelectedAction.setReport(preselectedRow.getReportDefinition().getName());
                            preSelectedAction
                                    .setDue(preselectedRow.getReportDefinition().getExpectedDisplayDueDate());
                            preSelectedAction
                                    .setDueDate(generateDueDate(preselectedRow.getReportDefinition(), new Date()));

                            recommendedActions.add(preSelectedAction);
                        } else { // If it is already occured, Update the due time.

                            ReportTableRow createAction = findApplicableRow(applicableRows,
                                    preSelectedAction.getReport());
                            preSelectedAction.setDue(createAction.getDue());
                            preSelectedAction.setDueDate(generateDueDate(createAction.getReportDefinition(),
                                    createAction.getBaseDate()));

                            //ignore the recomended due calculate the due date from today
                            action.setDue(
                                    preselectedRow.getReportDefinition().getExpectedDisplayDueDate(new Date()));
                            action.setDueDate(generateDueDate(preselectedRow.getReportDefinition(), new Date()));

                        }

                    }

                }

                if (StringUtils.equals(action.getAction(), WITHDRAW.getDisplayName())) {
                    action.setDue("");
                    action.setDueDate("");
                }
            }
        }

    }

    public AdverseEventResult findAdverseEvent(AdverseEvent thatAe,
            Map<AdverseEvent, AdverseEventResult> mapAE2DTO) {
        for (AdverseEvent thisAe : mapAE2DTO.keySet()) {
            //are Ids matching ?
            if (thatAe.getExternalId() != null && thisAe.getExternalId() != null
                    && StringUtils.equals(thisAe.getExternalId(), thatAe.getExternalId()))
                return mapAE2DTO.get(thisAe);

            //Compare Grade
            if (thatAe.getGrade() != null && thisAe.getGrade() != null
                    && !thisAe.getGrade().equals(thatAe.getGrade()))
                continue;

            //are dates matching ?
            if (DateUtils.compareDate(thisAe.getStartDate(), thatAe.getStartDate()) != 0)
                continue;
            if (DateUtils.compareDate(thisAe.getEndDate(), thatAe.getEndDate()) != 0)
                continue;
            if (thisAe.getEventApproximateTime() != null && thatAe.getEventApproximateTime() != null
                    && !(thisAe.getEventApproximateTime().equals(thatAe.getEventApproximateTime())))
                continue;

            //is the term matching ?
            if (thisAe.getAdverseEventCtcTerm() != null) {
                //ctc terminology
                AdverseEventCtcTerm thisCtcTerm = thisAe.getAdverseEventCtcTerm();
                AdverseEventCtcTerm thatCtcTerm = thatAe.getAdverseEventCtcTerm();
                if ((thisCtcTerm == null && thatCtcTerm != null) || (thatCtcTerm == null && thisCtcTerm != null))
                    continue;
                if ((thisCtcTerm != null && thatCtcTerm != null)
                        && thisCtcTerm.getTerm().getId() != thatCtcTerm.getTerm().getId())
                    continue;
                if (!StringUtils.equals(thisAe.getOtherSpecify(), thatAe.getOtherSpecify()))
                    continue;

                LowLevelTerm thisLLT = thisAe.getLowLevelTerm();
                LowLevelTerm thatLLT = thatAe.getLowLevelTerm();
                if ((thisLLT == null && thatLLT != null) || (thatLLT == null && thisLLT != null))
                    continue;
                if ((thisLLT != null && thatLLT != null) && thisLLT.getId() != thatLLT.getId())
                    continue;

            } else {
                //MedDRA terminology
                AdverseEventMeddraLowLevelTerm thisMedDRATerm = thisAe.getAdverseEventMeddraLowLevelTerm();
                AdverseEventMeddraLowLevelTerm thatMedDRATerm = thatAe.getAdverseEventMeddraLowLevelTerm();
                if ((thisMedDRATerm != null && thatMedDRATerm != null)
                        && thisMedDRATerm.getLowLevelTerm().getId() != thatMedDRATerm.getLowLevelTerm().getId())
                    continue;
            }
            //found a match
            return mapAE2DTO.get(thisAe);
        }
        return null;
    }

    private String constructLinkToReport(int studyId, int participantId, int rpId) {
        String hostname = messageSource.getMessage("rules.hostname", new String[] {}, "", Locale.getDefault());
        if (hostname == "" || hostname == null || hostname.contains("<hostname>")) {
            try {
                hostname = java.net.InetAddress.getLocalHost().getHostName();
            } catch (UnknownHostException ue) {
                hostname = "localhost";
            }
        }

        String port = messageSource.getMessage("rules.port", new String[] {}, "", Locale.getDefault());
        if (port == "" || port == null) {
            port = "8443";
        }

        String linkToReport = "https://" + hostname + ":" + port + "/caaers/pages/ae/captureRoutine?study="
                + studyId + "&participant=" + participantId + "&adverseEventReportingPeriod=" + rpId
                + "&_page=0&_target2=2&displayReportingPeriod=true&addReportingPeriodBinder=true";
        return linkToReport;
    }

    private AEsOutputMessage createResponseObject(RequestType requestType) {
        AEsOutputMessage response = null;
        if (requestType.equals(RequestType.Evaluate)) {
            response = new EvaluateAEsOutputMessage();
        } else {
            response = new SaveAndEvaluateAEsOutputMessage();
        }
        return response;
    }

    public AdverseEventConverter getConverter() {
        return converter;
    }

    public void setConverter(AdverseEventConverter converter) {
        this.converter = converter;
    }

    private Study fetchStudy(String identifier) {
        if (StringUtils.isEmpty(identifier)) {
            throw new CaaersSystemException("WS_SAE_004",
                    messageSource.getMessage("WS_SAE_004", new String[] {}, "", Locale.getDefault()));
        }
        Study study = null;
        try {
            Identifier si = new Identifier();
            si.setValue(identifier);
            study = studyDao.getByIdentifier(si);
        } catch (Exception e) {
            throw new CaaersSystemException("WS_GEN_001",
                    messageSource.getMessage("WS_GEN_001", new String[] {}, "", Locale.getDefault()));
        }
        if (study == null) {
            throw new CaaersSystemException("WS_SAE_005",
                    messageSource.getMessage("WS_SAE_005", new String[] { identifier }, "", Locale.getDefault()));
        }
        return study;
    }

    private void manageAdverseEventRecommendedReports(RequestType requestType, EvaluationResultDTO dto) {
        Map<AdverseEvent, List<ReportDefinition>> adverseEventReportDefinitionMap = dto
                .getAdverseEventRecommendedReportsMap();
        for (Map.Entry<AdverseEvent, List<ReportDefinition>> entry : adverseEventReportDefinitionMap.entrySet()) {
            AdverseEvent ae = entry.getKey();
            List<ReportDefinition> rds = entry.getValue();
            // Find out if the AE is serious
            if (rds != null && rds.size() > 0) {
                // update existing AE recommendation report or create new one 
                Iterator<ReportDefinition> reportDefinitionIterator = rds.iterator();
                while (reportDefinitionIterator.hasNext()) {
                    ReportDefinition reportDefinition = reportDefinitionIterator.next();
                    AdverseEventRecommendedReport aeRecomReport;
                    List<AdverseEventRecommendedReport> dbAeRecomReports = adverseEventRecommendedReportDao
                            .searchAdverseEventRecommendedReportsByAdverseEvent(ae);
                    if (dbAeRecomReports != null && !dbAeRecomReports.isEmpty()) {
                        // AE recommendation report already exists
                        aeRecomReport = dbAeRecomReports.get(0);
                        if (!reportDefinition.getOrganization()
                                .equals(aeRecomReport.getReportDefinition().getOrganization())
                                || !reportDefinition.getGroup()
                                        .equals(aeRecomReport.getReportDefinition().getGroup())) {
                            // CAAERS-6961: Only if there is a change in the recommended Report Org or Group set the AE
                            // reported flag to false, otherwise the AE is already considered added to the report.
                            aeRecomReport.setAeReported(false);
                        }
                    } else {
                        // create AE recommendation report
                        aeRecomReport = new AdverseEventRecommendedReport();
                        aeRecomReport.setAdverseEvent(ae);
                        aeRecomReport.setAeReported(false);
                    }

                    aeRecomReport.setReportDefinition(reportDefinition);
                    aeRecomReport.setDueDate(reportDefinition.getExpectedDueDate(ae.getGradedDate()));
                    adverseEventRecommendedReportDao.save(aeRecomReport);
                }
            } else {
                // delete old serious AE recommendation reports that are no longer serious in current evaluation
                List<AdverseEventRecommendedReport> dbAeRecomReports = adverseEventRecommendedReportDao
                        .searchAdverseEventRecommendedReportsByAdverseEvent(ae);
                if (dbAeRecomReports != null && !dbAeRecomReports.isEmpty()) {
                    AdverseEventRecommendedReport aeRecomReport = dbAeRecomReports.get(0);
                    adverseEventRecommendedReportDao.delete(aeRecomReport);
                }
            }
        }
    }

    private String generateDueDate(ReportDefinition rd, Date baseDate) {
        if (rd != null && baseDate != null)
            return DateUtils.formatToWSResponseDateWithTimeZone(rd.getExpectedDueDate(baseDate));
        return "";
    }

    public ParticipantDao getParticipantDao() {
        return participantDao;
    }

    public void setParticipantDao(ParticipantDao participantDao) {
        this.participantDao = participantDao;
    }

    public StudyDao getStudyDao() {
        return studyDao;
    }

    public void setStudyDao(StudyDao studyDao) {
        this.studyDao = studyDao;
    }

    public TreatmentAssignmentDao getTreatmentAssignmentDao() {
        return treatmentAssignmentDao;
    }

    public void setTreatmentAssignmentDao(TreatmentAssignmentDao treatmentAssignmentDao) {
        this.treatmentAssignmentDao = treatmentAssignmentDao;
    }

    public EvaluationService getAdverseEventEvaluationService() {
        return evaluationService;
    }

    public void setAdverseEventEvaluationService(EvaluationService evaluationService) {
        this.evaluationService = evaluationService;
    }

    public MessageSource getMessageSource() {
        return messageSource;
    }

    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public SAEAdverseEventReportingPeriodConverter getReportingPeriodConverter() {
        return reportingPeriodConverter;
    }

    public void setReportingPeriodConverter(SAEAdverseEventReportingPeriodConverter reportingPeriodConverter) {
        this.reportingPeriodConverter = reportingPeriodConverter;
    }

    public RecommendedActionService getRecommendedActionService() {
        return recommendedActionService;
    }

    public void setRecommendedActionService(RecommendedActionService recommendedActionService) {
        this.recommendedActionService = recommendedActionService;
    }

    public void setAdverseEventRecommendedReportDao(
            AdverseEventRecommendedReportDao adverseEventRecommendedReportDao) {
        this.adverseEventRecommendedReportDao = adverseEventRecommendedReportDao;
    }

    /**
     * To Create or Update Advese Events.
     * Sync Flag is used only incase of SAE Evaluation service, As service can soft-delete the Adverse Events.
     * @param rpSrc
     * @param errors
     * @param syncFlag
     * @return
     */

    private AdverseEventReportingPeriod createOrUpdateAdverseEvents(AdverseEventReportingPeriod rpSrc,
            ValidationErrors errors, boolean syncFlag) {

        Study study = fetchStudy(rpSrc.getStudy().getFundingSponsorIdentifierValue());
        if (study == null) {
            logger.error("Study not present in caAERS with the sponsor identifier : "
                    + rpSrc.getStudy().getFundingSponsorIdentifierValue());
            errors.addValidationError(
                    "WS_AEMS_003", "Study with sponsor identifier "
                            + rpSrc.getStudy().getFundingSponsorIdentifierValue() + " does not exist in caAERS",
                    rpSrc.getStudy().getFundingSponsorIdentifierValue());
            return null;
        }
        //async update study removed because the updates would most likely happen after this process is finished.

        //migrate the domain object
        AdverseEventReportingPeriod rpDest = new AdverseEventReportingPeriod();
        DomainObjectImportOutcome<AdverseEventReportingPeriod> rpOutcome = new DomainObjectImportOutcome<AdverseEventReportingPeriod>();
        reportingPeriodMigrator.migrate(rpSrc, rpDest, rpOutcome);
        logger.info("Reporting period migration result :" + String.valueOf(rpOutcome.getMessages()));
        if (rpOutcome.hasErrors()) {
            //translate error and create a response.
            logger.error("Errors while migrating :" + String.valueOf(rpOutcome.getErrorMessages()));
            errors.addValidationErrors(rpOutcome.getValidationErrors().getErrors());
            return null;
        }

        //check if we need the create path or update path.
        String tac = rpDest.getTreatmentAssignment() != null ? rpDest.getTreatmentAssignment().getCode() : null;
        String epochName = rpDest.getEpoch() != null ? rpDest.getEpoch().getName() : null;
        AdverseEventReportingPeriod rpFound = rpDest.getAssignment().findReportingPeriod(rpDest.getExternalId(),
                rpDest.getStartDate(), rpDest.getEndDate(), rpDest.getCycleNumber(), epochName, tac);

        ArrayList<AdverseEventReportingPeriod> reportingPeriodList = new ArrayList<AdverseEventReportingPeriod>(
                rpDest.getAssignment().getActiveReportingPeriods());
        if (rpFound != null) {
            // This is used only incase of SAE Evaluation Service.
            if (syncFlag) {
                syncAdverseEventWithSrc(rpFound, rpSrc);
            }
            int i = findIndexFromReportPeriodList(reportingPeriodList, rpFound);
            if (i >= 0)
                reportingPeriodList.remove(i);
        }

        ValidationErrors dateValidationErrors = validateRepPeriodDates(rpDest, reportingPeriodList,
                rpDest.getAssignment().getStartDateOfFirstCourse(), rpDest.getEpoch());
        logger.info("Reporting period validation result :" + String.valueOf(dateValidationErrors));
        if (dateValidationErrors.hasErrors()) {
            //translate errors and create a response
            logger.error("Errors while migrating :" + String.valueOf(dateValidationErrors));
            errors.addValidationErrors(dateValidationErrors.getErrors());
            return null;
        }

        //validate adverse events
        for (AdverseEvent adverseEvent : rpDest.getAdverseEvents()) {
            if (adverseEvent.getGradedDate() == null)
                adverseEvent.setGradedDate(new Date());
            Set<ConstraintViolation<AdverseEvent>> constraintViolations = validator.validate(adverseEvent,
                    AdverseEventGroup.class, Default.class);
            if (!constraintViolations.isEmpty()) {
                //translate errors to response.
                for (ConstraintViolation<AdverseEvent> v : constraintViolations) {
                    errors.addValidationError("WS_GEN_006", v.getMessage(), v.getPropertyPath());
                }
                return null;
            }
        }
        // validate Reporting Period.
        AdverseEventReportingPeriod rpTarget = rpFound;
        if (rpTarget == null)
            rpTarget = rpDest;

        Set<ConstraintViolation<AdverseEventReportingPeriod>> constraintViolations = validator.validate(rpTarget,
                CourseCycleGroup.class, Default.class);
        if (!constraintViolations.isEmpty()) {
            //translate errors to response.
            for (ConstraintViolation<AdverseEventReportingPeriod> v : constraintViolations) {
                errors.addValidationError("WS_GEN_006", v.getMessage(), v.getPropertyPath());
            }
            return null;
        }

        if (rpFound == null) {
            //new reporting period
            rpFound = rpDest;
            rpFound.getAssignment().addReportingPeriod(rpFound);
            // Validate the Reporting Period before saving.
            adverseEventValidatior.validate(rpFound, rpFound.getStudy(), errors);
            adverseEventReportingPeriodDao.save(rpFound);
            if (configuration.get(Configuration.ENABLE_WORKFLOW)) {
                Long wfId = adverseEventRoutingAndReviewRepository.enactReportingPeriodWorkflow(rpFound);
                logger.debug("Enacted workflow : " + wfId);
            }
        } else {
            //existing reporting period.
            reportingPeriodSynchronizer.migrate(rpDest, rpFound, rpOutcome);
            // Validate the Reporting Period before saving.
            adverseEventValidatior.validate(rpFound, rpFound.getStudy(), errors);
            if (errors.hasErrors()) {
                logger.error(
                        "Error(s) while validating with Adverse Event " + String.valueOf(errors.getErrorCount()));
                return null;
            }
            adverseEventReportingPeriodDao.save(rpFound);

        }
        return rpFound;
    }

    /**
     * Method to save the reporting period from outside the API methods. This can be used for re-saving the reporting period
     * as in the case of Adverse Event / requiresReporting flag.
     * @param reportingPeriod
     * @return
     */

    private void saveReportingPeriod(AdverseEventReportingPeriod reportingPeriod) {
        adverseEventReportingPeriodDao.save(reportingPeriod);
    }

    /**
     * Sync the adverse Events with Input source, as SAE service expects the complete list of adverse events.
     * @param rpFound
     * @param rpSrc
     */
    private void syncAdverseEventWithSrc(AdverseEventReportingPeriod rpFound, AdverseEventReportingPeriod rpSrc) {

        for (AdverseEvent ae : rpFound.getAdverseEvents()) {
            if (rpSrc.findAdverseEventByIdTermAndDates(ae) == null) { // If the reporting period is not found in  the source
                ae.setRetiredIndicator(true);
            }
        }

    }

    private ValidationErrors validateRepPeriodDates(AdverseEventReportingPeriod rPeriod,
            List<AdverseEventReportingPeriod> rPeriodList, Date firstCourseDate, Epoch epoch) {

        ValidationErrors errors = new ValidationErrors();
        Date startDate = rPeriod.getStartDate();
        Date endDate = rPeriod.getEndDate();

        // Check if the start date is equal to or before the end date.
        if (firstCourseDate != null && startDate != null && (firstCourseDate.getTime() - startDate.getTime() > 0)) {
            errors.addValidationError("WS_AEMS_014",
                    "Start date of this course/cycle cannot be earlier than the Start date of first course/cycle");
        }

        if (startDate != null && endDate != null && (endDate.getTime() - startDate.getTime() < 0)) {
            errors.addValidationError("WS_AEMS_015", "Course End date cannot be earlier than Start date.");
        }

        // Check if the start date is equal to end date.
        // This is allowed only for Baseline reportingPeriods and not for other
        // reporting periods.

        if (epoch != null && !epoch.getName().equals("Baseline")) {
            if (endDate != null && startDate.equals(endDate)) {
                errors.addValidationError("WS_AEMS_016",
                        "For Non-Baseline treatment type Start date cannot be equal to End date.");
            }

        }

        // Check if the start date - end date for the reporting Period overlaps
        // with the date range of an existing Reporting Period.
        for (AdverseEventReportingPeriod aerp : rPeriodList) {
            Date sDate = aerp.getStartDate();
            Date eDate = aerp.getEndDate();

            if (!aerp.getId().equals(rPeriod.getId())) {

                // CAAERS-7323 - If TAC or otherTreatmentAssignmentDescription of the passed in Reporting Period is different than the Reporting Period in loop, 
                // skip the validation for overlapping reporting period dates
                if (rPeriod.hasNoCommonTacOrOtherTreatmentAssignmentDescription((aerp))) {
                    continue;
                }

                // we should make sure that no existing Reporting Period, start
                // date falls, in-between these dates.
                if (startDate != null && endDate != null) {
                    if (DateUtils.compareDate(sDate, startDate) >= 0 && DateUtils.compareDate(sDate, endDate) < 0) {
                        errors.addValidationError("WS_AEMS_017",
                                "Course/cycle cannot overlap with an existing course/cycle.");
                        break;
                    }
                } else if (startDate != null && DateUtils.compareDate(sDate, startDate) == 0) {
                    errors.addValidationError("WS_AEMS_017",
                            "Course/cycle cannot overlap with an existing course/cycle.");
                    break;
                }

                // newly created reporting period start date, should not fall
                // within any other existing reporting periods
                if (sDate != null && eDate != null) {
                    if (DateUtils.compareDate(sDate, startDate) <= 0
                            && DateUtils.compareDate(startDate, eDate) < 0) {
                        errors.addValidationError("WS_AEMS_017",
                                "Course/cycle cannot overlap with an existing course/cycle.");
                        break;
                    }
                } else if (sDate != null && DateUtils.compareDate(sDate, startDate) == 0) {
                    errors.addValidationError("WS_AEMS_017",
                            "Course/cycle cannot overlap with an existing course/cycle.");
                    break;
                }
            }

            // If the epoch of reportingPeriod is not - Baseline , then it
            // cannot be earlier than a Baseline
            if (epoch != null && epoch.getName().equals("Baseline")) {
                if (aerp.getEpoch() != null && (!aerp.getEpoch().getName().equals("Baseline"))) {
                    if (DateUtils.compareDate(sDate, startDate) < 0) {
                        errors.addValidationError("WS_AEMS_018",
                                "Baseline treatment type cannot start after an existing Non-Baseline treatment type.");
                        return errors;
                    }
                }
            } else {
                if (aerp.getEpoch() != null && aerp.getEpoch().getName().equals("Baseline")) {
                    if (DateUtils.compareDate(startDate, sDate) < 0) {
                        errors.addValidationError("WS_AEMS_019",
                                "Non-Baseline treatment type cannot start before an existing Baseline treatment type.");
                        return errors;
                    }
                }
            }

            // Duplicate Baseline check
            if (epoch != null && epoch.getName().equals("Baseline")) {
                // Iterating through the already anything exists with the treatment type Baseline.
                for (AdverseEventReportingPeriod rp : rPeriodList) {

                    if (rp.getEpoch() != null && rp.getEpoch().getName() != null
                            && rp.getEpoch().getName().equals("Baseline")) {
                        errors.addValidationError("WS_AEMS_085", "A Baseline treatment type already exists");
                        break;
                    }
                }

            }

        }
        return errors;

    }

    private int findIndexFromReportPeriodList(List<AdverseEventReportingPeriod> reportingPeriodList,
            AdverseEventReportingPeriod rpFound) {
        int i = 0;
        for (AdverseEventReportingPeriod rp : reportingPeriodList) {
            if (rp.getId().equals(rpFound.getId())) {
                return i;
            }
            i++;
        }

        return -1;

    }

    public EvaluationService getEvaluationService() {
        return evaluationService;
    }

    public void setEvaluationService(EvaluationService evaluationService) {
        this.evaluationService = evaluationService;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    public AdverseEventReportingPeriodDao getAdverseEventReportingPeriodDao() {
        return adverseEventReportingPeriodDao;
    }

    public void setAdverseEventReportingPeriodDao(AdverseEventReportingPeriodDao adverseEventReportingPeriodDao) {
        this.adverseEventReportingPeriodDao = adverseEventReportingPeriodDao;
    }

    public AdverseEventReportingPeriodSynchronizer getReportingPeriodSynchronizer() {
        return reportingPeriodSynchronizer;
    }

    public void setReportingPeriodSynchronizer(
            AdverseEventReportingPeriodSynchronizer reportingPeriodSynchronizer) {
        this.reportingPeriodSynchronizer = reportingPeriodSynchronizer;
    }

    public AdverseEventReportingPeriodMigrator getReportingPeriodMigrator() {
        return reportingPeriodMigrator;
    }

    public void setReportingPeriodMigrator(AdverseEventReportingPeriodMigrator reportingPeriodMigrator) {
        this.reportingPeriodMigrator = reportingPeriodMigrator;
    }

    public AdverseEventValidatior getAdverseEventValidatior() {
        return adverseEventValidatior;
    }

    public void setAdverseEventValidatior(AdverseEventValidatior adverseEventValidatior) {
        this.adverseEventValidatior = adverseEventValidatior;
    }

    public AdverseEventRoutingAndReviewRepository getAdverseEventRoutingAndReviewRepository() {
        return adverseEventRoutingAndReviewRepository;
    }

    public void setAdverseEventRoutingAndReviewRepository(
            AdverseEventRoutingAndReviewRepository adverseEventRoutingAndReviewRepository) {
        this.adverseEventRoutingAndReviewRepository = adverseEventRoutingAndReviewRepository;
    }

    public Validator getValidator() {
        return validator;
    }

    public void setValidator(Validator validator) {
        this.validator = validator;
    }

    public AdverseEventRecommendedReportDao getAdverseEventRecommendedReportDao() {
        return adverseEventRecommendedReportDao;
    }

}