You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2026/05/03 13:39:25 UTC

(cayenne) branch master updated: CAY-2931 Modeler: default encoding detected as UTF8, should be UTF-8

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

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new 26eca86df CAY-2931 Modeler: default encoding detected as UTF8, should be UTF-8
26eca86df is described below

commit 26eca86dfcb1218c40525aabcaaeb263ed276217
Author: Andrus Adamchik <an...@objectstyle.com>
AuthorDate: Sun May 3 09:21:34 2026 -0400

    CAY-2931 Modeler: default encoding detected as UTF8, should be UTF-8
---
 RELEASE-NOTES.txt                                  |  1 +
 .../migration/toV5/_3_GeneralPrefsMigration.java   | 28 ++++++++-
 .../general/GeneralPreferencesController.java      | 24 ++++----
 .../toV5/_3_GeneralPrefsMigrationTest.java         | 66 ++++++++++++++++++++++
 .../general/GeneralPreferencesControllerTest.java  | 50 ++++++++++++++++
 5 files changed, 158 insertions(+), 11 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 9fecc4e5b..1d36b6c42 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -60,6 +60,7 @@ CAY-2904 Disjoint prefetch returns incorrect data
 CAY-2919 Exception in "Generate Database Schema"
 CAY-2921 Modeler: consistent duplicate name handling
 CAY-2926 Modeler: renaming an ObjEntity adds it to cgen excludeEntities
+CAY-2931 Modeler: default encoding detected as UTF8, should be UTF-8
 
 ----------------------------------
 Release: 5.0-M1
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/migration/toV5/_3_GeneralPrefsMigration.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/migration/toV5/_3_GeneralPrefsMigration.java
index be631a04e..6f0103cc8 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/migration/toV5/_3_GeneralPrefsMigration.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/migration/toV5/_3_GeneralPrefsMigration.java
@@ -24,6 +24,9 @@ import org.apache.cayenne.modeler.pref.PreferencesRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
 import java.util.prefs.BackingStoreException;
 import java.util.prefs.Preferences;
 
@@ -58,10 +61,33 @@ public class _3_GeneralPrefsMigration implements PreferenceMigration {
 
         Preferences target = repo.appPref(GeneralPrefs.NODE);
         target.putBoolean(GeneralPrefs.AUTO_LOAD_PROJECT, legacy.getBoolean("autoLoadProject", false));
-        target.put(GeneralPrefs.ENCODING, legacy.get("encoding", ""));
+        String encoding = normalizeEncoding(legacy.get("encoding", ""));
+        if (encoding != null) {
+            target.put(GeneralPrefs.ENCODING, encoding);
+        }
         target.put(GeneralPrefs.FAVOURITE_DATA_SOURCE, legacy.get("favouriteDataSource", ""));
 
         // legacy used the wrong name - "deletePrompt", for what was meant to be "delete with no prompt"
         target.putBoolean(GeneralPrefs.NO_DELETE_PROMPT, legacy.getBoolean("deletePrompt", false));
     }
+
+    /**
+     * Converts a legacy encoding name to its canonical Charset form. Older Modeler
+     * versions wrote whatever {@code OutputStreamWriter.getEncoding()} returned —
+     * historical aliases like "UTF8" — which no longer match the canonical names
+     * ("UTF-8") shown in the encoding dropdown. Returns {@code null} for empty
+     * or unrecognized values to signal that nothing should be written to the new
+     * prefs node.
+     */
+    static String normalizeEncoding(String encoding) {
+        if (encoding == null || encoding.isEmpty()) {
+            return null;
+        }
+        try {
+            return Charset.forName(encoding).name();
+        } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
+            LOGGER.warn("Dropping unsupported legacy encoding '{}'", encoding);
+            return null;
+        }
+    }
 }
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/preferences/general/GeneralPreferencesController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/preferences/general/GeneralPreferencesController.java
index 9a04d035f..aa41054b6 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/preferences/general/GeneralPreferencesController.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/preferences/general/GeneralPreferencesController.java
@@ -25,15 +25,15 @@ import org.apache.cayenne.modeler.ui.preferences.PreferenceDialogController;
 
 import javax.swing.*;
 import java.awt.*;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 public class GeneralPreferencesController extends ChildController<PreferenceDialogController> {
 
-    private static final String[] STANDARD_ENCODINGS = {
+    static final String[] STANDARD_ENCODINGS = {
             "ISO-8859-1", "US-ASCII", "UTF-8", "UTF-16", "UTF-16BE", "UTF-16LE"
     };
 
@@ -73,21 +73,25 @@ public class GeneralPreferencesController extends ChildController<PreferenceDial
     }
 
     /**
-     * Returns default encoding on the current platform.
+     * Returns the canonical name of the platform's default charset. Canonicalizing
+     * (vs. e.g. {@code OutputStreamWriter.getEncoding()}, which returns historical
+     * aliases like "UTF8") ensures the value compares cleanly against
+     * {@link #STANDARD_ENCODINGS}.
      */
-    protected String detectPlatformEncoding() {
-        return new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
+    static String detectPlatformEncoding() {
+        return Charset.defaultCharset().name();
     }
 
     /**
-     * Returns charsets that all JVMs must support cross-platform, with the platform
-     * default placed first and labeled. See java.nio.charset.Charset for the list of
-     * "standard" charsets.
+     * Returns charsets that all JVMs must support cross-platform, sorted
+     * alphabetically, with the platform default labeled in place. See
+     * java.nio.charset.Charset for the list of "standard" charsets.
      */
     private String[] supportedEncodings() {
         List<String> charsets = new ArrayList<>(Arrays.asList(STANDARD_ENCODINGS));
         charsets.remove(systemEncoding);
-        charsets.add(0, defaultLabel);
+        charsets.add(defaultLabel);
+        Collections.sort(charsets);
         return charsets.toArray(new String[0]);
     }
 
diff --git a/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/pref/migration/toV5/_3_GeneralPrefsMigrationTest.java b/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/pref/migration/toV5/_3_GeneralPrefsMigrationTest.java
new file mode 100644
index 000000000..076ed2dcd
--- /dev/null
+++ b/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/pref/migration/toV5/_3_GeneralPrefsMigrationTest.java
@@ -0,0 +1,66 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.modeler.pref.migration.toV5;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+
+
+public class _3_GeneralPrefsMigrationTest {
+
+    @Test
+    public void normalizeEncoding_historicalAlias() {
+        assertEquals("UTF-8", _3_GeneralPrefsMigration.normalizeEncoding("UTF8"));
+    }
+
+    @Test
+    public void normalizeEncoding_alreadyCanonical() {
+        assertEquals("UTF-8", _3_GeneralPrefsMigration.normalizeEncoding("UTF-8"));
+    }
+
+    @Test
+    public void normalizeEncoding_caseInsensitive() {
+        assertEquals("ISO-8859-1", _3_GeneralPrefsMigration.normalizeEncoding("iso-8859-1"));
+    }
+
+    @Test
+    public void normalizeEncoding_empty() {
+        assertNull(_3_GeneralPrefsMigration.normalizeEncoding(""));
+    }
+
+    @Test
+    public void normalizeEncoding_null() {
+        assertNull(_3_GeneralPrefsMigration.normalizeEncoding(null));
+    }
+
+    @Test
+    public void normalizeEncoding_unsupportedReturnsNull() {
+        assertNull(_3_GeneralPrefsMigration.normalizeEncoding("BogusCharset"));
+    }
+
+    @Test
+    public void normalizeEncoding_illegalNameReturnsNull() {
+        // Charset.forName throws IllegalCharsetNameException for names with illegal chars
+        assertNull(_3_GeneralPrefsMigration.normalizeEncoding("@#$"));
+    }
+}
diff --git a/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/ui/preferences/general/GeneralPreferencesControllerTest.java b/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/ui/preferences/general/GeneralPreferencesControllerTest.java
new file mode 100644
index 000000000..fb24adb99
--- /dev/null
+++ b/modeler/cayenne-modeler/src/test/java/org/apache/cayenne/modeler/ui/preferences/general/GeneralPreferencesControllerTest.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.modeler.ui.preferences.general;
+
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+
+public class GeneralPreferencesControllerTest {
+
+    @Test
+    public void standardEncodingsAreSupportedByJVM() {
+        assertTrue("STANDARD_ENCODINGS must not be empty",
+                GeneralPreferencesController.STANDARD_ENCODINGS.length > 0);
+
+        for (String encoding : GeneralPreferencesController.STANDARD_ENCODINGS) {
+            assertTrue("Charset not supported by JVM: " + encoding,
+                    Charset.isSupported(encoding));
+        }
+    }
+
+    @Test
+    public void detectPlatformEncodingReturnsCanonicalName() {
+        // Must return the canonical Charset name (e.g. "UTF-8"), not a historical
+        // alias (e.g. "UTF8"), so it matches entries in STANDARD_ENCODINGS.
+        String encoding = GeneralPreferencesController.detectPlatformEncoding();
+        assertEquals(Charset.forName(encoding).name(), encoding);
+    }
+}