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