StickyNoteLinkManager.java

/*
 * The coLAB project
 * Copyright (C) 2021-2023 AlbaSim, MEI, HEIG-VD, HES-SO
 *
 * Licensed under the MIT License
 */
package ch.colabproject.colab.api.controller.link;

import ch.colabproject.colab.api.controller.card.CardContentManager;
import ch.colabproject.colab.api.controller.card.CardManager;
import ch.colabproject.colab.api.controller.document.BlockManager;
import ch.colabproject.colab.api.controller.document.DocumentManager;
import ch.colabproject.colab.api.controller.document.ResourceManager;
import ch.colabproject.colab.api.model.card.Card;
import ch.colabproject.colab.api.model.document.TextDataBlock;
import ch.colabproject.colab.api.model.link.StickyNoteLink;
import ch.colabproject.colab.api.model.link.StickyNoteSourceable;
import ch.colabproject.colab.api.persistence.jpa.link.StickyNoteLinkDao;
import ch.colabproject.colab.generator.model.exceptions.HttpErrorMessage;
import ch.colabproject.colab.generator.model.exceptions.MessageI18nKey;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Sticky note link specific logic
 *
 * @author sandra
 */
@Stateless
@LocalBean
public class StickyNoteLinkManager {

    /** logger */
    private static final Logger logger = LoggerFactory.getLogger(StickyNoteLinkManager.class);

    // *********************************************************************************************
    // injections
    // *********************************************************************************************

    /**
     * Sticky note link persistence handling
     */
    @Inject
    private StickyNoteLinkDao linkDao;

    /**
     * Card specific logic management
     */
    @Inject
    private CardManager cardManager;

    /**
     * Card content specific logic management
     */
    @Inject
    private CardContentManager cardContentManager;

    /**
     * Resource / resource reference logic management
     */
    @Inject
    private ResourceManager resourceManager;

    /**
     * Document specific logic management
     */
    @Inject
    private DocumentManager documentManager;

    /**
     * Block specific logic management
     */
    @Inject
    private BlockManager blockManager;

    // *********************************************************************************************
    //
    // *********************************************************************************************

    /**
     * Possible source types
     *
     * @author sandra
     */
    public enum SrcType {
        /** from a card. */
        CARD,
        /** from a card content. */
        CARD_CONTENT,
        /** from a resource or ref */
        RESOURCE_OR_REF,
        /** from a document */
        DOCUMENT;
    }

    // *********************************************************************************************
    // find sticky note links
    // *********************************************************************************************

    /**
     * Retrieve the sticky note link. If not found, throw a {@link HttpErrorMessage}.
     *
     * @param linkId the id of the sticky note link
     *
     * @return the sticky note link if found
     *
     * @throws HttpErrorMessage if the sticky note link was not found
     */
    public StickyNoteLink assertAndGetStickyNoteLink(Long linkId) {
        StickyNoteLink link = linkDao.findStickyNoteLink(linkId);

        if (link == null) {
            logger.error("sticky note link #{} not found", linkId);
            throw HttpErrorMessage.dataError(MessageI18nKey.DATA_NOT_FOUND);
        }

        return link;
    }

    // *********************************************************************************************
    //
    // *********************************************************************************************

    /**
     * Create a link
     *
     * @param link the link to create
     *
     * @return the brand new link
     */
    public StickyNoteLink createStickyNoteLink(StickyNoteLink link) {
        logger.debug("create link {}", link);

        StickyNoteSourceable sourceObject = assertAndGetSourceObject(link);

        Card toCard = cardManager.assertAndGetCard(link.getDestinationCardId());

        if (link.getExplanation() == null) {
            TextDataBlock explanationTextDataBlock = blockManager.makeNewTextDataBlock();

            link.setExplanation(explanationTextDataBlock);
            explanationTextDataBlock.setExplainingStickyNoteLink(link);
        }

        link.setSrc(sourceObject);
        link.setDestinationCard(toCard);

        sourceObject.getStickyNoteLinksAsSrc().add(link);
        toCard.getStickyNoteLinksAsDest().add(link);

        return linkDao.persistStickyNoteLink(link);
    }

    /**
     * Delete a link
     *
     * @param linkId the id of the link to delete
     */
    public void deleteStickyNoteLink(Long linkId) {
        logger.debug("delete link #{}", linkId);

        // firstly fetch all objects and so ensure that they exist
        StickyNoteLink link = assertAndGetStickyNoteLink(linkId);

        StickyNoteSourceable sourceObject = link.getSrc();
        if (sourceObject == null) {
            throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
        }

        Card toCard = link.getDestinationCard();
        if (toCard == null) {
            throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
        }

        // secondly make the modifications
        sourceObject.getStickyNoteLinksAsSrc().remove(link);
        toCard.getStickyNoteLinksAsDest().remove(link);

        linkDao.deleteStickyNoteLink(link);
    }

    /**
     * Change the source
     *
     * @param linkId     the id of the link to update
     * @param newSrcType the type of the new source
     * @param newSrcId   the id of the new source
     */
    public void changeStickyNoteLinkSource(Long linkId, SrcType newSrcType, Long newSrcId) {
        logger.debug("change source of link #{} with {} #{}", linkId, newSrcType, newSrcId);

        // firstly fetch all objects and so ensure that they exist
        StickyNoteLink link = assertAndGetStickyNoteLink(linkId);

        StickyNoteSourceable oldSourceObject = assertAndGetSourceObject(link);

        StickyNoteSourceable newSourceObject = assertAndGetSourceObject(newSrcType, newSrcId);

        // secondly make the modifications
        oldSourceObject.getStickyNoteLinksAsSrc().remove(link);

        link.setSrc(newSourceObject);
        newSourceObject.getStickyNoteLinksAsSrc().add(link);
    }

    /**
     * Change the destination
     *
     * @param linkId    the id of the link to update
     * @param newDestId the id of the new destination card
     */
    public void changeStickyNoteLinkDestination(Long linkId, Long newDestId) {
        logger.debug("change destination of link #{} with #{}", linkId, newDestId);

        // firstly fetch all objects and so ensure that they exist
        StickyNoteLink link = assertAndGetStickyNoteLink(linkId);

        Card oldDest = cardManager.assertAndGetCard(link.getDestinationCardId());

        Card newDestination = cardManager.assertAndGetCard(newDestId);

        // secondly make the modifications
        oldDest.getStickyNoteLinksAsDest().remove(link);

        link.setDestinationCard(newDestination);
        newDestination.getStickyNoteLinksAsDest().add(link);
    }

    /**
     * Get the source object corresponding to the given type and id.
     *
     * @param srcType the source type
     * @param srcId   the id of the source
     *
     * @return the source of the sticky note
     */
    private StickyNoteSourceable assertAndGetSourceObject(SrcType srcType, Long srcId) {
        StickyNoteSourceable sourceObject;

        switch (srcType) {
            case CARD:
                sourceObject = cardManager.assertAndGetCard(srcId);
                break;
            case CARD_CONTENT:
                sourceObject = cardContentManager.assertAndGetCardContent(srcId);
                break;
            case RESOURCE_OR_REF:
                sourceObject = resourceManager.assertAndGetResourceOrRef(srcId);
                break;
            case DOCUMENT:
                sourceObject = documentManager.assertAndGetDocument(srcId);
                break;
            default:
                throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
        }

        return sourceObject;
    }

    /**
     * Get the source object of the link
     *
     * @param link the link
     *
     * @return the source object
     */
    private StickyNoteSourceable assertAndGetSourceObject(StickyNoteLink link) {
        StickyNoteSourceable sourceObject;

        SrcType srcType = inferSrcType(link);

        switch (srcType) {
            case CARD:
                sourceObject = cardManager.assertAndGetCard(link.getSrcCardId());
                break;
            case CARD_CONTENT:
                sourceObject = cardContentManager
                    .assertAndGetCardContent(link.getSrcCardContentId());
                break;
            case RESOURCE_OR_REF:
                sourceObject = resourceManager
                    .assertAndGetResourceOrRef(link.getSrcResourceOrRefId());
                break;
            case DOCUMENT:
                sourceObject = documentManager.assertAndGetDocument(link.getSrcDocumentId());
                break;
            default:
                throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
        }

        return sourceObject;
    }

    /**
     * Infer the source type of the link.
     * <p>
     * It can have one and only one source.
     *
     * @param link the link
     *
     * @return the source type used
     */
    private SrcType inferSrcType(StickyNoteLink link) {
        SrcType effectiveSrcType = null;

        if (link.isSrcCard()) {
            effectiveSrcType = SrcType.CARD;
        }
        if (link.isSrcCardContent()) {
            if (effectiveSrcType != null) { // it must match only one type
                throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
            }
            effectiveSrcType = SrcType.CARD_CONTENT;
        }
        if (link.isSrcResourceOrRef()) {
            if (effectiveSrcType != null) { // it must match only one type
                throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
            }
            effectiveSrcType = SrcType.RESOURCE_OR_REF;
        }
        if (link.isSrcDocument()) {
            if (effectiveSrcType != null) { // it must match only one type
                throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
            }
            effectiveSrcType = SrcType.DOCUMENT;
        }
        if (effectiveSrcType == null) { // it must match one type
            throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
        }

        return effectiveSrcType;
    }

    // *********************************************************************************************
    //
    // *********************************************************************************************

}