diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 348d6009..8627b211 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -34,7 +34,7 @@ jobs: - name: Check links id: lychee - uses: lycheeverse/lychee-action@v1.9.3 + uses: lycheeverse/lychee-action@v1.10.0 with: fail: true args: --max-concurrency 1 --cache --no-progress --exclude-all-private './**/*.md' diff --git a/.github/workflows/java-ea-maven.yml b/.github/workflows/java-ea-maven.yml index 53906f4d..a690e85f 100644 --- a/.github/workflows/java-ea-maven.yml +++ b/.github/workflows/java-ea-maven.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: java: [ 21 ] - os: [ ubuntu-latest ] + os: [ ubuntu-latest, macos-latest ] name: JDK${{ matrix.java }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -34,4 +34,4 @@ jobs: - name: Build and (headless) test with Maven uses: smithki/xvfb-action@v1.1.2 with: - run: mvn -U -B -ntp package + run: mvn -U -B -ntp verify diff --git a/.github/workflows/java8-maven.yml b/.github/workflows/java8-maven.yml index 822d1e01..d0683de0 100644 --- a/.github/workflows/java8-maven.yml +++ b/.github/workflows/java8-maven.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: java: [ 8, 21 ] - os: [ ubuntu-latest, macOS-latest, windows-latest ] + os: [ ubuntu-latest, macos-13, windows-latest ] name: JDK${{ matrix.java }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -34,7 +34,7 @@ jobs: - name: Build and (headless) test with Maven uses: smithki/xvfb-action@v1.1.2 with: - run: mvn -U -B -ntp package + run: mvn -U -B -ntp verify auto-merge-job: needs: build-and-test-job diff --git a/CHANGELOG.md b/CHANGELOG.md index 94f831eb..44fdf3af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [2.5.9] - 2024-05-28 + +### Fixed + +- Fix date parsing in SODS [#470](https://github.com/nbbrd/spreadsheet4j/issues/470) + +### Changed + +- Bump java-io-util from 0.0.27 to [0.0.28](https://github.com/nbbrd/spreadsheet4j/blob/develop/CHANGELOG.md) +- Bump fastexcel from 0.16.6 to [0.18.0](https://github.com/dhatim/fastexcel/compare/0.15.7...0.16.6) + ## [2.5.8] - 2024-02-26 ### Changed @@ -52,7 +63,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Bump SODS from [1.5.1 to 1.6.2](https://github.com/miachm/SODS/releases) - Bump fastexcel from [0.14.0 to 0.15.7](https://github.com/dhatim/fastexcel/releases) -- Bump jsoup from [1.15.3 to 1.16.1](https://github.com/jhy/jsoup/blob/master/CHANGES) +- Bump jsoup from [1.15.3 to 1.16.1](https://github.com/jhy/jsoup/blob/master/CHANGES.md) - Remove `spreadsheet-xl` dependency from `spreadsheet-poi` ## [2.5.2] - 2022-10-28 @@ -172,7 +183,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - JDK11 cleanup -[Unreleased]: https://github.com/nbbrd/spreadsheet4j/compare/v2.5.8...HEAD +[Unreleased]: https://github.com/nbbrd/spreadsheet4j/compare/v2.5.9...HEAD +[2.5.9]: https://github.com/nbbrd/spreadsheet4j/compare/v2.5.8...v2.5.9 [2.5.8]: https://github.com/nbbrd/spreadsheet4j/compare/v2.5.7...v2.5.8 [2.5.7]: https://github.com/nbbrd/spreadsheet4j/compare/v2.5.6...v2.5.7 [2.5.6]: https://github.com/nbbrd/spreadsheet4j/compare/v2.5.5...v2.5.6 diff --git a/pom.xml b/pom.xml index 6894d0d4..087b006e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 pom spreadsheet4j @@ -38,7 +38,7 @@ UTF-8 - 2024-02-26T12:39:59Z + 2024-05-28T08:50:45Z 1.8 1.8 @@ -65,7 +65,7 @@ org.checkerframework checker-qual - 3.42.0 + 3.43.0 @@ -86,7 +86,7 @@ com.github.nbbrd.java-io-util java-io-bom - 0.0.27 + 0.0.28 pom import @@ -105,22 +105,22 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.13.0 org.apache.maven.plugins maven-deploy-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins maven-install-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins maven-jar-plugin - 3.3.0 + 3.4.1 org.apache.maven.plugins @@ -148,7 +148,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.5.3 org.apache.maven.plugins @@ -163,17 +163,17 @@ org.gaul modernizer-maven-plugin - 2.7.0 + 2.9.0 de.thetaphi forbiddenapis - 3.6 + 3.7 com.github.nbbrd.heylogs heylogs-maven-plugin - 0.7.2 + 0.8.1 com.amashchenko.maven.plugin @@ -183,7 +183,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins @@ -203,12 +203,12 @@ org.jacoco jacoco-maven-plugin - 0.8.11 + 0.8.12 org.jreleaser jreleaser-maven-plugin - 1.10.0 + 1.12.0 @@ -301,9 +301,9 @@ - 1.18.30 + 1.18.32 1.9.0 - 1.5.0 + 1.5.1 1.37 @@ -466,12 +466,12 @@ org.kordamp.maven pomchecker-enforcer-rules - 1.10.0 + 1.11.0 org.codehaus.mojo extra-enforcer-rules - 1.7.0 + 1.8.0 diff --git a/spreadsheet-api/pom.xml b/spreadsheet-api/pom.xml index 4e13d2a4..64b3b396 100644 --- a/spreadsheet-api/pom.xml +++ b/spreadsheet-api/pom.xml @@ -5,7 +5,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-api diff --git a/spreadsheet-bom/pom.xml b/spreadsheet-bom/pom.xml index ea9fd60e..b6ee754a 100644 --- a/spreadsheet-bom/pom.xml +++ b/spreadsheet-bom/pom.xml @@ -6,7 +6,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-bom diff --git a/spreadsheet-fastexcel/pom.xml b/spreadsheet-fastexcel/pom.xml index 582b6cdc..5e98e979 100644 --- a/spreadsheet-fastexcel/pom.xml +++ b/spreadsheet-fastexcel/pom.xml @@ -6,7 +6,7 @@ spreadsheet-parent com.github.nbbrd.spreadsheet4j - 2.5.8 + 2.5.9 spreadsheet-fastexcel @@ -47,7 +47,7 @@ org.dhatim fastexcel - 0.16.6 + 0.18.0 diff --git a/spreadsheet-html/pom.xml b/spreadsheet-html/pom.xml index dbaaeb77..a3915e6a 100644 --- a/spreadsheet-html/pom.xml +++ b/spreadsheet-html/pom.xml @@ -5,7 +5,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-html diff --git a/spreadsheet-od/pom.xml b/spreadsheet-od/pom.xml index 0f7d8955..61218ff3 100644 --- a/spreadsheet-od/pom.xml +++ b/spreadsheet-od/pom.xml @@ -5,7 +5,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-od diff --git a/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdBook.java b/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdBook.java index f93007eb..3b2d1bf2 100644 --- a/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdBook.java +++ b/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdBook.java @@ -1,17 +1,17 @@ /* * Copyright 2013 National Bank of Belgium * - * Licensed under the EUPL, Version 1.1 or – as soon they will be approved + * Licensed under the EUPL, Version 1.1 or – as soon they will be approved * by the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * - * Unless required by applicable law or agreed to in writing, software + * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and + * See the Licence for the specific language governing permissions and * limitations under the Licence. */ package ec.util.spreadsheet.od; @@ -22,16 +22,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; /** - * * @author Philippe Charles */ +@lombok.AllArgsConstructor final class OdBook extends Book { - private final SpreadSheet book; - - public OdBook(SpreadSheet book) { - this.book = book; - } + private final @lombok.NonNull SpreadSheet book; @Override public int getSheetCount() { diff --git a/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdCell.java b/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdCell.java index d2238b34..64d7796a 100644 --- a/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdCell.java +++ b/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdCell.java @@ -1,53 +1,56 @@ /* * Copyright 2013 National Bank of Belgium * - * Licensed under the EUPL, Version 1.1 or – as soon they will be approved + * Licensed under the EUPL, Version 1.1 or – as soon they will be approved * by the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * - * Unless required by applicable law or agreed to in writing, software + * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and + * See the Licence for the specific language governing permissions and * limitations under the Licence. */ package ec.util.spreadsheet.od; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; /** - * * @author Philippe Charles */ //@FlyweightPattern @Deprecated +@lombok.RequiredArgsConstructor final class OdCell extends ec.util.spreadsheet.Cell { - private final ZoneId zoneId = ZoneId.systemDefault(); + private final @lombok.NonNull ZoneId zoneId; private transient Object value = null; - static boolean isValid(Object value) { - return value instanceof LocalDateTime - || value instanceof Number - || value instanceof String; - } - @Nullable OdCell withValue(@NonNull Object value) { this.value = value; - return isValid(value) ? this : null; + return isValidValue(value) ? this : null; + } + + private static boolean isValidValue(Object value) { + return value instanceof LocalDateTime + || value instanceof LocalDate + || value instanceof Number + || value instanceof String; } @Override public boolean isDate() { - return value instanceof LocalDateTime; + return value instanceof LocalDateTime || value instanceof LocalDate; } @Override @@ -61,16 +64,20 @@ public boolean isString() { } @Override - public Date getDate() { + public @NonNull Date getDate() { try { - return Date.from(((LocalDateTime) value).atZone(zoneId).toInstant()); - } catch (ClassCastException ex) { - throw new UnsupportedOperationException(ex); + return OdSheet.toDate((LocalDateTime) value, zoneId); + } catch (ClassCastException ex1) { + try { + return OdSheet.toDate((LocalDate) value, zoneId); + } catch (ClassCastException ex2) { + throw new UnsupportedOperationException(ex2); + } } } @Override - public Number getNumber() { + public @NonNull Number getNumber() { try { return (Number) value; } catch (ClassCastException ex) { @@ -79,7 +86,7 @@ public Number getNumber() { } @Override - public String getString() { + public @NonNull String getString() { try { return (String) value; } catch (ClassCastException ex) { diff --git a/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdSheet.java b/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdSheet.java index 3b161813..9365e4a2 100644 --- a/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdSheet.java +++ b/spreadsheet-od/src/main/java/ec/util/spreadsheet/od/OdSheet.java @@ -20,8 +20,10 @@ import com.github.miachm.sods.Sheet; import ec.util.spreadsheet.Cell; import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; @@ -35,15 +37,16 @@ final class OdSheet extends ec.util.spreadsheet.Sheet { private final String name; private final Range sheet; private final int columnCount; + private final ZoneId zoneId; @Deprecated private final OdCell flyweightCell; - private final ZoneId zoneId = ZoneId.systemDefault(); public OdSheet(Sheet sheet) { this.name = sheet.getName(); this.sheet = sheet.getDataRange(); this.columnCount = computeColumnCount(this.sheet); - this.flyweightCell = new OdCell(); + this.zoneId = ZoneId.systemDefault(); + this.flyweightCell = new OdCell(zoneId); } static int computeColumnCount(Range sheet) { @@ -72,6 +75,14 @@ static boolean isNullOrEmpty(Range sheet, int rowIdx, int columnIdx) throws Inde return sheet.getCell(rowIdx, columnIdx).getValue() == null; } + static Date toDate(LocalDateTime value, ZoneId zoneId) { + return Date.from(value.atZone(zoneId).toInstant()); + } + + static Date toDate(LocalDate value, ZoneId zoneId) { + return Date.from(value.atStartOfDay(zoneId).toInstant()); + } + @Override public int getRowCount() { return sheet.getNumRows(); @@ -91,17 +102,20 @@ public Cell getCell(int rowIdx, int columnIdx) { @Override public @Nullable Object getCellValue(@NonNegative int rowIdx, @NonNegative int columnIdx) throws IndexOutOfBoundsException { Object value = sheet.getCell(rowIdx, columnIdx).getValue(); - if (OdCell.isValid(value)) { - if (value instanceof LocalDateTime) { - return Date.from(((LocalDateTime) value).atZone(zoneId).toInstant()); - } + if (value instanceof LocalDateTime) { + return toDate((LocalDateTime) value, zoneId); + } else if (value instanceof LocalDate) { + return toDate((LocalDate) value, zoneId); + } else if (value instanceof Number) { + return value; + } else if (value instanceof String) { return value; } return null; } @Override - public String getName() { + public @NonNull String getName() { return name.replace("_", " "); } } diff --git a/spreadsheet-od/src/main/java/module-info.java b/spreadsheet-od/src/main/java/module-info.java index 96ca4fd0..c6639c4d 100644 --- a/spreadsheet-od/src/main/java/module-info.java +++ b/spreadsheet-od/src/main/java/module-info.java @@ -19,10 +19,11 @@ requires static org.checkerframework.checker.qual; requires static nbbrd.service; + requires static lombok; requires nbbrd.spreadsheet.api; requires com.github.miachm.sods; - + provides ec.util.spreadsheet.Book.Factory with ec.util.spreadsheet.od.OpenDocumentBookFactory; } diff --git a/spreadsheet-od/src/test/java/ec/util/spreadsheet/od/OdBookTest.java b/spreadsheet-od/src/test/java/ec/util/spreadsheet/od/OdBookTest.java new file mode 100644 index 00000000..fd07a83f --- /dev/null +++ b/spreadsheet-od/src/test/java/ec/util/spreadsheet/od/OdBookTest.java @@ -0,0 +1,57 @@ +package ec.util.spreadsheet.od; + +import com.github.miachm.sods.SpreadSheet; +import ec.util.spreadsheet.tck.BookAssert; +import ec.util.spreadsheet.tck.SheetAssert; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; + +import static java.util.Objects.requireNonNull; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +public class OdBookTest { + + @SuppressWarnings({"DataFlowIssue", "resource"}) + @Test + public void testFactory() { + assertThatNullPointerException().isThrownBy(() -> new OdBook(null)); + } + + @Test + public void testContent() throws IOException { + try (OdBook book = new OdBook(loadResource("/Top5Browsers.ods"))) { + BookAssert.assertThat(book).hasSheetCount(3); + SheetAssert.assertThat(book.getSheet(0)) + .hasName("Top 5 Browsers - Monthly") + .hasRowCount(42) + .hasColumnCount(7) + .hasCellValue(0, 0, null) + .hasCellValue(0, 1, "IE") + .hasCellValue(1, 0, OdSheet.toDate(LocalDateTime.of(2008, 7, 1, 0, 0), ZoneId.systemDefault())) + .hasCellValue(1, 1, 68.57); + } + + try (OdBook book = new OdBook(loadResource("/world_libre_office.ods"))) { + BookAssert.assertThat(book).hasSheetCount(4); + SheetAssert.assertThat(book.getSheet(0)) + .hasName("Europe") + .hasRowCount(383) // FIXME: should be 382? + .hasColumnCount(4) + .hasCellValue(0, 0, "date") + .hasCellValue(0, 1, "France") + .hasCellValue(1, 0, OdSheet.toDate(LocalDate.of(1990, 1, 1), ZoneId.systemDefault())) + .hasCellValue(1, 1, 395.8926090299); + } + } + + private static SpreadSheet loadResource(String name) throws IOException { + try (InputStream stream = OdBookTest.class.getResourceAsStream(name)) { + return new SpreadSheet(requireNonNull(stream)); + } + } +} diff --git a/spreadsheet-od/src/test/resources/world_libre_office.ods b/spreadsheet-od/src/test/resources/world_libre_office.ods new file mode 100644 index 00000000..c87edbbc Binary files /dev/null and b/spreadsheet-od/src/test/resources/world_libre_office.ods differ diff --git a/spreadsheet-poi/pom.xml b/spreadsheet-poi/pom.xml index 7f627760..d429eb7e 100644 --- a/spreadsheet-poi/pom.xml +++ b/spreadsheet-poi/pom.xml @@ -5,7 +5,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-poi @@ -100,7 +100,7 @@ org.apache.logging.log4j log4j-api - 2.23.0 + 2.23.1 diff --git a/spreadsheet-standalone/pom.xml b/spreadsheet-standalone/pom.xml index 74f795fe..807b8983 100644 --- a/spreadsheet-standalone/pom.xml +++ b/spreadsheet-standalone/pom.xml @@ -7,7 +7,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-standalone diff --git a/spreadsheet-xl/pom.xml b/spreadsheet-xl/pom.xml index c1fac03e..e30a7c79 100644 --- a/spreadsheet-xl/pom.xml +++ b/spreadsheet-xl/pom.xml @@ -6,7 +6,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-xl diff --git a/spreadsheet-xmlss/pom.xml b/spreadsheet-xmlss/pom.xml index 079a1b2f..b30fa123 100644 --- a/spreadsheet-xmlss/pom.xml +++ b/spreadsheet-xmlss/pom.xml @@ -5,7 +5,7 @@ com.github.nbbrd.spreadsheet4j spreadsheet-parent - 2.5.8 + 2.5.9 spreadsheet-xmlss