// $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.math.BigDecimal; import java.util.Map; import java.util.SortedSet; 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.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.MapKey; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import javax.persistence.Version; import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Sort; import org.hibernate.annotations.SortType; import org.hibernate.annotations.Type; import org.joda.time.LocalDate; import com.google.common.base.Function; import com.google.common.collect.Maps; import edu.harvard.med.screensaver.ScreensaverConstants; import edu.harvard.med.screensaver.model.AbstractEntityVisitor; import edu.harvard.med.screensaver.model.AuditedAbstractEntity; 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.activities.AdministrativeActivity; import edu.harvard.med.screensaver.model.annotations.ContainedEntity; import edu.harvard.med.screensaver.model.annotations.Derived; import edu.harvard.med.screensaver.model.annotations.ToMany; import edu.harvard.med.screensaver.model.meta.Cardinality; import edu.harvard.med.screensaver.model.meta.RelationshipPath; import edu.harvard.med.screensaver.model.users.AdministratorUser; /** * A set of plates representing a copy of a {@link Library}'s contents. A facility creates and uses * multiple copies of a library, rather than working with a single master copy, in * order to reduce reagent freeze/thaw cycles, minimize the impact of loss due to a * physical loss, etc. Note that in the Screensaver domain model, a library Copy represents * the physical instances of library plates that exist in reality. A copy is a physical manifestation * of a {@link Library}, which only specifies the layout of reagents across a * set plates. Therefore, even if a facility decided to work with a single, master set of library * plates, in Screensaver one would still have to define a single Copy for this master set of plates. * * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a> * @author <a mailto="john_sullivan@hms.harvard.edu">John Sullivan</a> */ @Entity @Table(uniqueConstraints={ @UniqueConstraint(columnNames={ "libraryId", "name" }) }) @org.hibernate.annotations.Proxy @ContainedEntity(containingEntityClass=Library.class) public class Copy extends AuditedAbstractEntity<Integer> implements Comparable<Copy> { private static final long serialVersionUID = 0L; public static final RelationshipPath<Copy> plates = RelationshipPath.from(Copy.class).to("plates"); public static final RelationshipPath<Copy> library = RelationshipPath.from(Copy.class).to("library", Cardinality.TO_ONE); public static final Function<Copy,String> ToName = new Function<Copy,String>() { public String apply(Copy c) { return c.getName(); } }; public static final Function<Copy,Library> ToLibrary = new Function<Copy,Library>() { public Library apply(Copy c) { return c.getLibrary(); } }; private Integer _version; private Library _library; private String _name; private CopyUsageType _usageType; private String _comments; private LocalDate _datePlated; private Map<Integer,Plate> _plates = Maps.newHashMap(); private PlateLocation _primaryPlateLocation; private PlateStatus _primaryPlateStatus; private ScreeningStatistics _screeningStatistics; private VolumeStatistics _volumeStatistics; private Integer _platesAvailable; private Integer _plateLocationsCount; private ConcentrationStatistics _concentrationStatistics; private BigDecimal _wellConcentrationDilutionFactor; @Override public Object acceptVisitor(AbstractEntityVisitor visitor) { return visitor.visit(this); } public int compareTo(Copy other) { int result = getLibrary().getLibraryName().compareTo(other.getLibrary().getLibraryName()); if (result == 0) { result = getName().compareTo(other.getName()); } return result; } /** * Get the id for the copy. * @return the id for the copy */ @Id @org.hibernate.annotations.GenericGenerator(name = "copy_id_seq", strategy = "sequence", parameters = { @Parameter(name = "sequence", value = "copy_id_seq") }) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "copy_id_seq") public Integer getCopyId() { return getEntityId(); } private void setCopyId(Integer copyId) { setEntityId(copyId); } /** * Get the library. * @return the library */ @ManyToOne @JoinColumn(name="libraryId", nullable=false, updatable=false) @org.hibernate.annotations.ForeignKey(name="fk_copy_to_library") @org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.PROXY) @edu.harvard.med.screensaver.model.annotations.ToOne(inverseProperty="copies") public Library getLibrary() { return _library; } /** * @motivation for hibernate */ private void setLibrary(Library library) { _library = library; } @OneToMany( mappedBy="copy", cascade={ CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, fetch=FetchType.LAZY ) @MapKey(name="plateNumber") @ToMany(hasNonconventionalMutation=true /*Maps not yet supported by automated model testing framework*/) @org.hibernate.annotations.Cascade(value={org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE}) public Map<Integer,Plate> getPlates() { return _plates; } private void setPlates(Map<Integer,Plate> plates) { _plates = plates; } private Plate createPlate(Integer plateNumber) { if (getLibrary().getStartPlate() > plateNumber || getLibrary().getEndPlate() < plateNumber) { throw new DataModelViolationException("plate number " + plateNumber + " is outside of library plate range (" + getLibrary().getStartPlate() + ".." + getLibrary().getEndPlate() + ")"); } Plate plate = new Plate(this, plateNumber); if (_plates.containsKey(plateNumber)) { throw new DuplicateEntityException(this, plate); } _plates.put(plateNumber, plate); return plate; } public Plate findPlate(int plateNumber) { return _plates.get(plateNumber); } @Column(nullable = false) @org.hibernate.annotations.Type(type="text") public String getName() { return _name; } public void setName(String name) { _name = name; } @Column(nullable = false) @org.hibernate.annotations.Type( type="edu.harvard.med.screensaver.model.libraries.CopyUsageType$UserType" ) public CopyUsageType getUsageType() { return _usageType; } public void setUsageType(CopyUsageType copyUsageType) { _usageType = copyUsageType; } public String getComments() { return _comments; } public void setComments(String comments) { _comments = comments; } /** * @motivation intended for use by {@link Library#createCopy} only. */ Copy(AdministratorUser createdBy, Library library, CopyUsageType usageType, String name) { this(createdBy, library); _library = library; _name = name; _usageType = usageType; } /** * @motivation UI DTO, for creating a new Copy */ public Copy(AdministratorUser createdBy, Library library) { super(createdBy); _library = library; for (int p = library.getStartPlate(); p <= library.getEndPlate(); ++p) { createPlate(p); } _primaryPlateStatus = PlateStatus.NOT_SPECIFIED; // _concentrationStatistics = new ConcentrationStatistics(); } /** * @motivation for hibernate and proxy/concrete subclass constructors */ protected Copy() {} /** * Get the version for the copy. * @return the version for the copy * @motivation for hibernate */ @Version @Column(nullable=false) private Integer getVersion() { return _version; } /** * @motivation for hibernate */ private void setVersion(Integer version) { _version = version; } @Transient public ScreeningStatistics getScreeningStatistics() { if (_screeningStatistics == null) { _screeningStatistics = new ScreeningStatistics(); } return _screeningStatistics; } public void setScreeningStatistics(ScreeningStatistics screeningStatistics) { _screeningStatistics = screeningStatistics; } @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE }) @JoinTable(name = "copyUpdateActivity", joinColumns = @JoinColumn(name = "copyId", 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; } @Column @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType") @Derived public LocalDate getDatePlated() { return _datePlated; } public void setDatePlated(LocalDate datePlated) { _datePlated = datePlated; } @ManyToOne(cascade = { CascadeType.MERGE }) @JoinColumn(name = "primaryPlateLocationId") @org.hibernate.annotations.ForeignKey(name = "fk_copy_to_primary_plate_location") @org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE }) @edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional = true) @Derived public PlateLocation getPrimaryPlateLocation() { return _primaryPlateLocation; } public void setPrimaryPlateLocation(PlateLocation primaryPlateLocation) { _primaryPlateLocation = primaryPlateLocation; } @Column(nullable = false) @org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.libraries.PlateStatus$UserType") @Derived public PlateStatus getPrimaryPlateStatus() { return _primaryPlateStatus; } public void setPrimaryPlateStatus(PlateStatus primaryPlateStatus) { _primaryPlateStatus = primaryPlateStatus; } @Transient public VolumeStatistics getVolumeStatistics() { return _volumeStatistics; } public void setVolumeStatistics(VolumeStatistics volumeStatistics) { _volumeStatistics = volumeStatistics; } @Column(nullable = true) public void setPlatesAvailable(Integer integer) { _platesAvailable = integer; } public Integer getPlatesAvailable() { return _platesAvailable; } @Column(nullable = true) public void setPlateLocationsCount(Integer size) { _plateLocationsCount = size; } public Integer getPlateLocationsCount() { return _plateLocationsCount; } public void setConcentrationStatistics(ConcentrationStatistics _concentrationStatistics) { this._concentrationStatistics = _concentrationStatistics; } @Column @edu.harvard.med.screensaver.model.annotations.Column(hasNonconventionalSetterMethod=true) public ConcentrationStatistics getConcentrationStatistics() { return _concentrationStatistics; } @Transient public ConcentrationStatistics getNullSafeConcentrationStatistics() { ConcentrationStatistics concentrationStatistics = getConcentrationStatistics(); if (concentrationStatistics == null) { return ConcentrationStatistics.NULL; } return concentrationStatistics; } @Transient public MolarConcentration getMinMolarConcentration() { return getNullSafeConcentrationStatistics().getMinMolarConcentration(); } @Transient public MolarConcentration getMaxMolarConcentration() { return getNullSafeConcentrationStatistics().getMaxMolarConcentration(); } @Transient public BigDecimal getMinMgMlConcentration() { return getNullSafeConcentrationStatistics().getMinMgMlConcentration(); } @Transient public BigDecimal getMaxMgMlConcentration() { return getNullSafeConcentrationStatistics().getMaxMgMlConcentration(); } @Transient public BigDecimal getPrimaryWellMgMlConcentration() { return getNullSafeConcentrationStatistics().getPrimaryWellMgMlConcentration(); } @Transient public MolarConcentration getPrimaryWellMolarConcentration() { return getNullSafeConcentrationStatistics().getPrimaryWellMolarConcentration(); } public void setWellConcentrationDilutionFactor(BigDecimal _plateDilutionFactor) { this._wellConcentrationDilutionFactor = _plateDilutionFactor; } @Column(precision = ScreensaverConstants.PLATE_DILUTION_FACTOR_PRECISION, scale = ScreensaverConstants.PLATE_DILUTION_FACTOR_SCALE ) public BigDecimal getWellConcentrationDilutionFactor() { if(_wellConcentrationDilutionFactor == null ) return BigDecimal.ONE; // Todo: this should not happen return _wellConcentrationDilutionFactor; } }