Skip to content

Commit bb74ab3

Browse files
authored
Merge pull request #1747 from swyfft-insurance/fix/ks/20260321_lazy-load-volatile-and-roundtrip-test
Fix volatile correctness in lazy load and add roundtrip test
2 parents f88c04b + a51b893 commit bb74ab3

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

ooxml/XSSF/UserModel/XSSFSheet.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public partial class XSSFSheet : POIXMLDocumentPart, ISheet
8686
private List<CellRangeAddress> arrayFormulas;
8787
private readonly XSSFDataValidationHelper dataValidationHelper;
8888
private XSSFDrawing drawing = null;
89-
private bool _worksheetLoaded = false;
89+
private volatile bool _worksheetLoaded = false;
9090
private readonly object _loadLock = new object();
9191
internal int _parseCount = 0;
9292
private List<CellRangeAddress> _cachedMergedRegions;

testcases/ooxml/XSSF/UserModel/TestXSSFSheetLazyLoad.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,5 +214,71 @@ public void MultipleSheetsSomeAccessedSomeNot()
214214
Assert.That(sheet1._parseCount, Is.EqualTo(1), "sheet1 was accessed, should be parsed once");
215215
Assert.That(sheet2._parseCount, Is.EqualTo(0), "sheet2 was not accessed, should not be parsed");
216216
}
217+
218+
[Test]
219+
public void SaveWithoutAccessPreservesUntouchedSheets()
220+
{
221+
// Create a 3-sheet workbook with distinct data
222+
byte[] originalBytes;
223+
using (var wb = new XSSFWorkbook())
224+
{
225+
for (int i = 0; i < 3; i++)
226+
{
227+
var s = wb.CreateSheet($"Sheet{i}");
228+
s.CreateRow(0).CreateCell(0).SetCellValue($"Original{i}");
229+
s.CreateRow(1).CreateCell(0).SetCellValue(i * 100.0);
230+
}
231+
using var ms = new MemoryStream();
232+
wb.Write(ms);
233+
originalBytes = ms.ToArray();
234+
}
235+
236+
// Open, touch only Sheet1, modify it, and save
237+
byte[] savedBytes;
238+
using (var ms = new MemoryStream(originalBytes))
239+
using (var wb = new XSSFWorkbook(ms))
240+
{
241+
var sheet0 = (XSSFSheet)wb.GetSheetAt(0);
242+
var sheet1 = (XSSFSheet)wb.GetSheetAt(1);
243+
var sheet2 = (XSSFSheet)wb.GetSheetAt(2);
244+
245+
// Only access sheet1 — sheets 0 and 2 should never be parsed
246+
sheet1.GetRow(0).GetCell(0).SetCellValue("Modified1");
247+
248+
Assert.That(sheet0._parseCount, Is.EqualTo(0), "sheet0 should not be parsed");
249+
Assert.That(sheet1._parseCount, Is.EqualTo(1), "sheet1 should be parsed once");
250+
Assert.That(sheet2._parseCount, Is.EqualTo(0), "sheet2 should not be parsed");
251+
252+
using var outMs = new MemoryStream();
253+
wb.Write(outMs);
254+
savedBytes = outMs.ToArray();
255+
}
256+
257+
// Re-open and verify all sheets survived the roundtrip
258+
using (var ms = new MemoryStream(savedBytes))
259+
using (var wb = new XSSFWorkbook(ms))
260+
{
261+
// Sheet0: untouched — should have original data
262+
var s0 = wb.GetSheetAt(0);
263+
Assert.That(s0.GetRow(0).GetCell(0).StringCellValue, Is.EqualTo("Original0"),
264+
"Untouched sheet0 should preserve original data");
265+
Assert.That(s0.GetRow(1).GetCell(0).NumericCellValue, Is.EqualTo(0.0),
266+
"Untouched sheet0 row 1 should preserve original data");
267+
268+
// Sheet1: modified
269+
var s1 = wb.GetSheetAt(1);
270+
Assert.That(s1.GetRow(0).GetCell(0).StringCellValue, Is.EqualTo("Modified1"),
271+
"Modified sheet1 should have new value");
272+
Assert.That(s1.GetRow(1).GetCell(0).NumericCellValue, Is.EqualTo(100.0),
273+
"Unmodified row in sheet1 should preserve original data");
274+
275+
// Sheet2: untouched — should have original data
276+
var s2 = wb.GetSheetAt(2);
277+
Assert.That(s2.GetRow(0).GetCell(0).StringCellValue, Is.EqualTo("Original2"),
278+
"Untouched sheet2 should preserve original data");
279+
Assert.That(s2.GetRow(1).GetCell(0).NumericCellValue, Is.EqualTo(200.0),
280+
"Untouched sheet2 row 1 should preserve original data");
281+
}
282+
}
217283
}
218284
}

0 commit comments

Comments
 (0)