edu.northwestern.bioinformatics.studycalendar.service.StudyService.java Source code

Java tutorial

Introduction

Here is the source code for edu.northwestern.bioinformatics.studycalendar.service.StudyService.java

Source

/*L
 * Copyright Northwestern University.
 *
 * Distributed under the OSI-approved BSD 3-Clause License.
 * See http://ncip.github.io/psc/LICENSE.txt for details.
 */

package edu.northwestern.bioinformatics.studycalendar.service;

import edu.northwestern.bioinformatics.studycalendar.StudyCalendarError;
import edu.northwestern.bioinformatics.studycalendar.StudyCalendarValidationException;
import edu.northwestern.bioinformatics.studycalendar.core.accesscontrol.ApplicationSecurityManager;
import edu.northwestern.bioinformatics.studycalendar.dao.ActivityDao;
import edu.northwestern.bioinformatics.studycalendar.dao.DaoFinder;
import edu.northwestern.bioinformatics.studycalendar.dao.DeletableDomainObjectDao;
import edu.northwestern.bioinformatics.studycalendar.dao.PlannedCalendarDao;
import edu.northwestern.bioinformatics.studycalendar.dao.ScheduledActivityDao;
import edu.northwestern.bioinformatics.studycalendar.dao.SiteDao;
import edu.northwestern.bioinformatics.studycalendar.dao.SpringDaoFinder;
import edu.northwestern.bioinformatics.studycalendar.dao.StudyDao;
import edu.northwestern.bioinformatics.studycalendar.domain.Activity;
import edu.northwestern.bioinformatics.studycalendar.domain.Child;
import edu.northwestern.bioinformatics.studycalendar.domain.Epoch;
import edu.northwestern.bioinformatics.studycalendar.domain.Notification;
import edu.northwestern.bioinformatics.studycalendar.domain.Parent;
import edu.northwestern.bioinformatics.studycalendar.domain.Period;
import edu.northwestern.bioinformatics.studycalendar.domain.PlanTreeNode;
import edu.northwestern.bioinformatics.studycalendar.domain.PlannedActivity;
import edu.northwestern.bioinformatics.studycalendar.domain.PlannedCalendar;
import edu.northwestern.bioinformatics.studycalendar.domain.Population;
import edu.northwestern.bioinformatics.studycalendar.domain.ScheduledActivity;
import edu.northwestern.bioinformatics.studycalendar.domain.ScheduledActivityMode;
import edu.northwestern.bioinformatics.studycalendar.domain.ScheduledCalendar;
import edu.northwestern.bioinformatics.studycalendar.domain.ScheduledStudySegment;
import edu.northwestern.bioinformatics.studycalendar.domain.Site;
import edu.northwestern.bioinformatics.studycalendar.domain.Study;
import edu.northwestern.bioinformatics.studycalendar.domain.StudySegment;
import edu.northwestern.bioinformatics.studycalendar.domain.StudySite;
import edu.northwestern.bioinformatics.studycalendar.domain.StudySubjectAssignment;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.Add;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.Amendment;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.Change;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.ChangeAction;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.Changeable;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.ChildrenChange;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.Delta;
import edu.northwestern.bioinformatics.studycalendar.domain.delta.PlannedCalendarDelta;
import edu.northwestern.bioinformatics.studycalendar.security.authorization.PscRole;
import edu.northwestern.bioinformatics.studycalendar.security.authorization.PscUser;
import edu.northwestern.bioinformatics.studycalendar.service.presenter.StudyWorkflowStatus;
import edu.northwestern.bioinformatics.studycalendar.service.presenter.TemplateAvailability;
import edu.northwestern.bioinformatics.studycalendar.tools.MapBuilder;
import gov.nih.nci.cabig.ctms.dao.DomainObjectDao;
import gov.nih.nci.cabig.ctms.domain.AbstractMutableDomainObject;
import gov.nih.nci.cabig.ctms.domain.DomainObject;
import gov.nih.nci.cabig.ctms.lang.NowFactory;
import gov.nih.nci.cabig.ctms.suite.authorization.ProvisioningSession;
import gov.nih.nci.cabig.ctms.suite.authorization.ProvisioningSessionFactory;
import gov.nih.nci.cabig.ctms.suite.authorization.SuiteRole;
import gov.nih.nci.cabig.ctms.suite.authorization.SuiteRoleMembership;
import gov.nih.nci.security.authorization.domainobjects.User;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import static edu.nwu.bioinformatics.commons.CollectionUtils.putInMappedList;

@Transactional
public class StudyService {
    private final Logger log = LoggerFactory.getLogger(getClass());

    private ActivityDao activityDao;
    private StudyDao studyDao;
    private SiteDao siteDao;
    private DeltaService deltaService;
    private TemplateService templateService;
    private PlannedCalendarDao plannedCalendarDao;
    private NowFactory nowFactory;
    private ScheduledActivityDao scheduledActivityDao;
    private NotificationService notificationService;
    private DaoFinder daoFinder;
    private WorkflowService workflowService;
    private ApplicationSecurityManager applicationSecurityManager;
    private ProvisioningSessionFactory provisioningSessionFactory;

    private static final String COPY = "copy";
    private PscUserService pscUserService;

    public void scheduleReconsent(final Study study, final Date startDate, final String details) throws Exception {
        List<StudySubjectAssignment> subjectAssignments = studyDao.getAssignmentsForStudy(study.getId());
        Activity reconsent = activityDao.getByName("Reconsent");
        List<String> emailAddressList = new ArrayList<String>();
        for (StudySubjectAssignment assignment : subjectAssignments) {
            if (!assignment.isOff()) {
                ScheduledActivity upcomingScheduledActivity = getNextScheduledActivity(
                        assignment.getScheduledCalendar(), startDate);
                if (upcomingScheduledActivity != null) {
                    ScheduledActivity reconsentEvent = new ScheduledActivity();
                    reconsentEvent.setIdealDate(upcomingScheduledActivity.getActualDate());
                    reconsentEvent.changeState(ScheduledActivityMode.SCHEDULED.createStateInstance(
                            upcomingScheduledActivity.getActualDate(), "Created From Reconsent"));
                    reconsentEvent.setDetails(details);
                    reconsentEvent.setActivity(reconsent);
                    reconsentEvent.setSourceAmendment(study.getAmendment());
                    upcomingScheduledActivity.getScheduledStudySegment().addEvent(reconsentEvent);
                    scheduledActivityDao.save(reconsentEvent);

                    Notification notification = new Notification(reconsentEvent);
                    assignment.addNotification(notification);
                    User studySubjectCalendarManager = assignment.getStudySubjectCalendarManager();
                    if (studySubjectCalendarManager != null) {
                        if (!emailAddressList.contains(studySubjectCalendarManager.getEmailId())) {
                            emailAddressList.add(studySubjectCalendarManager.getEmailId());
                        }
                    }
                }
            }
        }
        if (!emailAddressList.isEmpty()) {
            sendMailForScheduleReconsent(study, details, emailAddressList);
        }
        studyDao.save(study);
    }

    private void sendMailForScheduleReconsent(Study study, String details, List<String> emailAddressList) {
        String subjectHeader = "Subjects on ".concat(study.getAssignedIdentifier())
                .concat(" need to be reconsented");
        String message = "A reconsent activity with details ".concat(details)
                .concat(" has been added to the schedule of each subject on ").concat(study.getAssignedIdentifier())
                .concat(". Check your dashboard for upcoming subjects that need to be reconsented.");
        notificationService.sendNotificationMailToUsers(subjectHeader, message, emailAddressList);
    }

    private ScheduledActivity getNextScheduledActivity(final ScheduledCalendar calendar, final Date startDate) {
        for (ScheduledStudySegment studySegment : calendar.getScheduledStudySegments()) {
            if (!studySegment.isComplete()) {
                Map<Date, List<ScheduledActivity>> eventsByDate = studySegment.getActivitiesByDate();
                for (Date date : eventsByDate.keySet()) {
                    List<ScheduledActivity> events = eventsByDate.get(date);
                    for (ScheduledActivity event : events) {
                        if ((event.getActualDate().after(startDate) || event.getActualDate().equals(startDate))
                                && ScheduledActivityMode.SCHEDULED == event.getCurrentState().getMode()) {
                            return event;
                        }
                    }
                }
            }
        }
        return null;
    }

    // TODO: need replace all business uses of StudyDao#save with this method
    public void save(final Study study) {
        if (study.getId() == null) {
            applyDefaultManagingSites(study);
            applyDefaultStudyAccess(study);
        }
        studyDao.save(study);
        if (study.getAmendment() != null) {
            for (Amendment amendment : study.getAmendmentsList()) {
                deltaService.saveRevision(amendment);
            }
        }
        if (study.getDevelopmentAmendment() != null) {
            deltaService.saveRevision(study.getDevelopmentAmendment());
        }
    }

    @SuppressWarnings({ "unchecked" })
    private void applyDefaultManagingSites(Study study) {
        PscUser user = applicationSecurityManager.getUser();
        if (user == null)
            return;
        SuiteRoleMembership mem = user.getMemberships().get(PscRole.STUDY_CREATOR.getSuiteRole());
        if (mem != null && !mem.isAllSites()) {
            List<Site> sites = siteDao.reassociate((List<Site>) mem.getSites());
            for (Site site : sites) {
                study.addManagingSite(site);
            }
        }
    }

    private void applyDefaultStudyAccess(Study study) {
        PscUser user = applicationSecurityManager.getUser();
        if (user == null)
            return;
        SuiteRoleMembership mem2 = user.getMemberships()
                .get(PscRole.STUDY_CALENDAR_TEMPLATE_BUILDER.getSuiteRole());
        if (mem2 == null)
            return;

        if (pscUserService.isAuthorizationSystemReadOnly())
            return;

        ProvisioningSession session = provisioningSessionFactory.createSession(user.getCsmUser().getUserId());
        SuiteRoleMembership mem1 = user.getMemberships().get(PscRole.STUDY_CREATOR.getSuiteRole());
        mem2 = session.getProvisionableRoleMembership(SuiteRole.STUDY_CALENDAR_TEMPLATE_BUILDER);

        if (mem1 != null && !mem2.isAllStudies()) {
            if (!mem1.isAllSites() && !mem2.isAllSites()) {
                List<?> mem1Sites = mem1.getSites();
                for (Object site : mem2.getSites()) {
                    if (mem1Sites.contains(site)) {
                        mem2.addStudy(study);
                        session.replaceRole(mem2);
                        break;
                    }
                }
            } else {
                mem2.addStudy(study);
                session.replaceRole(mem2);
            }
        }
        user.setStale(true);
    }

    /**
     * Returns all the templates the user can see, sorted by workflow status.  A template may
     * show up in more than one status for the same user.  (E.g., a template can both be in
     * development [for the next amendment] and available [for the current one].)
     */
    public Map<TemplateAvailability, List<StudyWorkflowStatus>> getVisibleStudies(PscUser user) {
        return searchVisibleStudies(user, null);
    }

    /**
     * Returns all the templates the user can see, sorted by workflow status.  A template may
     * show up in more than one status for the same user.  (E.g., a template can both be in
     * development [for the next amendment] and available [for the current one].)
     */
    public Map<TemplateAvailability, List<StudyWorkflowStatus>> searchVisibleStudies(PscUser user, String term) {
        Map<TemplateAvailability, List<StudyWorkflowStatus>> results = new MapBuilder<TemplateAvailability, List<StudyWorkflowStatus>>()
                .put(TemplateAvailability.IN_DEVELOPMENT, new LinkedList<StudyWorkflowStatus>())
                .put(TemplateAvailability.PENDING, new LinkedList<StudyWorkflowStatus>())
                .put(TemplateAvailability.AVAILABLE, new LinkedList<StudyWorkflowStatus>()).toMap();

        for (Study visible : studyDao.searchVisibleStudies(user.getVisibleStudyParameters(), term)) {
            StudyWorkflowStatus sws = workflowService.build(visible, user);
            for (TemplateAvailability availability : sws.getTemplateAvailabilities()) {
                results.get(availability).add(sws);
            }
        }

        for (Map.Entry<TemplateAvailability, List<StudyWorkflowStatus>> entry : results.entrySet()) {
            Comparator<StudyWorkflowStatus> comparator;
            if (entry.getKey() == TemplateAvailability.IN_DEVELOPMENT) {
                comparator = StudyWorkflowStatus.byDevelopmentDisplayName();
            } else {
                comparator = StudyWorkflowStatus.byReleaseDisplayName();
            }

            Collections.sort(entry.getValue(), comparator);
        }

        return results;
    }

    public Study copy(final Study study, final Integer selectedAmendmentId) {
        if (study != null) {
            Amendment amendment = null;
            Study revisedStudy = study;
            if (selectedAmendmentId == null) {
                amendment = study.getAmendment();
            } else if (study.getDevelopmentAmendment() != null
                    && selectedAmendmentId.equals(study.getDevelopmentAmendment().getId())) {
                amendment = study.getDevelopmentAmendment();
                revisedStudy = deltaService.revise(study, amendment);
            }
            if (amendment == null) {
                throw new StudyCalendarValidationException(
                        "Can not find amendment for given amendment id:" + selectedAmendmentId);
            }
            String newStudyName = this.getNewStudyNameForCopyingStudy(revisedStudy.getName());
            Map<Study, Set<Population>> newStudy = revisedStudy.copy(newStudyName);
            Study copiedStudy = newStudy.keySet().iterator().next();
            Set<Population> populationSet = newStudy.values().iterator().next();
            studyDao.save(copiedStudy);
            Set<Population> populations = new TreeSet<Population>();
            for (Population population : populationSet) {
                Population copiedPopulation = new Population();
                copiedPopulation.setAbbreviation(population.getAbbreviation());
                copiedPopulation.setName(population.getName());
                copiedPopulation.setStudy(null);
                Change change = Add.create(copiedPopulation);
                if (copiedStudy.getDevelopmentAmendment() != null) {
                    deltaService.updateRevisionForStudy(copiedStudy.getDevelopmentAmendment(), copiedStudy, change);
                    deltaService.saveRevision(copiedStudy.getDevelopmentAmendment());
                }
                copiedPopulation = (Population) ((ChildrenChange) change).getChild();
                populations.add(copiedPopulation);

            }

            List<Epoch> epochs = new ArrayList<Epoch>();
            for (Delta delta : copiedStudy.getDevelopmentAmendment().getDeltas()) {
                if (delta.getNode() instanceof PlannedCalendar) {
                    List<ChildrenChange> changes = delta.getChanges();
                    for (ChildrenChange change : changes) {
                        if ((ChangeAction.ADD).equals(change.getAction())) {
                            epochs.add((Epoch) change.getChild());
                        }
                    }
                }
            }

            for (Epoch epoch : epochs) {
                List<StudySegment> studySegments = epoch.getChildren();
                for (StudySegment studySegment : studySegments) {
                    SortedSet<Period> periods = studySegment.getPeriods();
                    for (Period period : periods) {
                        List<PlannedActivity> plannedActivities = period.getChildren();
                        for (PlannedActivity plannedActivity : plannedActivities) {
                            plannedActivity.setPopulation(Population.findMatchingPopulationByAbbreviation(
                                    populations, plannedActivity.getPopulation()));
                        }
                    }
                }
            }
            if (copiedStudy.getDevelopmentAmendment() != null) {
                deltaService.saveRevision(copiedStudy.getDevelopmentAmendment());
            }
            return copiedStudy;
        } else {
            throw new StudyCalendarValidationException("Can not find study");
        }
    }

    @SuppressWarnings({ "unchecked" })
    public String getNewStudyNameForCopyingStudy(String studyName) {
        String templateName = studyName;
        templateName = templateName + " copy";

        final String searchString = templateName + "%";
        List<Study> studies = getStudyDao().searchStudiesByAssignedIdentifier(searchString);
        if (studies.size() == 0) {
            return templateName;
        }

        Collections.sort(studies, new CopiedStudyTemporaryNameComparator());
        Study study = studies.get(0);
        String name = study.getName();
        String numericPartSupposedly = name.substring(name.indexOf(COPY) + 4, name.length());
        int newNumber = 0;
        if (!StringUtils.isBlank(numericPartSupposedly)) {
            try {
                newNumber = Integer.valueOf(numericPartSupposedly.trim()) + 1;
            } catch (NumberFormatException e) {
                log.debug("Can't convert study's numeric string " + newNumber + " into int");
            }
        } else {
            newNumber = 2;
        }
        templateName = templateName + " " + newNumber;

        if (studyDao.getByAssignedIdentifier(templateName) != null) {
            return getNewStudyNameForCopyingStudy(templateName);
        }
        return templateName;
    }

    @SuppressWarnings({ "unchecked" })
    public String getNewStudyName() {
        String templateName = "[ABC 1000]";

        List<Study> studies = getStudyDao().searchStudiesByAssignedIdentifier("[ABC %]");
        List<Study> filteredStudies = new ArrayList<Study>();
        for (Study study : studies) {
            if (study.getAssignedIdentifier().matches("\\[ABC \\d\\d\\d\\d\\]")) {
                filteredStudies.add(study);
            }
        }
        Collections.sort(filteredStudies, new StudyTemporaryNameComparator());
        if (filteredStudies.size() == 0) {
            return templateName;
        }
        Study study = filteredStudies.get(0);
        String studyName = study.getName();
        String numericPartSupposedly = studyName.substring(studyName.indexOf(" ") + 1, studyName.lastIndexOf("]"));
        int newNumber = 1000;
        try {
            newNumber = new Integer(numericPartSupposedly) + 1;
        } catch (NumberFormatException e) {
            log.debug("Can't convert study's numeric string " + newNumber + " into int");
        }
        templateName = "[ABC " + newNumber + "]";
        return templateName;
    }

    public Study saveStudyFor(PlanTreeNode<?> node) {
        node = templateService.findCurrentNode(node);
        Study study = templateService.findStudy(node);
        save(study);
        return study;
    }

    public Study getStudyByAssignedIdentifier(final String assignedIdentifier) {
        Study study = studyDao.getByAssignedIdentifier(assignedIdentifier);
        if (study == null) {
            return null;
        }
        Hibernate.initialize(study);
        List<StudySite> sites = study.getStudySites();
        Hibernate.initialize(sites);
        for (StudySite studySite : sites) {
            Hibernate.initialize(studySite);
            Hibernate.initialize(studySite.getCurrentApprovedAmendment());
        }

        PlannedCalendar plannedCalendar = study.getPlannedCalendar();
        if (plannedCalendar != null) {
            plannedCalendarDao.initialize(plannedCalendar);
        }

        List<Amendment> amendmentList = study.getAmendmentsList();
        for (Amendment amendment : amendmentList) {
            initializeAmendment(amendment);
        }

        Amendment developmentAmendment = study.getDevelopmentAmendment();
        initializeAmendment(developmentAmendment);
        return study;
    }

    @SuppressWarnings({ "unchecked" })
    private void initializeAmendment(Amendment amendment) {
        Hibernate.initialize(amendment);
        if (amendment != null) {
            List<Delta<?>> deltas = amendment.getDeltas();
            Hibernate.initialize(deltas);
            for (Delta delta : deltas) {
                Hibernate.initialize(delta.getChanges());
                List<Change> changes = delta.getChanges();
                for (Change change : changes) {
                    Hibernate.initialize(change);
                    if (change instanceof ChildrenChange) {
                        ChildrenChange childrenChange = (ChildrenChange) change;
                        childrenChange.setChild(
                                (Child) getChild(childrenChange, ((Parent) delta.getNode()).childClass()));
                    }
                }
            }
        }
    }

    // Methods to get child from child id
    private DomainObject getChild(ChildrenChange change, Class<? extends Changeable> childClass) {
        if (change.getChild() != null) {
            return change.getChild();
        } else {
            DomainObjectDao<?> dao = getDaoFinder().findDao(childClass);
            return dao.getById(change.getChildId());
        }
    }

    /**
     * Mutates the provided example study into a saveable form and then saves it.
     * Specifically, it takes the plan tree embodied in the plannedCalendar of the
     * example study and translates it into a development amendment which, when
     * released, will have the same structure as the example.
     *
     * @param example
     */
    public void createInDesignStudyFromExamplePlanTree(Study example) {
        example.setAmendment(null);
        Amendment newDev = new Amendment();
        newDev.setDate(nowFactory.getNow());
        newDev.setName(Amendment.INITIAL_TEMPLATE_AMENDMENT_NAME);

        PlannedCalendarDelta delta = new PlannedCalendarDelta(example.getPlannedCalendar());
        List<Epoch> epochs = new ArrayList<Epoch>(example.getPlannedCalendar().getEpochs());
        example.getPlannedCalendar().getEpochs().clear();
        for (Epoch epoch : epochs) {
            Add.create(epoch).mergeInto(delta, nowFactory.getNow());
        }
        newDev.addDelta(delta);

        example.setDevelopmentAmendment(newDev);
        save(example);
    }

    /**
     * Returns a transient version of <tt>source</tt> that has every {@link Add} in every
     * amendment resolved to the version of the added node as it existed when it was added.
     *
     * @param source
     * @return a new, transient {@link Study} instance
     */
    public Study getCompleteTemplateHistory(Study source) {
        List<Study> snapshots = new LinkedList<Study>();
        snapshots.add(source.transientClone());
        while (last(snapshots).getAmendment() != null
                && last(snapshots).getAmendment().getPreviousAmendment() != null) {
            snapshots.add(deltaService.amendToPreviousVersion(last(snapshots).transientClone()));
            resolveCurrentAdds(last(snapshots));
        }
        // The current amendment for each snapshot is now the version we want
        // to include with the complete history
        Study complete = source.transientClone();
        if (complete.getAmendment() != null) {
            resolveCurrentAdds(complete);
        }
        Amendment current = complete.getAmendment();
        for (int i = 1; i < snapshots.size(); i++) {
            Amendment previousAmendment = snapshots.get(i).getAmendment();
            current.setPreviousAmendment(previousAmendment);
            current = previousAmendment;
        }
        return complete;
    }

    private void resolveCurrentAdds(Study study) {
        for (Delta<?> delta : study.getAmendment().getDeltas()) {
            for (Change change : delta.getChanges()) {
                if (change.getAction() == ChangeAction.ADD) {
                    Add add = (Add) change;
                    Child childTemplate;
                    try {
                        childTemplate = (Child) ((Parent) delta.getNode()).childClass().newInstance();
                        childTemplate.setId(add.getChildId());
                    } catch (InstantiationException e) {
                        throw new StudyCalendarError("Uninstantiable child class", e);
                    } catch (IllegalAccessException e) {
                        throw new StudyCalendarError("Inaccessible child class", e);
                    }
                    add.setChild(templateService.findEquivalentChild(study, childTemplate));
                }
            }
        }
    }

    private <T> T last(List<T> list) {
        if (list.isEmpty())
            return null;
        else
            return list.get(list.size() - 1);
    }

    ////// CONFIGURATION

    @Required
    public void setActivityDao(final ActivityDao activityDao) {
        this.activityDao = activityDao;
    }

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

    @Required
    public StudyDao getStudyDao() {
        return studyDao;
    }

    @Required
    public void setSiteDao(SiteDao siteDao) {
        this.siteDao = siteDao;
    }

    @Required
    public void setDeltaService(final DeltaService deltaService) {
        this.deltaService = deltaService;
    }

    @Required
    public void setTemplateService(final TemplateService templateService) {
        this.templateService = templateService;
    }

    @Required
    public void setPlannedCalendarDao(final PlannedCalendarDao plannedCalendarDao) {
        this.plannedCalendarDao = plannedCalendarDao;
    }

    @Required
    public void setNowFactory(NowFactory nowFactory) {
        this.nowFactory = nowFactory;
    }

    @Required
    public void setScheduledActivityDao(final ScheduledActivityDao scheduledActivityDao) {
        this.scheduledActivityDao = scheduledActivityDao;
    }

    @Required
    public void setNotificationService(final NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    public DaoFinder getDaoFinder() {
        return daoFinder;
    }

    @Required
    public void setDaoFinder(DaoFinder daoFinder) {
        this.daoFinder = daoFinder;
    }

    @Required
    public void setApplicationSecurityManager(ApplicationSecurityManager applicationSecurityManager) {
        this.applicationSecurityManager = applicationSecurityManager;
    }

    @Required
    public void setWorkflowService(WorkflowService workflowService) {
        this.workflowService = workflowService;
    }

    @Required
    public void setProvisioningSessionFactory(ProvisioningSessionFactory provisioningSessionFactory) {
        this.provisioningSessionFactory = provisioningSessionFactory;
    }

    @Required
    public void setPscUserService(PscUserService pscUserService) {
        this.pscUserService = pscUserService;
    }

    public void purge(Study study) {
        Map<Class<? extends AbstractMutableDomainObject>, List<Object>> nodes = StudyNode.allByType(study);
        Class[] order = { ScheduledActivity.class, ScheduledStudySegment.class, ScheduledCalendar.class,
                StudySubjectAssignment.class, StudySite.class, Change.class, Delta.class, Amendment.class,
                Study.class };

        for (Class klass : order) {
            if (nodes.containsKey(klass)) {
                DeletableDomainObjectDao dao = ((SpringDaoFinder) daoFinder).findDeletableDomainObjectDao(klass);
                dao.deleteAll(nodes.get(klass));
            }
        }
    }

    static private class StudyNode {
        public static Map<Class<? extends AbstractMutableDomainObject>, List<Object>> allByType(Study study) {
            Map<Class<? extends AbstractMutableDomainObject>, List<Object>> all = new HashMap<Class<? extends AbstractMutableDomainObject>, List<Object>>();

            putInMappedList(all, study.getClass(), study);

            for (StudySite studySite : study.getStudySites()) {
                for (StudySubjectAssignment assignment : studySite.getStudySubjectAssignments()) {
                    putInMappedList(all, assignment.getClass(), studySite);

                    ScheduledCalendar calendar = assignment.getScheduledCalendar();
                    putInMappedList(all, calendar.getClass(), calendar);
                    for (ScheduledStudySegment segment : calendar.getScheduledStudySegments()) {
                        putInMappedList(all, segment.getClass(), segment);
                        for (ScheduledActivity activity : segment.getActivities()) {
                            putInMappedList(all, activity.getClass(), activity);
                        }
                    }
                }
            }

            all.putAll(amendmentNodesByType(study));

            return all;
        }

        public static Map<Class<? extends AbstractMutableDomainObject>, List<Object>> amendmentNodesByType(
                Study study) {
            Map<Class<? extends AbstractMutableDomainObject>, List<Object>> nodes = new HashMap<Class<? extends AbstractMutableDomainObject>, List<Object>>();

            List<Amendment> amendments = new ArrayList<Amendment>(study.getAmendmentsList());
            amendments.addAll(study.getDevelopmentAmendmentList());

            for (Amendment amendment : amendments) {
                putInMappedList(nodes, amendment.getClass(), amendment);
                for (Delta<?> delta : amendment.getDeltas()) {
                    putInMappedList(nodes, Delta.class, delta);
                }
            }

            return nodes;
        }
    }

    ////// INNER CLASSES

    private class CopiedStudyTemporaryNameComparator implements Comparator<Study> {
        private final Logger logger = LoggerFactory.getLogger(getClass());

        /**
         * Compares the study name. Compares only  studies having name matches with copy * .
         *
         * @param study
         * @param anotherStudy
         * @return
         */
        public int compare(final Study study, final Study anotherStudy) {
            // String numericPartSupposedly = "";
            String name = study.getName();
            String anotherStudyName = anotherStudy.getName();
            if (name.indexOf(COPY) <= 0 || anotherStudyName.indexOf(COPY) <= 0) {
                return 0;
            }

            String numericPartSupposedly = name.substring(name.indexOf(COPY) + 4, name.length());
            String anotherNumericPartSupposedly = anotherStudyName.substring(anotherStudyName.indexOf(COPY) + 4,
                    anotherStudyName.length());
            Integer number = 0;
            Integer anotherNumber = 0;
            try {

                number = !StringUtils.isBlank(numericPartSupposedly) ? Integer.valueOf(numericPartSupposedly.trim())
                        : 0;
            } catch (NumberFormatException e) {
                logger.debug("error while comparing two stduies. first study name:" + study.getName()
                        + " another study name:" + anotherStudy.getName() + ". error message:" + e.getMessage());
            }
            try {

                anotherNumber = !StringUtils.isBlank(anotherNumericPartSupposedly)
                        ? Integer.valueOf(anotherNumericPartSupposedly.trim())
                        : 0;
            } catch (NumberFormatException e) {
                logger.debug("error while comparing two stduies. first study name:" + study.getName()
                        + " another study name:" + anotherStudy.getName() + ". error message:" + e.getMessage());
            }
            return anotherNumber.compareTo(number);
        }
    }

    private static class StudyTemporaryNameComparator implements Comparator<Study> {
        private final Logger logger = LoggerFactory.getLogger(getClass());

        /**
         * Compares the study name. Compares only  studies having name matches with [ABC *] .
         *
         * @param study
         * @param anotherStudy
         * @return
         */
        public int compare(final Study study, final Study anotherStudy) {
            // String numericPartSupposedly = "";
            String name = study.getName();
            String anotherStudyName = anotherStudy.getName();
            if (name.indexOf("ABC") <= 0 || anotherStudyName.indexOf("ABC") <= 0) {
                return 1;
            } else if (name.indexOf("]") <= 0 || anotherStudyName.indexOf("]") <= 0) {
                return 1;
            }
            if (name.indexOf("[") < 0 || anotherStudyName.indexOf("[") < 0) {
                return 1;
            }

            try {
                String numericPartSupposedly = name.substring(name.indexOf(" ") + 1, name.lastIndexOf("]"));
                String anotherNumericPartSupposedly = anotherStudyName.substring(anotherStudyName.indexOf(" ") + 1,
                        anotherStudyName.lastIndexOf("]"));
                Integer number = new Integer(numericPartSupposedly);
                Integer anotherNumber = new Integer(anotherNumericPartSupposedly);
                return anotherNumber.compareTo(number);

            } catch (NumberFormatException e) {
                logger.debug("error while comparing two stduies. first study name:" + study.getName()
                        + " another study name:" + anotherStudy.getName());
            }

            return 1;
        }
    }
}