pjfanning opened a new pull request, #1243:
URL: https://github.com/apache/poi/pull/1243
Opening a .docx eagerly constructs an `XWPFParagraph` for every paragraph
and an `XWPFRun` for every run, so anything done per run in those constructors
is paid by every document. Three things were.
### `XWPFRun` ran an XPath query on every run
The constructor built a ~200-character query string by concatenation and
called `r.selectPath(...)` looking for
`./mc:AlternateContent/mc:Choice/w:drawing`, on every run — while almost every
real run is plain text with no picture content at all.
The two query strings are now `static final` constants (they were built from
compile-time constants, so they are byte-identical), and the whole
picture/drawing block is skipped unless the run can actually hold such content:
`sizeOfPictArray() > 0 || sizeOfDrawingArray() > 0`, or a direct
`mc:AlternateContent` child found by a plain DOM sibling scan.
The guard is a strict superset of the three sources that feed
`pictTextObjs`. The XPath is anchored at `./mc:AlternateContent`, i.e. direct
children only, and the DOM scan checks exactly those children by namespace and
local name — so the case where `selectPath` matches while both `sizeOf` counts
are zero still passes the guard. When the guard is false all three sources are
provably empty, so `pictureText` would have been `""` and both loops no-ops.
### `XWPFParagraph` walked every run's children with a cursor
After building the runs it opened an `XmlCursor` and ran
`selectPath("child::*")` over the children of every run to find `CTFtnEdnRef`
footnote references, which are rare. Checked against the generated schema:
`CTFtnEdnRef` occurs in `CTR` only as `w:footnoteReference` and
`w:endnoteReference`, so the walk is now skipped when the run has neither.
The cursor walk itself is deliberately unchanged rather than replaced with
the typed accessors, because `getFootnoteReferenceArray()` +
`getEndnoteReferenceArray()` would group all footnote refs before all endnote
refs and change the append order for a run that mixes both.
### `XWPFTable` parsed every cell twice
`processCTRow` constructed a full `XWPFParagraph` — and therefore all its
`XWPFRun`s — for every paragraph of every cell, purely to precompute the `text`
field, then discarded the objects. But the line above it already did `new
XWPFTableRow(row, this)`, which builds an `XWPFTableCell` per cell, each of
which already builds an `XWPFParagraph` per paragraph. Every table cell was
being parsed twice on open. It now reads the paragraphs from the row it just
built.
Same set and order (`XWPFTableRow` uses `ctRow.getTcArray()`; a cell's
paragraphs are its direct `w:p` children in document order), and both paragraph
sets resolve to the same `XWPFDocument`, so `getText()` is unchanged. The
drawing-id reservations that the discarded objects performed were duplicates of
ones the cell paragraphs had already made.
I did not make `getText()` lazy: `text` is captured at construction today
and never updated by `addRow`/`createRow`/`setText`, so a lazy version would
start reflecting later mutations, and `text` is a `protected` field an
out-of-tree subclass could read.
All 313 `org.apache.poi.xwpf.*` tests pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]