+ * Due to limitations in the underlying SODS library, exact POI data format patterns + * are not fully supported. If the provided format index corresponds to a recognized + * Date format, it will be mapped to the ISO date style ("YYYY-MM-DD") to ensure + * dates are properly displayed. Other formats are currently stored but may not + * affect the final ODS cell rendering. + *
+ *+ * This is display-only: the full {@code HH:mm:ss} timestamp is still stored + * in {@code office:date-value} and returned unchanged by + * {@link Cell#getDateCellValue()}, even though time-inclusive formats + * render as date-only in spreadsheet applications. + *
+ * + * @param fmt the data format index + */ + @Override + public void setDataFormat(short fmt) { + this.dataFormat = fmt; + if (workbook != null) { + String formatStr = workbook.createDataFormat().getFormat(fmt); + if (DateUtil.isADateFormat(fmt, formatStr)) { + style.setDataStyle("YYYY-MM-DD"); + } + } + } + + @Override + public short getDataFormat() { + return dataFormat; + } + + @Override + public void setWrapText(boolean wrapped) { + this.wrapText = wrapped; + style.setWrap(wrapped); + } + + @Override + public boolean getWrapText() { + return wrapText; + } + + @Override + public int getFontIndex() { + return fontIndex; + } + + @Override + public int getFontIndexAsInt() { + return fontIndex; + } + + @Override + public short getIndex() { + return index; + } + + @Override + public void cloneStyleFrom(CellStyle source) {} + @Override + public void setBottomBorderColor(short color) {} + @Override + public short getBottomBorderColor() { return 0; } + @Override + public void setLeftBorderColor(short color) {} + @Override + public short getLeftBorderColor() { return 0; } + @Override + public void setRightBorderColor(short color) {} + @Override + public short getRightBorderColor() { return 0; } + @Override + public void setTopBorderColor(short color) {} + @Override + public short getTopBorderColor() { return 0; } + @Override + public void setFillBackgroundColor(short bg) {} + @Override + public short getFillBackgroundColor() { return 0; } + @Override + public void setHidden(boolean hidden) {} + @Override + public boolean getHidden() { return false; } + @Override + public void setLocked(boolean locked) {} + @Override + public boolean getLocked() { return false; } + @Override + public void setQuotePrefixed(boolean quotePrefix) {} + @Override + public boolean getQuotePrefixed() { return false; } + @Override + public void setIndention(short indent) {} + @Override + public short getIndention() { return 0; } + @Override + public void setRotation(short rotation) {} + @Override + public short getRotation() { return 0; } + @Override + public String getDataFormatString() { return ""; } + @Override + public void setShrinkToFit(boolean shrinkToFit) {} + @Override + public boolean getShrinkToFit() { return false; } + + @Override + public void invalidateCachedProperties() {} + + @Override + public java.util.EnumMap+ * Note: The SODS library has limited support for custom data formats. + * It natively only supports plain text ("@") and an ISO date format + * ("YYYY-MM-DD"). As a result, exact POI format patterns are not matched in + * the ODS output. Any date formats will map to "YYYY-MM-DD" internally, + * while other formats remain unstyled. + *
+ */ +public class OdsDataFormat implements DataFormat { + + private final List+ * This package maps the Apache POI {@code org.apache.poi.ss.usermodel} + * interfaces to the underlying {@code com.github.miachm.sods} library. + *
+ *+ * Standard spreadsheet styling elements (colors, fonts, alignments, text + * wrapping, and cell borders) are fully supported. + *
+ * + *+ * Custom cell data formatting (e.g., specific number formats and custom date + * patterns) has limited support due to the underlying SODS library. SODS only + * natively supports plain text ("@") or an ISO date format ("YYYY-MM-DD"). + *
+ *+ * Consequently, any target cell format representing a date is mapped to the + * standard {@code "YYYY-MM-DD"} format, while custom numerical and string + * format patterns remain unstyled. + *
+ *+ * This is a display-only limitation. Date/time values are still stored with + * full {@code HH:mm:ss} precision in the cell's underlying + * {@code office:date-value} attribute; only the visual rendering in + * spreadsheet applications (e.g. LibreOffice, Excel) is truncated to the + * date portion. + *
+ */ +package org.spdx.spreadsheetstore.ods; diff --git a/src/test/java/org/spdx/spreadsheetstore/SpreadsheetStoreTest.java b/src/test/java/org/spdx/spreadsheetstore/SpreadsheetStoreTest.java index 45fcae3..ffe94bb 100644 --- a/src/test/java/org/spdx/spreadsheetstore/SpreadsheetStoreTest.java +++ b/src/test/java/org/spdx/spreadsheetstore/SpreadsheetStoreTest.java @@ -1,5 +1,6 @@ /* * SPDX-FileContributor: Gary O'Neall + * SPDX-FileContributor: Arthit Suriyawongkul * SPDX-FileCopyrightText: Copyright (c) 2020 Source Auditor Inc. * SPDX-FileType: SOURCE * SPDX-License-Identifier: Apache-2.0 @@ -67,6 +68,7 @@ import org.spdx.library.model.v2.license.SpdxNoAssertionLicense; import org.spdx.library.model.v3_0_1.SpdxModelInfoV3_0; import org.spdx.library.referencetype.ListedReferenceTypes; +import org.spdx.spreadsheetstore.SpreadsheetStore.SpreadsheetFormatType; import org.spdx.storage.compatv2.CompatibleModelStoreWrapper; import org.spdx.storage.simple.InMemSpdxStore; import org.spdx.utility.compare.SpdxCompareException; @@ -495,6 +497,103 @@ public void testSerialize() throws InvalidSPDXAnalysisException, IOException, Sp } + /** + * Test method for + * {@link org.spdx.spreadsheetstore.SpreadsheetStore#serialize(java.io.OutputStream)} + * using ODS format. + * + * @throws InvalidSPDXAnalysisException + * @throws IOException + * @throws SpdxCompareException + */ + @SuppressWarnings("unchecked") + public void testSerializeOds() throws InvalidSPDXAnalysisException, IOException, SpdxCompareException { + SpreadsheetStore sst = new SpreadsheetStore(new InMemSpdxStore(), SpreadsheetFormatType.ODS); + String documentUri = "http://newdoc/uri"; + ModelCopyManager copyManager = new ModelCopyManager(); + compareStore.getAllItems(compareDocument.getDocumentUri(), SpdxConstantsCompatV2.CLASS_EXTERNAL_DOC_REF).forEach(tv -> { + try { + copyManager.copy(sst, compareStore, tv.getObjectUri(), + CompatibleModelStoreWrapper.LATEST_SPDX_2X_VERSION, documentUri + "#"); + } catch (InvalidSPDXAnalysisException e) { + throw new RuntimeException(e); + } + }); + compareStore.getAllItems(compareDocument.getDocumentUri(), null).forEach(tv -> { + try { + if (!SpdxConstantsCompatV2.CLASS_EXTERNAL_DOC_REF.equals(tv.getType())) { + copyManager.copy(sst, compareStore, tv.getObjectUri(), + CompatibleModelStoreWrapper.LATEST_SPDX_2X_VERSION, documentUri + "#"); + } + } catch (InvalidSPDXAnalysisException e) { + throw new RuntimeException(e); + } + }); + + Path tempFilePath = Files.createTempFile("temp", ".ods"); + try { + try (FileOutputStream out = new FileOutputStream(tempFilePath.toFile())) { + sst.serialize(out); + } + SpreadsheetStore resultStore = new SpreadsheetStore(new InMemSpdxStore()); + try (FileInputStream stream = new FileInputStream(tempFilePath.toFile())) { + resultStore.deSerialize(stream, false); + } + List