```
Title: Support SerializeConfig.fieldBased in fastjson1-compatible serialization

The fastjson 1.x `SerializeConfig` had a `fieldBased` flag
(`com.alibaba.fastjson.serializer.SerializeConfig`) that, when set to `true`,
caused serialization to write field values directly rather than calling getter
methods.  The v1-compatible module (`fastjson1-compatible`) currently ignores
this flag; all `JSON.toJSONString(Object, SerializeConfig, ...)` overloads
always use getter-based access, which breaks compatibility for classes where
getters are not appropriate – for example when a getter throws an exception
or returns a value that differs from the backing field.

We need to teach the v1-compatible serialization path to honor
`SerializeConfig.fieldBased`.  Specifically:

- When a `SerializeConfig` instance is constructed with `fieldBased = true` and
  passed to any `JSON.toJSONString` overload that accepts a `SerializeConfig`,
  the serializer must write field values directly, using the field name as the
  JSON key.  The `fieldBased = false` default should preserve the current
  getter-based behaviour.
- The serialization must work for arbitrary objects serialized through
  `writeAny`, which means the underlying writer resolution must be aware of the
  `FieldBased` feature so that an appropriate field-based writer is selected
  (similar to how other writer features influence the writer factory).

This is a genuine compatibility gap; fastjson 1.x users rely on
`SerializeConfig(true)` to safely serialize objects whose getters may not be
safe to call or where direct field access is required.
```

improve fastjson 1.x compatible, support SerializeConfig.fieldBased


**Feature Request: Polymorphic deserialization with `seeAlso` should support numeric type discriminators**

**Background**

The `@JSONType` annotation supports polymorphic types through `seeAlso` and `typeKey`. Subtypes specify a `typeName` (a string) that is expected as the value of the `typeKey` field in the JSON. Currently, this works when the discriminator value in the JSON is a quoted string (e.g., `"aniType": "1"`). However, if the discriminator field is numeric—for example, an `int` field `aniType` whose value is `1`—the JSON will contain a number literal (`"aniType": 1`). In that case, deserialization fails to map the number back to the correct subtype.

**Desired Behavior**

When a `seeAlso` polymorphic hierarchy uses a numeric `typeKey` field and the subtype `typeName` is a string representation of a number (e.g., `"1"`, `"2"`), the JSON parser should recognize the numeric token, convert it to a string, match it against the registered subtype `typeName`s, and instantiate the correct subclass. The numeric discriminator value must also be set on the resulting object (since the `typeKey` field is numeric).

**Example**

```java
@JSONType(seeAlso = {Dog.class, Cat.class}, typeKey = "aniType")
public class Animal {
    public int aniType;
}

@JSONType(typeName = "1")
public class Dog extends Animal {
    public String dogName;
}

@JSONType(typeName = "2")
public class Cat extends Animal {
    public String catName;
}
```

After serialization, a `Dog` produces `{"aniType":1,"dogName":"..."}`. Deserialization of that JSON back to `Animal.class` should yield a `Dog` instance with `aniType == 1`.

**Acceptance Criteria**

1. **Polymorphic resolution with numeric discriminators**
   Given a parent class annotated with `@JSONType(seeAlso = {...}, typeKey = "...")` and subclasses annotated with `@JSONType(typeName = "<numeric_string>")`, where the `typeKey` field is a numeric type (`int`, `long`, etc.):
   - Deserializing JSON that contains a numeric value for that key must correctly instantiate the expected subclass.
   - The numeric field must be set to the value present in the JSON.

2. **Backward compatibility**
   Existing usage with string discriminators (`"aniType": "dog"`, `typeName = "dog"`) must continue to work without change.

3. **Matchers on numeric value**
   The matching must work by converting the JSON number to its string representation (e.g., `1` → `"1"`) and comparing it against the `typeName` strings of the registered subtypes. No change to the annotation model is required—`typeName` remains a `String`.

4. **No impact on unrelated type resolution paths**
   The change must not affect standard polymorphic deserialization that does not rely on `seeAlso`, nor should it interfere with auto-type features.

seeAlso support number typeName, for issue #1488


When a field is annotated with `@JSONField(serializeUsing = SomeWriter.class)`, the custom `ObjectWriter` should be used for any runtime value whose class is the declared field type or a subclass of it. Currently the custom writer is used only when the runtime type exactly matches the declared field type; when the actual object is a derived class the custom writer is silently ignored and the default writer is used instead.

This should behave similarly to how custom writers for `Map` and `List` fields already handle subtype instances (e.g., `HashMap` or `ArrayList`). The custom writer should be reused for a subclass when:

- the field's declared type is a concrete class (not a type variable or generic array), and
- the `WriteClassName` feature is not enabled (to avoid interfering with type-name output).

JSONField#serializeUsing support derived class


**Title:** smartMatch fails for Lombok `@Setter` on fields that start with an uppercase letter

**Description:**
When deserializing with `smartMatch` (the default), fastjson2 does not correctly bind JSON keys to Java fields whose names start with an uppercase letter when the setters are generated by Lombok’s `@Setter`. For example:

```java
@Setter
class TestModel {
    private String OneDemo;
    private String TDemo;
    private String ThDemo;
}
```

Parsing `{"OneDemo":"1","TDemo":"2","ThDemo":"3"}` with `JSON.parseObject(json, TestModel.class)` should populate all three fields, but currently they remain null.

The issue is that the setter `setOneDemo()` is interpreted as targeting a property named `oneDemo` (lowercase start) under standard bean conventions, and no explicit name mapping is provided by annotations. However, the actual field name `OneDemo` (exactly the string after removing the `set` prefix) exists in the class. In this case the deserialization binding should treat the exact setter-derived name as an alternative name for the property so that the raw JSON key `"OneDemo"` can match.

**Expected behaviour:**
When creating a `FieldReader` from a setter method, if neither `FieldInfo.fieldName` nor `FieldInfo.alternateNames` is set by annotation processors, and the method name starts with `"set"`, fastjson2 should check whether a declared field with the **exact** name derived from the setter (the substring after `"set"`) exists in the target class. If such a field is found, it should be added as an alternate name so that smart matching succeeds.

This fallback must not override any explicitly configured names (from `@JSONField` or similar) and must only trigger when both `fieldName` and `alternateNames` are `null`. The fix should also be safe for non-Lombok classes — it must not change behaviour when the derived name does not correspond to an actual field.

**Acceptance criteria:**
- The provided `TestModel` example deserializes correctly.
- Existing tests for explicit field naming and smart matching continue to pass.


**Feature Request: Add `JSON.copy(T, JSONWriter.Feature...)` for deep-copy via fastjson2 ObjectWriter/ObjectReader**

**Summary**
Provide a new static utility method `JSON.copy` that creates a deep copy of a Java object by leveraging fastjson2's existing `ObjectWriter` and `ObjectReader` infrastructure. This allows users to duplicate complex object graphs without implementing clone logic, and naturally respects fastjson2’s conventions for bean access, value types, and type preservation.

**Signature**
```java
public static <T> T copy(T object, JSONWriter.Feature... features)
```

**Observable Behavior / Acceptance Criteria**

1. **Null safety**
   `JSON.copy(null)` must return `null`.

2. **Immutable / primitive value objects returned unchanged**
   When the input is a known immutable value type (e.g. `String`, `Integer`, `Long`, `BigDecimal`, `Boolean`, `Double`, …), the same object instance must be returned – no unnecessary allocation.

3. **Deep copy for Java beans**
   - A new instance of the bean’s class is created.
   - Every readable field/property value is copied (recursively) and written to the new instance.
   - After copying, mutating the original bean must not affect the copy.

4. **`FieldBased` feature**
   When `JSONWriter.Feature.FieldBased` is passed, field-based access (direct field access) must be used for reading/writing bean data instead of getters/setters.

5. **`BeanToArray` feature**
   When `JSONWriter.Feature.BeanToArray` is passed, the copy process must respect array-style bindings (like fastjson2’s bean-to-array mode) for both serialization and deserialization during the copy.

6. **Collections, maps, and generic containers**
   A new container instance is created, and each element/entry is recursively copied. The copy must be independent of the original.

7. **fastjson2 `JSONObject` / `JSONArray`**
   `JSONObject` and `JSONArray` inputs must be deep-copied; the copy must contain equal key-value pairs / elements but be a separate mutable instance.

8. **Fallback for other types**
   If the object’s type is not handled by a direct field-copy fast-path (e.g. certain complex or third-party types), the implementation must still produce a correct deep copy. The expected approach is to serialize the object to JSONB bytes (using `WriteClassName` to preserve concrete types) and deserialize back, applying the same `features` and respecting `SupportAutoType` / `SupportClassForName` on the reading side, with `SupportArrayToBean` enabled when `BeanToArray` is requested.

9. **Recursion**
   The copy must be fully recursive: nested beans, collections, maps, and arrays must all be deep-copied, not shared.

10. **No external side effects**
    The method must not modify the original object, and must not leave any short-lived resources open (e.g. streams, buffers).

**Context**
This feature was requested in issue #668. The new API should be added to the `JSON` interface in `com.alibaba.fastjson2`. All behavior must be compatible with the existing `ObjectWriter`/`ObjectReader` infrastructure and JSONB round-trip guarantees.

support new API JSON#copy, use ObjectWriter & ObjectReader copy java object, for issue #668


When `ContextAutoTypeBeforeHandler` is used to allow specific types instead of globally enabling `SupportAutoType`, deserializing JSONB‑encoded exceptions (e.g., custom exceptions with typed causes) fails with an “autoType not support” error, even though the type is listed in the handler.

Additionally, objects that contain a `List<SomeType>` field serialised via `JSONB` may deserialise incorrectly: the items inside the list end up as `Object` or as a different generic type instead of the type declared on the field. This is especially noticeable when the list’s runtime class is `java.util.Arrays$ArrayList` (the type returned by `Arrays.asList()`), because that class is not treated as a basic allowed type by `ContextAutoTypeBeforeHandler`.

**Expected behaviour**
- With a `ContextAutoTypeBeforeHandler` installed, exception types and their causally‑linked types that are in the handler’s allow list should deserialise successfully, just as they would when `SupportAutoType` is enabled globally.
- When a field has a generic list type (e.g., `List<Foo>`), the deserialised list items must be interpreted as the field’s declared type argument, not as the list’s own generic parameter or as `Object`.
- `java.util.Arrays$ArrayList` should be available as a “basic” type when the handler is constructed with `includeBasic = true`, so that lists created with `Arrays.asList(...)` can be deserialised without manual allow‑listing.

**Impact / context**
This affects any user migrating from the global `SupportAutoType` feature to the per‑type allow‑list approach provided by `ContextAutoTypeBeforeHandler`. Exceptions with nested causes and beans with generic list fields no longer round‑trip correctly under JSONB.

fix Exception/List deserialize with ContextAutoTypeBeforeHandler


When JSON Schema validation reports a violation of `exclusiveMinimum` or `exclusiveMaximum`, the error message uses inclusive operators (`>=` / `<=`) instead of the correct exclusive operators (`>` / `<`).

For example, given a schema with `"maximum": 10, "exclusiveMaximum": true` and an input of 11, the validation correctly fails but the message misleadingly states `expect <= 10` rather than `expect < 10`. The same issue exists for `exclusiveMinimum` (message says `expect >= X` instead of `expect > X`).

This affects both integer schemas (`IntegerSchema`) and number schemas (`NumberSchema`) across all `validate` overloads. The underlying boundary check is already correct; only the error message text needs to be updated to reflect the exclusive bound when the corresponding exclusive flag is true.

fix tip when exclusiveMaximum or exclusiveMinimum is true


Add support for a `ContextNameFilter` that can transform the serialized field name with access to the field's metadata (`BeanContext`), just as `ContextValueFilter` already provides context for value transformation. Users should be able to pass a `ContextNameFilter` to `JSON.toJSONString` (or configure it on a `JSONWriter.Context`) and have the filter's logic applied to each serialized field name.

Requirements:
- The filter interface should extend `Filter` and expose a single method: `String process(BeanContext context, Object object, String name, Object value)`.
- The `BeanContext` parameter must provide all field metadata available during serialization: the owning bean class, the field or method being serialized, the field's declared name, its label, its type, its format string, any associated features, and access to its annotations (e.g., `@JSONField`).
- The filter must be discoverable and configurable via `JSONWriter.Context`, analogous to how `ContextValueFilter` is set. When a `ContextNameFilter` instance is added (e.g., via `configFilter`), it should be stored and available for serialization.
- The filter must be taken into account in the writer's filter-presence check (`hasFilter`), so that the writer correctly decides when to use a filtered code path.
- If both a standard `NameFilter` and a `ContextNameFilter` are configured, the `NameFilter` should run first. The `NameFilter` transforms the raw field name; the `ContextNameFilter` then receives the result of `NameFilter` (or the original name if no `NameFilter` is present) as the `name` argument. This ensures compatibility and predictable ordering.
- The same `BeanContext` instance may be re-used or lazily created; if both `ContextNameFilter` and `ContextValueFilter` are configured for the same field, they should share the same `BeanContext` object to avoid unnecessary recreation.
- The filter must integrate correctly with property pre/post filters and property filters: if a field is excluded by a `PropertyPreFilter`, the name filter should not be invoked; if the `ContextNameFilter` returns `null`, the field should be excluded (the existing null-name exclusion logic should apply); if the name after filtering differs from the original field name, the writer should handle the rename correctly.
- The implementation must not break existing behavior when no `ContextNameFilter` is configured, and must not change the serialization order or performance for unfiltered usage paths.

Out of scope: JSONB path handling; this feature is for the JSON serialization path. The filter does not need to affect deserialization.

support ContextNameFilter, for issue #484


```
java.sql.Time fields annotated with @JSONField(format=...) are not serialized or deserialized correctly. The format annotation should be respected for both reading and writing, matching the existing behavior of java.sql.Timestamp fields.

Supported format specifiers:
- Custom patterns, e.g. "HHmmss" (time-only pattern)
- "iso8601": read a full ISO 8601 date‑time string; write the full date‑time derived from the Time's epoch millis
- "millis": read/write as a numeric epoch‑milliseconds value
- "unixtime": read/write as numeric epoch‑seconds value

Reading:
- When the JSON token is a number, interpret it as milliseconds by default, but as seconds if the format is "unixtime".
- When the JSON token is a string, parse it according to the format (ISO 8601, custom pattern, etc.).
- Null values must produce `null`.

Writing:
- The field‑level format must take precedence over any global date‑format settings (but still respect global defaults when no field format is set).
- For "unixtime": output seconds (not milliseconds).
- For "millis": output epoch‑milliseconds.
- For "iso8601": output a full ISO 8601 date‑time string (do not drop the date portion).
- For a custom pattern that does not contain a day‑of‑month component (e.g. "HHmmss"): format only the time part, but still apply the formatter correctly rather than falling back to `toString()`.
- When no format is specified, the existing default behaviour (write as the standard time‑string representation) should be preserved.

Round‑trip requirement: a Time value serialized with any of these formats must be deserializable back to an equal Time value.
```

bug fix for java.sql.Time codec, fix #164


**Title:** Support parsing fastjson1-style binary byte[] values in generic object reader

**Background**
When fastjson 1.x serialises a `byte[]` with `SerializerFeature.WriteClassName`, it emits an auto-type descriptor and a binary token representation (typically a hex string prefixed with `'x'`). Users migrating to fastjson2 need `JSON.parse()` (and the underlying generic ObjectReader) to deserialise these strings back to `byte[]`. Currently such input either throws an error or produces a `String` instead of the expected array.

**Observed behaviour**
- Serialise `byte[]` via fastjson1’s `JSONObject.toJSONString(data, SerializerFeature.WriteClassName, SerializerFeature.NotWriteDefaultValue)`.
- Call `com.alibaba.fastjson2.JSON.parse(jsonString)` on the result.
- The returned object is **not** a `byte[]` equal to the original; parsing fails or yields an incorrect type.

**Expected behaviour**
The generic ObjectReader used by `JSON.parse()` should recognise the binary value token starting with `'x'` and delegate to `JSONReader.readBinary()` to reconstruct the original `byte[]`.

**Scope & constraints**
- Only the text / String-based parsing path needs the addition; the JSONB path is already separate.
- The fix must be compatible with the existing `JSONReader.readBinary()` which already understands the fastjson1 binary serialisation format.
- No new public API is required; only a missing value-handling case inside the generic object reader.

**Acceptance criteria**
- A fastjson1-produced binary string (e.g., `"x010203"`) parsed via `com.alibaba.fastjson2.JSON.parse()` yields a `byte[]` with the original bytes.
- No regression for other value types (null, numbers, strings, dates, sets, etc.) already handled in the same reader.


**Issue:** `Sheet.setActiveCell()` for `.xls` workbooks appears to update the active cell in memory (`getActiveCell()` returns the new address), but after writing the workbook and reading it back, the active cell is not preserved (falls back to `A1`). The `.xlsx` format round-trips the active cell correctly. Fix the HSSF persistence layer so the active cell survives a save/load cycle for `.xls`.

Fix setting active cell in .xls by populating field_6_refs whenever row/column changes, this fixes bug 61905


## Implementation request

**Fix `XSSFColor.hashCode()` to be consistent with `equals()`**

`XSSFColor`'s `hashCode()` currently returns `ctColor.toString().hashCode()`, but `equals()` compares logical color properties (auto, indexed, RGB, theme, tint). This violates the Java contract: two objects that are `equals()` may have different hash codes. The problem manifests when `CTColor` objects differ only in XML artifacts that do not affect the logical color—for example, a namespace attribute on the element—so the `toString()` representations diverge while the colors are logically equal.

Change `hashCode()` to derive the hash from the same logical color facets that `equals()` uses, ensuring that two `XSSFColor` instances with logically equivalent color definitions produce the same hash code regardless of how the underlying `CTColor` was constructed (e.g., programmatic `setRgb` vs. parsing XML containing custom namespace declarations).

Fix equals() and hashCode() contract violation in XSSFColor (#1149)


Add support for the Excel `AREAS` function to the formula evaluator.

`AREAS(reference)` returns the number of areas in the supplied reference — a single cell or contiguous range counts as 1, a union of multiple ranges (e.g., `(B2:D4, E5, F6:I9)`) counts the individual constituent ranges.  The function takes exactly one argument; calls with zero arguments should produce a `#VALUE!` error.

The formula parser already handles the `AREAS` token and unions, but evaluation is not yet implemented, so formulas using it currently fail.  Please implement the evaluation so that all of the above cases return the correct numeric count.

[githib-107] add areas function support. Thanks to Inji Hanbin. This closes #107


**Title:** HSSF Workbook with missing `TabIdRecord` causes incorrect record index tracking and potential `ClassCastException`

**Summary**
When loading certain `.xls` workbooks that do not contain a `TabIdRecord` (for example, the sample `55982.xls`), the internal `WorkbookRecordList` still tracks a position for a non-existent `TabIdRecord`. This can lead to a `ClassCastException` or other incorrect behaviour when the workbook is later modified (e.g. when adding/removing sheets or editing pictures) because `InternalWorkbook.fixTabIdRecord()` attempts to retrieve a record at an index that actually holds a different record type.

**Steps to Reproduce**
1. Open an `.xls` workbook that has no `TabIdRecord`.
2. Inspect the `WorkbookRecordList.tabpos` field after loading – it will be `0` (the default), even though no `TabIdRecord` exists.
3. Call any operation that triggers `InternalWorkbook.fixTabIdRecord()`. Because `tabpos` is `0`, it retrieves the record at index `0`, casts it to `TabIdRecord`, and may throw a `ClassCastException` if the record at index `0` is something else (e.g. a `ChartRecord`).

**Expected Behaviour**
- If no `TabIdRecord` is present in the workbook, `tabpos` should reflect that absence with a distinct sentinel value (e.g. `-1`), and `fixTabIdRecord()` should short‑circuit before accessing the record list using that sentinel.
- Inserting other records (even at the front of the list) should not change the sentinel; the position should remain `-1` until a `TabIdRecord` is explicitly added.

**Additional Context**
The problem was noticed while implementing support for picture editing in HSSF documents, but it can affect any path that calls `fixTabIdRecord()`. The fix should ensure:
- The default value of `tabpos` is a clear “not found” sentinel.
- `fixTabIdRecord()` checks for that sentinel *before* fetching a record, avoiding any misinterpretation.
- A unit test should be added that opens `55982.xls`, asserts that `tabpos` is the sentinel, adds an arbitrary record to the front of the list, and verifies that `tabpos` does not change.

**Environment**
Apache POI trunk. Java 8+. The sample file `55982.xls` is available in the test-data repository.

Fix bug with record indexes for HSSF Workbooks


**Title:** `OPCPackage` should be safe for try-with-resources and idempotent close after `revert()`

**Background**
`OPCPackage` implements `Closeable`, so users naturally expect to use it with try-with-resources. However, for packages opened in read-only mode (`PackageAccess.READ`), the `close()` method currently delegates to `revert()`, which immediately closes the underlying zip data source. After `revert()` returns, `close()` continues execution and attempts to perform a save operation, which fails because the underlying store is already closed.

Additionally, if a user explicitly calls `revert()` and then later the try-with-resources block calls `close()`, the same failure occurs. This makes `OPCPackage` unsafe for try-with-resources and violates the general `Closeable` contract expectation that `close()` is idempotent.

**Requested change**
Make `OPCPackage.close()` safe to call after the package has already been closed (either by an explicit `revert()` or by a previous `close()`). Specifically:

1. Add a new public abstract method `boolean isClosed()` to `OPCPackage` that returns whether the package has been closed.
2. In `OPCPackage.close()`, check `isClosed()` at the very beginning and return immediately if the package is already closed. No further logic (including the current read-only-mode warning and subsequent save attempt) should execute for an already-closed package.
3. In `ZipPackage` (the only concrete subclass), implement `isClosed()` to reflect the state of the underlying zip archive. **Edge case:** The `zipArchive` field can be `null` if the package was created from scratch but never saved or if the archive hasn’t been initialized yet. In that case, `isClosed()` must return `false` (the package was never opened from a zip and therefore cannot have been closed). Only return `true` when `zipArchive` is non-null and its own `isClosed()` method returns `true`.

**Acceptance criteria**
- A read-only `OPCPackage` opened via `OPCPackage.open(path, PackageAccess.READ)` can be used in a try-with-resources block without throwing an exception.
- Calling `close()` after an explicit `revert()` is a safe no-op.
- Calling `close()` multiple times on the same package is a safe no-op.
- No change in behavior for read-write packages or the normal save-via-close workflow.
- `ZipPackage.isClosed()` correctly returns `false` when no zip has been initialized, and `true` only after the zip has been closed.

#64301 - Allow try-with-resources with OPCPackage.revert()


```
Title: DOLLAR function does not properly apply locale-specific currency formatting

The DOLLAR worksheet function currently evaluates numbers by building a
DecimalFormat pattern that only swaps decimal/thousands separators.
This does not match Excel’s behavior for most locales:

- The currency symbol and its placement (prefix or suffix) are not
  taken from the locale; the output often guesses a fixed position.
- Negative amounts are always rendered with a leading minus instead of
  using locale-appropriate negative formatting (parentheses for US
  locale, standard minus sign otherwise).
- The output may contain non-breaking spaces (\u00a0) instead of
  ordinary spaces, which causes mismatches with expected appearance.

Expected behavior:
  Evaluate DOLLAR using the user locale’s full currency formatting, so
  that the currency symbol, symbol placement, grouping, and negative
  pattern follow the locale conventions. The function must also respect
  the specified number of decimal places (defaulting to 0 if the
  argument is negative) and replace non-breaking spaces with regular
  spaces in the final string.

Locales that are currently producing wrong results include, among
others, da_DK, es_ES, and en_US. The fix should bring the output in
line with Excel’s DOLLAR in each supported locale.
```


```
Fix WorkbookFactory.create to accept File subclass arguments.

WorkbookFactory.create(File) presently fails when passed an instance of a
subclass of java.io.File (e.g., from JFileChooser.getSelectedFile()).
The dispatch to the format-specific factory (XSSFWorkbookFactory or
HSSFWorkbookFactory) uses reflection and expects java.io.File exactly,
so a subclass causes a no-such-method error.  InputStream arguments are
already handled correctly by normalizing any InputStream subclass; apply
the same treatment for File subclass arguments so that any File instance
can be opened.

The fix must:
- treat any File argument whose runtime class is a subclass of
  java.io.File as if it were a plain java.io.File for factory method
  resolution.
- continue to work for plain File instances, File paths, InputStreams,
  and all other existing argument types.
- support both .xls and .xlsx paths, as well as password-protected
  workbooks (the readOnly boolean path).
```

#62831 Fix WorkbookFactory.create with a subclass of File, eg from JFileChooser


We need to be able to read the absolute file path that Excel 2013 and later optionally saves inside a workbook. For `.xlsx` files this is stored as the `<x15ac:absPath>` element and is already accessible through the XSSF event-model reader. For `.xlsb` files the same metadata is present but XSSFBReader provides no way to retrieve it.

Add a public method to `XSSFBReader` that returns the stored absolute path string (or `null` if the information is absent) without interfering with the normal sheet iteration or other parsing. The method must handle missing data gracefully, close the workbook input stream reliably, and should be safe to call before or after retrieving sheets.


```
Fix the text-size limit warning threshold in `org.apache.poi.openxml4j.util.ZipSecureFile`.

The `setMaxTextSize` method currently warns only when the new limit exceeds
`0xFFFFFFFFL` (4 GiB), but the actual default maximum text size is
`10 × 1024 × 1024` characters (10 MiB). The warning should fire whenever the
supplied limit exceeds *that* default, not the entry-size default. This allows
callers to lift the text cap to a genuinely large value without a false sense of
safety, while still logging a warning when the limit is set above the default.

In addition, clearly separate the default constants for entry size and text
size so that each setter warns against its own default, and the code no longer
accidentally reuses one constant for the other.
```


**Title:** Add a username-aware temp-file strategy to prevent permission conflicts in multi-user deployments

**Background**
Apache POI’s default temporary-file strategy (`DefaultTempFileCreationStrategy`) creates a shared `poifiles` directory directly under `java.io.tmpdir`. On servers where multiple operating-system users run POI applications simultaneously, this single directory can cause file-permission problems because different users may be unable to read or delete each other’s temporary artifacts.

**Request**
Provide a new `TempFileCreationStrategy` implementation that isolates temporary files per user. The strategy should behave identically to the existing default implementation except that its temp directory is `{java.io.tmpdir}/poifiles_{user.name}`, where `{user.name}` is the value of the standard Java system property `"user.name"`. All other behavior—file-creation logic, prefix/suffix handling, delete-on-exit support, etc.—must remain exactly as in the default strategy.

The existing `DefaultTempFileCreationStrategy` must be preserved unchanged in its public contract and behavior; the new strategy should be a separate class that can be activated by calling `TempFile.setTempFileCreationStrategy(…)`.

**Acceptance criteria**
- A new, separable strategy class is available in the `org.apache.poi.util` package.
- When this strategy is set via `TempFile.setTempFileCreationStrategy`, temporary files are created inside a subdirectory named `poifiles_<username>` under `java.io.tmpdir`.
- The existing default strategy continues to create files inside `{java.io.tmpdir}/poifiles` without any change in behavior.
- The intended use case—eliminating file-permission collisions when multiple OS users share the same server—is documented in the class Javadoc.

[github-704] Add UserNameAwareTempFileCreationStrategy. Thanks to TigerZCoder. This closes #704


```
POIFSFileSystem creates new filesystems with an in-memory byte-array data source. This causes OutOfMemoryError when encrypting large documents, because the entire payload must be buffered in memory before writing. We need a way to encrypt large files without hitting memory limits.

Add a variant of POIFSFileSystem that uses a temporary file for its data storage instead of a byte array. The temporary file should be created using `TempFile.createTempFile` (prefix "poifs", suffix ".tmp") and automatically deleted when the filesystem is closed. The new filesystem should be usable as a drop-in replacement for POIFSFileSystem in encryption workflows so that large payloads can be passed to `Encryptor.getDataStream` and then written out.

The change should be marked as `@Beta` / experimental, and it must not alter the existing POIFSFileSystem behavior for regular in-memory operation.
```


The `XLOOKUP` function implementation in the POI formula engine is incomplete: it does not support the optional `[if_not_found]` argument, returns the wrong error code when no match is found, and fails to handle missing/empty arguments gracefully.

Desired behavior:

1. **Match the Excel error code for missing lookups:**
   When no match is found and no `[if_not_found]` value is supplied, the function must return `#N/A` (currently it returns `#NUM!`).

2. **Support the `[if_not_found]` argument:**
   When a fourth argument is provided, it should be evaluated and returned as the result when the lookup fails to find a match. If the argument is a blank or missing cell, it should be treated as an empty string; only a genuinely supplied text/number should be used.

3. **Treat missing/blank cells as empty strings rather than causing errors:**
   The current implementation calls `OperandResolver.coerceValueToString` on the lookup value, which throws an `EvaluationException` for a `MissingArgEval` (blank cell). Similarly, any `[if_not_found]` argument that is a missing/blank cell should not cause an error but be handled as an empty string.

The syntax in Excel is:
```
XLOOKUP(lookup_value, lookup_array, return_array, [if_not_found], [match_mode], [search_mode])
```
The implementation already appears in `org.apache.poi.ss.formula.atp.XLookupFunction`, but it only accepts the three required arguments. Extend it to accept the optional `[if_not_found]` argument and fix the return values and missing‑argument handling as described. The `match_mode` and `search_mode` arguments are out of scope for now and can be ignored.


**Title:** `CellUtil.setCellStyleProperty` with `FILL_FOREGROUND_COLOR_COLOR` (or `FILL_BACKGROUND_COLOR_COLOR`) set to `null` does not clear the fill color

**Description:**
When using `CellUtil.setCellStyleProperty(cell, CellUtil.FILL_FOREGROUND_COLOR_COLOR, null)` the foreground fill colour of the cell is **not** reset. The cell continues to display the previously assigned colour. The same problem occurs for `CellUtil.FILL_BACKGROUND_COLOR_COLOR` passed with a `null` value.

**Expected behaviour:**
Setting a fill colour property to `null` should reset that fill aspect to the default (automatic) state, effectively removing any custom fill colour. The cell should appear as it would with no explicit fill applied.

**Impact:**
There is no way through `CellUtil` to clear a fill colour once it has been set, forcing callers to drop into lower-level style manipulation to achieve a simple style reset. This behaviour is inconsistent with the general expectation that `null` means “reset to default”.

CellUtil: allow fill colors to be set to null


The `CellStyleTemplate` class stores border styles drawn via `drawBorders(…)` as `BorderStyle` enum values in its internal template map (keyed by properties such as `CellUtil.BORDER_TOP`). However, there is no public API to retrieve those values back as `BorderStyle`. The only existing property getter is `getTemplateProperty(…)`, which returns a `short` and is only appropriate for numeric properties (like border color indices). Its Javadoc also incorrectly describes it as retrieving border styles.

We need an additional method that returns the stored `BorderStyle` for a given cell and property key, so that callers can read border style values without receiving a `short` that would be zero for any non-`Short` stored object. The new method(s) should:

- Look up the template map for the cell address (row/col convenience overload is acceptable).
- Return the `BorderStyle` value if the stored object is a `BorderStyle` instance, or `null` if the property is not set / not a `BorderStyle`.
- Not rely on numeric coercion; it must handle the enum objects directly.

As part of this change, fix the Javadoc of the existing `getTemplateProperty` overloads to clarify that they retrieve template properties as a `short` (not specifically border styles).

This will allow users of `CellStyleTemplate` to read the border style values that were previously only writable.


```
Title: PaneInformation should implement equals() and hashCode() based on its fields

Description:
The class `org.apache.poi.ss.util.PaneInformation` represents information about split/frozen panes in a sheet. It holds six fields: `x`, `y`, `topRow`, `leftColumn`, `activePane`, and `frozen`. The class is a plain value object with no identity beyond its data. However, it currently inherits `equals()` and `hashCode()` from `Object`, so two `PaneInformation` instances with identical values are not considered equal, and hash codes differ. This makes the class unusable as a map key or in sets, and it causes confusing behaviour when comparing pane configurations obtained from different sources.

Expected behaviour:
Override `equals()` so that two `PaneInformation` objects are equal if and only if all six fields have the same values. Override `hashCode()` to be consistent with `equals()` (i.e. equal objects must have the same hash code). The class is immutable, so the hash code can be computed once if desired, but that is an implementation detail.
```

[github-314] Add equals and hashcode to PaneInformation. Thanks to Daniel Shuy. This closes #314


We need a public `OPCPackage.open(ZipEntrySource)` factory so that OOXML files can be read from a custom zip-entry provider.  The `ZipEntrySource` interface already exists in `org.apache.poi.openxml4j.util`, but `OPCPackage` only exposes file/path‑based open overloads.  The use‑case is streaming pre‑decrypted or compressed entries (e.g., from `CipherInputStream`‑backed entries) into POI without materializing the whole OPC package in memory.

The new overload should be **read‑only** – mirror the contract and guard/finally‑close behavior of the existing `open(File, PackageAccess)` paths for `READ` access.  There is no need for zip‑bomb protection on this code path; the source is trusted by the caller.

While exercising this path, `ReadOnlySharedStringsTable.readFrom(InputStream)` will spuriously treat a wrapped stream as empty when the stream’s `available()` returns 0 despite having data (common for `CipherInputStream` and other filter streams).  That boolean‑empty test must be replaced with a one‑byte peek/pushback so that emptiness detection works regardless of the InputStream type.  Without this second fix, any `ZipEntrySource` that returns transparently‑wrapped streams will silently skip shared‑string parsing and produce broken output.

#59841 - OOXML: enable custom zip streams via OPCPackage.open(ZipEntrySource)


CellUtil currently supports setting several boolean style properties via `setCellStyleProperty`, including `LOCKED`, `HIDDEN`, and `WRAP_TEXT`. Two other boolean properties on `CellStyle` — `shrinkToFit` and `quotePrefixed` — are not yet exposed through `CellUtil`. We should add full support for them so that users can set and retrieve these properties through `CellUtil` in the same way they do for the other booleans.

The expected behavior:

- Introduce named constants for the new property keys (e.g., `SHRINK_TO_FIT` and `QUOTE_PREFIXED`), and register them as boolean-valued properties in the same manner as `WRAP_TEXT`.
- In the `CellUtil` methods that build a property map from an existing `CellStyle`, include the values from `CellStyle.getShrinkToFit()` and `CellStyle.getQuotePrefixed()`.
- In the method that applies a property map to build a new `CellStyle`, call `setShrinkToFit` and `setQuotePrefixed` with the boolean values from the map.
- When reading a serialized stylesheet, the default values for both properties are `false`. Ensure that adding these properties does not change the behavior for cells whose styles do not explicitly set them.

The existing mechanism for `WRAP_TEXT` should serve as a direct template for the changes. No new API surface beyond the constants and property support is required.

Add tests covering `setCellStyleProperty` with each property individually and in combination with other style properties, verifying that existing style settings are preserved when a single property is changed through `CellUtil`.

[github-334] Add SHRINK_TO_FIT and QUOTE_PREFIXED to CellUtil. Thanks to funakawa718. This closes #334


```
Title: NullPointerException when reading a PPTX shape with a gradient fill that lacks gradient stops

Description:
When opening a .pptx file that contains a shape with a gradient fill but no gradient stop list (<a:gsLst> element is absent or empty), Apache POI throws a NullPointerException during normal read operations (e.g. retrieving the fill color of the shape). The same issue may occur if a linear gradient fill has fewer than two gradient stops, which also leads to an invalid paint object.

Expected behavior:
A gradient fill that is missing its gradient stops should be treated as having no fill (transparent / null fill), rather than causing an exception. The library should handle the missing or insufficient gradient stops gracefully and return a null-style fill, consistent with how other degenerate gradient definitions are handled.

Additional improvements for prevention:
The stress/integration test that iterates over all slides and shapes to exercise the slide show API currently only reads text from shapes. It should be extended to:
- recursively traverse group shapes,
- call paint-related properties on simple shapes (fill color, fill style, stroke style, line decoration),
so that similar regressions in the paint pipeline are caught earlier when loading real-world documents.

Impact:
This bug makes some valid (though edge-case) PPTX files unreadable, breaking downstream applications that rely on Apache POI for slide content extraction or rendering.

Steps to reproduce (conceptual):
1. Create or obtain a .pptx file with a shape that has a gradient fill whose XML omits the gradient stop list (<a:gsLst>) or contains only one stop.
2. Load the file with XMLSlideShow.
3. Access the shape's fill color via getFillColor() or iterate through the slides for general processing.
4. Observe the NullPointerException.
```

Bug 63200: Avoid NullPointerException in XSLFShape.selectPaint() and check some more in integration tests


**Title:** Clone sheet throws exception on unrecognized relationship types

**Description**
When an XSSF workbook contains a sheet that has relationships whose type is not recognized by Apache POI (for example, custom XML parts added by Excel add-ins or locale-specific features), calling `XSSFWorkbook.cloneSheet` fails with a `POIXMLException` “Can't clone sheet - unknown relation type found: ...”. This makes it impossible to clone the sheet even when the unknown relationships are irrelevant to the caller.

**Expected behavior**
`cloneSheet` should successfully clone any sheet from a valid OOXML workbook, even when the sheet includes relationship types the library does not know about. The cloning operation should:

1. **Succeed** – no exception is thrown, and a `Sheet` instance is returned.
2. **Skip unrecognized relationships** – any relation that POI cannot map to a known `XSSFRelation` should be omitted from the cloned sheet.
3. **Log a warning** – the fact that a relationship was skipped because it was unrecognized should be logged at warning level, indicating that the cloned sheet will not contain the data associated with that relationship (the rest of the sheet content, however, must remain intact).

The clone must still contain all other content that the library understands (drawings, comments, images, etc.). The only difference from a normal clone is the absence of the parts tied to unmapped relationships.

[bug-64759] ignore unknown relationships when cloning sheets


When using `TEXTJOIN` with an omitted delimiter – for example, `=TEXTJOIN(, TRUE, A2:D7)` – the function does not return the expected concatenated result. In Excel, an empty/missing first argument means "join with no delimiter" (i.e., the delimiter is treated as an empty string). However, our implementation currently fails for this case (likely returning an error) because the missing delimiter argument loses its emptiness and is not handled as an empty string.

Expected behavior: if the delimiter argument is supplied but literally missing (just the comma, no value) the function should concatenate the text values without any separator, exactly as it would for `=TEXTJOIN("", TRUE, …)`. This should also work consistently when the delimiter comes from a range reference where cells may be empty.

Please make `TEXTJOIN` handle missing/empty delimiter arguments gracefully by treating them as empty strings, matching Excel's actual semantics for this edge case.

fix TextJoin use case that was not handled


Subject: CellAddress should support formatting as R1C1 string

Currently `org.apache.poi.ss.util.CellAddress` provides `formatAsString()` which returns the address in A1 notation (e.g., `"A1"`, `"C2"`, `"E10"`). However, there is no public method to format the address in R1C1 notation, even though the underlying `CellReference` class already has `formatAsR1C1String()`. This asymmetry is inconvenient for users who need R1C1 references, which are common in VBA macros, certain regional settings, and some OOXML formula expressions.

Expected behavior: `CellAddress` should expose a method like `formatAsR1C1String()` that returns the R1C1-style string representation. For example:

- `CellAddress.A1` → `"R1C1"`
- `new CellAddress("C2")` → `"R2C3"`
- `new CellAddress(9, 4)` (0-based row 9, column 4) → `"R10C5"`

The implementation should be consistent with how `CellReference.formatAsR1C1String()` already works, and the new method should reside in the same module (`poi`) as `CellAddress`. It would also be helpful to have Javadoc referencing the existing `formatAsString()` method for discoverability.


When an Apache POI-generated .xls (HSSF) workbook contains conditional formatting with formula‑based rules, opening the file in Microsoft Excel often shows the formatting applied incorrectly until the user manually triggers a recalculation (e.g., pressing F9, or using other recalculation‑triggering actions). This affects the out‑of‑the‑box experience and can be confusing for recipients of the file who may not know that a manual recalc is needed.

We need the generated .xls file to signal that conditional formatting needs to be recalculated, so that Excel immediately evaluates the rules and displays the correct formatting upon opening.

Acceptance criteria:
- When conditional formatting is added through the public HSSF API (including rules that use formulas, plain comparisons, and both old‑style and new‑style conditional formatting records), the resulting workbook file must instruct Excel to recalculate the sheet’s conditional formatting on open.
- This behaviour must apply to all conditional formatting groups created via the API, regardless of how many rules they contain or whether they use CFRuleRecord or CFRule12Record records.
- Existing files that are read, modified, and written back must preserve the original recalc state for any conditional formatting that was already present – the flag should only be forced to “needs recalc” for newly created conditional formatting entries.
- The fix must not break round‑tripping: saving a workbook, re‑reading it, and saving again must not change the conditional‑formatting recalc flag unexpectedly.

Note: the issue was observed with formula‑based rules, but the safest approach is to set the recalc flag whenever new conditional formatting is introduced, as Excel handles the flag correctly for non‑formula rules as well.

52122: Excel does not handle conditional formatting based on formula correctly unless recalculation is forced


In POILogger's public API, the only intended logging entry point is the public varargs method `log(int level, Object... objs)`. Internally, this dispatches to two protected abstract methods: `log(int, Object)` and `log(int, Object, Throwable)`. However, several concrete subclasses (e.g., CommonsLogger, SystemOutLogger, and test implementations) have overridden these methods with public visibility, exposing them as part of the public API. That creates a confusing and inconsistent API surface, and could lead to callers accidentally bypassing the central varargs dispatch.

Fix:
- Make the overrides of those two dispatch methods protected in all POILogger subclasses, matching the base class declaration.
- Rename the two protected abstract methods (and all overrides) to a distinct name, such as `_log`, so the internal hook methods are not confused with the public `log(int, Object...)` varargs method.
- Ensure the varargs `log` method in the base class calls the renamed methods correctly. No logging behavior or public contract should change.

bug 59933: demote accessibility of POILogger implementations of log(int, Object) and log(int, Object, Throwable) to be consistent with the POILogger abstract class


```
Add a null-safe case-insensitive string equality method to `StringUtil` that always uses `Locale.ROOT` for case conversion.

The existing `StringUtil` has `startsWithIgnoreCase` and `endsWithIgnoreCase` but no direct equals-ignore-case helper. We need a method `equalsIgnoreCase(String, String)` with the following contract:

- Two `null` arguments are considered equal.
- A `null` argument is never equal to a non-`null` argument.
- Strings of different lengths are not equal, and this should be checked before any case conversion.
- Otherwise the strings are equal if and only if they are equal after converting both to lower-case using `Locale.ROOT`. The lower-casing must use the root locale explicitly, never the JVM default locale, so that the behavior is deterministic and avoids locale-specific mismatches (e.g. the Turkish ‘İ’/‘ı’ problem).

Annotate the method `@Internal` and, since this is a new API, mark it `@since 6.0.0`. The implementation should be consistent with the existing `toLowerCase(char)` helper in the same class that already uses `Locale.ROOT`.
```

add StringUtil.equalsIgnoreCase that uses root locale


We need to fix how `XSSFWorkbook` reads and writes the “force formula recalculation” property. Right now the setter and getter for this property manipulate the `calcId` attribute on the OOXML `<calcPr>` element. That’s wrong: `calcId` is the identifier of the calculation engine version (it should stay whatever Excel expects), not a recalculation flag. The correct OOXML attribute that controls “recalculate all formulas on next load” is `fullCalcOnLoad`.

Concretely:

- `setForceFormulaRecalculation(true)` must write `fullCalcOnLoad="true"` on the `<calcPr>` element. It should not change (or zero out) the `calcId` attribute.
- `getForceFormulaRecalculation()` must return `true` only when `fullCalcOnLoad` is explicitly present and set to `true`. It should return `false` when the attribute is missing, or when it is present but set to `false`. In particular, looking at `calcId` to decide this is the root cause of the bug report: after certain sequences of operations, the method returns `true` when the workbook does **not** actually have the full-recalc flag set.
- The existing co‑ordination with calculation mode should be preserved: when forcing recalculation, if the workbook is in manual calculation mode, the setter should still switch it to automatic.
- Both the setter and getter must survive a round‑trip through write and read back (i.e., the value must be saved in the OOXML and restored correctly).

Make sure the implementation follows the OOXML schema contract for `CTCalcPr` / `calcPr`, and that tests at least cover the scenarios listed in bug #61700.

#61700 getForceFormulaRecalculation() returns wrong value


I'd like `FormulaShifter` to support column copy operations, analogous to the existing row copy support. Currently, when programmatically copying a column (or range of columns) that contains formulas, the formula references are not updated to reflect the new column positions. The class already provides `createForRowCopy`, and there is a `ColumnMove` shift mode, but no `ColumnCopy` equivalent.

Please add a new shift mode `ColumnCopy` to the `ShiftMode` enum and expose a corresponding public factory method (e.g., `createForColumnCopy`), with parameters similar to those of `createForColumnMove`. When formulas are shifted using this mode:

* Relative column references should be adjusted by the column offset of the copy operation.
* Absolute column references should remain unchanged.
* If any adjusted reference falls outside the valid column range for the spreadsheet version (negative or beyond the last column index), it should produce a `#REF!` error cell (as other shift modes do).
* The behavior for all relevant `Ptg` types (`RefPtg`, `AreaPtg`, 3D references, etc.) should be consistent with how row copy handles the analogous row cases.

The copy semantics should be self-contained: only the formulas within the copied region need to have their references shifted by the copy distance, not all references on the sheet (in contrast to column *move*, which adjusts references sheet-wide to track the moved columns).

bug 61474, github #81: add ShiftMode#ColumnCopy for FormulaShifter


The IRR function fails to converge for longer cash flow series that produce a valid result in Excel. The Newton‑Raphson solver gives up after 20 iterations, returning NaN when the true IRR exists.

Increase the maximum iteration count to a value that allows convergence on realistic long‑horizon cash flows (e.g., 1000). Add diagnostic logging for the cases where the solver still cannot converge (zero denominator, zero derivative, or max iterations exhausted) to make future issues easier to diagnose. The absolute accuracy tolerance should stay unchanged.

[bug-64137] increase max iterations for IRR


**Title:** `T.DIST.RT` incorrectly treats negative X as positive

**Problem:**
When `T.DIST.RT(X, deg_freedom)` is evaluated with a negative value for `X`, the function returns the same result as if `X` were positive. For example, `T.DIST.RT(-2, 5)` currently returns the same value as `T.DIST.RT(2, 5)`. This is wrong according to Excel’s documented behavior: for negative `X`, the right‑tailed t-distribution probability should be `1 – T.DIST.RT(|X|, deg_freedom)`, not the right‑tail probability of the positive value.

**Expected behavior:**
- For positive `X`, `T.DIST.RT(X, df)` should return the right‑tail probability `P(T > X)`.
- For negative `X`, the same call should return `1 – T.DIST.RT(|X|, df)`, as the cumulative distribution for a negative value is the complement of the right‑tail probability of the absolute value.

**Impact:**
Statistical calculations, hypothesis tests, and any Excel workbook migrated to POI that rely on `T.DIST.RT` produce incorrect results when `X` is negative. This leads to broken parity with Excel and erroneous data analysis.

**Suspected cause:**
The implementation appears to be computing `Math.abs(X)` before delegating to the underlying `TDist.tdistOneTail` method. The underlying `tdistOneTail` utility already handles negative inputs correctly by returning the right‑tail probability for the given value, so the forced absolute value should be removed.

**Acceptance criteria:**
- `T.DIST.RT(-v, df)` returns a value different from `T.DIST.RT(v, df)` for `v > 0`.
- The result matches Excel’s `T.DIST.RT` for negative `X` (within floating‑point tolerance).


Please replace the internal `GUID` class used by HSSF's `HyperlinkRecord` with the existing `org.apache.poi.hpsf.ClassID` type. The internal `GUID` duplicates basic class-ID logic that already lives in `ClassID`, and the goal is to eliminate that duplication.

There are a few things that need to work for this to be a clean swap:

- `ClassID` must be directly usable as a replacement for the internal `GUID`. That means it needs to support construction from a `LittleEndianInput` (the binary record format the hyperlink record already reads) and a method to write itself to a `LittleEndianOutput`. It also needs a proper copy/clone mechanism so that the hyperlink record can duplicate the identifier when copying itself.
- The three well-known moniker GUIDs that were defined as static constants inside `HyperlinkRecord` (`STD_MONIKER`, `URL_MONIKER`, `FILE_MONIKER`) should move into the existing `ClassIDPredefined` enum. This way they’re defined alongside other standard class IDs and remain available as named values.
- The string representation of `ClassID` must adopt the standard bracketed UUID form `{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}`. Everything else that depends on that representation should be updated to use the bracketed form.
- The binary layout on disk must stay exactly the same. Hyperlink handling should round-trip without any change to the serialized bytes. All hyperlink types (URL, file, document link, UNC) need to keep working.

Replace internal GUID class with ClassID


When importing XML data into an XSSFWorkbook via custom XML mappings, the import time grows dramatically with the number of rows. A data set with 10,000 elements can take minutes to process, effectively making the feature unusable for realistic workloads.

The import should handle up to 10,000 mapped rows in a reasonable time (well under 10 seconds). The performance should scale linearly with the number of rows in the input XML, not degrade quadratically or worse.

Fix poor XPath performance when importing XSSF from XML


## Issue: XWPFParagraph lacks line‑spacing value API

### Problem
`XWPFParagraph` provides `setSpacingLineRule(LineSpacingRule)` to set the line‑spacing rule (AUTO, EXACT, AT_LEAST), but there is no method to set or retrieve the actual spacing value. Users can only define *what kind* of spacing but not *how much*. This makes it impossible to create documents with double line spacing, exact point‑based line height, or any other non‑default interline spacing via the public API while respecting the rule.

### Desired behavior
- Add a pair of public getter and setter that accepts a `double` value plus a `LineSpacingRule`.
  - When the rule is `AUTO`, the value is interpreted as **lines** (e.g., `1.5` for 1.5‑line spacing).
  - When the rule is `EXACT` or `AT_LEAST`, the value is interpreted as **points**.
  - Internally, the underlying OOXML element stores the value differently depending on the rule:
    - `AUTO` → 240ths of a line.
    - `EXACT` / `AT_LEAST` → 20ths of a point.
  - Conversion between the user‑facing `double` and the internal units must round correctly (e.g., `Math.round(spacing * factor)`) and preserve round‑trip fidelity.
- Provide a convenience overload for `setSpacingBetween(double)` that defaults to `AUTO`, so users can easily set line‑height multiples.
- A getter should return the spacing in the appropriate natural unit. If the spacing has not been set, return a distinguished value (e.g., `-1.0`).
- The existing `getSpacingLineRule()` should reflect the rule applied by the new setters.

### Edge cases / constraints
- When no line spacing is stored in the underlying XML (null or missing), the getter must not throw; return `-1` and default rule `AUTO`.
- The setters must handle the three rules correctly; `AT_LEAST` should be treated the same as `EXACT` for unit conversion (points).
- The original `setSpacingLineRule(rule)` remains available but does **not** modify the spacing value. The new API should supersede it for typical use.
- Update the `SimpleDocument` example that previously called `setSpacingLineRule(LineSpacingRule.EXACT)` without specifying a value to use the new API with an explicit spacing.

### References
- `XWPFParagraph` class, existing `setSpacingLineRule`, `getSpacingLineRule`, `getSpacingAfter`/`setSpacingAfter` (as conversion‑pattern reference).
- `LineSpacingRule` enum: `AUTO(1)`, `EXACT(2)`, `AT_LEAST(3)`.
- The underlying `CTSpacing` element’s `line` attribute and `lineRule` attribute.

60465: Cannot specify interline spacing for a Paragraph in XWPF


The D* database functions (DAVERAGE, DCOUNT, DCOUNTA, DGET, DMAX, DMIN, DPRODUCT, DSTDEV, DSTDEVP, DSUM, DVAR, DVARP) do not support the `<>` (not equal) comparison operator in criteria. In Excel, a criterion like `<>Apple` excludes rows where the column equals "Apple" (case-insensitive), and `<>12` excludes rows where the column equals 12. The current POI implementation does not handle `<>`, so such criteria are either ignored or misinterpreted.

Add support for the `<>` not-equal operator in the criteria evaluation logic used by all D* functions. The implementation must:

- Parse the `<>` prefix from a criterion string (just like `<` and `<=`, etc., are already handled).
- Compare the value appropriately: for numeric values, perform numeric inequality; for string values, compare case-insensitively.
- The change must apply uniformly across all D* functions that share the same underlying evaluator.

[github-692] D* functions are incompatible with the diamond operator. Thanks to Luk Spiewak. This closes #692


When an `OPCPackage` is opened with write access (e.g., `OPCPackage.open(file, PackageAccess.READ_WRITE)`) and its `close()` method is called—whether directly or via try-with-resources—the library saves changes back to the original file. If the saving step encounters a failure (for example, a `PartMarshaller` throws an exception while writing a part), the original file is currently overwritten with an incomplete or corrupt package. This can destroy the user’s data.

**Required behavior:**

* During `close()`, if saving fails for any reason, the **original file must not be modified**. It must remain exactly as it was before the `OPCPackage` was opened.
* After such a failed close, the original file must still be a valid OOXML package that can be opened again with `OPCPackage.open()` without errors (no `NotOfficeXmlFileException`, no “not a ZIP archive” failures).

Bug 63029: OPCPackage Potentially clobbers files on close()


**Title:** `IOUtils.skipFully` is unreliable – must guarantee exact skip count and update callers

**Description:**
`IOUtils.skipFully(InputStream, long)` currently relies on `InputStream.skip()` and `InputStream.available()`. This makes it fragile: `skip()` is allowed to skip fewer bytes than requested (or zero) without reaching EOF, and some stream implementations can misreport `available()`. The method can therefore return `-1` even when bytes are available, or return before the full requested length has been skipped, making its contract unpredictable.

**Acceptance criteria:**

1. **Guaranteed skip semantics**
   `skipFully` must read and discard **exactly** the number of bytes specified by the caller, unless EOF is reached first. It should achieve this by using `read(byte[], int, int)` into an internal discard buffer, not by delegating to `skip()` or checking `available()`.

2. **Return value contract**
   * If the stream has at least `toSkip` bytes remaining, return `toSkip`.
   * If EOF is reached *after* some bytes have been skipped, return the number of bytes actually skipped (which will be less than `toSkip`).
   * Return `-1` **only** when the stream is already at EOF (i.e., zero bytes could be read).
   * Return `0` when `toSkip == 0` (no I/O performed).
   * Throw `IllegalArgumentException` when `toSkip < 0`.

3. **Caller adjustments**
   Existing code that calls `skipFully` and interprets a return value of `-1` as an indicator of “could not skip the full amount” **must be reviewed and updated** to correctly detect short skips. Specifically:
   * In `HemfCommentRecord.readToByteArray(...)`: after calling `readFully` and `skipFully`, check whether the returned lengths match the requested lengths. If not, throw a `RecordFormatException` indicating the stream ended before the full record could be read.
   * In `UnimplementedHemfRecord.init(...)`: change the condition from `skipped < 0` to `skipped < recordSize` to detect EOF/short skips, and throw an appropriate `IOException` (or existing exception) if the full record size could not be skipped.

4. **No regressions**
   The fix must not change the externally observable behaviour of any other `IOUtils` method. The internal implementation must not introduce unnecessary synchronization overhead.

**Reference:**
Bug 61294 – based on code review feedback, the implementation should mirror the robust approach used in Apache Commons IO’s `skipFully` (which uses a read-buffer loop). This guarantees correct skipping regardless of the underlying stream implementation.

bug 61294 -- cleaned up based on PJ Fanning's code review.  Went with a copy/paste from commons-io.


```
Bug #61007: DataFormatter mishandles some semicolon‑delimited format strings

When a cell uses a custom number format like "0 ?/?;0", DataFormatter now produces "123" for a negative value such as -123.321. Excel shows "-123 1/3" for that format and value, which was the behaviour before recent changes. The regression was introduced when we started delegating all format strings that contain a semicolon directly to CellFormat. CellFormat’s fraction‑handling for negative numbers does not match Excel in this case.

We need to tighten the delegation rule in DataFormatter so that it only hands a format off to CellFormat when the format truly requires CellFormat’s multi‑section or conditional logic, and otherwise falls back to DataFormatter’s own handling. Concretely, DataFormatter should delegate to CellFormat only for:
- format strings that contain more than one semicolon (i.e., three or more sections), or
- format strings that contain Excel conditional‑range expressions (e.g. "[>=100]").

Simple two‑part formats with a single semicolon and no conditional range should be processed directly by DataFormatter, as they were before this regression. This approach restores the original fraction‑formatting output for negative numbers while still letting CellFormat handle the complex cases that it was introduced to support.
```


```
Add a `findPictureData(byte[] pictureData)` method to the `SlideShow` interface (org.apache.poi.sl.usermodel.SlideShow) that returns the existing `PictureData` if the exact byte content is already present, or `null` otherwise. The same picture-deduplication logic should be reused when adding pictures.

- XSLF: The existing package-private `XMLSlideShow.findPictureData` already implements the logic. Make it public and override the interface method.
- HSLF: Implement the method using the same checksum comparison that `HSLFSlideShow.addPicture` currently inlines. After adding the method, refactor `addPicture(byte[], PictureType)` to call `findPictureData` instead of duplicating the search.
- Ensure both implementations behave identically: returning a previously added picture when the same bytes are added again via any `addPicture` overload (file, stream, byte array), and returning `null` when the picture is not yet in the slideshow.
```

bug 58190: push down XMLSlideShow tests to BaseTestSlideShow, add coverage for HSLFSlideShow


The EMF header parser in `HemfHeader.init()` reads `nDescription` and `offDescription` as signed 32‑bit integers (via `readUInt()` cast to `int`) and uses them to allocate a byte array and to seek to the description buffer. This can be exploited by crafted EMF files with large or misaligned `offDescription` values to cause integer overflow in the `skip` calculation, allocate an excessively large buffer (leading to `OutOfMemoryError`), or attempt to read beyond the record boundary. Additionally, the allocation bypasses the existing safe‑allocation helpers (`IOUtils.safelyAllocate` / `safelyAllocateCheck`).

Fix the description parsing to:
- Store `nDescription` and `offDescription` as `long` to avoid unsigned‑to‑signed truncation and allow correct arithmetic with large values.
- Derive `descriptionBytes = (nDescription - 1) * 2` (UTF‑16LE character width) and validate that the description region lies entirely within the record: `offDescription + nDescription*2 <= recordSize + HEADER_SIZE` and `offDescription >= size + HEADER_SIZE` (i.e. the skip is non‑negative).
- Ensure the allocation size and skip do not exceed `Integer.MAX_VALUE` (so they remain representable for the stream operations).
- Use `IOUtils.safelyAllocateCheck(descriptionBytes, maxDescriptionLength)` before allocating, where `maxDescriptionLength` is capped to `recordSize`, and use `IOUtils.safelyAllocate` to create the buffer.
- Throw a `RecordFormatException` if any bound check fails.

This prevents large/large‑valued or misaligned description fields from causing an uncontrolled allocation or out‑of‑bounds read.

Validate EMF description bounds before allocation (#1060)


Add support for Excel's `TDIST.2T` function (two‑tailed Student's t‑distribution). It is an Analysis ToolPak function taking two arguments: `X` (the numeric value to evaluate) and `Deg_freedom` (degrees of freedom, truncated to an integer).
The function must:

- Return the two‑tailed t‑distribution probability.
- Reject non‑numeric arguments with `#VALUE!`.
- Return `#NUM!` if `X < 0` or if `Deg_freedom < 1` (after truncation).
- Accept positive `X`; the implementation can take the absolute value of `X` before computing the distribution (matching real Excel behavior).

The two‑tailed t‑distribution computation itself is already available inside the existing TDIST‑related code; reuse that rather than duplicating the statistical logic.
Wire the new function into the Analysis ToolPak registry so that formulas using `TDIST.2T(…)` evaluate correctly.


**Title:** Poor error handling when `poi-scratchpad.jar` is missing during EMF/WMF rendering or PPTX2PNG conversion

**Problem:**
When using the `PPTX2PNG` utility (or the `SlideShowFactory` API) to process `.emf`, `.wmf`, or `.ppt` files without `poi-scratchpad.jar` on the classpath, the resulting errors are opaque and misleading. For example:

- `SlideShowFactory.create(…)` throws an `IOException` wrapping a raw `ClassNotFoundException` for a scratchpad factory class, with no hint that the jar is missing.
- `PPTX2PNG` silently falls back to a bitmap renderer for metafiles (`BitmapImageRenderer`), producing degraded/no output and an unhelpful “unsupported format” message.

Users cannot easily diagnose that the problem is simply the absence of the optional `poi-scratchpad.jar`.

**Expected behavior:**

1. When a scratchpad-dependent format (`.ppt`, `.emf`, `.wmf`) is requested and the required classes are not found, the API should surface a **clear, user‑actionable error** indicating that `poi-scratchpad.jar` must be added to the classpath.
2. The `PPTX2PNG` tool should **not** silently fall back to a generic bitmap renderer when scratchpad is unavailable; it should detect the missing dependency and report it meaningfully.

**Affected areas (as visible to users):**

- `org.apache.poi.sl.usermodel.SlideShowFactory` – factory methods that instantiate HSLF/EMF/WMF handlers.
- `org.apache.poi.xslf.util.PPTX2PNG` – command-line conversion for EMF/WMF metafiles and PPT slideshows.

**Desired outcome:**
Any attempt to read or render a scratchpad‑dependent file format without the required jar should result in an `IOException` (or a meaningful subclass) whose message clearly states that `poi-scratchpad.jar` is missing from the classpath, rather than exposing internal class‑loading failures or silently degrading functionality.

Catch missing scratchpad state for EMF / WMF rendering


We need to be able to distinguish, when reading macros from an OLE2 or OOXML Office document with VBA, whether each macro module is a standard module, a class module, or a document module (e.g., a `ThisWorkbook` or sheet module). Currently `org.apache.poi.poifs.macros.VBAMacroReader` only returns the raw source text via `readMacros()`, with no indication of module kind. The VBA project storage inside the file contains a stream (usually named `project`) with lines like `Document=ThisWorkbook/&H...` and `Module=Module1` and `Class=...` that classify each module. We need an enhancement to `VBAMacroReader` that exposes this classification while keeping the existing `readMacros()` method working exactly as before (returning a `Map<String, String>` of module name to source code). The new capability can be via a new method that returns module objects with both the source code and an enum value representing the module type (Document, Module, or Class). The implementation should parse the `project` stream lines, ignoring section headers (`[...]`), and associate the type with each module by name. If the `project` stream is absent or its lines cannot be parsed for a particular module, the type should probably be left null or default — but the reader should not fail and should still return the source code. Also ensure empty modules are handled gracefully, returning empty source text rather than null or throwing exceptions. Backward compatibility is critical: existing callers of `readMacros()` must see no change in the map they get.

Apply patch from bug 61096: Add support for modules in VBAMacroReader


```
Subject: XSSFTextParagraph.getBulletAutoNumberScheme() throws ArrayIndexOutOfBoundsException for out‑of‑range auto‑number type index

Description:
When reading an Excel (.xlsx) file that contains a text box whose bullet auto‑numbering uses an invalid type index (e.g. 24, which is outside the valid range of the ListAutoNumber enum), calling XSSFTextParagraph.getBulletAutoNumberScheme() throws an ArrayIndexOutOfBoundsException. This is caused by directly indexing into the ListAutoNumber.values() array with the raw type value from the underlying OOXML (CTTextAutonumberBullet) without any bounds check.

Expected behavior:
The method should handle out‑of‑range auto‑number type indices gracefully instead of crashing. A reasonable fallback would be to return a default auto‑numbering scheme, such as ListAutoNumber.ARABIC_PLAIN. This allows the rest of the workbook to be processed even if one paragraph contains an invalid bullet definition.

Impact:
Any workbook produced by a third‑party tool that writes an out‑of‑range auto‑number type index becomes unreadable with POI, even though the file may be valid according to the stricter OOXML specification. The crash prevents further processing of the entire sheet and the exception message gives no clue about which part of the document is problematic.
```

Fix ArrayIndexOutOfBoundsException in XSSFTextParagraph when auto number scheme index is out of range (#1033)


**Summary:** Support XWPFTable in headers/footers and allow empty header/footer creation (breaking change)

**Current behavior:**
When a new header or footer is created via `XWPFHeaderFooterPolicy` or `XWPFDocument.createHeader`/`createFooter`, an empty paragraph is automatically inserted into the header/footer. There is no public API to add an `XWPFTable` to a header/footer. This makes it impossible to build a header/footer that contains only a table, and the forced empty paragraph always appears as the first content.

**Desired behavior:**
1. **No forced initial content:** Creating a header/footer should produce an initially empty body (no paragraphs, no tables). This is a breaking change – existing code that relied on the automatically inserted empty paragraph must be updated to add paragraphs explicitly.
2. **Table support:** Provide a method on the header/footer classes to add an `XWPFTable`, analogous to `XWPFDocument.createTable()` or the table-adding methods on `IBody`.
3. **Output validity:** Despite the in-memory model allowing an empty header/footer, the final OOXML must remain valid. At document save time, if a header/footer would be written with no body elements, ensure that a minimal empty paragraph is added automatically. Likewise, ensure that any table cell inside the header/footer contains at least one paragraph when saved, even if the model is temporarily empty.
4. **Consistency with the body model:** Headers/footers should implement the same `IBody`-style capabilities (add/remove paragraphs and tables) as the main document body, to the extent appropriate.

**Scope:**
- The change should touch the core header/footer model classes (`XWPFHeaderFooter`, its subclasses `XWPFHeader`/`XWPFFooter`) and the policy class `XWPFHeaderFooterPolicy`.
- Update any existing examples (especially `BetterHeaderFooterExample`) and any other code that assumes the old auto-paragraph behavior to use the new API explicitly.
- Keep the fix self-contained and avoid regressions in watermark handling or picture data retrieval.


**Title:** `XSLFPictureShape` has `getName()` but no public setter

**Description**
Users can obtain the name of an `XSLFPictureShape` via the existing `getName()` method, but the class does not expose any public API to change that name. This makes it impossible to assign custom, descriptive names to picture shapes after they are created, which prevents scripted workflows that rely on shape names for identification, post-processing, or interop.

**Scenario / reproduction**
1. Create a new `XMLSlideShow` and add a picture shape to a slide.
2. Call `getName()` – the shape does have a name (often a default or inferred string).
3. Attempt to replace it with a meaningful name such as `"Company Logo"` – no `setName` method exists.

**Expected behavior**
A `setName(String name)` method should be added to `XSLFPictureShape`. After calling `setName`, subsequent calls to `getName()` should return the new name. The new name must be persisted into the OOXML structure so that it survives a save/reload cycle (i.e., round-trips through `XMLSlideShow`).

The implementation should be a natural counterpart to `getName()` and handle the case where the underlying XML non-visual properties are **not yet present** (for example, in a freshly created shape that has not previously had a name explicitly set). In that situation the missing elements must be created. The method should return a boolean indicating whether the name was successfully applied (to match similar setter patterns in the library and to make failures detectable).

**Impact**
Without this method, `XSLFPictureShape` provides read-only access to the shape name, which is asymmetric and blocks any programmatic re-labelling of picture shapes. This is a frequently needed feature when building slide decks from templates or merging presentations where shape identity is based on user-supplied names.


We need a way to tell `DataFormatter` to always render years with four digits when formatting date cells, even if the cell’s format string specifies only a two-digit year.

**Acceptance criteria**

1. **New configuration flag on DataFormatter**
   Add a public setter (and default to `false`) that controls whether year-adjustment is applied. The default must not change existing behaviour.

2. **When the flag is false (the default)**
   Date formatting works exactly as it does today – format strings that use `yy` produce two-digit years, `yyyy` produce four-digit years, and so on.

3. **When the flag is true**
   Any date format string that contains a two-digit year token (`yy`) without also being part of a longer sequence like `yyy` or `yyyy` is upgraded to use a four-digit year token (`yyyy`):
   - `d/m/yy` → treated as `d/m/yyyy`
   - `yy-mm-dd` → `yyyy-mm-dd`
   - `m/d/yy h:mm` → `m/d/yyyy h:mm`
   - Multi-section format strings (e.g. positive;negative;zero;text) must have every occurrence of `yy` in every section upgraded to `yyyy`.
   - Format strings that already contain `yyyy` should remain unchanged (no double-upgrading).
   - Format strings that contain a three-digit `yyy` are left as-is (they are not altered).
   - Format strings that have no `yy` at all are passed through unchanged.

4. **Scope**
   The adjustment only applies to date-formatting paths. Other formatting (numbers, text, etc.) must not be affected.

5. **Thread safety / instance isolation**
   Changing the flag on one `DataFormatter` instance must not affect other instances.

**Context**
This is to support users who want to avoid ambiguous two-digit years in formatted output (e.g. for CSV exports or display) without having to rewrite their spreadsheet templates or format strings in code. A similar idea exists in the project already for CSV-friendly formatting via the `emulateCSV` constructor, but this is specifically about date-year width.

[bug-65703] new DataFormatter method: setUse4DigitYearsInAllDateFormats - defaults to false


When creating numbering for a Word document, users can add abstract numbering definitions via `XWPFNumbering.addAbstractNum(XWPFAbstractNum)`. The `XWPFAbstractNum` object can be created either from an existing `CTAbstractNum` bean or using its default constructor (no underlying XML bean). In the latter case, `addAbstractNum` currently throws a `NullPointerException` because it calls `abstractNum.getAbstractNum()` which returns `null`. The method should handle this gracefully: it should create the internal `CTAbstractNum` bean, attach it to the `XWPFAbstractNum` object, assign a valid abstract number ID, and store the definition. After adding, the numbering part must be fully functional – subsequent calls to add a number instance referencing that abstract num and round-tripping the document should preserve the definition and its ID.

Additionally, provide a way for client code to retrieve all abstract numbering definitions and all number instances that have been added, e.g. `getAbstractNums()` and `getNums()`, returning the lists maintained by `XWPFNumbering`. The returned lists must not be modifiable.

XWPFNumbering.addAbstractNum will definitely throw an exception


**Title**: INDIRECT function rejects lowercase `r`/`c` in R1C1 reference strings

When using the `INDIRECT` function with the second argument set to `FALSE` (R1C1 reference style), the formula evaluator fails if the reference string contains a lowercase `'r'` or `'c'`.
For example, `INDIRECT("r2c3", FALSE)` returns an error instead of the value from row 2, column 3.

**Expected behavior**
Microsoft Excel treats the R1C1 reference delimiters `R`/`C` as case‑insensitive—`"r2c3"`, `"R2C3"`, and any mixed‑case variant should all resolve to the same cell. Apache POI’s formula evaluator should match this behavior.

**Current behavior**
Lowercase `'r'` or `'c'` in an R1C1 string (including in range references like `"r1c1:r1c7"` or sheet‑qualified references like `"Sheet2!r1c2:r3c3"`) causes the evaluation to produce an error (e.g., `#REF!`). Uppercase‑only references work correctly.

**Impact**
Spreadsheets that contain perfectly valid (in Excel) INDIRECT formulas with lowercase R1C1 references will not evaluate correctly in POI‑based applications. This can silently corrupt calculated values during export, conversion, or server‑side recalculation.

**Scope**
The case‑insensitivity should apply wherever R1C1 reference text is parsed in the formula engine, including INDIRECT and any other entry points that accept R1C1 strings. The fix must preserve the original reference text for parsing of numeric components (row/column numbers and offsets), only the `R`/`C` delimiter detection itself should become case‑insensitive.


**Title:** OutOfMemoryError while writing large file-backed POIFS streams (e.g., encrypted files)

**Description:**
When writing large data streams into a file-backed `POIFSFileSystem` (for example, encrypting a multi-megabyte document via `Encryptor`), the process eventually fails with `OutOfMemoryError`. The root cause is that every block written maps a new `ByteBuffer` through `FileChannel.map()`, and those direct buffers accumulate in an internal list. They are only unmapped en masse when the `DataSource` is closed. For large files the unfreed direct memory exhausts the heap- and direct-memory budget long before the stream is fully written.

**Expected behaviour:**
Mapped `ByteBuffer`s should be released (unmapped) as soon as the block store no longer needs them. Specifically, after a block has been fully consumed and the writer moves on to a new block, the previous block’s buffer should be unmapped. This must work for both OLE2 big-block streams and mini-stream (“small blocks”) paths. The improvement must not alter the behaviour of the read-only code path (which uses heap buffers and does not need unmapping).

**Additional constraints / hints:**
- The block-store abstraction (`BlockStore`) is the right place to introduce a hook for releasing buffers, because both `POIFSFileSystem` and `POIFSMiniStore` control the lifecycle of block buffers.
- The file-backed data source (`FileBackedDataSource`) currently holds all mapped buffers in a single list for bulk cleanup during `close()`. It needs a way to unmap an individual buffer on demand, and the tracking structure should support efficient removal (the existing `ArrayList` is not ideal for this).
- After a buffer is unmapped, it must not be used again; the caller must guarantee it has finished writing to that block.
- The bulk cleanup on `close()` should still run to catch any remaining buffers, so no regression in resource handling.
- The fix must not change the API contracts of the public `POIFSFileSystem`, `POIFSStream`, or `Encryptor` classes.
- Typical reproduction: create a file-backed `POIFSFileSystem` (not in-memory), obtain an encrypted output stream, and write 80+ MB of data. This should succeed without hitting `OutOfMemoryError`.


**Title:** Allow passing MongoDB aggregation execute options (e.g., `allowDiskUse`) per resource/operation

**Background**
MongoDB’s `aggregate` command supports several options (like `allowDiskUse`, `collation`, `hint`) that control query execution. In API Platform’s ODM integration, all aggregation pipelines are executed via `execute()` with no options, which prevents users from leveraging these MongoDB features. A common need is to enable `allowDiskUse` for large collections that exceed the 100 MB in-memory sort limit.

**Request**
Add the ability to specify aggregate execution options via resource metadata, so that they are passed through to every `execute()` call when fetching collections, single items, subresources, and paginated results.

**Expected configuration API**
Resource metadata should accept a new key under the existing `doctrine_mongodb` operation attribute. The shape is:

```
doctrine_mongodb:
    execute_options:
        allowDiskUse: true
```

This should be configurable at the resource level (applied to all operations) and optionally overridden per operation (collection/item/subresource). The options should be a free-form array of key-value pairs that maps directly to the options argument of the underlying `execute()` method on the aggregation builder.

**Expected behavior**
- When a resource defines `doctrine_mongodb.execute_options`, those options must be passed to the aggregate `execute()` calls for:
  - Collection data provider (GET collection)
  - Item data provider (GET item)
  - Subresource data provider (all subresource aggregations, including intermediate aggregations built during subresource resolution)
  - Pagination extension (the internal aggregation that produces the count facet, and the final result aggregation)
- When no `execute_options` are defined, the existing behavior (call `execute()` with no arguments) must be preserved.
- The options should be retrieved via the resource metadata factory, respecting operation-level attribute fallback (i.e., per-operation settings override resource-level defaults, falling back through `resourceMetadata->getCollectionOperationAttribute` / `getItemOperationAttribute` / `getSubresourceOperationAttribute` with the final recursion-enabled flag).

**Additional notes**
- This feature is specific to the MongoDB ODM data providers and extensions (the `MongoDbOdm` namespace). No changes should affect the ORM side.
- The `allowDiskUse` option is just an example; users should be able to pass any valid [MongoDB aggregate command options](https://docs.mongodb.com/manual/reference/command/aggregate/#command-fields).
- Ensure service definitions for the data providers and the pagination extension are updated so the resource metadata factory is available where needed.

Possibility to add execute options for MongoDB (#3144)


In `ObjectMapperProvider::provide`, the target class for mapping is selected from the operation metadata but currently only considers the output class (falling back to the resource class). When an operation defines both an `input` and an `output` mapping — typical for write operations — the provider incorrectly maps the source data to the output DTO instead of the input DTO. The fix is to make the class selection respect the `input` class first, so the precedence becomes: `input` → `output` → resource class.

fix(state): prioritize input class over output in ObjectMapperProvider (#7879)


Fix JSON:API handling of null to-one relationships: they should serialize as `{"data": null}` instead of `{"data": []}`.
The generated JSON:API JSON schema for to-one relationship `data` should also permit `null` (in addition to the existing relationship object shape).
To-many relationships are unaffected: an empty to-many relationship should still serialize as `{"data": []}`.

fix(jsonapi): output null on a to-one relationship (#7686)


**Problem**
When a client sends a request body with an invalid value for a `BackedEnum` property (e.g. a string that does not correspond to any case of the enum), the API returns a **400 Bad Request** with a generic denormalization error. The expected behavior is a **422 Unprocessable Entity** response containing a constraint violation on that property, consistent with how other type‑mismatch errors are surfaced.

**Acceptance criteria**

1. **Invalid enum value → 422 validation violation**
   - For any resource with a `BackedEnum` typed property, submitting an unrecognized value must produce a `422` status with `Content-Type: application/problem+json` (or the configured error format).
   - The response body MUST contain a `violations` list, and one of the violations MUST have a `propertyPath` matching the enum field.

2. **Behaviour with `collectDenormalizationErrors`**
   - When the operation has `collectDenormalizationErrors` enabled, the same invalid enum value must still result in a `422` with the violation attached to the property. The response must not regress to a `400`.

3. **Other denormalization errors unchanged**
   - Only denormalization failures that can be attributed to a `BackedEnum` mismatch should be converted to `422` validation violations. Unrelated deserialization errors (e.g. completely unprocessable payloads) must continue to produce `400`.

4. **Nullable enum handling**
   - The detection must correctly handle nullable backed enum properties where the Symfony serializer may wrap the enum mismatch exception inside a chain of previous exceptions. In such cases the `BackedEnum` type must still be identified and the error surfaced as a `422` violation rather than a `400`.

**Non‑functional constraints**
- The implementation should reuse the violation construction logic already present for denormalization errors in the state provider that handles deserialization, so that violation messages remain consistent.
- The change must preserve the existing `Stopwatch` profiling and any other side‑effects of the provider.

fix(state): convert BackedEnum denormalization errors into validation violations (#8195)


```
Fix OpenAPI parameter schema generation for array QueryParameters.

When a QueryParameter already has a schema with type "array" (e.g., `['type' => 'array', 'items' => ['type' => 'string']]`), the generated OpenAPI parameter schema wrongly gets another array layer wrapped around it, producing a nested array (e.g., array of array instead of a simple array). The expected behavior is that when the user-provided schema is already an array type, it should be used as-is without additional array wrapping.
```

fix(filter): do not nest array while generating default schema (#7832)


**Title:** Deprecate `ApiPlatform\Symfony\Security\Exception\AccessDeniedException` in favor of `ApiPlatform\Metadata\Exception\AccessDeniedException`

**Description:**
The Symfony-specific access-denied exception class `ApiPlatform\Symfony\Security\Exception\AccessDeniedException` should be deprecated and users should be pointed to the equivalent class in the Metadata namespace (`ApiPlatform\Metadata\Exception\AccessDeniedException`). At the same time, internal framework code that throws this exception must not flood users with deprecation notices.

**Acceptance criteria:**
1. When user code directly instantiates `ApiPlatform\Symfony\Security\Exception\AccessDeniedException`, a deprecation notice is raised that clearly communicates the class is deprecated and references the Metadata replacement class.
2. When the framework itself throws this exception as part of its built-in access-control logic (e.g., when a security voter denies access during an API Platform request cycle), **no** deprecation notice is emitted. Only direct end-user instantiation should produce the notice.
3. The deprecated exception must remain fully backward-compatible: it still implements `HttpExceptionInterface`, returns status code `403`, and supports custom message, previous exception, and code – exactly as before.
4. The deprecation version string in any notice should be consistent with the project’s existing deprecation conventions (currently `api-platform/core`).

**Background:**
We already have a metadata-level `AccessDeniedException` that serves the same purpose and we want to consolidate on that. The Symfony-specific version is a thin subclass mainly used in access-checker providers inside the Symfony integration.

feat(symfony): deprecate Symfony Security AccessDeniedException (#8318)


When an API Platform operation specifies `inputFormats` (e.g., only `['xml' => 'application/xml']`) and a client sends a request with a Content-Type that does not appear in that list, the system should reject it cleanly:

- The response status code should be `415 Unsupported Media Type`.
- The response body should be a machine-readable error document (typically JSON problem details, following the `error_formats` configuration) containing an appropriate detail message about the unsupported content type and the allowed MIME types.

At present the behavior is unreliable; sometimes the error response format is wrong (e.g., HTML instead of the expected JSON problem structure), or the error body does not reflect the expected problem description. This appears to happen because the error-path code reuses the format from the original (failing) request instead of negotiating a fresh format for the error, and because the route definitions force `_format: null` in a way that can override operation-level defaults and interfere with format negotiation during error handling.

Acceptance criteria / constraints
- When a request arrives with an unsupported Content-Type, the error response format must be determined by the configured error-format pipeline, independent of the request’s original format.
- The route loading must allow an operation’s defaults (if any) to influence the format setting, still falling back to a null format when the operation does not provide one, so that format negotiation during errors works correctly.
- The fix must not regress existing successful error format handling (e.g., `jsonapi`, `jsonld`, `html`, `hal`, etc.).


# Bug Report: Empty `hydra:search` should be omitted when no searchable parameters exist

## Problem
When a resource collection operation declares query parameters (e.g., via `QueryParameter`) but **none of those parameters are associated with a search filter**, the Hydra JSON-LD response still includes a `hydra:search` IRI template. This template shows an empty variable list (`{?}`) and an empty `hydra:mapping` array, which gives the impression that search is supported when in fact there are no filterable fields.

## Expected Behavior
If a collection has parameters but none contribute to the Hydra search representation (for example, because they have no `filter` defined, or explicitly set `hydra: false`), the `hydra:search` key should **not** appear in the response at all. An empty search block is semantically meaningless and misleading to clients.

## Steps to Reproduce
1. Define an `ApiResource` with a `GetCollection` operation that includes a `QueryParameter` with `hydra: false` and no associated filter.
2. Request the collection in a Hydra-compatible format (e.g., `application/ld+json`).
3. Observe the response includes:
```json
"hydra:search": {
  "@type": "hydra:IriTemplate",
  "hydra:template": "/path{?}",
  "hydra:variableRepresentation": "BasicRepresentation",
  "hydra:mapping": []
}
```

## Impact
Clients (including API Platform’s own admin UI) may incorrectly detect a search capability and show empty or broken search interfaces. The Hydra specification expects `hydra:search` to describe discoverable search capabilities; emitting an empty one violates that expectation.

## Additional Context
The issue also occurs when a parameter uses a filter that does not provide a description (e.g., a filter without a `getDescription()` or one that returns empty). In all cases where no `mapping` and no search `keys` are generated, the block should be suppressed.

fix(hydra): hide search key when there is parameter without filter (#7773)