This is an automated email from the ASF dual-hosted git repository.

bamaer pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 69b93a814c use a large format mask to not lose decimal digits, fixes 
#3970 (#8580)
69b93a814c is described below

commit 69b93a814c8cd22e661493beecc359f0b9f7f0f3
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Fri Sep 25 11:27:59 2026 +0200

    use a large format mask to not lose decimal digits, fixes #3970 (#8580)
    
    * use a large format mask to not lose decimal digits, fixes #3970
    
    * fix remarks
---
 .../org/apache/hop/testing/DataSetCsvUtil.java     |  18 ++-
 .../apache/hop/testing/DataSetCsvWriterTest.java   | 128 +++++++++++++++++++++
 2 files changed, 144 insertions(+), 2 deletions(-)

diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java
index 29070d6e9f..c0e5e1c05a 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java
@@ -46,6 +46,16 @@ import org.apache.hop.core.vfs.HopVfs;
  * file defined by the tableName in the data set
  */
 public class DataSetCsvUtil {
+  /**
+   * Storage mask for Number and BigNumber fields without a format. 
DecimalFormat prints the
+   * shortest decimal representation of a double, so 3.14 is written as "3.14" 
while no digits are
+   * ever dropped (340 is the maximum number of fraction digits DecimalFormat 
honours for a double).
+   * Without it a BigNumber falls back to the default mask, which keeps 19 
fraction digits, or to a
+   * pattern built from its length and precision, which rounds and zero-pads. 
Declared precision is
+   * applied when comparing against golden data, not when storing.
+   */
+  static final String NUMBER_STORAGE_MASK = "0." + "#".repeat(340);
+
   public static void setValueFormats(IRowMeta rowMeta) {
     for (IValueMeta valueMeta : rowMeta.getValueMetaList()) {
       if (StringUtils.isEmpty(valueMeta.getConversionMask())) {
@@ -53,8 +63,8 @@ public class DataSetCsvUtil {
           case IValueMeta.TYPE_INTEGER:
             valueMeta.setConversionMask("0");
             break;
-          case IValueMeta.TYPE_NUMBER:
-            valueMeta.setConversionMask("0.#");
+          case IValueMeta.TYPE_NUMBER, IValueMeta.TYPE_BIGNUMBER:
+            valueMeta.setConversionMask(NUMBER_STORAGE_MASK);
             break;
           case IValueMeta.TYPE_DATE:
             valueMeta.setConversionMask("yyyyMMdd-HHmmss.SSS");
@@ -236,6 +246,10 @@ public class DataSetCsvUtil {
     if (valueMeta.isBinary()) {
       return decodeHex(value, valueMeta.getName());
     }
+    if (value != null && (valueMeta.isNumber() || valueMeta.isBigNumber())) {
+      // Earlier versions padded a BigNumber with a length to " 00001234.57"
+      value = value.trim();
+    }
     return valueMeta.convertData(stringMeta, value);
   }
 
diff --git 
a/plugins/misc/testing/src/test/java/org/apache/hop/testing/DataSetCsvWriterTest.java
 
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/DataSetCsvWriterTest.java
index a1c633b53d..e357ad6516 100644
--- 
a/plugins/misc/testing/src/test/java/org/apache/hop/testing/DataSetCsvWriterTest.java
+++ 
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/DataSetCsvWriterTest.java
@@ -22,9 +22,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.math.BigDecimal;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.Locale;
 import org.apache.hop.core.HopEnvironment;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.row.IRowMeta;
@@ -134,6 +136,132 @@ class DataSetCsvWriterTest {
     assertTrue(Files.exists(nested.resolve("nested.csv")));
   }
 
+  @Test
+  void numberWithoutFormatKeepsAllDigits() throws Exception {
+    Variables variables = new Variables();
+    variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, 
tempDir.toString());
+
+    DataSet dataSet = new DataSet();
+    dataSet.setName("numbers");
+    dataSet.setBaseFilename("numbers.csv");
+    dataSet.setFields(
+        List.of(
+            new DataSetField("id", IValueMeta.TYPE_INTEGER, -1, 0, "", "0"),
+            new DataSetField("pi", IValueMeta.TYPE_NUMBER, -1, -1, "", "")));
+
+    double[] values = {3.14, -1234.5678, 0.1 + 0.2, 1e-20, 12345678901.25, 
7.0};
+    IRowMeta rowMeta = dataSet.getSetRowMeta();
+    try (DataSetCsvWriter writer = new DataSetCsvWriter(variables, dataSet, 
rowMeta)) {
+      for (int i = 0; i < values.length; i++) {
+        writer.writeRow(new Object[] {(long) i, values[i]});
+      }
+      writer.writeRow(new Object[] {(long) values.length, null});
+    }
+
+    // The caller's row metadata must not be touched
+    assertTrue(rowMeta.getValueMeta(1).getConversionMask().isEmpty());
+
+    String csv = Files.readString(tempDir.resolve("numbers.csv"));
+    assertTrue(csv.contains("0,3.14"), csv);
+    // The previous "0.#" mask wrote -1234.6
+    assertTrue(csv.contains("1,-1234.5678"), csv);
+
+    List<Object[]> rows = DataSetCsvUtil.getAllRows(variables, dataSet);
+    assertEquals(values.length + 1, rows.size());
+    for (int i = 0; i < values.length; i++) {
+      assertEquals(values[i], (Double) rows.get(i)[1]);
+    }
+    assertNull(rows.get(values.length)[1]);
+  }
+
+  @Test
+  void bigNumberWithoutFormatKeepsAllDigits() throws Exception {
+    Locale defaultLocale = Locale.getDefault();
+    // A locale with a comma decimal separator and a dot grouping separator
+    Locale.setDefault(Locale.GERMANY);
+    try {
+      Variables variables = new Variables();
+      variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, 
tempDir.toString());
+
+      DataSet dataSet = new DataSet();
+      dataSet.setName("big-numbers");
+      dataSet.setBaseFilename("big-numbers.csv");
+      dataSet.setFields(
+          List.of(
+              new DataSetField("id", IValueMeta.TYPE_INTEGER, -1, 0, "", "0"),
+              new DataSetField("unsized", IValueMeta.TYPE_BIGNUMBER, -1, -1, 
"", ""),
+              new DataSetField("sized", IValueMeta.TYPE_BIGNUMBER, 10, 2, "", 
"")));
+
+      BigDecimal[] values = {
+        new BigDecimal("1234.56789"),
+        new BigDecimal("-1234567.5"),
+        new BigDecimal("0.1234567890123456789012345"),
+        new BigDecimal("12345678901234567890.5")
+      };
+      try (DataSetCsvWriter writer =
+          new DataSetCsvWriter(variables, dataSet, dataSet.getSetRowMeta())) {
+        for (int i = 0; i < values.length; i++) {
+          writer.writeRow(new Object[] {(long) i, values[i], values[i]});
+        }
+        writer.writeRow(new Object[] {(long) values.length, null, null});
+      }
+
+      String csv = Files.readString(tempDir.resolve("big-numbers.csv"));
+      // No grouping, a dot decimal separator and no zero-padding to the 
declared length
+      assertTrue(csv.contains("0,1234.56789,1234.56789"), csv);
+      assertTrue(csv.contains("1,-1234567.5,-1234567.5"), csv);
+
+      List<Object[]> rows = DataSetCsvUtil.getAllRows(variables, dataSet);
+      assertEquals(values.length + 1, rows.size());
+      for (int i = 0; i < values.length; i++) {
+        assertEquals(0, values[i].compareTo((BigDecimal) rows.get(i)[1]), csv);
+        assertEquals(0, values[i].compareTo((BigDecimal) rows.get(i)[2]), csv);
+      }
+      assertNull(rows.get(values.length)[1]);
+      assertNull(rows.get(values.length)[2]);
+    } finally {
+      Locale.setDefault(defaultLocale);
+    }
+  }
+
+  @Test
+  void bigNumberWrittenZeroPaddedIsStillRead() throws Exception {
+    Variables variables = new Variables();
+    variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, 
tempDir.toString());
+
+    DataSet dataSet = new DataSet();
+    dataSet.setName("padded");
+    dataSet.setBaseFilename("padded.csv");
+    dataSet.setFields(List.of(new DataSetField("sized", 
IValueMeta.TYPE_BIGNUMBER, 10, 2, "", "")));
+
+    // What earlier versions wrote for a BigNumber(10,2) field without a format
+    Files.writeString(tempDir.resolve("padded.csv"), "sized\n\" 
00001234.57\"\n-01234567.50\n");
+
+    List<Object[]> rows = DataSetCsvUtil.getAllRows(variables, dataSet);
+    assertEquals(2, rows.size());
+    assertEquals(0, new BigDecimal("1234.57").compareTo((BigDecimal) 
rows.get(0)[0]));
+    assertEquals(0, new BigDecimal("-1234567.5").compareTo((BigDecimal) 
rows.get(1)[0]));
+  }
+
+  @Test
+  void numberWithFormatUsesThatFormat() throws Exception {
+    Variables variables = new Variables();
+    variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, 
tempDir.toString());
+
+    DataSet dataSet = new DataSet();
+    dataSet.setName("formatted");
+    dataSet.setBaseFilename("formatted.csv");
+    dataSet.setFields(List.of(new DataSetField("pi", IValueMeta.TYPE_NUMBER, 
-1, -1, "", "0.00")));
+
+    try (DataSetCsvWriter writer =
+        new DataSetCsvWriter(variables, dataSet, dataSet.getSetRowMeta())) {
+      writer.writeRow(new Object[] {3.14159});
+    }
+
+    List<Object[]> rows = DataSetCsvUtil.getAllRows(variables, dataSet);
+    assertEquals(3.14, (Double) rows.get(0)[0]);
+  }
+
   @Test
   void binaryValuesRoundTripAsLowercaseHex() throws Exception {
     Variables variables = new Variables();

Reply via email to