ParagraphRepeatProcessor.java

package pro.verron.officestamper.preset.processors.repeatparagraph;

import org.docx4j.XmlUtils;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.P;
import pro.verron.officestamper.api.*;
import pro.verron.officestamper.core.CommentUtil;
import pro.verron.officestamper.core.SectionUtil;
import pro.verron.officestamper.core.StandardParagraph;
import pro.verron.officestamper.preset.CommentProcessorFactory;
import pro.verron.officestamper.preset.Paragraphs;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import static java.util.Collections.emptyIterator;
import static pro.verron.officestamper.core.SectionUtil.getPreviousSectionBreakIfPresent;
import static pro.verron.officestamper.core.SectionUtil.hasOddNumberOfSectionBreaks;

/// Class used internally to repeat document elements.
/// Used by the lib, should not be instantiated by clients.
///
/// @author Joseph Verron
/// @author Youssouf Naciri
/// @version ${version}
/// @since 1.2.2
public class ParagraphRepeatProcessor
        extends AbstractCommentProcessor
        implements CommentProcessorFactory.IParagraphRepeatProcessor {

    /* TODO replace the mapping by a Paragraphs to List<Object> mapping to better reflect the change*/
    private Map<Paragraph, Paragraphs> pToRepeat = new HashMap<>();

    private ParagraphRepeatProcessor(ParagraphPlaceholderReplacer placeholderReplacer) {
        super(placeholderReplacer);
    }

    /// Creates a new instance of [CommentProcessor] using the provided [ParagraphPlaceholderReplacer].
    ///
    /// @param placeholderReplacer the replacer to use for processing paragraph placeholders.
    ///
    /// @return a new instance of [ParagraphRepeatProcessor].
    public static CommentProcessor newInstance(ParagraphPlaceholderReplacer placeholderReplacer) {
        return new ParagraphRepeatProcessor(placeholderReplacer);
    }

    @Override public void repeatParagraph(Iterable<Object> objects) {
        var paragraph = getParagraph();
        var comment = getCurrentCommentWrapper();
        var elements = comment.getElements();
        var previousSectionBreak = getPreviousSectionBreakIfPresent(elements.getFirst(), comment.getParent());
        var oddNumberOfBreaks = hasOddNumberOfSectionBreaks(elements);
        var iterator = objects == null ? emptyIterator() : objects.iterator();
        var toRepeat = new Paragraphs(comment, iterator, elements, previousSectionBreak, oddNumberOfBreaks);
        pToRepeat.put(paragraph, toRepeat);
    }

    @Override public void commitChanges(DocxPart document) {
        for (Map.Entry<Paragraph, Paragraphs> entry : pToRepeat.entrySet()) {
            var current = entry.getKey();
            var replacement = entry.getValue();
            var toRemove = replacement.elements(P.class);
            var toAdd = generateParagraphsToAdd(document, replacement);
            current.replace(toRemove, toAdd);
        }
    }

    private List<P> generateParagraphsToAdd(DocxPart document, Paragraphs paragraphs) {
        var paragraphsToAdd = new LinkedList<P>();
        for (var it = paragraphs.data(); it.hasNext(); ) {
            Object expressionContext = it.next();
            for (Object paragraphToClone : paragraphs.elements()) {
                Object clone = XmlUtils.deepCopy(paragraphToClone);
                var comment = paragraphs.comment();
                var comment1 = comment.getComment();
                var commentId = comment1.getId();
                if (clone instanceof ContentAccessor contentAccessor) {
                    CommentUtil.deleteCommentFromElements(contentAccessor.getContent(), commentId);
                }
                if (clone instanceof P p) {
                    var paragraph = StandardParagraph.from(document, p);
                    placeholderReplacer.resolveExpressionsForParagraph(document, paragraph, expressionContext);
                    paragraphsToAdd.add(p);
                }
            }
            var sectPr = paragraphs.previousSectionBreak();
            if (paragraphs.oddNumberOfBreaks() && sectPr.isPresent() && it.hasNext()) {
                assert paragraphsToAdd.peekLast() != null : "There should be at least one ";
                SectionUtil.applySectionBreakToParagraph(sectPr.get(), paragraphsToAdd.peekLast());
            }
        }
        return paragraphsToAdd;
    }

    @Override public void reset() {
        pToRepeat = new HashMap<>();
    }
}