Skip to content

Commit

Permalink
Also replace placeholders in header/footer
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonOellerer committed Sep 29, 2022
1 parent 9c76817 commit 2d10260
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 11 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group 'com.docutools'
version = '1.5.3'
version = '1.5.4'

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ protected Path generate() throws IOException {
LocaleUtil.setUserLocale(locale);
logger.info("Set user locale to {}", locale);

List<IBodyElement> bodyElements = new ArrayList<>(document.getBodyElements().size());
List<IBodyElement> bodyElements = new ArrayList<>(document.getBodyElements().size() + document.getHeaderList().size());
bodyElements.addAll(document.getBodyElements());
bodyElements.addAll(document.getHeaderList().stream().flatMap(xwpfHeader -> xwpfHeader.getBodyElements().stream()).toList());
bodyElements.addAll(document.getFooterList().stream().flatMap(xwpfFooter -> xwpfFooter.getBodyElements().stream()).toList());

logger.debug("Retrieved all body elements, starting WordGenerator");
WordGenerator.apply(resolver, bodyElements, options);
Expand Down
117 changes: 108 additions & 9 deletions src/main/java/com/docutools/jocument/impl/word/WordUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import org.apache.logging.log4j.Logger;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFFooter;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
Expand Down Expand Up @@ -78,7 +80,7 @@ public static void replaceText(XWPFParagraph paragraph, String newText) {
* @return {@code true} when exists
*/
public static boolean exists(IBodyElement element) {
return findPos(element).orElse(-1) != -1;
return findPositionInBody(element).orElseGet(() -> findPositionInHeader(element).orElseGet(() -> findPositionInFooter(element).orElse(-1))) != -1;
}

/**
Expand All @@ -87,12 +89,22 @@ public static boolean exists(IBodyElement element) {
* @param element the element
* @return the index
*/
public static OptionalInt findPos(IBodyElement element) {
public static OptionalInt findPositionInBody(IBodyElement element) {
var document = element.getBody().getXWPFDocument();
if (element instanceof XWPFParagraph xwpfParagraph) {
return OptionalInt.of(document.getPosOfParagraph(xwpfParagraph));
var position = document.getPosOfParagraph(xwpfParagraph);
if (position >= 0) {
return OptionalInt.of(position);
} else {
return OptionalInt.empty();
}
} else if (element instanceof XWPFTable xwpfTable) {
return OptionalInt.of(document.getPosOfTable(xwpfTable));
var position = document.getPosOfTable(xwpfTable);
if (position >= 0) {
return OptionalInt.of(position);
} else {
return OptionalInt.empty();
}
}
logger.warn("Failed to find position of element {}", element);
return OptionalInt.empty();
Expand All @@ -108,7 +120,7 @@ public static OptionalInt findPos(IBodyElement element) {
public static List<IBodyElement> copyBefore(List<IBodyElement> elements, IBodyElement destination) {
return elements.stream()
.map(element -> copyBefore(element, destination))
.collect(Collectors.toList());
.toList();
}

/**
Expand Down Expand Up @@ -138,8 +150,95 @@ public static IBodyElement copyBefore(IBodyElement element, IBodyElement destina
*/
public static void removeIfExists(IBodyElement element) {
logger.debug("Removing element {}", element);
findPos(element)
.ifPresent(element.getBody().getXWPFDocument()::removeBodyElement);
var document = element.getBody().getXWPFDocument();
OptionalInt position = findPositionInBody(element);
if (position.isPresent()) {
document.removeBodyElement(position.getAsInt());
} else {
if (element instanceof XWPFParagraph xwpfParagraph) {
var positionInHeader = findPositionInHeader(xwpfParagraph, document.getHeaderList());
if (positionInHeader.isPresent()) {
document.getHeaderArray(positionInHeader.getAsInt()).removeParagraph(xwpfParagraph);
} else {
var positionInFooter = findPositionInFooter(xwpfParagraph, document.getFooterList());
positionInFooter.ifPresent(integer -> document.getHeaderArray(integer).removeParagraph(xwpfParagraph));
}
} else if (element instanceof XWPFTable xwpfTable) {
var positionInHeader = findPositionInHeader(xwpfTable, document.getHeaderList());
if (positionInHeader.isPresent()) {
document.getHeaderArray(positionInHeader.getAsInt()).removeTable(xwpfTable);
} else {
var positionInFooter = findPositionInFooter(xwpfTable, document.getFooterList());
positionInFooter.ifPresent(integer -> document.getHeaderArray(integer).removeTable(xwpfTable));
}
}
}
}

private static OptionalInt findPositionInHeader(IBodyElement element) {
if (element instanceof XWPFParagraph xwpfParagraph) {
return findPositionInHeader(xwpfParagraph, element.getBody().getXWPFDocument().getHeaderList());
} else if (element instanceof XWPFTable xwpfTable) {
return findPositionInHeader(xwpfTable, element.getBody().getXWPFDocument().getHeaderList());
}
return OptionalInt.empty();
}

private static OptionalInt findPositionInHeader(XWPFParagraph xwpfParagraph, List<XWPFHeader> headerList) {
var i = 0;
for (XWPFHeader xwpfHeader : headerList) {
for (XWPFParagraph paragraph : xwpfHeader.getParagraphs()) {
if (xwpfParagraph.equals(paragraph)) {
return OptionalInt.of(i);
}
}
}
return OptionalInt.empty();
}

private static OptionalInt findPositionInHeader(XWPFTable xwpfTable, List<XWPFHeader> headerList) {
var i = 0;
for (XWPFHeader xwpfHeader : headerList) {
for (XWPFTable table : xwpfHeader.getTables()) {
if (xwpfTable.equals(table)) {
return OptionalInt.of(i);
}
}
}
return OptionalInt.empty();
}

private static OptionalInt findPositionInFooter(IBodyElement element) {
if (element instanceof XWPFParagraph xwpfParagraph) {
return findPositionInFooter(xwpfParagraph, element.getBody().getXWPFDocument().getFooterList());
} else if (element instanceof XWPFTable xwpfTable) {
return findPositionInFooter(xwpfTable, element.getBody().getXWPFDocument().getFooterList());
}
return OptionalInt.empty();
}

private static OptionalInt findPositionInFooter(XWPFParagraph xwpfParagraph, List<XWPFFooter> footerList) {
var i = 0;
for (XWPFFooter xwpfFooter : footerList) {
for (XWPFParagraph paragraph : xwpfFooter.getParagraphs()) {
if (xwpfParagraph.equals(paragraph)) {
return OptionalInt.of(i);
}
}
}
return OptionalInt.empty();
}

private static OptionalInt findPositionInFooter(XWPFTable xwpfTable, List<XWPFFooter> footerList) {
var i = 0;
for (XWPFFooter xwpfFooter : footerList) {
for (XWPFTable table : xwpfFooter.getTables()) {
if (xwpfTable.equals(table)) {
return OptionalInt.of(i);
}
}
}
return OptionalInt.empty();
}

/**
Expand Down Expand Up @@ -231,7 +330,7 @@ public static Collection<Locale> detectLanguages(XWPFDocument document) {
.distinct()
.map(Locale::forLanguageTag)
.filter(WordUtilities::isValid)
.collect(Collectors.toList());
.toList();
}

/**
Expand Down Expand Up @@ -325,7 +424,7 @@ private static void cloneTable(XWPFTable original, XWPFTable clone) {
ctTcPr = ctTc.addNewTcPr();
}
ctTcPr.set(cell.getCTTc().getTcPr());
if (newCell.getParagraphs().size() > 0) {
if (!newCell.getParagraphs().isEmpty()) {
// The new cell might be created with empty paragraphs which we do not need, so we remove them here
IntStream.range(0, newCell.getParagraphs().size()).forEach(value -> newCell.removeParagraph(0));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.docutools.jocument.impl.word;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
Expand Down Expand Up @@ -362,4 +363,44 @@ void shouldProcessDocumentWithTOC() throws IOException, InterruptedException {
var documentWrapper = new XWPFDocumentWrapper(xwpfDocument);
assertThat(documentWrapper.bodyElement(0).asParagraph().runs(), hasSize(1));
}

@Test
@DisplayName("Placeholder in Header")
void shouldReplacePlaceholderInHeader() throws IOException, InterruptedException {
// Assemble
Template template = Template.fromClassPath("/templates/word/HeaderTemplate.docx")
.orElseThrow();
PlaceholderResolver resolver = new ReflectionResolver(SampleModelData.PICARD_PERSON);

// Act
Document document = template.startGeneration(resolver);
document.blockUntilCompletion(60000L); // 1 minute

// Assert
assertThat(document.completed(), is(true));
xwpfDocument = TestUtils.getXWPFDocumentFromDocument(document);
var headerList = xwpfDocument.getHeaderList();
assertThat(headerList.get(0).getText(), containsString("Jean-Luc Picard"));
assertThat(headerList.get(0).getText(), containsString("23.09.1948"));
}

@Test
@DisplayName("Placeholder in Footer")
void shouldReplacePlaceholderInFooter() throws IOException, InterruptedException {
// Assemble
Template template = Template.fromClassPath("/templates/word/FooterTemplate.docx")
.orElseThrow();
PlaceholderResolver resolver = new ReflectionResolver(SampleModelData.PICARD_PERSON);

// Act
Document document = template.startGeneration(resolver);
document.blockUntilCompletion(60000L); // 1 minute

// Assert
assertThat(document.completed(), is(true));
xwpfDocument = TestUtils.getXWPFDocumentFromDocument(document);
var footerList = xwpfDocument.getFooterList();
assertThat(footerList.get(0).getText(), containsString("Jean-Luc Picard"));
assertThat(footerList.get(0).getText(), containsString("23.09.1948"));
}
}
Binary file not shown.
Binary file not shown.

0 comments on commit 2d10260

Please sign in to comment.