001package jmri;
002
003import java.util.Date;
004import java.util.ResourceBundle;
005import jmri.Conditional.Operator;
006import jmri.jmrit.beantable.LogixTableAction;
007import jmri.jmrit.logix.OBlock;
008import jmri.jmrit.logix.Warrant;
009import jmri.jmrit.logix.WarrantManager;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 * The variable used in the antecedent (the 'if' part) of the Conditional.
015 * proposition. The states of ConditionalVariables and logic expression of the
016 * antecedent determine the state of the Conditional.
017 * <p>
018 * ConditionalVariable objects are fully mutable, so use the default equals()
019 * operator that checks for identical objects, not identical contents.
020 *
021 * This file is part of JMRI.
022 * <p>
023 * JMRI is free software; you can redistribute it and/or modify it under the
024 * terms of version 2 of the GNU General Public License as published by the Free
025 * Software Foundation. See the "COPYING" file for a copy of this license.
026 * <p>
027 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
028 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
029 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
030 *
031 * @author Pete Cressman Copyright (C) 2009
032 * @author Bob Jacobsen Copyright (C) 2016
033 */
034public class ConditionalVariable {
035
036    static final ResourceBundle rbx = ResourceBundle.getBundle("jmri.jmrit.conditional.ConditionalBundle");
037
038    public static final int NUM_COMPARE_OPERATIONS = 5;
039    public static final int LESS_THAN = 1;
040    public static final int LESS_THAN_OR_EQUAL = 2;
041    public static final int EQUAL = 3;
042    public static final int GREATER_THAN_OR_EQUAL = 4;
043    public static final int GREATER_THAN = 5;
044
045    private boolean _not = false;
046    // Not a variable attribute, but retained as an artifact of previous releases.  This will be used
047    // as the default operator immediately to the left of this variable in the antecedent statement.
048    // It may be over written by the antecedent statement in the Conditional to which this variable
049    // belongs.
050    private Operator _opern = Operator.NONE;
051    private Conditional.Type _type = Conditional.Type.NONE;
052    private String _name = "";
053    private String _dataString = "";
054    private int _num1 = 0;
055    private int _num2 = 0;
056    private String _guiName = "";       // Contains the user name of the referenced conditional
057    private NamedBeanHandle<?> _namedBean = null;
058    //private NamedBeanHandle<Sensor> _namedSensorBean = null;
059    protected jmri.NamedBeanHandleManager nbhm = jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class);
060    // Name clarification: Formerly was named '_triggersCalculation' because it controlled whether
061    // a listener was installed for this device and thus trigger calculation of the Conditional.
062    // Now named '_triggersActions' because listeners are always installed for activated Logix
063    // Conditionals and this parameter nows controls whether, if its change of state changes the
064    // state of the conditional, should that also  trigger the actions.
065    private boolean _triggersActions = true;
066    private int _state = NamedBean.UNKNOWN;        // tri-state
067
068    /**
069     * Create a blank ConditionalVariable, to be filled in later.
070     */
071    public ConditionalVariable() {
072    }
073
074    /**
075     * Create a ConditionalVariable with a set of given properties.
076     * @param not true if the ConditionalVariable should be negated
077     * @param opern the boolean operator for this ConditionalVariable
078     * @param type the type this ConditionalVariable operates on (Turnout, Sensor, ...)
079     * @param name the device name
080     * @param trigger true if actions should be performed if triggered
081     */
082    public ConditionalVariable(boolean not, Operator opern, Conditional.Type type, String name, boolean trigger) {
083        _not = not;
084        // setOpern does some checks of opern
085        _opern = opern;
086        _type = type;
087        _name = name;
088        _triggersActions = trigger;
089        _guiName = "";
090        try {
091            Conditional.ItemType itemType = type.getItemType();
092            switch (itemType) {
093                case SENSOR:
094                    try {
095                        Sensor sn = InstanceManager.sensorManagerInstance().provideSensor(_name);
096                        _namedBean = nbhm.getNamedBeanHandle(_name, sn);
097                    } catch (IllegalArgumentException e) {
098                        log.error("invalid sensor name= \"{}\" in state variable", _name);
099                    }
100                    break;
101                case TURNOUT:
102                    try {
103                        Turnout tn = InstanceManager.turnoutManagerInstance().provideTurnout(_name);
104                        _namedBean = nbhm.getNamedBeanHandle(_name, tn);
105                    } catch (IllegalArgumentException e) {
106                        log.error("invalid turnout name= \"{}\" in state variable", _name);
107                    }
108                    break;
109                case MEMORY:
110                    try {
111                        Memory my = InstanceManager.memoryManagerInstance().provideMemory(_name);
112                        _namedBean = nbhm.getNamedBeanHandle(_name, my);
113                    } catch (IllegalArgumentException e) {
114                        log.error("invalid memory name= \"{}\" in state variable", _name);
115                    }
116                    break;
117                case LIGHT:
118                    try {
119                        Light l = InstanceManager.lightManagerInstance().provideLight(_name);
120                        _namedBean = nbhm.getNamedBeanHandle(_name, l);
121                    } catch (IllegalArgumentException e) {
122                        log.error("invalid light name= \"{}\" in state variable", _name);
123                    }
124                    break;
125                case SIGNALHEAD:
126                    SignalHead s = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(_name);
127                    if (s == null) {
128                        log.error("invalid signalhead name= \"{}\" in state variable", _name);
129                        return;
130                    }
131                    _namedBean = nbhm.getNamedBeanHandle(_name, s);
132                    break;
133                case SIGNALMAST:
134                    try {
135                        SignalMast sm = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(_name);
136                        _namedBean = nbhm.getNamedBeanHandle(_name, sm);
137                    } catch (IllegalArgumentException e) {
138                        log.error("invalid signalmast name= \"{}\" in state variable", _name);
139                    }
140                    break;
141                case ENTRYEXIT:
142                    NamedBean nb = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).getBySystemName(_name);
143                    if (nb == null) {
144                        log.error("invalid entry exit name= \"{}\" in state variable", _name);
145                        return;
146                    }
147                    _namedBean = nbhm.getNamedBeanHandle(_name, nb);
148                    break;
149                case CONDITIONAL:
150                    Conditional c = InstanceManager.getDefault(jmri.ConditionalManager.class).getConditional(_name);
151                    if (c == null) {
152                        log.error("invalid conditional; name= \"{}\" in state variable", _name);
153                        return;
154                    }
155                    _namedBean = nbhm.getNamedBeanHandle(_name, c);
156                    break;
157                case WARRANT:
158                    Warrant w = InstanceManager.getDefault(WarrantManager.class).getWarrant(_name);
159                    if (w == null) {
160                        log.error("invalid warrant name= \"{}\" in state variable", _name);
161                        return;
162                    }
163                    _namedBean = nbhm.getNamedBeanHandle(_name, w);
164                    break;
165                case OBLOCK:
166                    OBlock b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(_name);
167                    if (b == null) {
168                        log.error("invalid block name= \"{}\" in state variable", _name);
169                        return;
170                    }
171                    _namedBean = nbhm.getNamedBeanHandle(_name, b);
172                    break;
173
174                default:
175                    log.warn("Unexpected type in ConditionalVariable ctor: {} -> {}", _type, itemType);
176                    break;
177            }
178        } catch (java.lang.NumberFormatException ex) {
179            //Can be Considered Normal where the logix is loaded prior to any other beans
180        } catch (IllegalArgumentException ex) {
181            log.warn("could not provide \"{}\" in constructor", _name);
182            _namedBean = null;
183        }
184    }
185
186    public boolean isNegated() {
187        return _not;
188    }
189
190    public void setNegation(boolean not) {
191        _not = not;
192    }
193
194    public Operator getOpern() {
195        return _opern;
196    }
197
198    public final void setOpern(Operator opern) {
199        _opern = opern;
200    }
201
202    public Conditional.Type getType() {
203        return _type;
204    }
205
206    public void setType(Conditional.Type type) {
207        _type = type;
208    }
209
210    public String getName() {
211        if (_namedBean != null) {
212            return _namedBean.getName();
213        }
214        /* As we have a trigger for something using the variable, then hopefully
215         all the managers have been loaded and we can get the bean, which prevented
216         the bean from being loaded in the first place */
217        setName(_name);
218        return _name;
219    }
220
221    public void setName(String name) {
222        _name = name;
223        NamedBean bean = null;
224        Conditional.ItemType itemType = _type.getItemType();
225
226        try {
227            switch (itemType) {
228                case NONE:
229                    break;
230                case CLOCK:
231                    break; // no beans for these, at least that I know of
232                case SENSOR:
233                    bean = InstanceManager.sensorManagerInstance().provideSensor(_name);
234                    break;
235                case TURNOUT:
236                    bean = InstanceManager.turnoutManagerInstance().provideTurnout(_name);
237                    break;
238                case LIGHT:
239                    bean = InstanceManager.lightManagerInstance().getLight(_name);
240                    break;
241                case MEMORY:
242                    bean = InstanceManager.memoryManagerInstance().provideMemory(_name);
243                    break;
244                case SIGNALMAST:
245                    bean = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(_name);
246                    break;
247                case SIGNALHEAD:
248                    bean = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(_name);
249                    break;
250                case CONDITIONAL:
251                    bean = InstanceManager.getDefault(jmri.ConditionalManager.class).getConditional(_name);
252                    break;
253                case WARRANT:
254                    bean = InstanceManager.getDefault(WarrantManager.class).getWarrant(_name);
255                    break;
256                case OBLOCK:
257                    bean = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(_name);
258                    break;
259                case ENTRYEXIT:
260                    bean = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).getNamedBean(_name);
261                    break;
262                default:
263                    log.error("Type {} not set for {}", itemType, _name);
264            }
265
266            //Once all refactored, we should probably register an error if the bean is returned null.
267            if (bean != null) {
268                _namedBean = nbhm.getNamedBeanHandle(_name, bean);
269            } else {
270                log.debug("Did not have or create \"{}\" in setName. namedBean is unchanged", _name);
271            }
272
273        } catch (IllegalArgumentException ex) {
274            log.warn("Did not have or create \"{}\" in setName", _name);
275            _namedBean = null;
276        }
277    }
278
279    public NamedBeanHandle<?> getNamedBean() {
280        return _namedBean;
281    }
282
283    public NamedBean getBean() {
284        if (_namedBean != null) {
285            return _namedBean.getBean();
286        }
287        setName(_name); //ReApply name as that will create namedBean, save replicating it here
288        if (_namedBean != null) {
289            return _namedBean.getBean();
290        }
291        return null;
292    }
293
294    public String getDataString() {
295        if (_type.getItemType() == Conditional.ItemType.MEMORY
296                && _namedBeanData != null) {
297            return _namedBeanData.getName();
298        }
299        return _dataString;
300    }
301
302    public void setDataString(String data) {
303        _dataString = data;
304        if (data != null && !data.equals("")
305                && _type.getItemType() == Conditional.ItemType.MEMORY) {
306            NamedBean bean = InstanceManager.memoryManagerInstance().getMemory(data);
307            if (bean != null) {
308                _namedBeanData = nbhm.getNamedBeanHandle(data, bean);
309            }
310        }
311    }
312
313    private NamedBeanHandle<?> _namedBeanData = null;
314
315    public NamedBean getNamedBeanData() {
316        if (_namedBeanData != null) {
317            return _namedBeanData.getBean();
318        }
319        return null;
320    }
321
322    public int getNum1() {
323        return _num1;
324    }
325
326    public void setNum1(int num) {
327        _num1 = num;
328    }
329
330    public int getNum2() {
331        return _num2;
332    }
333
334    public void setNum2(int num) {
335        _num2 = num;
336    }
337
338     /**
339     * @since 4.7.4
340     * @return the GUI name for the referenced conditional.
341     */
342    public String getGuiName() {
343        return _guiName;
344    }
345
346    /**
347     * Set the GUI name for the conditional state variable.
348     * @since 4.7.4
349     * @param guiName The referenced Conditional user name.
350     */
351    public void setGuiName(String guiName) {
352        _guiName = guiName;
353    }
354
355
356    /**
357     * If change of state of this object causes a change of state of the
358     * Conditional, should any actions be executed.
359     *
360     * @return true if actions should be performed if triggered
361     */
362    public boolean doTriggerActions() {
363        return _triggersActions;
364    }
365
366    public void setTriggerActions(boolean trigger) {
367        _triggersActions = trigger;
368    }
369
370    public int getState() {
371        return _state;
372    }
373
374    public void setState(int state) {
375        _state = state;
376    }
377
378    public void setState(boolean state) {
379        if (state) {
380            _state = Conditional.TRUE;
381        } else {
382            _state = Conditional.FALSE;
383        }
384    }
385
386    public String getTestTypeString() {
387        return _type.getTestTypeString();
388    }
389
390    /**
391     * Provide a localized text for screen display of the logic operator.
392     *
393     * @return translated string (from jmri.NamedBeanBundle.properties)
394     */
395    public String getOpernString() {
396        switch (_opern) {
397            case AND:
398                return Bundle.getMessage("LogicAND"); // NOI18N
399            case NONE:
400                return "";
401            case OR:
402                return Bundle.getMessage("LogicOR"); // NOI18N
403            default:
404                return "";
405        }
406    }
407
408    /**
409     * Evaluates this State Variable.
410     *
411     * @return true if variable evaluates true, otherwise false.
412     */
413    @SuppressWarnings("deprecation")        // Date.getMinutes, Date.getHours
414    public boolean evaluate() {
415        boolean result = true;
416        // evaluate according to state variable type
417        Conditional.ItemType itemType = _type.getItemType();
418        log.debug("evaluate: \"{}\" type= {} itemType= {}", getName(), _type, itemType);
419        switch (itemType) {
420            case SENSOR:
421                //Sensor sn = InstanceManager.sensorManagerInstance().provideSensor(getName());
422                Sensor sn = (Sensor) getBean();
423                if (sn == null) {
424                    log.error("invalid sensor name= \"{}\" in state variable", getName());
425                    return false;
426                }
427                if (_type == Conditional.Type.SENSOR_ACTIVE) {
428                    result = sn.getState() == Sensor.ACTIVE;
429                } else {
430                    result = sn.getState() == Sensor.INACTIVE;
431                }
432                break;
433            case TURNOUT:
434                Turnout t = (Turnout) getBean();
435                if (t == null) {
436                    log.error("invalid turnout name= \"{}\" in state variable", getName());
437                    return false;
438                }
439                if (_type == Conditional.Type.TURNOUT_THROWN) {
440                    result = t.getKnownState() == Turnout.THROWN;
441                } else {
442                    result = t.getKnownState() == Turnout.CLOSED;
443                }
444                break;
445            case LIGHT:
446                Light lgt = (Light) getBean();
447                if (lgt == null) {
448                    log.error("invalid light name= \"{}\" in state variable", getName());
449                    return false;
450                }
451                if (_type == Conditional.Type.LIGHT_ON) {
452                    result = lgt.getState() == Light.ON;
453                } else {
454                    result = lgt.getState() == Light.OFF;
455                }
456                break;
457            case SIGNALMAST:
458                SignalMast f = (SignalMast) getBean();
459                if (f == null) {
460                    log.error("invalid signal mast name= \"{}\" in state variable", getName());
461                    return false;
462                }
463                switch (_type) {
464                    case SIGNAL_MAST_LIT:
465                        result = f.getLit();
466                        break;
467                    case SIGNAL_MAST_HELD:
468                        result = f.getHeld();
469                        break;
470                    case SIGNAL_MAST_ASPECT_EQUALS:
471                        if (f.getAspect() == null) {
472                            result = false;
473                        } else {
474                            result = f.getAspect().equals(_dataString);
475                        }
476                        break;
477                    default:
478                        log.warn("unexpected type {} in ITEM_TYPE_SIGNALMAST", _type);
479                }
480                break;
481            case SIGNALHEAD:
482                SignalHead h = (SignalHead) getBean();
483                if (h == null) {
484                    log.error("invalid signal head name= \"{}\" in state variable", getName());
485                    return false;
486                }
487                switch (_type) {
488                    case SIGNAL_HEAD_RED:
489                        result = h.getAppearance() == SignalHead.RED;
490                        break;
491                    case SIGNAL_HEAD_YELLOW:
492                        result = h.getAppearance() == SignalHead.YELLOW;
493                        break;
494                    case SIGNAL_HEAD_GREEN:
495                        result = h.getAppearance() == SignalHead.GREEN;
496                        break;
497                    case SIGNAL_HEAD_DARK:
498                        result = h.getAppearance() == SignalHead.DARK;
499                        break;
500                    case SIGNAL_HEAD_FLASHRED:
501                        result = h.getAppearance() == SignalHead.FLASHRED;
502                        break;
503                    case SIGNAL_HEAD_FLASHYELLOW:
504                        result = h.getAppearance() == SignalHead.FLASHYELLOW;
505                        break;
506                    case SIGNAL_HEAD_FLASHGREEN:
507                        result = h.getAppearance() == SignalHead.FLASHGREEN;
508                        break;
509                    case SIGNAL_HEAD_LUNAR:
510                        result = h.getAppearance() == SignalHead.LUNAR;
511                        break;
512                    case SIGNAL_HEAD_FLASHLUNAR:
513                        result = h.getAppearance() == SignalHead.FLASHLUNAR;
514                        break;
515                    case SIGNAL_HEAD_LIT:
516                        result = h.getLit();
517                        break;
518                    case SIGNAL_HEAD_HELD:
519                        result = h.getHeld();
520                        break;
521                    default:
522                        result = false;
523                }
524                break;
525            case MEMORY:
526                Memory m = (Memory) getBean();
527                if (m == null) {
528                    log.error("invalid memory name= \"{}\" in state variable", getName());
529                    return false;
530                }
531                String value1 = null;
532                String value2 = null;
533                if (m.getValue() != null) {
534                    value1 = m.getValue().toString();
535                }
536                boolean caseInsensitive = ((_type == Conditional.Type.MEMORY_EQUALS_INSENSITIVE)
537                        || (_type == Conditional.Type.MEMORY_COMPARE_INSENSITIVE));
538                if ((_type == Conditional.Type.MEMORY_COMPARE)
539                        || (_type == Conditional.Type.MEMORY_COMPARE_INSENSITIVE)) {
540                    Memory m2;
541                    if (_namedBeanData != null) {
542                        m2 = (Memory) _namedBeanData.getBean();
543                    } else {
544                        try {
545                            m2 = InstanceManager.memoryManagerInstance().provideMemory(_dataString);
546                        } catch (IllegalArgumentException ex) {
547                            log.error("invalid data memory name= \"{}\" in state variable", _dataString);
548                            return false;
549                        }
550                    }
551                    if (m2.getValue() != null) {
552                        value2 = m2.getValue().toString();
553                    }
554                } else {
555                    value2 = _dataString;
556                }
557                result = compare(value1, value2, caseInsensitive);
558                break;
559            case CONDITIONAL:
560                Conditional c = InstanceManager.getDefault(jmri.ConditionalManager.class).getBySystemName(getName());
561                if (c == null) {
562                    c = InstanceManager.getDefault(jmri.ConditionalManager.class).getByUserName(getName());
563                    if (c == null) {
564                        log.error("invalid conditional name= \"{}\" in state variable", getName());
565                        return false;
566                    }
567                }
568                if (_type == Conditional.Type.CONDITIONAL_TRUE) {
569                    result = c.getState() == Conditional.TRUE;
570                } else {
571                    result = c.getState() == Conditional.FALSE;
572                }
573                break;
574            case WARRANT:
575                Warrant w = InstanceManager.getDefault(WarrantManager.class).getWarrant(getName());
576                if (w == null) {
577                    log.error("invalid Warrant name= \"{}\" in state variable", getName());
578                    return false;
579                }
580                switch (_type) {
581                    case ROUTE_FREE:
582                        result = w.routeIsFree();
583                        break;
584                    case ROUTE_OCCUPIED:
585                        result = w.routeIsOccupied();
586                        break;
587                    case ROUTE_ALLOCATED:
588                        result = w.isAllocated();
589                        break;
590                    case ROUTE_SET:
591                        result = w.hasRouteSet();
592                        break;
593                    case TRAIN_RUNNING:
594                        // not in either RUN or LEARN state
595                        result = !(w.getRunMode() == Warrant.MODE_NONE);
596                        break;
597                    default:
598                        result = false;
599                }
600                break;
601            case CLOCK:
602                Timebase fastClock = InstanceManager.getDefault(jmri.Timebase.class);
603                Date currentTime = fastClock.getTime();
604                int currentMinutes = (currentTime.getHours() * 60) + currentTime.getMinutes();
605                int beginTime = fixMidnight(_num1);
606                int endTime = fixMidnight(_num2);
607                // check if current time is within range specified
608                if (beginTime <= endTime) {
609                    // range is entirely within one day
610                    result = (beginTime <= currentMinutes) && (currentMinutes <= endTime);
611                } else {
612                    // range includes midnight
613                    result = beginTime <= currentMinutes || currentMinutes <= endTime;
614                }
615                break;
616            case OBLOCK:
617                OBlock b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(getName());
618                if (b == null) {
619                    log.error("invalid OBlock name= \"{}\" in state variable", getName());
620                    return false;
621                }
622                result = b.statusIs(_dataString);
623                break;
624            case ENTRYEXIT:
625                NamedBean e = getBean();
626                if (_type == Conditional.Type.ENTRYEXIT_ACTIVE) {
627                    result = e.getState() == 0x02;
628                } else {
629                    result = e.getState() == 0x04;
630                }
631                break;
632            default:
633                break;
634        }
635        // apply NOT if specified
636        if (_not) {
637            result = !result;
638        }
639        if (result) {
640            setState(Conditional.TRUE);
641        } else {
642            setState(Conditional.FALSE);
643        }
644        return (result);
645    }
646
647    /**
648     * Compare two values using the comparator set using the comparison
649     * instructions in {@link #setNum1(int)}.
650     *
651     * <strong>Note:</strong> {@link #getNum1()} must be one of {@link #LESS_THAN},
652     * {@link #LESS_THAN_OR_EQUAL}, {@link #EQUAL},
653     * {@link #GREATER_THAN_OR_EQUAL}, or {@link #GREATER_THAN}.
654     *
655     * @param value1          left side of the comparison
656     * @param value2          right side of the comparison
657     * @param caseInsensitive true if comparison should be case insensitive;
658     *                        false otherwise
659     * @return true if values compare per getNum1(); false otherwise
660     */
661    boolean compare(String value1, String value2, boolean caseInsensitive) {
662        if (value1 == null) {
663            return value2 == null;
664        } else {
665            if (value2 == null) {
666                return false;
667            }
668            value1 = value1.trim();
669            value2 = value2.trim();
670        }
671        try {
672            int n1 = Integer.parseInt(value1);
673            try {
674                int n2 = Integer.parseInt(value2);
675                if (_num1 == 0) { // for former code
676                    return n1 == n2;
677                }
678                log.debug("Compare numbers: n1= {} to n2= {}", n1, n2);
679                switch (_num1) // both are numbers
680                {
681                    case LESS_THAN:
682                        return (n1 < n2);
683                    case LESS_THAN_OR_EQUAL:
684                        return (n1 <= n2);
685                    case EQUAL:
686                        return (n1 == n2);
687                    case GREATER_THAN_OR_EQUAL:
688                        return (n1 >= n2);
689                    case GREATER_THAN:
690                        return (n1 > n2);
691                    default:
692                        log.error("Compare numbers: invalid compare case: {}", _num1);
693                        return false;
694                }
695            } catch (NumberFormatException nfe) {
696                return false;   // n1 is a number, n2 is not
697            }
698        } catch (NumberFormatException nfe) {
699            try {
700                Integer.parseInt(value2);
701                return false;     // n1 is not a number, n2 is
702            } catch (NumberFormatException ex) { // OK neither a number
703            }
704        }
705        log.debug("Compare Strings: value1= {} to value2= {}", value1, value2);
706        int compare;
707        if (caseInsensitive) {
708            compare = value1.compareToIgnoreCase(value2);
709        } else {
710            compare = value1.compareTo(value2);
711        }
712        if (_num1 == 0) { // for former code
713            return compare == 0;
714        }
715        switch (_num1) {
716            case LESS_THAN:
717                if (compare < 0) {
718                    return true;
719                }
720                break;
721            case LESS_THAN_OR_EQUAL:
722                if (compare <= 0) {
723                    return true;
724                }
725                break;
726            case EQUAL:
727                if (compare == 0) {
728                    return true;
729                }
730                break;
731            case GREATER_THAN_OR_EQUAL:
732                if (compare >= 0) {
733                    return true;
734                }
735                break;
736            case GREATER_THAN:
737                if (compare > 0) {
738                    return true;
739                }
740                break;
741            default:
742                // fall through
743                break;
744        }
745        return false;
746    }
747
748    public static int fixMidnight(int time) {
749        if (time > 24 * 60) {
750            time -= 24 * 60;
751        }
752        return time;
753    }
754
755    /**
756     * Convert Variable Type to Text String
757     *
758     * @param t the type
759     * @return the localized description
760     */
761    public static String getItemTypeString(Conditional.ItemType t) {
762        switch (t) {
763            case SENSOR:
764                return Bundle.getMessage("BeanNameSensor"); // NOI18N
765            case TURNOUT:
766                return Bundle.getMessage("BeanNameTurnout"); // NOI18N
767            case LIGHT:
768                return Bundle.getMessage("BeanNameLight"); // NOI18N
769            case SIGNALHEAD:
770                return Bundle.getMessage("BeanNameSignalHead"); // NOI18N
771            case SIGNALMAST:
772                return Bundle.getMessage("BeanNameSignalMast"); // NOI18N
773            case MEMORY:
774                return Bundle.getMessage("BeanNameMemory"); // NOI18N
775            case CONDITIONAL:
776                return Bundle.getMessage("BeanNameConditional"); // NOI18N
777            case WARRANT:
778                return Bundle.getMessage("BeanNameWarrant"); // NOI18N
779            case CLOCK:
780                return Bundle.getMessage("FastClock"); // NOI18N
781            case OBLOCK:
782                return Bundle.getMessage("BeanNameOBlock"); // NOI18N
783            case ENTRYEXIT:
784                return Bundle.getMessage("BeanNameEntryExit"); // NOI18N
785            default:
786                return "";
787        }
788    }
789
790    public static String getCompareOperationString(int index) {
791        switch (index) {
792            case LESS_THAN:
793                return rbx.getString("LessThan"); // NOI18N
794            case LESS_THAN_OR_EQUAL:
795                return rbx.getString("LessOrEqual"); // NOI18N
796            case 0:
797            case EQUAL:
798                return rbx.getString("Equal"); // NOI18N
799            case GREATER_THAN_OR_EQUAL:
800                return rbx.getString("GreaterOrEqual"); // NOI18N
801            case GREATER_THAN:
802                return rbx.getString("GreaterThan"); // NOI18N
803            default:
804                // fall through
805                break;
806        }
807        return ""; // NOI18N
808    }
809
810    public static String getCompareSymbols(int index) {
811        switch (index) {
812            case LESS_THAN:
813                return "<"; // NOI18N
814            case LESS_THAN_OR_EQUAL:
815                return "<="; // NOI18N
816            case 0:
817            case EQUAL:
818                return "="; // NOI18N
819            case GREATER_THAN_OR_EQUAL:
820                return ">="; // NOI18N
821            case GREATER_THAN:
822                return ">"; // NOI18N
823            default:
824                break;
825        }
826        return ""; // NOI18N
827    }
828
829    /**
830     * Identify action Data from Text String.
831     *
832     * @param str the text to check
833     * @return the conditional action type or -1 if if string does not
834     * correspond to an action Data as defined in ConditionalAction
835     */
836    public static Conditional.Type stringToVariableTest(String str) {
837        if (str.equals(Bundle.getMessage("SignalHeadStateRed"))) { // NOI18N
838            return Conditional.Type.SIGNAL_HEAD_RED;
839        } else if (str.equals(Bundle.getMessage("SignalHeadStateYellow"))) { // NOI18N
840            return Conditional.Type.SIGNAL_HEAD_YELLOW;
841        } else if (str.equals(Bundle.getMessage("SignalHeadStateGreen"))) { // NOI18N
842            return Conditional.Type.SIGNAL_HEAD_GREEN;
843        } else if (str.equals(Bundle.getMessage("SignalHeadStateDark"))) { // NOI18N
844            return Conditional.Type.SIGNAL_HEAD_DARK;
845        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingRed"))) { // NOI18N
846            return Conditional.Type.SIGNAL_HEAD_FLASHRED;
847        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingYellow"))) { // NOI18N
848            return Conditional.Type.SIGNAL_HEAD_FLASHYELLOW;
849        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingGreen"))) { // NOI18N
850            return Conditional.Type.SIGNAL_HEAD_FLASHGREEN;
851        } else if (str.equals(Bundle.getMessage("SignalHeadStateLunar"))) { // NOI18N
852            return Conditional.Type.SIGNAL_HEAD_LUNAR;
853        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingLunar"))) { // NOI18N
854            return Conditional.Type.SIGNAL_HEAD_FLASHLUNAR;
855        }
856        // empty strings can occur frequently with types that have no integer data
857        if (str.length() > 0) {
858            log.warn("Unexpected parameter to stringToVariableTest({})", str);
859        }
860        return Conditional.Type.ERROR;
861    }
862
863    @Override
864    public String toString() {
865        String type = _type.getTestTypeString();
866        Conditional.ItemType itemType = _type.getItemType();
867        switch (itemType) {
868            case SENSOR:
869                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
870                        new Object[]{Bundle.getMessage("BeanNameSensor"), getName(), type});
871            case TURNOUT:
872                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
873                        new Object[]{Bundle.getMessage("BeanNameTurnout"), getName(), type});
874            case LIGHT:
875                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
876                        new Object[]{Bundle.getMessage("BeanNameLight"), getName(), type});
877            case SIGNALHEAD:
878                if ((_type == Conditional.Type.SIGNAL_HEAD_LIT)
879                        || (_type == Conditional.Type.SIGNAL_HEAD_HELD)) {
880                    return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
881                            new Object[]{Bundle.getMessage("BeanNameSignalHead"), getName(), type});
882                } else {
883                    return java.text.MessageFormat.format(rbx.getString("SignalHeadStateDescrpt"),
884                            new Object[]{Bundle.getMessage("BeanNameSignalHead"), getName(), type});
885                }
886            case SIGNALMAST:
887                if ((_type == Conditional.Type.SIGNAL_MAST_LIT)
888                        || (_type == Conditional.Type.SIGNAL_MAST_HELD)) {
889                    return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
890                            new Object[]{Bundle.getMessage("BeanNameSignalMast"), getName(), type}); // NOI18N
891                } else {
892                    return java.text.MessageFormat.format(rbx.getString("SignalMastStateDescrpt"),
893                            new Object[]{Bundle.getMessage("BeanNameSignalMast"), getName(), _dataString}); // NOI18N
894                }
895            case MEMORY:
896                if ((_type == Conditional.Type.MEMORY_EQUALS)
897                        || (_type == Conditional.Type.MEMORY_EQUALS_INSENSITIVE)) {
898                    return java.text.MessageFormat.format(rbx.getString("MemoryValueDescrpt"),
899                            new Object[]{Bundle.getMessage("BeanNameMemory"), getName(), // NOI18N
900                                getCompareSymbols(_num1), _dataString});
901                } else {
902                    return java.text.MessageFormat.format(rbx.getString("MemoryCompareDescrpt"),
903                            new Object[]{Bundle.getMessage("BeanNameMemory"), getName(), // NOI18N
904                                getCompareSymbols(_num1), _dataString});
905                }
906            case CONDITIONAL:
907                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
908                        new Object[]{Bundle.getMessage("BeanNameConditional"), getGuiName(), type}); // NOI18N
909            case WARRANT:
910                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
911                        new Object[]{rbx.getString("WarrantRoute"), getName(), type});
912            case CLOCK:
913                return java.text.MessageFormat.format(rbx.getString("FastClockDescrpt"),
914                        new Object[]{Bundle.getMessage("FastClock"),
915                            LogixTableAction.formatTime(_num1 / 60, _num1 - ((_num1 / 60) * 60)),
916                            LogixTableAction.formatTime(_num2 / 60, _num2 - ((_num2 / 60) * 60))});
917            case OBLOCK:
918                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
919                        new Object[]{rbx.getString("OBlockStatus"), getName(), _dataString});
920            case ENTRYEXIT:
921                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
922                        new Object[]{Bundle.getMessage("BeanNameEntryExit"), getBean().getUserName(), type}); // NOI18N
923            case NONE:
924                return getName() + " type " + type;
925            default:
926                // fall through
927                break;
928        }
929        return super.toString();
930    }
931
932    private final static Logger log = LoggerFactory.getLogger(ConditionalVariable.class);
933}