Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CodeProjectionChoice.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CodeProjectionChoice.java	(revision 13543)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CodeProjectionChoice.java	(revision 13544)
@@ -4,30 +4,16 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.Dimension;
-import java.awt.GridBagLayout;
 import java.awt.event.ActionListener;
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.List;
-import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.table.AbstractTableModel;
 
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.gui.widgets.JosmTextField;
-import org.openstreetmap.josm.tools.GBC;
 
 /**
@@ -44,142 +30,4 @@
     public CodeProjectionChoice() {
         super(tr("By Code (EPSG)"), /* NO-ICON */ "core:code");
-    }
-
-    private static class CodeSelectionPanel extends JPanel implements ListSelectionListener, DocumentListener {
-
-        private final JosmTextField filter = new JosmTextField(30);
-        private final ProjectionCodeModel model = new ProjectionCodeModel();
-        private JTable table;
-        private final List<String> data;
-        private final List<String> filteredData;
-        private static final String DEFAULT_CODE = "EPSG:3857";
-        private String lastCode = DEFAULT_CODE;
-        private final transient ActionListener listener;
-
-        CodeSelectionPanel(String initialCode, ActionListener listener) {
-            this.listener = listener;
-            data = new ArrayList<>(Projections.getAllProjectionCodes());
-            data.sort(new CodeComparator());
-            filteredData = new ArrayList<>(data);
-            build();
-            setCode(initialCode != null ? initialCode : DEFAULT_CODE);
-            table.getSelectionModel().addListSelectionListener(this);
-        }
-
-        /**
-         * List model for the filtered view on the list of all codes.
-         */
-        private class ProjectionCodeModel extends AbstractTableModel {
-            @Override
-            public int getRowCount() {
-                return filteredData.size();
-            }
-
-            @Override
-            public String getValueAt(int index, int column) {
-                if (index >= 0 && index < filteredData.size()) {
-                    String code = filteredData.get(index);
-                    switch (column) {
-                        case 0: return code;
-                        case 1: return Projections.getProjectionByCode(code).toString();
-                        default: break;
-                    }
-                }
-                return null;
-            }
-
-            @Override
-            public int getColumnCount() {
-                return 2;
-            }
-
-            @Override
-            public String getColumnName(int column) {
-                switch (column) {
-                    case 0: return tr("Projection code");
-                    case 1: return tr("Projection name");
-                    default: return super.getColumnName(column);
-                }
-            }
-        }
-
-        private void build() {
-            filter.setColumns(40);
-            filter.getDocument().addDocumentListener(this);
-
-            table = new JTable(model);
-            table.setAutoCreateRowSorter(true);
-            JScrollPane scroll = new JScrollPane(table);
-            scroll.setPreferredSize(new Dimension(200, 214));
-
-            this.setLayout(new GridBagLayout());
-            this.add(filter, GBC.eol().weight(1.0, 0.0));
-            this.add(scroll, GBC.eol().fill(GBC.HORIZONTAL));
-        }
-
-        public String getCode() {
-            int idx = table.getSelectedRow();
-            if (idx == -1)
-                return lastCode;
-            return filteredData.get(table.convertRowIndexToModel(table.getSelectedRow()));
-        }
-
-        public final void setCode(String code) {
-            int idx = filteredData.indexOf(code);
-            if (idx != -1) {
-                selectRow(idx);
-            }
-        }
-
-        private void selectRow(int idx) {
-            table.setRowSelectionInterval(idx, idx);
-            ensureRowIsVisible(idx);
-        }
-
-        private void ensureRowIsVisible(int idx) {
-            table.scrollRectToVisible(table.getCellRect(idx, 0, true));
-        }
-
-        @Override
-        public void valueChanged(ListSelectionEvent e) {
-            listener.actionPerformed(null);
-            lastCode = getCode();
-        }
-
-        @Override
-        public void insertUpdate(DocumentEvent e) {
-            updateFilter();
-        }
-
-        @Override
-        public void removeUpdate(DocumentEvent e) {
-            updateFilter();
-        }
-
-        @Override
-        public void changedUpdate(DocumentEvent e) {
-            updateFilter();
-        }
-
-        private void updateFilter() {
-            filteredData.clear();
-            String filterTxt = filter.getText().trim().toLowerCase(Locale.ENGLISH);
-            for (String code : data) {
-                if (code.toLowerCase(Locale.ENGLISH).contains(filterTxt)
-                 || Projections.getProjectionByCode(code).toString().toLowerCase(Locale.ENGLISH).contains(filterTxt)) {
-                    filteredData.add(code);
-                }
-            }
-            model.fireTableDataChanged();
-            int idx = filteredData.indexOf(lastCode);
-            if (idx == -1) {
-                table.clearSelection();
-                if (table.getModel().getRowCount() > 0) {
-                    ensureRowIsVisible(0);
-                }
-            } else {
-                selectRow(idx);
-            }
-        }
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CodeSelectionPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CodeSelectionPanel.java	(revision 13544)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/projection/CodeSelectionPanel.java	(revision 13544)
@@ -0,0 +1,180 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.projection;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.AbstractTableModel;
+
+import org.openstreetmap.josm.data.projection.Projections;
+import org.openstreetmap.josm.gui.preferences.projection.CodeProjectionChoice.CodeComparator;
+import org.openstreetmap.josm.gui.widgets.JosmTextField;
+import org.openstreetmap.josm.tools.GBC;
+
+/**
+ * Panel allowing to select a projection by code.
+ * @since 13544 (extracted from {@link CodeProjectionChoice})
+ */
+public class CodeSelectionPanel extends JPanel implements ListSelectionListener, DocumentListener {
+
+    private final JosmTextField filter = new JosmTextField(30);
+    private final ProjectionCodeModel model = new ProjectionCodeModel();
+    private JTable table;
+    private final List<String> data;
+    private final List<String> filteredData;
+    private static final String DEFAULT_CODE = "EPSG:3857";
+    private String lastCode = DEFAULT_CODE;
+    private final transient ActionListener listener;
+
+    /**
+     * Constructs a new {@code CodeSelectionPanel}.
+     * @param initialCode projection code initially selected
+     * @param listener listener notified of selection change events
+     */
+    public CodeSelectionPanel(String initialCode, ActionListener listener) {
+        this.listener = listener;
+        data = new ArrayList<>(Projections.getAllProjectionCodes());
+        data.sort(new CodeComparator());
+        filteredData = new ArrayList<>(data);
+        build();
+        setCode(initialCode != null ? initialCode : DEFAULT_CODE);
+        table.getSelectionModel().addListSelectionListener(this);
+    }
+
+    /**
+     * List model for the filtered view on the list of all codes.
+     */
+    private class ProjectionCodeModel extends AbstractTableModel {
+        @Override
+        public int getRowCount() {
+            return filteredData.size();
+        }
+
+        @Override
+        public String getValueAt(int index, int column) {
+            if (index >= 0 && index < filteredData.size()) {
+                String code = filteredData.get(index);
+                switch (column) {
+                    case 0: return code;
+                    case 1: return Projections.getProjectionByCode(code).toString();
+                    default: break;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public int getColumnCount() {
+            return 2;
+        }
+
+        @Override
+        public String getColumnName(int column) {
+            switch (column) {
+                case 0: return tr("Projection code");
+                case 1: return tr("Projection name");
+                default: return super.getColumnName(column);
+            }
+        }
+    }
+
+    private void build() {
+        filter.setColumns(40);
+        filter.getDocument().addDocumentListener(this);
+
+        table = new JTable(model);
+        table.setAutoCreateRowSorter(true);
+        JScrollPane scroll = new JScrollPane(table);
+        scroll.setPreferredSize(new Dimension(200, 214));
+
+        this.setLayout(new GridBagLayout());
+        this.add(filter, GBC.eol().weight(1.0, 0.0));
+        this.add(scroll, GBC.eol().fill(GBC.HORIZONTAL));
+    }
+
+    /**
+     * Returns selected projection code.
+     * @return selected projection code
+     */
+    public String getCode() {
+        int idx = table.getSelectedRow();
+        if (idx == -1)
+            return lastCode;
+        return filteredData.get(table.convertRowIndexToModel(table.getSelectedRow()));
+    }
+
+    /**
+     * Sets selected projection code.
+     * @param code projection code to select
+     */
+    public final void setCode(String code) {
+        int idx = filteredData.indexOf(code);
+        if (idx != -1) {
+            selectRow(idx);
+        }
+    }
+
+    private void selectRow(int idx) {
+        table.setRowSelectionInterval(idx, idx);
+        ensureRowIsVisible(idx);
+    }
+
+    private void ensureRowIsVisible(int idx) {
+        table.scrollRectToVisible(table.getCellRect(idx, 0, true));
+    }
+
+    @Override
+    public void valueChanged(ListSelectionEvent e) {
+        listener.actionPerformed(null);
+        lastCode = getCode();
+    }
+
+    @Override
+    public void insertUpdate(DocumentEvent e) {
+        updateFilter();
+    }
+
+    @Override
+    public void removeUpdate(DocumentEvent e) {
+        updateFilter();
+    }
+
+    @Override
+    public void changedUpdate(DocumentEvent e) {
+        updateFilter();
+    }
+
+    private void updateFilter() {
+        filteredData.clear();
+        String filterTxt = filter.getText().trim().toLowerCase(Locale.ENGLISH);
+        for (String code : data) {
+            if (code.toLowerCase(Locale.ENGLISH).contains(filterTxt)
+             || Projections.getProjectionByCode(code).toString().toLowerCase(Locale.ENGLISH).contains(filterTxt)) {
+                filteredData.add(code);
+            }
+        }
+        model.fireTableDataChanged();
+        int idx = filteredData.indexOf(lastCode);
+        if (idx == -1) {
+            table.clearSelection();
+            if (table.getModel().getRowCount() > 0) {
+                ensureRowIsVisible(0);
+            }
+        } else {
+            selectRow(idx);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/I18n.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/I18n.java	(revision 13543)
+++ /trunk/src/org/openstreetmap/josm/tools/I18n.java	(revision 13544)
@@ -379,5 +379,5 @@
             ZipFile zipFile = new ZipFile(source, StandardCharsets.UTF_8);
             InputStream orig = zipFile.getInputStream(enfile);
-            InputStream trans = zipFile.getInputStream(langfile);
+            InputStream trans = zipFile.getInputStream(langfile)
         ) {
             if (orig != null && trans != null)
Index: /trunk/src/org/openstreetmap/josm/tools/TextAnalyzer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/TextAnalyzer.java	(revision 13544)
+++ /trunk/src/org/openstreetmap/josm/tools/TextAnalyzer.java	(revision 13544)
@@ -0,0 +1,135 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A helper class that analyzes the text and attempts to parse tags from it
+ * @since 13544 (extracted from {@link TextTagParser})
+ */
+public class TextAnalyzer {
+    private boolean quotesStarted;
+    private boolean esc;
+    private final StringBuilder s = new StringBuilder(200);
+    private String valueStops = "\n\r\t";
+    private int pos;
+    private final String data;
+    private final int n;
+
+    /**
+     * Create a new {@link TextAnalyzer}
+     * @param text The text to parse
+     */
+    public TextAnalyzer(String text) {
+        pos = 0;
+        data = Utils.strip(text);
+        n = data.length();
+        // fix #1604: allow space characters as value stops for single-line input only
+        if (data.indexOf('\r') == -1 && data.indexOf('\n') == -1) {
+            valueStops += " ";
+        }
+    }
+
+    /**
+     * Read tags from "Free format"
+     * @return map of tags
+     */
+    public Map<String, String> getFreeParsedTags() {
+        String k, v;
+        Map<String, String> tags = new HashMap<>();
+
+        while (true) {
+            skipEmpty();
+            if (pos == n) {
+                break;
+            }
+            k = parseString("\n\r\t= ");
+            if (pos == n) {
+                tags.clear();
+                break;
+            }
+            skipSign();
+            if (pos == n) {
+                tags.clear();
+                break;
+            }
+            v = parseString(valueStops);
+            tags.put(k, v);
+        }
+        return tags;
+    }
+
+    /**
+     * Parses current text to extract a key or value depending on given stop characters.
+     * @param stopChars Parsing will stop when one character of this string is found
+     * @return key or value extracted from current text
+     */
+    public String parseString(String stopChars) {
+        char[] stop = stopChars.toCharArray();
+        Arrays.sort(stop);
+        char c;
+        while (pos < n) {
+            c = data.charAt(pos);
+            if (esc) {
+                esc = false;
+                s.append(c); //  \" \\
+            } else if (c == '\\') {
+                esc = true;
+            } else if (c == '\"' && !quotesStarted) { // opening "
+                if (!s.toString().trim().isEmpty()) { // we had   ||some text"||
+                    s.append(c); // just add ", not open
+                } else {
+                    s.delete(0, s.length()); // forget that empty characthers and start reading "....
+                    quotesStarted = true;
+                }
+            } else if (c == '\"' && quotesStarted) {  // closing "
+                quotesStarted = false;
+                pos++;
+                break;
+            } else if (!quotesStarted && (Arrays.binarySearch(stop, c) >= 0)) {
+                // stop-symbol found
+                pos++;
+                break;
+            } else {
+                // skip non-printable characters
+                if (c >= 32) s.append(c);
+            }
+            pos++;
+        }
+
+        String res = s.toString();
+        s.delete(0, s.length());
+        return res.trim();
+    }
+
+    private void skipSign() {
+        char c;
+        boolean signFound = false;
+        while (pos < n) {
+            c = data.charAt(pos);
+            if (c == '\t' || c == '\n' || c == ' ') {
+                pos++;
+            } else if (c == '=') {
+                if (signFound) break; // a  =  =qwerty means "a"="=qwerty"
+                signFound = true;
+                pos++;
+            } else {
+                break;
+            }
+        }
+    }
+
+    private void skipEmpty() {
+        char c;
+        while (pos < n) {
+            c = data.charAt(pos);
+            if (c == '\t' || c == '\n' || c == '\r' || c == ' ') {
+                pos++;
+            } else {
+                break;
+            }
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/TextTagParser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/TextTagParser.java	(revision 13543)
+++ /trunk/src/org/openstreetmap/josm/tools/TextTagParser.java	(revision 13544)
@@ -5,5 +5,4 @@
 import static org.openstreetmap.josm.tools.I18n.trn;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
@@ -27,127 +26,4 @@
     private TextTagParser() {
         // Hide default constructor for utils classes
-    }
-
-    /**
-     * A helper class that analyzes the text and attempts to parse tags from it
-     */
-    public static class TextAnalyzer {
-        private boolean quotesStarted;
-        private boolean esc;
-        private final StringBuilder s = new StringBuilder(200);
-        private String valueStops = "\n\r\t";
-        private int pos;
-        private final String data;
-        private final int n;
-
-        /**
-         * Create a new {@link TextAnalyzer}
-         * @param text The text to parse
-         */
-        public TextAnalyzer(String text) {
-            pos = 0;
-            data = Utils.strip(text);
-            n = data.length();
-            // fix #1604: allow space characters as value stops for single-line input only
-            if (data.indexOf('\r') == -1 && data.indexOf('\n') == -1) {
-                valueStops += " ";
-            }
-        }
-
-        /**
-         * Read tags from "Free format"
-         * @return map of tags
-         */
-        private Map<String, String> getFreeParsedTags() {
-            String k, v;
-            Map<String, String> tags = new HashMap<>();
-
-            while (true) {
-                skipEmpty();
-                if (pos == n) {
-                    break;
-                }
-                k = parseString("\n\r\t= ");
-                if (pos == n) {
-                    tags.clear();
-                    break;
-                }
-                skipSign();
-                if (pos == n) {
-                    tags.clear();
-                    break;
-                }
-                v = parseString(valueStops);
-                tags.put(k, v);
-            }
-            return tags;
-        }
-
-        private String parseString(String stopChars) {
-            char[] stop = stopChars.toCharArray();
-            Arrays.sort(stop);
-            char c;
-            while (pos < n) {
-                c = data.charAt(pos);
-                if (esc) {
-                    esc = false;
-                    s.append(c); //  \" \\
-                } else if (c == '\\') {
-                    esc = true;
-                } else if (c == '\"' && !quotesStarted) { // opening "
-                    if (!s.toString().trim().isEmpty()) { // we had   ||some text"||
-                        s.append(c); // just add ", not open
-                    } else {
-                        s.delete(0, s.length()); // forget that empty characthers and start reading "....
-                        quotesStarted = true;
-                    }
-                } else if (c == '\"' && quotesStarted) {  // closing "
-                    quotesStarted = false;
-                    pos++;
-                    break;
-                } else if (!quotesStarted && (Arrays.binarySearch(stop, c) >= 0)) {
-                    // stop-symbol found
-                    pos++;
-                    break;
-                } else {
-                    // skip non-printable characters
-                    if (c >= 32) s.append(c);
-                }
-                pos++;
-            }
-
-            String res = s.toString();
-            s.delete(0, s.length());
-            return res.trim();
-        }
-
-        private void skipSign() {
-            char c;
-            boolean signFound = false;
-            while (pos < n) {
-                c = data.charAt(pos);
-                if (c == '\t' || c == '\n' || c == ' ') {
-                    pos++;
-                } else if (c == '=') {
-                    if (signFound) break; // a  =  =qwerty means "a"="=qwerty"
-                    signFound = true;
-                    pos++;
-                } else {
-                    break;
-                }
-            }
-        }
-
-        private void skipEmpty() {
-            char c;
-            while (pos < n) {
-                c = data.charAt(pos);
-                if (c == '\t' || c == '\n' || c == '\r' || c == ' ') {
-                    pos++;
-                } else {
-                    break;
-                }
-            }
-        }
     }
 
