001package jmri.jmrit.symbolicprog;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Mechanism to qualify on the value of a number.
008 * <p>
009 * The usual arithmetic operations are possible: ge, le, gt, lt, eq, ne. The
010 * sense of this is comparing "current value" to "constant", for example
011 * "current value gt 3".
012 * <p>
013 * You can also check whether the value "exists" (value of 1) or not (value of
014 * 0). Comparisons with the value of a non-existent variable always fail.
015 *
016 * @author Bob Jacobsen Copyright (C) 2010, 2014
017 *
018 */
019public abstract class ArithmeticQualifier extends AbstractQualifier {
020
021    public enum Test {
022
023        GE("ge"), // greater than or equal
024        LE("le"),
025        GT("gt"),
026        LT("lt"),
027        EQ("eq"),
028        NE("ne"),
029        EXISTS("exists");
030
031        Test(String relation) {
032            this.relation = relation;
033        }
034        String relation;
035
036        static Test decode(String r) {
037            for (Test t : Test.values()) {
038                if (t.relation.equals(r)) {
039                    return t;
040                }
041            }
042            return null;
043        }
044    }
045
046    Test test;
047
048    public ArithmeticQualifier(VariableValue watchedVal, int value, String relation) {
049        super(watchedVal);
050
051        this.test = Test.decode(relation);
052        this.value = Integer.toUnsignedLong(value);
053    }
054
055    @Override
056    public boolean currentDesiredState() {
057        if (returnFromExistsLogic()) {
058            return valueOfExistsLogic();
059        }
060
061        return availableStateFromValue(watchedVal.getLongValue());
062    }
063
064    @Override
065    protected boolean availableStateFromValue(Object now) {
066        if (returnFromExistsLogic()) {
067            return valueOfExistsLogic();
068        }
069
070        long nowVal = 0;
071        if (now instanceof Integer) {
072            nowVal = Integer.toUnsignedLong((int) now);
073        } else if (now instanceof Long) {
074            nowVal = (Long) now;
075        }
076
077        int compare = Long.compareUnsigned(nowVal, value);
078
079        switch (test) {
080            case GE:
081                return compare >= 0;
082            case LE:
083                return compare <= 0;
084            case GT:
085                return compare > 0;
086            case LT:
087                return compare < 0;
088            case EQ:
089                return compare == 0;
090            case NE:
091                return compare != 0;
092            default:
093                log.error("Unexpected switch value: {}", test);
094                return false;
095        }
096
097    }
098
099    @Override
100    public void update() {
101        setWatchedAvailable(currentDesiredState());
102    }
103
104    long value;
105
106    private boolean returnFromExistsLogic() {
107        if (test == Test.EXISTS) {
108            return true;
109        }
110        if (watchedVal == null) {
111            return true;
112        }
113        return false;
114    }
115
116    boolean warnedDoesntExist = false;
117
118    private boolean valueOfExistsLogic() {
119        if (test == Test.EXISTS) {
120            if (value == 0 && watchedVal == null) {
121                return true;
122            }
123            if (value != 0 && watchedVal != null) {
124                return true;
125            }
126            return false;
127        }
128        // here it's an arithmetic op on a variable
129        if (watchedVal == null) {
130            if (!warnedDoesntExist) {
131                warnedDoesntExist = true;
132                log.error("Arithmetic {} operation when watched value doesn't exist", test);
133            }
134            return true;  // this determines default for what happens when qualifier (watched) Variable isn't present
135        }
136        return false;  // should never be reached, because should only be invoked after returnFromExistsLogic() == true
137    }
138
139    private final static Logger log = LoggerFactory.getLogger(ArithmeticQualifier.class);
140}