001package jmri.jmrit.logixng.expressions;
002
003import java.beans.*;
004import java.util.*;
005
006import javax.annotation.Nonnull;
007
008import jmri.*;
009import jmri.jmrit.logix.Warrant;
010import jmri.jmrit.logix.WarrantManager;
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 warrant.
021 *
022 * @author Daniel Bergqvist Copyright 2018
023 */
024public class ExpressionWarrant extends AbstractDigitalExpression
025        implements PropertyChangeListener {
026
027    private final LogixNG_SelectNamedBean<Warrant> _selectNamedBean =
028            new LogixNG_SelectNamedBean<>(
029                    this, Warrant.class, InstanceManager.getDefault(WarrantManager.class), this);
030    private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is;
031    private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct;
032    private WarrantState _warrantState = WarrantState.RouteAllocated;
033    private String _stateReference = "";
034    private String _stateLocalVariable = "";
035    private String _stateFormula = "";
036    private ExpressionNode _stateExpressionNode;
037
038    public ExpressionWarrant(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        ExpressionWarrant copy = new ExpressionWarrant(sysName, userName);
050        copy.setComment(getComment());
051        _selectNamedBean.copy(copy._selectNamedBean);
052        copy.setBeanState(_warrantState);
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<Warrant> 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(WarrantState state) {
083        _warrantState = state;
084    }
085
086    public WarrantState getBeanState() {
087        return _warrantState;
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        Warrant warrant = _selectNamedBean.evaluateNamedBean(getConditionalNG());
163
164        if (warrant == null) return false;
165
166        WarrantState checkWarrantState;
167
168        if ((_stateAddressing == NamedBeanAddressing.Direct)) {
169            checkWarrantState = _warrantState;
170        } else {
171            checkWarrantState = WarrantState.valueOf(getNewState());
172        }
173
174        boolean result;
175
176        switch (checkWarrantState) {
177            case RouteFree:
178                result = warrant.routeIsFree();
179                break;
180            case RouteOccupied:
181                result = warrant.routeIsOccupied();
182                break;
183            case RouteAllocated:
184                result = warrant.isAllocated();
185                break;
186            case RouteSet:
187                result = warrant.hasRouteSet();
188                break;
189            case TrainRunning:
190                result = ! (warrant.getRunMode() == Warrant.MODE_NONE);
191                break;
192            default:
193                throw new UnsupportedOperationException("checkWarrantState has unknown value: " + checkWarrantState.name());
194        }
195        if (_is_IsNot == Is_IsNot_Enum.Is) {
196            return result;
197        } else {
198            return !result;
199        }
200    }
201
202    @Override
203    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
204        throw new UnsupportedOperationException("Not supported.");
205    }
206
207    @Override
208    public int getChildCount() {
209        return 0;
210    }
211
212    @Override
213    public String getShortDescription(Locale locale) {
214        return Bundle.getMessage(locale, "Warrant_Short");
215    }
216
217    @Override
218    public String getLongDescription(Locale locale) {
219        String namedBean = _selectNamedBean.getDescription(locale);
220        String state;
221
222        switch (_stateAddressing) {
223            case Direct:
224                state = Bundle.getMessage(locale, "AddressByDirect", _warrantState._text);
225                break;
226
227            case Reference:
228                state = Bundle.getMessage(locale, "AddressByReference", _stateReference);
229                break;
230
231            case LocalVariable:
232                state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable);
233                break;
234
235            case Formula:
236                state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula);
237                break;
238
239            default:
240                throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name());
241        }
242
243        return Bundle.getMessage(locale, "Warrant_Long", namedBean, _is_IsNot.toString(), state);
244    }
245
246    /** {@inheritDoc} */
247    @Override
248    public void setup() {
249        // Do nothing
250    }
251
252    /** {@inheritDoc} */
253    @Override
254    public void registerListenersForThisClass() {
255        if (!_listenersAreRegistered) {
256            _selectNamedBean.addPropertyChangeListener("KnownState", this);
257            _selectNamedBean.registerListeners();
258            _listenersAreRegistered = true;
259        }
260    }
261
262    /** {@inheritDoc} */
263    @Override
264    public void unregisterListenersForThisClass() {
265        if (_listenersAreRegistered) {
266            _selectNamedBean.removePropertyChangeListener("KnownState", this);
267            _selectNamedBean.unregisterListeners();
268            _listenersAreRegistered = false;
269        }
270    }
271
272    /** {@inheritDoc} */
273    @Override
274    public void propertyChange(PropertyChangeEvent evt) {
275        getConditionalNG().execute();
276    }
277
278    /** {@inheritDoc} */
279    @Override
280    public void disposeMe() {
281    }
282
283
284    public enum WarrantState {
285        RouteFree(Bundle.getMessage("WarrantTypeRouteFree")),
286        RouteOccupied(Bundle.getMessage("WarrantTypeOccupied")),
287        RouteAllocated(Bundle.getMessage("WarrantTypeAllocated")),
288        RouteSet(Bundle.getMessage("WarrantTypeRouteSet")),
289        TrainRunning(Bundle.getMessage("WarrantTypeTrainRunning"));
290
291        private final String _text;
292
293        private WarrantState(String text) {
294            this._text = text;
295        }
296
297        @Override
298        public String toString() {
299            return _text;
300        }
301
302    }
303
304    /** {@inheritDoc} */
305    @Override
306    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
307        log.debug("getUsageReport :: ExpressionWarrant: bean = {}, report = {}", cdl, report);
308        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression);
309    }
310
311    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionWarrant.class);
312
313}