// $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.Set;
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.OneToOne;
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 com.google.common.base.Function;
import com.google.common.collect.Sets;
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.MolarConcentration;
import edu.harvard.med.screensaver.model.Volume;
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.PropertyPath;
import edu.harvard.med.screensaver.model.meta.RelationshipPath;
import edu.harvard.med.screensaver.model.screenresults.AssayPlate;
import edu.harvard.med.screensaver.model.users.AdministratorUser;
/**
* A plate for a particular {@link Library} {@link Copy}.
*
* @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={ "copyId", "plateNumber" }) })
@org.hibernate.annotations.Proxy
@ContainedEntity(containingEntityClass = Copy.class, autoCreated = true)
public class Plate extends AuditedAbstractEntity<Integer> implements Comparable<Plate>
{
private static final long serialVersionUID = 0L;
public static final RelationshipPath<Plate> copy = RelationshipPath.from(Plate.class).to("copy", Cardinality.TO_ONE);
public static final RelationshipPath<Plate> library = RelationshipPath.from(Plate.class).to("copy", Cardinality.TO_ONE).to("library",Cardinality.TO_ONE);
public static final RelationshipPath<Plate> location = RelationshipPath.from(Plate.class).to("location", Cardinality.TO_ONE);
public static final RelationshipPath<Plate> platedActivity = RelationshipPath.from(Plate.class).to("platedActivity", Cardinality.TO_ONE);
public static final RelationshipPath<Plate> retiredActivity = RelationshipPath.from(Plate.class).to("retiredActivity", Cardinality.TO_ONE);
public static final RelationshipPath<Plate> stockPlate = PropertyPath.from(Plate.class).to("stockPlateMapping").toProperty("plate");
public static final PropertyPath<Plate> quadrant = PropertyPath.from(Plate.class).to("stockPlateMapping").toProperty("quadrant");
public static final Function<Plate,Integer> ToPlateNumber = new Function<Plate,Integer>() { public Integer apply(Plate p) { return p.getPlateNumber(); } };
public static final Function<Plate,Copy> ToCopy = new Function<Plate,Copy>() { public Copy apply(Plate p) { return p.getCopy(); } };
public static final Function<Plate,PlateLocation> ToLocation = new Function<Plate,PlateLocation>() {
public PlateLocation apply(Plate p)
{
return p.getLocation();
}
};
public static final Function<Plate,PlateStatus> ToStatus = new Function<Plate,PlateStatus>() {
public PlateStatus apply(Plate p)
{
return p.getStatus();
}
};
public static final Function<Plate,BigDecimal> ToWellConcentrationDilutionFactor = new Function<Plate,BigDecimal>() {
public BigDecimal apply(Plate p)
{
return p.getWellConcentrationDilutionFactor();
}
};
public static final Function<Plate,MolarConcentration> ToPrimaryWellMolarConcentration = new Function<Plate,MolarConcentration>() {
public MolarConcentration apply(Plate p)
{
return p.getNullSafeConcentrationStatistics().getPrimaryWellMolarConcentration();
}
};
public static final Function<Plate,BigDecimal> ToPrimaryWellMgMlConcentration = new Function<Plate,BigDecimal>() {
public BigDecimal apply(Plate p)
{
return p.getNullSafeConcentrationStatistics().getPrimaryWellMgMlConcentration();
}
};
private Integer _version;
private Copy _copy;
private Integer _plateNumber = 0;
private PlateLocation _location;
private String _facilityId;
private PlateType _plateType;
/** The default initial volume for a well on this copy plate. */
private Volume _wellVolume;
private ConcentrationStatistics _concentrationStatistics;
private PlateStatus _status;
private AdministrativeActivity _platedActivity;
private AdministrativeActivity _retiredActivity;
private String _comments;
private Set<AssayPlate> _assayPlates = Sets.newHashSet();//ImmutableSet.of(); (breaks JPA merge)
private StockPlateMapping _stockPlateMapping;
private ScreeningStatistics _screeningStatistics;
private VolumeStatistics _volumeStatistics;
/**
* Construct an initialized <code>Plate</code>. Intended only for use by {@link Copy}.
* @motivation intended only for use by {@link Copy}
*/
Plate(Copy copy,
Integer plateNumber)
{
super((AdministratorUser) copy.getCreatedBy());
_copy = copy;
_plateNumber = plateNumber;
_status = PlateStatus.NOT_SPECIFIED;
}
/**
* @motivation for hibernate and proxy/concrete subclass constructors
*/
protected Plate() {}
@Override
public Object acceptVisitor(AbstractEntityVisitor visitor)
{
return visitor.visit(this);
}
/**
* Get the id for the copy info.
* @return the id for the copy info
*/
@Id
@org.hibernate.annotations.GenericGenerator(
name="plate_id_seq",
strategy="sequence",
parameters = { @Parameter(name="sequence", value="plate_id_seq") }
)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="plate_id_seq")
public Integer getPlateId()
{
return getEntityId();
}
/**
* Get the copy.
* @return the copy
*/
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="copyId", nullable=false, updatable=false)
//@org.hibernate.annotations.Immutable
@org.hibernate.annotations.ForeignKey(name="fk_plate_to_copy")
@org.hibernate.annotations.LazyToOne(value=org.hibernate.annotations.LazyToOneOption.PROXY)
public Copy getCopy()
{
return _copy;
}
private void setCopy(Copy copy)
{
_copy = copy;
}
@OneToMany(mappedBy = "plateScreened", cascade = {})
@MapKey(name = "plateNumber")
@ToMany(hasNonconventionalMutation = true /* we don't update this relationship in memory */)
public Set<AssayPlate> getAssayPlates()
{
return _assayPlates;
}
private void setAssayPlates(Set<AssayPlate> assayPlates)
{
_assayPlates = assayPlates;
}
/**
* Get the plate number.
* @return the plate number
*/
@Column(nullable = false, updatable = false)
public int getPlateNumber()
{
return _plateNumber;
}
private void setPlateNumber(int plateNumber)
{
_plateNumber = plateNumber;
}
/**
* Get the location.
* @return the location
*/
@ManyToOne(cascade = { CascadeType.MERGE })
@JoinColumn(name = "plateLocationId")
@org.hibernate.annotations.ForeignKey(name = "fk_plate_to_plate_location")
@org.hibernate.annotations.Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
@edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional = true)
public PlateLocation getLocation()
{
return _location;
}
/**
* Set the location.
* @param location the new location
*/
public void setLocation(PlateLocation location)
{
_location = location;
}
public String getFacilityId()
{
return _facilityId;
}
public void setFacilityId(String facilityId)
{
_facilityId = facilityId;
}
/**
* Get the plate type.
* @return the plate type
*/
@org.hibernate.annotations.Type(
type="edu.harvard.med.screensaver.model.libraries.PlateType$UserType"
)
public PlateType getPlateType()
{
return _plateType;
}
/**
* Set the plate type.
* @param plateType the new plate type
*/
public void setPlateType(PlateType plateType)
{
_plateType = plateType;
}
/**
* Get the default volume for wells on this copy plate.
* @return the volume
*/
@Column(precision = ScreensaverConstants.VOLUME_PRECISION, scale = ScreensaverConstants.VOLUME_SCALE)
@org.hibernate.annotations.Type(type="edu.harvard.med.screensaver.db.usertypes.VolumeType")
public Volume getWellVolume()
{
return _wellVolume;
}
public void setWellVolume(Volume volume)
{
_wellVolume = volume;
}
public Plate withWellVolume(Volume volume)
{
setWellVolume(volume);
return this;
}
@Column(nullable = false)
@org.hibernate.annotations.Type(type = "edu.harvard.med.screensaver.model.libraries.PlateStatus$UserType")
public PlateStatus getStatus()
{
return _status;
}
public void setStatus(PlateStatus status)
{
_status = status;
}
public Plate withStatus(PlateStatus status)
{
setStatus(status);
return this;
}
/**
* @motivation for hibernate
*/
private void setPlateId(Integer plateId)
{
setEntityId(plateId);
}
/**
* @motivation for hibernate
*/
@Version
@Column(nullable=false)
private Integer getVersion() {
return _version;
}
private void setVersion(Integer version)
{
_version = version;
}
@Transient
public PlateSize getPlateSize()
{
return _copy.getLibrary().getPlateSize();
}
@Override
public int compareTo(Plate other)
{
int result = this.getPlateNumber() > other.getPlateNumber() ? 1 : this.getPlateNumber() < other.getPlateNumber() ? -1 : 0;
if (result == 0) {
result = this.getCopy().compareTo(other.getCopy());
}
return result;
}
public Plate withLocation(PlateLocation location)
{
setLocation(location);
return this;
}
public Plate withPlateType(PlateType plateType)
{
setPlateType(plateType);
return this;
}
@Column
public StockPlateMapping getStockPlateMapping()
{
return _stockPlateMapping;
}
public void setStockPlateMapping(StockPlateMapping stockPlateMapping)
{
_stockPlateMapping = stockPlateMapping;
}
@Transient
public ScreeningStatistics getScreeningStatistics()
{
return _screeningStatistics;
}
public void setScreeningStatistics(ScreeningStatistics screeningStatistics)
{
_screeningStatistics = screeningStatistics;
}
@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
@JoinTable(name = "plateUpdateActivity",
joinColumns = @JoinColumn(name = "plateId", 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)
// hasNonconventionalMutation: model testing framework doesn't understand this is a containment relationship, and so requires addUpdateActivity() method
@ToMany(singularPropertyName = "updateActivity", hasNonconventionalMutation = true)
@Override
public SortedSet<AdministrativeActivity> getUpdateActivities()
{
return _updateActivities;
}
@OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
@JoinColumn(name = "plated_activity_id", nullable = true, updatable = true, unique = true)
@edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional = true,
hasNonconventionalSetterMethod = true)
public AdministrativeActivity getPlatedActivity()
{
return _platedActivity;
}
/**
* Get the activity associated with changing the plate status from {@link PlateStatus#AVAILABLE Available} to any
* non-available status ({@link PlateStatus#RETIRED Retired} or greater.
*/
@OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
@JoinColumn(name = "retired_activity_id", nullable = true, updatable = true, unique = true)
@edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional = true,
hasNonconventionalSetterMethod = true)
public AdministrativeActivity getRetiredActivity()
{
return _retiredActivity;
}
public void setRetiredActivity(AdministrativeActivity retiredActivity)
{
_retiredActivity = retiredActivity;
}
public void setPlatedActivity(AdministrativeActivity platedActivity)
{
_platedActivity = platedActivity;
}
@Transient
public VolumeStatistics getVolumeStatistics()
{
return _volumeStatistics;
}
public void setVolumeStatistics(VolumeStatistics volumeStatistics)
{
_volumeStatistics = volumeStatistics;
}
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();
}
@Transient
public BigDecimal getWellConcentrationDilutionFactor()
{
return getCopy().getWellConcentrationDilutionFactor();
}
}