From e784efd4e5d988ba95f0fff14c5178d7cdb67c66 Mon Sep 17 00:00:00 2001
From: Taylor Smock <tsmock@fb.com>
Date: Wed, 26 May 2021 13:07:16 -0600
Subject: [PATCH 1/2] VectorTile: Feature: Always use root locale (stable
 numbers)

This fixes #20933.

Signed-off-by: Taylor Smock <tsmock@fb.com>
---
 .../josm/data/imagery/vectortile/mapbox/Feature.java           | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
index 7f4fa1023..9dc07ce73 100644
--- a/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
+++ b/src/org/openstreetmap/josm/data/imagery/vectortile/mapbox/Feature.java
@@ -5,6 +5,7 @@ import java.io.IOException;
 import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 import org.openstreetmap.josm.data.osm.TagMap;
 import org.openstreetmap.josm.data.protobuf.ProtobufPacked;
@@ -110,7 +111,7 @@ public class Feature {
             Object value = layer.getValue(number.intValue());
             if (value instanceof Double || value instanceof Float) {
                 // reset grouping if the instance is a singleton
-                final NumberFormat numberFormat = NumberFormat.getNumberInstance();
+                final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.ROOT);
                 final boolean grouping = numberFormat.isGroupingUsed();
                 try {
                     numberFormat.setGroupingUsed(false);
-- 
GitLab


From bd8b2251deec9d5d335f7b6ab12abb796d3ee68b Mon Sep 17 00:00:00 2001
From: Taylor Smock <tsmock@fb.com>
Date: Wed, 26 May 2021 14:40:50 -0600
Subject: [PATCH 2/2] FeatureTest: Add non-regression test for #20933.

Signed-off-by: Taylor Smock <tsmock@fb.com>
---
 .../vectortile/mapbox/FeatureTest.java        | 44 +++++++++++++-
 .../josm/testutils/annotations/I18n.java      | 59 +++++++++++++++++++
 2 files changed, 102 insertions(+), 1 deletion(-)
 create mode 100644 test/unit/org/openstreetmap/josm/testutils/annotations/I18n.java

diff --git a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java
index 5468fe649..fcc71345b 100644
--- a/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java
+++ b/test/unit/org/openstreetmap/josm/data/imagery/vectortile/mapbox/FeatureTest.java
@@ -12,9 +12,12 @@ import static org.openstreetmap.josm.data.imagery.vectortile.mapbox.LayerTest.ge
 import static org.openstreetmap.josm.data.imagery.vectortile.mapbox.LayerTest.getLayer;
 
 import java.text.NumberFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import org.junit.jupiter.api.Test;
+import org.openstreetmap.josm.testutils.annotations.I18n;
 
 /**
  * Test class for {@link Feature}
@@ -50,7 +53,7 @@ class FeatureTest {
         // This is the float we are adding
         // 49 74 24 00 == 1_000_000f
         // 3f 80 00 00 == 1f
-        byte[] newBytes = new byte[] {0x22, 0x09, 0x15, 0x00, 0x24, 0x74, 0x49};
+        byte[] newBytes = new byte[] {0x22, 0x05, 0x15, 0x00, 0x24, 0x74, 0x49};
         byte[] copyBytes = Arrays.copyOf(getSimpleFeatureLayerBytes(), getSimpleFeatureLayerBytes().length + newBytes.length - 4);
         // Change last few bytes
         System.arraycopy(newBytes, 0, copyBytes, 25, newBytes.length);
@@ -79,6 +82,45 @@ class FeatureTest {
         assertEquals("1000000", feature.getTags().get("a"));
     }
 
+    /**
+     * Non-regression test for #20933 (Russian)
+     * @see #testNumberGroupingDecimalEn()
+     */
+    @I18n("ru")
+    @Test
+    void testNumberGroupingDecimalRu() {
+        testNumberGroupingDecimal();
+    }
+
+    /**
+     * Non-regression test for #20933 (English)
+     * @see #testNumberGroupingDecimalRu()
+     */
+    @I18n("en")
+    @Test
+    void testNumberGroupingDecimalEn() {
+        testNumberGroupingDecimal();
+    }
+
+    /**
+     * Common parts for non-regression tests for #20933
+     * @see #testNumberGroupingDecimalEn()
+     * @see #testNumberGroupingDecimalRu()
+     */
+    private void testNumberGroupingDecimal() {
+        byte[] newBytes = new byte[] {0x22, 0x09, 0x19, -45, 0x4D, 0x62, 0x10, 0x58, -71, 0x67, 0x40};
+        byte[] copyBytes = Arrays.copyOf(getSimpleFeatureLayerBytes(), getSimpleFeatureLayerBytes().length + newBytes.length - 4);
+        // Change last few bytes
+        System.arraycopy(newBytes, 0, copyBytes, 25, newBytes.length);
+        // Update the length of the record
+        copyBytes[1] = (byte) (copyBytes[1] + newBytes.length - 4);
+        Layer layer = assertDoesNotThrow(() -> getLayer(copyBytes));
+        layer.getKey(0);
+        List<Feature> features = new ArrayList<>(layer.getFeatures());
+        assertEquals(1, features.size());
+        assertEquals("189.792", features.get(0).getTags().get("a"));
+    }
+
     private void testCreation(byte[] bytes) {
         Layer layer = assertDoesNotThrow(() -> getLayer(bytes));
         // Sanity check the layer
diff --git a/test/unit/org/openstreetmap/josm/testutils/annotations/I18n.java b/test/unit/org/openstreetmap/josm/testutils/annotations/I18n.java
new file mode 100644
index 000000000..0375427f2
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/testutils/annotations/I18n.java
@@ -0,0 +1,59 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+import static java.util.Collections.emptyList;
+
+/**
+ * Enables the i18n module for this test.
+ * @author Taylor Smock
+ * @see JOSMTestRules#i18n(String)
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@ExtendWith(I18n.I18nExtension.class)
+public @interface I18n {
+    /**
+     * Get the language to use for i18n
+     * @return The language (default "en").
+     */
+    String value() default "en";
+
+    /**
+     * Enables the i18n module for this test.
+     * @author Taylor Smock
+     * @see JOSMTestRules#i18n(String)
+     *
+     */
+    class I18nExtension implements AfterEachCallback, BeforeEachCallback {
+        @Override
+        public void afterEach(ExtensionContext context) throws Exception {
+            org.openstreetmap.josm.tools.I18n.set("en");
+            org.openstreetmap.josm.tools.I18n.set(org.openstreetmap.josm.tools.I18n.getOriginalLocale().getLanguage());
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            Optional<I18n> annotation = AnnotationSupport.findAnnotation(context.getElement(), I18n.class);
+            String language = "en";
+            if (annotation.isPresent()) {
+                language = annotation.get().value();
+            }
+            org.openstreetmap.josm.tools.I18n.set(language);
+        }
+    }
+}
-- 
GitLab

