001package jmri.jmrit.logixng.actions;
002
003import java.beans.*;
004import java.util.*;
005
006import jmri.*;
007import jmri.jmrit.logixng.*;
008import jmri.jmrit.logixng.util.ReferenceUtil;
009import jmri.jmrit.logixng.util.parser.*;
010import jmri.jmrit.logixng.util.parser.ExpressionNode;
011import jmri.jmrit.logixng.util.parser.RecursiveDescentParser;
012import jmri.script.swing.ScriptOutput;
013
014/**
015 * This action logs some data.
016 *
017 * @author Daniel Bergqvist Copyright 2021
018 */
019public class LogData extends AbstractDigitalAction
020        implements PropertyChangeListener, VetoableChangeListener {
021
022    private boolean _logToLog = true;
023    private boolean _logToScriptOutput = false;
024    private FormatType _formatType = FormatType.OnlyText;
025    private String _format = "";
026    private final List<Data> _dataList = new ArrayList<>();
027
028    public LogData(String sys, String user)
029            throws BadUserNameException, BadSystemNameException {
030        super(sys, user);
031    }
032
033    @Override
034    public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException {
035        DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class);
036        String sysName = systemNames.get(getSystemName());
037        String userName = userNames.get(getSystemName());
038        if (sysName == null) sysName = manager.getAutoSystemName();
039        LogData copy = new LogData(sysName, userName);
040        copy.setComment(getComment());
041        copy.setLogToLog(_logToLog);
042        copy.setLogToScriptOutput(_logToScriptOutput);
043        copy.setFormat(_format);
044        copy.setFormatType(_formatType);
045        for (Data data : _dataList) {
046            copy.getDataList().add(new Data(data));
047        }
048        return manager.registerAction(copy);
049    }
050
051    public void setLogToLog(boolean logToLog) {
052        _logToLog = logToLog;
053    }
054
055    public boolean getLogToLog() {
056        return _logToLog;
057    }
058
059    public void setLogToScriptOutput(boolean logToScriptOutput) {
060        _logToScriptOutput = logToScriptOutput;
061    }
062
063    public boolean getLogToScriptOutput() {
064        return _logToScriptOutput;
065    }
066
067    public void setFormatType(FormatType formatType) {
068        _formatType = formatType;
069    }
070
071    public FormatType getFormatType() {
072        return _formatType;
073    }
074
075    public void setFormat(String format) {
076        _format = format;
077    }
078
079    public String getFormat() {
080        return _format;
081    }
082
083    public List<Data> getDataList() {
084        return _dataList;
085    }
086
087    @Override
088    public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException {
089/*
090        if ("CanDelete".equals(evt.getPropertyName())) { // No I18N
091            if (evt.getOldValue() instanceof Memory) {
092                if (evt.getOldValue().equals(getMemory().getBean())) {
093                    throw new PropertyVetoException(getDisplayName(), evt);
094                }
095            }
096        } else if ("DoDelete".equals(evt.getPropertyName())) { // No I18N
097            if (evt.getOldValue() instanceof Memory) {
098                if (evt.getOldValue().equals(getMemory().getBean())) {
099                    setMemory((Memory)null);
100                }
101            }
102        }
103*/
104    }
105
106    /** {@inheritDoc} */
107    @Override
108    public Category getCategory() {
109        return Category.OTHER;
110    }
111
112    private List<Object> getDataValues() throws JmriException {
113        List<Object> values = new ArrayList<>();
114        for (Data _data : _dataList) {
115            switch (_data._dataType) {
116                case LocalVariable:
117                    values.add(getConditionalNG().getSymbolTable().getValue(_data._data));
118                    break;
119
120                case Memory:
121                    MemoryManager memoryManager = InstanceManager.getDefault(MemoryManager.class);
122                    Memory memory = memoryManager.getMemory(_data._data);
123                    if (memory == null) throw new IllegalArgumentException("Memory '" + _data._data + "' not found");
124                    values.add(memory.getValue());
125                    break;
126
127                case Reference:
128                    values.add(ReferenceUtil.getReference(
129                            getConditionalNG().getSymbolTable(), _data._data));
130                    break;
131
132                case Formula:
133                    if (_data._expressionNode != null) {
134                        values.add(_data._expressionNode.calculate(getConditionalNG().getSymbolTable()));
135                    }
136
137                    break;
138
139                default:
140                    throw new IllegalArgumentException("_formatType has invalid value: "+_formatType.name());
141            }
142        }
143        return values;
144    }
145
146    /** {@inheritDoc} */
147    @Override
148    public void execute() throws JmriException {
149
150        String str;
151
152        switch (_formatType) {
153            case OnlyText:
154                str = _format;
155                break;
156
157            case CommaSeparatedList:
158                StringBuilder sb = new StringBuilder();
159                for (Object value : getDataValues()) {
160                    if (sb.length() > 0) sb.append(", ");
161                    sb.append(value != null ? value.toString() : "null");
162                }
163                str = sb.toString();
164                break;
165
166            case StringFormat:
167                str = String.format(_format, getDataValues().toArray());
168                break;
169
170            default:
171                throw new IllegalArgumentException("_formatType has invalid value: "+_formatType.name());
172        }
173        doLogging(str);
174    }
175
176    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST",
177        justification="Pass generated String unchanged")
178    private void doLogging(String logString) {
179        if (_logToLog) {
180            log.warn(logString);
181        }
182        if (_logToScriptOutput) {
183            ScriptOutput.getDefault().getOutputArea().append(logString+"\n");
184        }
185    }
186
187    @Override
188    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
189        throw new UnsupportedOperationException("Not supported.");
190    }
191
192    @Override
193    public int getChildCount() {
194        return 0;
195    }
196
197    @Override
198    public String getShortDescription(Locale locale) {
199        return Bundle.getMessage(locale, "LogData_Short");
200    }
201
202    @Override
203    public String getLongDescription(Locale locale) {
204        String bundleKey;
205        switch (_formatType) {
206            case OnlyText:
207                bundleKey = "LogData_Long_TextOnly";
208                break;
209            case CommaSeparatedList:
210                bundleKey = "LogData_Long_CommaSeparatedList";
211                break;
212            case StringFormat:
213                bundleKey = "LogData_Long_StringFormat";
214                break;
215            default:
216                throw new RuntimeException("_formatType has unknown value: "+_formatType.name());
217        }
218        return Bundle.getMessage(locale, bundleKey, _format);
219    }
220
221    /** {@inheritDoc} */
222    @Override
223    public void setup() {
224        // Do nothing
225    }
226
227    /** {@inheritDoc} */
228    @Override
229    public void registerListenersForThisClass() {
230        // Do nothing
231    }
232
233    /** {@inheritDoc} */
234    @Override
235    public void unregisterListenersForThisClass() {
236        // Do nothing
237    }
238
239    /** {@inheritDoc} */
240    @Override
241    public void propertyChange(PropertyChangeEvent evt) {
242        getConditionalNG().execute();
243    }
244
245    /** {@inheritDoc} */
246    @Override
247    public void disposeMe() {
248    }
249
250
251    /** {@inheritDoc} */
252    @Override
253    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
254/*
255        log.debug("getUsageReport :: LogData: bean = {}, report = {}", cdl, report);
256        for (NamedBeanReference namedBeanReference : _namedBeanReferences.values()) {
257            if (namedBeanReference._handle != null) {
258                if (bean.equals(namedBeanReference._handle.getBean())) {
259                    report.add(new NamedBeanUsageReport("LogixNGAction", cdl, getLongDescription()));
260                }
261            }
262        }
263*/
264    }
265
266
267    public enum FormatType {
268        OnlyText(Bundle.getMessage("LogData_FormatType_TextOnly"), true, false),
269        CommaSeparatedList(Bundle.getMessage("LogData_FormatType_CommaSeparatedList"), false, true),
270        StringFormat(Bundle.getMessage("LogData_FormatType_StringFormat"), true, true);
271
272        private final String _text;
273        private final boolean _useFormat;
274        private final boolean _useData;
275
276        private FormatType(String text, boolean useFormat, boolean useData) {
277            this._text = text;
278            this._useFormat = useFormat;
279            this._useData = useData;
280        }
281
282        @Override
283        public String toString() {
284            return _text;
285        }
286
287        public boolean getUseFormat() {
288            return _useFormat;
289        }
290
291        public boolean getUseData() {
292            return _useData;
293        }
294
295    }
296
297
298    public enum DataType {
299        LocalVariable(Bundle.getMessage("LogData_Operation_LocalVariable")),
300        Memory(Bundle.getMessage("LogData_Operation_Memory")),
301        Reference(Bundle.getMessage("LogData_Operation_Reference")),
302        Formula(Bundle.getMessage("LogData_Operation_Formula"));
303
304        private final String _text;
305
306        private DataType(String text) {
307            this._text = text;
308        }
309
310        @Override
311        public String toString() {
312            return _text;
313        }
314
315    }
316
317
318    public static class Data {
319
320        private DataType _dataType = DataType.LocalVariable;
321        private String _data = "";
322        private ExpressionNode _expressionNode;
323
324        public Data(Data data) throws ParserException {
325            _dataType = data._dataType;
326            _data = data._data;
327            calculateFormula();
328        }
329
330        public Data(DataType dataType, String data) throws ParserException {
331            _dataType = dataType;
332            _data = data;
333            calculateFormula();
334        }
335
336        private void calculateFormula() throws ParserException {
337            if (_dataType == DataType.Formula) {
338                Map<String, Variable> variables = new HashMap<>();
339                RecursiveDescentParser parser = new RecursiveDescentParser(variables);
340                _expressionNode = parser.parseExpression(_data);
341            } else {
342                _expressionNode = null;
343            }
344        }
345
346        public void setDataType(DataType dataType) { _dataType = dataType; }
347        public DataType getDataType() { return _dataType; }
348
349        public void setData(String data) { _data = data; }
350        public String getData() { return _data; }
351
352    }
353
354
355    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogData.class);
356
357}