package edu.harvard.iq.dataverse;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.persistence.*;
/**
* Base of the object hierarchy for "anything that can be inside a dataverse".
*
* @author michael
*/
@NamedQueries({
@NamedQuery(name = "DvObject.findAll",
query = "SELECT o FROM DvObject o ORDER BY o.id"),
@NamedQuery(name = "DvObject.findById",
query = "SELECT o FROM DvObject o WHERE o.id=:id"),
@NamedQuery(name = "DvObject.ownedObjectsById",
query="SELECT COUNT(obj) FROM DvObject obj WHERE obj.owner.id=:id")
})
@Entity
// Inheritance strategy "JOINED" will create 4 db tables -
// the top-level dvobject, with the common columns, and the 3 child classes -
// dataverse, dataset and datafile. The ids from the main table will be reused
// in the child tables. (i.e., the id sequences will be "sparse" in the 3
// child tables). Tested, appears to be working properly. -- L.A. Nov. 4 2014
@Inheritance(strategy=InheritanceType.JOINED)
@Table(indexes = {@Index(columnList="dtype")
, @Index(columnList="owner_id")
, @Index(columnList="creator_id")
, @Index(columnList="releaseuser_id")})
public abstract class DvObject extends DataverseEntity implements java.io.Serializable {
public static final String DATAVERSE_DTYPE_STRING = "Dataverse";
public static final String DATASET_DTYPE_STRING = "Dataset";
public static final String DATAFILE_DTYPE_STRING = "DataFile";
public static final List<String> DTYPE_LIST = Arrays.asList(DATAVERSE_DTYPE_STRING, DATASET_DTYPE_STRING, DATAFILE_DTYPE_STRING);
public static final Visitor<String> NamePrinter = new Visitor<String>(){
@Override
public String visit(Dataverse dv) {
return dv.getName();
}
@Override
public String visit(Dataset ds) {
return ds.getLatestVersion().getTitle();
}
@Override
public String visit(DataFile df) {
return df.getFileMetadata().getLabel();
}
};
public static final Visitor<String> NameIdPrinter = new Visitor<String>(){
@Override
public String visit(Dataverse dv) {
return "[" + dv.getId() + " " + dv.getName() + "]";
}
@Override
public String visit(Dataset ds) {
return "[" + ds.getId() + (ds.getLatestVersion() != null ? " " + ds.getLatestVersion().getTitle() : "") + "]";
}
@Override
public String visit(DataFile df) {
return "[" + df.getId() + (df.getFileMetadata() != null ? " " + df.getFileMetadata().getLabel() : "") + "]";
}
};
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private DvObject owner;
private Timestamp publicationDate;
/** The user that released this dataverse */
@ManyToOne
private AuthenticatedUser releaseUser;
@Column( nullable = false )
private Timestamp createDate;
@Column(nullable = false)
private Timestamp modificationTime;
/**
* @todo Rename this to contentIndexTime (or something) to differentiate it
* from permissionIndexTime. Content Solr docs vs. permission Solr docs.
*/
private Timestamp indexTime;
/**
* @todo Make this nullable=true. Currently we can't because the
* CreateDataverseCommand saves the dataverse before it assigns a role.
*/
@Column(nullable = true)
private Timestamp permissionModificationTime;
private Timestamp permissionIndexTime;
private boolean previewImageAvailable;
public boolean isPreviewImageAvailable() {
return previewImageAvailable;
}
public void setPreviewImageAvailable(boolean status) {
this.previewImageAvailable = status;
}
public Timestamp getModificationTime() {
return modificationTime;
}
/**
* modificationTime is used for comparison with indexTime so we know if the
* Solr index is stale.
* @param modificationTime
*/
public void setModificationTime(Timestamp modificationTime) {
this.modificationTime = modificationTime;
}
public Timestamp getIndexTime() {
return indexTime;
}
/**
* indexTime is used for comparison with modificationTime so we know if the
* Solr index is stale.
* @param indexTime
*/
public void setIndexTime(Timestamp indexTime) {
this.indexTime = indexTime;
}
@ManyToOne
private AuthenticatedUser creator;
public interface Visitor<T> {
public T visit(Dataverse dv);
public T visit(Dataset ds);
public T visit(DataFile df);
}
/**
* Sets the owner of the object. This is {@code protected} rather than
* {@code public}, since different sub-classes have different possible owner
* types: a {@link DataFile} can only have a {@link Dataset}, for example.
*
* @param newOwner
*/
protected void setOwner(DvObjectContainer newOwner) {
owner = newOwner;
}
public DvObjectContainer getOwner() {
return (DvObjectContainer)owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/**
* @return Whether {@code this} takes no permissions from roles assigned on its parents.
*/
public abstract boolean isEffectivelyPermissionRoot();
public Timestamp getPublicationDate() {
return publicationDate;
}
public void setPublicationDate(Timestamp publicationDate) {
this.publicationDate = publicationDate;
}
public AuthenticatedUser getReleaseUser() {
return releaseUser;
}
public void setReleaseUser(AuthenticatedUser releaseUser) {
this.releaseUser = releaseUser;
}
public boolean isReleased() {
return publicationDate != null;
}
public Timestamp getCreateDate() {
return createDate;
}
public void setCreateDate(Timestamp createDate) {
this.createDate = createDate;
}
public AuthenticatedUser getCreator() {
return creator;
}
public void setCreator(AuthenticatedUser creator) {
this.creator = creator;
}
public abstract <T> T accept(Visitor<T> v);
@Override
public int hashCode() {
return Objects.hash(getId());
}
@Override
public abstract boolean equals(Object o);
@Override
public String toString() {
String classNameComps[] = getClass().getName().split("\\.");
return String.format("[%s id:%d %s]", classNameComps[classNameComps.length - 1],
getId(), toStringExtras());
}
/**
* Convenience method to add data to the default toString output.
*
* @return
*/
protected String toStringExtras() {
return "";
}
public abstract String getDisplayName();
// helper method used to mimic instanceof on JSF pge
public boolean isInstanceofDataverse() {
return this instanceof Dataverse;
}
public boolean isInstanceofDataset() {
return this instanceof Dataset;
}
public boolean isInstanceofDataFile() {
return this instanceof DataFile;
}
public Timestamp getPermissionModificationTime() {
return permissionModificationTime;
}
public void setPermissionModificationTime(Timestamp permissionModificationTime) {
this.permissionModificationTime = permissionModificationTime;
}
public Timestamp getPermissionIndexTime() {
return permissionIndexTime;
}
public void setPermissionIndexTime(Timestamp permissionIndexTime) {
this.permissionIndexTime = permissionIndexTime;
}
public Dataverse getDataverseContext() {
if (this instanceof Dataverse) {
return (Dataverse) this;
} else if (this.getOwner() != null){
return this.getOwner().getDataverseContext();
}
return null;
}
/**
*
* @param other
* @return {@code true} iff {@code other} is {@code this} or below {@code this} in the containment hierarchy.
*/
public abstract boolean isAncestorOf( DvObject other );
@OneToMany(mappedBy = "definitionPoint",cascade={ CascadeType.REMOVE, CascadeType.MERGE,CascadeType.PERSIST}, orphanRemoval=true)
List<RoleAssignment> roleAssignments;
}