001package jmri.jmrit.logixng.expressions;
002
003import java.beans.*;
004import java.util.*;
005
006import javax.annotation.Nonnull;
007
008import jmri.*;
009import jmri.jmrit.logix.OBlock;
010import jmri.jmrit.logix.OBlockManager;
011import jmri.jmrit.logixng.*;
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 oblock.
020 *
021 * @author Daniel Bergqvist Copyright 2018
022 */
023public class ExpressionOBlock extends AbstractDigitalExpression
024        implements PropertyChangeListener, VetoableChangeListener {
025
026    private NamedBeanAddressing _addressing = NamedBeanAddressing.Direct;
027    private NamedBeanHandle<OBlock> _oblockHandle;
028    private String _reference = "";
029    private String _localVariable = "";
030    private String _formula = "";
031    private ExpressionNode _expressionNode;
032    private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is;
033    private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct;
034    private OBlock.OBlockStatus _oblockState = OBlock.OBlockStatus.Unoccupied;
035    private String _stateReference = "";
036    private String _stateLocalVariable = "";
037    private String _stateFormula = "";
038    private ExpressionNode _stateExpressionNode;
039
040    public ExpressionOBlock(String sys, String user)
041            throws BadUserNameException, BadSystemNameException {
042        super(sys, user);
043    }
044
045    @Override
046    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException {
047        DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class);
048        String sysName = systemNames.get(getSystemName());
049        String userName = userNames.get(getSystemName());
050        if (sysName == null) sysName = manager.getAutoSystemName();
051        ExpressionOBlock copy = new ExpressionOBlock(sysName, userName);
052        copy.setComment(getComment());
053        if (_oblockHandle != null) copy.setOBlock(_oblockHandle);
054        copy.setBeanState(_oblockState);
055        copy.setAddressing(_addressing);
056        copy.setFormula(_formula);
057        copy.setLocalVariable(_localVariable);
058        copy.setReference(_reference);
059        copy.set_Is_IsNot(_is_IsNot);
060        copy.setStateAddressing(_stateAddressing);
061        copy.setStateFormula(_stateFormula);
062        copy.setStateLocalVariable(_stateLocalVariable);
063        copy.setStateReference(_stateReference);
064        return manager.registerExpression(copy);
065    }
066
067    public void setOBlock(@Nonnull String oblockName) {
068        assertListenersAreNotRegistered(log, "setOBlock");
069        OBlock oblock = InstanceManager.getDefault(OBlockManager.class).getOBlock(oblockName);
070        if (oblock != null) {
071            setOBlock(oblock);
072        } else {
073            removeOBlock();
074            log.error("oblock \"{}\" is not found", oblockName);
075        }
076    }
077
078    public void setOBlock(@Nonnull NamedBeanHandle<OBlock> handle) {
079        assertListenersAreNotRegistered(log, "setOBlock");
080        _oblockHandle = handle;
081        InstanceManager.getDefault(OBlockManager.class).addVetoableChangeListener(this);
082    }
083
084    public void setOBlock(@Nonnull OBlock oblock) {
085        assertListenersAreNotRegistered(log, "setOBlock");
086        setOBlock(InstanceManager.getDefault(NamedBeanHandleManager.class)
087                .getNamedBeanHandle(oblock.getDisplayName(), oblock));
088    }
089
090    public void removeOBlock() {
091        assertListenersAreNotRegistered(log, "setOBlock");
092        if (_oblockHandle != null) {
093            InstanceManager.getDefault(OBlockManager.class).removeVetoableChangeListener(this);
094            _oblockHandle = null;
095        }
096    }
097
098    public NamedBeanHandle<OBlock> getOBlock() {
099        return _oblockHandle;
100    }
101
102    public void setAddressing(NamedBeanAddressing addressing) throws ParserException {
103        _addressing = addressing;
104        parseFormula();
105    }
106
107    public NamedBeanAddressing getAddressing() {
108        return _addressing;
109    }
110
111    public void setReference(@Nonnull String reference) {
112        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
113            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
114        }
115        _reference = reference;
116    }
117
118    public String getReference() {
119        return _reference;
120    }
121
122    public void setLocalVariable(@Nonnull String localVariable) {
123        _localVariable = localVariable;
124    }
125
126    public String getLocalVariable() {
127        return _localVariable;
128    }
129
130    public void setFormula(@Nonnull String formula) throws ParserException {
131        _formula = formula;
132        parseFormula();
133    }
134
135    public String getFormula() {
136        return _formula;
137    }
138
139    private void parseFormula() throws ParserException {
140        if (_addressing == NamedBeanAddressing.Formula) {
141            Map<String, Variable> variables = new HashMap<>();
142
143            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
144            _expressionNode = parser.parseExpression(_formula);
145        } else {
146            _expressionNode = null;
147        }
148    }
149
150    public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) {
151        _is_IsNot = is_IsNot;
152    }
153
154    public Is_IsNot_Enum get_Is_IsNot() {
155        return _is_IsNot;
156    }
157
158    public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException {
159        _stateAddressing = addressing;
160        parseStateFormula();
161    }
162
163    public NamedBeanAddressing getStateAddressing() {
164        return _stateAddressing;
165    }
166
167    public void setBeanState(OBlock.OBlockStatus state) {
168        _oblockState = state;
169    }
170
171    public OBlock.OBlockStatus getBeanState() {
172        return _oblockState;
173    }
174
175    public void setStateReference(@Nonnull String reference) {
176        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
177            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
178        }
179        _stateReference = reference;
180    }
181
182    public String getStateReference() {
183        return _stateReference;
184    }
185
186    public void setStateLocalVariable(@Nonnull String localVariable) {
187        _stateLocalVariable = localVariable;
188    }
189
190    public String getStateLocalVariable() {
191        return _stateLocalVariable;
192    }
193
194    public void setStateFormula(@Nonnull String formula) throws ParserException {
195        _stateFormula = formula;
196        parseStateFormula();
197    }
198
199    public String getStateFormula() {
200        return _stateFormula;
201    }
202
203    private void parseStateFormula() throws ParserException {
204        if (_stateAddressing == NamedBeanAddressing.Formula) {
205            Map<String, Variable> variables = new HashMap<>();
206
207            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
208            _stateExpressionNode = parser.parseExpression(_stateFormula);
209        } else {
210            _stateExpressionNode = null;
211        }
212    }
213
214    @Override
215    public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException {
216        if ("CanDelete".equals(evt.getPropertyName())) { // No I18N
217            if (evt.getOldValue() instanceof OBlock) {
218                if (evt.getOldValue().equals(getOBlock().getBean())) {
219                    PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null);
220                    throw new PropertyVetoException(Bundle.getMessage("OBlock_OBlockInUseOBlockExpressionVeto", getDisplayName()), e); // NOI18N
221                }
222            }
223        } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N
224            if (evt.getOldValue() instanceof OBlock) {
225                if (evt.getOldValue().equals(getOBlock().getBean())) {
226                    removeOBlock();
227                }
228            }
229        }
230    }
231
232    /** {@inheritDoc} */
233    @Override
234    public Category getCategory() {
235        return Category.ITEM;
236    }
237
238    private String getNewState() throws JmriException {
239
240        switch (_stateAddressing) {
241            case Reference:
242                return ReferenceUtil.getReference(
243                        getConditionalNG().getSymbolTable(), _stateReference);
244
245            case LocalVariable:
246                SymbolTable symbolTable = getConditionalNG().getSymbolTable();
247                return TypeConversionUtil
248                        .convertToString(symbolTable.getValue(_stateLocalVariable), false);
249
250            case Formula:
251                return _stateExpressionNode != null
252                        ? TypeConversionUtil.convertToString(
253                                _stateExpressionNode.calculate(
254                                        getConditionalNG().getSymbolTable()), false)
255                        : null;
256
257            default:
258                throw new IllegalArgumentException("invalid _addressing state: " + _stateAddressing.name());
259        }
260    }
261
262    /** {@inheritDoc} */
263    @Override
264    public boolean evaluate() throws JmriException {
265        OBlock oblock;
266
267//        System.out.format("ExpressionOBlock.execute: %s%n", getLongDescription());
268
269        switch (_addressing) {
270            case Direct:
271                oblock = _oblockHandle != null ? _oblockHandle.getBean() : null;
272                break;
273
274            case Reference:
275                String ref = ReferenceUtil.getReference(
276                        getConditionalNG().getSymbolTable(), _reference);
277                oblock = InstanceManager.getDefault(OBlockManager.class)
278                        .getNamedBean(ref);
279                break;
280
281            case LocalVariable:
282                SymbolTable symbolTable = getConditionalNG().getSymbolTable();
283                oblock = InstanceManager.getDefault(OBlockManager.class)
284                        .getNamedBean(TypeConversionUtil
285                                .convertToString(symbolTable.getValue(_localVariable), false));
286                break;
287
288            case Formula:
289                oblock = _expressionNode != null ?
290                        InstanceManager.getDefault(OBlockManager.class)
291                                .getNamedBean(TypeConversionUtil
292                                        .convertToString(_expressionNode.calculate(
293                                                getConditionalNG().getSymbolTable()), false))
294                        : null;
295                break;
296
297            default:
298                throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name());
299        }
300
301//        System.out.format("ExpressionOBlock.execute: oblock: %s%n", oblock);
302
303        if (oblock == null) {
304//            log.warn("oblock is null");
305            return false;
306        }
307
308        OBlock.OBlockStatus checkOBlockState;
309
310        if ((_stateAddressing == NamedBeanAddressing.Direct)) {
311            checkOBlockState = _oblockState;
312        } else {
313            checkOBlockState = OBlock.OBlockStatus.valueOf(getNewState());
314        }
315
316        if (_is_IsNot == Is_IsNot_Enum.Is) {
317            return oblock.getState() == checkOBlockState.getStatus();
318        } else {
319            return oblock.getState() != checkOBlockState.getStatus();
320        }
321    }
322
323    @Override
324    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
325        throw new UnsupportedOperationException("Not supported.");
326    }
327
328    @Override
329    public int getChildCount() {
330        return 0;
331    }
332
333    @Override
334    public String getShortDescription(Locale locale) {
335        return Bundle.getMessage(locale, "OBlock_Short");
336    }
337
338    @Override
339    public String getLongDescription(Locale locale) {
340        String namedBean;
341        String state;
342
343        switch (_addressing) {
344            case Direct:
345                String oblockName;
346                if (_oblockHandle != null) {
347                    oblockName = _oblockHandle.getBean().getDisplayName();
348                } else {
349                    oblockName = Bundle.getMessage(locale, "BeanNotSelected");
350                }
351                namedBean = Bundle.getMessage(locale, "AddressByDirect", oblockName);
352                break;
353
354            case Reference:
355                namedBean = Bundle.getMessage(locale, "AddressByReference", _reference);
356                break;
357
358            case LocalVariable:
359                namedBean = Bundle.getMessage(locale, "AddressByLocalVariable", _localVariable);
360                break;
361
362            case Formula:
363                namedBean = Bundle.getMessage(locale, "AddressByFormula", _formula);
364                break;
365
366            default:
367                throw new IllegalArgumentException("invalid _addressing state: " + _addressing.name());
368        }
369
370        switch (_stateAddressing) {
371            case Direct:
372                state = Bundle.getMessage(locale, "AddressByDirect", _oblockState.getDescr());
373                break;
374
375            case Reference:
376                state = Bundle.getMessage(locale, "AddressByReference", _stateReference);
377                break;
378
379            case LocalVariable:
380                state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable);
381                break;
382
383            case Formula:
384                state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula);
385                break;
386
387            default:
388                throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name());
389        }
390
391        return Bundle.getMessage(locale, "OBlock_Long", namedBean, _is_IsNot.toString(), state);
392    }
393
394    /** {@inheritDoc} */
395    @Override
396    public void setup() {
397        // Do nothing
398    }
399
400    /** {@inheritDoc} */
401    @Override
402    public void registerListenersForThisClass() {
403        if (!_listenersAreRegistered && (_oblockHandle != null)) {
404            _oblockHandle.getBean().addPropertyChangeListener("state", this);
405            _listenersAreRegistered = true;
406        }
407    }
408
409    /** {@inheritDoc} */
410    @Override
411    public void unregisterListenersForThisClass() {
412        if (_listenersAreRegistered) {
413            _oblockHandle.getBean().removePropertyChangeListener("state", this);
414            _listenersAreRegistered = false;
415        }
416    }
417
418    /** {@inheritDoc} */
419    @Override
420    public void propertyChange(PropertyChangeEvent evt) {
421        getConditionalNG().execute();
422    }
423
424    /** {@inheritDoc} */
425    @Override
426    public void disposeMe() {
427    }
428
429    /** {@inheritDoc} */
430    @Override
431    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
432        log.debug("getUsageReport :: ExpressionOBlock: bean = {}, report = {}", cdl, report);
433        if (getOBlock() != null && bean.equals(getOBlock().getBean())) {
434            report.add(new NamedBeanUsageReport("LogixNGExpression", cdl, getLongDescription()));
435        }
436    }
437
438    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionOBlock.class);
439
440}