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.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 * This expression sets the state of a oblock.
021 *
022 * @author Daniel Bergqvist Copyright 2018
023 */
024public class ExpressionOBlock extends AbstractDigitalExpression
025        implements PropertyChangeListener {
026
027    private final LogixNG_SelectNamedBean<OBlock> _selectNamedBean =
028            new LogixNG_SelectNamedBean<>(
029                    this, OBlock.class, InstanceManager.getDefault(OBlockManager.class), this);
030    private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is;
031    private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct;
032    private OBlock.OBlockStatus _oblockState = OBlock.OBlockStatus.Unoccupied;
033    private String _stateReference = "";
034    private String _stateLocalVariable = "";
035    private String _stateFormula = "";
036    private ExpressionNode _stateExpressionNode;
037
038    public ExpressionOBlock(String sys, String user)
039            throws BadUserNameException, BadSystemNameException {
040        super(sys, user);
041    }
042
043    @Override
044    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException {
045        DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class);
046        String sysName = systemNames.get(getSystemName());
047        String userName = userNames.get(getSystemName());
048        if (sysName == null) sysName = manager.getAutoSystemName();
049        ExpressionOBlock copy = new ExpressionOBlock(sysName, userName);
050        copy.setComment(getComment());
051        _selectNamedBean.copy(copy._selectNamedBean);
052        copy.setBeanState(_oblockState);
053        copy.set_Is_IsNot(_is_IsNot);
054        copy.setStateAddressing(_stateAddressing);
055        copy.setStateFormula(_stateFormula);
056        copy.setStateLocalVariable(_stateLocalVariable);
057        copy.setStateReference(_stateReference);
058        return manager.registerExpression(copy);
059    }
060
061    public LogixNG_SelectNamedBean<OBlock> getSelectNamedBean() {
062        return _selectNamedBean;
063    }
064
065    public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) {
066        _is_IsNot = is_IsNot;
067    }
068
069    public Is_IsNot_Enum get_Is_IsNot() {
070        return _is_IsNot;
071    }
072
073    public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException {
074        _stateAddressing = addressing;
075        parseStateFormula();
076    }
077
078    public NamedBeanAddressing getStateAddressing() {
079        return _stateAddressing;
080    }
081
082    public void setBeanState(OBlock.OBlockStatus state) {
083        _oblockState = state;
084    }
085
086    public OBlock.OBlockStatus getBeanState() {
087        return _oblockState;
088    }
089
090    public void setStateReference(@Nonnull String reference) {
091        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
092            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
093        }
094        _stateReference = reference;
095    }
096
097    public String getStateReference() {
098        return _stateReference;
099    }
100
101    public void setStateLocalVariable(@Nonnull String localVariable) {
102        _stateLocalVariable = localVariable;
103    }
104
105    public String getStateLocalVariable() {
106        return _stateLocalVariable;
107    }
108
109    public void setStateFormula(@Nonnull String formula) throws ParserException {
110        _stateFormula = formula;
111        parseStateFormula();
112    }
113
114    public String getStateFormula() {
115        return _stateFormula;
116    }
117
118    private void parseStateFormula() throws ParserException {
119        if (_stateAddressing == NamedBeanAddressing.Formula) {
120            Map<String, Variable> variables = new HashMap<>();
121
122            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
123            _stateExpressionNode = parser.parseExpression(_stateFormula);
124        } else {
125            _stateExpressionNode = null;
126        }
127    }
128
129    /** {@inheritDoc} */
130    @Override
131    public Category getCategory() {
132        return Category.ITEM;
133    }
134
135    private String getNewState() throws JmriException {
136
137        switch (_stateAddressing) {
138            case Reference:
139                return ReferenceUtil.getReference(
140                        getConditionalNG().getSymbolTable(), _stateReference);
141
142            case LocalVariable:
143                SymbolTable symbolTable = 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        OBlock oblock = _selectNamedBean.evaluateNamedBean(getConditionalNG());
163
164        if (oblock == null) return false;
165
166        OBlock.OBlockStatus checkOBlockState;
167
168        if ((_stateAddressing == NamedBeanAddressing.Direct)) {
169            checkOBlockState = _oblockState;
170        } else {
171            checkOBlockState = OBlock.OBlockStatus.valueOf(getNewState());
172        }
173
174        int result = checkOBlockState.getStatus() & oblock.getState();
175        if (_is_IsNot == Is_IsNot_Enum.Is) {
176            return result != 0;
177        } else {
178            return result == 0;
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, "OBlock_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", _oblockState.getDescr());
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, "OBlock_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("state", this);
237            _selectNamedBean.registerListeners();
238            _listenersAreRegistered = true;
239        }
240    }
241
242    /** {@inheritDoc} */
243    @Override
244    public void unregisterListenersForThisClass() {
245        if (_listenersAreRegistered) {
246            _selectNamedBean.removePropertyChangeListener("state", 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    /** {@inheritDoc} */
264    @Override
265    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
266        log.debug("getUsageReport :: ExpressionOBlock: bean = {}, report = {}", cdl, report);
267        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression);
268    }
269
270    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionOBlock.class);
271
272}