![JAR search and dependency download from the Maven repository](/logo.png)
pro.verron.officestamper.core.CommentProcessorRegistry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of engine Show documentation
Show all versions of engine Show documentation
Office-stamper is a Java template engine for docx documents, forked from org.wickedsource.docx-stamper
package pro.verron.officestamper.core;
import org.docx4j.XmlUtils;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.Comments;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelParseException;
import org.springframework.lang.Nullable;
import pro.verron.officestamper.api.Comment;
import pro.verron.officestamper.api.CommentProcessor;
import pro.verron.officestamper.api.DocxPart;
import java.math.BigInteger;
import java.util.*;
import static pro.verron.officestamper.core.CommentUtil.getCommentString;
import static pro.verron.officestamper.core.CommentUtil.getComments;
import static pro.verron.officestamper.core.ExceptionUtil.treatException;
/**
* Allows registration of {@link CommentProcessor} objects. Each registered
* ICommentProcessor must implement an interface which has to be specified at
* registration time. Provides several getter methods to access the registered
* {@link CommentProcessor}.
*
* @author Joseph Verron
* @author Tom Hombergs
* @version ${version}
* @since 1.0.0
*/
public class CommentProcessorRegistry {
private static final Logger logger = LoggerFactory.getLogger(CommentProcessorRegistry.class);
private final DocxPart source;
private final Map, Object> commentProcessors;
private final boolean failOnUnresolvedExpression;
private final ExpressionResolver expressionResolver;
/**
* Creates a new CommentProcessorRegistry.
*
* @param expressionResolver the expression resolver
* @param commentProcessors the comment processors
* @param failOnUnresolvedExpression whether to fail on unresolved expressions
*/
public CommentProcessorRegistry(
DocxPart source,
ExpressionResolver expressionResolver,
Map, Object> commentProcessors,
boolean failOnUnresolvedExpression
) {
this.source = source;
this.expressionResolver = expressionResolver;
this.commentProcessors = commentProcessors;
this.failOnUnresolvedExpression = failOnUnresolvedExpression;
}
public void runProcessors(T expressionContext) {
var proceedComments = new ArrayList();
source.streamParagraphs()
.map(P::getContent)
.flatMap(Collection::stream)
.map(XmlUtils::unwrap)
.filter(R.class::isInstance)
.map(R.class::cast)
.forEach(run -> {
var comments = getComments(source);
var runParent = (P) run.getParent();
var optional = runProcessorsOnRunComment(comments, expressionContext, run, runParent);
if (optional.isPresent()) {
var comment = optional.get();
for (Object processor : commentProcessors.values()) {
var commentProcessor = (CommentProcessor) processor;
commentProcessor.commitChanges(source);
commentProcessor.reset();
}
proceedComments.add(comment);
}
});
// we run the paragraph afterward so that the comments inside work before the whole paragraph comments
source.streamParagraphs()
.forEach(p -> {
var document = source.document();
var comments = getComments(source);
var optional = runProcessorsOnParagraphComment(document, comments, expressionContext, p);
if (optional.isPresent()) {
for (Object processor : commentProcessors.values()) {
var commentProcessor = (CommentProcessor) processor;
commentProcessor.commitChanges(source);
commentProcessor.reset();
}
proceedComments.add(optional.get());
}
});
source.streamParagraphs()
.forEach(paragraph -> runProcessorsOnInlineContent(expressionContext, paragraph));
for (Comment comment : proceedComments) {
CommentUtil.deleteComment(comment);
}
}
private Optional runProcessorsOnRunComment(
Map comments,
T expressionContext,
R run,
P paragraph
) {
return CommentUtil
.getCommentAround(run, source.document())
.flatMap(c -> runCommentProcessors(
comments,
expressionContext,
c,
paragraph, run
));
}
/**
* Takes the first comment on the specified paragraph and tries to evaluate
* the string within the comment against all registered
* {@link CommentProcessor}s.
*
* @param document the Word document.
* @param comments the comments within the document.
* @param expressionContext the context root object
* @param the type of the context root object.
*/
private Optional runProcessorsOnParagraphComment(
WordprocessingMLPackage document,
Map comments,
T expressionContext,
P paragraph
) {
return CommentUtil
.getCommentFor(paragraph, document)
.flatMap(c -> runCommentProcessors(
comments,
expressionContext,
c,
paragraph, null
));
}
/**
* Finds all processor expressions within the specified paragraph and tries
* to evaluate it against all registered {@link CommentProcessor}s.
*
* @param context the context root object against which evaluation is done
* @param paragraph the paragraph to process.
* @param type of the context root object
*/
private void runProcessorsOnInlineContent(
T context,
P paragraph
) {
var paragraphWrapper = new StandardParagraph(paragraph);
var text = paragraphWrapper.asString();
var placeholders = Placeholders.findProcessors(text);
for (var placeholder : placeholders) {
for (var processor : commentProcessors.values()) {
((CommentProcessor) processor).setParagraph(paragraph);
}
try {
expressionResolver.setContext(context);
expressionResolver.resolve(placeholder);
paragraphWrapper.replace(placeholder, RunUtil.create(""));
logger.debug("Placeholder '{}' successfully processed by a comment processor.", placeholder);
} catch (SpelEvaluationException | SpelParseException e) {
var msg = "Placeholder '%s' failed to process.".formatted(placeholder);
treatException(e, failOnUnresolvedExpression, msg);
}
for (var processor : commentProcessors.values()) {
((CommentProcessor) processor).commitChanges(source);
}
}
}
private Optional runCommentProcessors(
Map comments,
T context,
Comments.Comment comment,
P paragraph,
@Nullable R run
) {
Comment commentWrapper = comments.get(comment.getId());
if (Objects.isNull(commentWrapper)) {
// no comment to process
return Optional.empty();
}
var placeholder = getCommentString(comment);
for (final Object processor : commentProcessors.values()) {
((CommentProcessor) processor).setParagraph(paragraph);
((CommentProcessor) processor).setCurrentRun(run);
((CommentProcessor) processor).setCurrentCommentWrapper(commentWrapper);
}
try {
expressionResolver.setContext(context);
expressionResolver.resolve(placeholder);
comments.remove(comment.getId());
logger.debug("Comment '{}' successfully processed by a comment processor.", placeholder);
return Optional.of(commentWrapper);
} catch (SpelEvaluationException | SpelParseException e) {
var msg = "Comment '%s' failed to process.".formatted(placeholder);
treatException(e, failOnUnresolvedExpression, msg);
return Optional.empty();
}
}
/**
* Resets all registered ICommentProcessors.
*/
public void reset() {
for (Object processor : commentProcessors.values()) {
((CommentProcessor) processor).reset();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy