001package jmri.jmrit.logixng.expressions;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.beans.VetoableChangeListener;
006import java.util.*;
007
008import javax.annotation.Nonnull;
009
010import jmri.*;
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.jmrit.logixng.util.parser.ExpressionNode;
016import jmri.jmrit.logixng.util.parser.RecursiveDescentParser;
017import jmri.util.TypeConversionUtil;
018
019/**
020 * Evaluates the state of a SignalMast.
021 *
022 * @author Daniel Bergqvist Copyright 2020
023 */
024public class ExpressionSignalMast extends AbstractDigitalExpression
025        implements PropertyChangeListener, VetoableChangeListener {
026
027    private final LogixNG_SelectNamedBean<SignalMast> _selectNamedBean =
028            new LogixNG_SelectNamedBean<>(
029                    this, SignalMast.class, InstanceManager.getDefault(SignalMastManager.class), this);
030
031    private NamedBeanAddressing _queryAddressing = NamedBeanAddressing.Direct;
032    private QueryType _queryType = QueryType.Aspect;
033    private String _queryReference = "";
034    private String _queryLocalVariable = "";
035    private String _queryFormula = "";
036    private ExpressionNode _queryExpressionNode;
037
038    private NamedBeanAddressing _aspectAddressing = NamedBeanAddressing.Direct;
039    private String _signalMastAspect = "";
040    private String _aspectReference = "";
041    private String _aspectLocalVariable = "";
042    private String _aspectFormula = "";
043    private ExpressionNode _aspectExpressionNode;
044
045    private final LogixNG_SelectNamedBean<SignalMast> _selectExampleNamedBean =
046            new LogixNG_SelectNamedBean<>(
047                    this, SignalMast.class, InstanceManager.getDefault(SignalMastManager.class), this);
048
049
050    public ExpressionSignalMast(String sys, String user)
051            throws BadUserNameException, BadSystemNameException {
052        super(sys, user);
053    }
054
055    @Override
056    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException {
057        DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class);
058        String sysName = systemNames.get(getSystemName());
059        String userName = userNames.get(getSystemName());
060        if (sysName == null) sysName = manager.getAutoSystemName();
061        ExpressionSignalMast copy = new ExpressionSignalMast(sysName, userName);
062        copy.setComment(getComment());
063        _selectNamedBean.copy(copy._selectNamedBean);
064        copy.setAspect(_signalMastAspect);
065        copy.setQueryAddressing(_queryAddressing);
066        copy.setQueryType(_queryType);
067        copy.setQueryFormula(_queryFormula);
068        copy.setQueryLocalVariable(_queryLocalVariable);
069        copy.setQueryReference(_queryReference);
070        copy.setAspectAddressing(_aspectAddressing);
071        copy.setAspectFormula(_aspectFormula);
072        copy.setAspectLocalVariable(_aspectLocalVariable);
073        copy.setAspectReference(_aspectReference);
074        _selectExampleNamedBean.copy(copy._selectExampleNamedBean);
075        return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames);
076    }
077
078    public LogixNG_SelectNamedBean<SignalMast> getSelectNamedBean() {
079        return _selectNamedBean;
080    }
081
082    public LogixNG_SelectNamedBean<SignalMast> getSelectExampleNamedBean() {
083        return _selectExampleNamedBean;
084    }
085
086    public void setQueryAddressing(NamedBeanAddressing addressing) throws ParserException {
087        _queryAddressing = addressing;
088        parseQueryFormula();
089    }
090
091    public NamedBeanAddressing getQueryAddressing() {
092        return _queryAddressing;
093    }
094
095    public void setQueryType(QueryType queryType) {
096        _queryType = queryType;
097    }
098
099    public QueryType getQueryType() {
100        return _queryType;
101    }
102
103    public void setQueryReference(@Nonnull String reference) {
104        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
105            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
106        }
107        _queryReference = reference;
108    }
109
110    public String getQueryReference() {
111        return _queryReference;
112    }
113
114    public void setQueryLocalVariable(@Nonnull String localVariable) {
115        _queryLocalVariable = localVariable;
116    }
117
118    public String getQueryLocalVariable() {
119        return _queryLocalVariable;
120    }
121
122    public void setQueryFormula(@Nonnull String formula) throws ParserException {
123        _queryFormula = formula;
124        parseQueryFormula();
125    }
126
127    public String getQueryFormula() {
128        return _queryFormula;
129    }
130
131    private void parseQueryFormula() throws ParserException {
132        if (_queryAddressing == NamedBeanAddressing.Formula) {
133            Map<String, Variable> variables = new HashMap<>();
134
135            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
136            _queryExpressionNode = parser.parseExpression(_queryFormula);
137        } else {
138            _queryExpressionNode = null;
139        }
140    }
141
142    public void setAspectAddressing(NamedBeanAddressing addressing) throws ParserException {
143        _aspectAddressing = addressing;
144        parseAspectFormula();
145    }
146
147    public NamedBeanAddressing getAspectAddressing() {
148        return _aspectAddressing;
149    }
150
151    public void setAspect(String aspect) {
152        if (aspect == null) _signalMastAspect = "";
153        else _signalMastAspect = aspect;
154    }
155
156    public String getAspect() {
157        return _signalMastAspect;
158    }
159
160    public void setAspectReference(@Nonnull String reference) {
161        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
162            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
163        }
164        _aspectReference = reference;
165    }
166
167    public String getAspectReference() {
168        return _aspectReference;
169    }
170
171    public void setAspectLocalVariable(@Nonnull String localVariable) {
172        _aspectLocalVariable = localVariable;
173    }
174
175    public String getAspectLocalVariable() {
176        return _aspectLocalVariable;
177    }
178
179    public void setAspectFormula(@Nonnull String formula) throws ParserException {
180        _aspectFormula = formula;
181        parseAspectFormula();
182    }
183
184    public String getAspectFormula() {
185        return _aspectFormula;
186    }
187
188    private void parseAspectFormula() throws ParserException {
189        if (_aspectAddressing == NamedBeanAddressing.Formula) {
190            Map<String, Variable> variables = new HashMap<>();
191
192            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
193            _aspectExpressionNode = parser.parseExpression(_aspectFormula);
194        } else {
195            _aspectExpressionNode = null;
196        }
197    }
198
199    /** {@inheritDoc} */
200    @Override
201    public Category getCategory() {
202        return Category.ITEM;
203    }
204
205    private String getNewAspect(ConditionalNG conditionalNG) throws JmriException {
206
207        switch (_aspectAddressing) {
208            case Direct:
209                return _signalMastAspect;
210
211            case Reference:
212                return ReferenceUtil.getReference(
213                        conditionalNG.getSymbolTable(), _aspectReference);
214
215            case LocalVariable:
216                SymbolTable symbolTable = conditionalNG.getSymbolTable();
217                return TypeConversionUtil
218                        .convertToString(symbolTable.getValue(_aspectLocalVariable), false);
219
220            case Formula:
221                return _aspectExpressionNode != null
222                        ? TypeConversionUtil.convertToString(
223                                _aspectExpressionNode.calculate(
224                                        conditionalNG.getSymbolTable()), false)
225                        : "";
226
227            default:
228                throw new IllegalArgumentException("invalid _aspectAddressing state: " + _aspectAddressing.name());
229        }
230    }
231
232    private QueryType getQuery(ConditionalNG conditionalNG) throws JmriException {
233
234        String oper = "";
235        try {
236            switch (_queryAddressing) {
237                case Direct:
238                    return _queryType;
239
240                case Reference:
241                    oper = ReferenceUtil.getReference(
242                            conditionalNG.getSymbolTable(), _queryReference);
243                    return QueryType.valueOf(oper);
244
245                case LocalVariable:
246                    SymbolTable symbolTable =
247                            conditionalNG.getSymbolTable();
248                    oper = TypeConversionUtil
249                            .convertToString(symbolTable.getValue(_queryLocalVariable), false);
250                    return QueryType.valueOf(oper);
251
252                case Formula:
253                    if (_aspectExpressionNode != null) {
254                        oper = TypeConversionUtil.convertToString(
255                                _queryExpressionNode.calculate(
256                                        conditionalNG.getSymbolTable()), false);
257                        return QueryType.valueOf(oper);
258                    } else {
259                        return null;
260                    }
261                default:
262                    throw new IllegalArgumentException("invalid _addressing state: " + _queryAddressing.name());
263            }
264        } catch (IllegalArgumentException e) {
265            throw new JmriException("Unknown query: "+oper, e);
266        }
267    }
268
269    /** {@inheritDoc} */
270    @Override
271    public boolean evaluate() throws JmriException {
272        final ConditionalNG conditionalNG = getConditionalNG();
273
274        SignalMast signalMast = _selectNamedBean.evaluateNamedBean(conditionalNG);
275
276        if (signalMast == null) return false;
277
278        QueryType query = getQuery(conditionalNG);
279
280        boolean result = false;
281
282        switch (query) {
283            case Aspect:
284                if (signalMast.getAspect() != null) {
285                    result = getNewAspect(conditionalNG).equals(signalMast.getAspect());
286                }
287                break;
288            case NotAspect:
289                if (signalMast.getAspect() != null) {
290                    result = ! getNewAspect(conditionalNG).equals(signalMast.getAspect());
291                }
292                break;
293            case Lit:
294                result = signalMast.getLit();
295                break;
296            case NotLit:
297                result = ! signalMast.getLit();
298                break;
299            case Held:
300                result = signalMast.getHeld();
301                break;
302            case NotHeld:
303                result = ! signalMast.getHeld();
304                break;
305            case IsPermissiveSmlDisabled:
306                result = signalMast.isPermissiveSmlDisabled();
307                break;
308            case IsPermissiveSmlNotDisabled:
309                result = ! signalMast.isPermissiveSmlDisabled();
310                break;
311            default:
312                throw new RuntimeException("Unknown enum: "+_queryType.name());
313        }
314
315        return result;
316    }
317
318    @Override
319    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
320        throw new UnsupportedOperationException("Not supported.");
321    }
322
323    @Override
324    public int getChildCount() {
325        return 0;
326    }
327
328    @Override
329    public String getShortDescription(Locale locale) {
330        return Bundle.getMessage(locale, "SignalMast_Short");
331    }
332
333    @Override
334    public String getLongDescription(Locale locale) {
335        String namedBean = _selectNamedBean.getDescription(locale);
336        String query;
337        String aspect;
338
339        switch (_queryAddressing) {
340            case Direct:
341                query = Bundle.getMessage(locale, "AddressByDirect", _queryType._text);
342                break;
343
344            case Reference:
345                query = Bundle.getMessage(locale, "AddressByReference", _queryReference);
346                break;
347
348            case LocalVariable:
349                query = Bundle.getMessage(locale, "AddressByLocalVariable", _queryLocalVariable);
350                break;
351
352            case Formula:
353                query = Bundle.getMessage(locale, "AddressByFormula", _queryFormula);
354                break;
355
356            default:
357                throw new IllegalArgumentException("invalid _queryAddressing state: " + _queryAddressing.name());
358        }
359
360        switch (_aspectAddressing) {
361            case Direct:
362                aspect = Bundle.getMessage(locale, "AddressByDirect", _signalMastAspect);
363                break;
364
365            case Reference:
366                aspect = Bundle.getMessage(locale, "AddressByReference", _aspectReference);
367                break;
368
369            case LocalVariable:
370                aspect = Bundle.getMessage(locale, "AddressByLocalVariable", _aspectLocalVariable);
371                break;
372
373            case Formula:
374                aspect = Bundle.getMessage(locale, "AddressByFormula", _aspectFormula);
375                break;
376
377            default:
378                throw new IllegalArgumentException("invalid _stateAddressing state: " + _aspectAddressing.name());
379        }
380
381        if (_queryAddressing == NamedBeanAddressing.Direct) {
382            if (_queryType == QueryType.Aspect) {
383                return Bundle.getMessage(locale, "SignalMast_LongAspect", namedBean, aspect);
384            } if (_queryType == QueryType.NotAspect) {
385                return Bundle.getMessage(locale, "SignalMast_LongNotAspect", namedBean, aspect);
386            } else {
387                return Bundle.getMessage(locale, "SignalMast_Long", namedBean, query);
388            }
389        } else {
390            return Bundle.getMessage(locale, "SignalMast_LongUnknownOper", namedBean, query, aspect);
391        }
392    }
393
394    /** {@inheritDoc} */
395    @Override
396    public void setup() {
397        // Do nothing
398    }
399
400    /** {@inheritDoc} */
401    @Override
402    public void registerListenersForThisClass() {
403        SignalMast signalMast = _selectNamedBean.getNamedBeanIfDirectAddressing();
404
405        if (!_listenersAreRegistered && (signalMast != null)) {
406            switch (_queryType) {
407                case Aspect:
408                case NotAspect:
409                    signalMast.addPropertyChangeListener("Aspect", this);
410                    break;
411
412                case Lit:
413                case NotLit:
414                    signalMast.addPropertyChangeListener("Lit", this);
415                    break;
416
417                case Held:
418                case NotHeld:
419                    signalMast.addPropertyChangeListener("Held", this);
420                    break;
421
422                case IsPermissiveSmlDisabled:
423                case IsPermissiveSmlNotDisabled:
424                    signalMast.addPropertyChangeListener("PermissiveSmlDisabled", this);
425                    break;
426
427                default:
428                    throw new RuntimeException("Unknown enum: "+_queryType.name());
429            }
430            _selectNamedBean.registerListeners();
431            _listenersAreRegistered = true;
432        }
433    }
434
435    /** {@inheritDoc} */
436    @Override
437    public void unregisterListenersForThisClass() {
438        SignalMast signalMast = _selectNamedBean.getNamedBeanIfDirectAddressing();
439
440        if (_listenersAreRegistered && (signalMast != null)) {
441            switch (_queryType) {
442                case Aspect:
443                case NotAspect:
444                    signalMast.removePropertyChangeListener("Aspect", this);
445                    break;
446
447                case Lit:
448                case NotLit:
449                    signalMast.removePropertyChangeListener("Lit", this);
450                    break;
451
452                case Held:
453                case NotHeld:
454                    signalMast.removePropertyChangeListener("Held", this);
455                    break;
456
457                case IsPermissiveSmlDisabled:
458                case IsPermissiveSmlNotDisabled:
459                    signalMast.removePropertyChangeListener("PermissiveSmlDisabled", this);
460                    break;
461
462                default:
463                    throw new RuntimeException("Unknown enum: "+_queryType.name());
464            }
465            _selectNamedBean.unregisterListeners();
466            _listenersAreRegistered = false;
467        }
468    }
469
470    /** {@inheritDoc} */
471    @Override
472    public void propertyChange(PropertyChangeEvent evt) {
473        getConditionalNG().execute();
474    }
475
476    /** {@inheritDoc} */
477    @Override
478    public void disposeMe() {
479    }
480
481
482
483    public enum QueryType {
484        Aspect(Bundle.getMessage("SignalMastQueryType_Aspect")),
485        NotAspect(Bundle.getMessage("SignalMastQueryType_NotAspect")),
486        Lit(Bundle.getMessage("SignalMastQueryType_Lit")),
487        NotLit(Bundle.getMessage("SignalMastQueryType_NotLit")),
488        Held(Bundle.getMessage("SignalMastQueryType_Held")),
489        NotHeld(Bundle.getMessage("SignalMastQueryType_NotHeld")),
490        IsPermissiveSmlDisabled(Bundle.getMessage("SignalMastQueryType_IsPermissiveSmlDisabled")),
491        IsPermissiveSmlNotDisabled(Bundle.getMessage("SignalMastQueryType_IsPermissiveSmlNotDisabled"));
492
493        private final String _text;
494
495        private QueryType(String text) {
496            this._text = text;
497        }
498
499        @Override
500        public String toString() {
501            return _text;
502        }
503
504    }
505
506    /** {@inheritDoc} */
507    @Override
508    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
509        log.debug("getUsageReport :: ExpressionSignalMast: bean = {}, report = {}", cdl, report);
510        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action);
511        _selectExampleNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action);
512    }
513
514    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionSignalMast.class);
515
516}