StickyNoteLink.java
/*
* The coLAB project
* Copyright (C) 2021-2023 AlbaSim, MEI, HEIG-VD, HES-SO
*
* Licensed under the MIT License
*/
package ch.colabproject.colab.api.model.link;
import static ch.colabproject.colab.api.model.link.ActivityFlowLink.LINK_SEQUENCE_NAME;
import ch.colabproject.colab.api.exceptions.ColabMergeException;
import ch.colabproject.colab.api.model.ColabEntity;
import ch.colabproject.colab.api.model.WithWebsocketChannels;
import ch.colabproject.colab.api.model.card.Card;
import ch.colabproject.colab.api.model.card.CardContent;
import ch.colabproject.colab.api.model.common.DeletionStatus;
import ch.colabproject.colab.api.model.common.Tracking;
import ch.colabproject.colab.api.model.document.AbstractResource;
import ch.colabproject.colab.api.model.document.Document;
import ch.colabproject.colab.api.model.document.TextDataBlock;
import ch.colabproject.colab.api.model.project.Project;
import ch.colabproject.colab.api.model.tools.EntityHelper;
import ch.colabproject.colab.api.security.permissions.Conditions;
import ch.colabproject.colab.api.ws.channel.tool.ChannelsBuilders.ChannelsBuilder;
import ch.colabproject.colab.api.ws.channel.tool.ChannelsBuilders.EmptyChannelBuilder;
import ch.colabproject.colab.generator.model.exceptions.HttpErrorMessage;
import ch.colabproject.colab.generator.model.exceptions.MessageI18nKey;
import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.CascadeType;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* Link to make an information accessible within a card.
* <p>
* The source of information can be either
* <ul>
* <li>a card</li>
* <li>a card content</li>
* <li>a resource</li>
* <li>a document</li>
* </ul>
*
* @author sandra
*/
@Entity
@Table(
indexes = {
@Index(columnList = "destinationcard_id"),
@Index(columnList = "explanation_id"),
@Index(columnList = "srccard_id"),
@Index(columnList = "srccardcontent_id"),
@Index(columnList = "srcdocument_id"),
@Index(columnList = "srcresourceorref_id")
}
)
public class StickyNoteLink implements ColabEntity, WithWebsocketChannels {
private static final long serialVersionUID = 1L;
// ---------------------------------------------------------------------------------------------
// fields
// ---------------------------------------------------------------------------------------------
/**
* Link ID
*/
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = LINK_SEQUENCE_NAME)
private Long id;
/**
* creation + modification + erasure tracking data
*/
@Embedded
private Tracking trackingData;
/**
* Is it in a bin or ready to be definitely deleted. Null means active.
*/
@Enumerated(EnumType.STRING)
private DeletionStatus deletionStatus;
/**
* The short description
*/
@Size(max = 255)
private String teaser;
/**
* The long description
*/
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@NotNull
@JsonbTransient
private TextDataBlock explanation;
/**
* The id of the long description (serialization sugar)
*/
@Transient
private Long explanationId;
/**
* The card, source of the sticky note
*/
@ManyToOne(fetch = FetchType.LAZY)
@JsonbTransient
private Card srcCard;
/**
* The ID of the source card (serialization sugar)
*/
@Transient
private Long srcCardId;
/**
* The card content, source of the sticky note
*/
@ManyToOne(fetch = FetchType.LAZY)
@JsonbTransient
private CardContent srcCardContent;
/**
* The ID of the source card content (serialization sugar)
*/
@Transient
private Long srcCardContentId;
/**
* The resource / resource reference, source of the sticky note
*/
@ManyToOne(fetch = FetchType.LAZY)
@JsonbTransient
private AbstractResource srcResourceOrRef;
/**
* The ID of the source resource / resource reference (serialization sugar)
*/
@Transient
private Long srcResourceOrRefId;
/**
* The document, source of the sticky note
*/
@ManyToOne(fetch = FetchType.LAZY)
@JsonbTransient
private Document srcDocument;
/**
* The ID of the source document (serialization sugar)
*/
@Transient
private Long srcDocumentId;
/**
* The card where the information is useful
*/
@ManyToOne(fetch = FetchType.LAZY)
@NotNull
@JsonbTransient
private Card destinationCard;
/**
* The ID of the destination card (serialization sugar)
*/
@Transient
private Long destinationCardId;
// ---------------------------------------------------------------------------------------------
// getters and setters
// ---------------------------------------------------------------------------------------------
/**
* @return the link id
*/
@Override
public Long getId() {
return id;
}
/**
* @param id the link id
*/
public void setId(Long id) {
this.id = id;
}
/**
* Get the tracking data
*
* @return tracking data
*/
@Override
public Tracking getTrackingData() {
return trackingData;
}
/**
* Set tracking data
*
* @param trackingData new tracking data
*/
@Override
public void setTrackingData(Tracking trackingData) {
this.trackingData = trackingData;
}
@Override
public DeletionStatus getDeletionStatus() {
return deletionStatus;
}
@Override
public void setDeletionStatus(DeletionStatus status) {
this.deletionStatus = status;
}
/**
* @return the short description
*/
public String getTeaser() {
return teaser;
}
/**
* @param teaser the short description
*/
public void setTeaser(String teaser) {
this.teaser = teaser;
}
/**
* @return the long description
*/
public TextDataBlock getExplanation() {
return explanation;
}
/**
* @param explanation the long description
*/
public void setExplanation(TextDataBlock explanation) {
this.explanation = explanation;
}
/**
* get the id of the explanation. To be sent to client.
*
* @return the id of the explanation
*/
public Long getExplanationId() {
if (explanation != null) {
return explanation.getId();
} else {
return explanationId;
}
}
/**
* set the id of the explanation. For serialization only.
*
* @param explanationId the id of the teaser
*/
public void setExplanationId(Long explanationId) {
this.explanationId = explanationId;
}
/**
* @return the card, source of the sticky note
*/
public Card getSrcCard() {
return srcCard;
}
/**
* @param srcCard the card, source of the sticky note
*/
public void setSrcCard(Card srcCard) {
this.srcCard = srcCard;
}
/**
* get the id of the source card. To be sent to client
*
* @return the id of source card
*/
public Long getSrcCardId() {
if (this.srcCard != null) {
return this.srcCard.getId();
} else {
return srcCardId;
}
}
/**
* set the id of the source card. For serialization only
*
* @param srcCardId the id of the source card
*/
public void setSrcCardId(Long srcCardId) {
this.srcCardId = srcCardId;
}
/**
* @return True if the source is a card
*/
public boolean isSrcCard() {
return this.srcCard != null || this.srcCardId != null;
}
/**
* @return the card content, source of the sticky note
*/
public CardContent getSrcCardContent() {
return srcCardContent;
}
/**
* @param srcCardContent the card content, source of the sticky note
*/
public void setSrcCardContent(CardContent srcCardContent) {
this.srcCardContent = srcCardContent;
}
/**
* get the id of the source card content. To be sent to client
*
* @return the id of source card content
*/
public Long getSrcCardContentId() {
if (this.srcCardContent != null) {
return this.srcCardContent.getId();
} else {
return srcCardContentId;
}
}
/**
* set the id of the source card content. For serialization only
*
* @param srcCardContentId the id of the source card content
*/
public void setSrcCardContentId(Long srcCardContentId) {
this.srcCardContentId = srcCardContentId;
}
/**
* @return True if the source is a card content
*/
public boolean isSrcCardContent() {
return this.srcCardContent != null || this.srcCardContentId != null;
}
/**
* @return the resource / resource reference, source of the sticky note
*/
public AbstractResource getSrcResourceOrRef() {
return srcResourceOrRef;
}
/**
* @param srcResourceOrRef the resource / resource reference, source of the sticky note
*/
public void setSrcResourceOrRef(AbstractResource srcResourceOrRef) {
this.srcResourceOrRef = srcResourceOrRef;
}
/**
* get the id of the source resource / resource reference. To be sent to client
*
* @return the id of source resource / resource reference
*/
public Long getSrcResourceOrRefId() {
if (this.srcResourceOrRef != null) {
return this.srcResourceOrRef.getId();
} else {
return srcResourceOrRefId;
}
}
/**
* set the id of the source resource / resource reference. For serialization only
*
* @param srcResourceOrRefId the id of the source resource / resource reference
*/
public void setSrcResourceOrRefId(Long srcResourceOrRefId) {
this.srcResourceOrRefId = srcResourceOrRefId;
}
/**
* @return True if the source is a resource / resource reference
*/
public boolean isSrcResourceOrRef() {
return this.srcResourceOrRef != null || this.srcResourceOrRefId != null;
}
/**
* @return the document, source of the sticky note
*/
public Document getSrcDocument() {
return srcDocument;
}
/**
* @param srcDocument the Document, source of the sticky note
*/
public void setSrcDocument(Document srcDocument) {
this.srcDocument = srcDocument;
}
/**
* get the id of the source document. To be sent to client
*
* @return the id of source document
*/
public Long getSrcDocumentId() {
if (this.srcDocument != null) {
return this.srcDocument.getId();
} else {
return srcDocumentId;
}
}
/**
* set the id of the source document. For serialization only
*
* @param srcDocumentId the id of the source document
*/
public void setSrcDocumentId(Long srcDocumentId) {
this.srcDocumentId = srcDocumentId;
}
/**
* @return True if the source is a document
*/
public boolean isSrcDocument() {
return this.srcDocument != null || this.srcDocumentId != null;
}
/**
* @return the card where the information is useful
*/
public Card getDestinationCard() {
return destinationCard;
}
/**
* @param destinationCard the card where the information is useful
*/
public void setDestinationCard(Card destinationCard) {
this.destinationCard = destinationCard;
}
/**
* get the id of the destination card. To be sent to client
*
* @return the id of destination card
*/
public Long getDestinationCardId() {
if (this.destinationCard != null) {
return this.destinationCard.getId();
} else {
return destinationCardId;
}
}
/**
* set the id of the destination card. For serialization only
*
* @param destinationCardId the id of the destination card
*/
public void setDestinationCardId(Long destinationCardId) {
this.destinationCardId = destinationCardId;
}
// ---------------------------------------------------------------------------------------------
// helpers
// ---------------------------------------------------------------------------------------------
/**
* @return the source of the link
*/
@JsonbTransient
public StickyNoteSourceable getSrc() {
if (this.srcCard != null) {
return this.srcCard;
}
if (this.srcCardContent != null) {
return this.srcCardContent;
}
if (this.srcResourceOrRef != null) {
return this.srcResourceOrRef;
}
if (this.srcDocument != null) {
return this.srcDocument;
}
throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
}
/**
* @param src the source of the link
*/
public void setSrc(StickyNoteSourceable src) {
if (src == null) {
resetSrc();
} else if (src instanceof Card) {
resetSrc();
setSrcCard((Card) src);
} else if (src instanceof CardContent) {
resetSrc();
setSrcCardContent((CardContent) src);
} else if (src instanceof AbstractResource) {
resetSrc();
setSrcResourceOrRef((AbstractResource) src);
} else if (src instanceof Document) {
resetSrc();
setSrcDocument((Document) src);
} else {
throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
}
}
/**
* Set every source to null
*/
private void resetSrc() {
setSrcCard(null);
setSrcCardId(null);
setSrcCardContent(null);
setSrcCardContentId(null);
setSrcResourceOrRef(null);
setSrcResourceOrRefId(null);
setSrcDocument(null);
setSrcDocumentId(null);
}
// ---------------------------------------------------------------------------------------------
// concerning the whole class
// ---------------------------------------------------------------------------------------------
@Override
public void mergeToUpdate(ColabEntity other) throws ColabMergeException {
if (other instanceof StickyNoteLink) {
StickyNoteLink o = (StickyNoteLink) other;
this.setDeletionStatus(o.getDeletionStatus());
this.setTeaser(o.getTeaser());
} else {
throw new ColabMergeException(this, other);
}
}
/**
* Get the project it belongs to
*
* @return project owner
*/
@JsonbTransient
public Project getProject() {
if (this.destinationCard != null) {
return this.destinationCard.getProject();
} else {
// such an orphan shouldn't exist...
return null;
}
}
@Override
public ChannelsBuilder getChannelsBuilder() {
if (this.destinationCard != null) {
return this.destinationCard.getChannelsBuilder();
} else {
// such an orphan shouldn't exist...
return new EmptyChannelBuilder();
}
}
@Override
@JsonbTransient
public Conditions.Condition getReadCondition() {
return new Conditions.Or(
this.getSrc().getReadCondition(),
this.getDestinationCard().getReadCondition()
);
}
@Override
@JsonbTransient
public Conditions.Condition getUpdateCondition() {
return this.getSrc().getUpdateCondition();
}
@Override
public int hashCode() {
return EntityHelper.hashCode(this);
}
@Override
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
public boolean equals(Object obj) {
return EntityHelper.equals(this, obj);
}
@Override
public String toString() {
return "StickyNoteLink{" + "id=" + id + ", deletion=" + getDeletionStatus()
+ ", srcCardId=" + srcCardId + ", srcCardContentId=" + srcCardContentId
+ ", srcResourceOrRefId=" + srcResourceOrRefId + ", srcDocId=" + srcDocumentId
+ ", destinationCardId=" + destinationCardId + ", teaser=" + teaser + "}";
}
}