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