// $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.util.ArrayList; import java.util.List; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Transient; import com.google.common.base.Function; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.IndexColumn; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; import edu.harvard.med.screensaver.model.AbstractEntityVisitor; import edu.harvard.med.screensaver.model.annotations.ContainedEntity; 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.RelationshipPath; /** * Silencing reagent of an RNAi library. * * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a> * @author <a mailto="john_sullivan@hms.harvard.edu">John Sullivan</a> */ @Entity @Immutable @ContainedEntity(containingEntityClass=Well.class) public class SilencingReagent extends Reagent { private static final long serialVersionUID = 0L; public static final RelationshipPath<SilencingReagent> vendorGenes = RelationshipPath.from(SilencingReagent.class).to("vendorGenes", Cardinality.TO_MANY); public static final RelationshipPath<SilencingReagent> facilityGenes = RelationshipPath.from(SilencingReagent.class).to("facilityGenes", Cardinality.TO_MANY); public static final RelationshipPath<SilencingReagent> duplexWells = RelationshipPath.from(SilencingReagent.class).to("duplexWells"); private static final Function<Well,SilencingReagent> wellToReagentTransformer = new Function<Well, SilencingReagent>() { public SilencingReagent apply(Well well) { return well.<SilencingReagent>getLatestReleasedReagent(); } }; private SilencingReagentType _silencingReagentType; private String _sequence; private String _antisenseSequence; private List<Gene> _vendorGenes; private List<Gene> _facilityGenes; private Set<Well> _duplexWells = Sets.newHashSet(); private boolean _isRestrictedSequence; /** * Construct an uninitialized <code>SilencingReagent</code>. * @motivation for hibernate and proxy/concrete subclass constructors */ protected SilencingReagent() {} /** * Construct a <code>SilencingReagent</code>. * * @motivation for use of {@link Well#createSilencingReagent} methods only */ SilencingReagent(ReagentVendorIdentifier rvi, Well well, LibraryContentsVersion libraryContentsVersion, SilencingReagentType silencingReagentType, String sequence, String antiSequence) { super(rvi, well, libraryContentsVersion); _silencingReagentType = silencingReagentType; _sequence = sequence; _antisenseSequence = antiSequence; _vendorGenes = new ArrayList<Gene>(); _facilityGenes = new ArrayList<Gene>(); } @Override public Object acceptVisitor(AbstractEntityVisitor visitor) { return visitor.visit(this); } @Column(nullable=true) @org.hibernate.annotations.Type(type="edu.harvard.med.screensaver.model.libraries.SilencingReagentType$UserType") public SilencingReagentType getSilencingReagentType() { return _silencingReagentType; } private void setSilencingReagentType(SilencingReagentType silencingReagentType) { _silencingReagentType = silencingReagentType; } /** * The genetic sequence of the silencing reagent. For pool wells, this may be * null (or empty), or can be a delimited list of the sequences of the * constituent duplexes. If left null/empty, it is still possible to find the * duplex sequences via {@link #getDuplexWells}.{@link Well#getLatestReleasedReagent}. */ @Column(nullable=true) @org.hibernate.annotations.Type(type="text") public String getSequence() { return _sequence; } private void setSequence(String sequence) { _sequence = sequence; } /** * The genetic anti-sense sequence of the silencing reagent. For pool wells, this may be * null (or empty), or can be a delimited list of the sequences of the * constituent duplexes. If left null/empty, it is still possible to find the * duplex sequences via {@link #getDuplexWells}.{@link Well#getLatestReleasedReagent}. */ @Column(nullable=true) @org.hibernate.annotations.Type(type="text") @edu.harvard.med.screensaver.model.annotations.Column(hasNonconventionalSetterMethod = true) public String getAntiSenseSequence() { return _antisenseSequence; } private void setAntiSenseSequence(String sequence) { _antisenseSequence = sequence; } @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.LAZY) @JoinTable(name = "reagentVendorGenes", joinColumns = @JoinColumn(name = "reagentId"), inverseJoinColumns = @JoinColumn(name = "geneId")) @IndexColumn(name = "ordinal") @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE }) @org.hibernate.annotations.ForeignKey(name = "fk_vendor_genes_to_reagent") @ToMany(hasNonconventionalMutation = true) public List<Gene> getVendorGenes() { return _vendorGenes; } private void setVendorGenes(List<Gene> vendorGenes) { _vendorGenes = vendorGenes; } @Transient public Gene getVendorGene() { if(_vendorGenes == null) { _vendorGenes = new ArrayList<Gene>(); } if(_vendorGenes.size() == 0 && getEntityId() == null) { _vendorGenes.add(new Gene()); } return _vendorGenes.size() == 0 ? null : _vendorGenes.get(0); } /** * Optional gene information provided by the screening facility, which can * differ from the vendor-provided gene information ({@link #getVendorGene()}). * In particular if updated gene information is available (names, symbols, * etc.), this can be reflected here, without affecting the original gene * information provided by the vendor. Also, if it is determined that this * silencing reagent in fact targets a different gene than expected, the * facility gene information can be used reflect this fact. */ @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.LAZY) @JoinTable(name = "reagentFacilityGenes", joinColumns = @JoinColumn(name = "reagentId"), inverseJoinColumns = @JoinColumn(name = "geneId")) @IndexColumn(name = "ordinal") @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE }) @org.hibernate.annotations.ForeignKey(name = "fk_facility_genes_to_reagent") @ToMany(hasNonconventionalMutation = true) public List<Gene> getFacilityGenes() { return _facilityGenes; } private void setFacilityGenes(List<Gene> facilityGenes) { _facilityGenes = facilityGenes; } @Transient public Gene getFacilityGene() { if(_facilityGenes == null) { _facilityGenes = new ArrayList<Gene>(); } if(_facilityGenes.size() == 0 && getEntityId() == null) { _facilityGenes.add(new Gene()); } return _facilityGenes.size() == 0 ? null : _facilityGenes.get(0); } @ManyToMany(cascade={}, fetch=FetchType.LAZY) @JoinTable( joinColumns=@JoinColumn(name="silencing_reagent_id"), inverseJoinColumns=@JoinColumn(name="wellId") ) @Cascade({}) @LazyCollection(LazyCollectionOption.TRUE) @ToMany(unidirectional=true, hasNonconventionalMutation=true) public Set<Well> getDuplexWells() { return _duplexWells; } private void setDuplexWells(Set<Well> duplexWells) { _duplexWells = duplexWells; } /** * Builder method when creating a new SilencingReagent, prior to being * persisted. Note: it is up to the client code to validate that the duplex * well targets the same gene as the pool (e.g., entrezgene IDs match, or * whatever "similarity" criteria is deemed appropriate for determining that * targeted gene is the same); the model allows for real-world errors, where a * pool well is erroneously contains silencing reagents that target different * genes. */ public SilencingReagent withDuplexWell(Well duplexWell) { validateImmutablePropertyInitialization(); _duplexWells.add(duplexWell); return this; } @Transient public Set<SilencingReagent> getDuplexSilencingReagents() { Iterable<SilencingReagent> reagents = Iterables.transform(getDuplexWells(), wellToReagentTransformer); reagents = Iterables.filter(reagents, Predicates.notNull()); return ImmutableSet.copyOf(reagents); } @Column(name = "is_restricted_sequence", nullable = false) public boolean isRestrictedSequence() { return _isRestrictedSequence; } private void setRestrictedSequence(boolean isRestrictedSequence) { _isRestrictedSequence = isRestrictedSequence; } public SilencingReagent withRestrictedSequence(boolean isRestrictedSequence) { validateImmutablePropertyInitialization(); setRestrictedSequence(isRestrictedSequence); return this; } }