001package jmri.jmrit.logixng.expressions;
002
003import java.beans.*;
004import java.util.*;
005
006import javax.annotation.Nonnull;
007
008import jmri.*;
009import jmri.jmrit.logixng.*;
010import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean;
011import jmri.jmrit.logixng.util.ReferenceUtil;
012import jmri.jmrit.logixng.util.parser.*;
013import jmri.jmrit.logixng.util.parser.ExpressionNode;
014import jmri.jmrit.logixng.util.parser.RecursiveDescentParser;
015import jmri.util.TypeConversionUtil;
016
017/**
018 * This expression sets the state of a light.
019 *
020 * @author Daniel Bergqvist Copyright 2018
021 */
022public class ExpressionLight extends AbstractDigitalExpression
023        implements PropertyChangeListener {
024
025    private final LogixNG_SelectNamedBean<Light> _selectNamedBean =
026            new LogixNG_SelectNamedBean<>(
027                    this, Light.class, InstanceManager.getDefault(LightManager.class), this);
028    private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is;
029    private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct;
030    private LightState _lightState = LightState.On;
031    private String _stateReference = "";
032    private String _stateLocalVariable = "";
033    private String _stateFormula = "";
034    private ExpressionNode _stateExpressionNode;
035
036    public ExpressionLight(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 ParserException {
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        ExpressionLight copy = new ExpressionLight(sysName, userName);
048        copy.setComment(getComment());
049        _selectNamedBean.copy(copy._selectNamedBean);
050        copy.setBeanState(_lightState);
051        copy.set_Is_IsNot(_is_IsNot);
052        copy.setStateAddressing(_stateAddressing);
053        copy.setStateFormula(_stateFormula);
054        copy.setStateLocalVariable(_stateLocalVariable);
055        copy.setStateReference(_stateReference);
056        return manager.registerExpression(copy);
057    }
058
059    public LogixNG_SelectNamedBean<Light> 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 setBeanState(LightState state) {
081        _lightState = state;
082    }
083
084    public LightState getBeanState() {
085        return _lightState;
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        Light light = _selectNamedBean.evaluateNamedBean(getConditionalNG());
161
162        if (light == null) return false;
163
164        LightState checkLightState;
165
166        if ((_stateAddressing == NamedBeanAddressing.Direct)) {
167            checkLightState = _lightState;
168        } else {
169            checkLightState = LightState.valueOf(getNewState());
170        }
171
172        LightState currentLightState = LightState.get(light.getKnownState());
173        if (_is_IsNot == Is_IsNot_Enum.Is) {
174            return currentLightState == checkLightState;
175        } else {
176            return currentLightState != checkLightState;
177        }
178    }
179
180    @Override
181    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
182        throw new UnsupportedOperationException("Not supported.");
183    }
184
185    @Override
186    public int getChildCount() {
187        return 0;
188    }
189
190    @Override
191    public String getShortDescription(Locale locale) {
192        return Bundle.getMessage(locale, "Light_Short");
193    }
194
195    @Override
196    public String getLongDescription(Locale locale) {
197        String namedBean = _selectNamedBean.getDescription(locale);
198        String state;
199
200        switch (_stateAddressing) {
201            case Direct:
202                state = Bundle.getMessage(locale, "AddressByDirect", _lightState._text);
203                break;
204
205            case Reference:
206                state = Bundle.getMessage(locale, "AddressByReference", _stateReference);
207                break;
208
209            case LocalVariable:
210                state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable);
211                break;
212
213            case Formula:
214                state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula);
215                break;
216
217            default:
218                throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name());
219        }
220
221        return Bundle.getMessage(locale, "Light_Long", namedBean, _is_IsNot.toString(), state);
222    }
223
224    /** {@inheritDoc} */
225    @Override
226    public void setup() {
227        // Do nothing
228    }
229
230    /** {@inheritDoc} */
231    @Override
232    public void registerListenersForThisClass() {
233        if (!_listenersAreRegistered) {
234            _selectNamedBean.addPropertyChangeListener("KnownState", this);
235            _selectNamedBean.registerListeners();
236            _listenersAreRegistered = true;
237        }
238    }
239
240    /** {@inheritDoc} */
241    @Override
242    public void unregisterListenersForThisClass() {
243        if (_listenersAreRegistered) {
244            _selectNamedBean.removePropertyChangeListener("KnownState", this);
245            _selectNamedBean.unregisterListeners();
246            _listenersAreRegistered = false;
247        }
248    }
249
250    /** {@inheritDoc} */
251    @Override
252    public void propertyChange(PropertyChangeEvent evt) {
253        getConditionalNG().execute();
254    }
255
256    /** {@inheritDoc} */
257    @Override
258    public void disposeMe() {
259    }
260
261
262    public enum LightState {
263        Off(Light.OFF, Bundle.getMessage("StateOff")),
264        On(Light.ON, Bundle.getMessage("StateOn")),
265        Other(-1, Bundle.getMessage("SensorOtherStatus"));
266
267        private final int _id;
268        private final String _text;
269
270        private LightState(int id, String text) {
271            this._id = id;
272            this._text = text;
273        }
274
275        static public LightState get(int id) {
276            switch (id) {
277                case Light.OFF:
278                    return Off;
279
280                case Light.ON:
281                    return On;
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
300    /** {@inheritDoc} */
301    @Override
302    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
303        log.debug("getUsageReport :: ExpressionLight: bean = {}, report = {}", cdl, report);
304        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression);
305    }
306    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionLight.class);
307
308}