001package jmri.jmrit.logixng.expressions;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.*;
006
007import javax.annotation.Nonnull;
008
009import jmri.*;
010import jmri.Conditional;
011import jmri.jmrit.logixng.*;
012import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean;
013import jmri.jmrit.logixng.util.ReferenceUtil;
014import jmri.jmrit.logixng.util.parser.*;
015import jmri.util.TypeConversionUtil;
016
017/**
018 * Evaluates the state of a Conditional.
019 *
020 * @author Daniel Bergqvist Copyright 2018
021 */
022public class ExpressionConditional extends AbstractDigitalExpression
023        implements PropertyChangeListener {
024
025    private final LogixNG_SelectNamedBean<Conditional> _selectNamedBean =
026            new LogixNG_SelectNamedBean<>(
027                    this, Conditional.class, InstanceManager.getDefault(ConditionalManager.class), this);
028    private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is;
029    private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct;
030    private ConditionalState _conditionalState = ConditionalState.False;
031    private String _stateReference = "";
032    private String _stateLocalVariable = "";
033    private String _stateFormula = "";
034    private ExpressionNode _stateExpressionNode;
035
036    public ExpressionConditional(String sys, String user)
037            throws BadUserNameException, BadSystemNameException {
038        super(sys, user);
039    }
040
041    @Override
042    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
043        DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class);
044        String sysName = systemNames.get(getSystemName());
045        String userName = userNames.get(getSystemName());
046        if (sysName == null) sysName = manager.getAutoSystemName();
047        ExpressionConditional copy = new ExpressionConditional(sysName, userName);
048        copy.setComment(getComment());
049        copy.set_Is_IsNot(_is_IsNot);
050        _selectNamedBean.copy(copy._selectNamedBean);
051        copy.setConditionalState(_conditionalState);
052        copy.setStateAddressing(_stateAddressing);
053        copy.setStateReference(_stateReference);
054        copy.setStateLocalVariable(_stateLocalVariable);
055        copy.setStateFormula(_stateFormula);
056        return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames);
057    }
058
059    public LogixNG_SelectNamedBean<Conditional> getSelectNamedBean() {
060        return _selectNamedBean;
061    }
062
063    public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) {
064        _is_IsNot = is_IsNot;
065    }
066
067    public Is_IsNot_Enum get_Is_IsNot() {
068        return _is_IsNot;
069    }
070
071    public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException {
072        _stateAddressing = addressing;
073        parseStateFormula();
074    }
075
076    public NamedBeanAddressing getStateAddressing() {
077        return _stateAddressing;
078    }
079
080    public void setConditionalState(ConditionalState state) {
081        _conditionalState = state;
082    }
083
084    public ConditionalState getConditionalState() {
085        return _conditionalState;
086    }
087
088    public void setStateReference(@Nonnull String reference) {
089        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
090            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
091        }
092        _stateReference = reference;
093    }
094
095    public String getStateReference() {
096        return _stateReference;
097    }
098
099    public void setStateLocalVariable(@Nonnull String localVariable) {
100        _stateLocalVariable = localVariable;
101    }
102
103    public String getStateLocalVariable() {
104        return _stateLocalVariable;
105    }
106
107    public void setStateFormula(@Nonnull String formula) throws ParserException {
108        _stateFormula = formula;
109        parseStateFormula();
110    }
111
112    public String getStateFormula() {
113        return _stateFormula;
114    }
115
116    private void parseStateFormula() throws ParserException {
117        if (_stateAddressing == NamedBeanAddressing.Formula) {
118            Map<String, Variable> variables = new HashMap<>();
119
120            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
121            _stateExpressionNode = parser.parseExpression(_stateFormula);
122        } else {
123            _stateExpressionNode = null;
124        }
125    }
126
127    /** {@inheritDoc} */
128    @Override
129    public Category getCategory() {
130        return Category.ITEM;
131    }
132
133    private String getNewState() throws JmriException {
134
135        switch (_stateAddressing) {
136            case Reference:
137                return ReferenceUtil.getReference(
138                        getConditionalNG().getSymbolTable(), _stateReference);
139
140            case LocalVariable:
141                SymbolTable symbolTable = getConditionalNG().getSymbolTable();
142                return TypeConversionUtil
143                        .convertToString(symbolTable.getValue(_stateLocalVariable), false);
144
145            case Formula:
146                return _stateExpressionNode != null
147                        ? TypeConversionUtil.convertToString(
148                                _stateExpressionNode.calculate(
149                                        getConditionalNG().getSymbolTable()), false)
150                        : null;
151
152            default:
153                throw new IllegalArgumentException("invalid _addressing state: " + _stateAddressing.name());
154        }
155    }
156
157    /** {@inheritDoc} */
158    @Override
159    public boolean evaluate() throws JmriException {
160        Conditional conditional = _selectNamedBean.evaluateNamedBean(getConditionalNG());
161
162        if (conditional == null) return false;
163
164        ConditionalState checkConditionalState;
165
166        if ((_stateAddressing == NamedBeanAddressing.Direct)) {
167            checkConditionalState = _conditionalState;
168        } else {
169            checkConditionalState = ConditionalState.valueOf(getNewState());
170        }
171
172        if (_is_IsNot == Is_IsNot_Enum.Is) {
173            return conditional.getState() == checkConditionalState.getID();
174        } else {
175            return conditional.getState() != checkConditionalState.getID();
176        }
177    }
178
179    @Override
180    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
181        throw new UnsupportedOperationException("Not supported.");
182    }
183
184    @Override
185    public int getChildCount() {
186        return 0;
187    }
188
189    @Override
190    public String getShortDescription(Locale locale) {
191        return Bundle.getMessage(locale, "Conditional_Short");
192    }
193
194    @Override
195    public String getLongDescription(Locale locale) {
196        String namedBean = _selectNamedBean.getDescription(locale);
197        String state;
198
199        switch (_stateAddressing) {
200            case Direct:
201                state = Bundle.getMessage(locale, "AddressByDirect", _conditionalState.toString());
202                break;
203
204            case Reference:
205                state = Bundle.getMessage(locale, "AddressByReference", _stateReference);
206                break;
207
208            case LocalVariable:
209                state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable);
210                break;
211
212            case Formula:
213                state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula);
214                break;
215
216            default:
217                throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name());
218        }
219
220        return Bundle.getMessage(locale, "Conditional_Long", namedBean, _is_IsNot.toString(), state);
221    }
222
223    /** {@inheritDoc} */
224    @Override
225    public void setup() {
226        // Do nothing
227    }
228
229    /** {@inheritDoc} */
230    @Override
231    public void registerListenersForThisClass() {
232        if (!_listenersAreRegistered) {
233            _selectNamedBean.addPropertyChangeListener("KnownState", this);
234            _selectNamedBean.registerListeners();
235            _listenersAreRegistered = true;
236        }
237    }
238
239    /** {@inheritDoc} */
240    @Override
241    public void unregisterListenersForThisClass() {
242        if (_listenersAreRegistered) {
243            _selectNamedBean.removePropertyChangeListener("KnownState", this);
244            _selectNamedBean.unregisterListeners();
245            _listenersAreRegistered = false;
246        }
247    }
248
249    /** {@inheritDoc} */
250    @Override
251    public void propertyChange(PropertyChangeEvent evt) {
252        getConditionalNG().execute();
253    }
254
255    /** {@inheritDoc} */
256    @Override
257    public void disposeMe() {
258    }
259
260
261
262    public enum ConditionalState {
263        False(Conditional.FALSE, Bundle.getMessage("ConditionalStateFalse")),
264        True(Conditional.TRUE, Bundle.getMessage("ConditionalStateTrue")),
265        Other(-1, Bundle.getMessage("ConditionalOtherStatus"));
266
267        private final int _id;
268        private final String _text;
269
270        private ConditionalState(int id, String text) {
271            this._id = id;
272            this._text = text;
273        }
274
275        static public ConditionalState get(int id) {
276            switch (id) {
277                case Conditional.FALSE:
278                    return False;
279
280                case Conditional.TRUE:
281                    return True;
282
283                default:
284                    return Other;
285            }
286        }
287
288        public int getID() {
289            return _id;
290        }
291
292        @Override
293        public String toString() {
294            return _text;
295        }
296
297    }
298
299    /** {@inheritDoc} */
300    @Override
301    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
302        log.debug("getUsageReport :: ExpressionConditional: bean = {}, report = {}", cdl, report);
303        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression);
304    }
305
306    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionConditional.class);
307
308}