package edu.ualberta.med.biobank.model;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Embedded;
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.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.Type;
import org.hibernate.validator.constraints.NotEmpty;
import edu.ualberta.med.biobank.common.util.RowColPos;
import edu.ualberta.med.biobank.validator.constraint.Empty;
import edu.ualberta.med.biobank.validator.constraint.NotUsed;
import edu.ualberta.med.biobank.validator.constraint.Unique;
import edu.ualberta.med.biobank.validator.constraint.model.ValidContainerType;
import edu.ualberta.med.biobank.validator.group.PreDelete;
import edu.ualberta.med.biobank.validator.group.PrePersist;
/**
* Describes a container configuration which may hold other child containers or
* specimens. Container types are used to create a representation of a physical
* container
*
* ET: Describes various containers that can hold specimens, these container
* types are used to build a container.
*
*/
@Entity
@Table(name = "CONTAINER_TYPE",
uniqueConstraints = {
@UniqueConstraint(columnNames = { "SITE_ID", "NAME" }),
@UniqueConstraint(columnNames = { "SITE_ID", "NAME_SHORT" }) })
@Unique.List({
@Unique(properties = { "site", "name" }, groups = PrePersist.class),
@Unique(properties = { "site", "nameShort" }, groups = PrePersist.class)
})
@NotUsed.List({
@NotUsed(by = Container.class, property = "containerType", groups = PreDelete.class),
@NotUsed(by = SpecimenPosition.class, property = "container.containerType", groups = PreDelete.class)
})
@Empty.List({
@Empty(property = "childContainerTypes", groups = PreDelete.class),
@Empty(property = "parentContainerTypes", groups = PreDelete.class),
@Empty(property = "specimenTypes", groups = PreDelete.class)
})
@ValidContainerType(groups = PrePersist.class)
public class ContainerType extends AbstractBiobankModel {
private static final long serialVersionUID = 1L;
private String name;
private String nameShort;
private boolean topLevel = false;
private Double defaultTemperature;
private Set<SpecimenType> specimenTypes = new HashSet<SpecimenType>(0);
private Set<ContainerType> childContainerTypes =
new HashSet<ContainerType>(0);
private ActivityStatus activityStatus = ActivityStatus.ACTIVE;
private Set<Comment> comments = new HashSet<Comment>(0);
private Capacity capacity = new Capacity();
private Site site;
private ContainerLabelingScheme childLabelingScheme;
private Set<ContainerType> parentContainerTypes =
new HashSet<ContainerType>(0);
@NotEmpty(message = "{edu.ualberta.med.biobank.model.ContainerType.name.NotEmpty}")
@Column(name = "NAME")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@NotEmpty(message = "{edu.ualberta.med.biobank.model.ContainerType.name.NotEmpty}")
@Column(name = "NAME_SHORT")
public String getNameShort() {
return this.nameShort;
}
public void setNameShort(String nameShort) {
this.nameShort = nameShort;
}
@Column(name = "TOP_LEVEL")
// TODO: rename to isTopLevel
public boolean getTopLevel() {
return this.topLevel;
}
public void setTopLevel(boolean topLevel) {
this.topLevel = topLevel;
}
// TODO: change to decimal
@Column(name = "DEFAULT_TEMPERATURE")
public Double getDefaultTemperature() {
return this.defaultTemperature;
}
public void setDefaultTemperature(Double defaultTemperature) {
this.defaultTemperature = defaultTemperature;
}
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "CONTAINER_TYPE_SPECIMEN_TYPE",
joinColumns = { @JoinColumn(name = "CONTAINER_TYPE_ID", nullable = false, updatable = false) },
inverseJoinColumns = { @JoinColumn(name = "SPECIMEN_TYPE_ID", nullable = false, updatable = false) })
public Set<SpecimenType> getSpecimenTypes() {
return this.specimenTypes;
}
public void setSpecimenTypes(Set<SpecimenType> specimenTypes) {
this.specimenTypes = specimenTypes;
}
/**
* The custom @SQLInsert allows a `SITE_ID` to be inserted into the
* correlation table so a foreign key can be created to ensure that
* {@link ContainerType}-s with the same {@link Site} can be related.
*
* @return
*/
@SQLInsert(sql = "INSERT INTO `CONTAINER_TYPE_CONTAINER_TYPE` (PARENT_CONTAINER_TYPE_ID, CHILD_CONTAINER_TYPE_ID, SITE_ID) SELECT ?, ID, SITE_ID FROM `CONTAINER_TYPE` WHERE ID = ?")
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "CONTAINER_TYPE_CONTAINER_TYPE",
joinColumns = { @JoinColumn(name = "PARENT_CONTAINER_TYPE_ID", nullable = false, updatable = false) },
inverseJoinColumns = { @JoinColumn(name = "CHILD_CONTAINER_TYPE_ID", nullable = false, updatable = false) })
@ForeignKey(name = "FK_ContainerType_childContainerTypes", inverseName = "FK_ContainerType_parentContainerTypes")
public Set<ContainerType> getChildContainerTypes() {
return this.childContainerTypes;
}
public void setChildContainerTypes(Set<ContainerType> childContainerTypes) {
this.childContainerTypes = childContainerTypes;
}
@NotNull(message = "{edu.ualberta.med.biobank.model.ContainerType.activityStatus.NotNull}")
@Column(name = "ACTIVITY_STATUS_ID", nullable = false)
@Type(type = "activityStatus")
public ActivityStatus getActivityStatus() {
return this.activityStatus;
}
public void setActivityStatus(ActivityStatus activityStatus) {
this.activityStatus = activityStatus;
}
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "CONTAINER_TYPE_COMMENT",
joinColumns = { @JoinColumn(name = "CONTAINER_TYPE_ID", nullable = false, updatable = false) },
inverseJoinColumns = { @JoinColumn(name = "COMMENT_ID", unique = true, nullable = false, updatable = false) })
public Set<Comment> getComments() {
return this.comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
@NotNull(message = "{edu.ualberta.med.biobank.model.ContainerType.capacity.NotNull}")
@Embedded
public Capacity getCapacity() {
return this.capacity;
}
public void setCapacity(Capacity capacity) {
this.capacity = capacity;
}
@NotNull(message = "{edu.ualberta.med.biobank.model.ContainerType.site.NotNull}")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SITE_ID", nullable = false)
public Site getSite() {
return this.site;
}
public void setSite(Site site) {
this.site = site;
}
@NotNull(message = "{edu.ualberta.med.biobank.model.ContainerType.childLabelingScheme.NotNull}")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CHILD_LABELING_SCHEME_ID", nullable = false)
public ContainerLabelingScheme getChildLabelingScheme() {
return this.childLabelingScheme;
}
public void setChildLabelingScheme(
ContainerLabelingScheme childLabelingScheme) {
this.childLabelingScheme = childLabelingScheme;
}
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "childContainerTypes")
public Set<ContainerType> getParentContainerTypes() {
return this.parentContainerTypes;
}
public void setParentContainerTypes(Set<ContainerType> parentContainerTypes) {
this.parentContainerTypes = parentContainerTypes;
}
@Transient
public Integer getRowCapacity() {
return this.getCapacity().getRowCapacity();
}
@Transient
public Integer getColCapacity() {
return this.getCapacity().getColCapacity();
}
@Transient
public String getPositionString(RowColPos position) {
return ContainerLabelingScheme.getPositionString(position,
getChildLabelingScheme().getId(), getRowCapacity(),
getColCapacity());
}
@Transient
public RowColPos getRowColFromPositionString(String position)
throws Exception {
return getChildLabelingScheme()
.getRowColFromPositionString(position, getRowCapacity(),
getColCapacity());
}
@Transient
public boolean isPallet96() {
return RowColPos.PALLET_96_ROW_MAX.equals(getRowCapacity())
&& RowColPos.PALLET_96_COL_MAX.equals(getColCapacity());
}
}