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]

Reply via email to