001package jmri.implementation;
002
003import java.awt.event.ActionListener;
004import javax.swing.Timer;
005
006import javax.annotation.CheckForNull;
007import javax.annotation.Nonnull;
008
009import jmri.Audio;
010import jmri.Conditional;
011import jmri.ConditionalAction;
012import jmri.InstanceManager;
013import jmri.Light;
014import jmri.Memory;
015import jmri.NamedBean;
016import jmri.NamedBeanHandle;
017import jmri.Route;
018import jmri.RouteManager;
019import jmri.Sensor;
020import jmri.SignalHead;
021import jmri.Turnout;
022import jmri.jmrit.Sound;
023import jmri.jmrit.beantable.LogixTableAction;
024import jmri.jmrit.logix.OBlockManager;
025import jmri.jmrit.logix.Warrant;
026import jmri.jmrit.logix.WarrantManager;
027
028/**
029 * The consequent of the antecedent of the conditional proposition. The data for
030 * the action to be taken when a Conditional calculates to True
031 * <p>
032 * This is in the implementations package because of a Swing dependence via the
033 * times. Java 1.5 or Java 1.6 might make it possible to break that, which will
034 * simplify things.
035 *
036 * @author Pete Cressman Copyright (C) 2009, 2010, 2011
037 * @author Matthew Harris copyright (c) 2009
038 */
039public class DefaultConditionalAction implements ConditionalAction {
040
041    private int _option = Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE;
042    private Conditional.Action _type = Conditional.Action.NONE;
043    private String _deviceName = " ";
044    private int _actionData = 0;
045    private String _actionString = "";
046    private NamedBeanHandle<?> _namedBean = null;
047
048    private Timer _timer = null;
049    private ActionListener _listener = null;
050    private boolean _timerActive = false;
051    private boolean _indirectAction = false;
052    private Sound _sound = null;
053
054    static final java.util.ResourceBundle rbx = java.util.ResourceBundle.getBundle("jmri.jmrit.conditional.ConditionalBundle");
055    private final jmri.NamedBeanHandleManager nbhm = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class);
056
057    public DefaultConditionalAction() {
058    }
059
060    public DefaultConditionalAction( int option, @Nonnull Conditional.Action type,
061            String name, int actionData, String actionStr ) {
062        _option = option;
063        _type = type;
064        _deviceName = name;
065        _actionData = actionData;
066        _actionString = actionStr;
067
068        NamedBean bean = getIndirectBean(_deviceName);
069        if (bean == null) {
070            bean = getActionBean(_deviceName);
071        }
072        if (bean != null) {
073            _namedBean = nbhm.getNamedBeanHandle(_deviceName, bean);
074        } else {
075            _namedBean = null;
076        }
077    }
078
079    @Override
080    public boolean equals(Object obj) {
081        if (obj == this) {
082            return true;
083        }
084        if (obj == null) {
085            return false;
086        }
087
088        if (getClass() != obj.getClass()) {
089            return false;
090        } else {
091            DefaultConditionalAction p = (DefaultConditionalAction) obj;
092            if ((p.getOption() != this.getOption())
093                    || (p.getType() != this.getType())
094                    || (p.getActionData() != this.getActionData())) {
095                return false;
096            }
097
098            if ((p._namedBean == null && this._namedBean != null)
099                    || (p._namedBean != null && this._namedBean == null)
100                    || (p._namedBean != null && this._namedBean != null && !p._namedBean.equals(this._namedBean))) {
101                return false;
102            }
103
104            if ((p._deviceName == null && this._deviceName != null)
105                    || (p._deviceName != null && this._deviceName == null)
106                    || (p._deviceName != null && this._deviceName != null && !p._deviceName.equals(this._deviceName))) {
107                return false;
108            }
109
110            if ((p._actionString == null && this._actionString != null)
111                    || (p._actionString != null && this._actionString == null)
112                    || (p._actionString != null && this._actionString != null && !p._actionString.equals(this._actionString))) {
113                return false;
114            }
115
116        }
117        return true;
118    }
119
120    @Override
121    public int hashCode() {
122        int hash = _option * 1000 + _type.getIntValue() * 1000 * 1000 + _actionData;
123        if (_deviceName != null) {
124            hash += _deviceName.hashCode();
125        }
126        return hash;
127    }
128
129    /**
130     * If this is an indirect reference, return the Memory bean.
131     *
132     */
133    @CheckForNull
134    private Memory getIndirectBean(@CheckForNull String devName) {
135        if (devName != null && devName.length() > 0 && devName.charAt(0) == '@') {
136            String memName = devName.substring(1);
137            Memory m = InstanceManager.memoryManagerInstance().getMemory(memName);
138            if (m != null) {
139                _indirectAction = true;
140                return m;
141            }
142            log.error("\"{}\" invalid indirect memory name in action {} of type {}", devName, _actionString, _type);
143        } else {
144            _indirectAction = false;
145        }
146        return null;
147    }
148
149    /**
150     * Return the device bean that will do the action.
151     *
152     */
153    @CheckForNull
154    private NamedBean getActionBean(@CheckForNull String devName) {
155        if (devName == null) {
156            return null;
157        }
158        NamedBean bean = null;
159        try {
160            switch (_type.getItemType()) {
161                case SENSOR:
162                    try {
163                        bean = InstanceManager.sensorManagerInstance().provideSensor(devName);
164                    } catch (IllegalArgumentException e) {
165                        bean = null;
166                        log.error("invalid sensor name= \"{}\" in conditional action", devName);
167                    }
168                    break;
169                case TURNOUT:
170                    try {
171                        bean = InstanceManager.turnoutManagerInstance().provideTurnout(devName);
172                    } catch (IllegalArgumentException e) {
173                        bean = null;
174                        log.error("invalid turnout name= \"{}\" in conditional action", devName);
175                    }
176                    break;
177                case MEMORY:
178                    try {
179                        bean = InstanceManager.memoryManagerInstance().provideMemory(devName);
180                    } catch (IllegalArgumentException e) {
181                        bean = null;
182                        log.error("invalid memory name= \"{}\" in conditional action", devName);
183                    }
184                    break;
185                case LIGHT:
186                    try {
187                        bean = InstanceManager.lightManagerInstance().getLight(devName);
188                    } catch (IllegalArgumentException e) {
189                        bean = null;
190                        log.error("invalid light name= \"{}\" in conditional action", devName);
191                    }
192                    break;
193                case SIGNALMAST:
194                    try {
195                        bean = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(devName);
196                    } catch (IllegalArgumentException e) {
197                        bean = null;
198                        log.error("invalid signal mast name= \"{}\" in conditional action", devName);
199                    }
200                    break;
201                case SIGNALHEAD:
202                    try {
203                        bean = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(devName);
204                    } catch (IllegalArgumentException e) {
205                        bean = null;
206                        log.error("invalid signal head name= \"{}\" in conditional action", devName);
207                    }
208                    break;
209                case WARRANT:
210                    try {
211                        bean = InstanceManager.getDefault(WarrantManager.class).getWarrant(devName);
212                    } catch (IllegalArgumentException e) {
213                        bean = null;
214                        log.error("invalid Warrant name= \"{}\" in conditional action", devName);
215                    }
216                    break;
217                case OBLOCK:
218                    try {
219                        bean = InstanceManager.getDefault(OBlockManager.class).getOBlock(devName);
220                    } catch (IllegalArgumentException e) {
221                        bean = null;
222                        log.error("invalid OBlock name= \"{}\" in conditional action", devName);
223                    }
224                    break;
225                case ENTRYEXIT:
226                    try {
227                        bean = InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).getNamedBean(devName);
228                    } catch (IllegalArgumentException e) {
229                        bean = null;
230                        log.error("invalid NX name= \"{}\" in conditional action", devName);
231                    }
232                    break;
233                case LOGIX:
234                    try {
235                        bean = InstanceManager.getDefault(jmri.LogixManager.class).getLogix(devName);
236                    } catch (IllegalArgumentException e) {
237                        bean = null;
238                        log.error("invalid Logix name= \"{}\" in conditional action", devName);
239                    }
240                    break;
241                default:
242                    if (getType() == Conditional.Action.TRIGGER_ROUTE) {
243                        try {
244                            bean = InstanceManager.getDefault(RouteManager.class).getRoute(devName);
245                        } catch (IllegalArgumentException e) {
246                            bean = null;
247                            log.error("invalid Route name= \"{}\" in conditional action", devName);
248                        }
249                    }
250            }
251        } catch (java.lang.NumberFormatException ex) {
252            // ingonred, can be considered normal if the logixs are loaded prior to any other beans
253        }
254        return bean;
255    }
256
257    /**
258     * The consequent device or element type.
259     */
260    @Override
261    @Nonnull
262    public Conditional.Action getType() {
263        return _type;
264    }
265
266    @Override
267    public void setType(@Nonnull Conditional.Action type) {
268        _type = type;
269    }
270
271    /**
272     * Set type from user name for it.
273     */
274    @Override
275    public void setType(String type) {
276        _type = stringToActionType(type);
277    }
278
279    /**
280     * Name of the device or element that is affected.
281     */
282    @Override
283    public String getDeviceName() {
284        if (_namedBean != null) {
285            return _namedBean.getName();
286        }
287        /* As we have a trigger for something using the action, then hopefully
288         all the managers have been loaded and we can get the bean, which prevented
289         the bean from being loaded in the first place */
290        setDeviceName(_deviceName);
291        return _deviceName;
292    }
293
294    @Override
295    public void setDeviceName(String deviceName) {
296        _deviceName = deviceName;
297        NamedBean bean = getIndirectBean(_deviceName);
298        if (bean == null) {
299            bean = getActionBean(_deviceName);
300        }
301        if (bean != null) {
302            _namedBean = nbhm.getNamedBeanHandle(_deviceName, bean);
303        } else {
304            _namedBean = null;
305        }
306    }
307
308    @Override
309    @CheckForNull
310    public NamedBeanHandle<?> getNamedBean() {
311        if (_indirectAction) {
312            Memory m = (Memory) (_namedBean.getBean());
313            String actionName = (String) m.getValue();
314            NamedBean bean = getActionBean(actionName);
315            if (bean != null) {
316                return nbhm.getNamedBeanHandle(actionName, bean);
317            } else {
318                return null;
319            }
320        }
321        return _namedBean;
322    }
323
324    @Override
325    @CheckForNull
326    public NamedBean getBean() {
327        NamedBean x = beanFromHandle();
328        if (x != null) {
329            return x;
330        }
331        setDeviceName(_deviceName); //ReApply name as that will create namedBean, save replicating it here
332        return beanFromHandle();
333    }
334
335    @CheckForNull
336    private NamedBean beanFromHandle(){
337        var handle = getNamedBean();
338        if (handle != null) {
339            return handle.getBean();
340        }
341        return null;
342    }
343
344    /**
345     * Options on when action is taken.
346     */
347    @Override
348    public int getOption() {
349        return _option;
350    }
351
352    @Override
353    public void setOption(int option) {
354        _option = option;
355    }
356
357    /**
358     * Integer data for action.
359     */
360    @Override
361    public int getActionData() {
362        return _actionData;
363    }
364
365    @Override
366    public void setActionData(int actionData) {
367        _actionData = actionData;
368    }
369
370    /**
371     * Set action data from I18N name for it.
372     */
373    @Override
374    public void setActionData(String actionData) {
375        _actionData = stringToActionData(actionData);
376    }
377
378    /**
379     * String data for action.
380     */
381    @Nonnull
382    @Override
383    public String getActionString() {
384        if (_actionString == null) {
385            _actionString = getTypeString();
386        }
387        return _actionString;
388    }
389
390    @Override
391    public void setActionString(String actionString) {
392        _actionString = actionString;
393    }
394
395    /*
396     * Get timer for delays and other timed events.
397     */
398    @Override
399    @CheckForNull
400    public Timer getTimer() {
401        return _timer;
402    }
403
404    /*
405     * Set timer for delays and other timed events.
406     */
407    @Override
408    public void setTimer(Timer timer) {
409        _timer = timer;
410    }
411
412    @Override
413    public boolean isTimerActive() {
414        return _timerActive;
415    }
416
417    @Override
418    public void startTimer() {
419        if (_timer != null) {
420            _timer.start();
421            _timerActive = true;
422        } else {
423            log.error("timer is null for {} of type {}", _deviceName, getTypeString());
424        }
425    }
426
427    @Override
428    public void stopTimer() {
429        if (_timer != null) {
430            _timer.stop();
431            _timerActive = false;
432        }
433    }
434
435    /*
436     * Set listener for delays and other timed events.
437     */
438    @Override
439    @CheckForNull
440    public ActionListener getListener() {
441        return _listener;
442    }
443
444    /*
445     * set listener for delays and other timed events
446     */
447    @Override
448    public void setListener(ActionListener listener) {
449        _listener = listener;
450    }
451
452    /**
453     * Get Sound file.
454     */
455    @Override
456    @CheckForNull
457    public Sound getSound() {
458        return _sound;
459    }
460
461    /**
462     * Set the sound file.
463     *
464     * @param sound the new sound file
465     */
466    protected void setSound(Sound sound) {
467        _sound = sound;
468    }
469
470    /*
471     * Methods that return user interface strings ****
472     */
473
474    /**
475     * @return name of this consequent type
476     */
477    @Nonnull
478    @Override
479    public String getTypeString() {
480        return _type.toString();
481    }
482
483    /**
484     * @return name of the option for this consequent type
485     */
486    @Nonnull
487    @Override
488    public String getOptionString(boolean type) {
489        return getOptionString(_option, type);
490    }
491
492    @Nonnull
493    @Override
494    public String getActionDataString() {
495        return getActionDataString(_type, _actionData);
496    }
497
498    /**
499     * Convert Variable Type to Text String.
500     *
501     * @.param t the Action type
502     * @.return a human readable description of the type or an empty String
503     *./
504    public static String getItemTypeString(int t) {
505        switch (t) {
506            case Conditional.ITEM_TYPE_SENSOR:
507                return (Bundle.getMessage("BeanNameSensor"));
508            case Conditional.ITEM_TYPE_TURNOUT:
509                return (Bundle.getMessage("BeanNameTurnout"));
510            case Conditional.ITEM_TYPE_LIGHT:
511                return (Bundle.getMessage("BeanNameLight"));
512            case Conditional.ITEM_TYPE_SIGNALHEAD:
513                return (Bundle.getMessage("BeanNameSignalHead"));
514            case Conditional.ITEM_TYPE_SIGNALMAST:
515                return (Bundle.getMessage("BeanNameSignalMast"));
516            case Conditional.ITEM_TYPE_MEMORY:
517                return (Bundle.getMessage("BeanNameMemory"));
518            case Conditional.ITEM_TYPE_LOGIX:
519                return (Bundle.getMessage("BeanNameLogix"));
520            case Conditional.ITEM_TYPE_WARRANT:
521                return (Bundle.getMessage("BeanNameWarrant"));
522            case Conditional.ITEM_TYPE_OBLOCK:
523                return (Bundle.getMessage("BeanNameOBlock"));
524            case Conditional.ITEM_TYPE_ENTRYEXIT:
525                return (Bundle.getMessage("BeanNameEntryExit"));
526            case Conditional.ITEM_TYPE_CLOCK:
527                return (Bundle.getMessage("FastClock"));
528            case Conditional.ITEM_TYPE_AUDIO:
529                return (Bundle.getMessage("BeanNameAudio"));
530            case Conditional.ITEM_TYPE_SCRIPT:
531                return (Bundle.getMessage("Script"));
532            case Conditional.ITEM_TYPE_OTHER:
533                return (rbx.getString("Other"));
534            default:
535                // fall through
536                break;
537        }
538        return "";
539    }
540*/
541    /**
542     * Convert Consequent Type to text String.
543     *
544     * @.param t the Action type
545     * @.return a human readable description of the type or an empty String
546     *./
547    public static String getActionTypeString(int t) {
548        switch (t) {
549            case Conditional.ACTION_NONE:
550                return (rbx.getString("ActionNone"));
551            case Conditional.ACTION_SET_TURNOUT:
552                return (rbx.getString("ActionSetTurnout"));
553            case Conditional.ACTION_SET_SIGNAL_APPEARANCE:
554                return (rbx.getString("ActionSetSignal"));
555            case Conditional.ACTION_SET_SIGNAL_HELD:
556                return (rbx.getString("ActionSetSignalHeld"));
557            case Conditional.ACTION_CLEAR_SIGNAL_HELD:
558                return (rbx.getString("ActionClearSignalHeld"));
559            case Conditional.ACTION_SET_SIGNAL_DARK:
560                return (rbx.getString("ActionSetSignalDark"));
561            case Conditional.ACTION_SET_SIGNAL_LIT:
562                return (rbx.getString("ActionSetSignalLit"));
563            case Conditional.ACTION_TRIGGER_ROUTE:
564                return (rbx.getString("ActionTriggerRoute"));
565            case Conditional.ACTION_SET_SENSOR:
566                return (rbx.getString("ActionSetSensor"));
567            case Conditional.ACTION_DELAYED_SENSOR:
568                return (rbx.getString("ActionDelayedSensor"));
569            case Conditional.ACTION_SET_LIGHT:
570                return (rbx.getString("ActionSetLight"));
571            case Conditional.ACTION_SET_MEMORY:
572                return (rbx.getString("ActionSetMemory"));
573            case Conditional.ACTION_ENABLE_LOGIX:
574                return (rbx.getString("ActionEnableLogix"));
575            case Conditional.ACTION_DISABLE_LOGIX:
576                return (rbx.getString("ActionDisableLogix"));
577            case Conditional.ACTION_PLAY_SOUND:
578                return (rbx.getString("ActionPlaySound"));
579            case Conditional.ACTION_RUN_SCRIPT:
580                return (rbx.getString("ActionRunScript"));
581            case Conditional.ACTION_DELAYED_TURNOUT:
582                return (rbx.getString("ActionDelayedTurnout"));
583            case Conditional.ACTION_LOCK_TURNOUT:
584                return (rbx.getString("ActionTurnoutLock"));
585            case Conditional.ACTION_RESET_DELAYED_SENSOR:
586                return (rbx.getString("ActionResetDelayedSensor"));
587            case Conditional.ACTION_CANCEL_SENSOR_TIMERS:
588                return (rbx.getString("ActionCancelSensorTimers"));
589            case Conditional.ACTION_RESET_DELAYED_TURNOUT:
590                return (rbx.getString("ActionResetDelayedTurnout"));
591            case Conditional.ACTION_CANCEL_TURNOUT_TIMERS:
592                return (rbx.getString("ActionCancelTurnoutTimers"));
593            case Conditional.ACTION_SET_FAST_CLOCK_TIME:
594                return (rbx.getString("ActionSetFastClockTime"));
595            case Conditional.ACTION_START_FAST_CLOCK:
596                return (rbx.getString("ActionStartFastClock"));
597            case Conditional.ACTION_STOP_FAST_CLOCK:
598                return (rbx.getString("ActionStopFastClock"));
599            case Conditional.ACTION_COPY_MEMORY:
600                return (rbx.getString("ActionCopyMemory"));
601            case Conditional.ACTION_SET_LIGHT_INTENSITY:
602                return (rbx.getString("ActionSetLightIntensity"));
603            case Conditional.ACTION_SET_LIGHT_TRANSITION_TIME:
604                return (rbx.getString("ActionSetLightTransitionTime"));
605            case Conditional.ACTION_CONTROL_AUDIO:
606                return (rbx.getString("ActionControlAudio"));
607            case Conditional.ACTION_JYTHON_COMMAND:
608                return (rbx.getString("ActionJythonCommand"));
609            case Conditional.ACTION_ALLOCATE_WARRANT_ROUTE:
610                return (rbx.getString("ActionAllocateWarrant"));
611            case Conditional.ACTION_DEALLOCATE_WARRANT_ROUTE:
612                return (rbx.getString("ActionDeallocateWarrant"));
613            case Conditional.ACTION_SET_ROUTE_TURNOUTS:
614                return (rbx.getString("ActionSetWarrantTurnouts"));
615            case Conditional.ACTION_AUTO_RUN_WARRANT:
616                return (rbx.getString("ActionAutoRunWarrant"));
617            case Conditional.ACTION_MANUAL_RUN_WARRANT:
618                return (rbx.getString("ActionManualRunWarrant"));
619            case Conditional.ACTION_CONTROL_TRAIN:
620                return (rbx.getString("ActionControlTrain"));
621            case Conditional.ACTION_SET_TRAIN_ID:
622                return (rbx.getString("ActionSetTrainId"));
623            case Conditional.ACTION_SET_TRAIN_NAME:
624                return (rbx.getString("ActionSetTrainName"));
625            case Conditional.ACTION_SET_SIGNALMAST_ASPECT:
626                return (rbx.getString("ActionSetSignalMastAspect"));
627            case Conditional.ACTION_THROTTLE_FACTOR:
628                return (rbx.getString("ActionSetThrottleFactor"));
629            case Conditional.ACTION_SET_SIGNALMAST_HELD:
630                return (rbx.getString("ActionSetSignalMastHeld"));
631            case Conditional.ACTION_CLEAR_SIGNALMAST_HELD:
632                return (rbx.getString("ActionClearSignalMastHeld"));
633            case Conditional.ACTION_SET_SIGNALMAST_DARK:
634                return (rbx.getString("ActionSetSignalMastDark"));
635            case Conditional.ACTION_SET_SIGNALMAST_LIT:
636                return (rbx.getString("ActionClearSignalMastDark"));
637            case Conditional.ACTION_SET_BLOCK_VALUE:
638                return (rbx.getString("ActionSetBlockValue"));
639            case Conditional.ACTION_SET_BLOCK_ERROR:
640                return (rbx.getString("ActionSetBlockError"));
641            case Conditional.ACTION_CLEAR_BLOCK_ERROR:
642                return (rbx.getString("ActionClearBlockError"));
643            case Conditional.ACTION_DEALLOCATE_BLOCK:
644                return (rbx.getString("ActionDeallocateBlock"));
645            case Conditional.ACTION_SET_BLOCK_OUT_OF_SERVICE:
646                return (rbx.getString("ActionSetBlockOutOfService"));
647            case Conditional.ACTION_SET_BLOCK_IN_SERVICE:
648                return (rbx.getString("ActionBlockInService"));
649            case Conditional.ACTION_SET_NXPAIR_ENABLED:
650                return (rbx.getString("ActionNXPairEnabled"));
651            case Conditional.ACTION_SET_NXPAIR_DISABLED:
652                return (rbx.getString("ActionNXPairDisabled"));
653            case Conditional.ACTION_SET_NXPAIR_SEGMENT:
654                return (rbx.getString("ActionNXPairSegment"));
655            default:
656                // fall through
657                break;
658        }
659        log.warn("Unexpected parameter to getActionTypeString({})", t);
660        return ("");
661    }
662*/
663    /**
664     * Convert consequent option to I18N String.
665     *
666     * @param opt  the option.
667     * @param type true if option is a change; false if option is a trigger.
668     * @return a human readable description of the option or an empty String.
669     */
670    @Nonnull
671    public static String getOptionString(int opt, boolean type) {
672        switch (opt) {
673            case Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE:
674                if (type) {
675                    return (rbx.getString("OnChangeToTrue"));
676                } else {
677                    return (rbx.getString("OnTriggerToTrue"));
678                }
679            case Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE:
680                if (type) {
681                    return (rbx.getString("OnChangeToFalse"));
682                } else {
683                    return (rbx.getString("OnTriggerToFalse"));
684                }
685            case Conditional.ACTION_OPTION_ON_CHANGE:
686                if (type) {
687                    return (rbx.getString("OnChange"));
688                } else {
689                    return (rbx.getString("OnTrigger"));
690                }
691            default:
692                // fall through
693                break;
694        }
695        log.warn("Unexpected parameter to getOptionString({})", opt);
696        return "";
697    }
698
699    /**
700     * Get action type from a String.
701     *
702     * @param str the string to get the type for
703     * @return the type or 0 if str is not a recognized action
704     */
705    @Nonnull
706    public static Conditional.Action stringToActionType(@CheckForNull String str) {
707        if (str != null) {
708            for (Conditional.Action action : Conditional.Action.values()) {
709                if (str.equals(action.toString())) {
710                    return action;
711                }
712            }
713        }
714        log.warn("Unexpected parameter to stringToActionType({})", str);
715        return Conditional.Action.NONE;
716    }
717
718    /**
719     * Get action Data from an I18N String.
720     *
721     * @param str the I18N string to get the action data for.
722     * @return the action data of -1 is str is not recognized.
723     */
724    public static int stringToActionData(@Nonnull String str) {
725        if (str.equals(Bundle.getMessage("TurnoutStateClosed"))) {
726            return Turnout.CLOSED;
727        } else if (str.equals(Bundle.getMessage("TurnoutStateThrown"))) {
728            return Turnout.THROWN;
729        } else if (str.equals(Bundle.getMessage("SensorStateActive"))) {
730            return Sensor.ACTIVE;
731        } else if (str.equals(Bundle.getMessage("SensorStateInactive"))) {
732            return Sensor.INACTIVE;
733        } else if (str.equals(rbx.getString("LightOn"))) {
734            return Light.ON;
735        } else if (str.equals(rbx.getString("LightOff"))) {
736            return Light.OFF;
737        } else if (str.equals(rbx.getString("TurnoutUnlock"))) {
738            return Turnout.UNLOCKED;
739        } else if (str.equals(rbx.getString("TurnoutLock"))) {
740            return Turnout.LOCKED;
741        } else if (str.equals(Bundle.getMessage("SignalHeadStateRed"))) {
742            return SignalHead.RED;
743        } else if (str.equals(Bundle.getMessage("SignalHeadStateYellow"))) {
744            return SignalHead.YELLOW;
745        } else if (str.equals(Bundle.getMessage("SignalHeadStateGreen"))) {
746            return SignalHead.GREEN;
747        } else if (str.equals(Bundle.getMessage("SignalHeadStateDark"))) {
748            return SignalHead.DARK;
749        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingRed"))) {
750            return SignalHead.FLASHRED;
751        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingYellow"))) {
752            return SignalHead.FLASHYELLOW;
753        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingGreen"))) {
754            return SignalHead.FLASHGREEN;
755        } else if (str.equals(Bundle.getMessage("SignalHeadStateLunar"))) {
756            return SignalHead.LUNAR;
757        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingLunar"))) {
758            return SignalHead.FLASHLUNAR;
759        } else if (str.equals(rbx.getString("AudioSourcePlay"))) {
760            return Audio.CMD_PLAY;
761        } else if (str.equals(rbx.getString("AudioSourceStop"))) {
762            return Audio.CMD_STOP;
763        } else if (str.equals(rbx.getString("AudioSourcePlayToggle"))) {
764            return Audio.CMD_PLAY_TOGGLE;
765        } else if (str.equals(rbx.getString("AudioSourcePause"))) {
766            return Audio.CMD_PAUSE;
767        } else if (str.equals(rbx.getString("AudioSourceResume"))) {
768            return Audio.CMD_RESUME;
769        } else if (str.equals(rbx.getString("AudioSourcePauseToggle"))) {
770            return Audio.CMD_PAUSE_TOGGLE;
771        } else if (str.equals(rbx.getString("AudioSourceRewind"))) {
772            return Audio.CMD_REWIND;
773        } else if (str.equals(rbx.getString("AudioSourceFadeIn"))) {
774            return Audio.CMD_FADE_IN;
775        } else if (str.equals(rbx.getString("AudioSourceFadeOut"))) {
776            return Audio.CMD_FADE_OUT;
777        } else if (str.equals(rbx.getString("AudioResetPosition"))) {
778            return Audio.CMD_RESET_POSITION;
779        }
780        // empty strings can occur frequently with types that have no integer data
781        if (str.length() > 0) {
782            log.warn("Unexpected parameter to stringToActionData({})", str);
783        }
784        return -1;
785    }
786
787    /**
788     * Get the Action Data String in I18N form.
789     * @param t the Condition.Action, must be null.
790     * @param data the constant for the action command type.
791     * @return the action / data string.
792     */
793    @Nonnull
794    public static String getActionDataString(@Nonnull Conditional.Action t, int data) {
795        switch (t) {
796            case SET_TURNOUT:
797            case DELAYED_TURNOUT:
798            case RESET_DELAYED_TURNOUT:
799                switch (data) {
800                    case Turnout.CLOSED:
801                        return (Bundle.getMessage("TurnoutStateClosed"));
802                    case Turnout.THROWN:
803                        return (Bundle.getMessage("TurnoutStateThrown"));
804                    case Route.TOGGLE:
805                        return (Bundle.getMessage("Toggle"));
806                    default:
807                        break;
808                }
809                break;
810            case SET_SIGNAL_APPEARANCE:
811                return DefaultSignalHead.getDefaultStateName(data);
812            case SET_SENSOR:
813            case DELAYED_SENSOR:
814            case RESET_DELAYED_SENSOR:
815                switch (data) {
816                    case Sensor.ACTIVE:
817                        return (Bundle.getMessage("SensorStateActive"));
818                    case Sensor.INACTIVE:
819                        return (Bundle.getMessage("SensorStateInactive"));
820                    case Route.TOGGLE:
821                        return (Bundle.getMessage("Toggle"));
822                    default:
823                        break;
824                }
825                break;
826            case SET_LIGHT:
827                switch (data) {
828                    case Light.ON:
829                        return (rbx.getString("LightOn"));
830                    case Light.OFF:
831                        return (rbx.getString("LightOff"));
832                    case Route.TOGGLE:
833                        return (Bundle.getMessage("Toggle"));
834                    default:
835                        break;
836                }
837                break;
838            case LOCK_TURNOUT:
839                switch (data) {
840                    case Turnout.UNLOCKED:
841                        return (rbx.getString("TurnoutUnlock"));
842                    case Turnout.LOCKED:
843                        return (rbx.getString("TurnoutLock"));
844                    case Route.TOGGLE:
845                        return (Bundle.getMessage("Toggle"));
846                    default:
847                        break;
848                }
849                break;
850            case CONTROL_AUDIO:
851                switch (data) {
852                    case Audio.CMD_PLAY:
853                        return (rbx.getString("AudioSourcePlay"));
854                    case Audio.CMD_STOP:
855                        return (rbx.getString("AudioSourceStop"));
856                    case Audio.CMD_PLAY_TOGGLE:
857                        return (rbx.getString("AudioSourcePlayToggle"));
858                    case Audio.CMD_PAUSE:
859                        return (rbx.getString("AudioSourcePause"));
860                    case Audio.CMD_RESUME:
861                        return (rbx.getString("AudioSourceResume"));
862                    case Audio.CMD_PAUSE_TOGGLE:
863                        return (rbx.getString("AudioSourcePauseToggle"));
864                    case Audio.CMD_REWIND:
865                        return (rbx.getString("AudioSourceRewind"));
866                    case Audio.CMD_FADE_IN:
867                        return (rbx.getString("AudioSourceFadeIn"));
868                    case Audio.CMD_FADE_OUT:
869                        return (rbx.getString("AudioSourceFadeOut"));
870                    case Audio.CMD_RESET_POSITION:
871                        return (rbx.getString("AudioResetPosition"));
872                    default:
873                        log.error("Unhandled Audio operation command: {}", data);
874                        break;
875                }
876                break;
877            case CONTROL_TRAIN:
878                switch (data) {
879                    case Warrant.HALT:
880                        return (rbx.getString("WarrantHalt"));
881                    case Warrant.RESUME:
882                        return (rbx.getString("WarrantResume"));
883                    case Warrant.RETRY_FWD:
884                        return (rbx.getString("WarrantMoveToNext"));
885                    case Warrant.SPEED_UP:
886                        return (rbx.getString("WarrantSpeedUp"));
887                    case Warrant.STOP:
888                        return (rbx.getString("WarrantStop"));
889                    case Warrant.ESTOP:
890                        return (rbx.getString("WarrantEStop"));
891                    case Warrant.ABORT:
892                        return (rbx.getString("WarrantAbort"));
893                    default:
894                        log.error("Unhandled Warrant control: {}", data);
895                }
896                break;
897            default:
898                // fall through
899                break;
900        }
901        return "";
902    }
903
904    /**
905     * {@inheritDoc }
906     */
907    @Nonnull
908    @Override
909    public String description(boolean triggerType) {
910        String str = getOptionString(triggerType) + ", " + getTypeString();
911        if (_deviceName.length() > 0) {
912            switch (_type) {
913                case CANCEL_TURNOUT_TIMERS:
914                case SET_SIGNAL_HELD:
915                case CLEAR_SIGNAL_HELD:
916                case SET_SIGNAL_DARK:
917                case SET_SIGNAL_LIT:
918                case TRIGGER_ROUTE:
919                case CANCEL_SENSOR_TIMERS:
920                case SET_MEMORY:
921                case ENABLE_LOGIX:
922                case DISABLE_LOGIX:
923                case COPY_MEMORY:
924                case SET_LIGHT_INTENSITY:
925                case SET_LIGHT_TRANSITION_TIME:
926                case ALLOCATE_WARRANT_ROUTE:
927                case DEALLOCATE_WARRANT_ROUTE:
928                case SET_SIGNALMAST_HELD:
929                case CLEAR_SIGNALMAST_HELD:
930                case SET_SIGNALMAST_DARK:
931                case SET_SIGNALMAST_LIT:
932                case SET_BLOCK_ERROR:
933                case CLEAR_BLOCK_ERROR:
934                case DEALLOCATE_BLOCK:
935                case SET_BLOCK_OUT_OF_SERVICE:
936                case SET_BLOCK_IN_SERVICE:
937                    str = str + ", \"" + _deviceName + "\".";
938                    break;
939                case SET_NXPAIR_ENABLED:
940                case SET_NXPAIR_DISABLED:
941                case SET_NXPAIR_SEGMENT:
942                    NamedBean bean = getBean();
943                    str = str + ", \"" + ( bean == null ? "<NULL>" : bean.getUserName() )+ "\".";
944                    break;
945                case SET_ROUTE_TURNOUTS:
946                case AUTO_RUN_WARRANT:
947                case MANUAL_RUN_WARRANT:
948                    str = str + " " + rbx.getString("onWarrant") + ", \"" + _deviceName + "\".";
949                    break;
950                case SET_SENSOR:
951                case SET_TURNOUT:
952                case SET_LIGHT:
953                case LOCK_TURNOUT:
954                case RESET_DELAYED_SENSOR:
955                case SET_SIGNAL_APPEARANCE:
956                case RESET_DELAYED_TURNOUT:
957                case DELAYED_TURNOUT:
958                case DELAYED_SENSOR:
959                case CONTROL_AUDIO:
960                    str = str + ", \"" + _deviceName + "\" " + rbx.getString("to")
961                            + " " + getActionDataString();
962                    break;
963                case GET_TRAIN_LOCATION:
964                case GET_BLOCK_WARRANT:
965                case GET_BLOCK_TRAIN_NAME:
966                    str = str + " \"" + _deviceName + "\" " + rbx.getString("intoMemory")
967                              + " " + _actionString;
968                    break;
969                case SET_SIGNALMAST_ASPECT:
970                    str = str + ", \"" + _deviceName + "\" " + rbx.getString("to")
971                            + " " + _actionString;
972                    break;
973                case CONTROL_TRAIN:
974                    str = str + " " + rbx.getString("onWarrant") + " \"" + _deviceName + "\" "
975                            + rbx.getString("to") + " " + getActionDataString();
976                    break;
977                default:
978                    break; // nothing needed for others
979            }
980        }
981        if (_actionString.length() > 0) {
982            switch (_type) {
983                case SET_MEMORY:
984                case COPY_MEMORY:
985                    str = str + " " + rbx.getString("to") + " " + _actionString + ".";
986                    break;
987                case PLAY_SOUND:
988                case RUN_SCRIPT:
989                    str = str + " " + rbx.getString("FromFile") + " " + _actionString + ".";
990                    break;
991                case RESET_DELAYED_TURNOUT:
992                case RESET_DELAYED_SENSOR:
993                case DELAYED_TURNOUT:
994                case DELAYED_SENSOR:
995                    str = str + rbx.getString("After") + " ";
996                    try {
997                        Float.valueOf(_actionString);
998                        str = str + _actionString + " " + rbx.getString("Seconds") + ".";
999                    } catch (NumberFormatException nfe) {
1000                        str = str + _actionString + " " + rbx.getString("ValueInMemory")
1001                                + " " + rbx.getString("Seconds") + ".";
1002                    }
1003                    break;
1004                case SET_LIGHT_TRANSITION_TIME:
1005                case SET_LIGHT_INTENSITY:
1006                    try {
1007                        //int t = Integer.parseInt(_actionString);
1008                        str = str + " " + rbx.getString("to") + " " + _actionString + ".";
1009                    } catch (NumberFormatException nfe) {
1010                        str = str + " " + rbx.getString("to") + " " + _actionString + " "
1011                                + rbx.getString("ValueInMemory") + ".";
1012                    }
1013                    break;
1014                case JYTHON_COMMAND:
1015                    str = str + " " + rbx.getString("ExecJythonCmd") + " " + _actionString + ".";
1016                    break;
1017                case SET_TRAIN_ID:
1018                case SET_TRAIN_NAME:
1019                    str = str + ", \"" + _actionString + "\" " + rbx.getString("onWarrant")
1020                            + " \"" + _deviceName + "\".";
1021                    break;
1022                case SET_BLOCK_VALUE:
1023                    str = str + ", \"" + _actionString + "\" " + rbx.getString("onBlock")
1024                            + " \"" + _deviceName + "\".";
1025                    break;
1026                default:
1027                    break; // nothing needed for others
1028            }
1029        }
1030        switch (_type) {
1031            case SET_LIGHT_INTENSITY:
1032            case SET_LIGHT_TRANSITION_TIME:
1033                str = str + " " + rbx.getString("to") + " " + _actionData + ".";
1034                break;
1035            case SET_FAST_CLOCK_TIME:
1036                str = str + " " + rbx.getString("to") + " "
1037                        + LogixTableAction.formatTime(_actionData / 60, _actionData - ((_actionData / 60) * 60));
1038                break;
1039            default:
1040                break; // nothing needed for others
1041        }
1042        return str;
1043    }
1044
1045    /** {@inheritDoc} */
1046    @Override
1047    public void dispose() {
1048        if (_sound != null) {
1049            _sound.dispose();
1050        }
1051    }
1052
1053    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultConditionalAction.class);
1054
1055}