edu.harvard.med.screensaver.model.screens.Screen.java Source code

Java tutorial

Introduction

Here is the source code for edu.harvard.med.screensaver.model.screens.Screen.java

Source

// $HeadURL$
// $Id$
//
// Copyright  2006, 2010, 2011, 2012 by the President and Fellows of Harvard College.
//
// Screensaver is an open-source project developed by the ICCB-L and NSRB labs
// at Harvard Medical School. This software is distributed under the terms of
// the GNU General Public License.

package edu.harvard.med.screensaver.model.screens;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.persistence.Version;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;

import org.apache.log4j.Logger;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.SortType;
import org.hibernate.annotations.Type;
import org.joda.time.LocalDate;

import edu.harvard.med.screensaver.ScreensaverConstants;
import edu.harvard.med.screensaver.db.ScreenResultsDAO;
import edu.harvard.med.screensaver.model.AbstractEntityVisitor;
import edu.harvard.med.screensaver.model.AttachedFile;
import edu.harvard.med.screensaver.model.AttachedFilesEntity;
import edu.harvard.med.screensaver.model.BusinessRuleViolationException;
import edu.harvard.med.screensaver.model.DataModelViolationException;
import edu.harvard.med.screensaver.model.DuplicateEntityException;
import edu.harvard.med.screensaver.model.MolarConcentration;
import edu.harvard.med.screensaver.model.RequiredPropertyException;
import edu.harvard.med.screensaver.model.activities.AdministrativeActivity;
import edu.harvard.med.screensaver.model.activities.AdministrativeActivityType;
import edu.harvard.med.screensaver.model.activities.ServiceActivity;
import edu.harvard.med.screensaver.model.annotations.Derived;
import edu.harvard.med.screensaver.model.annotations.ToMany;
import edu.harvard.med.screensaver.model.cells.ExperimentalCellInformation;
import edu.harvard.med.screensaver.model.cherrypicks.CherryPickLiquidTransfer;
import edu.harvard.med.screensaver.model.cherrypicks.CherryPickLiquidTransferStatus;
import edu.harvard.med.screensaver.model.cherrypicks.CherryPickRequest;
import edu.harvard.med.screensaver.model.cherrypicks.RNAiCherryPickRequest;
import edu.harvard.med.screensaver.model.cherrypicks.SmallMoleculeCherryPickRequest;
import edu.harvard.med.screensaver.model.libraries.Library;
import edu.harvard.med.screensaver.model.libraries.LibraryPlate;
import edu.harvard.med.screensaver.model.libraries.Plate;
import edu.harvard.med.screensaver.model.libraries.Reagent;
import edu.harvard.med.screensaver.model.libraries.Well;
import edu.harvard.med.screensaver.model.meta.Cardinality;
import edu.harvard.med.screensaver.model.meta.PropertyPath;
import edu.harvard.med.screensaver.model.meta.RelationshipPath;
import edu.harvard.med.screensaver.model.screenresults.AnnotationType;
import edu.harvard.med.screensaver.model.screenresults.AssayPlate;
import edu.harvard.med.screensaver.model.screenresults.ScreenResult;
import edu.harvard.med.screensaver.model.users.AdministratorUser;
import edu.harvard.med.screensaver.model.users.LabHead;
import edu.harvard.med.screensaver.model.users.ScreeningRoomUser;
import edu.harvard.med.screensaver.model.users.ScreensaverUser;
import edu.harvard.med.screensaver.util.NullSafeUtils;

/**
 * A screen tracks the progress of performing a screening assay, including a description of its biological significance,
 * its experimental protocol, and additional data to support the administrative needs of the facility. After screening
 * data is generated, a screen will contain a {@link ScreenResult}.
 * 
 * @author <a mailto="john_sullivan@hms.harvard.edu">John Sullivan</a>
 * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a>
 */
@Entity
@org.hibernate.annotations.Proxy
public class Screen extends Study implements AttachedFilesEntity<ScreenAttachedFileType, Integer> {

    // private static data

    private static final Logger log = Logger.getLogger(Screen.class);
    private static final long serialVersionUID = 0L;

    public static final RelationshipPath<Screen> thisEntity = RelationshipPath.from(Screen.class);
    public static final PropertyPath<Screen> facilityId = thisEntity.toProperty("facilityId");
    public static final RelationshipPath<Screen> screenResult = thisEntity.to("screenResult", Cardinality.TO_ONE);
    public static final RelationshipPath<Screen> labHead = thisEntity.to("labHead", Cardinality.TO_ONE);
    public static final RelationshipPath<Screen> leadScreener = thisEntity.to("leadScreener", Cardinality.TO_ONE);
    public static final RelationshipPath<Screen> collaborators = thisEntity.to("collaborators");
    public static final RelationshipPath<Screen> annotationTypes = thisEntity.to("annotationTypes");
    public static final RelationshipPath<Screen> cherryPickRequests = thisEntity.to("cherryPickRequests");
    public static final RelationshipPath<Screen> labActivities = thisEntity.to("labActivities");
    public static final RelationshipPath<Screen> serviceActivities = thisEntity.to("serviceActivities");
    public static final RelationshipPath<Screen> statusItems = thisEntity.to("statusItems");
    public static final RelationshipPath<Screen> fundingSupports = thisEntity.to("fundingSupports");
    public static final PropertyPath<Screen> billingItems = thisEntity.toCollectionOfValues("billingItems");
    public static final RelationshipPath<Screen> attachedFiles = thisEntity.to("attachedFiles");
    public static final RelationshipPath<Screen> publications = thisEntity.to("publications");
    public static final PropertyPath<Screen> keywords = thisEntity.toCollectionOfValues("keywords");
    public static final PropertyPath<Screen> cellLines = thisEntity.toCollectionOfValues("cellLines");
    public static final RelationshipPath<Screen> pinTransferApprovalActivity = thisEntity
            .to("pinTransferApprovalActivity", Cardinality.TO_ONE);
    public static final RelationshipPath<Screen> reagents = thisEntity.to("reagents");
    public static final RelationshipPath<Screen> assayPlates = thisEntity.to("assayPlates");
    public static final RelationshipPath<Screen> experimentalCellInfomationSet = thisEntity
            .to("experimentalCellInformationSet");

    public static final Function<Screen, String> ToFacilityId = new Function<Screen, String>() {
        public String apply(Screen screen) {
            return screen.getFacilityId();
        }
    };

    public static final Function<Screen, String> ToNameFunction = new Function<Screen, String>() {
        public String apply(Screen s) {
            return s.getFacilityId();
        }
    };
    public static final Function<Screen, ScreenDataSharingLevel> ToDataSharingLevel = new Function<Screen, ScreenDataSharingLevel>() {
        public ScreenDataSharingLevel apply(Screen s) {
            return s.getDataSharingLevel();
        }
    };

    // private instance data

    // study (provides annotation of library contents)

    private Integer _version;
    private String _title;
    private ScreeningRoomUser _leadScreener; // should rename
    private LabHead _labHead;
    private SortedSet<ScreeningRoomUser> _collaborators = new TreeSet<ScreeningRoomUser>();
    private Set<Publication> _publications = new HashSet<Publication>();
    private String _url;
    private String _summary;
    private String _comments;
    private SortedSet<AnnotationType> _annotationTypes = new TreeSet<AnnotationType>();
    private Set<Reagent> _reagents = new HashSet<Reagent>();
    private StudyType _studyType;
    private boolean _isDownloadable = true;
    private Well wellStudied;

    // generic screen

    private String _facilityId;
    private ScreenType _screenType;
    private Set<AttachedFile> _attachedFiles = new HashSet<AttachedFile>();
    private SortedSet<String> _keywords = new TreeSet<String>();
    private String _publishableProtocol;
    private ScreenResult _screenResult;
    private SortedSet<AssayPlate> _assayPlates = Sets.newTreeSet();
    private ProjectPhase _projectPhase;
    private String _projectId;

    // iccb screen

    private SortedSet<StatusItem> _statusItems = new TreeSet<StatusItem>();
    private SortedSet<LabActivity> _labActivities = new TreeSet<LabActivity>();
    private SortedSet<ServiceActivity> _serviceActivities = new TreeSet<ServiceActivity>();
    private LocalDate _dataMeetingScheduled;
    private LocalDate _dataMeetingComplete;
    private BillingInformation _billingInformation = new BillingInformation(this, false);
    private List<BillingItem> _billingItems = new ArrayList<BillingItem>();
    private Set<FundingSupport> _fundingSupports = new HashSet<FundingSupport>();
    private LocalDate _dateOfApplication;
    private Set<AbaseTestset> _abaseTestsets = new HashSet<AbaseTestset>();
    private String _abaseStudyId;
    private String _abaseProtocolId;
    private String _comsRegistrationNumber;
    private LocalDate _comsApprovalDate;
    private String _publishableProtocolComments;
    private LocalDate _publishableProtocolDateEntered;
    private String _publishableProtocolEnteredBy;
    private AdministrativeActivity _pinTransferApprovalActivity;
    private Set<CherryPickRequest> _cherryPickRequests = Sets.newHashSet();
    private ScreenDataSharingLevel _dataSharingLevel;
    private LocalDate _minAllowedDataPrivacyExpirationDate;
    private LocalDate _maxAllowedDataPrivacyExpirationDate;
    private LocalDate _dataPrivacyExpirationDate;
    private LocalDate _dataPrivacyExpirationNotifiedDate;

    private LocalDate _pubchemDepositedDate;
    private Integer _pubchemAssayId;

    private int _assayPlatesScreenedCount;
    private int _librariesScreenedCount;
    private int _libraryPlatesScreenedCount;
    private int _libraryPlatesDataLoadedCount;
    private int _libraryPlatesDataAnalyzedCount;
    private int _screenedExperimentalWellCount;
    private int _uniqueScreenedExperimentalWellCount;
    private int _totalPlatedLabCherryPicks;
    private Integer _minScreenedReplicateCount;
    private Integer _maxScreenedReplicateCount;
    private Integer _minDataLoadedReplicateCount;
    private Integer _maxDataLoadedReplicateCount;

    private Species _species;
    private AssayType _assayType;

    //  private CellLine _cellLine;
    private SortedSet<CellLine> _cellLines = new TreeSet<CellLine>();

    private TransfectionAgent _transfectionAgent;
    private MolarConcentration perturbagenMolarConcentration;
    private BigDecimal perturbagenUgMlConcentration;

    // lincs screen

    private SortedSet<ExperimentalCellInformation> _experimentalCellInformationSet = Sets.newTreeSet();

    /**
     * Construct an uninitialized <code>Screen</code>.
     *
     * @motivation for new Screen creation via user interface, where even required
     *             fields are allowed to be uninitialized, initially
     * @motivation for hibernate and proxy/concrete subclass constructors
     */
    protected Screen() {
    }

    public Screen(AdministratorUser createdBy) {
        super(createdBy);
        _dataSharingLevel = ScreenDataSharingLevel.PRIVATE;
    }

    /**
     * Construct an initialized <code>Screen</code>.
     * @param facilityId the screen facility ID
     * @param leadScreener the lead screener
     * @param labHead the lab head
     * @param screenType the screen type
     * @param studyType the study type
     * @param projectPhase TODO
     * @param title the title
     */
    public Screen(AdministratorUser createdBy, String facilityId, ScreeningRoomUser leadScreener, LabHead labHead,
            ScreenType screenType, StudyType studyType, ProjectPhase projectPhase, String title) {
        this(createdBy);
        _facilityId = facilityId;
        _screenType = screenType; // note: must be set before updateFacilityUsageRoleForAssociatedScreens() is called
        _studyType = studyType;
        _projectPhase = projectPhase;
        setLeadScreener(leadScreener);
        setLabHead(labHead);
        _title = title;
    }

    // public instance methods

    public Object acceptVisitor(AbstractEntityVisitor visitor) {
        // TODO: HACK, until we have the real Study->Screen->IccbScreen hierarchy
        if (isStudyOnly()) {
            return visitor.visit((Study) this);
        }
        return visitor.visit(this);
    }

    /**
     * Get the id for the screen.
     * @return the id for the screen
     */
    @Id
    @org.hibernate.annotations.GenericGenerator(name = "screen_id_seq", strategy = "sequence", parameters = {
            @org.hibernate.annotations.Parameter(name = "sequence", value = "screen_id_seq") })
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "screen_id_seq")
    public Integer getScreenId() {
        return getEntityId();
    }

    @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinTable(name = "screenUpdateActivity", joinColumns = @JoinColumn(name = "screenId", nullable = false, updatable = false), inverseJoinColumns = @JoinColumn(name = "updateActivityId", nullable = false, updatable = false, unique = true))
    @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
    @Sort(type = SortType.NATURAL)
    @ToMany(singularPropertyName = "updateActivity", hasNonconventionalMutation = true /* model testing framework doesn't understand this is a containment relationship, and so requires addUpdateActivity() method*/)
    @Override
    public SortedSet<AdministrativeActivity> getUpdateActivities() {
        return _updateActivities;
    }

    /**
     * Get the lead screener.
     * @return the lead screener
     */
    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinColumn(name = "leadScreenerId"/*, nullable=false*/)
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_to_lead_screener")
    @org.hibernate.annotations.LazyToOne(value = org.hibernate.annotations.LazyToOneOption.PROXY)
    @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
    @edu.harvard.med.screensaver.model.annotations.ToOne(inverseProperty = "screensLed")
    public ScreeningRoomUser getLeadScreener() {
        return _leadScreener;
    }

    /**
     * Set the lead screener.
     * @param leadScreener the new lead screener
     */
    public void setLeadScreener(ScreeningRoomUser leadScreener) {
        if (isHibernateCaller()) {
            _leadScreener = leadScreener;
            return;
        }
        if (leadScreener == null) {
            throw new NullPointerException();
        }
        if (_leadScreener != null) {
            _leadScreener.getScreensLed().remove(this);
            _leadScreener.updateFacilityUsageRoleForAssociatedScreens();
        }
        _leadScreener = leadScreener;
        _leadScreener.getScreensLed().add(this);
        _leadScreener.updateFacilityUsageRoleForAssociatedScreens();
    }

    /**
     * Get the lab head.
     * @return the lab head
     */
    @ManyToOne(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinColumn(name = "labHeadId")
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_to_lab_head")
    @org.hibernate.annotations.LazyToOne(value = org.hibernate.annotations.LazyToOneOption.PROXY)
    @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
    @edu.harvard.med.screensaver.model.annotations.ToOne(inverseProperty = "screensHeaded")
    public LabHead getLabHead() {
        return _labHead;
    }

    /**
     * Set the lab head.
     * @param labHead the new lab head
     */
    public void setLabHead(LabHead labHead) {
        if (isHibernateCaller()) {
            _labHead = labHead;
            return;
        }
        if (NullSafeUtils.nullSafeEquals(labHead, _labHead)) {
            return;
        }
        if (_labHead != null) {
            _labHead.getScreensHeaded().remove(this);
            _labHead.updateFacilityUsageRoleForAssociatedScreens();
        }
        _labHead = labHead;
        if (_labHead != null) {
            _labHead.getScreensHeaded().add(this);
            _labHead.updateFacilityUsageRoleForAssociatedScreens();
        }
    }

    /**
     * Get the set of collaborators.
     * @return the set of collaborators
     */
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "collaboratorLink", joinColumns = @JoinColumn(name = "screenId"), inverseJoinColumns = @JoinColumn(name = "collaboratorId"))
    @org.hibernate.annotations.ForeignKey(name = "fk_collaborator_link_to_screen")
    @org.hibernate.annotations.LazyCollection(value = org.hibernate.annotations.LazyCollectionOption.TRUE)
    @edu.harvard.med.screensaver.model.annotations.ToMany(inverseProperty = "screensCollaborated")
    @Sort(type = SortType.NATURAL)
    public SortedSet<ScreeningRoomUser> getCollaborators() {
        return _collaborators;
    }

    /**
     * Add the collaborator.
     * @param collaborator the collaborator to add
     * @return true iff the screen did not already have the collaborator
     */
    public boolean addCollaborator(ScreeningRoomUser collaborator) {
        if (_collaborators.add(collaborator)) {
            collaborator.getScreensCollaborated().add(this);
            collaborator.updateFacilityUsageRoleForAssociatedScreens();
            return true;
        }
        return false;
    }

    /**
     * Remove the collaborator.
     * @param collaborator the collaborator to remove
     * @return true iff the screen previously had the collaborator
     */
    public boolean removeCollaborator(ScreeningRoomUser collaborator) {
        if (_collaborators.remove(collaborator)) {
            collaborator.getScreensCollaborated().remove(this);
            collaborator.updateFacilityUsageRoleForAssociatedScreens();
            return true;
        }
        return false;
    }

    @Transient
    public Set<ScreeningRoomUser> getAssociatedScreeningRoomUsers() {
        Set<ScreeningRoomUser> users = new HashSet<ScreeningRoomUser>();
        if (getLabHead() != null) {
            users.add(getLabHead());
        }
        users.add(getLeadScreener());
        users.addAll(getCollaborators());
        return users;
    }

    /**
     * Get the screen result.
     * @return the screen result
     */
    @OneToOne(mappedBy = "screen", cascade = { CascadeType.PERSIST, CascadeType.MERGE,
            CascadeType.REMOVE }, fetch = FetchType.LAZY)
    @org.hibernate.annotations.LazyToOne(value = org.hibernate.annotations.LazyToOneOption.PROXY)
    @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE,
            org.hibernate.annotations.CascadeType.DELETE })
    public ScreenResult getScreenResult() {
        return _screenResult;
    }

    AssayPlate createAssayPlate(Plate plateScreened, int replicateOrdinal, LibraryScreening libraryScreening) {
        AssayPlate assayPlate = new AssayPlate(this, plateScreened, replicateOrdinal);
        assayPlate.setLibraryScreening(libraryScreening);
        if (!_assayPlates.add(assayPlate)) {
            throw new DuplicateEntityException(this, assayPlate);
        }
        return assayPlate;
    }

    /**
     * @motivation this alternate factory method is necessary for cases where
     *             Screensaver has not been used to track library copies or
     *             library screenings, so that the necessary Plate and/or
     *             AssayPlate entities do not exist.
     */
    public AssayPlate createAssayPlate(int plateNumber, int replicateOrdinal) {
        AssayPlate assayPlate = new AssayPlate(this, plateNumber, replicateOrdinal);
        if (!_assayPlates.add(assayPlate)) {
            throw new DuplicateEntityException(this, assayPlate);
        }
        return assayPlate;
    }

    public SortedSet<AssayPlate> findAssayPlates(final int plateNumber) {
        return ImmutableSortedSet.copyOf(Iterables.filter(getAssayPlates(), new Predicate<AssayPlate>() {
            public boolean apply(AssayPlate ap) {
                return ap.getPlateNumber() == plateNumber;
            }
        }));
    }

    /**
     * The collection of {@link AssayPlate}s that have been created for this
     * screen, which may contain multiple instances for a given library
     * {@link Plate} and replicate (if it required re-screening).
     * 
     * @return Collection of {@link AssayPlate}s
     */
    @OneToMany(mappedBy = "screen", cascade = { CascadeType.ALL }, orphanRemoval = true)
    @Sort(type = SortType.NATURAL)
    public SortedSet<AssayPlate> getAssayPlates() {
        return _assayPlates;
    }

    private void setAssayPlates(SortedSet<AssayPlate> assayPlates) {
        _assayPlates = assayPlates;
    }

    public int getLibrariesScreenedCount() {
        return _librariesScreenedCount;
    }

    public void setLibrariesScreenedCount(int librariesScreenedCount) {
        _librariesScreenedCount = librariesScreenedCount;
    }

    /**
     * @return a SortedSet of LibraryPlates. The library property will be null if the AssayPlate was not screened, but has
     *         had data loaded.
     */
    @Transient
    public SortedSet<LibraryPlate> getLibraryPlatesScreened() {
        Multimap<Integer, AssayPlate> index = Multimaps.index(
                Iterables.filter(getAssayPlates(), AssayPlate.IsScreened), new Function<AssayPlate, Integer>() {
                    @Override
                    public Integer apply(AssayPlate p) {
                        return p.getPlateNumber();
                    }
                });
        SortedSet<LibraryPlate> libraryPlates = Sets.newTreeSet();
        for (Integer plateNumber : index.keySet()) {
            AssayPlate firstAssayPlate = Iterables.get(index.get(plateNumber), 0);
            Library library = null;
            if (firstAssayPlate.getPlateScreened() != null) {
                library = firstAssayPlate.getPlateScreened().getCopy().getLibrary();
            }
            libraryPlates.add(new LibraryPlate(plateNumber, library, Sets.newHashSet(index.get(plateNumber))));
        }
        return libraryPlates;
    }

    @Transient
    public SortedSet<AssayPlate> getAssayPlatesDataLoaded() {
        return Sets.newTreeSet(Iterables.filter(getAssayPlates(), AssayPlate.IsDataLoaded));
    }

    @Transient
    public SortedSet<AssayPlate> getAssayPlatesScreened() {
        return Sets.newTreeSet(Iterables.filter(getAssayPlates(), AssayPlate.HasLibraryScreening));
    }

    @Derived
    public int getAssayPlatesScreenedCount() {
        return _assayPlatesScreenedCount;
    }

    public void setAssayPlatesScreenedCount(int assayPlatesScreenedCount) {
        _assayPlatesScreenedCount = assayPlatesScreenedCount;
    }

    @Derived
    public int getLibraryPlatesScreenedCount() {
        return _libraryPlatesScreenedCount;
    }

    public void setLibraryPlatesScreenedCount(int libraryPlatesScreenedCount) {
        _libraryPlatesScreenedCount = libraryPlatesScreenedCount;
    }

    @Derived
    public int getLibraryPlatesDataLoadedCount() {
        return _libraryPlatesDataLoadedCount;
    }

    public void setLibraryPlatesDataLoadedCount(int libraryPlatesDataLoadedCount) {
        _libraryPlatesDataLoadedCount = libraryPlatesDataLoadedCount;
    }

    @Derived
    public int getLibraryPlatesDataAnalyzedCount() {
        return _libraryPlatesDataAnalyzedCount;
    }

    public void setLibraryPlatesDataAnalyzedCount(int libraryPlatesDataAnalyzedCount) {
        _libraryPlatesDataAnalyzedCount = libraryPlatesDataAnalyzedCount;
    }

    @Derived
    public Integer getMinScreenedReplicateCount() {
        return _minScreenedReplicateCount;
    }

    public void setMinScreenedReplicateCount(Integer minScreenedReplicateCount) {
        _minScreenedReplicateCount = minScreenedReplicateCount;
    }

    @Derived
    public Integer getMaxScreenedReplicateCount() {
        return _maxScreenedReplicateCount;
    }

    public void setMaxScreenedReplicateCount(Integer maxScreenedReplicateCount) {
        _maxScreenedReplicateCount = maxScreenedReplicateCount;
    }

    @Derived
    public Integer getMinDataLoadedReplicateCount() {
        return _minDataLoadedReplicateCount;
    }

    public void setMinDataLoadedReplicateCount(Integer minDataLoadedReplicateCount) {
        _minDataLoadedReplicateCount = minDataLoadedReplicateCount;
    }

    @Derived
    public Integer getMaxDataLoadedReplicateCount() {
        return _maxDataLoadedReplicateCount;
    }

    public void setMaxDataLoadedReplicateCount(Integer maxDataLoadedReplicateCount) {
        _maxDataLoadedReplicateCount = maxDataLoadedReplicateCount;
    }

    @Derived
    public int getScreenedExperimentalWellCount() {
        return _screenedExperimentalWellCount;
    }

    public void setScreenedExperimentalWellCount(int screenedExperimentalWellCount) {
        _screenedExperimentalWellCount = screenedExperimentalWellCount;
    }

    @Derived
    public int getUniqueScreenedExperimentalWellCount() {
        return _uniqueScreenedExperimentalWellCount;
    }

    public void setUniqueScreenedExperimentalWellCount(int uniqueScreenedExperimentalWellCount) {
        _uniqueScreenedExperimentalWellCount = uniqueScreenedExperimentalWellCount;
    }

    @Derived
    public int getTotalPlatedLabCherryPicks() {
        return _totalPlatedLabCherryPicks;
    }

    public void setTotalPlatedLabCherryPicks(int totalPlatedLabCherryPicks) {
        _totalPlatedLabCherryPicks = totalPlatedLabCherryPicks;
    }

    /**
     * Create and return a new screen result for the screen.
     * 
     * @return the new screen result
     */
    public ScreenResult createScreenResult() {
        _screenResult = new ScreenResult(this, null);
        return _screenResult;
    }

    /**
     * Clear the screen result (in memory only). Use {@link ScreenResultsDAO#deleteScreenResult(ScreenResult)} to delete
     * from persistent storage.
     */
    public void clearScreenResult() {
        _screenResult = null;
        for (AssayPlate assayPlate : getAssayPlates()) {
            assayPlate.setScreenResultDataLoading(null);
        }
    }

    /**
     * Get the status items. A Screen may only contain one status with a given
     * {@link ScreenStatus#getRank() rank} value (StatusItems with the same rank are mutually
     * exclusive). Ordering of StatusItems must be equivalent whether by
     * {@link ScreenStatus#getRank() rank} or {@link StatusItem#getStatusDate() date}.
     *
     * @return the status items
     */
    @ElementCollection(fetch = FetchType.EAGER)
    @JoinTable(name = "screen_status_item", joinColumns = @JoinColumn(name = "screen_id"))
    @Sort(type = SortType.NATURAL)
    public SortedSet<StatusItem> getStatusItems() {
        return _statusItems;
    }

    @Transient
    public StatusItem getCurrentStatusItem() {
        if (_statusItems.isEmpty()) {
            return null;
        }
        return _statusItems.last();
    }

    /**
     * Create and return a new <code>StatusItem</code> for this screen.
     * @param statusDate the status date
     * @param screenStatus the status value
     * @return the new status item
     */
    public StatusItem createStatusItem(LocalDate statusDate, ScreenStatus screenStatus) {
        for (StatusItem statusItem : _statusItems) {
            if (statusItem.getStatus().getRank() == screenStatus.getRank()) {
                throw new BusinessRuleViolationException("screen status " + screenStatus
                        + " is mutually exclusive with existing screen status " + statusItem.getStatus());
            }
            if (screenStatus.getRank() < statusItem.getStatus().getRank()) {
                if (statusDate.compareTo(statusItem.getStatusDate()) > 0) {
                    throw new BusinessRuleViolationException(
                            "date of new screen status must not be after date of subsequent screen status");
                }
            } else {
                if (statusDate.compareTo(statusItem.getStatusDate()) < 0) {
                    throw new BusinessRuleViolationException(
                            "date of new screen status must not be before the date of the previous screen status");
                }
            }
        }
        StatusItem newStatusItem = new StatusItem(statusDate, screenStatus);
        _statusItems.add(newStatusItem);
        return newStatusItem;
    }

    /**
     * Get the lab activities.
     * @return the lab activities
     */
    @OneToMany(mappedBy = "screen", cascade = { CascadeType.ALL })
    @Sort(type = SortType.NATURAL)
    @edu.harvard.med.screensaver.model.annotations.ToMany(singularPropertyName = "labActivity", hasNonconventionalMutation = true /* uses createLibraryScreening() and createRNAiCherryPickScreening */)
    public SortedSet<LabActivity> getLabActivities() {
        return _labActivities;
    }

    /**
     * Get the service activities.
     * @return the service activities
     */
    @OneToMany(mappedBy = "servicedScreen", cascade = { CascadeType.ALL })
    @Sort(type = SortType.NATURAL)
    @edu.harvard.med.screensaver.model.annotations.ToMany(singularPropertyName = "serviceActivity", hasNonconventionalMutation = true)
    public SortedSet<ServiceActivity> getServiceActivities() {
        return _serviceActivities;
    }

    /**
     * Get all the lab activities for this screen of a particular type.
     * @param <E> the type of the lab activities to get
     * @param clazz the type of the lab activities to get
     * @return all the lab activities for this screen of a particular type.
     */
    @SuppressWarnings("unchecked")
    @Transient
    public <E extends LabActivity> SortedSet<E> getLabActivitiesOfType(Class<E> clazz) {
        SortedSet<E> result = new TreeSet<E>();
        for (LabActivity labActivity : _labActivities) {
            if (clazz.isAssignableFrom(labActivity.getClass())) {
                result.add((E) labActivity);
            }
        }
        return result;
    }

    /**
     * Create and return a new library screening for the screen.
     * @param performedBy the user that performed the screening
     * @param dateOfActivity the date the lab activity took place
     * @return the new library screening
     */
    public LibraryScreening createLibraryScreening(AdministratorUser recordedBy, ScreeningRoomUser performedBy,
            LocalDate dateOfActivity) {
        LibraryScreening libraryScreening = new LibraryScreening(this, recordedBy, performedBy, dateOfActivity);
        _labActivities.add(libraryScreening);
        return libraryScreening;
    }

    /**
     * Create and return a new cherry pick liquid transfer for the screen.
     * @param performedBy the user that performed the activity
     * @param dateOfActivity the date the lab activity took place
     * @param status the status of the cherry pick liquid transfer
     * @return the new cherry pick liquid transfer
     */
    public CherryPickLiquidTransfer createCherryPickLiquidTransfer(AdministratorUser recordedBy,
            ScreensaverUser performedBy, LocalDate dateOfActivity, CherryPickLiquidTransferStatus status) {
        CherryPickLiquidTransfer cherryPickLiquidTransfer = new CherryPickLiquidTransfer(this, recordedBy,
                performedBy, dateOfActivity, status);
        _labActivities.add(cherryPickLiquidTransfer);
        return cherryPickLiquidTransfer;
    }

    /**
     * Create and return a new cherry pick screening for the screen.
     * @param performedBy the user that performed the screening
     * @param dateOfActivity the date the screening took place
     * @param cherryPickRequest the cherry pick request
     * @return the newly created cherry pick screening
     */
    public CherryPickScreening createCherryPickScreening(AdministratorUser recordedBy,
            ScreeningRoomUser performedBy, LocalDate dateOfActivity, CherryPickRequest cherryPickRequest) {
        CherryPickScreening screening = new CherryPickScreening(this, recordedBy, performedBy, dateOfActivity,
                cherryPickRequest);
        _labActivities.add(screening);
        return screening;
    }

    /**
     * Get the cherry pick requests.
     * @return the cherry pick requests
     */
    @OneToMany(mappedBy = "screen", cascade = { CascadeType.ALL })
    public Set<CherryPickRequest> getCherryPickRequests() {
        return _cherryPickRequests;
    }

    /**
     * Create and return a new cherry pick request for the screen of the appropriate type ({@link
     * SmallMoleculeCherryPickRequest} or {@link RNAiCherryPickRequest}. The cherry pick request will
     * have the {@link #getLeadScreener() lead screener} as the {@link
     * CherryPickRequest#getRequestedBy() requestor}, and the current date as the {@link
     * CherryPickRequest#getDateRequested() date requested}. It will not be a legacy ScreenDB
     * cherry pick.
     * @return the new cherry pick request
     */
    public CherryPickRequest createCherryPickRequest(AdministratorUser createdBy) {
        return createCherryPickRequest(createdBy, getLeadScreener(), new LocalDate());
    }

    /**
     * Create and return a new cherry pick request for the screen of the appropriate type ({@link
     * SmallMoleculeCherryPickRequest} or {@link RNAiCherryPickRequest}. The cherry pick request will
     * not be a legacy ScreenDB cherry pick.
     * @param requestedBy the requestor
     * @param dateRequested the date requested
     * @return the new cherry pick request
     */
    public CherryPickRequest createCherryPickRequest(AdministratorUser createdBy, ScreeningRoomUser requestedBy,
            LocalDate dateRequested) {
        CherryPickRequest cherryPickRequest;
        if (getScreenType().equals(ScreenType.RNAI)) {
            cherryPickRequest = new RNAiCherryPickRequest(createdBy, this, requestedBy, dateRequested);
        } else if (getScreenType().equals(ScreenType.SMALL_MOLECULE)) {
            cherryPickRequest = new SmallMoleculeCherryPickRequest(createdBy, this, requestedBy, dateRequested);
        } else {
            throw new UnsupportedOperationException(
                    "screen of type " + getScreenType() + " does not support cherry pick requests");
        }
        _cherryPickRequests.add(cherryPickRequest);
        return cherryPickRequest;
    }

    /**
     * Get the set of abase testsets.
     * @return the abase testsets
     */
    @OneToMany(mappedBy = "screen", cascade = { CascadeType.ALL }, orphanRemoval = true)
    public Set<AbaseTestset> getAbaseTestsets() {
        return _abaseTestsets;
    }

    /**
     * Create and return an <code>AbaseTestset</code> for the screen.
     * @param testsetDate the testset date
     * @param testsetName the testset name
     * @param comments the comments
     * @return the new abase testset
     */
    public AbaseTestset createAbaseTestset(LocalDate testsetDate, String testsetName, String comments) {
        AbaseTestset abaseTestset = new AbaseTestset(this, testsetDate, testsetName, comments);
        _abaseTestsets.add(abaseTestset);
        return abaseTestset;
    }

    /**
     * Get the publications.
     * @return the publications
     */
    @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.LAZY)
    @JoinTable(name = "screenPublicationLink", joinColumns = @JoinColumn(name = "screenId"), inverseJoinColumns = @JoinColumn(name = "publicationId"))
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_publication_link_to_screen")
    public Set<Publication> getPublications() {
        return _publications;
    }

    // note: for automated model unit tests, we can't name this createPublication or addPublication
    public Publication addCopyOfPublication(Publication publicationDTO) {
        Publication publication = new Publication();
        publication.setPubmedId(publicationDTO.getPubmedId());
        publication.setPubmedCentralId(publicationDTO.getPubmedCentralId());
        publication.setTitle(publicationDTO.getTitle());
        publication.setYearPublished(publicationDTO.getYearPublished());
        publication.setAuthors(publicationDTO.getAuthors());
        publication.setJournal(publicationDTO.getJournal());
        publication.setVolume(publicationDTO.getVolume());
        publication.setPages(publicationDTO.getPages());
        addPublication(publication);
        return publication;
    }

    /**
     * Get the attached files.
     * @return the attached files
     */
    @OneToMany(mappedBy = "screen", cascade = { CascadeType.ALL }, orphanRemoval = true)
    @ToMany(hasNonconventionalMutation = true)
    public Set<AttachedFile> getAttachedFiles() {
        return _attachedFiles;
    }

    /**
     * Create and return a new attached file for the screen.
     * @param filename the filename
     * @param fileType the file type
     * @param fileContents the file contents
     * @throws IOException
     */
    public AttachedFile createAttachedFile(String filename, ScreenAttachedFileType fileType, LocalDate fileDate,
            String fileContents) throws IOException {
        return createAttachedFile(filename, fileType, fileDate, new ByteArrayInputStream(fileContents.getBytes()));
    }

    /**
     * Create and return a new attached file for the screen.
     * 
     * @param filename the filename
     * @param fileType the file type
     * @param fileContents the file contents
     * @throws IOException
     */
    public AttachedFile createAttachedFile(String filename, ScreenAttachedFileType fileType, LocalDate fileDate,
            InputStream fileContents) throws IOException {
        AttachedFile attachedFile = new AttachedFile(this, filename, fileType, fileDate, fileContents);
        _attachedFiles.add(attachedFile);
        return attachedFile;
    }

    public void removeAttachedFile(AttachedFile attachedFile) {
        _attachedFiles.remove(attachedFile);
    }

    /**
     * Get the billing information.
     * @return the billing information
     */
    @edu.harvard.med.screensaver.model.annotations.Column(hasNonconventionalSetterMethod = true)
    public BillingInformation getBillingInformation() {
        return _billingInformation;
    }

    /**
     * Get the set of billing items.
     * @return the billing items
     */
    @ElementCollection
    @edu.harvard.med.screensaver.model.annotations.ElementCollection(hasNonconventionalMutation = true)
    @JoinTable(name = "screen_billing_item", joinColumns = @JoinColumn(name = "screen_id"))
    @org.hibernate.annotations.IndexColumn(name = "ordinal")
    public List<BillingItem> getBillingItems() {
        return _billingItems;
    }

    /**
     * Set the set of billing items.
     * @param billingItems the new set of billing items
     * @motivation for hibernate
     */
    private void setBillingItems(List<BillingItem> billingItems) {
        _billingItems = billingItems;
    }

    /**
     * Create and return a new billing item for this billing information.
     * @param itemToBeCharged the item to be charged
     * @param amount the amount
     * @param dateSentForBilling the date sent for billing
     * @return the new billing item for this billing information
     */
    public BillingItem createBillingItem(String itemToBeCharged, BigDecimal amount, LocalDate dateSentForBilling) {
        if (itemToBeCharged == null) {
            throw new RequiredPropertyException(this, "billing item name");
        }
        if (amount == null) {
            throw new RequiredPropertyException(this, "billing item amount");
        }
        // allowed, as per [#1607]
        //if (dateSentForBilling == null) {
        //  throw new RequiredPropertyException(this, "billing item date faxed");
        //}
        BillingItem billingItem = new BillingItem(itemToBeCharged, amount, dateSentForBilling);
        _billingItems.add(billingItem);
        return billingItem;
    }

    public BillingItem addCopyOfBillingItem(BillingItem dtoBillingItem) {
        return createBillingItem(dtoBillingItem.getItemToBeCharged(), dtoBillingItem.getAmount(),
                dtoBillingItem.getDateSentForBilling());
    }

    @Column(unique = true, nullable = false)
    @org.hibernate.annotations.Type(type = "text")
    public String getFacilityId() {
        return _facilityId;
    }

    public void setFacilityId(String name) {
        _facilityId = name;
    }

    @Column
    @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.screens.Species$UserType")
    public Species getSpecies() {
        return _species;
    }

    public void setSpecies(Species value) {
        _species = value;
    }

    @Column
    @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.screens.AssayType$UserType")
    public AssayType getAssayType() {
        return _assayType;
    }

    public void setAssayType(AssayType value) {
        _assayType = value;
    }

    /**
     * Get the study type.
     * @return the study type
     */
    @Column(nullable = false)
    @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.screens.StudyType$UserType")
    public StudyType getStudyType() {
        return _studyType;
    }

    /**
     * Set the study type.
     *
     * @param studyType the new studyType
     */
    public void setStudyType(StudyType studyType) {
        // commenting this until comprehensive testing is performed, since it requires many relationships to be eager fetched, which may cause problems
        //  if (isDataLoaded()) {
        //    throw new BusinessRuleViolationException("screen type is immutable after screen contains data");
        //  }
        _studyType = studyType;
    }

    /**
     * Get the screen type.
     * @return the screen type
     */
    @Column(nullable = false)
    @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.screens.ScreenType$UserType")
    public ScreenType getScreenType() {
        return _screenType;
    }

    /**
     * Set the screen type.
     * @param screenType the new screen type
     * @motivation for hibernate
     */
    public void setScreenType(ScreenType screenType) {
        // commenting this until comprehensive testing is performed, since it requires many relationships to be eager fetched, which may cause problems
        //    if (isDataLoaded()) {
        //      throw new BusinessRuleViolationException("screen type is immutable after screen contains data");
        //    }
        _screenType = screenType;
    }

    public void setProjectPhase(ProjectPhase projectPhase) {
        _projectPhase = projectPhase;
    }

    @Column(nullable = false)
    @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.screens.ProjectPhase$UserType")
    public ProjectPhase getProjectPhase() {
        return _projectPhase;
    }

    @Transient
    public boolean isDataLoaded() {
        return getScreenResult() != null || !getCherryPickRequests().isEmpty() || !getAnnotationTypes().isEmpty()
                || !getLabActivities().isEmpty();
    }

    /**
     * Get the title.
     * @return the title
     */
    @Column(nullable = false)
    @org.hibernate.annotations.Type(type = "text")
    public String getTitle() {
        return _title;
    }

    /**
     * Set the title.
     * @param title the new title
     */
    public void setTitle(String title) {
        _title = title;
    }

    public void setProjectId(String projectId) {
        _projectId = projectId;
    }

    @Column(nullable = true)
    @org.hibernate.annotations.Type(type = "text")
    public String getProjectId() {
        return _projectId;
    }

    /**
     * Get the study url.
     * @return the study url
     */
    @Column(nullable = true)
    @org.hibernate.annotations.Type(type = "text")
    public String getUrl() {
        return _url;
    }

    /**
     * Set the study url.
     * @param url the new study url
     */
    public void setUrl(String url) {
        _url = url;
    }

    /**
     * Get the data meeting scheduled.
     * @return the data meeting scheduled
     */
    @Column
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getDataMeetingScheduled() {
        return _dataMeetingScheduled;
    }

    /**
     * Set the data meeting scheduled date.
     * @param dataMeetingScheduled the new data meeting scheduled date
     */
    public void setDataMeetingScheduled(LocalDate dataMeetingScheduled) {
        _dataMeetingScheduled = dataMeetingScheduled;
    }

    /**
     * Get the data meeting completed date.
     * @return the data meeting completed date
     */
    @Column
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getDataMeetingComplete() {
        return _dataMeetingComplete;
    }

    /**
     * Set the data meeting complete.
     * @param dataMeetingComplete the new data meeting complete
     */
    public void setDataMeetingComplete(LocalDate dataMeetingComplete) {
        _dataMeetingComplete = dataMeetingComplete;
    }

    /**
     * Get the keywords.
     * @return the keywords
     */
    @ElementCollection
    @Column(name = "keyword", nullable = false)
    @JoinTable(name = "screenKeyword", joinColumns = @JoinColumn(name = "screenId"))
    @Sort(type = SortType.NATURAL)
    @org.hibernate.annotations.Type(type = "text")
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_keyword_to_screen")
    public SortedSet<String> getKeywords() {
        return _keywords;
    }

    /**
     * Set the keywords.
     * @param keywords the new keywords
     */
    public void setKeywords(SortedSet<String> keywords) {
        _keywords = keywords;
    }

    /**
     * Add the keyword.
     * @param keyword the keyword to add
     * @return true iff the screen did not already have the keyword
     */
    public boolean addKeyword(String keyword) {
        return _keywords.add(keyword);
    }

    /**
     * Remove the keyword.
     * @param keyword the keyword to remove
     * @return true iff the screen previously had the keyword
     */
    public boolean removeKeyword(String keyword) {
        return _keywords.remove(keyword);
    }

    /**
     * Get the set of funding supports.
     * @return the set of funding supports
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "screenFundingSupportLink", joinColumns = @JoinColumn(name = "screenId"), inverseJoinColumns = @JoinColumn(name = "fundingSupportId"))
    @org.hibernate.annotations.LazyCollection(value = org.hibernate.annotations.LazyCollectionOption.TRUE)
    @edu.harvard.med.screensaver.model.annotations.ToMany(unidirectional = true)
    public Set<FundingSupport> getFundingSupports() {
        return _fundingSupports;
    }

    /**
     * Add the funding support.
     * @param fundingSupport the funding support to add
     * @return true iff the screen did not already have the funding support
     */
    public boolean addFundingSupport(FundingSupport fundingSupport) {
        return _fundingSupports.add(fundingSupport);
    }

    /**
     * Remove the funding support.
     * @param fundingSupport the funding support to remove
     * @return true iff the screen previously had the funding support
     */
    public boolean removeFundingSupport(FundingSupport fundingSupport) {
        return _fundingSupports.remove(fundingSupport);
    }

    /**
     * Get the summary.
     * @return the summary
     */
    @org.hibernate.annotations.Type(type = "text")
    public String getSummary() {
        return _summary;
    }

    /**
     * Set the summary.
     * @param summary the new summary
     */
    public void setSummary(String summary) {
        _summary = summary;
    }

    /**
     * Get the comments.
     * @return the comments
     */
    @org.hibernate.annotations.Type(type = "text")
    public String getComments() {
        return _comments;
    }

    /**
     * Set the comments.
     * @param comments the new comments
     */
    public void setComments(String comments) {
        _comments = comments;
    }

    /**
     * Get the abase study id.
     * @return the abase study id
     */
    @org.hibernate.annotations.Type(type = "text")
    public String getAbaseStudyId() {
        return _abaseStudyId;
    }

    /**
     * Set the abase study id.
     * @param abaseStudyId the new abase study id
     */
    public void setAbaseStudyId(String abaseStudyId) {
        _abaseStudyId = abaseStudyId;
    }

    /**
     * Get the abase protocol id.
     * @return the abase protocol id
     */
    @org.hibernate.annotations.Type(type = "text")
    public String getAbaseProtocolId() {
        return _abaseProtocolId;
    }

    /**
     * Set the abase protocol id.
     * @param abaseProtocolId the new abase protocol id
     */
    public void setAbaseProtocolId(String abaseProtocolId) {
        _abaseProtocolId = abaseProtocolId;
    }

    @org.hibernate.annotations.Type(type = "text")
    public String getComsRegistrationNumber() {
        return _comsRegistrationNumber;
    }

    public void setComsRegistrationNumber(String comsRegistrationNumber) {
        _comsRegistrationNumber = comsRegistrationNumber;
    }

    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getComsApprovalDate() {
        return _comsApprovalDate;
    }

    public void setComsApprovalDate(LocalDate comsApprovalDate) {
        _comsApprovalDate = comsApprovalDate;
    }

    // TODO: extract PublishableProtocol value-typed collection

    /**
     * Get the date the publishable protocol was entered.
     * @return the date the publishable protocol was entered
     */
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getPublishableProtocolDateEntered() {
        return _publishableProtocolDateEntered;
    }

    /**
     * Set the date the publishable protocol was entered.
     * @param publishableProtocolDateEntered the new date the publishable protocol was entered
     */
    public void setPublishableProtocolDateEntered(LocalDate publishableProtocolDateEntered) {
        _publishableProtocolDateEntered = publishableProtocolDateEntered;
    }

    /**
     * Get the initials of the administrator who entered the publishable protocol.
     * @return the initials of the administrator who entered the publishable protocol
     */
    @org.hibernate.annotations.Type(type = "text")
    public String getPublishableProtocolEnteredBy() {
        return _publishableProtocolEnteredBy;
    }

    /**
     * Set the initials of the administrator who entered the publishable protocol.
     * @param publishableProtocolEnteredBy the new initials of the administrator who
     * entered the publishable protocol
     */
    public void setPublishableProtocolEnteredBy(String publishableProtocolEnteredBy) {
        _publishableProtocolEnteredBy = publishableProtocolEnteredBy;
    }

    /**
     * Get the publishable protocol.
     * @return the publishable protocol
     */
    @org.hibernate.annotations.Type(type = "text")
    public String getPublishableProtocol() {
        return _publishableProtocol;
    }

    /**
     * Set the publishable protocol.
     * @param publishableProtocol the new publishable protocol
     */
    public void setPublishableProtocol(String publishableProtocol) {
        _publishableProtocol = publishableProtocol;
    }

    /**
     * Get the publishable protocol comments.
     * @return the publishable protocol comments
     */
    @org.hibernate.annotations.Type(type = "text")
    public String getPublishableProtocolComments() {
        return _publishableProtocolComments;
    }

    /**
     * Set the publishable protocol comments.
     * @param publishableProtocolComments the new publishable protocol comments
     */
    public void setPublishableProtocolComments(String publishableProtocolComments) {
        _publishableProtocolComments = publishableProtocolComments;
    }

    @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.LAZY)
    @JoinColumn(name = "pin_transfer_admin_activity_id")
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_to_pin_transfer_admin_activity")
    @org.hibernate.annotations.LazyToOne(value = org.hibernate.annotations.LazyToOneOption.PROXY)
    @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
    @edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional = true, hasNonconventionalSetterMethod = true)
    public AdministrativeActivity getPinTransferApprovalActivity() {
        return _pinTransferApprovalActivity;
    }

    private void setPinTransferApprovalActivity(AdministrativeActivity pinTransferApprovalActivity) {
        _pinTransferApprovalActivity = pinTransferApprovalActivity;
    }

    public void setPinTransferApproved(AdministratorUser recordedBy, AdministratorUser approvedBy,
            LocalDate dateApproved, String comments) {
        if (_pinTransferApprovalActivity != null) {
            throw new BusinessRuleViolationException("pin transfer approval already recorded");
        }
        _pinTransferApprovalActivity = new AdministrativeActivity(recordedBy, approvedBy, dateApproved,
                AdministrativeActivityType.PIN_TRANSFER_APPROVAL);
        _pinTransferApprovalActivity.setComments(comments);
    }

    /**
     * Get the date of application.
     * @return the date of application
     */
    @Column
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getDateOfApplication() {
        return _dateOfApplication;
    }

    /**
     * Set the date of application.
     * @param dateOfApplication the new date of application
     */
    public void setDateOfApplication(LocalDate dateOfApplication) {
        _dateOfApplication = dateOfApplication;
    }

    /**
     * Get the annotation types provided by this study.
     * @return the annotation types
     */
    @OneToMany(mappedBy = "study", cascade = { CascadeType.ALL }, orphanRemoval = true)
    @Sort(type = SortType.NATURAL)
    public SortedSet<AnnotationType> getAnnotationTypes() {
        return _annotationTypes;
    }

    /**
     * Create and return a new annotation type for the study.
     * @param name the name of the annotation type
     * @param description the description for the annotation type
     * @param isNumeric true iff this annotation type contains numeric result values
     * @return the new annotation type
     */
    public AnnotationType createAnnotationType(String name, String description, boolean isNumeric) {
        verifyNameIsUnique(name);
        AnnotationType annotationType = new AnnotationType(this, name, description, _annotationTypes.size(),
                isNumeric);
        _annotationTypes.add(annotationType);
        return annotationType;
    }

    /**
     * Get the set of reagents associated with this screen result. <i>Do not modify
     * the returned collection.</i> To add a reagent, call {@link #addReagent}.
     * @motivation efficiently find all reagent-related data for a study (w/o reading annotationTypes.annotationValues.reagents)
     * @return the set of reagents associated with this screen result
     */
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "studyReagentLink", joinColumns = @JoinColumn(name = "studyId"), inverseJoinColumns = @JoinColumn(name = "reagentId"))
    @org.hibernate.annotations.ForeignKey(name = "fk_reagent_link_to_study")
    @org.hibernate.annotations.LazyCollection(value = org.hibernate.annotations.LazyCollectionOption.TRUE)
    @edu.harvard.med.screensaver.model.annotations.ToMany(inverseProperty = "studies")
    public Set<Reagent> getReagents() {
        return _reagents;
    }

    /**
     * Add the reagent.
     * @param reagent the reagent to add
     * @return true iff the screen did not already have the reagent
     */
    public boolean addReagent(Reagent reagent) {
        return addReagent(reagent, true);
    }

    public boolean addReagent(Reagent reagent, boolean createStudiesLink) {
        if (_reagents.add(reagent)) {
            if (createStudiesLink) {
                reagent.addStudy(this);
            }
            return true;
        }
        return false;
    }

    /**
     * Remove the reagent.
     * @param reagent the reagent to remove
     * @return true iff the screen previously had the reagent
     */
    public boolean removeReagent(Reagent reagent) {
        if (_reagents.remove(reagent)) {
            reagent.removeStudy(this);
            return true;
        }
        return false;
    }

    public boolean addPublication(Publication p) {
        return _publications.add(p);
    }

    @Transient
    public List<ScreenStatus> getCandidateStatuses() {
        List<ScreenStatus> candidateStatuses = new ArrayList<ScreenStatus>(Arrays.asList(ScreenStatus.values()));
        Set<Integer> illegalStatusRanks = new HashSet<Integer>();
        for (StatusItem statusItem : getStatusItems()) {
            illegalStatusRanks.add(statusItem.getStatus().getRank());
        }
        Iterator<ScreenStatus> iter = candidateStatuses.iterator();
        while (iter.hasNext()) {
            if (illegalStatusRanks.contains(iter.next().getRank())) {
                iter.remove();
            }
        }
        return candidateStatuses;
    }

    // private instance methods

    /**
     * Set the id for the screen.
     * @param screenId the new id for the screen
     * @motivation for hibernate
     */
    private void setScreenId(Integer screenId) {
        setEntityId(screenId);
    }

    /**
     * Get the version for the screen.
     * @return the version for the screen
     * @motivation for hibernate
     */
    @Version
    @Column(nullable = false)
    private Integer getVersion() {
        return _version;
    }

    /**
     * Set the version for the screen.
     * @param version the new version for the screen
     * @motivation for hibernate
     */
    private void setVersion(Integer version) {
        _version = version;
    }

    /**
     * Set the set of collaborators.
     * @param collaborators the new set of collaborators
     * @motivation for hibernate
     */
    private void setCollaborators(SortedSet<ScreeningRoomUser> collaborators) {
        _collaborators = collaborators;
    }

    /**
     * Set the screen result.
     * @param screenResult the new screen result
     * @motivation for hibernate
     */
    private void setScreenResult(ScreenResult screenResult) {
        _screenResult = screenResult;
    }

    /**
     * Set the status items.
     * @param statusItems the new status items
     * @motivation for hibernate
     */
    private void setStatusItems(SortedSet<StatusItem> statusItems) {
        _statusItems = statusItems;
    }

    /**
     * @motivation for hibernate
     */
    private void setLabActivities(SortedSet<LabActivity> labActivities) {
        _labActivities = labActivities;
    }

    /**
     * @motivation for hibernate
     */
    private void setServiceActivities(SortedSet<ServiceActivity> values) {
        _serviceActivities = values;
    }

    /**
     * Set the cherry pick requests.
     * @param cherryPickRequests the new cherry pick requests
     * @motivation for hibernate
     */
    private void setCherryPickRequests(Set<CherryPickRequest> cherryPickRequests) {
        _cherryPickRequests = cherryPickRequests;
    }

    /**
     * Set the abase testsets.
     * @param abaseTestsets the new abase testsets
     * @motivation for hibernate
     */
    private void setAbaseTestsets(Set<AbaseTestset> abaseTestsets) {
        _abaseTestsets = abaseTestsets;
    }

    /**
     * Set the publications.
     * @param publications the new publications
     * @motivation for hibernate
     */
    private void setPublications(Set<Publication> publications) {
        _publications = publications;
    }

    /**
     * Set the attached files.
     * @param attachedFiles the new attached files
     * @motivation for hibernate
     */
    private void setAttachedFiles(Set<AttachedFile> attachedFiles) {
        _attachedFiles = attachedFiles;
    }

    /**
     * Set the billing information.
     * @param billingInformation the new billing information
     * @motivation for hibernate
     */
    private void setBillingInformation(BillingInformation billingInformation) {
        _billingInformation = billingInformation;
    }

    /**
     * Set the funding supports.
     * @param fundingSupports the new funding supports
     * @motivation for hibernate
     */
    private void setFundingSupports(Set<FundingSupport> fundingSupports) {
        _fundingSupports = fundingSupports;
    }

    /**
     * Set the annotation types.
     * @param annotationTypes the new annotation types
     * @motivation for hibernate
     */
    private void setAnnotationTypes(SortedSet<AnnotationType> annotationTypes) {
        _annotationTypes = annotationTypes;
    }

    /**
     * Set the reagents.
     * @param reagents the new reagents
     * @motivation for hibernate
     */
    private void setReagents(Set<Reagent> reagents) {
        _reagents = reagents;
    }

    private void verifyNameIsUnique(String name) {
        for (AnnotationType at : getAnnotationTypes()) {
            if (at.getName().equals(name)) {
                throw new DuplicateEntityException(this, at);
            }
        }
    }

    /**
     * Get the set of libraries that this screen has been authorized to screen,
     * even if the library's screening status would otherwise not permit it.
     */
    @Transient
    public Set<Library> getLibrariesPermitted() {
        return Collections.emptySet();
    }

    //  public void setCellLine(CellLine _cellLine)
    //  {
    //    this._cellLine = _cellLine;
    //  }

    //  @ManyToOne
    //  @JoinColumn(name="cellLineId", nullable=true)
    //  @org.hibernate.annotations.ForeignKey(name="fk_screen_to_cell_line")
    //  @org.hibernate.annotations.Cascade(value={ org.hibernate.annotations.CascadeType.SAVE_UPDATE })
    //  @edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional=true)  
    //  public CellLine getCellLine()
    //  {
    //    return _cellLine;
    //  }

    /**
     * Get the set of cell lines.
     * @return the set of cell lines
     */
    @ManyToMany(fetch = FetchType.LAZY)
    @edu.harvard.med.screensaver.model.annotations.Column(hasNonconventionalSetterMethod = true)
    @JoinTable(name = "screenCellLine", joinColumns = @JoinColumn(name = "screenId"), inverseJoinColumns = @JoinColumn(name = "cellLineId"))
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_cell_line_to_screen")
    @org.hibernate.annotations.LazyCollection(value = org.hibernate.annotations.LazyCollectionOption.TRUE)
    @Sort(type = SortType.NATURAL)
    public SortedSet<CellLine> getCellLines() {
        return _cellLines;
    }

    /**
     * Set the cellLines.
     * @param cellLines the new cellLines
     */
    private void setCellLines(SortedSet<CellLine> cellLines) {
        _cellLines = cellLines;
    }

    /**
     * Add the cell line.
     * @param keyword the cell line to add
     * @return true iff the screen did not already have the cell line
     */
    public boolean addCellLine(CellLine cellLine) {
        return _cellLines.add(cellLine);
    }

    /**
     * Remove the cell line.
     * @param cell line the cell line to remove
     * @return true iff the screen previously had the cellline
     */
    public boolean removeCellLine(CellLine cellLine) {
        return _cellLines.remove(cellLine);
    }

    public void setTransfectionAgent(TransfectionAgent _transfectionAgent) {
        this._transfectionAgent = _transfectionAgent;
    }

    @ManyToOne
    @JoinColumn(name = "transfectionAgentId", nullable = true)
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_to_transfection_agent")
    @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
    @edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional = true)
    public TransfectionAgent getTransfectionAgent() {
        return _transfectionAgent;
    }

    @Column(nullable = false)
    //@org.hibernate.annotations.Type(type="edu.harvard.med.screensaver.model.screens.ScreenDataSharingLevel$UserType")
    public ScreenDataSharingLevel getDataSharingLevel() {
        return _dataSharingLevel;
    }

    public void setDataSharingLevel(ScreenDataSharingLevel dataSharingLevel) {
        _dataSharingLevel = dataSharingLevel;
    }

    /**
     * The date on which a level 2 or 3 screen is to become level 1.
     */
    @Column
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getDataPrivacyExpirationDate() {
        return _dataPrivacyExpirationDate;
    }

    public void setDataPrivacyExpirationDate(LocalDate dataPrivacyExpirationDate) {
        if (dataPrivacyExpirationDate == null) {
            if (_maxAllowedDataPrivacyExpirationDate != null || _minAllowedDataPrivacyExpirationDate != null) {
                throw new DataModelViolationException(
                        "null value not allowed for condition if(maxAllowedDataPrivacyExpirationDate != null || minAllowedDataPrivacyExpirationDate != null)");
            }
        }

        LocalDate min = _minAllowedDataPrivacyExpirationDate;
        LocalDate max = _maxAllowedDataPrivacyExpirationDate;
        if (max != null && dataPrivacyExpirationDate.compareTo(max) > 0) {
            dataPrivacyExpirationDate = max;
        } else if (min != null && dataPrivacyExpirationDate.compareTo(min) < 0) {
            dataPrivacyExpirationDate = min;
        }
        _dataPrivacyExpirationDate = dataPrivacyExpirationDate;
    }

    public void setMinAllowedDataPrivacyExpirationDate(LocalDate minAllowedDataPrivacyExpirationDate) {
        _minAllowedDataPrivacyExpirationDate = minAllowedDataPrivacyExpirationDate;
        if (minAllowedDataPrivacyExpirationDate == null)
            return;
        if (_dataPrivacyExpirationDate == null
                || _dataPrivacyExpirationDate.compareTo(minAllowedDataPrivacyExpirationDate) < 0) {
            _dataPrivacyExpirationDate = minAllowedDataPrivacyExpirationDate;
        }
    }

    @Column
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getMinAllowedDataPrivacyExpirationDate() {
        return _minAllowedDataPrivacyExpirationDate;
    }

    public void setMaxAllowedDataPrivacyExpirationDate(LocalDate maxAllowedDataPrivacyExpirationDate) {
        _maxAllowedDataPrivacyExpirationDate = maxAllowedDataPrivacyExpirationDate;
        if (maxAllowedDataPrivacyExpirationDate == null)
            return;
        if (_dataPrivacyExpirationDate == null
                || _dataPrivacyExpirationDate.compareTo(maxAllowedDataPrivacyExpirationDate) > 0) {
            _dataPrivacyExpirationDate = maxAllowedDataPrivacyExpirationDate;
        }
    }

    @Column
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getMaxAllowedDataPrivacyExpirationDate() {
        return _maxAllowedDataPrivacyExpirationDate;
    }

    /**
     * Call this to set the {@link Screen#getDataPrivacyExpirationDate()} after the 
     * {@link Screen#getMinAllowedDataPrivacyExpirationDate()} and the {@link Screen#getMaxAllowedDataPrivacyExpirationDate()}
     * have been set.<br>
     */
    private void updateDataPrivacyExpirationDate() {
        LocalDate requestedDate = getDataPrivacyExpirationDate();
        LocalDate max = getMaxAllowedDataPrivacyExpirationDate();
        LocalDate min = getMinAllowedDataPrivacyExpirationDate();

        if (requestedDate.compareTo(max) > 0 & max != null) {
            _dataPrivacyExpirationDate = max;
        } else if (min != null && requestedDate.compareTo(min) < 0) {
            _dataPrivacyExpirationDate = min;
        } else {
            _dataPrivacyExpirationDate = requestedDate;
        }
    }

    public void setDataPrivacyExpirationNotifiedDate(LocalDate dataPrivacyExpirationNotifiedDate) {
        _dataPrivacyExpirationNotifiedDate = dataPrivacyExpirationNotifiedDate;
    }

    /**
     * The date at which a dataPrivacyExpiration email was sent to Screensaver
     * Users associated with this Screen.
     */
    @Column
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getDataPrivacyExpirationNotifiedDate() {
        return _dataPrivacyExpirationNotifiedDate;
    }

    @Column(nullable = true)
    @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType")
    public LocalDate getPubchemDepositedDate() {
        return _pubchemDepositedDate;
    }

    public void setPubchemDepositedDate(LocalDate pubchemDepositedDate) {
        _pubchemDepositedDate = pubchemDepositedDate;
    }

    @Column(nullable = true)
    public Integer getPubchemAssayId() {
        return _pubchemAssayId;
    }

    public void setPubchemAssayId(Integer pubchemAssayId) {
        _pubchemAssayId = pubchemAssayId;
    }

    /**
     * Specify the specific compound studied, if applicable
     * for [#3155] Create a linkable field for "compound studied" on the study
     */
    public void setWellStudied(Well wellStudied) {
        this.wellStudied = wellStudied;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "wellStudiedId", nullable = true, updatable = true)
    @org.hibernate.annotations.ForeignKey(name = "fk_screen_to_well_studied")
    @org.hibernate.annotations.LazyToOne(value = org.hibernate.annotations.LazyToOneOption.PROXY)
    public Well getWellStudied() {
        return wellStudied;
    }

    @Column(precision = ScreensaverConstants.MOLAR_CONCENTRATION_PRECISION, scale = ScreensaverConstants.MOLAR_CONCENTRATION_SCALE)
    @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.db.usertypes.MolarConcentrationType")
    public MolarConcentration getPerturbagenMolarConcentration() {
        return perturbagenMolarConcentration;
    }

    public void setPerturbagenMolarConcentration(MolarConcentration value) {
        this.perturbagenMolarConcentration = value;
    }

    public void setPerturbagenUgMlConcentration(BigDecimal ugMlConcentration) {
        this.perturbagenUgMlConcentration = ugMlConcentration;
    }

    @Column(precision = ScreensaverConstants.UG_ML_CONCENTRATION_PRECISION, scale = ScreensaverConstants.UG_ML_CONCENTRATION_SCALE)
    public BigDecimal getPerturbagenUgMlConcentration() {
        return this.perturbagenUgMlConcentration;
    }

    /**
     */
    @OneToMany(mappedBy = "screen", cascade = { CascadeType.ALL }, orphanRemoval = false)
    @ToMany(hasNonconventionalMutation = true)
    @org.hibernate.annotations.Sort(type = org.hibernate.annotations.SortType.NATURAL)
    @org.hibernate.annotations.ForeignKey(name = "fk_experimental_cell_information_set_to_screen")
    public Set<ExperimentalCellInformation> getExperimentalCellInformationSet() {
        return _experimentalCellInformationSet;
    }

    public void setExperimentalCellInformationSet(SortedSet<ExperimentalCellInformation> value) {
        _experimentalCellInformationSet = value;
    }

}