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