Skip to content

[Bug] Cell style strategy corrupts the cached @ContentStyle(dataFormat) of other rows #956

Description

@nkuprins

Search before asking

  • I searched in the issues and found nothing similar.

Fesod version

current main (4b426ba)

JDK version

Temurin 25 (code path is version-independent, affects 8+)

Operating system

Linux (not OS-specific)

Steps To Reproduce

A @ContentStyle(dataFormat = N) annotation's number format is silently overwritten by a registered cell style strategy, producing the wrong number format in the output file.

@Data
public class DemoData {
    @ExcelProperty("value")
    @ContentStyle(dataFormat = 4) // "#,##0.00"
    private Double value;
}

// content style list alternating between a style WITH a data format and one WITHOUT
WriteCellStyle withFormat = new WriteCellStyle();
DataFormatData dfd = new DataFormatData();
dfd.setIndex((short) 2); // "0.00"
withFormat.setDataFormatData(dfd);
WriteCellStyle withoutFormat = new WriteCellStyle();

FesodSheet.write(file, DemoData.class)
        .registerWriteHandler(new HorizontalCellStyleStrategy(null, Arrays.asList(withFormat, withoutFormat)))
        .sheet()
        .doWrite(Arrays.asList(new DemoData(1111.5), new DemoData(2222.5), new DemoData(3333.5)));

The second data row's strategy style defines no data format, so that cell should fall back to the annotation's #,##0.00.

Current Behavior

All three data rows come out with format index 2 (0.00). Reading the written file back with POI:

dataRow0 format = 2 (expected 2)
dataRow1 format = 2 (expected 4 - annotation)   <-- wrong
dataRow2 format = 2 (expected 2)

With .filedCacheLocation(CacheLocationEnum.MEMORY) it is worse: the corruption leaks into later, completely unrelated writes of the same bean class - a follow-up write with no custom handlers at all also emits format 2 instead of the annotation's 4, because the static ClassUtils.CONTENT_CACHE now holds the mutated format.

Expected Behavior

Rows whose strategy style does not define a data format keep the @ContentStyle(dataFormat = 4) annotation format (#,##0.00), and the cached annotation metadata is never mutated by a style strategy.

Anything else?

Root cause: WriteCellStyle.merge aliases the source's DataFormatData into the target without cloning:

https://github.com/apache/fesod/blob/main/fesod-sheet/src/main/java/org/apache/fesod/sheet/write/metadata/style/WriteCellStyle.java#L180-L186

if (target.getDataFormatData() == null) {
    target.setDataFormatData(source.getDataFormatData()); // shared reference, no copy
}

The annotation's DataFormatData is parsed once and cached. After this merge, the per-cell style points at that cached object, so when a second style source merges into the same cell, DataFormatData.merge writes into it and permanently changes the cached annotation format.

The writeFont field a few lines below already handles this correctly by cloning:

target.setWriteFont(source.getWriteFont().clone());

Doing the same for DataFormatData fixes it:

target.setDataFormatData(source.getDataFormatData().clone());

Are you willing to submit a PR?

  • I'm willing to submit a PR!

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions