Index: /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FDSettings.java
===================================================================
--- /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FDSettings.java	(revision 36056)
+++ /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FDSettings.java	(revision 36057)
@@ -9,4 +9,7 @@
 import org.openstreetmap.josm.spi.preferences.Config;
 
+/**
+ * A holder for plugin settings
+ */
 public class FDSettings {
     public NamedColorProperty COLOR_FIXED = new NamedColorProperty("fastdraw.color.delete", Color.red);
Index: /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawConfigDialog.java
===================================================================
--- /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawConfigDialog.java	(revision 36056)
+++ /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawConfigDialog.java	(revision 36057)
@@ -27,11 +27,10 @@
 import org.openstreetmap.josm.tools.TextTagParser;
 
+/**
+ * A configuration dialog
+ */
 public class FastDrawConfigDialog extends ExtendedDialog {
 
-    private final JLabel label1 = new JLabel(tr("Epsilon multiplier"));
-    private final JLabel label2 = new JLabel(tr("Starting Epsilon"));
-    private final JLabel label3 = new JLabel(tr("Max points count per 1 km"));
-    private final JLabel label4 = new JLabel(/* I18n: Combobox to select what a press to return key does */ tr("Enter key mode"));
-    private final JLabel label5 = new JLabel(tr("Auto add tags"));
+    private static final long serialVersionUID = -4894608522010226092L;
     private final JFormattedTextField text1 = new JFormattedTextField(NumberFormat.getInstance());
     private final JFormattedTextField text2 = new JFormattedTextField(NumberFormat.getInstance());
@@ -47,6 +46,10 @@
     private final FDSettings settings;
 
+    /**
+     * Create anew configuration dialog
+     * @param settings The settings to use
+     */
     public FastDrawConfigDialog(FDSettings settings) {
-        super(MainApplication.getMainFrame(), tr("FastDraw configuration"), new String[] {tr("Ok"), tr("Cancel")});
+        super(MainApplication.getMainFrame(), tr("FastDraw configuration"), tr("Ok"), tr("Cancel"));
         this.settings = settings;
 
@@ -55,11 +58,11 @@
         all.setLayout(layout);
         JButton pasteButton = new JButton(new AbstractAction(tr("Paste"), ImageProvider.get("apply")) {
+            private static final long serialVersionUID = -8597276971260620654L;
+
             @Override
             public void actionPerformed(ActionEvent e) {
                 String s = ClipboardUtils.getClipboardStringContent();
-                if (s != null) {
-                    if (TextTagParser.getValidatedTagsFromText(s, TextTagPaster::warning) != null) {
-                        addTags.setText(s);
-                    }
+                if (s != null && TextTagParser.getValidatedTagsFromText(s, TextTagPaster::warning) != null) {
+                    addTags.setText(s);
                 }
             }
@@ -68,16 +71,22 @@
 
         addTags.getModel().prefs().load("fastdraw.tags-history");
-        while (addTags.getModel().find("") != null)
+        while (addTags.getModel().find("") != null) {
             addTags.getModel().removeElement("");
+        }
 
+        JLabel label1 = new JLabel(tr("Epsilon multiplier"));
         all.add(label1, GBC.std().insets(10, 0, 0, 0));
         all.add(text1, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+        JLabel label2 = new JLabel(tr("Starting Epsilon"));
         all.add(label2, GBC.std().insets(10, 0, 0, 0));
         all.add(text2, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+        JLabel label3 = new JLabel(tr("Max points count per 1 km"));
         all.add(label3, GBC.std().insets(10, 0, 0, 0));
         all.add(text3, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
+        JLabel label4 = new JLabel(/* I18n: Combobox to select what a press to return key does */ tr("Enter key mode"));
         all.add(label4, GBC.std().insets(10, 0, 0, 0));
         all.add(combo1, GBC.eop().fill(GBC.HORIZONTAL).insets(5, 0, 0, 5));
 
+        JLabel label5 = new JLabel(tr("Auto add tags"));
         all.add(label5, GBC.std().insets(10, 0, 0, 0));
         all.add(pasteButton, GBC.eop().insets(0, 0, 0, 5));
@@ -105,11 +114,8 @@
 
         setContent(all, false);
-        setButtonIcons(new String[] {"ok", "cancel"});
-        setToolTipTexts(new String[] {
-                tr("Save settings"),
-                tr("Cancel")
-        });
+        setButtonIcons("ok", "cancel");
+        setToolTipTexts(tr("Save settings"),
+                tr("Cancel"));
         setDefaultButton(1);
-        //configureContextsensitiveHelp("/Action/DownloadObject", true /* show help button */);
     }
 
Index: /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawingMode.java
===================================================================
--- /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawingMode.java	(revision 36056)
+++ /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawingMode.java	(revision 36057)
@@ -50,8 +50,9 @@
  */
 class FastDrawingMode extends MapMode implements MapViewPaintable, KeyPressReleaseListener, ModifierExListener {
+    private static final long serialVersionUID = -3395918050593965929L;
     // CHECKSTYLE.OFF: LineLength
-    private static final String SIMPLIFYMODE_MESSAGE =
+    private static final String SIMPLIFY_MODE_MESSAGE =
             tr("Q=Options, Enter=save, Ctrl-Enter=save with tags, Up/Down=tune");
-    private static final String DRAWINGMODE_MESSAGE =
+    private static final String DRAWING_MODE_MESSAGE =
             tr("Click or Click&drag to continue, Ctrl-Click to add fixed node, Shift-Click to delete, Enter to simplify or save, Ctrl-Shift-Click to start new line");
     // CHECKSTYLE.ON: LineLength
@@ -68,5 +69,4 @@
     private final Cursor cursorShift;
     private final Cursor cursorReady;
-    //private final Cursor cursorNode;
     private final Cursor cursorDrawing;
     private boolean nearSomeNode;
@@ -80,6 +80,6 @@
     private Way oldWay;
 
-    FastDrawingMode(MapFrame mapFrame) {
-        super(tr("FastDrawing"), "turbopen.png", tr("Fast drawing mode"),
+    FastDrawingMode() {
+        super(tr("FastDrawing"), "turbopen", tr("Fast drawing mode"),
                 Shortcut.registerShortcut("mapmode:fastdraw", tr("Mode: {0}", tr("Fast drawing mode")), KeyEvent.VK_F, Shortcut.SHIFT),
                 Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
@@ -89,7 +89,5 @@
         cursorShift = ImageProvider.getCursor("crosshair", "new");
         cursorReady = ImageProvider.getCursor("crosshair", "ready");
-        //cursorNode = ImageProvider.getCursor("crosshair", "joinnode");
         cursorDrawing = ImageProvider.getCursor("crosshair", "mode");
-        //loadPrefs();
     }
 
@@ -98,4 +96,5 @@
     @Override
     public void enterMode() {
+        this.updateEnabledState();
         if (!isEnabled())
             return;
@@ -150,5 +149,5 @@
     }
 
-    private final ArrayList<Point> fixedPoints = new ArrayList<>(3000); // temporary storate for paint
+    private final ArrayList<Point> fixedPoints = new ArrayList<>(3000); // temporary storage for paint
 
     //////////    Event listener methods
@@ -201,20 +200,18 @@
                     g.fillRect(p2.x - rp, p2.y - rp, dp, dp);
                 }
-                if (!drawing) {
-                    if (!line.wasSimplified() && nearestPointIndex == i+1) {
-                        if (shift) {
-                            // highlight node to delete
-                            g.setStroke(settings.deleteStroke);
-                            g.setColor(settings.COLOR_DELETE.get());
-                            g.drawLine(p2.x - 5, p2.y - 5, p2.x + 5, p2.y + 5);
-                            g.drawLine(p2.x - 5, p2.y + 5, p2.x + 5, p2.y - 5);
-                            g.setStroke(settings.normalStroke);
-                        } else if (ctrl) {
-                            // highlight node to toggle fixation
-                            g.setStroke(settings.deleteStroke);
-                            g.setColor(line.isFixed(pp2) ? settings.COLOR_NORMAL.get() : settings.COLOR_FIXED.get());
-                            g.fillOval(p2.x - bigDotSize/2-2, p2.y - bigDotSize/2-2, bigDotSize+4, bigDotSize+4);
-                            g.setStroke(settings.normalStroke);
-                        }
+                if (!drawing && !line.wasSimplified() && nearestPointIndex == i+1) {
+                    if (shift) {
+                        // highlight node to delete
+                        g.setStroke(settings.deleteStroke);
+                        g.setColor(settings.COLOR_DELETE.get());
+                        g.drawLine(p2.x - 5, p2.y - 5, p2.x + 5, p2.y + 5);
+                        g.drawLine(p2.x - 5, p2.y + 5, p2.x + 5, p2.y - 5);
+                        g.setStroke(settings.normalStroke);
+                    } else if (ctrl) {
+                        // highlight node to toggle fixation
+                        g.setStroke(settings.deleteStroke);
+                        g.setColor(line.isFixed(pp2) ? settings.COLOR_NORMAL.get() : settings.COLOR_FIXED.get());
+                        g.fillOval(p2.x - bigDotSize/2-2, p2.y - bigDotSize/2-2, bigDotSize+4, bigDotSize+4);
+                        g.setStroke(settings.normalStroke);
                     }
                 }
@@ -286,5 +283,4 @@
 
     private void startDrawing(Point point, boolean fixFlag) {
-        //if (line.isClosed()) { setStatusLine(tr(SIMPLIFYMODE_MESSAGE));return;  }
         drawing = true;
         if (line.wasSimplified()) {
@@ -293,5 +289,4 @@
             saveAsWay(false);
             newDrawing();
-            //line.clearSimplifiedVersion();
         }
 
@@ -301,5 +296,4 @@
             if (nd1 != null) {
                 // found node, make it fixed point of the line
-                //System.out.println("node "+nd1);
                 p = nd1.getCoor();
                 line.fixPoint(p);
@@ -325,5 +319,5 @@
         drawing = false;
         highlightedFragmentStart = null;
-        if (!line.isClosed()) setStatusLine(DRAWINGMODE_MESSAGE);
+        if (!line.isClosed()) setStatusLine(DRAWING_MODE_MESSAGE);
         updateCursor();
         repaint();
@@ -380,5 +374,5 @@
             return;
         }
-        if (line.isClosed()) setStatusLine(SIMPLIFYMODE_MESSAGE);
+        if (line.isClosed()) setStatusLine(SIMPLIFY_MODE_MESSAGE);
 
         // do not draw points close to existing points - we do not want self-intersections
@@ -391,5 +385,6 @@
         // free mouse-drawing
         if (nearSomeNode) {
-            if (settings.snapNodes && lastP != null && Math.hypot(e.getX() - lastP.x, e.getY() - lastP.y) > 1e-2) {
+            if (nd1 != null && settings.snapNodes && lastP != null
+                    && Math.hypot((double) e.getX() - lastP.x, (double) e.getY() - lastP.y) > 1e-2) {
                 line.addFixed(nd1.getCoor()); // snap to node coords
                 repaint();
@@ -397,5 +392,5 @@
             }
         } else {
-            if (lastP != null && Math.hypot(e.getX() - lastP.x, e.getY() - lastP.y) > settings.minPixelsBetweenPoints) {
+            if (lastP != null && Math.hypot((double) e.getX() - lastP.x, (double) e.getY() - lastP.y) > settings.minPixelsBetweenPoints) {
                 line.addLast(getLatLon(e)); // add new point
                 repaint();
@@ -426,5 +421,4 @@
             // first Enter = simplify, second = save the way
             if (!line.wasSimplified()) {
-                //line.simplify(eps);
                 switch(settings.simplifyMode) {
                 case 0: //case 1:
@@ -456,5 +450,5 @@
         case KeyEvent.VK_UP:
             if (ctrl || shift || alt) return;
-            // less details
+            // fewer details
             e.consume();
             if (line.wasSimplified()) changeEpsilon(1/settings.epsilonMult);
@@ -483,9 +477,9 @@
                     tr("{0} m - length of the line\n{1} nodes\n{2} points per km (maximum)\n{3} points per km (average)",
                             line.getLength(), line.getPoints().size(), line.getNodesPerKm(settings.pkmBlockSize),
-                            line.getNodesPerKm(1000000)),
+                            line.getNodesPerKm(1_000_000)),
                     tr("Line information"), JOptionPane.INFORMATION_MESSAGE);
             break;
         case KeyEvent.VK_Q:
-            // less details
+            // fewer details
             e.consume();
             new FastDrawConfigDialog(settings).showDialog();
@@ -508,5 +502,4 @@
     @Override
     public void doKeyReleased(KeyEvent keyEvent) {
-        //System.out.println("released "+keyEvent);
         if (keyEvent.getKeyCode() == KeyEvent.VK_SPACE) stopDrawing();
         updateCursor();
@@ -550,5 +543,5 @@
         } else {
             w = new Way(oldWay);
-            w.setNodes(new ArrayList<Node>()); // nodes will be created frosm scratch
+            w.setNodes(new ArrayList<>()); // nodes will be created from scratch
         }
 
@@ -586,6 +579,6 @@
         if (!settings.autoTags.isEmpty()) {
             Map<String, String> tags = TextTagParser.readTagsFromText(settings.autoTags);
-            for (String k: tags.keySet()) {
-                w.put(k, tags.get(k));
+            for (Map.Entry<String, String> entry : tags.entrySet()) {
+                w.put(entry.getKey(), entry.getValue());
             }
         }
@@ -620,5 +613,4 @@
 
     void changeEpsilon(double k) {
-        //System.out.println(tr("Eps={0}", eps));
         eps *= k;
         line.simplify(eps);
@@ -636,21 +628,4 @@
         repaint();
     }
-
-    /*private Node findClosestNode(LatLon p, double d) {
-        Node nn=null;
-        double dist,minD=1e10,x,y;
-        Point pscreen=getPoint(p);   x=pscreen.x; y=pscreen.y;
-        BBox b=new BBox(new LatLon(p.lat()-deltaLatLon,p.lon()-deltaLatLon),
-                new LatLon(p.lat()+deltaLatLon,p.lon()+deltaLatLon));
-        List<Node> nodes = getCurrentDataSet().searchNodes(b);
-        for (Node n: nodes) {
-            dist = Math.sqrt(getPoint(n.getCoor()).distanceSq(x,y));
-            if (dist<d && dist<minD) {
-                nn=n;
-                minD=dist;
-            };
-        }
-        return nn;
-    }*/
 
     private void loadFromWay(Way w) {
@@ -681,5 +656,5 @@
         setStatusLine(tr("Eps={0}, {1} points, {2} p/km",
                 eps, line.getSimplePointsCount(), line.getNodesPerKm(settings.pkmBlockSize))+" "
-                +SIMPLIFYMODE_MESSAGE);
+                + SIMPLIFY_MODE_MESSAGE);
     }
 
@@ -687,12 +662,12 @@
         MapView mapView = MainApplication.getMap().mapView;
         if (shift) mapView.setCursor(cursorShift); else
-            if (line.isClosed() || (nearestPointIndex == 0)) mapView.setCursor(cursorReady); else
-                if (ctrl) mapView.setCursor(cursorCtrl); else
-                    if (nearSomeNode && settings.snapNodes) mapView.setCursor(cursorCtrl); else
-                        if (drawing) mapView.setCursor(cursorDrawing); else
-                            mapView.setCursor(cursorDraw);
-    }
-
-    private void repaint() {
+            if (line.isClosed() || (nearestPointIndex == 0)) mapView.setCursor(cursorReady);
+            else if (ctrl) mapView.setCursor(cursorCtrl);
+            else if (nearSomeNode && settings.snapNodes) mapView.setCursor(cursorCtrl);
+            else if (drawing) mapView.setCursor(cursorDrawing);
+            else mapView.setCursor(cursorDraw);
+    }
+
+    private static void repaint() {
         MainApplication.getMap().mapView.repaint();
     }
@@ -703,5 +678,5 @@
         if (selectedWays != null // if there is a selection
                 && selectedWays.size() == 1 // and one way is selected
-                && line.getPoints().size() == 0) /* and ther is no already drawn line */ {
+                && line.getPoints().isEmpty()) /* and there is no already drawn line */ {
             // we can start drawing new way starting from old one
             Way w = selectedWays.iterator().next();
Index: /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawingPlugin.java
===================================================================
--- /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawingPlugin.java	(revision 36056)
+++ /applications/editors/josm/plugins/FastDraw/src/org/openstreetmap/josm/plugins/fastdraw/FastDrawingPlugin.java	(revision 36057)
@@ -8,4 +8,7 @@
 import org.openstreetmap.josm.plugins.PluginInformation;
 
+/**
+ * The plugin entry point
+ */
 public class FastDrawingPlugin extends Plugin {
 
@@ -17,5 +20,5 @@
     public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
         if (oldFrame == null && newFrame != null) {
-            MainApplication.getMap().addMapMode(new IconToggleButton(new FastDrawingMode(MainApplication.getMap())));
+            MainApplication.getMap().addMapMode(new IconToggleButton(new FastDrawingMode()));
         }
     }
Index: /applications/editors/josm/plugins/FastDraw/test/unit/org/openstreetmap/josm/plugins/fastdraw/DrawnPolyLineTest.java
===================================================================
--- /applications/editors/josm/plugins/FastDraw/test/unit/org/openstreetmap/josm/plugins/fastdraw/DrawnPolyLineTest.java	(revision 36056)
+++ /applications/editors/josm/plugins/FastDraw/test/unit/org/openstreetmap/josm/plugins/fastdraw/DrawnPolyLineTest.java	(revision 36057)
@@ -1,2 +1,3 @@
+// License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.plugins.fastdraw;
 
Index: /applications/editors/josm/plugins/FastDraw/test/unit/org/openstreetmap/josm/plugins/fastdraw/FastDrawingModeTest.java
===================================================================
--- /applications/editors/josm/plugins/FastDraw/test/unit/org/openstreetmap/josm/plugins/fastdraw/FastDrawingModeTest.java	(revision 36057)
+++ /applications/editors/josm/plugins/FastDraw/test/unit/org/openstreetmap/josm/plugins/fastdraw/FastDrawingModeTest.java	(revision 36057)
@@ -0,0 +1,39 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.fastdraw;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
+
+/**
+ * Test class for {@link FastDrawingMode}
+ */
+@Main
+@Projection
+class FastDrawingModeTest {
+    /**
+     * Non-regression test for #21659: IAE: Listener {@link FastDrawingMode} was not registered before or already removed
+     */
+    @Test
+    void testNonRegression21659() {
+        final OsmDataLayer osmDataLayer = new OsmDataLayer(new DataSet(), "testNonRegression21659", null);
+        final FastDrawingMode mode = new FastDrawingMode();
+        assertFalse(mode.isEnabled());
+        MainApplication.getLayerManager().addLayer(osmDataLayer);
+        mode.updateEnabledState();
+        assertTrue(mode.isEnabled());
+        osmDataLayer.lock();
+        mode.updateEnabledState();
+        assertFalse(mode.isEnabled());
+        osmDataLayer.unlock();
+        mode.enterMode();
+        assertDoesNotThrow(mode::exitMode);
+    }
+}
