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

import ch.colabproject.colab.api.model.document.Document;
import ch.colabproject.colab.api.model.document.DocumentFile;
import ch.colabproject.colab.api.model.document.TextDataBlock;
import ch.colabproject.colab.api.model.link.StickyNoteLink;
import ch.colabproject.colab.api.persistence.jpa.document.DocumentDao;
import ch.colabproject.colab.generator.model.exceptions.HttpErrorMessage;
import ch.colabproject.colab.generator.model.exceptions.MessageI18nKey;
import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Document specific logic
 *
 * @author sandra
 */
@Stateless
@LocalBean
public class DocumentManager {

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

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

    /**
     * Document persistence handler
     */
    @Inject
    private DocumentDao documentDao;

    /**
     * Index generation specific logic management
     */
    @Inject
    private IndexGeneratorHelper<Document> indexGenerator;

    // *********************************************************************************************
    // find document
    // *********************************************************************************************

    /**
     * Retrieve the document. If not found, throw a {@link HttpErrorMessage}.
     *
     * @param documentId the id of the document
     *
     * @return the document if found
     *
     * @throws HttpErrorMessage if the document was not found
     */
    public Document assertAndGetDocument(Long documentId) {
        Document document = documentDao.findDocument(documentId);

        if (document == null) {
            logger.error("document #{} not found", documentId);
            throw HttpErrorMessage.dataError(MessageI18nKey.DATA_NOT_FOUND);
        }

        return document;
    }

    /**
     * Retrieve the text data block. If not found, throw a {@link HttpErrorMessage}.
     *
     * @param textDataBlockId the id of the text data block
     *
     * @return the text data block if found
     *
     * @throws HttpErrorMessage if the text data block was not found
     */
    public TextDataBlock assertAndGetTextDataBlock(Long textDataBlockId) {
        Document document = assertAndGetDocument(textDataBlockId);

        if (!(document instanceof TextDataBlock)) {
            logger.error("#{} is not a text data block", document);
            throw HttpErrorMessage.dataError(MessageI18nKey.DATA_NOT_FOUND);
        }

        return (TextDataBlock) document;
    }

    /**
     * Retrieve the document file. If not found, throw a {@link HttpErrorMessage}.
     *
     * @param documentFileId the id of the document file
     *
     * @return the document file if found
     *
     * @throws HttpErrorMessage if the document file was not found
     */
    public DocumentFile assertAndGetDocumentFile(Long documentFileId) {
        Document document = assertAndGetDocument(documentFileId);

        if (!(document instanceof DocumentFile)) {
            logger.error("#{} is not a document file", document);
            throw HttpErrorMessage.dataError(MessageI18nKey.DATA_NOT_FOUND);
        }

        return (DocumentFile) document;
    }

    /**
     * Retrieve the list of documents the given document is part of
     *
     * @param document the document
     *
     * @return the list of documents the given document is part of
     *
     * @throws HttpErrorMessage If no collection could be found
     */
    public List<Document> getDocumentList(Document document) {
        if (document.hasOwningCardContent()) {
            return document.getOwningCardContent().getDeliverables();

        } else if (document.hasOwningResource()) {
            return document.getOwningResource().getDocuments();
        }

        throw HttpErrorMessage.dataError(MessageI18nKey.DATA_INTEGRITY_FAILURE);
    }

    // *********************************************************************************************
    // move document
    // *********************************************************************************************

    /**
     * Move the document to the top
     *
     * @param documentId the id of the document to move
     */
    public void moveDocumentToTop(Long documentId) {
        logger.debug("move document #{} at top", documentId);

        Document document = assertAndGetDocument(documentId);
        List<Document> docList = getDocumentList(document);

        indexGenerator.moveItemToBeginning(document, docList);

    }

    /**
     * Move the given document one floor up
     *
     * @param documentId the id of the document to move
     */
    public void moveDocumentUp(Long documentId) {
        logger.debug("move document #{} up", documentId);

        Document document = assertAndGetDocument(documentId);
        List<Document> docList = getDocumentList(document);

        indexGenerator.moveOneStepAhead(document, docList);
    }

    /**
     * Move the given document above the given reference item
     *
     * @param documentId the id of the document to move
     * @param baseDocId  the id of the reference document
     */
    public void moveDocumentAbove(Long documentId, Long baseDocId) {
        logger.debug("move document #{} above #{}", documentId, baseDocId);

        Document document = assertAndGetDocument(documentId);

        Document baseDocument = assertAndGetDocument(baseDocId);
        List<Document> docList = getDocumentList(baseDocument);

        indexGenerator.moveItemBefore(document, baseDocument, docList);

    }

    /**
     * Move the given document one floor down
     *
     * @param documentId the id of the document to move
     */
    public void moveDocumentDown(Long documentId) {
        logger.debug("move document #{} down", documentId);

        Document document = assertAndGetDocument(documentId);
        List<Document> docList = getDocumentList(document);

        indexGenerator.moveOneStepBehind(document, docList);
    }

    /**
     * Move the given document below the given reference item.
     *
     * @param documentId the id of the document to move
     * @param baseDocId  the id of the reference document
     */
    public void moveDocumentBelow(Long documentId, Long baseDocId) {
        logger.debug("move document #{} below #{}", documentId, baseDocId);

        Document document = assertAndGetDocument(documentId);

        Document baseDocument = assertAndGetDocument(baseDocId);
        List<Document> docList = getDocumentList(baseDocument);

        indexGenerator.moveItemAfter(document, baseDocument, docList);
    }

    /**
     * Move the document to the bottom
     *
     * @param documentId the id of the document to move
     */
    public void moveDocumentToBottom(Long documentId) {
        logger.debug("move document #{} at bottom", documentId);

        Document document = assertAndGetDocument(documentId);
        List<Document> docList = getDocumentList(document);

        indexGenerator.moveItemToEnd(document, docList);
    }

    // *********************************************************************************************
    // integrity check
    // *********************************************************************************************

    /**
     * Check the integrity of the document
     *
     * @param document the document to check
     *
     * @return true iff the document is complete and safe
     */
    public boolean checkIntegrity(Document document) {
        if (document == null) {
            return false;
        }

        if (countOwners(document) != 1) {
            return false;
        }

        return true;
    }

    /**
     * Count the number of entities owning the document. It should be only one.
     *
     * @param document the document to check
     *
     * @return the number of owning entities
     */
    protected int countOwners(Document document) {
        int result = 0;

        if (document.hasOwningCardContent()) {
            result++;
        }

        if (document.hasOwningResource()) {
            result++;
        }

        if (document instanceof TextDataBlock) {
            TextDataBlock block = (TextDataBlock) document;

            if (block.getPurposingCardType() != null) {
                result++;
            }

            if (block.getTeasingResource() != null) {
                result++;
            }

            if (block.getExplainingStickyNoteLink() != null) {
                result++;
            }
        }

        return result;
    }

    // *********************************************************************************************
    // retrieve the elements linked to documents
    // *********************************************************************************************

    /**
     * Get all sticky note links of which the given document is the source
     *
     * @param docId the id of the document
     *
     * @return all sticky note links which has the given document as source
     */
    public List<StickyNoteLink> getStickyNoteLinkAsSrc(Long docId) {
        logger.debug("get sticky note links where the document #{} is the source", docId);

        Document document = assertAndGetDocument(docId);

        return document.getStickyNoteLinksAsSrc();
    }

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

    // As a document is either linked to a resource or to a card content, most of
    // the operations are
    // made from there

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

}