apache / apache/poi

XWPFDocument#setParagraph causes inconsistency between bodyElements and paragraphs

Open
#980 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
2.3k
Forks
844
Avg merge
5h 52m
Merged PRs (30d)
47

Description

### Description
Calling `XWPFDocument#setParagraph(XWPFParagraph paragraph, int pos)` may cause
an inconsistency between the internal `bodyElements` and `paragraphs` lists.

After calling `setParagraph`, the element stored at the same position in
`bodyElements` and `paragraphs` may no longer refer to the same paragraph
instance.

### Impact
This inconsistency breaks `XWPFDocument#removeBodyElement(int pos)`.

`removeBodyElement` relies on `getParagraphPos(int bodyPos)` to locate the
corresponding paragraph index. When the paragraph was previously replaced via
`setParagraph`, `getParagraphPos` may return `-1`, causing
`paragraphs.remove(paraPos)` to fail with an exception.

### Root Cause Analysis
In `setParagraph`, two different update mechanisms are used:

* The `paragraphs` list is updated via `ArrayList#set`, directly replacing the
paragraph reference.
* The underlying XML (`CTDocument`) is updated via
`ctDocument.getBody().setPArray(...)`.

During XML processing, the generated XMLBeans code eventually calls
`XObj.copy_contents_from`, which **copies** the XML contents instead of
reusing the existing `CTP` / `XWPFParagraph` instance.

As a result, the paragraph object referenced by `paragraphs` differs from the
one created and stored in `bodyElements`, leading to inconsistent internal
state.

### Steps to Reproduce
A sample DOCX file is attached.

```java
public static void main(String[] args) throws IOException {
FileInputStream fis =
new FileInputStream("test_1989242873218412545.docx");

try (XWPFDocument document = new XWPFDocument(fis)) {
List paragraphs = document.getParagraphs();
document.setParagraph(paragraphs.get(5), 6);

// For debugging: inspect internal state after setParagraph
System.out.println("--");
}
}
Expected Behavior

After calling setParagraph, the internal bodyElements and paragraphs
collections should remain consistent, and subsequent calls to
removeBodyElement should work correctly.

Actual Behavior

bodyElements and paragraphs become inconsistent, causing
removeBodyElement to fail when removing a paragraph.

Additional Information

I have identified the cause and implemented a local fix.
A Pull Request will be submitted shortly.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with XWPFDocument#setParagraph, then inspect how it interacts with the bodyElements and paragraphs collections and with removeBodyElement(int pos) and getParagraphPos(int bodyPos). Reproduce the issue using the attached DOCX and the supplied call sequence; done means the collections remain consistent after replacement and removeBodyElement succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.