001package jmri.jmrit.beantable;
002
003import java.awt.BorderLayout;
004import java.awt.Component;
005import java.awt.Container;
006import java.awt.FlowLayout;
007import java.awt.event.ActionEvent;
008import java.beans.PropertyChangeListener;
009import java.util.*;
010
011import javax.annotation.CheckForNull;
012import javax.annotation.Nonnull;
013import javax.swing.*;
014import javax.swing.table.AbstractTableModel;
015import javax.swing.table.TableColumn;
016import javax.swing.table.TableColumnModel;
017
018import jmri.Conditional.Operator;
019import jmri.*;
020import jmri.implementation.DefaultConditionalAction;
021import jmri.script.swing.ScriptFileChooser;
022import jmri.util.FileUtil;
023import jmri.util.JmriJFrame;
024import jmri.util.swing.JmriJOptionPane;
025
026/**
027 * Swing action to create and register groups of Logix Condtionals to perform a
028 * railroad control task.
029 *
030 * @author Pete Cressman Copyright (C) 2009
031 * @author Egbert Broerse i18n 2016
032 *
033 */
034public class LRouteTableAction extends AbstractTableAction<Logix> {
035
036    static final ResourceBundle rbx = ResourceBundle.getBundle("jmri.jmrit.beantable.LRouteTableBundle");
037
038    /**
039     * Create an action with a specific title.
040     * <p>
041     * Note that the argument is the Action title, not the title of the
042     * resulting frame. Perhaps this should be changed?
043     *
044     * @param s title of the action
045     */
046    public LRouteTableAction(String s) {
047        super(s);
048        _logixManager = InstanceManager.getNullableDefault(LogixManager.class);
049        _conditionalManager = InstanceManager.getNullableDefault(ConditionalManager.class);
050        // disable ourself if there is no Logix manager or no Conditional manager available
051        if ((_logixManager == null) || (_conditionalManager == null)) {
052            setEnabled(false);
053        }
054        _systemName.setName("hwAddressTextField"); // for GUI test
055        _userName.setName("userNameTextField"); // for GUI Test
056    }
057
058    public LRouteTableAction() {
059        this(Bundle.getMessage("TitleLRouteTable"));
060    }
061
062    /**
063     * Create the JTable DataModel, along with the changes for the specific case
064     * of Road Conditionals.
065     */
066    @Override
067    protected void createModel() {
068        m = new LBeanTableDataModel();
069    }
070
071    class LBeanTableDataModel extends BeanTableDataModel<Logix> {
072
073        // overlay the value column with the enable column
074        // overlay the delete column with the edit column
075        public static final int ENABLECOL = VALUECOL;
076        public static final int EDITCOL = DELETECOL;
077
078        /**
079         * Override to filter out the LRoutes from the rest of Logix.
080         */
081        @Override
082        protected synchronized void updateNameList() {
083            // first, remove listeners from the individual objects
084            if (sysNameList != null) {
085                for (int i = 0; i < sysNameList.size(); i++) {
086                    // if object has been deleted, it's not here; ignore it
087                    NamedBean b = getBySystemName(sysNameList.get(i));
088                    if (b != null) {
089                        b.removePropertyChangeListener(this);
090                    }
091                }
092            }
093            sysNameList = new ArrayList<>();
094            // and add them back in
095            getManager().getNamedBeanSet().forEach(b -> {
096                if (b.getSystemName().startsWith(getLogixSystemPrefix())) {
097                    sysNameList.add(b.getSystemName());
098                    b.addPropertyChangeListener(this);
099                }
100            });
101            log.debug("updateNameList: sysNameList size= {}", sysNameList.size());
102        }
103
104        @Override
105        public String getColumnName(int col) {
106            switch (col) {
107                case EDITCOL:
108                    return ""; // no heading on "Edit"
109                case ENABLECOL:
110                    return Bundle.getMessage("ColumnHeadEnabled");
111                default:
112                    return super.getColumnName(col);
113            }
114        }
115
116        @Override
117        public Class<?> getColumnClass(int col) {
118            switch (col) {
119                case EDITCOL:
120                    return JButton.class;
121                case ENABLECOL:
122                    return Boolean.class;
123                default:
124                    return super.getColumnClass(col);
125            }
126        }
127
128        @Override
129        public int getPreferredWidth(int col) {
130            // override default value for SystemName and UserName columns
131            switch (col) {
132                case SYSNAMECOL:
133                    return new JTextField(20).getPreferredSize().width;
134                case USERNAMECOL:
135                case COMMENTCOL:
136                    return new JTextField(25).getPreferredSize().width;
137                case EDITCOL:
138                    // not actually used due to the configDeleteColumn, setColumnToHoldButton, configureButton
139                    return new JTextField(Bundle.getMessage("ButtonEdit")).getPreferredSize().width+4;
140                case ENABLECOL:
141                    // not actually used due to the configValueColumn, setColumnToHoldButton, configureButton
142                    return new JTextField(5).getPreferredSize().width;
143                default:
144                    return super.getPreferredWidth(col);
145            }
146        }
147
148        @Override
149        public boolean isCellEditable(int row, int col) {
150            switch (col) {
151                case EDITCOL:
152                case ENABLECOL:
153                    return true;
154                default:
155                    return super.isCellEditable(row, col);
156            }
157        }
158
159        @Override
160        public Object getValueAt(int row, int col) {
161            switch (col) {
162                case EDITCOL:
163                    return Bundle.getMessage("ButtonEdit");
164                case ENABLECOL:
165                    return ((Logix) getValueAt(row, SYSNAMECOL)).getEnabled();
166                default:
167                    return super.getValueAt(row, col);
168            }
169        }
170
171        @Override
172        public void setValueAt(Object value, int row, int col) {
173            switch (col) {
174                case EDITCOL:
175                    // set up to edit
176                    String sName = ((Logix) getValueAt(row, SYSNAMECOL)).getSystemName();
177                    editPressed(sName);
178                    break;
179                case ENABLECOL:
180                    // alternate
181                    Logix x = (Logix) getValueAt(row, SYSNAMECOL);
182                    boolean v = x.getEnabled();
183                    x.setEnabled(!v);
184                    break;
185                default:
186                    super.setValueAt(value, row, col);
187                    break;
188            }
189        }
190
191        /**
192         * Delete the bean after all the checking has been done.
193         * <p>
194         * Deactivate the Logix and remove its conditionals.
195         */
196        @Override
197        protected void doDelete(Logix logix) {
198            if (logix != null) {
199                logix.deActivateLogix();
200                // delete the Logix and all its Conditionals
201                _logixManager.deleteLogix(logix);
202            }
203        }
204
205        @Override
206        protected boolean matchPropertyName(java.beans.PropertyChangeEvent e) {
207            if (Logix.PROPERTY_ENABLED.equals(e.getPropertyName())) {
208                return true;
209            } else {
210                return super.matchPropertyName(e);
211            }
212        }
213
214        @Override
215        public Manager<Logix> getManager() {
216            return _logixManager;
217        }
218
219        @Override
220        public Logix getBySystemName(@Nonnull String name) {
221            return _logixManager.getBySystemName(name);
222        }
223
224        @Override
225        public Logix getByUserName(@Nonnull String name) {
226            return _logixManager.getByUserName(name);
227        }
228
229        /*public int getDisplayDeleteMsg() { return InstanceManager.getDefault(jmri.UserPreferencesManager.class).getMultipleChoiceOption(getClassName(),"delete"); }
230         public void setDisplayDeleteMsg(int boo) { InstanceManager.getDefault(jmri.UserPreferencesManager.class).setMultipleChoiceOption(getClassName(), "delete", boo); }*/
231        @Override
232        protected String getMasterClassName() {
233            return getClassName();
234        }
235
236        @Override
237        public void configureTable(JTable table) {
238            table.setDefaultRenderer(Boolean.class, new EnablingCheckboxRenderer());
239            table.setDefaultRenderer(JComboBox.class, new jmri.jmrit.symbolicprog.ValueRenderer());
240            table.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor());
241            super.configureTable(table);
242        }
243
244        // Not needed - here for interface compatibility
245        @Override
246        public void clickOn(Logix t) {
247        }
248
249        @Override
250        public String getValue(String s) {
251            return "";
252        }
253
254        // typical to get correct width
255        @Override
256        protected void configDeleteColumn(JTable table) {
257            // have the DELETECOL = EDITCOL column hold a button
258            setColumnToHoldButton(table, DELETECOL,
259                    new JButton(Bundle.getMessage("ButtonEdit")));
260        }
261
262        @Override
263        protected void configValueColumn(JTable table) {
264        }
265
266        @Override
267        protected String getBeanType() {
268            return "LRoute";
269        }
270
271    }
272
273    @Override
274    protected void setTitle() {
275        f.setTitle(Bundle.getMessage("TitleLRouteTable"));
276    }
277
278    @Override
279    protected String helpTarget() {
280        return "package.jmri.jmrit.beantable.LRouteTable";
281    }
282
283///////////////////////////////////// Edit window //////////////////////////////
284    private ConditionalManager _conditionalManager = null;
285    private LogixManager _logixManager = null;
286
287    private JTextField _systemName = new JTextField(15);
288    private JTextField _userName = new JTextField(25);
289
290    JmriJFrame _addFrame = null;
291    private JTabbedPane _tabbedPane = null;
292
293    private RouteInputModel _inputModel;
294    private JComboBox<String> _testStateCombo;
295    private JRadioButton _inputAllButton;
296    private boolean _showAllInput;
297
298    private RouteOutputModel _outputModel;
299    private JComboBox<String> _setStateCombo;
300    private JRadioButton _outputAllButton;
301    private boolean _showAllOutput;
302
303    private AlignmentModel _alignModel;
304    private JRadioButton _alignAllButton;
305    private boolean _showAllAlign;
306
307    private JCheckBox _lockCheckBox;
308    private boolean _lock = false;
309
310    private JPanel _typePanel;
311    private JRadioButton _newRouteButton;
312    private boolean _newRouteType = true;
313    private JRadioButton _initializeButton;
314    private boolean _initialize = false;
315
316    private JTextField soundFile = new JTextField(30);
317    private JTextField scriptFile = new JTextField(30);
318
319    private JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
320    private JButton createButton = new JButton(Bundle.getMessage("ButtonCreate"));
321    private JButton deleteButton = new JButton(Bundle.getMessage("ButtonDelete"));
322    private JButton updateButton = new JButton(Bundle.getMessage("ButtonUpdate"));
323
324    private boolean routeDirty = false;  // true to fire reminder to save work
325    private boolean checkEnabled = InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled();
326
327    ArrayList<RouteInputElement> _inputList;
328    private HashMap<String, RouteInputElement> _inputMap;
329    private HashMap<String, RouteInputElement> _inputUserMap;
330    private ArrayList<RouteInputElement> _includedInputList;
331
332    ArrayList<RouteOutputElement> _outputList;
333    private HashMap<String, RouteOutputElement> _outputMap;
334    private HashMap<String, RouteOutputElement> _outputUserMap;
335    private ArrayList<RouteOutputElement> _includedOutputList;
336
337    ArrayList<AlignElement> _alignList;
338    private HashMap<String, AlignElement> _alignMap;
339    private HashMap<String, AlignElement> _alignUserMap;
340    private ArrayList<AlignElement> _includedAlignList;
341
342    void buildLists() {
343        TreeSet<RouteInputElement> inputTS = new TreeSet<>(new RouteElementComparator());
344        TreeSet<RouteOutputElement> outputTS = new TreeSet<>(new RouteElementComparator());
345        //TreeSet <RouteInputElement>inputTS = new TreeSet<RouteInputElement>();
346        //TreeSet <RouteOutputElement>outputTS = new TreeSet<RouteOutputElement>();
347        TurnoutManager tm = InstanceManager.turnoutManagerInstance();
348        tm.getNamedBeanSet().forEach( nb -> {
349            String userName = nb.getUserName();
350            String systemName = nb.getSystemName();
351            inputTS.add(new RouteInputTurnout(systemName, userName));
352            outputTS.add(new RouteOutputTurnout(systemName, userName));
353        });
354
355        TreeSet<AlignElement> alignTS = new TreeSet<>(new RouteElementComparator());
356        SensorManager sm = InstanceManager.sensorManagerInstance();
357        sm.getNamedBeanSet().forEach( nb -> {
358            String userName = nb.getUserName();
359            String systemName = nb.getSystemName();
360            inputTS.add(new RouteInputSensor(systemName, userName));
361            outputTS.add(new RouteOutputSensor(systemName, userName));
362            alignTS.add(new AlignElement(systemName, userName));
363        });
364        LightManager lm = InstanceManager.lightManagerInstance();
365        lm.getNamedBeanSet().forEach( nb -> {
366            String userName = nb.getUserName();
367            String systemName = nb.getSystemName();
368            inputTS.add(new RouteInputLight(systemName, userName));
369            outputTS.add(new RouteOutputLight(systemName, userName));
370        });
371        SignalHeadManager shm = InstanceManager.getDefault(SignalHeadManager.class);
372        shm.getNamedBeanSet().forEach( nb -> {
373            String userName = nb.getUserName();
374            String systemName = nb.getSystemName();
375            inputTS.add(new RouteInputSignal(systemName, userName));
376            outputTS.add(new RouteOutputSignal(systemName, userName));
377        });
378        _includedInputList = new ArrayList<>();
379        _includedOutputList = new ArrayList<>();
380        _inputList = new ArrayList<>(inputTS.size());
381        _outputList = new ArrayList<>(outputTS.size());
382        _inputMap = new HashMap<>(inputTS.size());
383        _outputMap = new HashMap<>(outputTS.size());
384        _inputUserMap = new HashMap<>();
385        _outputUserMap = new HashMap<>();
386        Iterator<RouteInputElement> it = inputTS.iterator();
387        while (it.hasNext()) {
388            RouteInputElement elt = it.next();
389            _inputList.add(elt);
390            String key = elt.getType() + elt.getSysName();
391            _inputMap.put(key, elt);
392            String user = elt.getUserName();
393            if (user != null) {
394                key = elt.getType() + user;
395                _inputUserMap.put(key, elt);
396            }
397        }
398        Iterator<RouteOutputElement> itOut = outputTS.iterator();
399        while (itOut.hasNext()) {
400            RouteOutputElement elt = itOut.next();
401            _outputList.add(elt);
402            String key = elt.getType() + elt.getSysName();
403            _outputMap.put(key, elt);
404            String user = elt.getUserName();
405            if (user != null) {
406                key = elt.getType() + user;
407                _outputUserMap.put(key, elt);
408            }
409        }
410        _includedAlignList = new ArrayList<>();
411        _alignList = new ArrayList<>(alignTS.size());
412        _alignMap = new HashMap<>(alignTS.size());
413        _alignUserMap = new HashMap<>();
414        Iterator<AlignElement> itAlign = alignTS.iterator();
415        while (itAlign.hasNext()) {
416            AlignElement elt = itAlign.next();
417            _alignList.add(elt);
418            String key = elt.getType() + elt.getSysName();
419            _alignMap.put(key, elt);
420            String user = elt.getUserName();
421            if (user != null) {
422                key = elt.getType() + user;
423                _alignUserMap.put(key, elt);
424            }
425        }
426    }
427
428    /**
429     * Edit button in Logix Route table pressed.
430     *
431     * @param sName system name of Logix to edit
432     */
433    void editPressed(String sName) {
434        // Logix was found, initialize for edit
435        Logix logix = _logixManager.getBySystemName(sName);
436        if (logix == null) {
437            log.error("Logix \"{}\" not Found.", sName);
438            return;
439        }
440        // deactivate this Logix
441        _systemName.setText(sName);
442        // create the Edit Logix Window
443        // Use separate Runnable so window is created on top
444        Runnable t = () -> {
445            setupEdit(null);
446            _addFrame.setVisible(true);
447        };
448        javax.swing.SwingUtilities.invokeLater(t);
449    }
450
451    /**
452     * Interprets the conditionals from the Logix that was selected for editing
453     * and attempts to reconstruct the window entries.
454     *
455     * @param e the action event
456     */
457    void setupEdit(ActionEvent e) {
458        makeEditWindow();
459        Logix logix = checkNamesOK();
460        if (logix == null) {
461            return;
462        }
463        logix.deActivateLogix();
464        // get information for this route
465        _systemName.setEnabled(false);
466        _userName.setEnabled(false);
467        _systemName.setText(logix.getSystemName());
468        _userName.setText(logix.getUserName());
469        String logixSysName = logix.getSystemName();
470        int numConditionals = logix.getNumConditionals();
471        log.debug("setupEdit: logixSysName= {}, numConditionals= {}", logixSysName, numConditionals);
472        for (int i = 0; i < numConditionals; i++) {
473            String cSysName = logix.getConditionalByNumberOrder(i);
474            switch (getRouteConditionalType(logixSysName, cSysName)) {
475                case 'T':
476                    getControlsAndActions(cSysName);
477                    break;
478                case 'A':
479                    getAlignmentSensors(cSysName);
480                    break;
481                case 'L':
482                    getLockConditions(cSysName);
483                    break;
484                default:
485                    log.warn("Unexpected getRouteConditionalType {}", getRouteConditionalType(logixSysName, cSysName));
486                    break;
487            }
488        }
489        // set up buttons and notes
490        deleteButton.setVisible(true);
491        cancelButton.setVisible(true);
492        updateButton.setVisible(true);
493        _typePanel.setVisible(false);
494        _initialize = getLogixInitializer().equals(logixSysName);
495        if (_initialize) {
496            _initializeButton.doClick();
497        } else {
498            _newRouteButton.doClick();
499        }
500        createButton.setVisible(false);
501        _addFrame.setTitle(rbx.getString("LRouteEditTitle"));
502    }
503
504    /**
505     * Get the type letter from the possible LRoute conditional.
506     *
507     * @param logixSysName logix system name
508     * @param cSysName conditional system name
509     * @return the type letter
510     */
511    char getRouteConditionalType(String logixSysName, String cSysName) {
512        if (cSysName.startsWith(logixSysName)) {
513            char[] chNum = cSysName.substring(logixSysName.length()).toCharArray();
514            int i = 0;
515            while (Character.isDigit(chNum[i])) {
516                i++;
517            }
518            return chNum[i];
519        }
520        return 0;
521    }
522
523    /**
524     * Extract the Control (input) and Action (output) elements and their
525     * states.
526     *
527     * @param cSysName the conditional system name
528     */
529    void getControlsAndActions(String cSysName) {
530        Conditional c = _conditionalManager.getBySystemName(cSysName);
531        if (c != null) {
532            List<ConditionalAction> actionList = c.getCopyOfActions();
533            boolean onChange = false;
534            for (int k = 0; k < actionList.size(); k++) {
535                ConditionalAction action = actionList.get(k);
536                int type;
537                switch (action.getType()) {
538                    case SET_SENSOR:
539                        type = SENSOR_TYPE;
540                        break;
541                    case SET_TURNOUT:
542                        type = TURNOUT_TYPE;
543                        break;
544                    case SET_LIGHT:
545                        type = LIGHT_TYPE;
546                        break;
547                    case SET_SIGNAL_APPEARANCE:
548                    case SET_SIGNAL_HELD:
549                    case CLEAR_SIGNAL_HELD:
550                    case SET_SIGNAL_DARK:
551                    case SET_SIGNAL_LIT:
552                        type = SIGNAL_TYPE;
553                        break;
554                    case RUN_SCRIPT:
555                        scriptFile.setText(action.getActionString());
556                        continue;
557                    case PLAY_SOUND:
558                        soundFile.setText(action.getActionString());
559                        continue;
560                    default:
561                        JmriJOptionPane.showMessageDialog(
562                                _addFrame, java.text.MessageFormat.format(rbx.getString("TypeWarn"),
563                                        new Object[]{action.toString(), c.getSystemName()}),
564                                rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
565                        continue;
566                }
567                String name = action.getDeviceName();
568                String key = type + name;
569                RouteOutputElement elt = _outputUserMap.get(key);
570                if (elt == null) { // try in system name map
571                    elt = _outputMap.get(key);
572                }
573                if (elt == null) {
574                    JmriJOptionPane.showMessageDialog(
575                            _addFrame, java.text.MessageFormat.format(rbx.getString("TypeWarn"),
576                                    new Object[]{action.toString(), c.getSystemName()}),
577                            rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
578                } else {
579                    elt.setIncluded(true);
580                    elt.setState(action.getActionData());
581                    boolean change = (action.getOption() == Conditional.ACTION_OPTION_ON_CHANGE);
582                    if (k == 0) {
583                        onChange = change;
584                    } else if (change != onChange) {
585                        JmriJOptionPane.showMessageDialog(
586                                _addFrame, java.text.MessageFormat.format(rbx.getString("OnChangeWarn"),
587                                        new Object[]{action.toString(), c.getSystemName()}),
588                                rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
589                    }
590                }
591            }
592            List<ConditionalVariable> varList = c.getCopyOfStateVariables();
593            for (int k = 0; k < varList.size(); k++) {
594                ConditionalVariable variable = varList.get(k);
595                Conditional.Type testState = variable.getType();
596                //boolean negated = variable.isNegated();
597                int type;
598                switch (testState) {
599                    case SENSOR_ACTIVE:
600                        type = SENSOR_TYPE;
601                        //if (negated) testState = Conditional.TYPE_SENSOR_INACTIVE;
602                        break;
603                    case SENSOR_INACTIVE:
604                        type = SENSOR_TYPE;
605                        //if (negated) testState = Conditional.TYPE_SENSOR_ACTIVE;
606                        break;
607                    case TURNOUT_CLOSED:
608                        type = TURNOUT_TYPE;
609                        //if (negated) testState = Conditional.TYPE_TURNOUT_THROWN;
610                        break;
611                    case TURNOUT_THROWN:
612                        type = TURNOUT_TYPE;
613                        //if (negated) testState = Conditional.TYPE_TURNOUT_CLOSED;
614                        break;
615                    case LIGHT_ON:
616                        type = LIGHT_TYPE;
617                        //if (negated) testState = Conditional.TYPE_LIGHT_OFF;
618                        break;
619                    case LIGHT_OFF:
620                        type = LIGHT_TYPE;
621                        //if (negated) testState = Conditional.TYPE_LIGHT_ON;
622                        break;
623                    case SIGNAL_HEAD_LIT:
624                    case SIGNAL_HEAD_RED:
625                    case SIGNAL_HEAD_YELLOW:
626                    case SIGNAL_HEAD_GREEN:
627                    case SIGNAL_HEAD_DARK:
628                    case SIGNAL_HEAD_FLASHRED:
629                    case SIGNAL_HEAD_FLASHYELLOW:
630                    case SIGNAL_HEAD_FLASHGREEN:
631                    case SIGNAL_HEAD_HELD:
632                        type = SIGNAL_TYPE;
633                        break;
634                    default:
635                        if (!getLogixInitializer().equals(variable.getName())) {
636                            JmriJOptionPane.showMessageDialog(
637                                    _addFrame, java.text.MessageFormat.format(rbx.getString("TypeWarnVar"),
638                                            new Object[]{variable.toString(), c.getSystemName()}),
639                                    rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
640                        }
641                        continue;
642                }
643                int testStateInt = testState.getIntValue();
644                Operator opern = variable.getOpern();
645                if (k != 0 && (opern == Conditional.Operator.AND)) {
646                    // guess this is a VETO
647                    testStateInt += VETO;
648                } else if (onChange) {
649                    testStateInt = Route.ONCHANGE;
650                }
651                String name = variable.getName();
652                String key = type + name;
653                RouteInputElement elt = _inputUserMap.get(key);
654                if (elt == null) { // try in system name map
655                    elt = _inputMap.get(key);
656                }
657                if (elt == null) {
658                    if (!getLogixInitializer().equals(name)) {
659                        JmriJOptionPane.showMessageDialog(
660                                _addFrame, java.text.MessageFormat.format(rbx.getString("TypeWarnVar"),
661                                        new Object[]{variable.toString(), c.getSystemName()}),
662                                rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
663                    }
664                } else {
665                    elt.setIncluded(true);
666                    elt.setState(testStateInt);
667                }
668            }
669        }
670    }   // getControlsAndActions
671
672    /**
673     * Extract the Alignment Sensors and their types.
674     *
675     * @param cSysName the conditional system name
676     */
677    private void getAlignmentSensors(String cSysName) {
678        Conditional c = _conditionalManager.getBySystemName(cSysName);
679        if (c != null) {
680            AlignElement element = null;
681            List<ConditionalAction> actionList = c.getCopyOfActions();
682            for (int k = 0; k < actionList.size(); k++) {
683                ConditionalAction action = actionList.get(k);
684                if (action.getType() != Conditional.Action.SET_SENSOR) {
685                    JmriJOptionPane.showMessageDialog(
686                            _addFrame, java.text.MessageFormat.format(rbx.getString("AlignWarn1"),
687                                    new Object[]{action.toString(), c.getSystemName()}),
688                            rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
689                } else {
690                    String name = action.getDeviceName();
691                    String key = SENSOR_TYPE + name;
692                    element = _alignUserMap.get(key);
693                    if (element == null) { // try in system name map
694                        element = _alignMap.get(key);
695                    }
696                    if (element == null) {
697                        JmriJOptionPane.showMessageDialog(
698                                _addFrame, java.text.MessageFormat.format(rbx.getString("TypeWarn"),
699                                        new Object[]{action.toString(), c.getSystemName()}),
700                                rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
701
702                    } else if (!name.equals(action.getDeviceName())) {
703                        JmriJOptionPane.showMessageDialog(
704                                _addFrame, java.text.MessageFormat.format(rbx.getString("AlignWarn2"),
705                                        new Object[]{action.toString(), action.getDeviceName(), c.getSystemName()}),
706                                rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
707
708                    } else {
709                        element.setIncluded(true);
710                    }
711                }
712            }
713            // the action elements are identified in getControlsAndActions().
714            //  Just identify the type of sensing
715            List<ConditionalVariable> varList = c.getCopyOfStateVariables();
716            int atype = 0;
717            for (int k = 0; k < varList.size(); k++) {
718                ConditionalVariable variable = varList.get(k);
719                Conditional.Type testState = variable.getType();
720                int type;
721                switch (testState) {
722                    case SENSOR_ACTIVE:
723                    case SENSOR_INACTIVE:
724                        type = SENSOR_TYPE;
725                        break;
726                    case TURNOUT_CLOSED:
727                    case TURNOUT_THROWN:
728                        type = TURNOUT_TYPE;
729                        break;
730                    case LIGHT_ON:
731                    case LIGHT_OFF:
732                        type = LIGHT_TYPE;
733                        break;
734                    case SIGNAL_HEAD_LIT:
735                    case SIGNAL_HEAD_RED:
736                    case SIGNAL_HEAD_YELLOW:
737                    case SIGNAL_HEAD_GREEN:
738                    case SIGNAL_HEAD_DARK:
739                    case SIGNAL_HEAD_FLASHRED:
740                    case SIGNAL_HEAD_FLASHYELLOW:
741                    case SIGNAL_HEAD_FLASHGREEN:
742                    case SIGNAL_HEAD_HELD:
743                        type = SIGNAL_TYPE;
744                        break;
745                    default:
746                        if (!getLogixInitializer().equals(variable.getName())) {
747                            JmriJOptionPane.showMessageDialog(
748                                    _addFrame, java.text.MessageFormat.format(rbx.getString("TypeWarnVar"),
749                                            new Object[]{variable.toString(), c.getSystemName()}),
750                                    rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
751                        }
752                        continue;
753                }
754                if (k == 0) {
755                    atype = type;
756                } else if (atype != type) {
757                    // more than one type. therefor, ALL
758                    atype = ALL_TYPE;
759                    break;
760                }
761            }
762            if (element != null) {
763                element.setState(atype);
764            }
765        }
766    }
767
768    /**
769     * Extract the Lock expression. For now, same as action control expression.
770     *
771     * @param cSysName the conditional system name
772     */
773    private void getLockConditions(String cSysName) {
774        Conditional c = _conditionalManager.getBySystemName(cSysName);
775        if (c != null) {
776            _lock = true;
777            // Verify conditional is what we think it is
778            ArrayList<RouteOutputElement> tList = makeTurnoutLockList();
779            List<ConditionalAction> actionList = c.getCopyOfActions();
780            if (actionList.size() != tList.size()) {
781                JmriJOptionPane.showMessageDialog(
782                        _addFrame, java.text.MessageFormat.format(rbx.getString("LockWarn1"),
783                                new Object[]{Integer.toString(tList.size()), c.getSystemName(),
784                                    Integer.toString(actionList.size())}),
785                        rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
786            }
787            for (int k = 0; k < actionList.size(); k++) {
788                ConditionalAction action = actionList.get(k);
789                if (action.getType() != Conditional.Action.LOCK_TURNOUT) {
790                    JmriJOptionPane.showMessageDialog(
791                            _addFrame, java.text.MessageFormat.format(rbx.getString("LockWarn2"),
792                                    new Object[]{action.getDeviceName(), c.getSystemName()}),
793                            rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
794                } else {
795                    String name = action.getDeviceName();
796                    boolean found = false;
797                    ArrayList<RouteOutputElement> lockList = makeTurnoutLockList();
798                    for (int j = 0; j < lockList.size(); j++) {
799                        RouteOutputElement elt = lockList.get(j);
800                        if (name.equals(elt.getUserName()) || name.equals(elt.getSysName())) {
801                            found = true;
802                            break;
803                        }
804                    }
805                    if (!found) {
806                        JmriJOptionPane.showMessageDialog(
807                                _addFrame, java.text.MessageFormat.format(rbx.getString("LockWarn3"),
808                                        new Object[]{name, c.getSystemName()}),
809                                rbx.getString("EditDiff"), JmriJOptionPane.WARNING_MESSAGE);
810                    }
811                }
812            }
813        }
814    }
815
816    /**
817     * Responds to the Cancel button.
818     *
819     * @param e the action event
820     */
821    private void cancelPressed(ActionEvent e) {
822        if (_addFrame.getTitle().equals(rbx.getString("LRouteEditTitle"))) { // Warnings shown are useless when cancelling Add New LRoute
823            Logix logix = checkNamesOK();
824            if (logix != null) {
825                logix.activateLogix();
826            }
827        }
828        clearPage();
829    }
830
831    @Override
832    protected void addPressed(ActionEvent e) {
833        makeEditWindow();
834        _tabbedPane.setSelectedIndex(0);
835        createButton.setVisible(true);
836        cancelButton.setVisible(true);
837        _typePanel.setVisible(true);
838        _addFrame.setVisible(true);
839        _systemName.setEnabled(true);
840        _userName.setEnabled(true);
841        _addFrame.setTitle(rbx.getString("LRouteAddTitle"));
842
843        _addFrame.setEscapeKeyClosesWindow(true);
844        _addFrame.getRootPane().setDefaultButton(createButton);
845    }
846
847    /**
848     * Set up Create/Edit LRoute pane
849     */
850    private void makeEditWindow() {
851        buildLists();
852        if (_addFrame == null) {
853            _addFrame = new JmriJFrame(rbx.getString("LRouteAddTitle"), false, false);
854            _addFrame.addHelpMenu("package.jmri.jmrit.beantable.LRouteAddEdit", true);
855            _addFrame.setLocation(100, 30);
856
857            _tabbedPane = new JTabbedPane();
858
859            //////////////////////////////////// Tab 1 /////////////////////////////
860            JPanel tab1 = new JPanel();
861            tab1.setLayout(new BoxLayout(tab1, BoxLayout.Y_AXIS));
862            tab1.add(Box.createVerticalStrut(10));
863            // add system name
864            JPanel p = new JPanel();
865            p.setLayout(new FlowLayout());
866            p.add(new JLabel(Bundle.getMessage("LabelSystemName")));
867            p.add(_systemName);
868            _systemName.setToolTipText(rbx.getString("SystemNameHint"));
869            tab1.add(p);
870            // add user name
871            p = new JPanel();
872            p.setLayout(new FlowLayout());
873            p.add(new JLabel(Bundle.getMessage("LabelUserName")));
874            p.add(_userName);
875            _userName.setToolTipText(rbx.getString("UserNameHint"));
876            tab1.add(p);
877
878            JPanel pa = new JPanel();
879            p = new JPanel();
880            p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
881            p.add(new JLabel(rbx.getString("Guide1")));
882            p.add(new JLabel(rbx.getString("Guide2")));
883            p.add(new JLabel(rbx.getString("Guide3")));
884            p.add(new JLabel(rbx.getString("Guide4")));
885            pa.add(p);
886            tab1.add(pa);
887
888            _newRouteButton = new JRadioButton(rbx.getString("NewRoute"), true);
889            JRadioButton oldRoute = new JRadioButton(rbx.getString("OldRoute"), false);
890            _initializeButton = new JRadioButton(rbx.getString("Initialize"), false);
891            _newRouteButton.setToolTipText(rbx.getString("NewRouteHint"));
892            _newRouteButton.addActionListener( e -> {
893                _newRouteType = true;
894                _systemName.setEnabled(true);
895            });
896            oldRoute.setToolTipText(rbx.getString("OldRouteHint"));
897            oldRoute.addActionListener( e -> {
898                _newRouteType = false;
899                _systemName.setEnabled(true);
900            });
901            _initializeButton.setToolTipText(rbx.getString("InitializeHint"));
902            _initializeButton.addActionListener( e -> {
903                _initialize = true;
904                _newRouteType = true;
905                _systemName.setEnabled(false);
906                _systemName.setText(getLogixInitializer());
907            });
908            _typePanel = makeShowButtons(_newRouteButton, oldRoute, _initializeButton, rbx.getString("LRouteType") + ":");
909            _typePanel.setBorder(BorderFactory.createEtchedBorder());
910            tab1.add(_typePanel);
911            tab1.add(Box.createVerticalGlue());
912
913            // add buttons
914            JPanel pb = new JPanel();
915            pb.setLayout(new FlowLayout());
916            // Cancel button
917            pb.add(cancelButton);
918            cancelButton.addActionListener(this::cancelPressed);
919            cancelButton.setToolTipText(Bundle.getMessage("TooltipCancelRoute"));
920            cancelButton.setName("CancelButton");
921            // Add Route button
922            pb.add(createButton);
923            createButton.addActionListener(this::createPressed);
924            createButton.setToolTipText(rbx.getString("CreateHint"));
925            createButton.setName("CreateButton");
926            // Delete Route button
927            pb.add(deleteButton);
928            deleteButton.addActionListener(this::deletePressed);
929            deleteButton.setToolTipText(rbx.getString("DeleteHint"));
930            // Update Route button
931            pb.add(updateButton);
932            updateButton.addActionListener( e -> updatePressed());
933            updateButton.setToolTipText(rbx.getString("UpdateHint"));
934            updateButton.setName("UpdateButton");
935
936            // Show the initial buttons, and hide the others
937            cancelButton.setVisible(true);
938            updateButton.setVisible(false);
939            createButton.setVisible(false);
940            deleteButton.setVisible(false);
941            tab1.add(pb);
942
943            tab1.setVisible(true);
944            _tabbedPane.addTab(rbx.getString("BasicTab"), null, tab1, rbx.getString("BasicTabHint"));
945
946            //////////////////////////////////// Tab 2 /////////////////////////////
947            JPanel tab2 = new JPanel();
948            tab2.setLayout(new BoxLayout(tab2, BoxLayout.Y_AXIS));
949            tab2.add(new JLabel(rbx.getString("OutputTitle") + ":"));
950            _outputAllButton = new JRadioButton(Bundle.getMessage("All"), true);
951            JRadioButton includedOutputButton = new JRadioButton(Bundle.getMessage("Included"), false);
952            tab2.add(makeShowButtons(_outputAllButton, includedOutputButton, null, Bundle.getMessage("Show") + ":"));
953            _outputAllButton.addActionListener( e -> {
954                // Setup for display of all Turnouts, if needed
955                if (!_showAllOutput) {
956                    _showAllOutput = true;
957                    _outputModel.fireTableDataChanged();
958                }
959            });
960            includedOutputButton.addActionListener( e -> {
961                // Setup for display of included Turnouts only, if needed
962                if (_showAllOutput) {
963                    _showAllOutput = false;
964                    initializeIncludedOutputList();
965                    _outputModel.fireTableDataChanged();
966                }
967            });
968            tab2.add(new JLabel(rbx.getString("PickOutput")));
969
970            _outputModel = new RouteOutputModel();
971            JTable routeOutputTable = new JTable(_outputModel);
972            JScrollPane _outputScrollPane = makeColumns(routeOutputTable, _setStateCombo, true);
973            tab2.add(_outputScrollPane, BorderLayout.CENTER);
974            tab2.setVisible(true);
975            _tabbedPane.addTab(rbx.getString("ActionTab"), null, tab2, rbx.getString("ActionTabHint"));
976
977            //////////////////////////////////// Tab 3 /////////////////////////////
978            JPanel tab3 = new JPanel();
979            tab3.setLayout(new BoxLayout(tab3, BoxLayout.Y_AXIS));
980            tab3.add(new JLabel(rbx.getString("InputTitle") + ":"));
981            _inputAllButton = new JRadioButton(Bundle.getMessage("All"), true);
982            JRadioButton includedInputButton = new JRadioButton(Bundle.getMessage("Included"), false);
983            tab3.add(makeShowButtons(_inputAllButton, includedInputButton, null, Bundle.getMessage("Show") + ":"));
984            _inputAllButton.addActionListener( e -> {
985                // Setup for display of all Turnouts, if needed
986                if (!_showAllInput) {
987                    _showAllInput = true;
988                    _inputModel.fireTableDataChanged();
989                }
990            });
991            includedInputButton.addActionListener( e -> {
992                // Setup for display of included Turnouts only, if needed
993                if (_showAllInput) {
994                    _showAllInput = false;
995                    initializeIncludedInputList();
996                    _inputModel.fireTableDataChanged();
997                }
998            });
999            tab3.add(new JLabel(rbx.getString("PickInput")));
1000
1001            _inputModel = new RouteInputModel();
1002            JTable routeInputTable = new JTable(_inputModel);
1003            //ROW_HEIGHT = routeInputTable.getRowHeight();
1004            JScrollPane _inputScrollPane = makeColumns(routeInputTable, _testStateCombo, true);
1005            tab3.add(_inputScrollPane, BorderLayout.CENTER);
1006            tab3.setVisible(true);
1007            _tabbedPane.addTab(rbx.getString("TriggerTab"), null, tab3, rbx.getString("TriggerTabHint"));
1008
1009            ////////////////////// Tab 4 /////////////////
1010            JPanel tab4 = new JPanel();
1011            tab4.setLayout(new BoxLayout(tab4, BoxLayout.Y_AXIS));
1012            tab4.add(new JLabel(rbx.getString("MiscTitle") + ":"));
1013            // Enter filenames for sound, script
1014            JPanel p25 = new JPanel();
1015            p25.setLayout(new FlowLayout());
1016            p25.add(new JLabel(Bundle.getMessage("LabelPlaySound")));
1017            JButton ss = new JButton("...");
1018            ss.addActionListener( e -> setSoundPressed());
1019            p25.add(ss);
1020            p25.add(soundFile);
1021            tab4.add(p25);
1022
1023            p25 = new JPanel();
1024            p25.setLayout(new FlowLayout());
1025            p25.add(new JLabel(Bundle.getMessage("LabelRunScript")));
1026            ss = new JButton("...");
1027            ss.addActionListener( e -> setScriptPressed());
1028            p25.add(ss);
1029            p25.add(scriptFile);
1030            tab4.add(p25);
1031
1032            p25 = new JPanel();
1033            p25.setLayout(new FlowLayout());
1034            p25.add(new JLabel(rbx.getString("SetLocks") + ":"));
1035            _lockCheckBox = new JCheckBox(rbx.getString("Lock"), true);
1036            _lockCheckBox.addActionListener( e -> {
1037                // Setup for display of all Turnouts, if needed
1038                _lock = _lockCheckBox.isSelected();
1039            });
1040            p25.add(_lockCheckBox);
1041            tab4.add(p25);
1042
1043            _alignAllButton = new JRadioButton(Bundle.getMessage("All"), true);
1044            JRadioButton includedAlignButton = new JRadioButton(Bundle.getMessage("Included"), false);
1045            tab4.add(makeShowButtons(_alignAllButton, includedAlignButton, null, Bundle.getMessage("Show") + ":"));
1046            _alignAllButton.addActionListener( e -> {
1047                // Setup for display of all Turnouts, if needed
1048                if (!_showAllAlign) {
1049                    _showAllAlign = true;
1050                    _alignModel.fireTableDataChanged();
1051                }
1052            });
1053            includedAlignButton.addActionListener( e -> {
1054                // Setup for display of included Turnouts only, if needed
1055                if (_showAllAlign) {
1056                    _showAllAlign = false;
1057                    initializeIncludedAlignList();
1058                    _alignModel.fireTableDataChanged();
1059                }
1060            });
1061            tab4.add(new JLabel(rbx.getString("PickAlign")));
1062            _alignModel = new AlignmentModel();
1063            JTable alignTable = new JTable(_alignModel);
1064            JComboBox<String> _alignCombo = new JComboBox<>();
1065            for (String state : ALIGNMENT_STATES) {
1066                _alignCombo.addItem(state);
1067            }
1068            JScrollPane alignScrollPane = makeColumns(alignTable, _alignCombo, false);
1069            //alignTable.setPreferredScrollableViewportSize(new java.awt.Dimension(250,200));
1070            _alignCombo = new JComboBox<>();
1071            for (String state : ALIGNMENT_STATES) {
1072                _alignCombo.addItem(state);
1073            }
1074            tab4.add(alignScrollPane, BorderLayout.CENTER);
1075            tab4.setVisible(true);
1076            _tabbedPane.addTab(rbx.getString("MiscTab"), null, tab4, rbx.getString("MiscTabHint"));
1077
1078            Container contentPane = _addFrame.getContentPane();
1079            //tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
1080
1081            ///////////////////////////////////
1082            JPanel pt = new JPanel();
1083            pt.add(_tabbedPane);
1084            contentPane.add(pt);
1085
1086            // set listener for window closing
1087            _addFrame.addWindowListener(new java.awt.event.WindowAdapter() {
1088                @Override
1089                public void windowClosing(java.awt.event.WindowEvent e) {
1090                    // remind to save, if Route was created or edited
1091                    if (routeDirty) {
1092                        showReminderMessage();
1093                    }
1094                    clearPage();
1095                    _addFrame.setVisible(false);
1096                    _inputModel.dispose();
1097                    _outputModel.dispose();
1098                    routeDirty = false;
1099                }
1100            });
1101
1102            _addFrame.pack();
1103            _inputAllButton.doClick();
1104            _outputAllButton.doClick();
1105            _alignAllButton.doClick();
1106            _newRouteButton.doClick();
1107            if (_initialize) {
1108                _initializeButton.doClick();
1109            }
1110        } else {
1111            _addFrame.setVisible(true);
1112        }
1113    }
1114
1115    private void showReminderMessage() {
1116        if (checkEnabled) {
1117            return;
1118        }
1119        InstanceManager.getDefault(UserPreferencesManager.class).
1120            showInfoMessage(Bundle.getMessage("ReminderTitle"), Bundle.getMessage("ReminderSaveString", Bundle.getMessage("BeanNameLRoute")),
1121                    getClassName(),
1122                    "remindSaveRoute"); // NOI18N
1123    }
1124
1125    /*
1126     * Utility for addPressed
1127     */
1128    private JPanel makeShowButtons(JRadioButton allButton, JRadioButton includeButton,
1129            JRadioButton extraButton, String msg) {
1130        JPanel panel = new JPanel();
1131        panel.add(new JLabel(msg));
1132        panel.add(allButton);
1133        panel.add(includeButton);
1134        ButtonGroup selGroup = new ButtonGroup();
1135        selGroup.add(allButton);
1136        selGroup.add(includeButton);
1137        if (extraButton != null) {
1138            panel.add(extraButton);
1139            selGroup.add(extraButton);
1140        }
1141        return panel;
1142    }
1143
1144    /*
1145     * Utility for addPressed
1146     */
1147    private JScrollPane makeColumns(@Nonnull JTable table, JComboBox<String> box, boolean specialBox) {
1148        table.setRowSelectionAllowed(false);
1149        //table.setPreferredScrollableViewportSize(new java.awt.Dimension(250,450));
1150        TableColumnModel columnModel = table.getColumnModel();
1151
1152        TableColumn sNameColumnT = columnModel.getColumn(RouteElementModel.SNAME_COLUMN);
1153        sNameColumnT.setResizable(true);
1154        sNameColumnT.setMinWidth(75);
1155        //sNameColumnT.setMaxWidth(110);
1156
1157        TableColumn uNameColumnT = columnModel.getColumn(RouteElementModel.UNAME_COLUMN);
1158        uNameColumnT.setResizable(true);
1159        uNameColumnT.setMinWidth(75);
1160        //uNameColumnT.setMaxWidth(260);
1161
1162        TableColumn typeColumnT = columnModel.getColumn(RouteElementModel.TYPE_COLUMN);
1163        typeColumnT.setResizable(true);
1164        typeColumnT.setMinWidth(50);
1165        //typeColumnT.setMaxWidth(110);
1166
1167        TableColumn includeColumnT = columnModel.getColumn(RouteElementModel.INCLUDE_COLUMN);
1168        includeColumnT.setResizable(false);
1169        includeColumnT.setMinWidth(30);
1170        includeColumnT.setMaxWidth(60);
1171
1172        TableColumn stateColumnT = columnModel.getColumn(RouteElementModel.STATE_COLUMN);
1173        if (specialBox) {
1174            box = new JComboBox<>();
1175            stateColumnT.setCellEditor(new ComboBoxCellEditor(box));
1176        } else {
1177            stateColumnT.setCellEditor(new DefaultCellEditor(box));
1178        }
1179        stateColumnT.setResizable(false);
1180        stateColumnT.setMinWidth(75);
1181        //stateColumnT.setMaxWidth(1310);
1182
1183        return new JScrollPane(table);
1184    }
1185
1186    /**
1187     * Initialize list of included input elements
1188     */
1189    private void initializeIncludedInputList() {
1190        _includedInputList = new ArrayList<>();
1191        for (int i = 0; i < _inputList.size(); i++) {
1192            if (_inputList.get(i).isIncluded()) {
1193                _includedInputList.add(_inputList.get(i));
1194            }
1195        }
1196    }
1197
1198    /**
1199     * Initialize list of included input elements
1200     */
1201    private void initializeIncludedOutputList() {
1202        _includedOutputList = new ArrayList<>();
1203        for (int i = 0; i < _outputList.size(); i++) {
1204            if (_outputList.get(i).isIncluded()) {
1205                _includedOutputList.add(_outputList.get(i));
1206            }
1207        }
1208    }
1209
1210    /**
1211     * Initialize list of included alignment sensors
1212     */
1213    private void initializeIncludedAlignList() {
1214        _includedAlignList = new ArrayList<>();
1215        for (int i = 0; i < _alignList.size(); i++) {
1216            if (_alignList.get(i).isIncluded()) {
1217                _includedAlignList.add(_alignList.get(i));
1218            }
1219        }
1220    }
1221
1222    private ArrayList<RouteOutputElement> makeTurnoutLockList() {
1223        ArrayList<RouteOutputElement> list = new ArrayList<>();
1224        for (int i = 0; i < _outputList.size(); i++) {
1225            if (_outputList.get(i).isIncluded()) {
1226                RouteOutputElement elt = _outputList.get(i);
1227                if ((elt.getType() == TURNOUT_TYPE) && (elt.getState() != Route.TOGGLE)) {
1228                    list.add(elt);
1229                }
1230            }
1231        }
1232        return list;
1233    }
1234
1235    private void showMessage(String msg) {
1236
1237        JmriJOptionPane.showMessageDialog(
1238                _addFrame, rbx.getString(msg), Bundle.getMessage("WarningTitle"),
1239                JmriJOptionPane.WARNING_MESSAGE);
1240    }
1241
1242    private boolean checkNewNamesOK() {
1243        // Get system name and user name
1244        String sName = _systemName.getText();
1245        if (sName.length() == 0 || sName.equals(getLogixSystemPrefix())) {
1246            showMessage("EnterNames");
1247            return false;
1248        }
1249        if (!sName.startsWith(getLogixSystemPrefix())) {
1250            sName = getLogixSystemPrefix() + sName;
1251        }
1252        // check if a Route with this system name already exists
1253        if (_logixManager.getBySystemName(sName) != null) {
1254            // Route already exists
1255            showMessage("DuplicateSys");
1256            updateButton.setVisible(true);
1257            return false;
1258        }
1259        String uName = _userName.getText();
1260        // check if a Route with the same user name exists
1261        if (!uName.isEmpty()) {
1262            if (_logixManager.getByUserName(uName) != null) {
1263                // Route with this user name already exists
1264                showMessage("DuplicateUser");
1265                updateButton.setVisible(true);
1266                return false;
1267            } else {
1268                return true;
1269            }
1270        }
1271        _systemName.setText(sName);
1272        return true;
1273    }
1274
1275    @CheckForNull
1276    private Logix checkNamesOK() {
1277        // Get system name and user name
1278        String sName = _systemName.getText();
1279        if (sName.length() == 0) {
1280            showMessage("EnterNames");
1281            return null;
1282        }
1283        Logix logix = _logixManager.getBySystemName(sName);
1284        if (!sName.startsWith(getLogixSystemPrefix())) {
1285            sName = getLogixSystemPrefix() + sName;
1286        }
1287        if (logix != null) {
1288            return logix;
1289        }
1290        String uName = _userName.getText();
1291        if (uName.length() != 0) {
1292            logix = _logixManager.getByUserName(uName);
1293            if (logix != null) {
1294                return logix;
1295            }
1296        }
1297        logix = _logixManager.createNewLogix(sName, uName);
1298        if (logix == null) {
1299            // should never get here
1300            log.error("Unknown failure to create Route with System Name: {}", sName);
1301        }
1302        return logix;
1303    }
1304
1305    private JFileChooser soundChooser = null;
1306
1307    /**
1308     * Set the sound file
1309     */
1310    private void setSoundPressed() {
1311        if (soundChooser == null) {
1312            soundChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath());
1313            soundChooser.setFileFilter(new jmri.util.NoArchiveFileFilter());
1314        }
1315        soundChooser.rescanCurrentDirectory();
1316        int retVal = soundChooser.showOpenDialog(null);
1317        // handle selection or cancel
1318        if (retVal == JFileChooser.APPROVE_OPTION) {
1319            try {
1320                soundFile.setText(FileUtil.getPortableFilename(soundChooser.getSelectedFile().getCanonicalPath()));
1321            } catch (java.io.IOException e) {
1322                log.error("exception setting sound file", e);
1323            }
1324        }
1325    }
1326
1327    private ScriptFileChooser scriptChooser = null;
1328
1329    /**
1330     * Set the script file
1331     */
1332    private void setScriptPressed() {
1333        if (scriptChooser == null) {
1334            scriptChooser = new ScriptFileChooser();
1335        }
1336        scriptChooser.rescanCurrentDirectory();
1337        int retVal = scriptChooser.showOpenDialog(null);
1338        // handle selection or cancel
1339        if (retVal == JFileChooser.APPROVE_OPTION) {
1340            try {
1341                scriptFile.setText(FileUtil.getPortableFilename(scriptChooser.getSelectedFile().getCanonicalPath()));
1342            } catch (java.io.IOException e) {
1343                log.error("exception setting script file", e);
1344            }
1345        }
1346    }
1347
1348    /**
1349     * Responds to the Add Route button.
1350     *
1351     * @param e the action event
1352     */
1353    void createPressed(ActionEvent e) {
1354        if (!checkNewNamesOK()) {
1355            return;
1356        }
1357        updatePressed();
1358    }
1359
1360    /**
1361     * Responds to the Delete button.
1362     *
1363     * @param e the action event
1364     */
1365    private void deletePressed(ActionEvent e) {
1366        Logix l = checkNamesOK();
1367        if (l != null) {
1368            l.deActivateLogix();
1369            // delete the Logix and all its Conditionals
1370            _logixManager.deleteLogix(l);
1371        }
1372        finishUpdate();
1373    }
1374
1375    /**
1376     * Update the Route Table.
1377     */
1378    private void updatePressed() {
1379        Logix logix = checkNamesOK();
1380        if (logix == null) {
1381            log.error("No Logix found!");
1382            return;
1383        }
1384        String sName = logix.getSystemName();
1385        // Check if the User Name has been changed
1386        String uName = _userName.getText();
1387        logix.setUserName(uName);
1388
1389        initializeIncludedInputList();
1390        initializeIncludedOutputList();
1391        initializeIncludedAlignList();
1392        log.debug("updatePressed: _includedInputList.size()= {}, _includedOutputList.size()= {}, _includedAlignList.size()= {}",
1393            _includedInputList.size(), _includedOutputList.size(), _includedAlignList.size());
1394
1395        ////// Construct output actions for trigger conditionals ///////////
1396        ArrayList<ConditionalAction> actionList = new ArrayList<>();
1397        for (int i = 0; i < _includedOutputList.size(); i++) {
1398            RouteOutputElement elt = _includedOutputList.get(i);
1399            String name = elt.getUserName();
1400            if (name == null || name.length() == 0) {
1401                name = elt.getSysName();
1402            }
1403            int state = elt.getState();    // actionData
1404            Conditional.Action actionType = Conditional.Action.NONE;
1405            String params = "";
1406            switch (elt.getType()) {
1407                case SENSOR_TYPE:
1408                    actionType = Conditional.Action.SET_SENSOR;
1409                    break;
1410                case TURNOUT_TYPE:
1411                    actionType = Conditional.Action.SET_TURNOUT;
1412                    break;
1413                case LIGHT_TYPE:
1414                    actionType = Conditional.Action.SET_LIGHT;
1415                    break;
1416                case SIGNAL_TYPE:
1417                    actionType = Conditional.Action.SET_SIGNAL_APPEARANCE;
1418                    if (state > OFFSET) {
1419                        actionType = Conditional.Action.getOperatorFromIntValue(state & ~OFFSET);
1420                    }
1421                    break;
1422                default:
1423                    log.debug("updatePressed: Unknown action type {}", elt.getType());
1424            }
1425            actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
1426                    actionType, name, state, params));
1427        }
1428        String file = scriptFile.getText();
1429        if (file.length() > 0) {
1430            actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
1431                    Conditional.Action.RUN_SCRIPT, "", -1, file));
1432        }
1433        file = soundFile.getText();
1434        if (file.length() > 0) {
1435            actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
1436                    Conditional.Action.PLAY_SOUND, "", -1, file));
1437        }
1438        ArrayList<ConditionalAction> onChangeList = cloneActionList(actionList, Conditional.ACTION_OPTION_ON_CHANGE);
1439
1440        /////// Construct 'AND' clause from 'VETO' controls ////////
1441        ArrayList<ConditionalVariable> vetoList = new ArrayList<>();
1442        if (!_initialize) {
1443            for (int i = 0; i < _includedInputList.size(); i++) {
1444                RouteInputElement elt = _includedInputList.get(i);
1445                String name = elt.getUserName();
1446                if (name == null || name.length() == 0) {
1447                    name = elt.getSysName();
1448                }
1449                Operator opern = ( i == 0 ? Conditional.Operator.NONE : Conditional.Operator.AND);
1450                int state = elt.getState();
1451                if (VETO < state) {
1452                    vetoList.add(new ConditionalVariable(true, opern,
1453                            Conditional.Type.getOperatorFromIntValue(state & ~VETO), name, _newRouteType));
1454                }
1455            }
1456        }
1457
1458        ///////////////// Make Trigger Conditional Controls /////////////////
1459        ArrayList<ConditionalVariable> oneTriggerList = new ArrayList<>();
1460        ArrayList<ConditionalVariable> twoTriggerList = new ArrayList<>();
1461        if (!_initialize) {
1462            for (int i = 0; i < _includedInputList.size(); i++) {
1463                RouteInputElement elt = _includedInputList.get(i);
1464                String name = elt.getUserName();
1465                if (name == null || name.length() == 0) {
1466                    name = elt.getSysName();
1467                }
1468                Operator opern = _newRouteType ? Conditional.Operator.OR : Conditional.Operator.AND;
1469                if (i == 0) {
1470                    opern = Conditional.Operator.NONE;
1471                }
1472                int type = elt.getState();
1473                if (VETO > type) {
1474                    if (Route.ONCHANGE == type) {
1475                        switch (elt.getType()) {
1476                            case SENSOR_TYPE:
1477                                type = Conditional.TYPE_SENSOR_ACTIVE;
1478                                break;
1479                            case TURNOUT_TYPE:
1480                                type = Conditional.TYPE_TURNOUT_CLOSED;
1481                                break;
1482                            case LIGHT_TYPE:
1483                                type = Conditional.TYPE_LIGHT_ON;
1484                                break;
1485                            case SIGNAL_TYPE:
1486                                type = Conditional.TYPE_SIGNAL_HEAD_LIT;
1487                                break;
1488                            default:
1489                                log.debug("updatePressed: Unknown state variable type {}", elt.getType());
1490                        }
1491                        twoTriggerList.add(new ConditionalVariable(false, opern,
1492                            Conditional.Type.getOperatorFromIntValue(type), name, true));
1493                    } else {
1494                        oneTriggerList.add(new ConditionalVariable(false, opern,
1495                            Conditional.Type.getOperatorFromIntValue(type), name, true));
1496                    }
1497                }
1498            }
1499            if (actionList.isEmpty()) {
1500                JmriJOptionPane.showMessageDialog(
1501                        _addFrame, rbx.getString("noAction"),
1502                        rbx.getString("addErr"), JmriJOptionPane.ERROR_MESSAGE);
1503                return;
1504            }
1505        } else {
1506            oneTriggerList.add(new ConditionalVariable(false, Conditional.Operator.NONE,
1507                    Conditional.Type.NONE, getLogixInitializer(), true));
1508        }
1509        log.debug("actionList.size()= {}, oneTriggerList.size()= {}, twoTriggerList.size()= {}, "
1510                + "onChangeList.size()= {}, vetoList.size()= {}",
1511            actionList.size(), oneTriggerList.size(), twoTriggerList.size(), onChangeList.size(), vetoList.size());
1512
1513        logix.deActivateLogix();
1514
1515        // remove old Conditionals for actions (ver 2.5.2 only -remove a bad idea)
1516        char[] ch = sName.toCharArray();
1517        int hash = 0;
1518        for (int i = 0; i < ch.length; i++) {
1519            hash += ch[i];
1520        }
1521        String cSystemName = getConditionalSystemPrefix() + "T" + hash;
1522        removeConditionals(cSystemName, logix);
1523        cSystemName = getConditionalSystemPrefix() + "F" + hash;
1524        removeConditionals(cSystemName, logix);
1525        cSystemName = getConditionalSystemPrefix() + "A" + hash;
1526        removeConditionals(cSystemName, logix);
1527        cSystemName = getConditionalSystemPrefix() + "L" + hash;
1528        removeConditionals(cSystemName, logix);
1529        int n = 0;
1530        do {
1531            n++;
1532            cSystemName = sName + n + "A";
1533        } while (removeConditionals(cSystemName, logix));
1534        n = 0;
1535        do {
1536            n++;
1537            cSystemName = sName + n + "T";
1538        } while (removeConditionals(cSystemName, logix));
1539        cSystemName = sName + "L";
1540        removeConditionals(cSystemName, logix);
1541
1542        int numConds = 1;
1543        if (_newRouteType) {
1544            numConds = makeRouteConditional(numConds, /*false,*/ actionList, oneTriggerList,
1545                    vetoList, logix, sName, uName, "T");
1546            if (!_initialize && !twoTriggerList.isEmpty()) {
1547                numConds = makeRouteConditional(numConds, /*true, actionList,*/ onChangeList, twoTriggerList,
1548                        null, logix, sName, uName, "T");
1549            }
1550        } else {
1551            for (int i = 0; i < oneTriggerList.size(); i++) {
1552                ArrayList<ConditionalVariable> vList = new ArrayList<>();
1553                vList.add(oneTriggerList.get(i));
1554                numConds = makeRouteConditional(numConds, /*false,*/ actionList, vList,
1555                        vetoList, logix, sName, uName, "T");
1556            }
1557            for (int i = 0; i < twoTriggerList.size(); i++) {
1558                ArrayList<ConditionalVariable> vList = new ArrayList<>();
1559                vList.add(twoTriggerList.get(i));
1560                numConds = makeRouteConditional(numConds, /*true, actionList,*/ onChangeList, vList,
1561                        vetoList, logix, sName, uName, "T");
1562            }
1563        }
1564        if (numConds == 1) {
1565            JmriJOptionPane.showMessageDialog(
1566                    _addFrame, rbx.getString("noVars"),
1567                    rbx.getString("addErr"), JmriJOptionPane.ERROR_MESSAGE);
1568            return;
1569        }
1570
1571        ///////////////// Make Alignment Conditionals //////////////////////////
1572        numConds = 1;
1573        for (int i = 0; i < _includedAlignList.size(); i++) {
1574            ArrayList<ConditionalVariable> vList = new ArrayList<>();
1575            ArrayList<ConditionalAction> aList = new ArrayList<>();
1576            AlignElement sensor = _includedAlignList.get(i);
1577            String name = sensor.getUserName();
1578            if (name == null || name.length() == 0) {
1579                name = sensor.getSysName();
1580            }
1581            aList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
1582                    Conditional.Action.SET_SENSOR, name, Sensor.ACTIVE, ""));
1583            aList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE,
1584                    Conditional.Action.SET_SENSOR, name, Sensor.INACTIVE, ""));
1585            int alignType = sensor.getState();
1586            for (int k = 0; k < _includedOutputList.size(); k++) {
1587                RouteOutputElement elt = _includedOutputList.get(k);
1588                Conditional.Type varType = Conditional.Type.NONE;
1589                boolean add = (ALL_TYPE == alignType);
1590                switch (elt.getType()) {
1591                    case SENSOR_TYPE:
1592                        if (alignType == SENSOR_TYPE) {
1593                            add = true;
1594                        }
1595                        switch (elt.getState()) {
1596                            case Sensor.INACTIVE:
1597                                varType = Conditional.Type.SENSOR_INACTIVE;
1598                                break;
1599                            case Sensor.ACTIVE:
1600                                varType = Conditional.Type.SENSOR_ACTIVE;
1601                                break;
1602                            case Route.TOGGLE:
1603                                add = false;
1604                                break;
1605                            default:
1606                                log.warn("Unexpected state {} from elt.getState() in SENSOR_TYPE", elt.getState());
1607                                break;
1608                        }
1609                        break;
1610                    case TURNOUT_TYPE:
1611                        if (alignType == TURNOUT_TYPE) {
1612                            add = true;
1613                        }
1614                        switch (elt.getState()) {
1615                            case Turnout.CLOSED:
1616                                varType = Conditional.Type.TURNOUT_CLOSED;
1617                                break;
1618                            case Turnout.THROWN:
1619                                varType = Conditional.Type.TURNOUT_THROWN;
1620                                break;
1621                            case Route.TOGGLE:
1622                                add = false;
1623                                break;
1624                            default:
1625                                log.warn("Unexpected state {} from elt.getState() in TURNOUT_TYPE", elt.getState());
1626                                break;
1627                        }
1628                        break;
1629                    case LIGHT_TYPE:
1630                        if (alignType == LIGHT_TYPE) {
1631                            add = true;
1632                        }
1633                        switch (elt.getState()) {
1634                            case Light.ON:
1635                                varType = Conditional.Type.LIGHT_ON;
1636                                break;
1637                            case Light.OFF:
1638                                varType = Conditional.Type.LIGHT_OFF;
1639                                break;
1640                            case Route.TOGGLE:
1641                                add = false;
1642                                break;
1643                            default:
1644                                log.warn("Unexpected state {} from elt.getState() in LIGHT_TYPE", elt.getState());
1645                                break;
1646                        }
1647                        break;
1648                    case SIGNAL_TYPE:
1649                        if (alignType == SIGNAL_TYPE) {
1650                            add = true;
1651                        }
1652                        switch (elt.getState()) {
1653                            case SET_SIGNAL_DARK:
1654                            case SignalHead.DARK:
1655                                varType = Conditional.Type.SIGNAL_HEAD_DARK;
1656                                break;
1657                            case SignalHead.RED:
1658                                varType = Conditional.Type.SIGNAL_HEAD_RED;
1659                                break;
1660                            case SignalHead.FLASHRED:
1661                                varType = Conditional.Type.SIGNAL_HEAD_FLASHRED;
1662                                break;
1663                            case SignalHead.YELLOW:
1664                                varType = Conditional.Type.SIGNAL_HEAD_YELLOW;
1665                                break;
1666                            case SignalHead.FLASHYELLOW:
1667                                varType = Conditional.Type.SIGNAL_HEAD_FLASHYELLOW;
1668                                break;
1669                            case SignalHead.GREEN:
1670                                varType = Conditional.Type.SIGNAL_HEAD_GREEN;
1671                                break;
1672                            case SignalHead.FLASHGREEN:
1673                                varType = Conditional.Type.SIGNAL_HEAD_FLASHGREEN;
1674                                break;
1675                            case SET_SIGNAL_HELD:
1676                                varType = Conditional.Type.SIGNAL_HEAD_HELD;
1677                                break;
1678                            case CLEAR_SIGNAL_HELD:
1679                                add = false;    // don't know how to test for this
1680                                break;
1681                            case SET_SIGNAL_LIT:
1682                                varType = Conditional.Type.SIGNAL_HEAD_LIT;
1683                                break;
1684                            default:
1685                                log.warn("Unexpected state {} from elt.getState() in SIGNAL_TYPE", elt.getState());
1686                                break;
1687                        }
1688                        break;
1689                    default:
1690                        log.debug("updatePressed: Unknown Alignment state variable type {}", elt.getType());
1691                }
1692                if (add && !_initialize) {
1693                    String eltName = elt.getUserName();
1694                    if (eltName == null || eltName.length() == 0) {
1695                        eltName = elt.getSysName();
1696                    }
1697                    vList.add(new ConditionalVariable(false, Conditional.Operator.AND,
1698                            varType, eltName, true));
1699                }
1700            }
1701            if (!vList.isEmpty()) {
1702                numConds = makeAlignConditional(numConds, aList, vList, logix, sName, uName);
1703            } else {
1704                JmriJOptionPane.showMessageDialog(
1705                        _addFrame, java.text.MessageFormat.format(rbx.getString("NoAlign"),
1706                                new Object[]{name, sensor.getAlignType()}),
1707                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
1708            }
1709        }
1710        ///////////////// Make Lock Conditional //////////////////////////
1711        if (_lock) {
1712            ArrayList<ConditionalAction> aList = new ArrayList<>();
1713            for (int k = 0; k < _includedOutputList.size(); k++) {
1714                RouteOutputElement elt = _includedOutputList.get(k);
1715                if (elt.getType() != TURNOUT_TYPE || elt.getState() == Route.TOGGLE) {
1716                    continue;
1717                }
1718                String eltName = elt.getUserName();
1719                if (eltName == null || eltName.length() == 0) {
1720                    eltName = elt.getSysName();
1721                }
1722                aList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
1723                        Conditional.Action.LOCK_TURNOUT,
1724                        eltName, Turnout.LOCKED, ""));
1725                aList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE,
1726                        Conditional.Action.LOCK_TURNOUT,
1727                        eltName, Turnout.UNLOCKED, ""));
1728            }
1729            makeRouteConditional(numConds, /*false,*/ aList, oneTriggerList,
1730                    vetoList, logix, sName, uName, "L");
1731        }
1732        log.debug("Conditionals added= {}", logix.getNumConditionals());
1733        for (int i = 0; i < logix.getNumConditionals(); i++) {
1734            log.debug("Conditional SysName= \"{}\"", logix.getConditionalByNumberOrder(i));
1735        }
1736        logix.activateLogix();
1737        log.debug("Conditionals added= {}", logix.getNumConditionals());
1738        for (int i = 0; i < logix.getNumConditionals(); i++) {
1739            log.debug("Conditional SysName= \"{}\"", logix.getConditionalByNumberOrder(i));
1740        }
1741        finishUpdate();
1742    } //updatePressed
1743
1744    private boolean removeConditionals(String cSystemName, Logix logix) {
1745        Conditional c = _conditionalManager.getBySystemName(cSystemName);
1746        if (c != null) {
1747            logix.deleteConditional(cSystemName);
1748            return true;
1749        }
1750        return false;
1751    }
1752
1753    /**
1754     * Create a new Route conditional.
1755     *
1756     * @param numConds number of existing route conditionals
1757     * @param actionList actions to take in conditional
1758     * @param triggerList triggers for conditional to take actions
1759     * @param vetoList controls that veto taking actions
1760     * @param logix Logix to add the conditional to
1761     * @param sName system name for conditional
1762     * @param uName user name for conditional
1763     * @param type type of conditional
1764     * @return number of conditionals after the creation
1765     * @throws IllegalArgumentException if "user input no good"
1766     */
1767    private int makeRouteConditional(int numConds, /*boolean onChange,*/ ArrayList<ConditionalAction> actionList,
1768            ArrayList<ConditionalVariable> triggerList, ArrayList<ConditionalVariable> vetoList,
1769            Logix logix, String sName, String uName, String type) {
1770        if (log.isDebugEnabled()) {
1771            log.debug("makeRouteConditional: numConds= {}, triggerList.size()= {}", numConds, triggerList.size());
1772        }
1773        if (triggerList.isEmpty() && (vetoList == null || vetoList.isEmpty())) {
1774            return numConds;
1775        }
1776        StringBuilder antecedent = new StringBuilder();
1777        ArrayList<ConditionalVariable> varList = new ArrayList<>();
1778
1779        int tSize = triggerList.size();
1780        if (tSize > 0) {
1781            if (tSize > 1) {
1782                antecedent.append("(");
1783            }
1784            antecedent.append("R1"); // NOI18N
1785            for (int i = 1; i < tSize; i++) {
1786                antecedent.append(" ").append(Bundle.getMessage("LogicOR")).append(" R").append(i + 1); // NOI18N
1787            }
1788            if (tSize > 1) {
1789                antecedent.append(")");
1790            }
1791            for (int i = 0; i < triggerList.size(); i++) {
1792                //varList.add(cloneVariable(triggerList.get(i)));
1793                varList.add(triggerList.get(i));
1794            }
1795        }
1796
1797        if (vetoList != null && !vetoList.isEmpty()) {
1798            int vSize = vetoList.size();
1799            if (tSize > 0) {
1800                antecedent.append(" ").append(Bundle.getMessage("LogicAND")).append(" ");
1801            }
1802            if (vSize > 1) {
1803                antecedent.append("(");
1804            }
1805            antecedent.append(Bundle.getMessage("LogicNOT")).append(" R").append(1 + tSize); // NOI18N
1806            for (int i = 1; i < vSize; i++) {
1807                antecedent.append(" ").append(Bundle.getMessage("LogicAND")).append(" ").append(Bundle.getMessage("LogicNOT")).append(" R").append(i + 1 + tSize); // NOI18N
1808            }
1809            if (vSize > 1) {
1810                antecedent.append(")");
1811            }
1812            for (int i = 0; i < vetoList.size(); i++) {
1813                //varList.add(cloneVariable(vetoList.get(i)));
1814                varList.add(vetoList.get(i));
1815            }
1816        }
1817        String cSystemName = sName + numConds + type;
1818        String cUserName = CONDITIONAL_USER_PREFIX + numConds + "C " + uName;
1819        Conditional c = null;
1820        try {
1821            c = _conditionalManager.createNewConditional(cSystemName, cUserName);
1822        } catch (Exception ex) {
1823            // user input no good
1824            handleCreateException(sName);
1825            // throw without creating any
1826            throw new IllegalArgumentException("user input no good");
1827        }
1828        c.setStateVariables(varList);
1829        //int option = onChange ? Conditional.ACTION_OPTION_ON_CHANGE : Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE;
1830        //c.setAction(cloneActionList(actionList, option));
1831        c.setAction(actionList);
1832        Conditional.AntecedentOperator logicType =
1833                _newRouteType
1834                ? Conditional.AntecedentOperator.MIXED
1835                : Conditional.AntecedentOperator.ALL_AND;
1836        c.setLogicType(logicType, antecedent.toString());
1837        logix.addConditional(cSystemName, 0);
1838        log.debug("Conditional added: SysName= \"{}\"", cSystemName);
1839        c.calculate(true, null);
1840
1841        return numConds + 1;
1842    }
1843
1844    private void handleCreateException(String sysName) {
1845        JmriJOptionPane.showMessageDialog(_addFrame,
1846                Bundle.getMessage("ErrorLRouteAddFailed", sysName) + "\n" + Bundle.getMessage("ErrorAddFailedCheck"),
1847                Bundle.getMessage("ErrorTitle"),
1848                JmriJOptionPane.ERROR_MESSAGE);
1849    }
1850
1851    /**
1852     * Create a new alignment conditional.
1853     *
1854     * @param numConds number of existing route conditionals
1855     * @param actionList actions to take in conditional
1856     * @param triggerList triggers for conditional to take actions
1857     * @param logix Logix to add the conditional to
1858     * @param sName system name for conditional
1859     * @param uName user name for conditional
1860     * @return number of conditionals after the creation
1861     * @throws IllegalArgumentException if "user input no good"
1862     */
1863    private int makeAlignConditional(int numConds, ArrayList<ConditionalAction> actionList,
1864            ArrayList<ConditionalVariable> triggerList,
1865            Logix logix, String sName, String uName) {
1866        if (triggerList.isEmpty()) {
1867            return numConds;
1868        }
1869        String cSystemName = sName + numConds + "A";
1870        String cUserName = CONDITIONAL_USER_PREFIX + numConds + "A " + uName;
1871        Conditional c = null;
1872        try {
1873            c = _conditionalManager.createNewConditional(cSystemName, cUserName);
1874        } catch (Exception ex) {
1875            // user input no good
1876            handleCreateException(sName);
1877            // throw without creating any
1878            throw new IllegalArgumentException("user input no good");
1879        }
1880        c.setStateVariables(triggerList);
1881        //c.setAction(cloneActionList(actionList, Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE));
1882        c.setAction(actionList);
1883        c.setLogicType(Conditional.AntecedentOperator.ALL_AND, "");
1884        logix.addConditional(cSystemName, 0);
1885        log.debug("Conditional added: SysName= \"{}\"", cSystemName);
1886        c.calculate(true, null);
1887        return numConds+1;
1888    }
1889
1890    @Nonnull
1891    private ArrayList<ConditionalAction> cloneActionList(ArrayList<ConditionalAction> actionList, int option) {
1892        ArrayList<ConditionalAction> list = new ArrayList<>();
1893        for (int i = 0; i < actionList.size(); i++) {
1894            ConditionalAction action = actionList.get(i);
1895            ConditionalAction clone = new DefaultConditionalAction();
1896            clone.setType(action.getType());
1897            clone.setOption(option);
1898            clone.setDeviceName(action.getDeviceName());
1899            clone.setActionData(action.getActionData());
1900            clone.setActionString(action.getActionString());
1901            list.add(clone);
1902        }
1903        return list;
1904    }
1905
1906    private void finishUpdate() {
1907        routeDirty = true;
1908        clearPage();
1909    }
1910
1911    private void clearPage() {
1912        // move to show all turnouts if not there
1913        cancelIncludedOnly();
1914        deleteButton.setVisible(false);
1915        cancelButton.setVisible(false);
1916        updateButton.setVisible(false);
1917        createButton.setVisible(false);
1918        _systemName.setText("");
1919        _userName.setText("");
1920        soundFile.setText("");
1921        scriptFile.setText("");
1922        for (int i = _inputList.size() - 1; i >= 0; i--) {
1923            _inputList.get(i).setIncluded(false);
1924        }
1925        for (int i = _outputList.size() - 1; i >= 0; i--) {
1926            _outputList.get(i).setIncluded(false);
1927        }
1928        for (int i = _alignList.size() - 1; i >= 0; i--) {
1929            _alignList.get(i).setIncluded(false);
1930        }
1931        _lock = false;
1932        _newRouteType = true;
1933        _newRouteButton.doClick();
1934        _lockCheckBox.setSelected(_lock);
1935        if (routeDirty) {
1936            showReminderMessage();
1937            routeDirty = false;
1938        }
1939        _addFrame.setVisible(false);
1940    }
1941
1942    /**
1943     * Cancels included only option
1944     */
1945    private void cancelIncludedOnly() {
1946        if (!_showAllInput) {
1947            _inputAllButton.doClick();
1948        }
1949        if (!_showAllOutput) {
1950            _outputAllButton.doClick();
1951        }
1952        if (!_showAllAlign) {
1953            _alignAllButton.doClick();
1954        }
1955    }
1956
1957////////////////////////////// Internal Utility Classes ////////////////////////////////
1958    private class ComboBoxCellEditor extends DefaultCellEditor {
1959
1960        ComboBoxCellEditor() {
1961            super(new JComboBox<String>());
1962        }
1963
1964        ComboBoxCellEditor(JComboBox<String> comboBox) {
1965            super(comboBox);
1966        }
1967
1968        @SuppressWarnings("unchecked") // getComponent call requires an unchecked cast
1969        @Override
1970        public Component getTableCellEditorComponent(JTable table, Object value,
1971                boolean isSelected, int row, int column) {
1972
1973            RouteElementModel model = (RouteElementModel) table.getModel();
1974
1975            RouteElement elt;
1976            String[] items;
1977            if (model.isInput()) {
1978                if (_showAllInput) {
1979                    elt = _inputList.get(row);
1980                } else {
1981                    elt = _includedInputList.get(row);
1982                }
1983                items = getInputComboBoxItems(elt.getType());
1984            } else {
1985                if (_showAllOutput) {
1986                    elt = _outputList.get(row);
1987                } else {
1988                    elt = _includedOutputList.get(row);
1989                }
1990                items = getOutputComboBoxItems(elt.getType());
1991            }
1992            JComboBox<String> comboBox = (JComboBox<String>) getComponent();
1993            comboBox.removeAllItems();
1994            for (String item : items) {
1995                comboBox.addItem(item);
1996            }
1997            return super.getTableCellEditorComponent(table, value, isSelected, row, column);
1998        }
1999
2000        private String[] getInputComboBoxItems(int type) {
2001            switch (type) {
2002                case SENSOR_TYPE:
2003                    return INPUT_SENSOR_STATES;
2004                case TURNOUT_TYPE:
2005                    return INPUT_TURNOUT_STATES;
2006                case LIGHT_TYPE:
2007                    return INPUT_LIGHT_STATES;
2008                case SIGNAL_TYPE:
2009                    return INPUT_SIGNAL_STATES;
2010                default:
2011                    log.warn("Unhandled object type: {}", type);
2012                    break;
2013            }
2014            return new String[]{};
2015        }
2016
2017        private String[] getOutputComboBoxItems(int type) {
2018            switch (type) {
2019                case SENSOR_TYPE:
2020                    return OUTPUT_SENSOR_STATES;
2021                case TURNOUT_TYPE:
2022                    return OUTPUT_TURNOUT_STATES;
2023                case LIGHT_TYPE:
2024                    return OUTPUT_LIGHT_STATES;
2025                case SIGNAL_TYPE:
2026                    return OUTPUT_SIGNAL_STATES;
2027                default:
2028                    log.warn("Unhandled type: {}", type);
2029            }
2030            return new String[]{};
2031        }
2032
2033    }
2034
2035    /**
2036     * Base Table model for selecting Route elements
2037     */
2038    abstract class RouteElementModel extends AbstractTableModel implements PropertyChangeListener {
2039
2040        abstract boolean isInput();
2041
2042        @Override
2043        public Class<?> getColumnClass(int c) {
2044            if (c == INCLUDE_COLUMN) {
2045                return Boolean.class;
2046            } else {
2047                return String.class;
2048            }
2049        }
2050
2051        @Override
2052        public int getColumnCount() {
2053            return 5;
2054        }
2055
2056        @Override
2057        public String getColumnName(int c) {
2058            switch (c) {
2059                case SNAME_COLUMN:
2060                    return Bundle.getMessage("ColumnSystemName");
2061                case UNAME_COLUMN:
2062                    return Bundle.getMessage("ColumnUserName");
2063                case TYPE_COLUMN:
2064                    return rbx.getString("Type");
2065                case INCLUDE_COLUMN:
2066                    return Bundle.getMessage("Include");
2067                default:
2068                    log.warn("Unhandled column type: {}", c);
2069                    break;
2070            }
2071            return "";
2072        }
2073
2074        @Override
2075        public boolean isCellEditable(int r, int c) {
2076            return ((c == INCLUDE_COLUMN) || (c == STATE_COLUMN));
2077        }
2078
2079        @Override
2080        public void propertyChange(java.beans.PropertyChangeEvent e) {
2081            if ( Manager.PROPERTY_LENGTH.equals( e.getPropertyName())) {
2082                // a new NamedBean is available in the manager
2083                fireTableDataChanged();
2084            }
2085        }
2086
2087        public void dispose() {
2088            InstanceManager.turnoutManagerInstance().removePropertyChangeListener(this);
2089        }
2090
2091        public static final int SNAME_COLUMN = 0;
2092        public static final int UNAME_COLUMN = 1;
2093        public static final int TYPE_COLUMN = 2;
2094        public static final int INCLUDE_COLUMN = 3;
2095        public static final int STATE_COLUMN = 4;
2096    }
2097
2098    /**
2099     * Table model for selecting input variables
2100     */
2101    private class RouteInputModel extends RouteElementModel {
2102
2103        @Override
2104        public boolean isInput() {
2105            return true;
2106        }
2107
2108        @Override
2109        public String getColumnName(int c) {
2110            if (c == STATE_COLUMN) {
2111                return rbx.getString("SetTrigger");
2112            }
2113            return super.getColumnName(c);
2114        }
2115
2116        @Override
2117        public int getRowCount() {
2118            if (_showAllInput) {
2119                return _inputList.size();
2120            } else {
2121                return _includedInputList.size();
2122            }
2123        }
2124
2125        @Override
2126        public Object getValueAt(int r, int c) {
2127            ArrayList<RouteInputElement> inputList;
2128            if (_showAllInput) {
2129                inputList = _inputList;
2130            } else {
2131                inputList = _includedInputList;
2132            }
2133            // some error checking
2134            if (r >= inputList.size()) {
2135                log.debug("row out of range");
2136                return null;
2137            }
2138            switch (c) {
2139                case SNAME_COLUMN:
2140                    return inputList.get(r).getSysName();
2141                case UNAME_COLUMN:
2142                    return inputList.get(r).getUserName();
2143                case TYPE_COLUMN:
2144                    return inputList.get(r).getTypeString();
2145                case INCLUDE_COLUMN:
2146                    return inputList.get(r).isIncluded();
2147                case STATE_COLUMN:
2148                    return inputList.get(r).getTestState();
2149                default:
2150                    return null;
2151            }
2152        }
2153
2154        @Override
2155        public void setValueAt(Object type, int r, int c) {
2156            ArrayList<RouteInputElement> inputList;
2157            if (_showAllInput) {
2158                inputList = _inputList;
2159            } else {
2160                inputList = _includedInputList;
2161            }
2162            switch (c) {
2163                case INCLUDE_COLUMN:
2164                    inputList.get(r).setIncluded(((Boolean) type));
2165                    break;
2166                case STATE_COLUMN:
2167                    inputList.get(r).setTestState((String) type);
2168                    break;
2169                default:
2170                    log.warn("Unexpected column {} in setValueAt", c);
2171                    break;
2172            }
2173        }
2174    }
2175
2176    /**
2177     * Table model for selecting output variables
2178     */
2179    private class RouteOutputModel extends RouteElementModel {
2180
2181        @Override
2182        public boolean isInput() {
2183            return false;
2184        }
2185
2186        @Override
2187        public String getColumnName(int c) {
2188            if (c == STATE_COLUMN) {
2189                return rbx.getString("SetAction");
2190            }
2191            return super.getColumnName(c);
2192        }
2193
2194        @Override
2195        public int getRowCount() {
2196            if (_showAllOutput) {
2197                return _outputList.size();
2198            } else {
2199                return _includedOutputList.size();
2200            }
2201        }
2202
2203        @Override
2204        public Object getValueAt(int r, int c) {
2205            ArrayList<RouteOutputElement> outputList;
2206            if (_showAllOutput) {
2207                outputList = _outputList;
2208            } else {
2209                outputList = _includedOutputList;
2210            }
2211            // some error checking
2212            if (r >= outputList.size()) {
2213                log.debug("row out of range");
2214                return null;
2215            }
2216            switch (c) {
2217                case SNAME_COLUMN:  // slot number
2218                    return outputList.get(r).getSysName();
2219                case UNAME_COLUMN:  //
2220                    return outputList.get(r).getUserName();
2221                case INCLUDE_COLUMN:
2222                    return outputList.get(r).isIncluded();
2223                case TYPE_COLUMN:
2224                    return outputList.get(r).getTypeString();
2225                case STATE_COLUMN:  //
2226                    return outputList.get(r).getSetToState();
2227                default:
2228                    return null;
2229            }
2230        }
2231
2232        @Override
2233        public void setValueAt(Object type, int r, int c) {
2234            ArrayList<RouteOutputElement> outputList;
2235            if (_showAllOutput) {
2236                outputList = _outputList;
2237            } else {
2238                outputList = _includedOutputList;
2239            }
2240            switch (c) {
2241                case INCLUDE_COLUMN:
2242                    outputList.get(r).setIncluded(((Boolean) type));
2243                    break;
2244                case STATE_COLUMN:
2245                    outputList.get(r).setSetToState((String) type);
2246                    break;
2247                default:
2248                    log.warn("Unexpected column {} in setValueAt", c);
2249                    break;
2250            }
2251        }
2252    }
2253
2254    /**
2255     * Table model for selecting output variables
2256     */
2257    private class AlignmentModel extends RouteElementModel {
2258
2259        @Override
2260        public boolean isInput() {
2261            return false;
2262        }
2263
2264        @Override
2265        public String getColumnName(int c) {
2266            if (c == STATE_COLUMN) {
2267                return rbx.getString("Alignment");
2268            }
2269            return super.getColumnName(c);
2270        }
2271
2272        @Override
2273        public int getRowCount() {
2274            if (_showAllAlign) {
2275                return _alignList.size();
2276            } else {
2277                return _includedAlignList.size();
2278            }
2279        }
2280
2281        @Override
2282        public Object getValueAt(int r, int c) {
2283            ArrayList<AlignElement> alignList;
2284            if (_showAllAlign) {
2285                alignList = _alignList;
2286            } else {
2287                alignList = _includedAlignList;
2288            }
2289            // some error checking
2290            if (r >= alignList.size()) {
2291                log.debug("row out of range");
2292                return null;
2293            }
2294            switch (c) {
2295                case SNAME_COLUMN:  // slot number
2296                    return alignList.get(r).getSysName();
2297                case UNAME_COLUMN:  //
2298                    return alignList.get(r).getUserName();
2299                case INCLUDE_COLUMN:
2300                    return alignList.get(r).isIncluded();
2301                case TYPE_COLUMN:
2302                    return Bundle.getMessage("BeanNameSensor");
2303                case STATE_COLUMN:  //
2304                    return alignList.get(r).getAlignType();
2305                default:
2306                    return null;
2307            }
2308        }
2309
2310        @Override
2311        public void setValueAt(Object type, int r, int c) {
2312            ArrayList<AlignElement> alignList;
2313            if (_showAllAlign) {
2314                alignList = _alignList;
2315            } else {
2316                alignList = _includedAlignList;
2317            }
2318            switch (c) {
2319                case INCLUDE_COLUMN:
2320                    alignList.get(r).setIncluded(((Boolean) type));
2321                    break;
2322                case STATE_COLUMN:
2323                    alignList.get(r).setAlignType((String) type);
2324                    break;
2325                default:
2326                    log.warn("Unexpected column {} in setValueAt", c);
2327                    break;
2328            }
2329        }
2330    }
2331
2332    public static final String CONDITIONAL_USER_PREFIX = "Route ";
2333
2334    public static final int SENSOR_TYPE = 1;
2335    public static final int TURNOUT_TYPE = 2;
2336    public static final int LIGHT_TYPE = 3;
2337    public static final int SIGNAL_TYPE = 4;
2338    public static final int CONDITIONAL_TYPE = 5;
2339    public static final int ALL_TYPE = 6;
2340
2341    // Should not conflict with state variable types
2342    public static final int VETO = 0x80;
2343    // due to the unecessary bit assignments in SignalHead for appearances,
2344    // offset the following
2345    public static final int OFFSET = 0x30;
2346    public static final int SET_SIGNAL_HELD = Conditional.ACTION_SET_SIGNAL_HELD + OFFSET;
2347    public static final int CLEAR_SIGNAL_HELD = Conditional.ACTION_CLEAR_SIGNAL_HELD + OFFSET;
2348    public static final int SET_SIGNAL_DARK = Conditional.ACTION_SET_SIGNAL_DARK + OFFSET;
2349    public static final int SET_SIGNAL_LIT = Conditional.ACTION_SET_SIGNAL_LIT + OFFSET;
2350
2351    private static final String ALIGN_SENSOR = rbx.getString("AlignSensor");
2352    private static final String ALIGN_TURNOUT = rbx.getString("AlignTurnout");
2353    private static final String ALIGN_LIGHT = rbx.getString("AlignLight");
2354    private static final String ALIGN_SIGNAL = rbx.getString("AlignSignal");
2355    private static final String ALIGN_ALL = rbx.getString("AlignAll");
2356
2357    private static final String ON_CHANGE = Bundle.getMessage("OnConditionChange");
2358    private static final String ON_ACTIVE = Bundle.getMessage("OnCondition")
2359        + " " + Bundle.getMessage("SensorStateActive");
2360    private static final String ON_INACTIVE = Bundle.getMessage("OnCondition")
2361        + " " + Bundle.getMessage("SensorStateInactive");
2362    private static final String VETO_ON_ACTIVE = "Veto " + Bundle.getMessage("WhenCondition")
2363        + " " + Bundle.getMessage("SensorStateActive");
2364    private static final String VETO_ON_INACTIVE = "Veto " + Bundle.getMessage("WhenCondition")
2365        + " " + Bundle.getMessage("SensorStateInactive");
2366    private static final String ON_THROWN = Bundle.getMessage("OnCondition")
2367        + " " + Bundle.getMessage("TurnoutStateThrown");
2368    private static final String ON_CLOSED = Bundle.getMessage("OnCondition")
2369        + " " + Bundle.getMessage("TurnoutStateClosed");
2370    private static final String VETO_ON_THROWN = "Veto " + Bundle.getMessage("WhenCondition")
2371        + " " + Bundle.getMessage("TurnoutStateThrown");
2372    private static final String VETO_ON_CLOSED = "Veto " + Bundle.getMessage("WhenCondition")
2373        + " " + Bundle.getMessage("TurnoutStateClosed");
2374    private static final String ON_LIT = Bundle.getMessage("OnCondition")
2375        + " " + Bundle.getMessage("ColumnHeadLit");
2376    private static final String ON_UNLIT = rbx.getString("OnUnLit");
2377    private static final String VETO_ON_LIT = "Veto " + Bundle.getMessage("WhenCondition")
2378        + " " + Bundle.getMessage("ColumnHeadLit");
2379    private static final String VETO_ON_UNLIT = rbx.getString("VetoUnLit");
2380    private static final String ON_RED = Bundle.getMessage("OnCondition")
2381        + " " + Bundle.getMessage("SignalHeadStateRed");
2382    private static final String ON_FLASHRED = Bundle.getMessage("OnCondition")
2383        + " " + Bundle.getMessage("SignalHeadStateFlashingRed");
2384    private static final String ON_YELLOW = Bundle.getMessage("OnCondition")
2385        + " " + Bundle.getMessage("SignalHeadStateYellow");
2386    private static final String ON_FLASHYELLOW = Bundle.getMessage("OnCondition")
2387        + " " + Bundle.getMessage("SignalHeadStateFlashingYellow");
2388    private static final String ON_GREEN = Bundle.getMessage("OnCondition")
2389        + " " + Bundle.getMessage("SignalHeadStateGreen");
2390    private static final String ON_FLASHGREEN = Bundle.getMessage("OnCondition")
2391        + " " + Bundle.getMessage("SignalHeadStateFlashingGreen");
2392    private static final String ON_DARK = Bundle.getMessage("OnCondition")
2393        + " " + Bundle.getMessage("SignalHeadStateDark");
2394    private static final String ON_SIGNAL_LIT = Bundle.getMessage("OnCondition")
2395        + " " + Bundle.getMessage("ColumnHeadLit");
2396    private static final String ON_SIGNAL_HELD = Bundle.getMessage("OnCondition")
2397        + " " + Bundle.getMessage("SignalHeadStateHeld");
2398    private static final String VETO_ON_RED = "Veto " + Bundle.getMessage("WhenCondition")
2399        + " " + Bundle.getMessage("SignalHeadStateRed");
2400    private static final String VETO_ON_FLASHRED = "Veto " + Bundle.getMessage("WhenCondition")
2401        + " " + Bundle.getMessage("SignalHeadStateFlashingRed");
2402    private static final String VETO_ON_YELLOW = "Veto " + Bundle.getMessage("WhenCondition")
2403        + " " + Bundle.getMessage("SignalHeadStateYellow");
2404    private static final String VETO_ON_FLASHYELLOW = "Veto " + Bundle.getMessage("WhenCondition")
2405        + " " + Bundle.getMessage("SignalHeadStateFlashingYellow");
2406    private static final String VETO_ON_GREEN = "Veto " + Bundle.getMessage("WhenCondition")
2407        + " " + Bundle.getMessage("SignalHeadStateGreen");
2408    private static final String VETO_ON_FLASHGREEN = "Veto " + Bundle.getMessage("WhenCondition")
2409        + " " + Bundle.getMessage("SignalHeadStateFlashingGreen");
2410    private static final String VETO_ON_DARK = "Veto " + Bundle.getMessage("WhenCondition")
2411        + " " + Bundle.getMessage("SignalHeadStateDark");
2412    private static final String VETO_ON_SIGNAL_LIT = "Veto " + Bundle.getMessage("WhenCondition")
2413        + " " + Bundle.getMessage("ColumnHeadLit");
2414    private static final String VETO_ON_SIGNAL_HELD = "Veto " + Bundle.getMessage("WhenCondition")
2415        + " " + Bundle.getMessage("SignalHeadStateHeld");
2416
2417    private static final String SET_TO_ACTIVE = Bundle.getMessage("SetBeanState",
2418        Bundle.getMessage("BeanNameSensor"), Bundle.getMessage("SensorStateActive"));
2419    private static final String SET_TO_INACTIVE = Bundle.getMessage("SetBeanState",
2420        Bundle.getMessage("BeanNameSensor"), Bundle.getMessage("SensorStateInactive"));
2421    private static final String SET_TO_CLOSED = Bundle.getMessage("SetBeanState",
2422        Bundle.getMessage("BeanNameTurnout"), Bundle.getMessage("TurnoutStateClosed"));
2423    private static final String SET_TO_THROWN = Bundle.getMessage("SetBeanState",
2424        Bundle.getMessage("BeanNameTurnout"), Bundle.getMessage("TurnoutStateThrown"));
2425    private static final String SET_TO_TOGGLE = Bundle.getMessage("SetBeanState",
2426        "", Bundle.getMessage("Toggle"));
2427    private static final String SET_TO_ON = Bundle.getMessage("SetBeanState",
2428        Bundle.getMessage("BeanNameLight"), Bundle.getMessage("StateOn"));
2429    private static final String SET_TO_OFF = Bundle.getMessage("SetBeanState",
2430        Bundle.getMessage("BeanNameLight"), Bundle.getMessage("StateOff"));
2431    private static final String SET_TO_DARK = Bundle.getMessage("SetBeanState",
2432        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateDark"));
2433    private static final String SET_TO_LIT = Bundle.getMessage("SetBeanState",
2434        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("ColumnHeadLit"));
2435    private static final String SET_TO_HELD = Bundle.getMessage("SetBeanState",
2436        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateHeld"));
2437    private static final String SET_TO_CLEAR = rbx.getString("SetClear");
2438    private static final String SET_TO_RED = Bundle.getMessage("SetBeanState",
2439        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateRed"));
2440    private static final String SET_TO_FLASHRED = Bundle.getMessage("SetBeanState",
2441        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateFlashingRed"));
2442    private static final String SET_TO_YELLOW = Bundle.getMessage("SetBeanState",
2443        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateYellow"));
2444    private static final String SET_TO_FLASHYELLOW = Bundle.getMessage("SetBeanState",
2445        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateFlashingYellow"));
2446    private static final String SET_TO_GREEN = Bundle.getMessage("SetBeanState",
2447        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateGreen"));
2448    private static final String SET_TO_FLASHGREEN = Bundle.getMessage("SetBeanState",
2449        Bundle.getMessage("BeanNameSignalHead"), Bundle.getMessage("SignalHeadStateFlashingGreen"));
2450
2451    private static final String[] ALIGNMENT_STATES = {
2452        ALIGN_SENSOR, ALIGN_TURNOUT, ALIGN_LIGHT, ALIGN_SIGNAL, ALIGN_ALL};
2453    private static final String[] INPUT_SENSOR_STATES = {
2454        ON_ACTIVE, ON_INACTIVE, ON_CHANGE, VETO_ON_ACTIVE, VETO_ON_INACTIVE};
2455    private static final String[] INPUT_TURNOUT_STATES = {
2456        ON_THROWN, ON_CLOSED, ON_CHANGE, VETO_ON_THROWN, VETO_ON_CLOSED};
2457    private static final String[] INPUT_LIGHT_STATES = {
2458        ON_LIT, ON_UNLIT, ON_CHANGE, VETO_ON_LIT, VETO_ON_UNLIT};
2459    private static final String[] INPUT_SIGNAL_STATES = {
2460        ON_RED, ON_FLASHRED, ON_YELLOW, ON_FLASHYELLOW, ON_GREEN,
2461        ON_FLASHGREEN, ON_DARK, ON_SIGNAL_LIT, ON_SIGNAL_HELD, VETO_ON_RED,
2462        VETO_ON_FLASHRED, VETO_ON_YELLOW, VETO_ON_FLASHYELLOW, VETO_ON_GREEN,
2463        VETO_ON_FLASHGREEN, VETO_ON_DARK, VETO_ON_SIGNAL_LIT, VETO_ON_SIGNAL_HELD};
2464    private static final String[] OUTPUT_SENSOR_STATES = {SET_TO_ACTIVE, SET_TO_INACTIVE, SET_TO_TOGGLE};
2465    private static final String[] OUTPUT_TURNOUT_STATES = {SET_TO_CLOSED, SET_TO_THROWN, SET_TO_TOGGLE};
2466    private static final String[] OUTPUT_LIGHT_STATES = {SET_TO_ON, SET_TO_OFF, SET_TO_TOGGLE};
2467    private static final String[] OUTPUT_SIGNAL_STATES = {SET_TO_DARK, SET_TO_LIT, SET_TO_HELD, SET_TO_CLEAR,
2468        SET_TO_RED, SET_TO_FLASHRED, SET_TO_YELLOW,
2469        SET_TO_FLASHYELLOW, SET_TO_GREEN, SET_TO_FLASHGREEN};
2470
2471    private static String getLogixSystemPrefix() {
2472        // Note: RouteExportToLogix uses ":RTX:" which is right?
2473        return InstanceManager.getDefault(LogixManager.class).getSystemNamePrefix() + ":RTX";
2474    }
2475
2476    // should be private or package protected, but hey, its Logix! so its public
2477    // because Logix is scattered across all of JMRI without rhyme or reason
2478    public static String getLogixInitializer() {
2479        return getLogixSystemPrefix() + "INITIALIZER";
2480    }
2481
2482    private String getConditionalSystemPrefix() {
2483        return getLogixSystemPrefix() + "C";
2484    }
2485
2486    /**
2487     * Sorts RouteElement
2488     */
2489    public static class RouteElementComparator implements java.util.Comparator<RouteElement> {
2490        // RouteElement objects aren't really NamedBeans, as they don't inherit
2491        // so we have to create our own comparator object here.  This assumes they
2492        // the do have a named-bean-like system name format.
2493        RouteElementComparator() {
2494        }
2495
2496        private static final jmri.util.AlphanumComparator ac = new jmri.util.AlphanumComparator();
2497
2498        @Override
2499        public int compare(RouteElement e1, RouteElement e2) {
2500            String s1 = e1.getSysName();
2501            String s2 = e2.getSysName();
2502
2503            int p1len = Manager.getSystemPrefixLength(s1);
2504            int p2len = Manager.getSystemPrefixLength(s2);
2505
2506            int comp = ac.compare(s1.substring(0, p1len), s2.substring(0, p2len));
2507            if (comp != 0) {
2508                return comp;
2509            }
2510
2511            char c1 = s1.charAt(p1len);
2512            char c2 = s2.charAt(p2len);
2513
2514            if (c1 == c2) {
2515                return ac.compare(s1.substring(p1len+1), s2.substring(p2len+1));
2516            } else {
2517                return (c1 > c2) ? +1 : -1 ;
2518            }
2519        }
2520
2521    }
2522
2523    /**
2524     * Base class for all the output (ConditionalAction) and input
2525     * (ConditionalVariable) elements
2526     */
2527    static class RouteElement {
2528
2529        private String _sysName;
2530        private String _userName;
2531        private int _type;
2532        private String _typeString;
2533        private boolean _included;
2534        int _state;
2535
2536        RouteElement(String sysName, String userName, int type) {
2537            _sysName = sysName;
2538            _userName = userName;
2539            _type = type;
2540            _included = false;
2541            switch (type) {
2542                case SENSOR_TYPE:
2543                    _typeString = Bundle.getMessage("BeanNameSensor");
2544                    break;
2545                case TURNOUT_TYPE:
2546                    _typeString = Bundle.getMessage("BeanNameTurnout");
2547                    break;
2548                case LIGHT_TYPE:
2549                    _typeString = Bundle.getMessage("BeanNameLight");
2550                    break;
2551                case SIGNAL_TYPE:
2552                    _typeString = Bundle.getMessage("BeanNameSignalHead");
2553                    break;
2554                case CONDITIONAL_TYPE:
2555                    _typeString = Bundle.getMessage("BeanNameConditional");
2556                    break;
2557                default:
2558                    log.warn("Unexpected type {} in RouteElement constructor", type);
2559                    break;
2560            }
2561        }
2562
2563        String getSysName() {
2564            return _sysName;
2565        }
2566
2567        String getUserName() {
2568            return _userName;
2569        }
2570
2571        int getType() {
2572            return _type;
2573        }
2574
2575        String getTypeString() {
2576            return _typeString;
2577        }
2578
2579        boolean isIncluded() {
2580            return _included;
2581        }
2582
2583        void setIncluded(boolean include) {
2584            _included = include;
2585        }
2586
2587        int getState() {
2588            return _state;
2589        }
2590
2591        final void setState(int state) {
2592            _state = state;
2593        }
2594    }
2595
2596    abstract class RouteInputElement extends RouteElement {
2597
2598        RouteInputElement(String sysName, String userName, int type) {
2599            super(sysName, userName, type);
2600        }
2601
2602        abstract String getTestState();
2603
2604        abstract void setTestState(String state);
2605    }
2606
2607    private class RouteInputSensor extends RouteInputElement {
2608
2609        RouteInputSensor(String sysName, String userName) {
2610            super(sysName, userName, SENSOR_TYPE);
2611            setState(Conditional.TYPE_SENSOR_ACTIVE);
2612        }
2613
2614        @Override
2615        String getTestState() {
2616            switch (_state) {
2617                case Conditional.TYPE_SENSOR_INACTIVE:
2618                    return ON_INACTIVE;
2619                case Conditional.TYPE_SENSOR_ACTIVE:
2620                    return ON_ACTIVE;
2621                case Route.ONCHANGE:
2622                    return ON_CHANGE;
2623                case VETO + Conditional.TYPE_SENSOR_INACTIVE:
2624                    return VETO_ON_INACTIVE;
2625                case VETO + Conditional.TYPE_SENSOR_ACTIVE:
2626                    return VETO_ON_ACTIVE;
2627                default:
2628                    log.error("Unhandled test state type: {}", _state);
2629                    break;
2630            }
2631            return "";
2632        }
2633
2634        @Override
2635        void setTestState(String state) {
2636            if (ON_INACTIVE.equals(state)) {
2637                _state = Conditional.TYPE_SENSOR_INACTIVE;
2638            } else if (ON_ACTIVE.equals(state)) {
2639                _state = Conditional.TYPE_SENSOR_ACTIVE;
2640            } else if (ON_CHANGE.equals(state)) {
2641                _state = Route.ONCHANGE;
2642            } else if (VETO_ON_INACTIVE.equals(state)) {
2643                _state = VETO + Conditional.TYPE_SENSOR_INACTIVE;
2644            } else if (VETO_ON_ACTIVE.equals(state)) {
2645                _state = VETO + Conditional.TYPE_SENSOR_ACTIVE;
2646            }
2647        }
2648    }
2649
2650    private class RouteInputTurnout extends RouteInputElement {
2651
2652        RouteInputTurnout(String sysName, String userName) {
2653            super(sysName, userName, TURNOUT_TYPE);
2654            setState(Conditional.TYPE_TURNOUT_CLOSED);
2655        }
2656
2657        @Override
2658        String getTestState() {
2659            switch (_state) {
2660                case Conditional.TYPE_TURNOUT_CLOSED:
2661                    return ON_CLOSED;
2662                case Conditional.TYPE_TURNOUT_THROWN:
2663                    return ON_THROWN;
2664                case Route.ONCHANGE:
2665                    return ON_CHANGE;
2666                case VETO + Conditional.TYPE_TURNOUT_CLOSED:
2667                    return VETO_ON_CLOSED;
2668                case VETO + Conditional.TYPE_TURNOUT_THROWN:
2669                    return VETO_ON_THROWN;
2670                default:
2671                    log.warn("Unhandled test state type: {}", _state);
2672            }
2673            return "";
2674        }
2675
2676        @Override
2677        void setTestState(String state) {
2678            if (ON_CLOSED.equals(state)) {
2679                _state = Conditional.TYPE_TURNOUT_CLOSED;
2680            } else if (ON_THROWN.equals(state)) {
2681                _state = Conditional.TYPE_TURNOUT_THROWN;
2682            } else if (ON_CHANGE.equals(state)) {
2683                _state = Route.ONCHANGE;
2684            } else if (VETO_ON_CLOSED.equals(state)) {
2685                _state = VETO + Conditional.TYPE_TURNOUT_CLOSED;
2686            } else if (VETO_ON_THROWN.equals(state)) {
2687                _state = VETO + Conditional.TYPE_TURNOUT_THROWN;
2688            }
2689        }
2690    }
2691
2692    private class RouteInputLight extends RouteInputElement {
2693
2694        RouteInputLight(String sysName, String userName) {
2695            super(sysName, userName, LIGHT_TYPE);
2696            setState(Conditional.TYPE_LIGHT_OFF);
2697        }
2698
2699        @Override
2700        String getTestState() {
2701            switch (_state) {
2702                case Conditional.TYPE_LIGHT_OFF:
2703                    return ON_UNLIT;
2704                case Conditional.TYPE_LIGHT_ON:
2705                    return ON_LIT;
2706                case Route.ONCHANGE:
2707                    return ON_CHANGE;
2708                case VETO + Conditional.TYPE_LIGHT_OFF:
2709                    return VETO_ON_UNLIT;
2710                case VETO + Conditional.TYPE_LIGHT_ON:
2711                    return VETO_ON_LIT;
2712                default:
2713                    log.warn("Unhandled test state: {}", _state);
2714                    break;
2715            }
2716            return "";
2717        }
2718
2719        @Override
2720        void setTestState(String state) {
2721            if (ON_UNLIT.equals(state)) {
2722                _state = Conditional.TYPE_LIGHT_OFF;
2723            } else if (ON_LIT.equals(state)) {
2724                _state = Conditional.TYPE_LIGHT_ON;
2725            } else if (ON_CHANGE.equals(state)) {
2726                _state = Route.ONCHANGE;
2727            } else if (VETO_ON_UNLIT.equals(state)) {
2728                _state = VETO + Conditional.TYPE_LIGHT_OFF;
2729            } else if (VETO_ON_LIT.equals(state)) {
2730                _state = VETO + Conditional.TYPE_LIGHT_ON;
2731            }
2732        }
2733    }
2734
2735    private class RouteInputSignal extends RouteInputElement {
2736
2737        RouteInputSignal(String sysName, String userName) {
2738            super(sysName, userName, SIGNAL_TYPE);
2739            setState(Conditional.TYPE_SIGNAL_HEAD_LIT);
2740        }
2741
2742        @Override
2743        String getTestState() {
2744            switch (_state) {
2745                case Conditional.TYPE_SIGNAL_HEAD_RED:
2746                    return ON_RED;
2747                case Conditional.TYPE_SIGNAL_HEAD_FLASHRED:
2748                    return ON_FLASHRED;
2749                case Conditional.TYPE_SIGNAL_HEAD_YELLOW:
2750                    return ON_YELLOW;
2751                case Conditional.TYPE_SIGNAL_HEAD_FLASHYELLOW:
2752                    return ON_FLASHYELLOW;
2753                case Conditional.TYPE_SIGNAL_HEAD_GREEN:
2754                    return ON_GREEN;
2755                case Conditional.TYPE_SIGNAL_HEAD_FLASHGREEN:
2756                    return ON_FLASHGREEN;
2757                case Conditional.TYPE_SIGNAL_HEAD_DARK:
2758                    return ON_DARK;
2759                case Conditional.TYPE_SIGNAL_HEAD_LIT:
2760                    return ON_SIGNAL_LIT;
2761                case Conditional.TYPE_SIGNAL_HEAD_HELD:
2762                    return ON_SIGNAL_HELD;
2763                case VETO + Conditional.TYPE_SIGNAL_HEAD_RED:
2764                    return VETO_ON_RED;
2765                case VETO + Conditional.TYPE_SIGNAL_HEAD_FLASHRED:
2766                    return VETO_ON_FLASHRED;
2767                case VETO + Conditional.TYPE_SIGNAL_HEAD_YELLOW:
2768                    return VETO_ON_YELLOW;
2769                case VETO + Conditional.TYPE_SIGNAL_HEAD_FLASHYELLOW:
2770                    return VETO_ON_FLASHYELLOW;
2771                case VETO + Conditional.TYPE_SIGNAL_HEAD_GREEN:
2772                    return VETO_ON_GREEN;
2773                case VETO + Conditional.TYPE_SIGNAL_HEAD_FLASHGREEN:
2774                    return VETO_ON_FLASHGREEN;
2775                case VETO + Conditional.TYPE_SIGNAL_HEAD_DARK:
2776                    return VETO_ON_DARK;
2777                case VETO + Conditional.TYPE_SIGNAL_HEAD_LIT:
2778                    return VETO_ON_SIGNAL_LIT;
2779                case VETO + Conditional.TYPE_SIGNAL_HEAD_HELD:
2780                    return VETO_ON_SIGNAL_HELD;
2781                default:
2782                    log.warn("Unhandled test state: {}", _state);
2783                    break;
2784            }
2785            return "";
2786        }
2787
2788        @Override
2789        void setTestState(String state) {
2790            if (ON_RED.equals(state)) {
2791                _state = Conditional.TYPE_SIGNAL_HEAD_RED;
2792            } else if (ON_FLASHRED.equals(state)) {
2793                _state = Conditional.TYPE_SIGNAL_HEAD_FLASHRED;
2794            } else if (ON_YELLOW.equals(state)) {
2795                _state = Conditional.TYPE_SIGNAL_HEAD_YELLOW;
2796            } else if (ON_FLASHYELLOW.equals(state)) {
2797                _state = Conditional.TYPE_SIGNAL_HEAD_FLASHYELLOW;
2798            } else if (ON_GREEN.equals(state)) {
2799                _state = Conditional.TYPE_SIGNAL_HEAD_GREEN;
2800            } else if (ON_FLASHGREEN.equals(state)) {
2801                _state = Conditional.TYPE_SIGNAL_HEAD_FLASHGREEN;
2802            } else if (ON_DARK.equals(state)) {
2803                _state = Conditional.TYPE_SIGNAL_HEAD_DARK;
2804            } else if (ON_SIGNAL_LIT.equals(state)) {
2805                _state = Conditional.TYPE_SIGNAL_HEAD_LIT;
2806            } else if (ON_SIGNAL_HELD.equals(state)) {
2807                _state = Conditional.TYPE_SIGNAL_HEAD_HELD;
2808            } else if (VETO_ON_RED.equals(state)) {
2809                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_RED;
2810            } else if (VETO_ON_FLASHRED.equals(state)) {
2811                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_FLASHRED;
2812            } else if (VETO_ON_YELLOW.equals(state)) {
2813                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_YELLOW;
2814            } else if (VETO_ON_FLASHYELLOW.equals(state)) {
2815                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_FLASHYELLOW;
2816            } else if (VETO_ON_GREEN.equals(state)) {
2817                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_GREEN;
2818            } else if (VETO_ON_FLASHGREEN.equals(state)) {
2819                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_FLASHGREEN;
2820            } else if (VETO_ON_DARK.equals(state)) {
2821                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_DARK;
2822            } else if (VETO_ON_SIGNAL_LIT.equals(state)) {
2823                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_LIT;
2824            } else if (VETO_ON_SIGNAL_HELD.equals(state)) {
2825                _state = VETO + Conditional.TYPE_SIGNAL_HEAD_HELD;
2826            }
2827        }
2828    }
2829
2830    abstract class RouteOutputElement extends RouteElement {
2831
2832        RouteOutputElement(String sysName, String userName, int type) {
2833            super(sysName, userName, type);
2834        }
2835
2836        abstract String getSetToState();
2837
2838        abstract void setSetToState(String state);
2839    }
2840
2841    private class RouteOutputSensor extends RouteOutputElement {
2842
2843        RouteOutputSensor(String sysName, String userName) {
2844            super(sysName, userName, SENSOR_TYPE);
2845            setState(Sensor.ACTIVE);
2846        }
2847
2848        @Override
2849        String getSetToState() {
2850            switch (_state) {
2851                case Sensor.INACTIVE:
2852                    return SET_TO_INACTIVE;
2853                case Sensor.ACTIVE:
2854                    return SET_TO_ACTIVE;
2855                case Route.TOGGLE:
2856                    return SET_TO_TOGGLE;
2857                default:
2858                    log.warn("Unhandled set to state: {}", _state);
2859                    break;
2860            }
2861            return "";
2862        }
2863
2864        @Override
2865        void setSetToState(String state) {
2866            if (SET_TO_INACTIVE.equals(state)) {
2867                _state = Sensor.INACTIVE;
2868            } else if (SET_TO_ACTIVE.equals(state)) {
2869                _state = Sensor.ACTIVE;
2870            } else if (SET_TO_TOGGLE.equals(state)) {
2871                _state = Route.TOGGLE;
2872            }
2873        }
2874    }
2875
2876    private class RouteOutputTurnout extends RouteOutputElement {
2877
2878        RouteOutputTurnout(String sysName, String userName) {
2879            super(sysName, userName, TURNOUT_TYPE);
2880            setState(Turnout.CLOSED);
2881        }
2882
2883        @Override
2884        String getSetToState() {
2885            switch (_state) {
2886                case Turnout.CLOSED:
2887                    return SET_TO_CLOSED;
2888                case Turnout.THROWN:
2889                    return SET_TO_THROWN;
2890                case Route.TOGGLE:
2891                    return SET_TO_TOGGLE;
2892                default:
2893                    log.warn("Unhandled set to state: {}", _state);
2894            }
2895            return "";
2896        }
2897
2898        @Override
2899        void setSetToState(String state) {
2900            if (SET_TO_CLOSED.equals(state)) {
2901                _state = Turnout.CLOSED;
2902            } else if (SET_TO_THROWN.equals(state)) {
2903                _state = Turnout.THROWN;
2904            } else if (SET_TO_TOGGLE.equals(state)) {
2905                _state = Route.TOGGLE;
2906            }
2907        }
2908    }
2909
2910    private class RouteOutputLight extends RouteOutputElement {
2911
2912        RouteOutputLight(String sysName, String userName) {
2913            super(sysName, userName, LIGHT_TYPE);
2914            setState(Light.ON);
2915        }
2916
2917        @Override
2918        String getSetToState() {
2919            switch (_state) {
2920                case Light.ON:
2921                    return SET_TO_ON;
2922                case Light.OFF:
2923                    return SET_TO_OFF;
2924                case Route.TOGGLE:
2925                    return SET_TO_TOGGLE;
2926                default:
2927                    log.warn("Unhandled set to state: {}", _state);
2928            }
2929            return "";
2930        }
2931
2932        @Override
2933        void setSetToState(String state) {
2934            if (SET_TO_ON.equals(state)) {
2935                _state = Light.ON;
2936            } else if (SET_TO_OFF.equals(state)) {
2937                _state = Light.OFF;
2938            } else if (SET_TO_TOGGLE.equals(state)) {
2939                _state = Route.TOGGLE;
2940            }
2941        }
2942    }
2943
2944    private class RouteOutputSignal extends RouteOutputElement {
2945
2946        RouteOutputSignal(String sysName, String userName) {
2947            super(sysName, userName, SIGNAL_TYPE);
2948            setState(SignalHead.RED);
2949        }
2950
2951        @Override
2952        String getSetToState() {
2953            switch (_state) {
2954                case SignalHead.DARK:
2955                    return SET_TO_DARK;
2956                case SignalHead.RED:
2957                    return SET_TO_RED;
2958                case SignalHead.FLASHRED:
2959                    return SET_TO_FLASHRED;
2960                case SignalHead.YELLOW:
2961                    return SET_TO_YELLOW;
2962                case SignalHead.FLASHYELLOW:
2963                    return SET_TO_FLASHYELLOW;
2964                case SignalHead.GREEN:
2965                    return SET_TO_GREEN;
2966                case SignalHead.FLASHGREEN:
2967                    return SET_TO_FLASHGREEN;
2968                case CLEAR_SIGNAL_HELD:
2969                    return SET_TO_CLEAR;
2970                case SET_SIGNAL_LIT:
2971                    return SET_TO_LIT;
2972                case SET_SIGNAL_HELD:
2973                    return SET_TO_HELD;
2974                default:
2975                    log.warn("Unhandled set to state: {}", _state);
2976                    break;
2977            }
2978            return "";
2979        }
2980
2981        @Override
2982        void setSetToState(String state) {
2983            if (SET_TO_DARK.equals(state)) {
2984                _state = SignalHead.DARK;
2985            } else if (SET_TO_RED.equals(state)) {
2986                _state = SignalHead.RED;
2987            } else if (SET_TO_FLASHRED.equals(state)) {
2988                _state = SignalHead.FLASHRED;
2989            } else if (SET_TO_YELLOW.equals(state)) {
2990                _state = SignalHead.YELLOW;
2991            } else if (SET_TO_FLASHYELLOW.equals(state)) {
2992                _state = SignalHead.FLASHYELLOW;
2993            } else if (SET_TO_GREEN.equals(state)) {
2994                _state = SignalHead.GREEN;
2995            } else if (SET_TO_FLASHGREEN.equals(state)) {
2996                _state = SignalHead.FLASHGREEN;
2997            } else if (SET_TO_CLEAR.equals(state)) {
2998                _state = CLEAR_SIGNAL_HELD;
2999            } else if (SET_TO_LIT.equals(state)) {
3000                _state = SET_SIGNAL_LIT;
3001            } else if (SET_TO_HELD.equals(state)) {
3002                _state = SET_SIGNAL_HELD;
3003            }
3004        }
3005    }
3006
3007    class AlignElement extends RouteElement {
3008
3009        AlignElement(String sysName, String userName) {
3010            super(sysName, userName, SENSOR_TYPE);
3011            setState(TURNOUT_TYPE);
3012        }
3013
3014        String getAlignType() {
3015            switch (_state) {
3016                case SENSOR_TYPE:
3017                    return ALIGN_SENSOR;
3018                case TURNOUT_TYPE:
3019                    return ALIGN_TURNOUT;
3020                case LIGHT_TYPE:
3021                    return ALIGN_LIGHT;
3022                case SIGNAL_TYPE:
3023                    return ALIGN_SIGNAL;
3024                case ALL_TYPE:
3025                    return ALIGN_ALL;
3026                default:
3027                    log.warn("Unhandled align type state: {}", _state);
3028                    break;
3029            }
3030            return "";
3031        }
3032
3033        void setAlignType(String state) {
3034            if (ALIGN_SENSOR.equals(state)) {
3035                _state = SENSOR_TYPE;
3036            } else if (ALIGN_TURNOUT.equals(state)) {
3037                _state = TURNOUT_TYPE;
3038            } else if (ALIGN_LIGHT.equals(state)) {
3039                _state = LIGHT_TYPE;
3040            } else if (ALIGN_SIGNAL.equals(state)) {
3041                _state = SIGNAL_TYPE;
3042            } else if (ALIGN_ALL.equals(state)) {
3043                _state = ALL_TYPE;
3044            }
3045        }
3046    }
3047
3048    @Override
3049    public void setMessagePreferencesDetails() {
3050        InstanceManager.getDefault(UserPreferencesManager.class).setPreferenceItemDetails(
3051            getClassName(), "remindSaveRoute", Bundle.getMessage("HideSaveReminder"));
3052        super.setMessagePreferencesDetails();
3053    }
3054
3055    @Override
3056    protected String getClassName() {
3057        return LRouteTableAction.class.getName();
3058    }
3059
3060    @Override
3061    public String getClassDescription() {
3062        return Bundle.getMessage("TitleLRouteTable");
3063    }
3064
3065    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LRouteTableAction.class);
3066}