org.wickedsource.docxstamper.processor.CommentProcessorRegistry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of docx-stamper Show documentation
Show all versions of docx-stamper Show documentation
Template engine for .docx documents.
package org.wickedsource.docxstamper.processor;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.wml.Comments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelParseException;
import org.wickedsource.docxstamper.api.DocxStamperException;
import org.wickedsource.docxstamper.api.UnresolvedExpressionException;
import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor;
import org.wickedsource.docxstamper.api.coordinates.ParagraphCoordinates;
import org.wickedsource.docxstamper.api.coordinates.RunCoordinates;
import org.wickedsource.docxstamper.el.ExpressionResolver;
import org.wickedsource.docxstamper.el.ExpressionUtil;
import org.wickedsource.docxstamper.proxy.ProxyBuilder;
import org.wickedsource.docxstamper.proxy.ProxyException;
import org.wickedsource.docxstamper.replace.PlaceholderReplacer;
import org.wickedsource.docxstamper.util.CommentUtil;
import org.wickedsource.docxstamper.util.CommentWrapper;
import org.wickedsource.docxstamper.util.ParagraphWrapper;
import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker;
import org.wickedsource.docxstamper.util.walk.CoordinatesWalker;
/**
* Allows registration of ICommentProcessor objects. Each registered
* ICommentProcessor must implement an interface which has to be specified at
* registration time. Provides several getter methods to access the registered
* ICommentProcessors.
*/
public class CommentProcessorRegistry {
private Logger logger = LoggerFactory.getLogger(CommentProcessorRegistry.class);
private Map> commentProcessorInterfaces = new HashMap<>();
private List commentProcessors = new ArrayList<>();
private ExpressionResolver expressionResolver = new ExpressionResolver();
private ExpressionUtil expressionUtil = new ExpressionUtil();
private PlaceholderReplacer placeholderReplacer;
private boolean failOnInvalidExpression = true;
public CommentProcessorRegistry(PlaceholderReplacer placeholderReplacer) {
this.placeholderReplacer = placeholderReplacer;
}
public void setExpressionResolver(ExpressionResolver expressionResolver) {
this.expressionResolver = expressionResolver;
}
public void registerCommentProcessor(Class> interfaceClass,
ICommentProcessor commentProcessor) {
this.commentProcessorInterfaces.put(commentProcessor, interfaceClass);
this.commentProcessors.add(commentProcessor);
}
/**
* Lets each registered ICommentProcessor have a run on the specified docx
* document. At the end of the document the commit method is called for each
* ICommentProcessor. The ICommentProcessors are run in the order they were
* registered.
*
* @param document the docx document over which to run the registered ICommentProcessors.
* @param proxyBuilder a builder for a proxy around the context root object to customize its interface
* @param type of the contextRoot object.
*/
public void runProcessors(final WordprocessingMLPackage document, final ProxyBuilder proxyBuilder) {
final Map comments = CommentUtil.getComments(document);
CoordinatesWalker walker = new BaseCoordinatesWalker(document) {
@Override
protected void onParagraph(ParagraphCoordinates paragraphCoordinates) {
runProcessorsOnParagraphComment(document, comments, proxyBuilder, paragraphCoordinates);
runProcessorsOnInlineContent(proxyBuilder, paragraphCoordinates);
}
@Override
protected CommentWrapper onRun(RunCoordinates runCoordinates,
ParagraphCoordinates paragraphCoordinates) {
return runProcessorsOnRunComment(document, comments, proxyBuilder, runCoordinates,
paragraphCoordinates);
}
};
walker.walk();
for (ICommentProcessor processor : commentProcessors) {
processor.commitChanges(document);
}
}
/**
* Finds all processor expressions within the specified paragraph and tries
* to evaluate it against all registered {@link ICommentProcessor}s.
*
* @param proxyBuilder a builder for a proxy around the context root object to customize its interface
* @param paragraphCoordinates the paragraph to process.
* @param type of the context root object
*/
private void runProcessorsOnInlineContent(ProxyBuilder proxyBuilder,
ParagraphCoordinates paragraphCoordinates) {
ParagraphWrapper paragraph = new ParagraphWrapper(paragraphCoordinates.getParagraph());
List processorExpressions = expressionUtil
.findProcessorExpressions(paragraph.getText());
for (String processorExpression : processorExpressions) {
String strippedExpression = expressionUtil.stripExpression(processorExpression);
for (final ICommentProcessor processor : commentProcessors) {
Class> commentProcessorInterface = commentProcessorInterfaces.get(processor);
proxyBuilder.withInterface(commentProcessorInterface, processor);
processor.setCurrentParagraphCoordinates(paragraphCoordinates);
}
try {
T contextRootProxy = proxyBuilder.build();
expressionResolver.resolveExpression(strippedExpression, contextRootProxy);
placeholderReplacer.replace(paragraph, processorExpression, null);
logger.debug(String.format(
"Processor expression '%s' has been successfully processed by a comment processor.",
processorExpression));
} catch (SpelEvaluationException | SpelParseException e) {
if (failOnInvalidExpression) {
throw new UnresolvedExpressionException(strippedExpression, e);
} else {
logger.warn(String.format(
"Skipping processor expression '%s' because it can not be resolved by any comment processor. Reason: %s. Set log level to TRACE to view Stacktrace.",
processorExpression, e.getMessage()));
logger.trace("Reason for skipping processor expression: ", e);
}
} catch (ProxyException e) {
throw new DocxStamperException("Could not create a proxy around context root object", e);
}
}
}
/**
* Takes the first comment on the specified paragraph and tries to evaluate
* the string within the comment against all registered
* {@link ICommentProcessor}s.
*
* @param document the word document.
* @param comments the comments within the document.
* @param proxyBuilder a builder for a proxy around the context root object to customize its interface
* @param paragraphCoordinates the paragraph whose comments to evaluate.
* @param the type of the context root object.
*/
private void runProcessorsOnParagraphComment(final WordprocessingMLPackage document,
final Map comments, ProxyBuilder proxyBuilder,
ParagraphCoordinates paragraphCoordinates) {
Comments.Comment comment = CommentUtil
.getCommentFor(paragraphCoordinates.getParagraph(), document);
if (comment == null) {
// no comment to process
return;
}
String commentString = CommentUtil.getCommentString(comment);
for (final ICommentProcessor processor : commentProcessors) {
Class> commentProcessorInterface = commentProcessorInterfaces.get(processor);
proxyBuilder.withInterface(commentProcessorInterface, processor);
processor.setCurrentParagraphCoordinates(paragraphCoordinates);
}
CommentWrapper commentWrapper = comments.get(comment.getId());
try {
T contextRootProxy = proxyBuilder.build();
expressionResolver.resolveExpression(commentString, contextRootProxy);
CommentUtil.deleteComment(commentWrapper);
logger.debug(
String.format("Comment '%s' has been successfully processed by a comment processor.",
commentString));
} catch (SpelEvaluationException | SpelParseException e) {
if (failOnInvalidExpression) {
throw new UnresolvedExpressionException(commentString, e);
} else {
logger.warn(String.format(
"Skipping comment expression '%s' because it can not be resolved by any comment processor. Reason: %s. Set log level to TRACE to view Stacktrace.",
commentString, e.getMessage()));
logger.trace("Reason for skipping comment: ", e);
}
} catch (ProxyException e) {
throw new DocxStamperException("Could not create a proxy around context root object", e);
}
}
private CommentWrapper runProcessorsOnRunComment(final WordprocessingMLPackage document,
final Map comments, ProxyBuilder proxyBuilder, RunCoordinates runCoordinates,
ParagraphCoordinates paragraphCoordinates) {
Comments.Comment comment = CommentUtil.getCommentAround(runCoordinates.getRun(), document);
if (comment == null) {
// no comment to process
return null;
}
String commentString = CommentUtil.getCommentString(comment);
for (final ICommentProcessor processor : commentProcessors) {
Class> commentProcessorInterface = commentProcessorInterfaces.get(processor);
proxyBuilder.withInterface(commentProcessorInterface, processor);
processor.setCurrentParagraphCoordinates(paragraphCoordinates);
processor.setCurrentRunCoordinates(runCoordinates);
}
try {
T contextRootProxy = proxyBuilder.build();
CommentWrapper commentWrapper = comments.get(comment.getId());
try {
expressionResolver.resolveExpression(commentString, contextRootProxy);
logger.debug(
String.format("Comment '%s' has been successfully processed by a comment processor.",
commentString));
return commentWrapper;
} catch (SpelEvaluationException | SpelParseException e) {
if (failOnInvalidExpression) {
throw new UnresolvedExpressionException(commentString, e);
} else {
logger.warn(String.format(
"Skipping comment expression '%s' because it can not be resolved by any comment processor. Reason: %s. Set log level to TRACE to view Stacktrace.",
commentString, e.getMessage()));
logger.trace("Reason for skipping comment: ", e);
}
}
} catch (ProxyException e) {
throw new DocxStamperException("Could not create a proxy around context root object", e);
}
return null;
}
public boolean isFailOnInvalidExpression() {
return failOnInvalidExpression;
}
public void setFailOnInvalidExpression(boolean failOnInvalidExpression) {
this.failOnInvalidExpression = failOnInvalidExpression;
}
public void reset() {
for (ICommentProcessor processor : commentProcessors) {
processor.reset();
}
}
}