Office-stamper now supports arbitrary nesting depth for repeated document sections. A 13-line recursive fix lets
templates mirror complex domain models (school → grades → classes → students) without data flattening. Includes
comprehensive tests and works with existing templates.
Commit: 0a774c1
Why this stands out
Imagine generating a school report where each grade needs a list of classes, and each class needs a table of students. Before this commit, office-stamper would silently break at the second level of nesting.
note as n
School
|_ Grade 1
|_ Class A
|_ Student 1
|_ Student 2
|_ ...
|_ Class B
|_ Student 3
|_ Student 4
|_ ...
|_ Grade 2
|_ Class C
|_ ...
end note
repeatDocPart would fail when comment wrappers nested—the processor couldn’t correctly extract child comments into the
sub-template. The processor would extract only the immediate child comment, orphaning deeper comments and causing
rendering to skip nested levels entirely.
This commit introduces recursive comment extraction, unlocking true hierarchical document generation.
From an Agile perspective, this delivers a complete vertical slice: the feature works for arbitrary nesting depth, comes with a comprehensive multi-level test (school → grade → class → student), and required only a 13-line change to production code. That’s high leverage.
What changed
The core change is in RepeatDocPartProcessor.extractSubTemplate():
Before: Only direct children of a comment wrapper were extracted:
private void extractedSubComments(CommentWrapper commentWrapper, Comments comments) {
commentWrapper
.getChildren()
.forEach(comment -> comments.getComment()
.add(comment.getComment()));
}
After: Recursively extract all descendant comments:
private void extractedSubComments(CommentWrapper commentWrapper, Comments comments) {
for (CommentWrapper child : commentWrapper.getChildren()) {
comments.getComment()
.add(child.getComment());
if (CollectionUtils.isEmpty(child.getChildren())) {
continue;
}
extractedSubComments(child, comments); // Recurse
}
}
This depth-first traversal ensures that when a sub-template is stamped, it contains all the comment metadata it needs to process its own nested repeats.
The test tells the story
The RepeatDocPartNestingTest exemplifies test-as-specification. It models a realistic hierarchy:
- School contains 3 grades
- Each Grade contains 3 classes
- Each Class contains 5 students
- Students are rendered in a table with number, name, and age columns The test walks the generated document and validates:
- Correct paragraph counts at each level
- Proper text interpolation (“Grade No.2 there are 3 classes”)
- Table structure preservation (3 cells per student row) This isn’t a toy example; it mirrors real enterprise scenarios like org charts, BOM hierarchies, or multi-level approval matrices.
Agile/craftsmanship/docs-as-code lens
-
Small batches, big impact: The production change is 13 lines. The test infrastructure (context classes, fixtures) is larger, but that’s the right ratio—tests define the contract and protect against regression.
-
Recursive simplicity: Instead of managing explicit stacks or breadth-first queues, the code leverages a natural recursion. The call stack is the traversal state. This aligns with the WordprocessingML structure (comments form a tree) and keeps the cognitive load low.
-
Documentation through fixtures: The RepeatDocPartNestingTest.docx template itself is documentation. Future maintainers can open it in Word, see the comment structure, and understand the feature without parsing code.
Solo maintainer + enterprise usage angle
For a solo maintainer, recursion is a double-edged sword—it is elegant but can hide stack-overflow risks. The mitigation
here is structural: WordprocessingML comments have finite depth (typically 2–4 levels in practice), and the Spring
CollectionUtils.isEmpty() guard prevents infinite loops on malformed trees.
For enterprise users, this removes a major adoption blocker. Before, nested repeats required flattening the data model or splitting templates. Now, the document structure can mirror the domain model directly. That reduces impedance mismatch between business stakeholders (who think hierarchically) and template authors.
Risks and mitigations
Risk: Stack overflow on pathological templates with deeply nested comments.
Mitigation: WordprocessingML comments rarely exceed 5–6 levels; modern JVMs handle this easily. Consider adding a depth guard if telemetry shows abuse.
Risk: Comment extraction order matters if processors have side effects.
Mitigation: The depth-first traversal is deterministic and matches document order. Side effects should be avoided in processors, but if needed, they’ll fire predictably.
Risk: Performance on wide trees (e.g., 100 children per level).
Mitigation: The recursion is linear in the number of comments. Even 1000 nested comments will complete in milliseconds.
Quick Start: Nested Repeats in 3 Steps
Step 1: Nest Your Comments in Word
Open your template, add a comment around the outer loop, then add child comments for inner loops. Name them
descriptively (repeatGrade, repeatClass).
Step 2: Structure Your Context Object
record School(List grades) {}
record Grade(int number, List classes) {}
record AClass(String name, List students) {}
Step 3: Stamp It
stamper.stampAndLoad(template, new School(grades));
References
- Commit: 0a774c1
- Issue: Issue #92 · verronpro/office-stamper.
- PR: Pull Request #100 · verronpro/office-stamper.
- Contributors: NoNException, zongzi