Java tutorial
// $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.users; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; 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.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Transient; import com.google.common.base.Function; import com.google.common.collect.Sets; import org.apache.log4j.Logger; import org.hibernate.annotations.Sort; import org.hibernate.annotations.SortType; import org.joda.time.LocalDate; 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.activities.Activity; import edu.harvard.med.screensaver.model.activities.ServiceActivity; import edu.harvard.med.screensaver.model.annotations.ToMany; import edu.harvard.med.screensaver.model.annotations.ToOne; 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.screens.Screen; import edu.harvard.med.screensaver.model.screens.ScreenType; /** * A person that is using the screening facility to conduct one or more {@link Screen}s. * * @see AdministratorUser * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a> * @author <a mailto="john_sullivan@hms.harvard.edu">John Sullivan</a> */ @Entity @PrimaryKeyJoinColumn(name = "screensaverUserId") @org.hibernate.annotations.ForeignKey(name = "fk_screening_room_user_to_screensaver_user") @org.hibernate.annotations.Proxy public class ScreeningRoomUser extends ScreensaverUser implements AttachedFilesEntity<UserAttachedFileType, Integer>, ChecklistItemsEntity<Integer> { // private static data private static final Logger log = Logger.getLogger(ScreeningRoomUser.class); private static final long serialVersionUID = 0L; public static final RelationshipPath<ScreeningRoomUser> LabHead = RelationshipPath.from(ScreeningRoomUser.class) .to("labHead", Cardinality.TO_ONE); public static final RelationshipPath<ScreeningRoomUser> attachedFiles = RelationshipPath .from(ScreeningRoomUser.class).to("attachedFiles"); public static final RelationshipPath<ScreeningRoomUser> screensLed = RelationshipPath .from(ScreeningRoomUser.class).to("screensLed"); public static final RelationshipPath<ScreeningRoomUser> screensCollaborated = RelationshipPath .from(ScreeningRoomUser.class).to("screensCollaborated"); public static final PropertyPath<ScreeningRoomUser> facilityUsageRoles = PropertyPath .from(ScreeningRoomUser.class).toCollectionOfValues("facilityUsageRoles"); public static final RelationshipPath<ScreeningRoomUser> checklistItemEvents = RelationshipPath .from(ScreeningRoomUser.class).to("checklistItemEvents"); public static final RelationshipPath<ScreeningRoomUser> serviceActivities = RelationshipPath .from(ScreeningRoomUser.class).to("serviceActivities"); public static final Function<ScreeningRoomUser, String> ToDisplayStringFunction = new Function<ScreeningRoomUser, String>() { public String apply(ScreeningRoomUser u) { return ScreensaverUser.ToDisplayStringFunction.apply(u); } }; public static final Function<ScreeningRoomUser, String> ToFullNameLastFirstAndIdAndLabName = new Function<ScreeningRoomUser, String>() { public String apply(ScreeningRoomUser lh) { return ScreeningRoomUser.ToFullNameLastFirstAndId.apply(lh) + (lh.getLab().getLabAffiliation() == null ? "" : (" - " + lh.getLab().getLabAffiliation().getAffiliationName())); } }; // private instance data private SortedSet<ChecklistItemEvent> _checklistItemEvents = Sets.newTreeSet(); private Set<Screen> _screensLed = Sets.newHashSet(); private Set<Screen> _screensCollaborated = Sets.newHashSet(); private Set<AttachedFile> _attachedFiles = Sets.newHashSet(); protected ScreeningRoomUserClassification _userClassification; private Set<FacilityUsageRole> _facilityUsageRoles = Sets.newHashSet(); private String _comsCrhbaPermitNumber; private String _comsCrhbaPermitPrincipalInvestigator; private ChecklistItemEvent _lastNotifiedSMUAChecklistItemEvent; private ChecklistItemEvent _lastNotifiedRNAiUAChecklistItemEvent; private SortedSet<ServiceActivity> _serviceActivities; private LabHead _labHead; protected transient Lab _lab; // public constructor /** * Construct an uninitialized <code>ScreeningRoomUser</code>. * @motivation for hibernate and proxy/concrete subclass constructors */ protected ScreeningRoomUser() { } /** * Construct an uninitialized <code>ScreeningRoomUser</code>. * @motivation for new ScreeningRoomUser creation via user interface, where even required * fields are allowed to be uninitialized, initially */ public ScreeningRoomUser(AdministratorUser createdBy) { super(createdBy); } /** @motivation for test code only */ public ScreeningRoomUser(String firstName, String lastName, ScreeningRoomUserClassification userClassification) { super(firstName, lastName); setUserClassification(userClassification); } /** @motivation for test code only */ public ScreeningRoomUser(String firstName, String lastName) { this(firstName, lastName, ScreeningRoomUserClassification.UNASSIGNED); } @Override public Object acceptVisitor(AbstractEntityVisitor visitor) { return visitor.visit(this); } /** * Get the set of checklist item events. * @return the checklist item events */ @OneToMany(mappedBy = "screeningRoomUser", cascade = { CascadeType.ALL }, orphanRemoval = true) @Sort(type = SortType.NATURAL) public SortedSet<ChecklistItemEvent> getChecklistItemEvents() { return _checklistItemEvents; } @Transient public SortedSet<ChecklistItemEvent> getChecklistItemEvents(ChecklistItem checklistItem) { SortedSet<ChecklistItemEvent> result = new TreeSet<ChecklistItemEvent>(); for (ChecklistItemEvent itemEvent : getChecklistItemEvents()) { if (itemEvent.getChecklistItem().equals(checklistItem)) { result.add(itemEvent); } } return result; } /** * Create a new checklist item activation/completed event for the user. * @param checklistItem the checklist item * @param datePerformed the date the checklist item was performed by the user or otherwise enacted * @return the new checklist item for the user * @see ChecklistItemEvent#createChecklistItemExpirationEvent(LocalDate, AdministratorUser) */ public ChecklistItemEvent createChecklistItemActivationEvent(ChecklistItem checklistItem, LocalDate datePerformed, AdministratorUser recordedBy) { SortedSet<ChecklistItemEvent> checklistItemEvents = getChecklistItemEvents(checklistItem); if (checklistItemEvents.size() > 0) { if (!checklistItemEvents.last().isExpiration()) { throw new DataModelViolationException( "cannot add checklist item activation when checklist item is already activated"); } if (datePerformed.compareTo(checklistItemEvents.last().getDatePerformed()) < 0) { // throw new DataModelViolationException("checklist item activation date must be on or after the previous expiration date"); log.info( "Note: setting ChecklistItemEvent to a date that is on or before the previous expiration date:\n" + "UserId: " + getEntityId() + "\nOld date: " + checklistItemEvents.last().getDatePerformed() + "\nNew date: " + datePerformed); } } ChecklistItemEvent checklistItemEvent = new ChecklistItemEvent(checklistItem, this, datePerformed, recordedBy); _checklistItemEvents.add(checklistItemEvent); return checklistItemEvent; } /** * Create a new checklist item "not applicable" event for the user. Onlvy * valid to call this method if no other events exist for this checklist item, * for this user. * * @param checklistItem the checklist item * @param datePerformed the date the checklist item was marked as * "not applicable" * @return the new checklist item for the user */ public ChecklistItemEvent createChecklistItemNotApplicableEvent(ChecklistItem checklistItem, LocalDate datePerformed, AdministratorUser recordedBy) { SortedSet<ChecklistItemEvent> checklistItemEvents = getChecklistItemEvents(checklistItem); if (checklistItemEvents.size() > 0) { throw new DataModelViolationException( "cannot set a checklist item to 'not applicable' if the item is already activated/deactivated/completed"); } ChecklistItemEvent checklistItemEvent = new ChecklistItemEvent(checklistItem, this, datePerformed, recordedBy, true); _checklistItemEvents.add(checklistItemEvent); return checklistItemEvent; } /** * Get the set of screens for which this user is the lead screener. * @return the set of screens for which this user is the lead screener */ @OneToMany(mappedBy = "leadScreener", fetch = FetchType.LAZY) @edu.harvard.med.screensaver.model.annotations.ToMany(singularPropertyName = "screenLed", inverseProperty = "leadScreener") public Set<Screen> getScreensLed() { return _screensLed; } public boolean addScreenLed(Screen screenLed) { if (_screensLed.add(screenLed)) { screenLed.setLeadScreener(this); return true; } return false; } /** * Get the set of screens for which this user was a collaborator. * @return the set of screens for which this user was a collaborator */ @ManyToMany(mappedBy = "collaborators", targetEntity = Screen.class, fetch = FetchType.LAZY) @org.hibernate.annotations.ForeignKey(name = "fk_collaborator_link_to_screening_room_user") @org.hibernate.annotations.LazyCollection(value = org.hibernate.annotations.LazyCollectionOption.TRUE) @edu.harvard.med.screensaver.model.annotations.ToMany(singularPropertyName = "screenCollaborated", inverseProperty = "collaborators") public Set<Screen> getScreensCollaborated() { return _screensCollaborated; } /** * Add the screens for which this user was a collaborator. * @param screenCollaborated the screens for which this user was a collaborato to add * @return true iff the screening room user did not already have the screens for which this user was a collaborato */ public boolean addScreenCollaborated(Screen screenCollaborated) { if (_screensCollaborated.add(screenCollaborated)) { return screenCollaborated.getCollaborators().add(this); } return false; } /** * Remove the screens for which this user was a collaborator. * @param screenCollaborated the screens from which this user is no longer a collaborator * @return true iff the screening room user previously was a collaborator on the screen */ public boolean removeScreenCollaborated(Screen screenCollaborated) { if (_screensCollaborated.remove(screenCollaborated)) { return screenCollaborated.getCollaborators().remove(this); } return false; } /** * @return a Set of Screens comprised of the screens this user has led and collaborated on. */ @Transient public Set<Screen> getAllAssociatedScreens() { Set<Screen> screens = new HashSet<Screen>(); screens.addAll(getScreensLed()); screens.addAll(getScreensCollaborated()); return screens; } /** * @return the Set of ScreeningRoomUsers that are lab members or the lab head * of this user's lab, and all collaborators on screens this user is * associated with. This user himself is not included in the returned * Set. */ @Transient public Set<ScreeningRoomUser> getAssociatedUsers() { Set<ScreeningRoomUser> associates = Sets.newHashSet(getLab().getLabMembersAndLabHead()); for (Screen screen : getAllAssociatedScreens()) { associates.addAll(screen.getAssociatedScreeningRoomUsers()); } associates.remove(this); return associates; } /** * Get the user classification. * @return the user classification */ @Column(nullable = false) @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.users.ScreeningRoomUserClassification$UserType") public ScreeningRoomUserClassification getUserClassification() { return _userClassification; } /** * Set the user classification. * @param userClassification the new user classification */ public void setUserClassification(ScreeningRoomUserClassification userClassification) { assert userClassification != null : "user classification must be non-null"; if (userClassification == ScreeningRoomUserClassification.PRINCIPAL_INVESTIGATOR) { throw new BusinessRuleViolationException( "cannot change the classification of a non-lab head to principal investigator"); } _userClassification = userClassification; } @ElementCollection @Column(name = "facilityUsageRole", nullable = false) @JoinTable(name = "screening_room_user_facility_usage_role", joinColumns = @JoinColumn(name = "screening_room_user_id")) @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.users.FacilityUsageRole$UserType") public Set<FacilityUsageRole> getFacilityUsageRoles() { return _facilityUsageRoles; } public void setFacilityUsageRoles(Set<FacilityUsageRole> facilityUsages) { _facilityUsageRoles = facilityUsages; } /** * Get the COMS-CRHBA permit number. * @return the COMS-CRHBA permit number */ @org.hibernate.annotations.Type(type = "text") public String getComsCrhbaPermitNumber() { return _comsCrhbaPermitNumber; } /** * Set the COMS-CRHBA permit number. * @param comsCrhbaPermitNumber the new COMS-CRHBA permit number for the user */ public void setComsCrhbaPermitNumber(String comsCrhbaPermitNumber) { _comsCrhbaPermitNumber = comsCrhbaPermitNumber; } /** * Get the COMS-CRHBA permit principal investigator. * @return the COMS-CRHBA permit principal investigator */ @org.hibernate.annotations.Type(type = "text") public String getComsCrhbaPermitPrincipalInvestigator() { return _comsCrhbaPermitPrincipalInvestigator; } /** * Set the COMS-CRHBA permit principal investigator. * @param comsCrhbaPermitPrincipalInvestigator the new COMS-CRHBA permit principal investigator */ public void setComsCrhbaPermitPrincipalInvestigator(String comsCrhbaPermitPrincipalInvestigator) { _comsCrhbaPermitPrincipalInvestigator = comsCrhbaPermitPrincipalInvestigator; } /** * Get the attached files. * @return the attached files */ @OneToMany(mappedBy = "screeningRoomUser", cascade = { CascadeType.ALL }, orphanRemoval = true) @ToMany(hasNonconventionalMutation = true) public Set<AttachedFile> getAttachedFiles() { return _attachedFiles; } private void setAttachedFiles(Set<AttachedFile> attachedFiles) { _attachedFiles = attachedFiles; } public AttachedFile createAttachedFile(String filename, UserAttachedFileType fileType, LocalDate fileDate, String fileContents) throws IOException { return createAttachedFile(filename, fileType, fileDate, new ByteArrayInputStream(fileContents.getBytes())); } public AttachedFile createAttachedFile(String filename, UserAttachedFileType 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 whether this user is an RNAi screener. * @return <code>true</code> iff this user is an RNAi screener. */ @Transient public boolean isRnaiUser() { return getScreensaverUserRoles().contains(ScreensaverUserRole.RNAI_DSL_LEVEL3_SHARED_SCREENS); } /** * Get whether this user is a small molecule screener. * @return <code>true</code> iff this user is a small molecule screener. */ @Transient public boolean isSmallMoleculeUser() { return getScreensaverUserRoles().contains(ScreensaverUserRole.SM_DSL_LEVEL3_SHARED_SCREENS); } // protected instance methods @Override protected boolean validateRole(ScreensaverUserRole role) { if (log.isDebugEnabled()) { log.debug("validateRole " + this + " " + role); } return !role.isAdministrative(); } // private constructor and instance methods /** * @motivation for hibernate */ private void setChecklistItemEvents(SortedSet<ChecklistItemEvent> checklistItemEvents) { _checklistItemEvents = checklistItemEvents; } /** * Set the screens for which this user was the lead screener. * @param screensLed the new screens for which this user was the lead screener * @motivation for hibernate */ private void setScreensLed(Set<Screen> screensLed) { _screensLed = screensLed; } /** * Set the screens for which this user was a collaborator. * @param screensCollaborated the new screens for which this user was a collaborator * @motivation for hibernate */ private void setScreensCollaborated(Set<Screen> screensCollaborated) { _screensCollaborated = screensCollaborated; } // public lab methods /** * Get the Lab to which this user belongs. This is an abstraction of the * labHead relationship, as the labHead "is" the lab, but that concept is * confusing from the perspective of client code, so we provide an explicit * Lab object instead. * * @return the Lab to which this user belongs; a ScreeningRoomUser that is not a LabHead may not have a lab at all, in * which case a Lab will be returned that has a null labHead and empty labName and labAffiliation (Null Object * pattern); modifying its labMembers collection will have not effect on persisted * data. * @motivation prevents client code from being able to set properties that * should only be set on the associated LabHead (e.g. labAffiliation). */ @Transient public Lab getLab() { if (_lab == null) { if (_labHead != null) { _lab = _labHead.getLab(); } else { _lab = new Lab(null); } } return _lab; } /** * Set or change the lab of a non-lab head (i.e., non-Principal Investigator) screening * room user. * * @param lab the new lab; null if the user is no longer a member of any lab */ public void setLab(Lab lab) { if (isHeadOfLab()) { throw new DataModelViolationException("cannot modify the lab of a lab head"); } // remove from existing lab getLab().getLabMembers().remove(this); _lab = lab; _labHead = null; // add to new lab getLab().getLabMembers().add(this); _labHead = getLab().getLabHead(); } /** * Return true iff this user is the head of a lab. Users are considered a lab * heads if they have * {@link ScreeningRoomUserClassification#PRINCIPAL_INVESTIGATOR} * classification. * * @return true iff this user is the head of a lab * @motivation cannot name this method isLabHead or java.beans.BeanInfo * classes get confused about the appropriate type and getter * method for property labHead, screwing up our model unit tests */ @Transient public boolean isHeadOfLab() { return false; } // private lab methods /** * Get the ScreeningRoomUser that is the head of this user's lab, which may be * this user. * * @see #getLab() * @return the lab head; <code>this</code>, if the this user is the lab head. */ @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE }) @JoinColumn(name = "labHeadId", nullable = true) @org.hibernate.annotations.ForeignKey(name = "fk_screening_room_user_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 = "labMembers") private LabHead getLabHead() { return _labHead; } /** * Set the lab head. * @param labHead the new lab head */ protected void setLabHead(LabHead labHead) { _labHead = labHead; } /** * Adds the appropriate {@link FacilityUsageRole} for the screen type of the * screen with which the user is being associated with. */ public void updateFacilityUsageRoleForAssociatedScreens() { _facilityUsageRoles.clear(); for (Screen screen : getAllAssociatedScreens()) { if (screen.getScreenType() == ScreenType.SMALL_MOLECULE) { getFacilityUsageRoles().add(FacilityUsageRole.SMALL_MOLECULE_SCREENER); } else if (screen.getScreenType() == ScreenType.RNAI) { getFacilityUsageRoles().add(FacilityUsageRole.RNAI_SCREENER); } } } /** * Get the ChecklistItemEvent of the successful notification, if any, that was sent to this User * for the Small Molecule User Agreement expiration. * <p> * This is an ICCB-specific property, which should not be part of the Screensaver domain model, but is currently, for * practical reasons. */ @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "lastNotifiedSmuaChecklistItemEventId", nullable = true) @org.hibernate.annotations.ForeignKey(name = "fk_screening_room_user_to_notified_smua_checklist_item_event") @ToOne(hasNonconventionalSetterMethod = true /* this is only set under special circumstances */) public ChecklistItemEvent getLastNotifiedSMUAChecklistItemEvent() { return _lastNotifiedSMUAChecklistItemEvent; } public void setLastNotifiedSMUAChecklistItemEvent(ChecklistItemEvent lastExpiredSMUAChecklistItemEvent) { _lastNotifiedSMUAChecklistItemEvent = lastExpiredSMUAChecklistItemEvent; } /** * Get the ChecklistItemEvent of the successful notification, if any, that was sent to this User * for the Small Molecule User Agreement expiration. * <p> * This is an ICCB-specific property, which should not be part of the Screensaver domain model, but is currently, for * practical reasons. */ @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "lastNotifiedRnaiuaChecklistItemEventId", nullable = true) @org.hibernate.annotations.ForeignKey(name = "fk_screening_room_user_to_notified_rnaiua_checklist_item_event") @ToOne(hasNonconventionalSetterMethod = true /* this is only set under special circumstances */) public ChecklistItemEvent getLastNotifiedRNAiUAChecklistItemEvent() { return _lastNotifiedRNAiUAChecklistItemEvent; } public void setLastNotifiedRNAiUAChecklistItemEvent(ChecklistItemEvent lastExpiredRNAiUAChecklistItemEvent) { _lastNotifiedRNAiUAChecklistItemEvent = lastExpiredRNAiUAChecklistItemEvent; } @Transient public SortedSet<Activity> getAssociatedActivities() { SortedSet<Activity> activities = Sets.newTreeSet(); activities.addAll(getActivitiesPerformed()); activities.addAll(getServiceActivities()); return activities; } @OneToMany(mappedBy = "servicedUser") @edu.harvard.med.screensaver.model.annotations.ToMany(singularPropertyName = "serviceActivity") @Sort(type = SortType.NATURAL) public SortedSet<ServiceActivity> getServiceActivities() { return _serviceActivities; } private void setServiceActivities(SortedSet<ServiceActivity> serviceActivities) { _serviceActivities = serviceActivities; } }