TextDataBlock.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.document;

import ch.colabproject.colab.api.exceptions.ColabMergeException;
import ch.colabproject.colab.api.model.ColabEntity;
import ch.colabproject.colab.api.model.card.CardType;
import ch.colabproject.colab.api.model.link.StickyNoteLink;
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 javax.json.bind.annotation.JsonbTransient;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * Container of text data
 *
 * @author sandra
 */
@Entity
@DiscriminatorValue("TEXT_DATA_BLOCK")
public class TextDataBlock extends Document {

    private static final long serialVersionUID = 1L;

    // ---------------------------------------------------------------------------------------------
    // fields
    // ---------------------------------------------------------------------------------------------
    /**
     * The mime type of the information
     */
    @Size(max = 255)
    private String mimeType;

    /**
     * The information contained
     */
    @Lob
    private String textData;

    /**
     * Current revision hash
     */
    @NotBlank
    @Size(max = 255)
    private String revision = "0";

    /**
     * is the current live block healthy? To heal an un-healthy block, pending changes must be
     * deleted.
     */
    @NotNull
    private boolean healthy = true;

    /**
     * The card type it is the purpose of
     */
    @OneToOne(mappedBy = "purpose", fetch = FetchType.LAZY)
    @JsonbTransient
    private CardType purposingCardType;

    // no need of purposingCardTypeId

    /**
     * The resource it is the teaser of
     */
    @OneToOne(mappedBy = "teaser", fetch = FetchType.LAZY)
    @JsonbTransient
    private Resource teasingResource;

    // no need of teasingResourceId

    /**
     * The sticky note link it is the explanation of
     */
    @OneToOne(mappedBy = "explanation", fetch = FetchType.LAZY)
    @JsonbTransient
    private StickyNoteLink explainingStickyNoteLink;

    // no need of explainingStickyNoteLink

    // ---------------------------------------------------------------------------------------------
    // getters and setters
    // ---------------------------------------------------------------------------------------------
    /**
     * @return the mime type of the information
     */
    public String getMimeType() {
        return mimeType;
    }

    /**
     * @param mimeType the mime type of the information to set
     */
    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }

    /**
     * @return the information contained
     */
    public String getTextData() {
        return textData;
    }

    /**
     * @param data the information contained to set
     */
    public void setTextData(String data) {
        this.textData = data;
    }

    /**
     * Get the value of revision
     *
     * @return the value of revision
     */
    public String getRevision() {
        return revision;
    }

    /**
     * Set the value of revision
     *
     * @param revision new value of revision
     */
    public void setRevision(String revision) {
        this.revision = revision;
    }

    /**
     * @return the card type it is the purpose of
     */
    public CardType getPurposingCardType() {
        return purposingCardType;
    }

    /**
     * @param cardType the card type it is the purpose of
     */
    public void setPurposingCardType(CardType cardType) {
        this.purposingCardType = cardType;
    }

    /**
     * @return the resource it is the teaser of
     */
    public Resource getTeasingResource() {
        return teasingResource;
    }

    /**
     * @param resource the resource it is the teaser of
     */
    public void setTeasingResource(Resource resource) {
        this.teasingResource = resource;
    }

    /**
     * @return the sticky note link it is the explanation of
     */
    public StickyNoteLink getExplainingStickyNoteLink() {
        return explainingStickyNoteLink;
    }

    /**
     * @param stickyNoteLink the sticky note link it is the explanation of
     */
    public void setExplainingStickyNoteLink(StickyNoteLink stickyNoteLink) {
        this.explainingStickyNoteLink = stickyNoteLink;
    }

    /**
     * Is the block healthy?
     *
     * @return true if the block is healthy
     */
    public boolean isHealthy() {
        return healthy;
    }

    /**
     * Set the block health status
     *
     * @param healthy true is the block is healthy
     */
    public void setHealthy(boolean healthy) {
        this.healthy = healthy;
    }

    // ---------------------------------------------------------------------------------------------
    // concerning the whole class
    // ---------------------------------------------------------------------------------------------
    @Override
    public void mergeToUpdate(ColabEntity other) throws ColabMergeException {
        super.mergeToUpdate(other);

        if (other instanceof TextDataBlock) {
            TextDataBlock o = (TextDataBlock) other;
            this.setMimeType(o.getMimeType());
            this.setTextData(o.getTextData());
            // Note : the revision is handled by the LiveManager
            // Note : healthy is handled by the LiveManager
        } else {
            throw new ColabMergeException(this, other);
        }
    }

    /**
     * Get the project it belongs to
     *
     * @return block owner
     */
    @Override
    @JsonbTransient
    public Project getProject() {
        if (this.getOwningCardContent() != null) {
            // The document is a deliverable of a card content
            return this.getOwningCardContent().getProject();
        } else if (this.getOwningResource() != null) {
            // The document is part of a resource
            return this.getOwningResource().getProject();
        } else if (this.purposingCardType != null) {
            // It is the purpose of a card type
            return this.purposingCardType.getProject();
        } else if (this.teasingResource != null) {
            // It is the teaser of a resource
            return this.teasingResource.getProject();
        } else if (this.explainingStickyNoteLink != null) {
            // It is the explanation of a sticky note link
            return this.explainingStickyNoteLink.getProject();
        } else {
            // such an orphan shouldn't exist...
            return null;
        }
    }

    // Note : needed to set JsonbTransient, else it is generated in ColabClient.d.ts
    @JsonbTransient
    @Override
    public ChannelsBuilder getChannelsBuilder() {
        if (this.owningCardContent != null) {
            // The document is a deliverable of a card content
            return this.owningCardContent.getChannelsBuilder();
        } else if (this.owningResource != null) {
            // The document is part of a resource
            return this.owningResource.getChannelsBuilder();
        } else if (this.purposingCardType != null) {
            // It is the purpose of a card type
            return this.purposingCardType.getChannelsBuilder();
        } else if (this.teasingResource != null) {
            // It is the teaser of a resource
            return this.teasingResource.getChannelsBuilder();
        } else if (this.explainingStickyNoteLink != null) {
            // It is the explanation of a sticky note link
            return this.explainingStickyNoteLink.getChannelsBuilder();
        } else {
            // such an orphan shouldn't exist...
            return new EmptyChannelBuilder();
        }
    }

// TODO sandra work in progress - ACL on text data blocks
    @Override
    @JsonbTransient
    public Conditions.Condition getReadCondition() {
        if (getOwningCardContent() != null) {
            // The document is a deliverable of a card content
            return new Conditions.HasCardReadRight(getOwningCardContent());
        } else if (getOwningResource() != null) {
            // The document is part of a resource
            return getOwningResource().getReadCondition();
        } else if (this.purposingCardType != null) {
            // It is the purpose of a card type
            return this.purposingCardType.getReadCondition();
        } else if (this.teasingResource != null) {
            // It is the teaser of a resource
            return this.teasingResource.getReadCondition();
        } else if (this.explainingStickyNoteLink != null) {
            // It is the explanation of a sticky note link
            return this.explainingStickyNoteLink.getReadCondition();
        } else {
            // such an orphan shouldn't exist...
            return Conditions.defaultForOrphan;
        }
    }

    @Override
    @JsonbTransient
    public Conditions.Condition getUpdateCondition() {
        if (getOwningCardContent() != null) {
            // The document is a deliverable of a card content
            return getOwningCardContent().getUpdateCondition();
        } else if (getOwningResource() != null) {
            // The document is part of a resource
            return getOwningResource().getUpdateCondition();
        } else if (this.purposingCardType != null) {
            // It is the purpose of a card type
            return this.purposingCardType.getUpdateCondition();
        } else if (this.teasingResource != null) {
            // It is the teaser of a resource
            return this.teasingResource.getUpdateCondition();
        } else if (this.explainingStickyNoteLink != null) {
            // It is the explanation of a sticky note link
            return this.explainingStickyNoteLink.getUpdateCondition();
        } else {
            // such an orphan shouldn't exist...
            return Conditions.defaultForOrphan;
        }
    }

    @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 "TextDataBlock{" + super.toPartialString() + ", mimeType=" + mimeType + "}";
    }

}