001package jmri.jmrix;
002
003import java.awt.Color;
004import java.awt.Component;
005import java.awt.GridBagConstraints;
006import java.awt.Insets;
007import java.awt.event.ActionEvent;
008import java.awt.event.ActionListener;
009import java.awt.event.FocusEvent;
010import java.awt.event.FocusListener;
011import java.awt.event.ItemEvent;
012import java.awt.event.KeyEvent;
013import java.awt.event.KeyListener;
014import java.util.Collections;
015import java.util.Enumeration;
016import java.util.Map;
017import java.util.ResourceBundle;
018import java.util.Vector;
019import javax.swing.JButton;
020import javax.swing.JComboBox;
021import javax.swing.JComponent;
022import javax.swing.JLabel;
023import javax.swing.JList;
024import javax.swing.JOptionPane;
025import javax.swing.JPanel;
026import javax.swing.JSpinner;
027import javax.swing.JTextField;
028import javax.swing.ListCellRenderer;
029import javax.swing.SpinnerNumberModel;
030import javax.swing.event.ChangeEvent;
031import javax.swing.event.ChangeListener;
032import jmri.InstanceManager;
033import jmri.util.PortNameMapper;
034import jmri.util.PortNameMapper.SerialPortFriendlyName;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037import purejavacomm.CommPortIdentifier;
038
039/**
040 * Abstract base class for common implementation of the SerialConnectionConfig.
041 *
042 * @author Bob Jacobsen Copyright (C) 2001, 2003
043 */
044abstract public class AbstractSerialConnectionConfig extends AbstractConnectionConfig {
045
046    /**
047     * Ctor for an object being created during load process.
048     *
049     * @param p port being configured
050     */
051    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "Thought to be safe as default connection config")
052    public AbstractSerialConnectionConfig(jmri.jmrix.PortAdapter p) {
053        this((jmri.jmrix.SerialPortAdapter) p);
054    }
055
056    public AbstractSerialConnectionConfig(jmri.jmrix.SerialPortAdapter p) {
057        adapter = p;
058    }
059
060    /**
061     * Ctor for a functional object with no preexisting adapter. Expect that the
062     * subclass setInstance() will fill the adapter member.
063     */
064    public AbstractSerialConnectionConfig() {
065        adapter = null;
066
067    }
068
069    @Override
070    public jmri.jmrix.SerialPortAdapter getAdapter() {
071        return adapter;
072    }
073
074    protected boolean init = false;
075
076    /**
077     * {@inheritDoc}
078     */
079    @Override
080    protected void checkInitDone() {
081        log.debug("init called for {}", name());
082        if (init) {
083            return;
084        }
085
086        baudBox.addActionListener(new ActionListener() {
087            @Override
088            public void actionPerformed(ActionEvent e) {
089                adapter.configureBaudRate((String) baudBox.getSelectedItem());
090                p.setComboBoxLastSelection(adapter.getClass().getName() + ".baud", (String) baudBox.getSelectedItem()); // NOI18N
091            }
092        });
093
094        if (adapter.getSystemConnectionMemo() != null) {
095            systemPrefixField.addActionListener(new ActionListener() {
096                @Override
097                public void actionPerformed(ActionEvent e) {
098                    if (!adapter.getSystemConnectionMemo().setSystemPrefix(systemPrefixField.getText())) {
099                        JOptionPane.showMessageDialog(null, Bundle.getMessage("ConnectionPrefixDialog", systemPrefixField.getText()));
100                        systemPrefixField.setValue(adapter.getSystemConnectionMemo().getSystemPrefix());
101                    }
102                }
103            });
104            systemPrefixField.addFocusListener(new FocusListener() {
105                @Override
106                public void focusLost(FocusEvent e) {
107                    if (!adapter.getSystemConnectionMemo().setSystemPrefix(systemPrefixField.getText())) {
108                        JOptionPane.showMessageDialog(null, Bundle.getMessage("ConnectionPrefixDialog", systemPrefixField.getText()));
109                        systemPrefixField.setValue(adapter.getSystemConnectionMemo().getSystemPrefix());
110                    }
111                }
112
113                @Override
114                public void focusGained(FocusEvent e) {
115                }
116            });
117            connectionNameField.addActionListener(new ActionListener() {
118                @Override
119                public void actionPerformed(ActionEvent e) {
120                    if (!adapter.getSystemConnectionMemo().setUserName(connectionNameField.getText())) {
121                        JOptionPane.showMessageDialog(null, Bundle.getMessage("ConnectionNameDialog", connectionNameField.getText()));
122                        connectionNameField.setText(adapter.getSystemConnectionMemo().getUserName());
123                    }
124                }
125            });
126            connectionNameField.addFocusListener(new FocusListener() {
127                @Override
128                public void focusLost(FocusEvent e) {
129                    if (!adapter.getSystemConnectionMemo().setUserName(connectionNameField.getText())) {
130                        JOptionPane.showMessageDialog(null, Bundle.getMessage("ConnectionNameDialog", connectionNameField.getText()));
131                        connectionNameField.setText(adapter.getSystemConnectionMemo().getUserName());
132                    }
133                }
134
135                @Override
136                public void focusGained(FocusEvent e) {
137                }
138            });
139        }
140
141        portBox.addFocusListener(new FocusListener() {
142            @Override
143            public void focusGained(FocusEvent e) {
144                refreshPortBox();
145            }
146
147            @Override
148            public void focusLost(FocusEvent e) {
149            }
150        });
151
152        // set/change delay interval between (actually before) output (Turnout) commands
153        outputIntervalSpinner.addChangeListener(new ChangeListener() {
154            @Override
155            public void stateChanged(ChangeEvent e) {
156                adapter.getSystemConnectionMemo().setOutputInterval((Integer) outputIntervalSpinner.getValue());
157            }
158        });
159
160        for (Map.Entry<String, Option> entry : options.entrySet()) {
161            final String item = entry.getKey();
162            if (entry.getValue().getComponent() instanceof JComboBox) {
163                ((JComboBox<?>) entry.getValue().getComponent()).addActionListener((ActionEvent e) -> {
164                    adapter.setOptionState(item, options.get(item).getItem());
165                });
166            }
167        }
168
169        init = true;
170    }
171
172    @Override
173    public void updateAdapter() {
174        log.debug("updateAdapter() to {}", systemPrefixField.getText());
175        adapter.setPort(PortNameMapper.getPortFromName((String) portBox.getSelectedItem()));
176        adapter.configureBaudRateFromIndex(baudBox.getSelectedIndex()); // manage by index, not item value
177        for (Map.Entry<String, Option> entry : options.entrySet()) {
178            adapter.setOptionState(entry.getKey(), entry.getValue().getItem());
179        }
180
181        if (adapter.getSystemConnectionMemo() != null && !adapter.getSystemConnectionMemo().setSystemPrefix(systemPrefixField.getText())) {
182            systemPrefixField.setValue(adapter.getSystemConnectionMemo().getSystemPrefix());
183            connectionNameField.setText(adapter.getSystemConnectionMemo().getUserName());
184        }
185    }
186
187    jmri.UserPreferencesManager p = jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class);
188    protected JComboBox<String> portBox = new JComboBox<>();
189    protected JLabel portBoxLabel;
190    protected JComboBox<String> baudBox = new JComboBox<>();
191    protected JLabel baudBoxLabel;
192    protected String[] baudList;
193
194    private SpinnerNumberModel intervalSpinner = new SpinnerNumberModel(250, 0, 10000, 1); // 10 sec max seems long enough
195    // the following items are protected so they can be hidden when not applicable from a specific ConnectionConfig (ie. Simulator) implementation
196    protected JSpinner outputIntervalSpinner = new JSpinner(intervalSpinner);
197    protected JLabel outputIntervalLabel;
198    protected JButton outputIntervalReset = new JButton(Bundle.getMessage("ButtonReset"));
199
200    protected jmri.jmrix.SerialPortAdapter adapter = null;
201
202    /**
203     * {@inheritDoc}
204     */
205    @Override
206    abstract protected void setInstance();
207
208    @Override
209    public String getInfo() {
210        String t = (String) portBox.getSelectedItem();
211        if (t != null) {
212            return PortNameMapper.getPortFromName(t);
213            //return t;
214        } else if ((adapter != null) && (adapter.getCurrentPortName() != null)) {
215            return adapter.getCurrentPortName();
216        }
217
218        return JmrixConfigPane.NONE;
219    }
220
221    @SuppressWarnings("UseOfObsoleteCollectionType")
222    Vector<String> v;
223    @SuppressWarnings("UseOfObsoleteCollectionType")
224    Vector<String> originalList;
225    String invalidPort = null;
226
227    @SuppressWarnings("UseOfObsoleteCollectionType")
228    public void refreshPortBox() {
229        if (!init) {
230            v = getPortNames();
231            portBox.setRenderer(new ComboBoxRenderer());
232            // Add this line to ensure that the combo box header isn't made too narrow
233            portBox.setPrototypeDisplayValue("A fairly long port name of 40 characters"); //NO18N
234        } else {
235            Vector<String> v2 = getPortNames();
236            if (v2.equals(originalList)) {
237                log.debug("List of valid Ports has not changed, therefore we will not refresh the port list");
238                // but we will insist on setting the current value into the port
239                adapter.setPort(PortNameMapper.getPortFromName((String) portBox.getSelectedItem()));
240                return;
241            }
242            log.debug("List of valid Ports has been changed, therefore we will refresh the port list");
243            v = new Vector<>();
244            v.setSize(v2.size());
245            Collections.copy(v, v2);
246        }
247
248        if (v == null) {
249            log.error("port name Vector v is null!");
250            return;
251        }
252
253        /* as we make amendments to the list of port in vector v, we keep a copy of it before
254         modification, this copy is then used to validate against any changes in the port lists.
255         */
256        originalList = new Vector<>();
257        originalList.setSize(v.size());
258        Collections.copy(originalList, v);
259        if (portBox.getActionListeners().length > 0) {
260            portBox.removeActionListener(portBox.getActionListeners()[0]);
261        }
262        portBox.removeAllItems();
263        log.debug("getting fresh list of available Serial Ports");
264
265        if (v.isEmpty()) {
266            v.add(0, Bundle.getMessage("noPortsFound"));
267        }
268        String portName = adapter.getCurrentPortName();
269        if (portName != null && !portName.equals(Bundle.getMessage("noneSelected")) && !portName.equals(Bundle.getMessage("noPortsFound"))) {
270            if (!v.contains(portName)) {
271                v.add(0, portName);
272                invalidPort = portName;
273                portBox.setForeground(Color.red);
274            } else if (invalidPort != null && invalidPort.equals(portName)) {
275                invalidPort = null;
276            }
277        } else {
278            if (!v.contains(portName)) {
279                v.add(0, Bundle.getMessage("noneSelected"));
280            } else if (p.getComboBoxLastSelection(adapter.getClass().getName() + ".port") == null) {
281                v.add(0, Bundle.getMessage("noneSelected"));
282            }
283        }
284        updateSerialPortNames(portName, portBox, v);
285
286        // If there's no name selected, select one that seems most likely
287        boolean didSetName = false;
288        if (portName == null || portName.equals(Bundle.getMessage("noneSelected")) || portName.equals(Bundle.getMessage("noPortsFound"))) {
289            for (int i = 0; i < portBox.getItemCount(); i++) {
290                for (String friendlyName : getPortFriendlyNames()) {
291                    if ((portBox.getItemAt(i)).contains(friendlyName)) {
292                        portBox.setSelectedIndex(i);
293                        adapter.setPort(PortNameMapper.getPortFromName(portBox.getItemAt(i)));
294                        didSetName = true;
295                        break;
296                    }
297                }
298            }
299            // if didn't set name, don't leave it hanging
300            if (!didSetName) {
301                portBox.setSelectedIndex(0);
302            }
303        }
304        // finally, insist on synchronization of selected port name with underlying port
305        adapter.setPort(PortNameMapper.getPortFromName((String) portBox.getSelectedItem()));
306
307        // add a listener for later changes
308        portBox.addActionListener((ActionEvent e) -> {
309            String port = PortNameMapper.getPortFromName((String) portBox.getSelectedItem());
310            adapter.setPort(port);
311        });
312    }
313
314    String value;
315
316    /**
317     * {@inheritDoc}
318     */
319    @Override
320    @SuppressWarnings("UseOfObsoleteCollectionType")
321    public void loadDetails(final JPanel details) {
322        _details = details;
323        setInstance();
324        if (!init) {
325            //Build up list of options
326            String[] optionsAvailable = adapter.getOptions();
327            options.clear();
328            for (String i : optionsAvailable) {
329                JComboBox<String> opt = new JComboBox<>(adapter.getOptionChoices(i));
330                opt.setSelectedItem(adapter.getOptionState(i));
331                // check that it worked
332                if (!adapter.getOptionState(i).equals(opt.getSelectedItem())) {
333                    // no, set 1st option choice
334                    opt.setSelectedIndex(0);
335                    // log before setting new value to show old value
336                    log.warn("Loading found invalid value for option {}, found \"{}\", setting to \"{}\"", i, adapter.getOptionState(i), opt.getSelectedItem());
337                    adapter.setOptionState(i, (String) opt.getSelectedItem());
338                }
339                options.put(i, new Option(adapter.getOptionDisplayName(i), opt, adapter.isOptionAdvanced(i)));
340            }
341        }
342
343        try {
344            v = getPortNames();
345            if (log.isDebugEnabled()) {
346                log.debug("loadDetails called in class {}", this.getClass().getName());
347                log.debug("adapter class: {}", adapter.getClass().getName());
348                log.debug("loadDetails called for {}", name());
349                if (v != null) {
350                    log.debug("Found {} ports", v.size());
351                } else {
352                    log.debug("Zero-length port vector");
353                }
354            }
355        } catch (java.lang.UnsatisfiedLinkError e1) {
356            log.error("UnsatisfiedLinkError - the serial library has not been installed properly");
357            log.error("java.library.path={}", System.getProperty("java.library.path", "<unknown>"));
358            javax.swing.JOptionPane.showMessageDialog(null, "Failed to load comm library.\nYou have to fix that before setting preferences.");
359            return;
360        }
361
362        if (adapter.getSystemConnectionMemo() != null) {
363            systemPrefixField.setValue(adapter.getSystemConnectionMemo().getSystemPrefix());
364            connectionNameField.setText(adapter.getSystemConnectionMemo().getUserName());
365            NUMOPTIONS = NUMOPTIONS + 2;
366        }
367
368        refreshPortBox();
369
370        baudList = adapter.validBaudRates(); // when not supported should not return null, but an empty String[] {}
371        // need to remove ActionListener before addItem() or action event will occur
372        if (baudBox.getActionListeners().length > 0) {
373            baudBox.removeActionListener(baudBox.getActionListeners()[0]);
374        }
375        // rebuild baudBox combo list
376        baudBox.removeAllItems();
377        if (log.isDebugEnabled()) {
378            log.debug("after remove, {} items, first is {}", baudBox.getItemCount(),
379                    baudBox.getItemAt(0));
380        }
381
382        // empty array means: baud not supported by adapter (but extends serialConnConfig)
383        if (baudList.length == 0) {
384            log.debug("empty array received from adapter");
385        }
386        for (String baudList1 : baudList) {
387            baudBox.addItem(baudList1);
388        }
389        if (log.isDebugEnabled()) {
390            log.debug("after reload, {} items, first is {}", baudBox.getItemCount(),
391                    baudBox.getItemAt(0));
392        }
393
394        if (baudList.length > 1) {
395            baudBox.setToolTipText(Bundle.getMessage("TipBaudRateMatch"));
396            baudBox.setEnabled(true);
397        } else {
398            baudBox.setToolTipText(Bundle.getMessage("TipBaudRateFixed"));
399            baudBox.setEnabled(false);
400        }
401
402        NUMOPTIONS = NUMOPTIONS + options.size();
403
404        portBoxLabel = new JLabel(Bundle.getMessage("SerialPortLabel"));
405        baudBoxLabel = new JLabel(Bundle.getMessage("BaudRateLabel"));
406        if (baudBox.getItemCount() > 0) { // skip when adapter returned an empty array (= spotbug's preference)
407            baudBox.setSelectedIndex(adapter.getCurrentBaudIndex());
408        }
409        // connection (memo) specific output command delay option, calls jmri.jmrix.SystemConnectionMemo#setOutputInterval(int)
410        outputIntervalLabel = new JLabel(Bundle.getMessage("OutputIntervalLabel"));
411        outputIntervalSpinner.setToolTipText(Bundle.getMessage("OutputIntervalTooltip",
412                adapter.getSystemConnectionMemo().getDefaultOutputInterval(),adapter.getManufacturer()));
413        JTextField field = ((JSpinner.DefaultEditor) outputIntervalSpinner.getEditor()).getTextField();
414        field.setColumns(6);
415        outputIntervalSpinner.setMaximumSize(outputIntervalSpinner.getPreferredSize()); // set spinner JTextField width
416        outputIntervalSpinner.setValue(adapter.getSystemConnectionMemo().getOutputInterval());
417        outputIntervalSpinner.setEnabled(true);
418        outputIntervalReset.addActionListener((ActionEvent event) -> {
419            outputIntervalSpinner.setValue(adapter.getSystemConnectionMemo().getDefaultOutputInterval());
420            adapter.getSystemConnectionMemo().setOutputInterval(adapter.getSystemConnectionMemo().getDefaultOutputInterval());
421        });
422
423        showAdvanced.setFont(showAdvanced.getFont().deriveFont(9f));
424        showAdvanced.setForeground(Color.blue);
425        showAdvanced.addItemListener((ItemEvent e) -> {
426            showAdvancedItems();
427        });
428        showAdvancedItems();
429        init = false;       // need to reload action listeners
430        checkInitDone();
431    }
432
433    @Override
434    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE",
435        justification = "Type is checked before casting")
436    protected void showAdvancedItems() {
437        _details.removeAll();
438        cL.anchor = GridBagConstraints.WEST;
439        cL.insets = new Insets(2, 5, 0, 5);
440        cR.insets = new Insets(2, 0, 0, 5);
441        cR.anchor = GridBagConstraints.WEST;
442        cR.gridx = 1;
443        cL.gridx = 0;
444        int i = 0;
445        int stdrows = 0;
446        boolean incAdvancedOptions = true;
447        if (!isBaudAdvanced()) {
448            stdrows++;
449        }
450        if (!isPortAdvanced()) {
451            stdrows++;
452        }
453        for (Map.Entry<String, Option> entry : options.entrySet()) {
454            if (!entry.getValue().isAdvanced()) {
455                stdrows++;
456            }
457        }
458
459        if (adapter.getSystemConnectionMemo() != null) {
460            stdrows = stdrows + 2;
461        }
462        if (stdrows == NUMOPTIONS) {
463            incAdvancedOptions = false;
464        }
465        _details.setLayout(gbLayout);
466        i = addStandardDetails(incAdvancedOptions, i);
467        if (showAdvanced.isSelected()) {
468
469            if (isPortAdvanced()) {
470                cR.gridy = i;
471                cL.gridy = i;
472                gbLayout.setConstraints(portBoxLabel, cL);
473                gbLayout.setConstraints(portBox, cR);
474
475                _details.add(portBoxLabel);
476                _details.add(portBox);
477                i++;
478            }
479
480            if (isBaudAdvanced()) {
481                cR.gridy = i;
482                cL.gridy = i;
483                gbLayout.setConstraints(baudBoxLabel, cL);
484                gbLayout.setConstraints(baudBox, cR);
485                _details.add(baudBoxLabel);
486                _details.add(baudBox);
487                i++;
488            }
489
490            for (Map.Entry<String, Option> entry : options.entrySet()) {
491                if (entry.getValue().isAdvanced()) {
492                    cR.gridy = i;
493                    cL.gridy = i;
494                    gbLayout.setConstraints(entry.getValue().getLabel(), cL);
495                    gbLayout.setConstraints(entry.getValue().getComponent(), cR);
496                    _details.add(entry.getValue().getLabel());
497                    _details.add(entry.getValue().getComponent());
498                    i++;
499                }
500            }
501
502            // interval config field
503            cR.gridy = i;
504            cL.gridy = i;
505            gbLayout.setConstraints(outputIntervalLabel, cL);
506            _details.add(outputIntervalLabel);
507            JPanel intervalPanel = new JPanel();
508            gbLayout.setConstraints(intervalPanel, cR);
509            intervalPanel.add(outputIntervalSpinner);
510            intervalPanel.add(outputIntervalReset);
511            _details.add(intervalPanel);
512            i++;
513
514        }
515        cL.gridwidth = 2;
516        for (JComponent item : additionalItems) {
517            cL.gridy = i;
518            gbLayout.setConstraints(item, cL);
519            _details.add(item);
520            i++;
521        }
522        cL.gridwidth = 1;
523
524        if (_details.getParent() != null && _details.getParent() instanceof javax.swing.JViewport) {
525            javax.swing.JViewport vp = (javax.swing.JViewport) _details.getParent();
526            vp.revalidate();
527            vp.repaint();
528        }
529    }
530
531    protected int addStandardDetails(boolean incAdvanced, int i) {
532        if (!isPortAdvanced()) {
533            cR.gridy = i;
534            cL.gridy = i;
535            gbLayout.setConstraints(portBoxLabel, cL);
536            gbLayout.setConstraints(portBox, cR);
537            _details.add(portBoxLabel);
538            _details.add(portBox);
539            i++;
540        }
541
542        if (!isBaudAdvanced()) {
543            cR.gridy = i;
544            cL.gridy = i;
545            gbLayout.setConstraints(baudBoxLabel, cL);
546            gbLayout.setConstraints(baudBox, cR);
547            _details.add(baudBoxLabel);
548            _details.add(baudBox);
549            i++;
550        }
551
552        return addStandardDetails(adapter, incAdvanced, i);
553    }
554
555    public boolean isPortAdvanced() {
556        return false;
557    }
558
559    public boolean isBaudAdvanced() {
560        return true;
561    }
562
563    @Override
564    public String getManufacturer() {
565        return adapter.getManufacturer();
566    }
567
568    @Override
569    public void setManufacturer(String manufacturer) {
570        setInstance();
571        adapter.setManufacturer(manufacturer);
572    }
573
574    @Override
575    public boolean getDisabled() {
576        if (adapter == null) {
577            return true;
578        }
579        return adapter.getDisabled();
580    }
581
582    @Override
583    public void setDisabled(boolean disabled) {
584        if (adapter != null) {
585            adapter.setDisabled(disabled);
586        }
587    }
588
589    @Override
590    public String getConnectionName() {
591        if ((adapter != null) && (adapter.getSystemConnectionMemo() != null)) {
592            return adapter.getSystemConnectionMemo().getUserName();
593        } else {
594            return name();
595        }
596    }
597
598    @Override
599    public void dispose() {
600        super.dispose();
601        if (adapter != null) {
602            adapter.dispose();
603            adapter = null;
604        }
605    }
606
607    class ComboBoxRenderer extends JLabel
608            implements ListCellRenderer<String> {
609
610        public ComboBoxRenderer() {
611            setHorizontalAlignment(LEFT);
612            setVerticalAlignment(CENTER);
613        }
614
615        /*
616         * This method finds the image and text corresponding
617         * to the selected value and returns the label, set up
618         * to display the text and image.
619         */
620        @Override
621        public Component getListCellRendererComponent(
622                JList<? extends String> list,
623                String name,
624                int index,
625                boolean isSelected,
626                boolean cellHasFocus) {
627
628            setOpaque(index > -1);
629            setForeground(Color.black);
630            list.setSelectionForeground(Color.black);
631            if (isSelected && index > -1) {
632                setBackground(list.getSelectionBackground());
633            } else {
634                setBackground(list.getBackground());
635            }
636            if (invalidPort != null) {
637                String port = PortNameMapper.getPortFromName(name);
638                if (port.equals(invalidPort)) {
639                    list.setSelectionForeground(Color.red);
640                    setForeground(Color.red);
641                }
642            }
643
644            setText(name);
645
646            return this;
647        }
648    }
649
650    /**
651     * Handle friendly port names. Note that this
652     * changes the selection in portCombo, so
653     * that should be tracked after this returns.
654     *
655     * @param portName The currently-selected port name
656     * @param portCombo The combo box that's displaying the available ports
657     * @param portList The list of valid (unfriendly) port names
658     */
659    @SuppressWarnings("UseOfObsoleteCollectionType")
660    protected synchronized static void updateSerialPortNames(String portName, JComboBox<String> portCombo, Vector<String> portList) {
661        for (Map.Entry<String, SerialPortFriendlyName> en : PortNameMapper.getPortNameMap().entrySet()) {
662            en.getValue().setValidPort(false);
663        }
664        for (int i = 0; i < portList.size(); i++) {
665            String commPort = portList.elementAt(i);
666            SerialPortFriendlyName port = PortNameMapper.getPortNameMap().get(commPort);
667            if (port == null) {
668                port = new SerialPortFriendlyName(commPort, null);
669                PortNameMapper.getPortNameMap().put(commPort, port);
670            }
671            port.setValidPort(true);
672            portCombo.addItem(port.getDisplayName());
673            if (commPort.equals(portName)) {
674                portCombo.setSelectedIndex(i);
675            }
676        }
677    }
678
679    @SuppressWarnings("UseOfObsoleteCollectionType")
680    protected Vector<String> getPortNames() {
681        //reloadDriver(); // Refresh the list of communication ports
682        // first, check that the comm package can be opened and ports seen
683        Vector<String> portNameVector = new Vector<>();
684        Enumeration<CommPortIdentifier> portIDs = CommPortIdentifier.getPortIdentifiers();
685        // find the names of suitable ports
686        while (portIDs.hasMoreElements()) {
687            CommPortIdentifier id = portIDs.nextElement();
688            // filter out line printers
689            if (id.getPortType() != CommPortIdentifier.PORT_PARALLEL) // accumulate the names in a vector
690            {
691                portNameVector.addElement(id.getName());
692            }
693        }
694        return portNameVector;
695    }
696
697    /**
698     * This provides a method to return potentially meaningful names that are
699     * used in OS to help identify ports against Hardware.
700     *
701     * @return array of friendly port names
702     */
703    protected String[] getPortFriendlyNames() {
704        return new String[]{};
705    }
706
707    /**
708     * This is purely here for systems that do not implement the
709     * SystemConnectionMemo and can be removed once they have been migrated.
710     *
711     * @return Resource bundle for action model
712     */
713    protected ResourceBundle getActionModelResourceBundle() {
714        return null;
715    }
716
717    /**
718     *
719     * @deprecated since 4.19.7 without direct replacement
720     */
721    @Deprecated
722    protected final void addToActionList() {
723        // nothing to do
724    }
725
726    /**
727     *
728     * @deprecated since 4.19.7 without direct replacement
729     */
730    @Deprecated
731    protected void removeFromActionList() {
732        // nothing to do
733    }
734
735    private final static Logger log = LoggerFactory.getLogger(AbstractSerialConnectionConfig.class);
736
737}