Ticket #24940: utf8-file-names-dialog.patch

File utf8-file-names-dialog.patch, 10.2 KB (added by wangi, 14 hours ago)
  • src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java

    diff --git src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java
    index af74571464..0f0ff68af2 100644
    import org.openstreetmap.josm.spi.preferences.Config;  
    3131import org.openstreetmap.josm.tools.GBC;
    3232import org.openstreetmap.josm.tools.ImageProvider;
    3333import org.openstreetmap.josm.tools.InputMapUtils;
     34import org.openstreetmap.josm.tools.Utils;
    3435import org.openstreetmap.josm.tools.bugreport.BugReport;
    3536import org.openstreetmap.josm.tools.bugreport.BugReportQueue.SuppressionMode;
    3637import org.openstreetmap.josm.tools.bugreport.BugReportSender;
    … … import org.openstreetmap.josm.tools.bugreport.ReportedException;  
    4546 */
    4647public class BugReportDialog extends JDialog {
    4748    private static final int MAX_MESSAGE_SIZE = 500;
     49    /** whether the user has been told about file names that cannot be represented, which is done once per session */
     50    private static volatile boolean unmappableFileNameExplained;
    4851    // This is explicitly not an ExtendedDialog - we still want to be able to display bug reports if there are problems with preferences/..
    4952    private final JPanel content = new JPanel(new GridBagLayout());
    5053    private final BugReport report;
    … … public class BugReportDialog extends JDialog {  
    228231                    JOptionPane.ERROR_MESSAGE
    229232                    );
    230233            return SuppressionMode.NONE;
     234        } else if (e.isUnmappableFileName()) {
     235            // Not a bug in JOSM, but a locale which cannot represent the name of a file, see #14596. Explain how to
     236            // fix it rather than asking for a bug report, and only once: browsing other folders throws it again.
     237            if (!unmappableFileNameExplained) {
     238                unmappableFileNameExplained = true;
     239                GuiHelper.runInEDTAndWait(() -> JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
     240                        tr("<html>JOSM cannot handle the name of a file, because it is running with the character set {0} "
     241                                + "for file names, which cannot represent it.<br>"
     242                                + "This happens when JOSM is started without a UTF-8 locale, for example with {1}, "
     243                                + "or with a locale which is not installed.<br><br>"
     244                                + "Please start JOSM with the environment variable {2}.</html>",
     245                                Utils.getSystemProperty("sun.jnu.encoding"), "<tt>LANG=C</tt>", "<tt>LC_ALL=C.UTF-8</tt>"),
     246                        tr("Unsupported file name"),
     247                        JOptionPane.WARNING_MESSAGE));
     248            }
     249            return SuppressionMode.NONE;
    231250        } else {
    232251            PluginDownloadTask downloadTask = PluginHandler.updateOrdisablePluginAfterException(e);
    233252            return GuiHelper.runInEDTAndWaitAndReturn(() -> {
  • src/org/openstreetmap/josm/tools/bugreport/ReportedException.java

    diff --git src/org/openstreetmap/josm/tools/bugreport/ReportedException.java src/org/openstreetmap/josm/tools/bugreport/ReportedException.java
    index 28774399ee..9a2d7f4543 100644
    package org.openstreetmap.josm.tools.bugreport;  
    44import java.io.PrintWriter;
    55import java.io.Serializable;
    66import java.lang.reflect.InvocationTargetException;
     7import java.nio.file.InvalidPathException;
    78import java.util.ArrayList;
    89import java.util.Arrays;
    910import java.util.Collection;
    … … import java.util.function.Supplier;  
    2122
    2223import org.openstreetmap.josm.tools.Logging;
    2324import org.openstreetmap.josm.tools.StreamUtils;
     25import org.openstreetmap.josm.tools.Utils;
    2426
    2527/**
    2628 * This is a special exception that cannot be directly thrown.
    … … public class ReportedException extends RuntimeException {  
    277279        return StreamUtils.toStream(CauseTraceIterator::new).anyMatch(OutOfMemoryError.class::isInstance);
    278280    }
    279281
     282    /**
     283     * Check if this is caused by the name of a file which cannot be represented in the character set Java uses for
     284     * file names. Java takes that character set from the locale, so this happens when JOSM runs in the C/POSIX locale,
     285     * or with a locale which is not installed: file names are then ASCII only, and opening a folder which holds a
     286     * non-ASCII name fails. See #14596.
     287     * @return <code>true</code> if it is.
     288     * @since xxx
     289     */
     290    public boolean isUnmappableFileName() {
     291        return !"UTF-8".equalsIgnoreCase(Utils.getSystemProperty("sun.jnu.encoding"))
     292                && StreamUtils.toStream(CauseTraceIterator::new).anyMatch(t -> t instanceof InvalidPathException
     293                        && String.valueOf(((InvalidPathException) t).getReason()).contains("unmappable characters"));
     294    }
     295
    280296    /**
    281297     * Iterates over the causes for this exception. Ignores cycles and aborts iteration then.
    282298     * @author Michal Zangl
  • new file test/unit/org/openstreetmap/josm/gui/bugreport/BugReportDialogTest.java

    diff --git test/unit/org/openstreetmap/josm/gui/bugreport/BugReportDialogTest.java test/unit/org/openstreetmap/josm/gui/bugreport/BugReportDialogTest.java
    new file mode 100644
    index 0000000000..086a0f5d9d
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.bugreport;
     3
     4import static org.junit.jupiter.api.Assertions.assertEquals;
     5
     6import java.nio.file.InvalidPathException;
     7import java.util.Collections;
     8
     9import javax.swing.JOptionPane;
     10
     11import org.junit.jupiter.api.Test;
     12import org.openstreetmap.josm.TestUtils;
     13import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
     14import org.openstreetmap.josm.tools.bugreport.BugReport;
     15import org.openstreetmap.josm.tools.bugreport.BugReportQueue.SuppressionMode;
     16import org.openstreetmap.josm.tools.bugreport.ReportedException;
     17
     18/**
     19 * Unit tests of {@link BugReportDialog}.
     20 */
     21class BugReportDialogTest {
     22
     23    /**
     24     * A file name the locale cannot represent is explained to the user, once, instead of asking for a bug report.
     25     * See #14596.
     26     */
     27    @Test
     28    void testUnmappableFileNameIsExplainedOnce() {
     29        TestUtils.assumeWorkingJMockit();
     30        final JOptionPaneSimpleMocker mocker = new JOptionPaneSimpleMocker(Collections.singletonMap(
     31                "<html>JOSM cannot handle the name of a file, because it is running with the character set ANSI_X3.4-1968 "
     32                        + "for file names, which cannot represent it.<br>"
     33                        + "This happens when JOSM is started without a UTF-8 locale, for example with <tt>LANG=C</tt>, "
     34                        + "or with a locale which is not installed.<br><br>"
     35                        + "Please start JOSM with the environment variable <tt>LC_ALL=C.UTF-8</tt>.</html>",
     36                JOptionPane.OK_OPTION));
     37        final ReportedException e = BugReport.intercept(new InvalidPathException("/home/user/Capture d'écran.png",
     38                "Malformed input or input contains unmappable characters"));
     39        final String jnuEncoding = System.getProperty("sun.jnu.encoding");
     40        try {
     41            System.setProperty("sun.jnu.encoding", "ANSI_X3.4-1968");
     42            // no bug report dialog: that would fail here, as the tests run headless
     43            assertEquals(SuppressionMode.NONE, BugReportDialog.showFor(e, 0));
     44            // browsing another folder throws it again, which is not worth another message
     45            assertEquals(SuppressionMode.NONE, BugReportDialog.showFor(e, 1));
     46        } finally {
     47            System.setProperty("sun.jnu.encoding", jnuEncoding);
     48        }
     49        assertEquals(1, mocker.getInvocationLog().size());
     50        assertEquals("Unsupported file name", mocker.getInvocationLog().get(0)[2]);
     51    }
     52}
  • test/unit/org/openstreetmap/josm/tools/bugreport/ReportedExceptionTest.java

    diff --git test/unit/org/openstreetmap/josm/tools/bugreport/ReportedExceptionTest.java test/unit/org/openstreetmap/josm/tools/bugreport/ReportedExceptionTest.java
    index 1125bfd5d2..36cf937fff 100644
     
    22package org.openstreetmap.josm.tools.bugreport;
    33
    44import static org.junit.jupiter.api.Assertions.assertEquals;
     5import static org.junit.jupiter.api.Assertions.assertFalse;
     6import static org.junit.jupiter.api.Assertions.assertTrue;
    57
     8import java.nio.file.InvalidPathException;
    69import java.util.Arrays;
    710
    811import org.junit.jupiter.api.Test;
    … … class ReportedExceptionTest {  
    6063        e.put("testList", Arrays.asList(a));
    6164    }
    6265
     66    /**
     67     * Tests {@link ReportedException#isUnmappableFileName()}: a file name which the character set used for file names
     68     * cannot represent is recognised, also as the cause of another exception, but only if that character set is not
     69     * UTF-8 - otherwise the locale is not to blame. See #14596.
     70     */
     71    @Test
     72    void testIsUnmappableFileName() {
     73        // the exception sun.nio.fs.UnixPath throws for "Capture d'écran.png" when the locale is C or not installed
     74        InvalidPathException unmappable = new InvalidPathException("/home/user/Capture d'\u00e9cran.png",
     75                "Malformed input or input contains unmappable characters");
     76        String jnuEncoding = System.getProperty("sun.jnu.encoding");
     77        try {
     78            System.setProperty("sun.jnu.encoding", "ANSI_X3.4-1968");
     79            assertTrue(new ReportedException(unmappable).isUnmappableFileName());
     80            assertTrue(new ReportedException(new IllegalStateException(unmappable)).isUnmappableFileName());
     81            assertFalse(new ReportedException(new InvalidPathException("a\0b", "Nul character not allowed")).isUnmappableFileName());
     82            assertFalse(new ReportedException(new IllegalStateException()).isUnmappableFileName());
     83
     84            System.setProperty("sun.jnu.encoding", "UTF-8");
     85            assertFalse(new ReportedException(unmappable).isUnmappableFileName());
     86        } finally {
     87            System.setProperty("sun.jnu.encoding", jnuEncoding);
     88        }
     89    }
     90
    6391    /**
    6492     * Tests that {@link ReportedException#isSame(ReportedException)} works as expected.
    6593     */