// $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.libraries;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import com.google.common.collect.Sets;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.MapKeyManyToMany;
import org.hibernate.annotations.Type;
import org.joda.time.LocalDate;
import edu.harvard.med.screensaver.model.AbstractEntity;
import edu.harvard.med.screensaver.model.AttachedFile;
import edu.harvard.med.screensaver.model.AttachedFilesEntity;
import edu.harvard.med.screensaver.model.annotations.ContainedEntity;
import edu.harvard.med.screensaver.model.annotations.ToMany;
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.AnnotationValue;
import edu.harvard.med.screensaver.model.screens.Publication;
import edu.harvard.med.screensaver.model.screens.Screen;
/**
* A substance, such as a {@link SmallMoleculeReagent} or {@link SilencingReagent}, used to
* test the response of a biological system to a specific perturbation. Reagents
* are contained in {@link Library} {@link Well wells}.
*
* @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a>
*/
@Entity
@Immutable
@Table(uniqueConstraints={@UniqueConstraint(columnNames={"wellId", "libraryContentsVersionId"})})
@org.hibernate.annotations.Table(appliesTo = "reagent", indexes = {
@Index(name = "reagent_vendor_identifier_index", columnNames = { "vendorIdentifier" }) })
@org.hibernate.annotations.Proxy(lazy=false) // proxying causes problems with casts of getLatestReleasedReagent() return value
@Inheritance(strategy=InheritanceType.JOINED)
@ContainedEntity(containingEntityClass=Well.class)
public abstract class Reagent extends AbstractEntity<Integer> implements Comparable<Reagent>, AttachedFilesEntity<ReagentAttachedFileType,Integer>
{
private static final long serialVersionUID = 1;
public static final RelationshipPath<Reagent> libraryContentsVersion = RelationshipPath.from(Reagent.class).to("libraryContentsVersion", Cardinality.TO_ONE);
public static final RelationshipPath<Reagent> well = RelationshipPath.from(Reagent.class).to("well", Cardinality.TO_ONE);
public static final RelationshipPath<Reagent> annotationValues = RelationshipPath.from(Reagent.class).to("annotationValues");
public static final RelationshipPath<Reagent> studies = RelationshipPath.from(Reagent.class).to("studies");
public static final PropertyPath<Reagent> vendorName = RelationshipPath.from(Reagent.class).toProperty("vendorId.vendorName");
public static final PropertyPath<Reagent> vendorIdentifier = RelationshipPath.from(Reagent.class).toProperty("vendorId.vendorIdentifier");
public static final RelationshipPath<Reagent> publications = RelationshipPath.from(Reagent.class).to("publications");
public static final RelationshipPath<Reagent> attachedFiles = RelationshipPath.from(Reagent.class).to("attachedFiles");
private LibraryContentsVersion _libraryContentsVersion;
private Well _well;
private ReagentVendorIdentifier _vendorId;
private Map<AnnotationType,AnnotationValue> _annotationValues = new HashMap<AnnotationType,AnnotationValue>();
private Set<Screen> _studies = new HashSet<Screen>();
private Set<Publication> _publications = Sets.newHashSet();
private Set<AttachedFile> _attachedFiles = new HashSet<AttachedFile>();
private String _vendorBatchId;
private Integer _facilityBatchId;
private String _vendorNameSynonym;
/**
* @motivation for hibernate and proxy/concrete subclass constructors
*/
protected Reagent() {}
/**
* @motivation for {@link Library#createWell}
*/
protected Reagent(ReagentVendorIdentifier reagentVendorIdentifier, Well well, LibraryContentsVersion libraryContentsVersion)
{
_vendorId = reagentVendorIdentifier;
_well = well;
_libraryContentsVersion = libraryContentsVersion;
}
public int compareTo(Reagent o)
{
return _vendorId.compareTo(o._vendorId);
}
@Id
@org.hibernate.annotations.GenericGenerator(
name="reagent_id_seq",
strategy="sequence",
parameters = { @org.hibernate.annotations.Parameter(name="sequence", value="reagent_id_seq") }
)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="reagent_id_seq")
public Integer getReagentId()
{
return getEntityId();
}
private void setReagentId(Integer reagentId)
{
setEntityId(reagentId);
}
@Column
public ReagentVendorIdentifier getVendorId()
{
if (_vendorId == null) {
return ReagentVendorIdentifier.NULL_VENDOR_ID;
}
return _vendorId;
}
private void setVendorId(ReagentVendorIdentifier vendorId)
{
_vendorId = vendorId;
}
@Type(type="text")
@edu.harvard.med.screensaver.model.annotations.Column(hasNonconventionalSetterMethod = true)
public String getVendorNameSynonym()
{
return _vendorNameSynonym;
}
private void setVendorNameSynonym(String value){
_vendorNameSynonym = value;
}
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="wellId", nullable=false, updatable=false)
@org.hibernate.annotations.ForeignKey(name="fk_reagent_to_well")
@org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.PROXY)
public Well getWell()
{
return _well;
}
private void setWell(Well well)
{
_well = well;
}
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="libraryContentsVersionId", nullable=false, updatable=false)
@org.hibernate.annotations.ForeignKey(name="fk_reagent_to_library_contents_version")
@org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.PROXY)
public LibraryContentsVersion getLibraryContentsVersion()
{
return _libraryContentsVersion;
}
private void setLibraryContentsVersion(LibraryContentsVersion libraryContentsVersion)
{
_libraryContentsVersion = libraryContentsVersion;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "reagent")
@ToMany(hasNonconventionalMutation=true /* model unit tests don't handle Maps yet, tested in ReagentTest#testAnnotationValueMap */)
/* @LazyCollection(LazyCollectionOption.EXTRA) */
@MapKeyManyToMany(joinColumns = { @JoinColumn(name = "annotationTypeId") }, targetEntity = AnnotationType.class)
public Map<AnnotationType,AnnotationValue> getAnnotationValues()
{
return _annotationValues;
}
@ManyToMany(targetEntity = Screen.class, mappedBy = "reagents", fetch = FetchType.LAZY)
@ToMany(singularPropertyName = "study", hasNonconventionalMutation = true /*
* model unit tests don't handle immutable
* to-many relationships, tested in
* ReagentTest#testAnnotationValueMap
*/)
//@JoinColumn(name = "studyId", nullable = false, updatable = false)
@org.hibernate.annotations.ForeignKey(name = "fk_reagent_to_study")
@org.hibernate.annotations.LazyCollection(value = org.hibernate.annotations.LazyCollectionOption.TRUE)
public Set<Screen> getStudies()
{
return _studies;
}
public boolean addStudy(Screen study)
{
if (_studies.add(study)) {
study.addReagent(this);
return true;
}
return false;
}
public boolean removeStudy(Screen study)
{
return removeStudy(study, true);
}
public boolean removeStudy(Screen study, boolean removeReagentStudyLink)
{
if (_studies.remove(study)) {
if (removeReagentStudyLink) study.removeReagent(this);
return true;
}
return false;
}
/**
* Set the set of annotation values
*
* @param annotationValues the new set of annotation values
* @motivation for hibernate
*/
private void setAnnotationValues(Map<AnnotationType,AnnotationValue> annotationValues)
{
_annotationValues = annotationValues;
}
private void setStudies(Set<Screen> studies)
{
_studies = studies;
}
/**
* Get the publications.
*
* @return the publications
*/
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.LAZY)
@ToMany(hasNonconventionalMutation = true)
@JoinTable(name = "reagentPublicationLink", joinColumns = @JoinColumn(name = "reagentId"), inverseJoinColumns = @JoinColumn(name = "publicationId"))
@org.hibernate.annotations.ForeignKey(name = "fk_reagent_publication_link_to_reagent")
@org.hibernate.annotations.LazyCollection(value = org.hibernate.annotations.LazyCollectionOption.TRUE)
public Set<Publication> getPublications()
{
return _publications;
}
private void setPublications(Set<Publication> publications)
{
_publications = publications;
}
public boolean addPublication(Publication p)
{
return _publications.add(p);
}
/**
* Get the attached files.
*
* @return the attached files
*/
@OneToMany(mappedBy = "reagent",
cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE },
fetch = FetchType.LAZY,
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,
ReagentAttachedFileType 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. Use {@link Publication#setAttachedFile} to create an
* attached file that is associated with a Publication.
*
* @param filename the filename
* @param fileType the file type
* @param fileContents the file contents
* @throws IOException
*/
public AttachedFile createAttachedFile(String filename,
ReagentAttachedFileType 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);
}
/**
* Set the attached files.
*
* @param attachedFiles the new attached files
* @motivation for hibernate
*/
private void setAttachedFiles(Set<AttachedFile> attachedFiles)
{
_attachedFiles = attachedFiles;
}
@org.hibernate.annotations.Type(type = "text")
@edu.harvard.med.screensaver.model.annotations.Column(hasNonconventionalSetterMethod = true)
// we don't want to include this LINCS-only property in the constructor
public String getVendorBatchId()
{
return _vendorBatchId;
}
@edu.harvard.med.screensaver.model.annotations.Column(hasNonconventionalSetterMethod = true)
// we don't want to include this LINCS-only property in the constructor
public Integer getFacilityBatchId()
{
return _facilityBatchId;
}
/**
* @motivation for hibernate
*/
private void setVendorBatchId(String vendorBatchId)
{
_vendorBatchId = vendorBatchId;
}
public Reagent forVendorBatchId(String vendorBatchId)
{
validateImmutablePropertyInitialization();
_vendorBatchId = vendorBatchId;
return this;
}
/**
* @motivation for hibernate
*/
private void setFacilityBatchId(Integer facilityBatchId)
{
_facilityBatchId = facilityBatchId;
}
public Reagent forFacilityBatchId(Integer facilityBatchId)
{
validateImmutablePropertyInitialization();
_facilityBatchId = facilityBatchId;
return this;
}
}