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                        String aspect = f.getAspect();
472                        if ( aspect == null) {
473                            result = false;
474                        } else {
475                            result = aspect.equals(_dataString);
476                        }
477                        break;
478                    default:
479                        log.warn("unexpected type {} in ITEM_TYPE_SIGNALMAST", _type);
480                }
481                break;
482            case SIGNALHEAD:
483                SignalHead h = (SignalHead) getBean();
484                if (h == null) {
485                    log.error("invalid signal head name= \"{}\" in state variable", getName());
486                    return false;
487                }
488                switch (_type) {
489                    case SIGNAL_HEAD_RED:
490                        result = h.getAppearance() == SignalHead.RED;
491                        break;
492                    case SIGNAL_HEAD_YELLOW:
493                        result = h.getAppearance() == SignalHead.YELLOW;
494                        break;
495                    case SIGNAL_HEAD_GREEN:
496                        result = h.getAppearance() == SignalHead.GREEN;
497                        break;
498                    case SIGNAL_HEAD_DARK:
499                        result = h.getAppearance() == SignalHead.DARK;
500                        break;
501                    case SIGNAL_HEAD_FLASHRED:
502                        result = h.getAppearance() == SignalHead.FLASHRED;
503                        break;
504                    case SIGNAL_HEAD_FLASHYELLOW:
505                        result = h.getAppearance() == SignalHead.FLASHYELLOW;
506                        break;
507                    case SIGNAL_HEAD_FLASHGREEN:
508                        result = h.getAppearance() == SignalHead.FLASHGREEN;
509                        break;
510                    case SIGNAL_HEAD_LUNAR:
511                        result = h.getAppearance() == SignalHead.LUNAR;
512                        break;
513                    case SIGNAL_HEAD_FLASHLUNAR:
514                        result = h.getAppearance() == SignalHead.FLASHLUNAR;
515                        break;
516                    case SIGNAL_HEAD_LIT:
517                        result = h.getLit();
518                        break;
519                    case SIGNAL_HEAD_HELD:
520                        result = h.getHeld();
521                        break;
522                    default:
523                        result = false;
524                }
525                break;
526            case MEMORY:
527                Memory m = (Memory) getBean();
528                if (m == null) {
529                    log.error("invalid memory name= \"{}\" in state variable", getName());
530                    return false;
531                }
532                String value1 = null;
533                String value2 = null;
534                if (m.getValue() != null) {
535                    value1 = m.getValue().toString();
536                }
537                boolean caseInsensitive = ((_type == Conditional.Type.MEMORY_EQUALS_INSENSITIVE)
538                        || (_type == Conditional.Type.MEMORY_COMPARE_INSENSITIVE));
539                if ((_type == Conditional.Type.MEMORY_COMPARE)
540                        || (_type == Conditional.Type.MEMORY_COMPARE_INSENSITIVE)) {
541                    Memory m2;
542                    if (_namedBeanData != null) {
543                        m2 = (Memory) _namedBeanData.getBean();
544                    } else {
545                        try {
546                            m2 = InstanceManager.memoryManagerInstance().provideMemory(_dataString);
547                        } catch (IllegalArgumentException ex) {
548                            log.error("invalid data memory name= \"{}\" in state variable", _dataString);
549                            return false;
550                        }
551                    }
552                    if (m2.getValue() != null) {
553                        value2 = m2.getValue().toString();
554                    }
555                } else {
556                    value2 = _dataString;
557                }
558                result = compare(value1, value2, caseInsensitive);
559                break;
560            case CONDITIONAL:
561                Conditional c = InstanceManager.getDefault(jmri.ConditionalManager.class).getBySystemName(getName());
562                if (c == null) {
563                    c = InstanceManager.getDefault(jmri.ConditionalManager.class).getByUserName(getName());
564                    if (c == null) {
565                        log.error("invalid conditional name= \"{}\" in state variable", getName());
566                        return false;
567                    }
568                }
569                if (_type == Conditional.Type.CONDITIONAL_TRUE) {
570                    result = c.getState() == Conditional.TRUE;
571                } else {
572                    result = c.getState() == Conditional.FALSE;
573                }
574                break;
575            case WARRANT:
576                Warrant w = InstanceManager.getDefault(WarrantManager.class).getWarrant(getName());
577                if (w == null) {
578                    log.error("invalid Warrant name= \"{}\" in state variable", getName());
579                    return false;
580                }
581                switch (_type) {
582                    case ROUTE_FREE:
583                        result = w.routeIsFree();
584                        break;
585                    case ROUTE_OCCUPIED:
586                        result = w.routeIsOccupied();
587                        break;
588                    case ROUTE_ALLOCATED:
589                        result = w.isAllocated();
590                        break;
591                    case ROUTE_SET:
592                        result = w.hasRouteSet();
593                        break;
594                    case TRAIN_RUNNING:
595                        // not in either RUN or LEARN state
596                        result = !(w.getRunMode() == Warrant.MODE_NONE);
597                        break;
598                    default:
599                        result = false;
600                }
601                break;
602            case CLOCK:
603                Timebase fastClock = InstanceManager.getDefault(jmri.Timebase.class);
604                Date currentTime = fastClock.getTime();
605                int currentMinutes = (currentTime.getHours() * 60) + currentTime.getMinutes();
606                int beginTime = fixMidnight(_num1);
607                int endTime = fixMidnight(_num2);
608                // check if current time is within range specified
609                if (beginTime <= endTime) {
610                    // range is entirely within one day
611                    result = (beginTime <= currentMinutes) && (currentMinutes <= endTime);
612                } else {
613                    // range includes midnight
614                    result = beginTime <= currentMinutes || currentMinutes <= endTime;
615                }
616                break;
617            case OBLOCK:
618                OBlock b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(getName());
619                if (b == null) {
620                    log.error("invalid OBlock name= \"{}\" in state variable", getName());
621                    return false;
622                }
623                result = b.statusIs(_dataString);
624                break;
625            case ENTRYEXIT:
626                NamedBean e = getBean();
627                if (_type == Conditional.Type.ENTRYEXIT_ACTIVE) {
628                    result = e.getState() == 0x02;
629                } else {
630                    result = e.getState() == 0x04;
631                }
632                break;
633            default:
634                break;
635        }
636        // apply NOT if specified
637        if (_not) {
638            result = !result;
639        }
640        if (result) {
641            setState(Conditional.TRUE);
642        } else {
643            setState(Conditional.FALSE);
644        }
645        return (result);
646    }
647
648    /**
649     * Compare two values using the comparator set using the comparison
650     * instructions in {@link #setNum1(int)}.
651     *
652     * <strong>Note:</strong> {@link #getNum1()} must be one of {@link #LESS_THAN},
653     * {@link #LESS_THAN_OR_EQUAL}, {@link #EQUAL},
654     * {@link #GREATER_THAN_OR_EQUAL}, or {@link #GREATER_THAN}.
655     *
656     * @param value1          left side of the comparison
657     * @param value2          right side of the comparison
658     * @param caseInsensitive true if comparison should be case insensitive;
659     *                        false otherwise
660     * @return true if values compare per getNum1(); false otherwise
661     */
662    boolean compare(String value1, String value2, boolean caseInsensitive) {
663        if (value1 == null) {
664            return value2 == null;
665        } else {
666            if (value2 == null) {
667                return false;
668            }
669            value1 = value1.trim();
670            value2 = value2.trim();
671        }
672        try {
673            int n1 = Integer.parseInt(value1);
674            try {
675                int n2 = Integer.parseInt(value2);
676                if (_num1 == 0) { // for former code
677                    return n1 == n2;
678                }
679                log.debug("Compare numbers: n1= {} to n2= {}", n1, n2);
680                switch (_num1) // both are numbers
681                {
682                    case LESS_THAN:
683                        return (n1 < n2);
684                    case LESS_THAN_OR_EQUAL:
685                        return (n1 <= n2);
686                    case EQUAL:
687                        return (n1 == n2);
688                    case GREATER_THAN_OR_EQUAL:
689                        return (n1 >= n2);
690                    case GREATER_THAN:
691                        return (n1 > n2);
692                    default:
693                        log.error("Compare numbers: invalid compare case: {}", _num1);
694                        return false;
695                }
696            } catch (NumberFormatException nfe) {
697                return false;   // n1 is a number, n2 is not
698            }
699        } catch (NumberFormatException nfe) {
700            try {
701                Integer.parseInt(value2);
702                return false;     // n1 is not a number, n2 is
703            } catch (NumberFormatException ex) { // OK neither a number
704            }
705        }
706        log.debug("Compare Strings: value1= {} to value2= {}", value1, value2);
707        int compare;
708        if (caseInsensitive) {
709            compare = value1.compareToIgnoreCase(value2);
710        } else {
711            compare = value1.compareTo(value2);
712        }
713        if (_num1 == 0) { // for former code
714            return compare == 0;
715        }
716        switch (_num1) {
717            case LESS_THAN:
718                if (compare < 0) {
719                    return true;
720                }
721                break;
722            case LESS_THAN_OR_EQUAL:
723                if (compare <= 0) {
724                    return true;
725                }
726                break;
727            case EQUAL:
728                if (compare == 0) {
729                    return true;
730                }
731                break;
732            case GREATER_THAN_OR_EQUAL:
733                if (compare >= 0) {
734                    return true;
735                }
736                break;
737            case GREATER_THAN:
738                if (compare > 0) {
739                    return true;
740                }
741                break;
742            default:
743                // fall through
744                break;
745        }
746        return false;
747    }
748
749    public static int fixMidnight(int time) {
750        if (time > 24 * 60) {
751            time -= 24 * 60;
752        }
753        return time;
754    }
755
756    /**
757     * Convert Variable Type to Text String
758     *
759     * @param t the type
760     * @return the localized description
761     */
762    public static String getItemTypeString(Conditional.ItemType t) {
763        switch (t) {
764            case SENSOR:
765                return Bundle.getMessage("BeanNameSensor"); // NOI18N
766            case TURNOUT:
767                return Bundle.getMessage("BeanNameTurnout"); // NOI18N
768            case LIGHT:
769                return Bundle.getMessage("BeanNameLight"); // NOI18N
770            case SIGNALHEAD:
771                return Bundle.getMessage("BeanNameSignalHead"); // NOI18N
772            case SIGNALMAST:
773                return Bundle.getMessage("BeanNameSignalMast"); // NOI18N
774            case MEMORY:
775                return Bundle.getMessage("BeanNameMemory"); // NOI18N
776            case CONDITIONAL:
777                return Bundle.getMessage("BeanNameConditional"); // NOI18N
778            case WARRANT:
779                return Bundle.getMessage("BeanNameWarrant"); // NOI18N
780            case CLOCK:
781                return Bundle.getMessage("FastClock"); // NOI18N
782            case OBLOCK:
783                return Bundle.getMessage("BeanNameOBlock"); // NOI18N
784            case ENTRYEXIT:
785                return Bundle.getMessage("BeanNameEntryExit"); // NOI18N
786            default:
787                return "";
788        }
789    }
790
791    public static String getCompareOperationString(int index) {
792        switch (index) {
793            case LESS_THAN:
794                return rbx.getString("LessThan"); // NOI18N
795            case LESS_THAN_OR_EQUAL:
796                return rbx.getString("LessOrEqual"); // NOI18N
797            case 0:
798            case EQUAL:
799                return rbx.getString("Equal"); // NOI18N
800            case GREATER_THAN_OR_EQUAL:
801                return rbx.getString("GreaterOrEqual"); // NOI18N
802            case GREATER_THAN:
803                return rbx.getString("GreaterThan"); // NOI18N
804            default:
805                // fall through
806                break;
807        }
808        return ""; // NOI18N
809    }
810
811    public static String getCompareSymbols(int index) {
812        switch (index) {
813            case LESS_THAN:
814                return "<"; // NOI18N
815            case LESS_THAN_OR_EQUAL:
816                return "<="; // NOI18N
817            case 0:
818            case EQUAL:
819                return "="; // NOI18N
820            case GREATER_THAN_OR_EQUAL:
821                return ">="; // NOI18N
822            case GREATER_THAN:
823                return ">"; // NOI18N
824            default:
825                break;
826        }
827        return ""; // NOI18N
828    }
829
830    /**
831     * Identify action Data from Text String.
832     *
833     * @param str the text to check
834     * @return the conditional action type or -1 if if string does not
835     * correspond to an action Data as defined in ConditionalAction
836     */
837    public static Conditional.Type stringToVariableTest(String str) {
838        if (str.equals(Bundle.getMessage("SignalHeadStateRed"))) { // NOI18N
839            return Conditional.Type.SIGNAL_HEAD_RED;
840        } else if (str.equals(Bundle.getMessage("SignalHeadStateYellow"))) { // NOI18N
841            return Conditional.Type.SIGNAL_HEAD_YELLOW;
842        } else if (str.equals(Bundle.getMessage("SignalHeadStateGreen"))) { // NOI18N
843            return Conditional.Type.SIGNAL_HEAD_GREEN;
844        } else if (str.equals(Bundle.getMessage("SignalHeadStateDark"))) { // NOI18N
845            return Conditional.Type.SIGNAL_HEAD_DARK;
846        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingRed"))) { // NOI18N
847            return Conditional.Type.SIGNAL_HEAD_FLASHRED;
848        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingYellow"))) { // NOI18N
849            return Conditional.Type.SIGNAL_HEAD_FLASHYELLOW;
850        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingGreen"))) { // NOI18N
851            return Conditional.Type.SIGNAL_HEAD_FLASHGREEN;
852        } else if (str.equals(Bundle.getMessage("SignalHeadStateLunar"))) { // NOI18N
853            return Conditional.Type.SIGNAL_HEAD_LUNAR;
854        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingLunar"))) { // NOI18N
855            return Conditional.Type.SIGNAL_HEAD_FLASHLUNAR;
856        }
857        // empty strings can occur frequently with types that have no integer data
858        if (str.length() > 0) {
859            log.warn("Unexpected parameter to stringToVariableTest({})", str);
860        }
861        return Conditional.Type.ERROR;
862    }
863
864    @Override
865    public String toString() {
866        String type = _type.getTestTypeString();
867        Conditional.ItemType itemType = _type.getItemType();
868        switch (itemType) {
869            case SENSOR:
870                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
871                        new Object[]{Bundle.getMessage("BeanNameSensor"), getName(), type});
872            case TURNOUT:
873                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
874                        new Object[]{Bundle.getMessage("BeanNameTurnout"), getName(), type});
875            case LIGHT:
876                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
877                        new Object[]{Bundle.getMessage("BeanNameLight"), getName(), type});
878            case SIGNALHEAD:
879                if ((_type == Conditional.Type.SIGNAL_HEAD_LIT)
880                        || (_type == Conditional.Type.SIGNAL_HEAD_HELD)) {
881                    return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
882                            new Object[]{Bundle.getMessage("BeanNameSignalHead"), getName(), type});
883                } else {
884                    return java.text.MessageFormat.format(rbx.getString("SignalHeadStateDescrpt"),
885                            new Object[]{Bundle.getMessage("BeanNameSignalHead"), getName(), type});
886                }
887            case SIGNALMAST:
888                if ((_type == Conditional.Type.SIGNAL_MAST_LIT)
889                        || (_type == Conditional.Type.SIGNAL_MAST_HELD)) {
890                    return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
891                            new Object[]{Bundle.getMessage("BeanNameSignalMast"), getName(), type}); // NOI18N
892                } else {
893                    return java.text.MessageFormat.format(rbx.getString("SignalMastStateDescrpt"),
894                            new Object[]{Bundle.getMessage("BeanNameSignalMast"), getName(), _dataString}); // NOI18N
895                }
896            case MEMORY:
897                if ((_type == Conditional.Type.MEMORY_EQUALS)
898                        || (_type == Conditional.Type.MEMORY_EQUALS_INSENSITIVE)) {
899                    return java.text.MessageFormat.format(rbx.getString("MemoryValueDescrpt"),
900                            new Object[]{Bundle.getMessage("BeanNameMemory"), getName(), // NOI18N
901                                getCompareSymbols(_num1), _dataString});
902                } else {
903                    return java.text.MessageFormat.format(rbx.getString("MemoryCompareDescrpt"),
904                            new Object[]{Bundle.getMessage("BeanNameMemory"), getName(), // NOI18N
905                                getCompareSymbols(_num1), _dataString});
906                }
907            case CONDITIONAL:
908                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
909                        new Object[]{Bundle.getMessage("BeanNameConditional"), getGuiName(), type}); // NOI18N
910            case WARRANT:
911                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
912                        new Object[]{rbx.getString("WarrantRoute"), getName(), type});
913            case CLOCK:
914                return java.text.MessageFormat.format(rbx.getString("FastClockDescrpt"),
915                        new Object[]{Bundle.getMessage("FastClock"),
916                            LogixTableAction.formatTime(_num1 / 60, _num1 - ((_num1 / 60) * 60)),
917                            LogixTableAction.formatTime(_num2 / 60, _num2 - ((_num2 / 60) * 60))});
918            case OBLOCK:
919                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
920                        new Object[]{rbx.getString("OBlockStatus"), getName(), _dataString});
921            case ENTRYEXIT:
922                return java.text.MessageFormat.format(rbx.getString("VarStateDescrpt"),
923                        new Object[]{Bundle.getMessage("BeanNameEntryExit"), getBean().getUserName(), type}); // NOI18N
924            case NONE:
925                return getName() + " type " + type;
926            default:
927                // fall through
928                break;
929        }
930        return super.toString();
931    }
932
933    private final static Logger log = LoggerFactory.getLogger(ConditionalVariable.class);
934}