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