Support Excel trailing-comma scaling in `DataFormatter` number formatting. In Excel, trailing commas in a number format string mean "divide by 1000 per comma" (e.g. `#,##0,` formats 1,000,000 as "1,000"). Currently the formatter passes the pattern directly to `java.text.DecimalFormat`, which treats trailing commas as literal commas, producing incorrect output.

The fix should:
- detect trailing commas in the format pattern,
- strip them to create a valid pattern for `DecimalFormat`,
- scale the numeric value by dividing by 1000 for each trailing comma,
- apply the standard format (including locale-specific grouping/decimal separators and Excel rounding mode) to the scaled value.

This must work for any locale and for patterns with or without a fractional part (e.g. `#,##0.0,`), and for multiple trailing commas (millions, billions, etc.). Edge cases: patterns with no trailing commas should behave exactly as before; the scaling divisor should be a power of 1000.

[github-25] support excel number trailing comma format - Thabks to Luca Martini for proving the patch


**Title:** Slide import deletes unrecognized XML elements inside group shapes

**Description**
When a slide containing group shapes is imported into another presentation via `XSLFSheet.importContent`, properties that were applied directly to the underlying XML beans (properties not exposed through high-level POI setters) are silently removed from shapes inside groups.

The most common trigger is an `XSLFAutoShape` inside an `XSLFGroupShape` that has a shadow, glow, or similar effect set by directly manipulating the `CTShape` obtained through `getXmlObject()`. After calling `importContent`, the `<a:effectLst>` element and its children are missing from the imported shape, which means the visual effect is lost.

**Steps to reproduce**
1. Create a slide and add a group shape.
2. Inside the group, add an `XSLFAutoShape` and apply a shadow effect through low-level `CTShape` manipulation (e.g., `csh.getSpPr().addNewEffectLst()…`).
3. Import the slide into a target presentation via `XSLFSlide.importContent`.
4. Inspect the imported group shape’s child – the `<a:effectLst>` element is gone.

**Expected behavior**
`importContent` (and any path that copies slides or shapes) should preserve the complete XML content of every shape, including elements that are not explicitly modeled by POI’s Java wrappers. No data should be silently dropped.

**Observations / hints**
- The problem does **not** occur for top-level shapes on a slide; it is specific to shapes nested inside an `XSLFGroupShape`.
- The root cause appears to be that the group’s internal copy logic destroys and recreates child shapes afresh, then copies only the high-level properties. Any low-level XML additions that are unknown to POI are left behind.
- The fix should ensure that when the source and target already contain the same number of children (as happens during `importContent`), the existing child shapes are updated in place rather than being cleared and rebuilt, so that unrecognized XML elements survive.

#60662 - Slide import delete unrecognized elements in group shape


**Title:** Add `XSSFReader.getSheetIterator()` to avoid downcasting

**Background**
`XSSFReader.getSheetsData()` returns `Iterator<InputStream>`. The actual runtime object is `XSSFReader.SheetIterator`, which provides useful methods like `getSheetName()`, `getSheetComments()`, and `getShapes()`. Users who need those methods are forced to perform a fragile, undocumented downcast. The subclass `XSSFBReader` currently overrides `getSheetsData()` to return its own `SheetIterator`.

**Request**
1. Add a new public method `getSheetIterator()` to `XSSFReader` that returns `SheetIterator` directly, so callers can access sheet metadata without casting.
2. Have the existing `getSheetsData()` delegate to the new method so backward compatibility is preserved and no existing code breaks.
3. Override `getSheetIterator()` in `XSSFBReader` instead of (or in addition to) `getSheetsData()`, so the binary-format reader exposes the same concrete return type.
4. Update any internal call sites (examples, tests) that currently downcast the result of `getSheetsData()` to use the new method instead.
5. While touching the examples, ensure the `InputStream` objects returned by the iterator are properly closed to avoid resource-leak warnings.


```
Title: DeferredSXSSFWorkbook.removeSheetAt throws NullPointerException and/or leaves removed sheet in output

Description:
Removing a sheet from a `DeferredSXSSFWorkbook` via `removeSheetAt(int)` is broken in two ways:

1. It frequently throws a `NullPointerException` because the internal sheet-deregistration logic assumes the streaming wrapper and its data writer are always non-null, even when the writer has not been initialized (common for deferred sheets with row generators that haven't been flushed yet).

2. Even when no exception is thrown, the removed sheet may still appear in the saved XLSX output, or the data of remaining sheets may be corrupted/absent. The problem seems to be that `DeferredSXSSFWorkbook`’s custom `sheetIterator()` does not correctly reflect sheet removals that occur during the workbook’s lifetime. During save, this iterator is used to generate the XML for each sheet, so an out-of-date view causes the removed sheet’s content to be included or to interfere with normal output.

**Steps to reproduce (conceptual):**
- Create a `DeferredSXSSFWorkbook`.
- Add two sheets with row generators that write distinct cell values.
- Call `removeSheetAt(0)`.
- Write the workbook to a stream and read it back as an `XSSFWorkbook`.
- Observe that either an NPE occurs, or the reopened workbook still contains the removed sheet, or the expected data from the surviving sheet is missing.

**Expected behavior:**
- `removeSheetAt` completes without exceptions, even when the sheet’s streaming writer hasn’t been used yet.
- After removal and saving, the output file must *not* contain the removed sheet. The remaining sheet(s) must contain exactly the data produced by their row generators.
- Any internal iteration over sheets (e.g., during saving) must respect sheet removals, returning only the currently-active sheets.
- Cleanup of any temporary resources associated with the removed sheet should be safe when those resources haven’t been written to (i.e., handle null writers gracefully).

**Additional notes:**
- `SXSSFWorkbook` (the non-deferred parent) does not exhibit the same problem; the issue is likely in how `DeferredSXSSFWorkbook` overrides sheet iteration and in the base class’s `deregisterSheetMapping` method.
- The fix should ensure that `DeferredSXSSFWorkbook`’s sheet iterator delegates to the same robust mechanism used by `SXSSFWorkbook` and that any sheet-mapping cleanup is null-safe.
```


```
Add support for the Excel SQRTPI function in the Analysis ToolPak.

The SQRTPI function takes a single numeric argument and returns sqrt(pi * number). Currently, the function is not implemented, so evaluating it in a workbook returns an error. Please implement the function to return the square root of (Math.PI * argument), with the following error handling:

- If the argument is negative, return a #NUM! error.
- If the number of arguments is not exactly 1, return a #VALUE! error.
- If the argument is non-numeric and cannot be coerced to a number, return the appropriate error (typically #VALUE!).

The implementation should follow the same patterns used by other simple numeric ATP functions (e.g., SIGN, SINGLE), and should be registered in the AnalysisToolPak registry so that it is callable from formulas.
```


Apply the project's zip bomb guardrails to the encrypted-temp-zip path.

Right now `AesZipFileZipEntrySource` processes a decrypted ZIP stream by wrapping it in a bare `ZipArchiveInputStream`, skipping the `ZipArchiveThresholdInputStream` used everywhere else. This means a malicious encrypted OOXML file can embed an entry with a tiny compressed size that blows up into a huge payload during temp-zip repackaging, completely bypassing the `ZipSecureFile` limits (min inflate ratio, max entry size) that protect normal OOXML processing.

Make the encrypted temp-zip path honour those same limits. An encrypted file that triggers zip-bomb detection should produce an `IOException` describing the violation (i.e., the same rejection behavior that unencrypted files already get), and the existing `ZipSecureFile` threshold settings must be respected.

Apply zip bomb checks to encrypted temp ZIP processing (#1057)


**Title:** T.DIST (left-tail) formula ignores the `cumulative` flag

**Affected function:** `T.DIST` – the left-tailed Student’s t-distribution (3‑argument variant).

**Symptom:**
The implementation always computes the right‑tail probability `P(|X| ≥ x)` (via `Math.abs(x)`) regardless of the third argument (`cumulative`). This makes `T.DIST` behave like the legacy `TDIST` rather than the correct left‑tail distribution.

**Expected behaviour:**
- When `cumulative = TRUE`, return the cumulative distribution function `P(X ≤ x)`.
- When `cumulative = FALSE`, return the probability density function at `x`.
- Both must use the supplied degrees of freedom (integer `≥ 1`; otherwise `#NUM!`).
- Non‑numeric or missing arguments should produce `#VALUE!` as usual.

**Constraints / edge cases:**
- The computation must work for negative `x` values as well (the CDF at negative `x` is not simply `1 – P(X ≤ |x|)`; it should follow the standard t‑distribution CDF).
- The precision of the returned results should be high (close to machine accuracy, not the loose 0.01 tolerance currently used in several related tests).
- Do **not** break the existing `TDIST`, `T.DIST.RT`, or `T.DIST.2T` implementations; they share some internal helpers but this fix is restricted to the left‑tail variant.

**Hints / existing references:**
- The left‑tail implementation lives in the class that handles `T.DIST` (not the legacy `TDIST`).
- There are already unit tests for related functions (`TestTDist*`) that can serve as a guide for acceptable accuracy and error‑handling patterns.

get some T.DIST tests to work (still not right on cumulative=false)


In the `XDDFErrorBars` chart API, `getErrorBarType()` and `getErrorValueType()` throw a `NullPointerException` when called on an instance that has not yet had those properties set (e.g., immediately after construction). The `getErrorDirection()` method already handles this situation gracefully by returning `null`. Make the other two getters consistent: return `null` instead of throwing when the underlying OOXML element is absent. Verify that a freshly constructed `XDDFErrorBars` object returns `null` for all three property getters (`getErrorBarType()`, `getErrorValueType()`, `getErrorDirection()`).

try to fix issue with missing classes in poi-ooxml-schemas


```
Add support for Excel's CORREL function in the formula evaluator.

The function CORREL(array1, array2) is currently not implemented; the
function-mapping table contains a commented-out entry for index 307. As a
result any spreadsheet formula that uses CORREL triggers a #NAME? error.

Implement the function so that it accepts two arguments (each may be a cell
range, area reference, 3D reference, or inline array) and returns the
Pearson correlation coefficient as a number. The implementation must match
standard Excel behaviour:

- Blank cells are treated as 0.
- String cells are coerced to a number if possible; otherwise they cause a
  #VALUE! error.
- Error values in the supplied data (e.g. #DIV/0!) propagate.
- The two arrays must contain the same number of data points; if they
  differ, an appropriate error (typically #N/A) is produced.

Register the function in the existing function-mapping table and ensure it
follows the same conventions (singleton instance, Fixed2ArgFunction, etc.)
used by the other statistical functions already present in the codebase.

A reference example from the Microsoft documentation: with Data1 = {3,2,4,5,6}
and Data2 = {9,7,12,15,17}, the formula =CORREL(A2:A6,B2:B6) should yield
approximately 0.997054.
```


```
OOXML schema lookup uses wrong classloader under OSGi

`POIXMLTypeLoader` determines the `SchemaTypeLoader` by either an
externally‑provided `ClassLoader` (via `setClassLoader`) or the thread’s
context classloader. In environments like OSGi, the context classloader
often does not have access to the OOXML schema classes, causing schema
type lookup to fail.  Additionally, the externally‑set classloader
approach is fragile and introduces unnecessary state.

Change the implementation so the classloader used to obtain the
`SchemaTypeLoader` is derived from the `SchemaType` argument itself
(i.e. `type.getClass().getClassLoader()`).  The thread‑local caching
should persist a `SchemaTypeLoader` per thread, but it must be built
from the `SchemaType`’s classloader rather than from an external or
context loader.

As part of this change, deprecate `POIXMLTypeLoader.setClassLoader`
and turn it into a no‑op.  The method is no longer needed and will be
removed in a future version.

The fix must work correctly when the thread’s context classloader is a
parent classloader that does not contain the OOXML classes.  Thread
safety and the existing per‑thread caching semantics must be preserved.
```

#61478 - POI OOXML-Schema lookup uses wrong classloader


```
Title: Support highlight (background) color for text runs in XSLF (PPTX)

Description:
Currently `XSLFTextRun` does not expose an API for the highlight (background) color of a text run, and the rendering path in `DrawTextParagraph` does not apply any highlight when building the attributed string. As a result, even if a highlight is defined in the underlying OOXML markup, it is silently ignored when the slide is drawn.

This request is to add proper highlight-color support for XSLF text runs:

1. **Public API**
   - Add `getHighlightColor()` and `setHighlightColor()` methods to `XSLFTextRun`.
   - The getter should return the highlight as a `PaintStyle` (or null if not set).
   - The setter should accept both a `java.awt.Color` convenience overload and a `PaintStyle`.
   - Only `SolidPaint` highlights are supported. Passing a non-`SolidPaint` `PaintStyle` (e.g., gradient or texture) should throw an `IllegalArgumentException`.
   - Setting the highlight to `null` should remove the highlight property from the underlying text-run properties.

2. **Rendering**
   - When `DrawTextParagraph` renders a text run, if the run is an instance of `HighlightColorSupport` (or equivalent) and has a non-null highlight, the resulting `AttributedString` must set `TextAttribute.BACKGROUND` to the resolved `java.awt.Paint`.
   - Runs without a highlight must not have the background attribute set, so previous rendering behavior for non-highlighted text remains unchanged.

3. **Format scope**
   - This feature is for XSLF (PPTX) only. The HSLF (PPT) text runs do not need to be changed, but any common interface introduced should be implemented only where appropriate.

The expected behavior: after setting a highlight color on an `XSLFTextRun`, rendering the slide via `Slide.draw(Graphics2D)` must paint the text background with that color. The API must round-trip correctly when reading an existing slide and calling get/set.
```

[github-409] Textruns not honouring highlight. Thans to gffloodg. This closes #409


**Feature Request: Support for nested Group Shapes and ShapeContainer implementation in XSSFShapeGroup**

**Summary**
Currently, `XSSFShapeGroup` cannot contain another group shape; there is no public method to add a sub‑group inside a group. Additionally, `XSSFShapeGroup` does not implement `ShapeContainer<XSSFShape>`, making it impossible to iterate directly over its child shapes or use it with enhanced for loops. The anchor / transformation settings for nested groups are also missing, so any attempt to manually create a nested group would produce incorrect positioning.

**Desired Behavior**

1. **Nested groups** – `XSSFShapeGroup` should expose a `createGroup(XSSFChildAnchor)` method, analogous to the existing `createSimpleShape(XSSFChildAnchor)`. This method must:
   - Create a new `XSSFShapeGroup` as a child of the current group.
   - Be associated with the same drawing as the parent group.
   - Set the child anchor correctly so that the nested group’s position and size are relative to the parent group’s client area.

2. **Anchor / transform handling** – When a nested group is created, the group’s transformation (`off`, `ext`, `chOff`, `chExt`) should be derived from the child anchor. The offset and extent should match the anchor’s offset and extent, while the child offset should be set to zero and the child extents equal to the extents, ensuring correct relative positioning.

3. **Container contract** – `XSSFShapeGroup` should implement `ShapeContainer<XSSFShape>`:
   - `iterator()` must return an iterator over all direct child shapes of the group (delegating to the drawing’s existing `getShapes(XSSFShapeGroup)`).
   - `getShapeName()` should return the group’s non‑visual drawing property name, consistent with other shape types.

**Motivation**
Users need to build complex diagrams with multiple levels of grouping (e.g., a group containing several shapes and another sub‑group). Without this support, the API forces all groups to be at the top level of a drawing, severely limiting composition. The missing `ShapeContainer` interface also prevents treating a group as a standard iterable container, breaking patterns used across the library.

#60521 - Enable nested XSSF GroupShapes and fix calculation of Client-/ChildAnchor


**Title:** SXSSFWorkbook custom `write` path leaks plaintext workbook bytes when using encrypted sheet temp files

**Background**
Users can extend `SXSSFWorkbook` and override `write(OutputStream)` to encrypt the sheet-data temporary files on disk (via a custom `SheetDataWriter` and an `AesZipFileZipEntrySource`).  The goal is a “fully secure disk environment” where the final workbook can optionally be encrypted and no unencrypted data ever touches disk.

**Problem**
In the overridden `write` flow, after sheet temp files are flushed, the underlying `XSSFWorkbook` is serialised into a `ByteArrayOutputStream`.  That plaintext byte array is then passed to `AesZipFileZipEntrySource.createZipEntrySource(InputStream)` before calling `injectData`.  For large workbooks this can be a memory pressure point, but more critically, if any part of the pipeline spills to disk or if the stream is later persisted, plaintext workbook data may appear outside the intended encrypted envelope – violating the “no unencrypted data on disk” invariant.

**Desired behaviour**
When the custom write path is used, the serialised workbook bytes must also be written through an AES-encrypted temporary file on disk, and that encrypted file fed into the `ZipEntrySource` for injection.  After the write completes, the encrypted temporary file must be reliably deleted.  This ensures that any data that could end up on disk during the write operation is encrypted.

**Additional contract refinements**
1. `ZipEntrySource` should extend `java.io.Closeable` so that it can be used with resource-management utilities without casting.
2. Implementations of `ZipEntrySource.close()` must be idempotent (safe to call more than once), cleaning up underlying resources and deleting temporary files only on the first call.

**Acceptance criteria (behavioural)**
- Using the custom `SXSSFWorkbook` subclass that writes encrypted sheet temp files, calling `write(OutputStream)` must not produce any plaintext workbook data in a temporary file on disk.
- The written output must still be a valid OOXML workbook that can be read back.
- No resource leaks or double-close exceptions must occur when `close()` is invoked more than once.
- The final workbook may optionally be encrypted (the consumer can serialize the output stream to an encrypted file), but the intermediate encryption mechanism for the workbook data must be independent and not interfere with the final stream content.

bug 60153: patch from PJ Fanning to demonstrate that SXSSFWorkbook SheetDataWriter can write encrypted temporary files to disk and the workbook can be AES encrypted when written to disk for a fully secure disk environment


As a user of `TempFile`, I often need to create temporary files in a specific directory for the duration of a single logical operation, while other parts of the application (or other threads) continue using the normal location. Today I can call `setThreadLocalTempFileCreationStrategy` beforehand and restore it manually afterwards, but that requires a tedious try/finally block and is easy to get wrong, especially when the operation might throw exceptions or call other code that also temporarily changes the strategy.

I’d like a scoped convenience method on `TempFile` that lets me temporarily install a different `TempFileCreationStrategy` for the current thread for the duration of a supplied Runnable / Supplier, and that **guarantees** the previous thread-local strategy (or the lack of one) is restored when the block completes — whether it returns normally or throws an exception.

Specifically:

- The method should accept a `TempFileCreationStrategy` and a `Supplier<R>` (or similar functional interface) that represents the code to execute with that strategy active.
- While the supplied code runs, the given strategy must be the active *thread-local* strategy; the global default strategy must not be changed.
- After the supplied code finishes (normally or by throwing), the previous thread-local strategy (which could be `null`, a previously set value, or a value installed by an outer nested call) must be restored.
- The method must return whatever value the provided code produces.
- The method must reject `null` arguments for the strategy and the task with a `NullPointerException`.

Nested usage is important: if the task itself calls this same scoped method again with yet another strategy, the inner call should correctly restore to the intermediate strategy, not jump back to the original one. This must work without interfering with other threads.

A natural name for this might be `withStrategy`. The existing `setTempFileCreationStrategy` and `setThreadLocalTempFileCreationStrategy` methods should remain unchanged; this is purely an addition to support safe, scoped temporary overrides.

[bug-69714] add TempFile.withStrategy. Thanks to Attila Kelemen. This closes #825


**Title:** Provide StAX helper methods for XMLOutputFactory and XMLEventFactory

**Background**
The `StaxHelper` utility class already offers `newXMLInputFactory()`, which returns a pre-configured `XMLInputFactory` with security‑sensitive defaults (namespace‑aware, no validation, no DTD, no external entities) and gracefully logs warnings when a property cannot be set on older StAX implementations. No analogous helpers exist for output factories or event factories, so every consumer must repeat similar setup boilerplate.

**Request**
Add static factory methods to `StaxHelper` for creating `XMLOutputFactory` and `XMLEventFactory` with sensible, consistent defaults.

- `newXMLOutputFactory()` must enable `IS_REPAIRING_NAMESPACES` by default.
- `newXMLEventFactory()` must return a default `XMLEventFactory`.
- Property‑setting attempts must be safe against older StAX parsers that throw `AbstractMethodError`; the behavior should mirror the existing `newXMLInputFactory()` approach of logging a warning rather than failing.
- Keep the methods in the same class (`org.apache.poi.util.StaxHelper`), consistent with the existing helper.

This will allow the rest of the codebase to obtain pre‑configured StAX output and event factories without duplicating error‑handling logic.


**Title:** EMR_EXTCREATEPEN: missing bounds check on `numStyleEntries` allows huge allocations or negative array sizes

**Severity:** Medium
**Component:** HEMF / EMF record parsing (poi-scratchpad)
**Labels:** security, correctness, input validation

### Problem
When parsing an `EMR_EXTCREATEPEN` record, the parser reads the unsigned `NumStyleEntries` field from the binary stream and immediately uses it as the length of a `float[]` dash pattern array:

```java
float[] dashPattern = new float[numStyleEntries];
```

A malicious or corrupted EMF file can supply an enormous `NumStyleEntries` value (e.g., `0x40000000`), causing the JVM to attempt a multi-gigabyte allocation and likely trigger an `OutOfMemoryError`. Values with the high bit set become negative when narrowed to `int`, producing a `NegativeArraySizeException`. Neither outcome is handled gracefully; both break the parser and can be used as a denial-of-service vector.

Other EMF record parsers already guard against unreasonable allocations by applying a maximum size/length check during `init`. No such guard exists for the dash-pattern array in `EmfExtCreatePen`.

### Expected behavior
Before allocating the array, verify that `numStyleEntries` does not exceed a reasonable upper bound (aligned with the project’s existing allocation limits for EMF record data) and that it is not so large as to overflow when interpreted as a signed `int`. If the value is out of range, the parser should throw a `RecordFormatException` rather than attempting the allocation.

### Notes for the fix
- The `numStyleEntries` field is an unsigned 32‑bit integer read from the stream; care must be taken when converting to a signed `int`.
- The project already has a mechanism for capping allocation sizes in EMF processing (see how other record types validate lengths). Please reuse that existing guard pattern/helper here to keep behaviour consistent.
- The fix should only affect the dash‑pattern allocation path inside `EmfExtCreatePen`; no other record parsing behaviour should change.

apply allocation bounds check to EmfExtCreatePen style entries (#1113)


**Title:** Fix WMF/EMF bounding box selection to respect position, not only size
**Area:** `HemfPicture.draw` – bounding box selection heuristic
**See:** #64716 (issue tracker)

### Description

When rendering WMF (or EMF) images, the `HemfPicture.draw` method currently picks the best matching bounding box for content layout by comparing the diagonal length of the candidate boxes (`emfBounds`, `winBounds`, `viewBounds`) to the diagonal of a reference rectangle (`recBounds`). This heuristic is purely size-based; it ignores *where* the candidate rectangles are located on the page.

Because of this, when the candidate bounding boxes are at different positions but have a similar diagonal length, the wrong box may be selected, leading to the image content being drawn at an incorrect position.

### Desired behavior

Replace the size-only selection heuristic with one that takes **both size and position** into account. A natural approach is to compare the corners of each candidate box to the corners of the reference box. For example, you could sum the squared distances between the four corners of the candidate and the four corners of the reference rectangle, and pick the candidate that minimizes that sum.

The existing helper `dia()` that computes a rectangle’s "diagonal" should be removed in favor of a new comparison that uses corner coordinates. Make sure that the selection still works correctly for the existing set of test files, including WMF files.

### Why this matters

Without this fix, certain WMF images rendered by POI (e.g., inside PowerPoint slides) appear shifted away from their intended position. The change should make WMF rendering positionally correct while not regressing EMF handling.

### Acceptance criteria

- WMF images that were previously misplaced now render at the correct location.
- The selection remains stable for the existing set of EMF/WMF test documents.
- The implementation uses a comparison based on positions (e.g., corner distances) rather than only a size measure.


**Title:** `HSSFWorkbook` cannot change its encryption mode, and saving with non‑CryptoAPI encryption crashes on document properties

**Background**
At present there is no public API on `HSSFWorkbook` to read or change the encryption mode after construction. The only way to influence encryption is to set a password via `Biff8EncryptionKey` before writing, which always results in `cryptoAPI` mode. Users have asked for the ability to switch to `binaryRC4` or `xor` modes (e.g. for compatibility with LibreOffice), or to remove encryption entirely, without losing the workbook contents.

Additionally, when an `HSSFWorkbook` is written with a non‑CryptoAPI encryption mode active, the save operation fails with an `EncryptedDocumentException` complaining that “Only CryptoAPI encryption supports encrypted property sets”. This makes the other encryption modes unusable for normal save operations even if the mode could be set.

**Scope / Requirements**
1. **Expose encryption mode on `HSSFWorkbook`**
   Provide a way to inspect the current encryption mode (possible values: `null` = no encryption, `xor`, `binaryRC4`, `cryptoAPI`) and to change it.

2. **Changing the mode must be safe**
   - Setting the mode to `null` should effectively remove encryption (equivalent to clearing the current password).
   - Setting a supported mode (`xor`, `binaryRC4`, `cryptoAPI`) must update the workbook’s internal records so that a subsequent write respects the new mode.
   - Setting any other mode must be rejected with a clear `IllegalArgumentException`.

3. **Property set handling**
   When saving a workbook that uses `binaryRC4` or `xor` encryption, the library must *not* try to encrypt the document properties (summary / document summary streams). Only `cryptoAPI` mode supports encrypted property sets. For the other modes, write the properties unencrypted, without throwing an exception.

4. **Round‑trip invariance**
   After setting a mode and password, writing, and then re‑reading the resulting file, the encryption mode must be correctly reported, and the original cell data must be intact.

**Affected area**
The HSSF (.xls) binary workbook and its interactions with the common property‑writing logic in `POIDocument`. The work will likely touch `HSSFWorkbook` and the property‑set writing path.


**Implementation request: `XDDFChartAxis.getNumberFormat()` should return `null` when no number format is set**

### Current behaviour (bug)
When a chart axis has no explicit number format — for example on a freshly created value axis, or when reading certain workbook files — calling `axis.getNumberFormat()` causes a `NullPointerException` or returns an unexpected empty/broken value instead of indicating absence.

### Expected behaviour
`XDDFChartAxis.getNumberFormat()` must return `null` when no number format is configured on the axis. This matches the semantics already provided by the `hasNumberFormat()` guard method, and mirrors the pattern used elsewhere in the axis API where absence is represented by `null` rather than by forcing creation or access of an underlying empty element.

### Acceptance criteria
1. For any axis instance where `hasNumberFormat()` returns `false`, `getNumberFormat()` returns `null`.
2. For axes where a format has been set, the method continues to return the correct format string as before.
3. The behaviour must hold for all axis types (value, category, date, etc.) that extend `XDDFChartAxis`.
4. No existing tests that assume `getNumberFormat()` may throw or return a non-null value for an unset format should break; the change is backward-compatible in the sense that callers already able to handle `null` from other similar getters are safe.

[bug-67510] issue with empty num format on chart axes


In the streaming `SXSSFWorkbook` implementation, formula cells that receive a pre‑evaluated *string* result via `setCellValue(String)` do not behave correctly; the cached value is either lost or causes errors when the workbook is written/read back. Numeric pre‑evaluated values (via `setCellValue(double)`) work as expected. The behaviour should be consistent with the non‑streaming XSSF (and HSSF) implementations.

**Acceptance criteria**

1. **Cached string preservation**
   - Given a formula cell created in an SXSSF sheet with `setCellFormula("someFormula")` and then given a pre‑calculated string value with `setCellValue("someStringResult")`, the cell must remain a formula cell.
   - After writing the workbook and reading it back, the cell must still report formula type, the original formula must be unchanged, and the pre‑evaluated string value must be available (e.g. via `getCachedFormulaResultTypeEnum()` returning `STRING` and the string value retrievable).

2. **Type‑change value transfer**
   - If a formula cell already has a cached value (numeric or string) and the cached result type is changed via `setCellValue` to a different type, the pre‑existing cached value must be carried over into the new cached‑value object.
   - Concretely: a cell with numeric cached value `55.0` that is then set to a string cached value via `setCellValue("55")` should keep the original numeric cached value internally available, matching the behaviour of the non‑streaming implementations.

3. **No regressions**
   - Existing functionality (numeric cached values, plain string/numeric non‑formula cells) must continue to work without change.
   - Round‑tripping through file for such formula cells must not throw exceptions or produce corrupted content.
```

Bug 55384: Handle setting pre-evaluation string correctly in SXSSF as well


```text
Title: OOXML constructors from InputStream should not close the stream

Currently, when constructing an `XSSFWorkbook` (and likely other OOXML objects like
`XSSFSlideShow` or `XWPFDocument`) from a `java.io.InputStream`, the constructor
internally closes the stream after reading the document. This breaks scenarios where
the caller is using a stream that contains multiple OOXML files — for example, a
`ZipInputStream` wrapping a ZIP archive that holds several `.xlsx` entries.

Expected behavior:
- After a **successful** construction the provided `InputStream` must remain open.
  The caller is responsible for closing the stream when it is no longer needed.
- Internal resources (the zip-decompressor, temporary buffers, etc.) used to parse
  the OPC package should still be released properly so no resource leaks occur.
- The fix should be applied in the shared low-level OOXML packaging layer
  (`openxml4j/opc`) so that it is uniform across all OOXML formats.

On errors (when the constructor throws an exception) the library may close the
stream as a safety measure, but the primary requirement is that a successfully
loaded workbook leaves the stream untouched.

Acceptance:
- `XSSFWorkbook wb = new XSSFWorkbook(inputStream);` does **not** close `inputStream`.
- A single `InputStream` (e.g. a `ZipInputStream` containing multiple `.xlsx` files)
  can be read sequentially; each entry produces a valid workbook and the stream
  remains usable for the next entry.
```

[bug-67579] do not close stream when reading XSSFWorkbook based on stream


**Bug: XWPF table cells should not be auto‑populated with a paragraph when loading existing documents**

**Problem**
When reading a `.docx` file containing a table with truly empty cells (cells without any block‑level element such as a paragraph), the `XWPFTableCell` constructor currently adds a default paragraph to satisfy the OOXML requirement that every table cell contain at least one block‑level element. This alters the in‑memory document just by loading it: empty cells are no longer empty. As a result, a round‑trip (load and save) can produce unintended content, and the API misrepresents the original file.

**Desired behavior**
- Loading an existing document must preserve the original content—cells that were empty in the XML should remain empty in the POI model.
- The responsibility for inserting a mandatory paragraph should be moved to the `XWPFTable` table‑creation logic (e.g., `createEmptyTable`), which already inserts a paragraph into the first cell of a newly created table. That keeps completely new tables valid while leaving existing content untouched.
- `XWPFTableCell` itself should no longer add a paragraph in its constructor.
- Accessors such as `getCell(...)` or `getParagraphs()` on a table cell must not modify the cell’s content; repeated calls must return the same state.

**Non‑obvious edge cases**
- A newly created empty cell (via `XWPFTableRow.addNewTableCell()`) should also start with zero paragraphs; callers can explicitly add a paragraph if needed, but the constructor must not force one.
- The existing `createEmptyTable` method in `XWPFTable` already inserts a paragraph in the first cell—only that behavior must be preserved so that empty tables remain valid per the OOXML spec. The fix must not break that invariant.
- Code that previously relied on a newly constructed cell having at least one paragraph (e.g., by directly accessing paragraph 0) must be updated to handle the possibility of an empty cell; the test suite should reflect these new expectations.

Bug 64838: Do not populate cells with a paragraph when loading an existing document


A user reported that when using `CellUtil` to apply fill foreground and background colors to cells in .xlsx workbooks (XSSF / SXSSF), the desired style is often not correctly matched to an existing workbook style, causing unnecessary style duplication. In some cases setting a fill color via a short index (e.g. `FILL_FOREGROUND_COLOR`) without also providing a `Color` object (the `FILL_FOREGROUND_COLOR_COLOR` property) leads to incorrect behavior, and attempts to set a `Color` object that is not in the workbook's palette may throw an `IllegalArgumentException` in OOXML formats.

The expectation is that `CellUtil.setCellStyleProperties` (and internal helpers) should:

- find and reuse an existing cell style when the only differences are fill foreground/background color properties that are null (unset) in one map but present in the other; the style matching should treat an unset color property as matching any value, preventing unnecessary creation of a new style.
- correctly apply fill colors regardless of whether they are supplied as a short index only, a `Color` object only, or both, and fall back gracefully to the short index when `setFillForegroundColor(Color)` / `setFillBackgroundColor(Color)` throws an `IllegalArgumentException` (which may happen for custom/unindexed colors in XSSF/SXSSF).
- work consistently across HSSF, XSSF, and SXSSF so that calling `CellUtil.setCellStyleProperties` with only a short fill foreground color set (and no explicit background color) produces a valid style without errors and without leaving the cell in an unexpected state.

The fix should be limited to `CellUtil` and its style-matching and color-setting logic; the public API signatures should remain unchanged.


The `LittleEndianInputStream.read(byte[], int, int)` method tracks a `readIndex` that is supposed to reflect the total number of bytes successfully read from the stream. Currently, when the underlying stream returns `-1` indicating end-of-file, `readIndex` is decremented by one instead of staying unchanged. This causes `getReadIndex()` to return incorrect values after a read attempt that reaches EOF:

- For an empty stream, `getReadIndex()` returns `-1` after a single read attempt.
- For a stream containing exactly N bytes, after reading all N bytes and then attempting another read (which hits EOF), `getReadIndex()` returns N-1 instead of N.

The read index must be a monotonically non-decreasing count of the bytes that have actually been consumed. It must never decrease, and it must stay correct when the stream is re-read after the underlying stream signals EOF.

Acceptance criteria:
- After constructing a `LittleEndianInputStream` on an empty byte array, and calling `read(byte[], int, int)` once, `getReadIndex()` returns `0`.
- After reading exactly the available bytes from a stream (so that `read(...)` returns valid data followed by `-1` on a subsequent call), `getReadIndex()` returns the number of bytes that were actually read. The EOF return value must not alter this count.
- Existing behavior for successful reads (where data is returned) must remain unchanged; the index must continue to advance by the number of bytes read.


**Title:** WorkdayCalculator.calculateWorkdays returns incorrect value when start == end
**Component:** SS / Formula / ATP
**Type:** Bug

**Description**
`WorkdayCalculator.calculateWorkdays` produces wrong results when the start date and end date are identical.
For a single workday (weekday, not a holiday) it returns a negative number (e.g., -1) instead of
1. For a single day that is a weekend or a holiday it returns negative values instead of 0.

The contract of `calculateWorkdays` should be: count the number of workdays in the inclusive
range `[start, end]`. For a single‑day range the result must be 1 when the day is a workday and
0 when it is a weekend or a holiday.

**Root cause**
The helpers `pastDaysOfWeek` and `calculateNonWeekendHolidays` decide the sign of their result
with the condition `start < end`. When the two dates are equal that condition is false, causing
the correctly computed count (positive) to be negated.

**Expected fix**
Change the sign logic in both helpers to use `<=` instead of `<` so that equal dates return a
positive count.

Additionally, the protected method `isNonWorkday` is not used anywhere in production. It should
be formally deprecated (using the project’s `@Removal` annotation) and scheduled for removal in
a future version.

Acceptance criteria:
- `calculateWorkdays(day, day, noHolidays)` returns 1 when `day` is a normal weekday.
- `calculateWorkdays(day, day, noHolidays)` returns 0 when `day` is a weekend.
- `calculateWorkdays(day, day, holidaysContainingDay)` returns 0 when `day` is a holiday.
- `isNonWorkday` is deprecated and still passes existing contract when called directly.

Bug 60452: WorkdayCalculator calculateWorkdays does return wrong value for same day input


We need to harden the OOXML parsing against XML entity expansion ("Billion Laughs") attacks.
A crafted Office file can use nested entity expansions to cause denial of service by consuming excessive memory or CPU when the library parses its internal XML streams.

The current entity expansion limit in the XML parser configuration is either too high (4096) or, in some paths, effectively unlimited (0), which allows an attacker to trigger resource exhaustion.

The acceptance criteria for the fix:

1. Limit entity expansions to the smallest practical value (1) consistently across **all** XML parsing entry points used for OOXML content — both SAX and DOM paths.
2. If a parsed XML document attempts a second entity expansion, parsing must fail with an appropriate `SAXException` (or equivalent) rather than proceed.
3. The limit must apply regardless of whether the underlying parser is Xerces with a security manager, or a fallback using the built‑in `entityExpansionLimit` property.
4. For the DOM path, also disable the `expand-entity-references` feature to avoid indirect expansion.
5. Normal, well‑formed Office Open XML files that do not rely on entity expansion must continue to parse successfully.

The observable behavior after the fix: opening a malicious `.xlsx` (or other OOXML file) whose internal XML parts contain entity expansion chains should throw a parse error indicating the entity expansion limit has been exceeded, rather than hanging or consuming excessive resources.


Implement the Excel `TREND` function for the formula evaluation engine.

- `TREND(known_y's, [known_x's], [new_x's], [constant])` fits a linear regression and returns predicted y-values for the given x-values.
- If `known_x's` is omitted, default to the array `{1, 2, 3, …}` of the same size as `known_y's`.
- If `new_x's` is omitted, use `known_x's`.
- If `constant` is omitted, default to `TRUE` (include an intercept). `FALSE` forces the regression through the origin.
- The result shape should match `new_x's` when provided, otherwise match the shape of the input that determines the output size (matching Excel's behavior).
- Reject inputs where the number of x and y observations are inconsistent, or where the regression cannot be computed (singular matrix, too few degrees of freedom, etc.).
- Register the function at the standard function index `50` so that `TREND` is callable from spreadsheet formulas.

No UI or file-format changes are needed; only the formula evaluation backend.

Bug 62836: Implementation of Excel TREND function


Excel's `TDIST.RT` function is not recognised by the formula engine. When a workbook contains a formula like `=TDIST.RT(1.96, 60)` the cell displays `#NAME?`, and there is no way to compute the right‑tailed Student’s t‑distribution directly from the built‑in function set.

`TDIST.RT` is a two‑argument Analysis ToolPak function (introduced as a replacement for the older three‑argument `TDIST`). Its signature is `TDIST.RT(x, deg_freedom)` and it returns the right‑tailed probability of the t‑distribution. According to Excel’s specification:

* Both arguments must be numeric – non‑numeric inputs yield `#VALUE!`.
* `deg_freedom` must be ≥ 1, i.e. if it is less than 1 (after truncation to an integer) the result is `#NUM!`.
* The function uses the absolute value of `x`, so a negative `x` should not cause an error (unlike the legacy `TDIST`, which rejects negative `x`).

The request is to add full evaluation support for `TDIST.RT` so that it behaves correctly as an ATP function. The existing `TDIST` implementation already contains a helper that computes the one‑tailed distribution, so ideally the new function should share that mathematical computation while applying its own simpler argument handling (two arguments, no tails parameter, absolute value on `x`, and the error rules described above). In addition, any registration tables that map function names to implementations must be updated so that `TDIST.RT` resolves to this new handler.


Add `CEILING.MATH` to the Analysis ToolPak functions (partial, significance argument not yet supported).

`CEILING.MATH` rounds a number up to the nearest integer or multiple of significance. Its Excel signature is `CEILING.MATH(number, [significance], [mode])`. For this issue only the first and third arguments need to be handled; the second argument (`significance`) can be ignored for now (behave as if it is 1).

Requirements:
- Register the new function so that `CEILING.MATH` is recognized in formulas.
- If called with no arguments, return `#VALUE!`.
- If the first argument is non-numeric, return `#NUM!`.
- Default behaviour: round the first argument up to the next integer (`Math.ceil`) and return the result.
- If a third argument (`mode`) is present and its value is negative, negative numbers should round down (away from zero) instead. In that case use `Math.floor` for negative input.
- Otherwise ignore the third argument and always round up.

Strictly follow these Microsoft-documented examples (significance parameter is supplied but not yet used for rounding):
  `CEILING.MATH(24.3,5)` → 25
  `CEILING.MATH(6.7)` → 7
  `CEILING.MATH(-8.1,2)` → -8
  `CEILING.MATH(-5.5,2,-1)` → -6

Do not implement the significance rounding logic; just parse the arguments and apply the floor/ceil logic as described.

partial implementation CEILING.MATH function (still need to support optional 2nd param)


**Feature request: Add support for the AVERAGEIF spreadsheet function**

Right now evaluating a formula that uses `AVERAGEIF` results in an error (likely `#VALUE!` or an unrecognised name error), because the function isn’t implemented. The goal is to make `AVERAGEIF` behave the same as it does in Microsoft Excel.

### Acceptance criteria

1. **Two‑argument form** – `AVERAGEIF(range, criteria)`
   Averages all numeric cells in `range` that satisfy the `criteria`. Cells containing text, logical values, or blanks in the range are ignored.

2. **Three‑argument form** – `AVERAGEIF(criteria_range, criteria, average_range)`
   Evaluates the `criteria` against `criteria_range`, and where the criterion is met, averages the corresponding cells in `average_range`. The dimensions of `average_range` must be consistent with `criteria_range` (they are typically the same size and shape, and the top‑left cells are aligned).

3. **Criteria matching**
   The `criteria` argument follows the same syntax already used by `COUNTIF`, `SUMIF`, and `AVERAGEIFS`:
   - Literal numbers (e.g., `10`).
   - Strings that start with comparison operators (`">32"`, `"<A"`).
   - Plain strings (exact text match) and wildcard expressions (`"a*"`, `"*x?"`).
   - Boolean criteria are matched against `TRUE`/`FALSE` values in the range.
   - Error criteria (e.g. `"#N/A"`) must result in an error return value.

4. **Empty result set**
   If no cells match the criteria, `AVERAGEIF` should return `#DIV/0!`.

5. **Non‑numeric values in the average range**
   Only cells containing numeric values are included in the sum and the count; text, booleans, blanks, and errors are skipped. If the *criteria range* cell is an error, that error should propagate (the average is not calculated).

6. **Integration with the function registry**
   `AVERAGEIF` must be registered so that it is available when formulas are evaluated through the POI formula evaluation engine — in the same way that `AVERAGEIFS`, `COUNTIF`, `SUMIF`, etc. are already available. (Hint: it’s an Analysis ToolPak function that currently maps to `null`.)

7. **Consistency with existing IFS‑style functions**
   The implementation should stay consistent with how `AVERAGEIFS` and `COUNTIF` work internally. For instance, the criteria predicate creation should be identical to `COUNTIF`, and the averaging logic should mirror the approach taken by `AVERAGEIFS` but adapted for the single‑criterion, optional‑average‑range signature.

### Out of scope
- Support for 3‑D references or non‑rectangular ranges (standard worksheet range handling is sufficient).
- Ignore any existing Excel‑oddities that were never reported; the aim is to cover the common behaviour described here.

[github-330] add averageif function support


**Ticket: Refactor `CopyCompare` to eliminate `ThreadLocal` (Sonar code smell)**

The example class `o.a.p.examples.hpsf.CopyCompare` currently holds its output `PrintStream` in a `private static final ThreadLocal<PrintStream>` field. This triggers a Sonar warning and is unnecessary complexity for this use-case.

We want to keep the existing public behavior:
* `CopyCompare.main(String[] args)` still works as a command-line entry point, printing comparison result to the output stream.
* It is still possible to redirect output programmatically (e.g., for testing) without relying on a thread-local.

Acceptance criteria:

1. Remove the `ThreadLocal<PrintStream>` field. Replace it with a plain instance field (`PrintStream`) that defaults to `System.out`.
2. Make the class instantiable (remove the private no-arg constructor). The class can remain `final`.
3. Extract the current logic from `main` into a public instance method that performs the comparison (the same algorithm, but using the instance's output field). `main` should delegate to a default instance and call that method.
4. The `setOut` method becomes an instance method that simply reassigns the output field on the target instance.
5. Update the only external caller, the integration-stress handler `HPSFFileHandler`, to use the new API:
   * Create an instance of `CopyCompare`.
   * Redirect output by calling the instance `setOut` with the capturing `PrintStream`.
   * Invoke the instance comparison method (or the static `main` method, but ensure output still flows through the same instance – the original redirection must still work).
   * The test’s assertion on captured output must continue to pass unchanged.

The comparison logic and the `InputStreamSupplier` helper should not change.

Note: `CopyCompare` is an example class; we should still be able to run it standalone via `main` without any new required arguments.

rewrite CopyCompare to remove its ThreadLocal which causes a sonar code smell


Apologies for the inconvenience, but I've encountered a feature gap that causes formula evaluation failures for valid Excel spreadsheets. Here's the issue:

---

**Title:** Missing support for 2-argument LOOKUP function (array form)

**Description:**
The Apache POI formula engine currently only supports the 3-argument variant of LOOKUP (`LOOKUP(lookup_value, lookup_vector, result_vector)`). When a spreadsheet contains the 2-argument (array) form `LOOKUP(lookup_value, array)`, evaluation throws a `RuntimeException` with the message "Two arg version of LOOKUP not supported yet". This blocks processing of any workbook that relies on the array form of LOOKUP, which is a perfectly valid Excel function.

**Expected behavior:**
The formula engine should implement the array form of LOOKUP according to Excel's specification:

- If the `array` argument is wider than it is tall (more columns than rows), LOOKUP searches for `lookup_value` in the first row, and returns the value from the **last row** at the same column position.
- If the `array` is taller than it is wide (more rows than columns), or is square, LOOKUP searches for `lookup_value` in the first column, and returns the value from the **last column** at the same row position.
- As with the 3-argument form, the lookup uses an approximate (range) match, expecting the lookup vector to be sorted in ascending order. The search and value retrieval should leverage the existing `LookupUtils` helpers for creating `ValueVector` instances and performing the lookup.

**Impact:**
Any Excel file using the 2-argument LOOKUP function (e.g. `=LOOKUP(A1, C1:E10)`) will fail to evaluate with POI. This is a common formula pattern for simple array lookups, and its absence forces users to rewrite formulas or use alternative libraries.

**Additional context:**
The 3-argument form already works correctly and reuses a number of internal utilities for resolving the table array, creating row/column vectors, and performing the binary search. The fix for the 2-argument form should build on that existing infrastructure without introducing new fundamental mechanisms. The only missing piece is the decision logic that selects the lookup and result vectors based on the shape of the provided array.

bug 52063: support 2-argument LOOKUP(lookup_value, array) function in Excel


```
Title: HMEFMessage.getMessageMAPIAttribute does not return attributes for custom MAPI properties

When a MAPIProperty is constructed via MAPIProperty.createCustom(id, type, name),
HMEFMessage.getMessageMAPIAttribute(property) fails to find the corresponding attribute,
even when the message actually contains an attribute with that numeric property ID.
This means code that looks up custom (non-standard) MAPI attributes always gets null.

Expected behavior:
The lookup should match attributes by their property's numeric ID, not by object
identity. Given a custom MAPIProperty with a specific ID, getMessageMAPIAttribute
should return any MAPIAttribute whose stored property has the same ID.

Reproduction steps:
1. Parse a TNEF/HMEF message that contains custom MAPI properties (e.g. with IDs
   like 0xE28, 0xE29).
2. Create a custom MAPIProperty with that same ID using MAPIProperty.createCustom.
3. Call message.getMessageMAPIAttribute(customProp).

Observed: null is returned.
Expected: the matching MAPIAttribute is returned.

Impact:
Any code that needs to retrieve vendor-specific or extended MAPI attributes
from an HMEF message is broken, because the custom property objects created
at retrieval time are never identity-equal to the property objects stored
inside the parsed attributes.
```

Fix HMEFMessage to allow fetching attributes by custom MAPI Properties


We've received a bug report: "DataFormatter does not respect two-part Excel number formats that use a semicolon to separate positive/negative/zero or conditional parts. For example, `[>=10]#,##0;[<10]0.0` should apply the first format for values 10 and above, and the second for values below 10. `0 ?/?;0` should display positive values as fractions and zero/negative as plain integers without the fractional part. Currently, only formats with three or more parts (two or more semicolons) are handled correctly; two-part formats are not being interpreted conditionally and produce incorrect output."

This should prompt the agent to locate the relevant code in `DataFormatter.java` (the getFormat method) where the check for multiple parts is done, and adjust the condition to handle all `;`-containing formats, not just those with at least two semicolons. The actual test changes are not specified, so the agent would derive the expected behavior from knowledge of Excel and the public API's contract of matching Excel's output.


**Title:** `IOUtils.safelyClone` unsafe with large `length` values and missing input validation in production

**Description:**
Fuzzing the library with Jazzer revealed two problems in `org.apache.poi.util.IOUtils.safelyClone`:

1. When the `length` argument exceeds the number of bytes actually available in the source array (i.e. `length > src.length - offset`), the method may attempt an out-of-bounds copy (`Arrays.copyOfRange(src, offset, offset + length)`). The allocation safety check (`safelyAllocateCheck`) correctly caps the checked length with `Math.min(src.length - offset, length)`, but the subsequent copy still uses the uncapped `offset + length`, leading to `ArrayIndexOutOfBoundsException` or – if the check were ever bypassed – an overly large allocation.

2. The method currently uses Java assertions (`assert`) to verify that `offset`, `length`, and `maxLength` are non‑negative. Assertions are typically disabled in production builds, so invalid negative arguments pass silently and cause hard-to-diagnose failures deeper in the stack.

**Expected behavior:**
- Invalid arguments (negative `offset`, `length`, or `maxLength`) must always throw a descriptive exception (e.g. `RecordFormatException`) so that misuse is caught early, even in production.
- The actual number of bytes copied must never exceed the remaining bytes in the source array. Both the allocation safety check and the `copyOfRange` call should operate on `min(src.length - offset, length)`. This prevents out-of-bounds accesses and ensures the allocation guard reflects the real copy size.

**Impact:**
Production environments that disable assertions can suffer from undetected misuse, potentially causing crashes or `ArrayIndexOutOfBoundsException`s when processing malicious or malformed Office documents. The unbounded copy length can also circumvent the allocation limit intended to protect against denial-of-service attacks.

Fix issues found when fuzzing Apache POI via Jazzer


**Title:** Relational operators (`<`,`<=`,`=`,`>=`,`>`) mishandle results of `IFERROR` when the second argument is omitted

**Summary**
When a formula uses `IFERROR(expression,)` (i.e. with a missing second argument) and the result is compared to a number, the comparison does not treat the missing argument as zero / blank, leading to incorrect `TRUE`/`FALSE` values.

**Steps to reproduce**
In an Excel workbook evaluated by Apache POI, place a formula like:
```
=IFERROR(0/0,)<0
```
The division `0/0` triggers an error, so `IFERROR` falls back to the missing second argument. The expected behaviour is that a missing argument (like a blank cell) is treated as the value 0 in a numeric comparison. Therefore:
- `IFERROR(0/0,)<0` → `FALSE`
- `IFERROR(0/0,)<=0` → `TRUE`
- `IFERROR(0/0,)=0` → `TRUE`
- `IFERROR(0/0,)>=0` → `TRUE`
- `IFERROR(0/0,)>0` → `FALSE`

Currently, POI returns wrong boolean results for these comparisons. The problem also occurs when the missing-argument operand appears on the right side, e.g. `0<IFERROR(0/0,)`, which should also be `FALSE`.

**Expected behaviour**
Every relational operator (`<`, `<=`, `=`, `>=`, `>`) should handle the result of `IFERROR` with an omitted second argument the same way it handles a blank cell: treat it as zero in numeric comparisons. This must work regardless of argument position (left or right operand of the comparison).

**Impact**
Formulas that rely on `IFERROR` with a default “blank” result and then compare that result to zero produce incorrect logical outcomes, breaking spreadsheet calculations that expect Excel‑compatible semantics.

Bug 64393: Adjust relational operators to handle MissingArgEval correctly


Some .xlsx files contain VML drawings (e.g., for cell comments or form controls) whose root `<xml>` element declares a default namespace of `http://schemas.openxmlformats.org/spreadsheetml/2006/main`.  This namespace is incorrect for VML content and prevents the file from being read – POI throws an `XmlException` or silently fails to load the shapes.

We need POI to handle such files gracefully.  When a VML drawing part is loaded, the parser must recognise and strip that spurious default‑namespace declaration from the XML before parsing, so that the embedded shapes are accessible as they would be in a well‑formed file.

Constraints:
- Any existing workaround applied to the raw VML stream (in particular, fixing unescaped `<br>` tags) must continue to work without regression.
- The fix must not alter the semantic content or structure of the VML shapes beyond removing the offending namespace declaration.

The observable success criterion is: a workbook containing VML with this extra namespace can be opened, and the VML shapes can be inspected and manipulated through the usual XSSF VML drawing API without exceptions.

#65061 - Handle VmlDrawings containing spreadsheet-ml default namespace


Feature request: Implement `HWPFDocument.write(File)`.

Currently, calling `write(File)` on an HWPFDocument throws an `IllegalStateException` with the message "Coming soon!".
This method should be implemented to actually write the Word document to the given file in the standard OLE2 (POIFS) format.
The existing `write(OutputStream)` method already works correctly and should continue to work.
After writing to a file it must be possible to re‑open that file via `FileInputStream` or `POIFSFileSystem` and read back the same document content (e.g., the same paragraph text as before the write).

Acceptance criteria:
- `doc.write(file)` completes without exceptions and produces a valid POIFS file on disk.
- The written file can be reopened and the document's paragraph text matches the original.
- The existing `write(OutputStream)` method remains functional and its behavior is unchanged.

HWPFDocument.write(File) support and tests #57919


When a resource defines a GET operation with serialization groups (e.g., `normalizationContext: {'groups': 'related'}`) but the GET collection operation has no groups, the serialization of embedded relations in the collection response incorrectly uses the GET operation's groups. This causes the embedded relation to appear as a full object with properties in the collection output, instead of being serialized as a simple IRI reference. The expected behavior is that the collection uses its own serialization context (which lacks groups), so embedded relations should appear as IRIs. On the other hand, the single-item endpoint should still correctly embed the relation with its groups-active fields.

Acceptance criteria:
- When fetching a collection of resources, embedded relations respect only the serialization groups defined on the collection operation itself. If no groups are defined there, relations appear as IRIs, not as nested objects.
- When fetching a single resource by its item endpoint, embedded relations respect the groups configured on the item operation (e.g., full embedding with designated fields).
- This behavior must be consistent across all supported serialization formats, including JSON-LD/Hydra and HAL.

The issue appears because during normalization of a collection, the context's operation is temporarily changed to the item operation for each member, and the original root operation information is lost when resolving serialization groups for embedded resources later on. The fix should ensure that the root operation used for serialization groups is always the one that initiated the normalization (i.e., the collection operation when normalizing a collection), regardless of any per-item operation swaps.

fix(serializer): read groups off the root operation (#5196)


**Title:** Custom controller declared on an API resource operation is ignored in Laravel routes
**Area:** Laravel integration (resource routing)

### Current behavior
When an API resource operation (e.g., `Get`, `Post`) declares a custom controller via the `controller` option in its metadata, the Laravel route still uses the default `ApiPlatformController`. The custom controller is never invoked.

### Expected behavior
- If an operation metadata explicitly defines a `controller` that is a valid class name, that class should be used as the route handler instead of `ApiPlatformController`.
- If the `controller` value is a string that is *not* an existing class (e.g., a service alias like `api_platform.action.not_exposed`), the route must resolve the handler from the application container so that it continues to work correctly.

### Non-obvious edge case / safety constraint
Some internal operations use a string controller alias (such as `api_platform.action.not_exposed`) that is expected to throw a `NotExposedHttpException` when called. The fix must preserve this behavior — those routes must still resolve to a callable that throws the appropriate HTTP exception, and must not break the existing error handling.

### Acceptance criteria
- A resource with `#[Get(uriTemplate: '/...', controller: MyController::class)]` should route requests to `MyController`.
- A resource whose `controller` is a service alias (not a class name) should route to the resolved instance from the container.
- The existing route that uses `api_platform.action.not_exposed` must still respond with an HTTP exception (e.g., `NotExposedHttpException`) when accessed directly.

fix(laravel): use controller if declared (#7687)


## Bug: PropertyFilter's OpenAPI/Swagger parameter name is missing array brackets

The `PropertyFilter` accepts a query parameter whose value is an array (e.g., `?properties[]=name&properties[]=email`). The generated OpenAPI and Swagger documentation meta-data currently reports the parameter name without the `[]` suffix – for example `name: properties` instead of `name: properties[]`.

The description example already correctly illustrates the array syntax (`properties[]={propertyName}&properties[]={anotherPropertyName}…`), but the `name` field is inconsistent. This causes tools that consume the OpenAPI/Swagger spec (SDK generators, API explorers) to produce incorrect request serialization.

Make the OpenAPI and Swagger description sections for this filter use the array-style name (append `[]` to the configured parameter name) so that the specification matches the actual query-string format and the example.

swagger/openapi bug with PropertyFilter use ?properties[]= not ?properties= (#2783)


In the OpenAPI schema generation, properties with a `Regex` constraint that has `match: false` (inverted regex) do not receive a `pattern` field, unlike those with `match: true`. This is because the validator metadata restriction for `Regex` constraints (`PropertySchemaRegexRestriction`) only activates when `match` is `true`. Symfomy's `Regex` constraint provides an `getHtmlPattern()` method that correctly converts an inverted regex to a valid HTML5 pattern (using negative lookahead), so we can safely handle both cases uniformly.

We should update the restriction to support any `Regex` instance, regardless of the `match` setting, and always use `getHtmlPattern()` to derive the OpenAPI `pattern`. The `create()` method already uses that method; only the `supports()` guard needs widening.

Acceptance criteria:
- A `Regex` constraint with `match: false` on an API resource property produces an appropriate `pattern` in the generated OpenAPI schema (derived from the constraint's html pattern).
- The schema generation for `Regex` with `match: true` continues to work as before.

feat(openapi): handle Regex match=false (#4336)


In API Platform's Laravel integration, Eloquent resource operations support a `policy` string that names a Laravel policy method checked by `ResourceAccessChecker`. Currently setting this value is manual for every operation, even when the model already has a corresponding Laravel policy discoverable via `Gate::getPolicyFor()`.

Make the metadata factory automatically set the operation `policy` when:

- the operation does not already have an explicit policy, and
- the Eloquent model backing the resource resolves to a policy via `Gate::getPolicyFor($model)`, and
- the corresponding policy method exists on that policy class.

Map each operation type to a policy method following Laravel conventions:

- `Put` / `Patch` → `update`
- `Post` → `create`
- `Get` → `view`
- `GetCollection` → `viewAny`
- `Delete` → `delete`

When a `Put` operation has `allowCreate` enabled (upsert semantics), resolve the policy method as `create` instead of `update`.

Separately, in `ResourceAccessChecker::isGranted`, when the `$extraVariables['object']` is a `Paginator` instance (collection operations), pass the resource class string as the second argument to `Gate::allows`. This ensures collection-level policy methods receive the model class rather than a paginator instance, matching Laravel's `viewAny` signature.

feat(laravel): automatically register policies (#6623)


LinkFactory still treats a property named `id` as a fallback identifier even when that property is explicitly annotated with `identifier: false`. A resource that sets `#[ApiProperty(identifier: false)]` on its `id` field—and that also has no other identifier—should be treated as having no identifiers (or fall back to a different rule), but currently `id` is still added. The fix should make the fallback path respect an explicit `identifier=false` and only auto-assign `id` when the property’s `identifier` setting is not declared at all. The existing behavior (where `id` is picked up when `identifier` is unset) must remain unchanged.

fix: do not auto-set id as identifier if it is set as identifier=false


When global defaults are provided for array-typed options (e.g. `cache_headers` in the defaults array passed to `AttributesResourceMetadataCollectionFactory`), they currently fully replace any array value already set on the resource or operation through attributes. This causes resource-level or operation-level array configuration to be silently dropped.

Expected behavior: array defaults should be **merged** with the operation/resource array, with the resource/operation's values taking priority for overlapping keys. Scalar defaults must remain unchanged: they should still only be applied when no value is already set. This behaviour should apply to all array-typed metadata options (such as `cache_headers`, `normalizationContext`, `denormalizationContext`, and any other options that accept arrays), not just cache headers.


```
Bug: Elasticsearch ItemProvider catches Missing404Exception instead of using client’s ignore option

The `ItemProvider::provide()` method currently wraps the `Client::get()` call in a `try`/`catch` block for `Elasticsearch\Common\Exceptions\Missing404Exception` and returns `null` when the document is not found. This approach ties the provider to an internal exception class from the official `elasticsearch-php` client. If the client ever changes its exception hierarchy or the way missing documents are reported, this code will break unnecessarily.

The Elasticsearch client already supports an `ignore` parameter that accepts an array of HTTP status codes for which no exception should be thrown. When `ignore` includes `404`, the client returns a normal document response with `"found": false` instead of throwing `Missing404Exception`.

The provider should be changed to:
- pass `['client' => ['ignore' => 404]]` to the client’s `get()` call,
- remove the `try`/`catch` for `Missing404Exception`, and
- inspect the `found` key of the returned document array to decide whether to return `null`.

This will make the code more robust, reduce its dependency on a client-specific exception, and follow the intended client usage pattern.
```

refactor(elasticsearch): ignore Missing404Exception instead of catching it (#5302)


Issue #8184: Multiple `:property` placeholder parameters with different property lists are silently deduplicated, causing loss of parameter definitions.

### Problem
When a resource or operation defines more than one parameter attribute using the `:property` placeholder (e.g., `#[QueryParameter(key: ':property', properties: ['a','b'])]` and `#[QueryParameter(key: ':property', properties: ['c','d'])]`), only one of them survives. The `Parameters` collection currently deduplicates entries based solely on the parameter key and the parameter class, treating the property list as irrelevant. This means the second definition either replaces the first (if later) or is discarded, and never both coexist.

Later, when the `:property` template is expanded per field, the lost parameter definitions never produce their expected per-field parameters, and the associated filter/configuration is missing for those fields.

This affects every entry point where `Parameters` accepts or merges parameters:
- Direct construction via `new Parameters([...])`
- The `add` method
- Merging global and operation-level parameters
- Any caching layer that builds a cache key from parameter identity

### Expected behavior
- Multiple parameters with a `:property` placeholder in their key should coexist in the same `Parameters` collection if their *property lists* differ.
- If two such parameters have identical property lists (and the same parameter class), the later one should override the earlier one, consistent with existing deduplication rules for non-placeholder parameters.
- The `:property` placeholder can appear anywhere in the key (e.g., `search[:property]`), so the logic must detect the substring, not require the whole key to equal `:property`.
- The change must not break deduplication for non-placeholder keys or for placeholders with identical property lists.

### Scope
- The primary contract is the `Parameters` collection (both constructor and `add`).
- Any code that tests whether a parameter already exists and decides to skip re-adding (e.g., during global/local merge) must apply the same property-list-aware uniqueness rule.
- Cache keys derived from parameter identity (e.g., in the per-property expansion factory) must include enough information to distinguish different property sets, otherwise the expansion for one set may be incorrectly reused for another.

### References
- `Parameter::getProperties()` and `Parameter::getProperty()` can be used to compute the “property identity” of a parameter.
- Existing tests for `Parameters` (e.g., the deduplication test) should still pass.

fix(metadata): :property dedup drops repeated parameters (#8196)


**Summary:** JSON-LD anonymous resources currently use blank nodes (`_:...`) instead of Skolem IRIs. There is no clean way to suppress the `@id` property entirely. The `ApiProperty` metadata’s `iri` handling is deprecated and needs a dedicated field.

**Observed behavior**
- When API Platform generates JSON‑LD for embedded or anonymous resources (e.g., objects without a defined identifier), it produces a blank‑node identifier like `_:<spl_object_id/hash>`.
- The `ApiProperty` metadata class exposes `getIri()`/`withIri()` but they are implemented using the deprecated `types` array, making it impossible to cleanly set an `iri` to `false` to omit `@id`.

**Expected behavior**
1. Anonymous resources must use a Skolem IRI (e.g., `/.well-known/genid/<random>`) instead of a blank node. If the context already provides an explicit `iri`, that explicit value must still take precedence.
2. When the stored `iri` value is exactly `false`, the `@id` key must be omitted from the JSON‑LD output (no identifier is generated). This allows users to opt out of IRI generation for embedded value objects or other cases where an `@id` is not desired.
3. `ApiProperty` should gain a dedicated `iri` field (accepting a string or `false`). `getIri()` must return that field’s value; `withIri($iri)` must set it. The deprecated methods that were tied to the old implementation must be removed (they are already marked for removal in the next major version).
4. When a relation is being normalized, if the corresponding property metadata has an `iri` set, that value must be forwarded into the child normalization context so the nested representation respects it.
5. The XML metadata schema and extractor adapter must support the new `iri` attribute on property definitions, with the same semantics (string or `false`).

**Impact / why needed**
- Blank nodes cause interoperability issues with strict JSON‑LD consumers that cannot handle them. Skolem IRIs are the recommended practice per the JSON‑LD specification.
- The current `ApiProperty` IRI handling is deprecated and confusing; a dedicated `iri` field aligns with the long‑term metadata design.
- The ability to suppress `@id` entirely is a common request when embedding non‑addressable sub‑resources, and `iri: false` provides a first‑class, non‑hacky knob for that.

fix(json-ld): use a Skolem IRI instead of blank nodes (#4731)


**Bug report: Multiple `[:property]` query parameters lose their expanded keys and do not work correctly**

When defining more than one query parameter that uses the `:property` placeholder (e.g. `search[:property]` and `filter[:property]`) in an `ApiResource` operation, only one of the placeholders expands into the expected concrete parameters. The others either disappear from the generated parameter set or keep the literal `:property` string in their key, breaking request handling.

**Expected behavior**

Each `[:property]` parameter should expand into a separate concrete `QueryParameter` for every property listed in its `properties` array. The generated parameters must carry the correct key (e.g. `search[title]`, `search[description]`, `filter[category]`, `filter[brand]`) and work independently in requests.

**Observed behavior**

When multiple `[:property]` parameters are declared, only the last one in the definition order produces correct expanded parameters. Earlier placeholder parameters either vanish or lose their key, so the endpoint cannot resolve the corresponding query arguments.

**Reproduction sketch**

A resource definition like this demonstrates the problem:

```php
#[ApiResource(
    operations: [
        new GetCollection(
            parameters: [
                'search[:property]' => new QueryParameter(
                    filter: new PartialSearchFilter(),
                    properties: ['title', 'description']
                ),
                'filter[:property]' => new QueryParameter(
                    filter: new ExactFilter(),
                    properties: ['category', 'brand'],
                ),
            ]
        ),
    ]
)]
```

Requests using `?search[title]=...` or `?filter[category]=...` then fail to filter correctly, or the parameters are simply absent from the OpenAPI spec.

**Impact**

This makes it impractical to use the `[:property]` placeholder together with multiple parameter definitions, forcing workarounds such as manually enumerating every combination or splitting operations. The feature as documented should support multiple placeholder-based parameters in a single operation definition.

fix(metadata): property placeholder on multiple parameters (#7598)


**Title:** IdentifiersExtractor should support PHP backed enums as resource identifiers

**Description**

When an API resource uses a PHP backed enum (`int`‑ or `string`‑backed) as an identifier (for example a `Status` enum that serves as the resource’s primary key or a composite identifier), the `IdentifiersExtractor` is currently unable to resolve the value and throws a `RuntimeException` (“We were not able to resolve the identifier matching parameter …”).

API Platform should be able to extract the backing value from such enums so that correct URIs can be generated for resources identified by enum values. The behaviour should match what is already done for scalars and `Stringable` objects: the backing value (an `int` or `string`) must be returned as a `string` suitable for use in URI paths.

**Acceptance Criteria / Constraints**

- When an identifier property value is an instance of `\BackedEnum` (both `\IntBackedEnum` and `\StringBackedEnum`) the extractor uses the enum’s backing value, converted to a string.
- For int‑backed enums the value should appear as its numeric string (e.g. `'1'`).
- For string‑backed enums the value is used directly.
- Non‑backed enums (plain unit enumerations) are not supported for identifiers; the extractor may continue to reject them (or treat them like any other unsupported type – no new behaviour is required).
- Existing handling of `null`, scalars, and `Stringable` objects must not be altered.

**Why**

Hypermedia‑driven APIs built with API Platform already allow resources to be identified by scalar values. With PHP 8.1+ enums becoming common, many domain models use backed enums as identifiers. The platform should support this out of the box, just as it does for strings and integers.

feat(metadata): class IdentifiersExtractor now handles enums (#5411)


**Title:** ODM IriFilter does not resolve references for resources with a custom API identifier

**Description**
When a MongoDB ODM (Doctrine ODM) resource uses a non-ID field as its API identifier via `ApiProperty(identifier: true)`, the `ApiPlatform\Doctrine\Odm\Filter\IriFilter` on a referenced field fails to filter correctly. The filter either returns no results (or all results) depending on the IRI, instead of the matching documents.

**Steps to reproduce**
1. Define an ODM document `Agent` where the document identifier (`#[ODM\Id]`) is not the API identifier. Instead, mark another field (e.g., `agentId`) as the API identifier with `#[ApiProperty(identifier: true)]`.
2. Define an ODM document `Mail` with a reference `author` pointing to `Agent`.
3. Add an `IriFilter` on the `author` parameter of a collection endpoint for `Mail`.
4. Insert an `Agent` with `agentId = "AGENT_001"` and a `Mail` linked to that agent.
5. Call `GET /mails?author=/agents/AGENT_001`.

**Expected behavior**
The filter should resolve the IRI to the correct `Agent` reference and return the one `Mail` whose author has `agentId = "AGENT_001"`.

**Actual behavior**
The filter does not match the expected document. In many cases it produces an empty result set, or attempts to match against a raw string (the IRI path) instead of the reference document.

**Impact**
The IriFilter is unusable on ODM collections that reference entities using a custom API identifier. This breaks common API design patterns where the API surface uses a business identifier (e.g., a code or slug) rather than the internal MongoDB `_id`. The ORM version of the same filter works correctly for similar setups, so this appears to be an ODM-specific defect.

**Additional context**
The problem seems to be that the IRI-to-document resolution step does not obtain a proper document object when the identifier field is not the ODM id, so the filter build receives a string instead of an object and produces an incorrect query. The same resolution logic for item providers (used during normal GET /agents/AGENT_001) already handles custom identifiers correctly—likely the IriFilter’s parameter resolution path is missing analogous support for custom identifiers when the resolved value is used to build the filter match.

fix(doctrine): IriFilter ignores custom ApiProperty identifier on ODM (#7937)


**Title:** Problem serializer crashes on unrecognized server-error status codes

**Description:**
When the API Platform problem serializer handles a response with a 5xx status code that is not defined in Symfony’s status text mapping (e.g., `509 Bandwidth Limit Exceeded`), the error normalizer fails with a PHP `Undefined index` notice. This prevents the API from returning a proper RFC 7807 problem detail; instead the client might receive a raw PHP error or a broken response, potentially exposing internal details.

**Expected behavior:**
For any server error status code (500–599) that lacks a dedicated status text, the normalizer should safely fall back to the generic `500 Internal Server Error` text. This ensures the API always returns a valid problem detail with a safe, non‑revealing message, consistent with the existing logic that hides original error messages for 5xx responses when debug mode is off.

**Impact:**
Applications that generate non‑standard 5xx codes (either directly or through proxies) are at risk of leaking exception fragments or server‑side notices. The bug affects the core problem‑response normalization, undermining the library’s security‑hardening of error output.

fix(serializer): error status text defaults to 500 (#4277)


# Bug: Pattern parameter priority not respected in parameter ordering after expansion

**Summary**
When defining a query parameter with a pattern placeholder (e.g., `order[:property]`) and a specific priority, the expanded individual parameters (like `order[name]`) correctly inherit the parent’s priority **value**, but they do **not** appear in the correct position when the parameter collection is iterated. The collection order should reflect priority (highest first), yet expanded parameters end up at the end of the list regardless of their inherited priority.

**Steps to reproduce**
1. Annotate an API resource with a `GetCollection` operation containing at least two query parameters:
   - A pattern parameter such as `order[:property]` with `priority: 10`.
   - A static parameter (e.g., `q`) with `priority: 0`.
2. Compile the metadata and iterate over the resulting `Parameters` collection.

**Expected behaviour**
The expanded parameters (e.g., `order[name]`) that inherit a priority of 10 should appear **before** the static parameter with priority 0 when iterating over the parameters in a priority-ordered fashion.

**Actual behaviour**
The expanded parameters appear after the static parameter despite having a higher priority. The iteration order is not updated to reflect the inherited priorities after pattern expansion.

**Impact**
- The OpenAPI/Swagger documentation output will list parameters in an incorrect order.
- Runtime consumers that rely on parameter ordering (e.g., some filter processors or serialization paths) may behave unexpectedly.

**Context / constraints**
- All parameter objects, including pattern-expanded ones, already carry a correct priority value; the issue is only about the collection’s traversal order.
- After pattern expansion, the `Parameters` collection should be re-sorted to respect priority (with explicit priorities taking precedence over the default priority when none is set).
- The fix must work for any `[:property]` pattern placeholder, not just the `order` example.

fix(metadata): sort parameters by priority after pattern expansion (#7788)


**Title:** JSON-LD streaming output uses wrong `@type` values and ignores `genId: false` for embedded resources

**Environment**
API Platform Core with `jsonStream: true` on an operation, producing `application/ld+json` responses.

**Description**

1. **`@type` always uses the short class name instead of the configured `types` IRI**
   When a resource or operation defines `types: ['https://schema.org/Product']`, the streamed JSON‑LD output contains `"@type":"Product"` rather than the expected `"https://schema.org/Product"`. Linked data clients rely on the explicit type URI, so this breaks semantic interoperability.

2. **Embedded resource objects unconditionally include `@id` even when `genId: false`**
   Properties that embed another API resource and are annotated with `genId: false` (indicating the child should not receive its own IRI) still produce an `@id` field in the streamed output. The expected JSON‑LD should omit `@id` entirely for such anonymous resources.

3. **`@type` for nested/embedded resources is resolved incorrectly**
   When the embedded class has its own `types` configuration (e.g., `types: 'https://schema.org/AggregateRating'`), the generated `@type` for the nested object is either missing or uses the short class name instead of the configured IRI.

4. **Hard‑coded `'id'` virtual property mapping for `@id` and `@type`**
   The internal mapping for the `@id` and `@type` JSON‑LD properties uses a fixed virtual property name `'id'` to refer to the identifier, regardless of the resource’s actual identifier property (e.g., `code`). This can lead to collisions or incorrect behaviour when the resource identifies itself by a different property.

5. **JSON streamer feature detection is incomplete**
   The condition that enables the JSON streamer only checks for `Symfony\Component\JsonStreamer\JsonStreamWriter` but misses `Symfony\Bundle\FrameworkBundle\Controller\ControllerHelper`. This causes the streamer to be “enabled” in environments where the necessary HTTP stack is not available, resulting in runtime failures.

**Expected Behaviour**

- **Root resource:** `@type` should use the `types` attribute value if it is defined and contains exactly one IRI; otherwise it should fall back to the operation/resource short name. If `types` contains multiple IRIs, the full array should be emitted.
- **Embedded resource that is an API resource class:** `@type` should be resolved from that class’s own metadata (operation types, falling back to short name).
- **`genId: false` on a property:** The corresponding embedded object must NOT contain an `@id` key.
- **Identifier mapping:** The virtual property that backs `@id`/`@type` generation should reference the resource’s declared identifier property, not a hard‑coded `'id'`.
- **Feature detection:** The JSON streamer should only be enabled when both `JsonStreamWriter` **and** `ControllerHelper` exist.

**Steps to Reproduce (conceptual)**
1. Define a resource with `types: ['https://schema.org/Product']` and `jsonStream: true`.
2. Add a property that embeds another resource class and mark it with `genId: false`.
3. Give the embedded resource its own `types` IRI (e.g., a schema.org type).
4. Stream the primary resource as `application/ld+json`.
5. Inspect the output:
   - `@type` of the root uses the short name, not the schema IRI.
   - The embedded object still has `@id` despite `genId: false`.
   - The embedded object’s `@type` is either missing or incorrect.

**Impact**
Any consumer relying on correct JSON‑LD type URIs (search engines, linked data tools, schema.org validators) will reject or misinterpret the output. The `genId` contract is also silently violated, forcing clients to receive IRIs where none are intended.

fix(jsonld): various json streamer fixes (#7374)


**Title:** HAL: circular reference detection and default handling
**Component:** Serializer – HAL
**Type:** bug fix / missing feature

### Problem
When serializing resources in HAL format that contain circular references (e.g. `ResourceA` → `ResourceB` → `ResourceA`), the HAL normalizer does not detect the cycle and either recurses indefinitely or fails with a generic error. MaxDepth alone is not sufficient because cycles can reappear within the allowed depth.

### Expected behavior
The HAL normalizer should respect the same Symfony Serializer context options as other normalizers:

- `AbstractNormalizer::CIRCULAR_REFERENCE_LIMIT` (default = 1)
- `AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER`

When the configured limit is reached for a given object instance, the normalizer should call the handler (if set) or use a built-in default that produces a minimal, valid HAL representation **instead** of embedding the object again.

### Default HAL representation for circular references
If no custom handler is provided, the normalizer should represent the repeated object as a self link:

```json
{
  "_links": {
    "self": { "href": "/path-to-resource" }
  }
}
```

If the resource IRI cannot be generated, a safe fallback (e.g. `null`) should be returned to avoid throwing an exception during normal flow.

### Implementation constraints
- Detection must use an object-instance identity check (e.g. `spl_object_hash`) with a counter stored in the serialization context.
- The counter storage should be namespaced (e.g. a dedicated context key) so it does not conflict with the base Symfony normalizer’s own circular reference counters when the same normalizer is used across formats.
- The default handler should be registered in the normalizer’s constructor so that user-configured handlers are still honored, and the default only applies when nothing else is set.
- The fix should only affect the HAL serialization path; other formats (JSON-LD, JSON:API, etc.) should continue to work as before.

### Acceptance criteria
- Serializing a mutually-referencing resource graph in HAL no longer results in infinite recursion or a stack overflow.
- When the circular reference limit is reached, the output includes a self link instead of an embedded object.
- A user-provided circular reference handler works as expected and, if set, takes precedence over the HAL default.
- The normalizer still works correctly for non-circular HAL resources and does not degrade performance.

---

*This is a core serialization reliability improvement. The fix should be contained inside the HAL ItemNormalizer and leverage the existing IriConverter to build the self link.*

fix(hal): detecting and handling circular reference (#6752)


The GraphQL layer in API Platform currently treats all query operations (item fetches and collection fetches) under the same generic operation key `query`, which causes several incorrect behaviors:

- **Type names in the GraphQL schema are wrong.**
  A non-collection item query exposes a type named after the resource (e.g., `DummyFriend`), but it should expose `DummyFriendItem` to clearly distinguish it from `DummyFriendCollection`. This leads to confusion and potential client-side caching issues.

- **Configuration scoped to specific operation names is ignored.**
  When a resource defines filters, normalization context, or DTO input/output classes under the `collection_query` or `item_query` keys, those settings are not applied. Instead, only settings under the generic `query` key are used, because the operation name is being replaced by a fallback value (`query`).

- **Property metadata context is incorrect.**
  The `graphql_operation_name` context passed when reading property metadata depends on a chain that includes a fallback to `'item_query'`, which overrides the intention of collection queries. This can lead to properties being incorrectly marked as readable/writable or having the wrong serialization groups.

- **The output class for DTOs is not resolved per operation.**
  When an output DTO class is configured for a specific query operation (e.g., `item_query`), the system still reads the configuration for the generic `query` operation, so the DTO is not used.

The expected behavior is:

1. Queries that return a single item use the operation name `item_query` for metadata lookup (filters, normalization context, DTO classes, property settings) and expose a GraphQL type suffixed with `Item` (e.g., `DummyFriendItem`).
2. Queries that return a collection use the operation name `collection_query` for metadata lookup and expose a type suffixed with `Collection`.
3. Mutations that reference a query type (e.g., to decide the wrapped output type) should respect the specific item query type when no dedicated mutation normalization context exists.
4. No implicit fallback to `'query'` or `'item_query'` should override the actual operation name provided by the schema builder; if a caller passes a null operation name, the system must handle it without assuming it is a specific type of query.

Implement the necessary adjustments so that configuration and type generation honor the distinct operation names for item and collection queries. The fix should ensure that all GraphQL-related metadata (filters, normalization context, DTO input/output classes, property readability) are resolved using the operation name as-is, without introducing new fallbacks that would conflate item and collection behaviors again.

Fix some issue with the separation of query (#3053)


**Title:** Documentation endpoint returns OpenAPI v3 for non‑JSON formats when `spec_version=3`

**Report**

When the API documentation entrypoint is requested with `?spec_version=3` and an `Accept` header that negotiates a format other than `json` (for example `application/ld+json`, which maps to `jsonld`), the response is an OpenAPI 3 document serialized as JSON. The OpenAPI representation should only be produced when the requested format is `json`. For other formats the endpoint must keep serving the corresponding documentation representation (Hydra, etc.), ignoring `spec_version=3` for the purpose of switching to OpenAPI.

**Expected behaviour**

* OpenAPI v3 output is returned only when the request format is `json`.
* When the format is anything else (e.g. `jsonld`, `html`, …), the normal documentation pipeline is used, even if `spec_version=3` is present.
* Access to the `spec_version` key must be guarded against an undefined‑index notice (e.g. via null‑coalesce).

**Location**

The logic lives in `DocumentationAction::__invoke`. Currently the branch that invokes the `OpenApiFactory` is solely gated on `3 === $context['spec_version']`; it needs an additional condition that verifies the request format equals `json` (as reported by `$request->getRequestFormat()`).

Fix #3844 open api shows with jsonld normalizer


When using API Platform alongside other serializers, the `MetadataAwareNameConverterPass` incorrectly replaces the global serializer name converter instead of only affecting the default serializer. This causes custom serializers to pick up the API Platform name converter (including the Hydra prefix decorator) and break.

**Desired behavior:**
- The metadata-aware name converter should be applied only to the default `serializer` service, not to any other serializer services.
- If the application has set a custom name converter via the `api_platform.name_converter` alias, it must still be used as the fallback for the metadata-aware converter, and the default serializer's name converter should be set to that custom converter (via the `.serializer.name_converter` parameter).
- Other serializers must remain unaffected.

**Non-obvious constraints:**
- Setting the fallback on the metadata-aware converter should work whether or not a custom `api_platform.name_converter` alias exists.
- The changes must not interfere with the existing decoration chain (e.g., `HydraPrefixNameConverter` decorating `api_platform.name_converter`).

fix(symfony): only set name_converter for the default serializer (#6101) (#7365)