001package jmri.jmrit.logixng.actions;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.*;
006
007import javax.annotation.Nonnull;
008
009import jmri.*;
010import jmri.jmrit.logixng.*;
011import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean;
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 action triggers a reporter.
020 *
021 * @author Daniel Bergqvist Copyright 2021
022 * @author Dave Sand Copyright 2021
023 */
024public class ActionReporter extends AbstractDigitalAction
025        implements PropertyChangeListener {
026
027    private final LogixNG_SelectNamedBean<Reporter> _selectNamedBean =
028            new LogixNG_SelectNamedBean<>(
029                    this, Reporter.class, InstanceManager.getDefault(ReporterManager.class), this);
030
031    private final LogixNG_SelectNamedBean<Memory> _selectMemoryNamedBean =
032            new LogixNG_SelectNamedBean<>(
033                    this, Memory.class, InstanceManager.getDefault(MemoryManager.class), this);
034
035    private ReporterValue _reporterValue = ReporterValue.CopyCurrentReport;
036
037    private NamedBeanAddressing _dataAddressing = NamedBeanAddressing.Direct;
038    private String _dataReference = "";
039    private String _dataLocalVariable = "";
040    private String _dataFormula = "";
041    private ExpressionNode _dataExpressionNode;
042
043//    private NamedBeanHandle<Memory> _memoryHandle;
044
045    public ActionReporter(String sys, String user)
046            throws BadUserNameException, BadSystemNameException {
047        super(sys, user);
048    }
049
050    @Override
051    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException {
052        DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class);
053        String sysName = systemNames.get(getSystemName());
054        String userName = userNames.get(getSystemName());
055        if (sysName == null) sysName = manager.getAutoSystemName();
056        ActionReporter copy = new ActionReporter(sysName, userName);
057        copy.setComment(getComment());
058        _selectNamedBean.copy(copy._selectNamedBean);
059        _selectMemoryNamedBean.copy(copy._selectMemoryNamedBean);
060
061        copy.setReporterValue(_reporterValue);
062
063        copy.setDataAddressing(_dataAddressing);
064        copy.setDataReference(_dataReference);
065        copy.setDataLocalVariable(_dataLocalVariable);
066        copy.setDataFormula(_dataFormula);
067//        if (_memoryHandle != null) copy.setMemory(_memoryHandle);
068
069        return manager.registerAction(copy);
070    }
071
072    public LogixNG_SelectNamedBean<Reporter> getSelectNamedBean() {
073        return _selectNamedBean;
074    }
075
076    public LogixNG_SelectNamedBean<Memory> getSelectMemoryNamedBean() {
077        return _selectMemoryNamedBean;
078    }
079
080    public void setReporterValue(ReporterValue value) {
081        _reporterValue = value;
082    }
083
084    public ReporterValue getReporterValue() {
085        return _reporterValue;
086    }
087
088    public void setDataAddressing(NamedBeanAddressing addressing) throws ParserException {
089        _dataAddressing = addressing;
090        parseDataFormula();
091    }
092
093    public NamedBeanAddressing getDataAddressing() {
094        return _dataAddressing;
095    }
096
097    public void setDataReference(@Nonnull String reference) {
098        if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) {
099            throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference");
100        }
101        _dataReference = reference;
102    }
103
104    public String getDataReference() {
105        return _dataReference;
106    }
107
108    public void setDataLocalVariable(@Nonnull String localVariable) {
109        _dataLocalVariable = localVariable;
110    }
111
112    public String getDataLocalVariable() {
113        return _dataLocalVariable;
114    }
115
116    public void setDataFormula(@Nonnull String formula) throws ParserException {
117        _dataFormula = formula;
118        parseDataFormula();
119    }
120
121    public String getDataFormula() {
122        return _dataFormula;
123    }
124
125    private void parseDataFormula() throws ParserException {
126        if (_dataAddressing == NamedBeanAddressing.Formula) {
127            Map<String, Variable> variables = new HashMap<>();
128
129            RecursiveDescentParser parser = new RecursiveDescentParser(variables);
130            _dataExpressionNode = parser.parseExpression(_dataFormula);
131        } else {
132            _dataExpressionNode = null;
133        }
134    }
135
136
137    /** {@inheritDoc} */
138    @Override
139    public Category getCategory() {
140        return Category.ITEM;
141    }
142
143    Object getReporterData(Reporter reporter) throws JmriException {
144        Object obj;
145        ReporterValue value = _reporterValue;
146
147        switch (value) {
148            case CopyCurrentReport:
149                obj = reporter.getCurrentReport();
150                break;
151            case CopyLastReport:
152                obj = reporter.getLastReport();
153                break;
154            case CopyState:
155                obj = reporter.getState();
156                break;
157            default:
158                throw new IllegalArgumentException("invalid value name: " + value.name());
159        }
160
161        return obj;
162    }
163
164    void updateDestination(Object data) throws JmriException {
165        switch (_dataAddressing) {
166            case Direct:
167                Memory memory = _selectMemoryNamedBean.evaluateNamedBean(getConditionalNG());
168                if (memory != null) {
169                    memory.setValue(data);
170                }
171                break;
172
173            case Reference:
174                String refName = ReferenceUtil.getReference(
175                        getConditionalNG().getSymbolTable(), _dataReference);
176                log.debug("ref ref = {}, name = {}", _dataReference, refName);
177                Memory refMem = InstanceManager.getDefault(MemoryManager.class).getMemory(refName);
178                if (refMem == null) {
179                    throw new IllegalArgumentException("invalid memory reference: " + refName);
180                }
181                refMem.setValue(data);
182                break;
183
184            case LocalVariable:
185                log.debug("LocalVariable: lv = {}", _dataLocalVariable);
186                getConditionalNG().getSymbolTable().setValue(_dataLocalVariable, data);
187                break;
188
189            case Formula:
190                String formulaName = _dataExpressionNode != null
191                        ? TypeConversionUtil.convertToString(
192                                _dataExpressionNode.calculate(
193                                        getConditionalNG().getSymbolTable()), false)
194                        : null;
195                if (formulaName == null) {
196                    throw new IllegalArgumentException("invalid memory formula, name is null");
197                }
198
199                Memory formulaMem = InstanceManager.getDefault(MemoryManager.class).getMemory(formulaName);
200                if (formulaMem == null) {
201                    throw new IllegalArgumentException("invalid memory formula: " + formulaName);
202                }
203                formulaMem.setValue(data);
204                break;
205
206            default:
207                throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name());
208        }
209    }
210
211    /** {@inheritDoc} */
212    @Override
213    public void execute() throws JmriException {
214        // Get the reporter bean
215        Reporter reporter = _selectNamedBean.evaluateNamedBean(getConditionalNG());
216        if (reporter == null) return;
217        log.debug("reporter = {}", reporter.getDisplayName());
218
219        // Get the reporter data
220        Object data = getReporterData(reporter);
221        log.debug("data = {}", data);
222
223        // Update the destination
224        updateDestination(data);
225    }
226
227    @Override
228    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
229        throw new UnsupportedOperationException("Not supported.");
230    }
231
232    @Override
233    public int getChildCount() {
234        return 0;
235    }
236
237    @Override
238    public String getShortDescription(Locale locale) {
239        return Bundle.getMessage(locale, "ActionReporter_Short");
240    }
241
242    @Override
243    public String getLongDescription(Locale locale) {
244        String bean = _selectNamedBean.getDescription(locale);
245        String dest = "";
246
247        switch (_dataAddressing) {
248            case Direct:
249                String memoryName = _selectMemoryNamedBean.getDescription(locale);
250                dest = Bundle.getMessage(locale, "AddressByDirect", memoryName);
251                break;
252
253            case Reference:
254                dest = Bundle.getMessage(locale, "AddressByReference", _dataReference);
255                break;
256
257            case LocalVariable:
258                dest = Bundle.getMessage(locale, "AddressByLocalVariable", _dataLocalVariable);
259                break;
260
261            case Formula:
262                dest = Bundle.getMessage(locale, "AddressByFormula", _dataFormula);
263                break;
264
265            default:
266                throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name());
267        }
268
269        String item = getReporterValue().toString();
270
271        return Bundle.getMessage(locale, "ActionReporter_Long", item, bean, dest);
272    }
273
274    /** {@inheritDoc} */
275    @Override
276    public void setup() {
277        // Do nothing
278    }
279
280    /** {@inheritDoc} */
281    @Override
282    public void registerListenersForThisClass() {
283        _selectNamedBean.registerListeners();
284    }
285
286    /** {@inheritDoc} */
287    @Override
288    public void unregisterListenersForThisClass() {
289        _selectNamedBean.unregisterListeners();
290    }
291
292    /** {@inheritDoc} */
293    @Override
294    public void disposeMe() {
295    }
296
297    public enum ReporterValue {
298        CopyCurrentReport(Bundle.getMessage("ActionReporter_CopyCurrentReport")),
299        CopyLastReport(Bundle.getMessage("ActionReporter_CopyLastReport")),
300        CopyState(Bundle.getMessage("ActionReporter_CopyState"));
301
302        private final String _text;
303
304        private ReporterValue(String text) {
305            this._text = text;
306        }
307
308        @Override
309        public String toString() {
310            return _text;
311        }
312
313    }
314
315    /** {@inheritDoc} */
316    @Override
317    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
318        _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action);
319        _selectMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action);
320    }
321
322    /** {@inheritDoc} */
323    @Override
324    public void propertyChange(PropertyChangeEvent evt) {
325        getConditionalNG().execute();
326    }
327
328    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionReporter.class);
329
330}