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.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
174        if (_logToLog) log.warn(str);
175        if (_logToScriptOutput) ScriptOutput.getDefault().getOutputArea().append(str+"\n");
176    }
177
178    @Override
179    public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException {
180        throw new UnsupportedOperationException("Not supported.");
181    }
182
183    @Override
184    public int getChildCount() {
185        return 0;
186    }
187
188    @Override
189    public String getShortDescription(Locale locale) {
190        return Bundle.getMessage(locale, "LogData_Short");
191    }
192
193    @Override
194    public String getLongDescription(Locale locale) {
195        return Bundle.getMessage(locale, "LogData_Long");
196    }
197
198    /** {@inheritDoc} */
199    @Override
200    public void setup() {
201        // Do nothing
202    }
203
204    /** {@inheritDoc} */
205    @Override
206    public void registerListenersForThisClass() {
207        // Do nothing
208    }
209
210    /** {@inheritDoc} */
211    @Override
212    public void unregisterListenersForThisClass() {
213        // Do nothing
214    }
215
216    /** {@inheritDoc} */
217    @Override
218    public void propertyChange(PropertyChangeEvent evt) {
219        getConditionalNG().execute();
220    }
221
222    /** {@inheritDoc} */
223    @Override
224    public void disposeMe() {
225    }
226
227
228    /** {@inheritDoc} */
229    @Override
230    public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) {
231/*        
232        log.debug("getUsageReport :: LogData: bean = {}, report = {}", cdl, report);
233        for (NamedBeanReference namedBeanReference : _namedBeanReferences.values()) {
234            if (namedBeanReference._handle != null) {
235                if (bean.equals(namedBeanReference._handle.getBean())) {
236                    report.add(new NamedBeanUsageReport("LogixNGAction", cdl, getLongDescription()));
237                }
238            }
239        }
240*/
241    }
242
243
244    public enum FormatType {
245        OnlyText(Bundle.getMessage("LogData_FormatType_TextOnly"), true, false),
246        CommaSeparatedList(Bundle.getMessage("LogData_FormatType_CommaSeparatedList"), false, true),
247        StringFormat(Bundle.getMessage("LogData_FormatType_StringFormat"), true, true);
248
249        private final String _text;
250        private final boolean _useFormat;
251        private final boolean _useData;
252
253        private FormatType(String text, boolean useFormat, boolean useData) {
254            this._text = text;
255            this._useFormat = useFormat;
256            this._useData = useData;
257        }
258
259        @Override
260        public String toString() {
261            return _text;
262        }
263
264        public boolean getUseFormat() {
265            return _useFormat;
266        }
267
268        public boolean getUseData() {
269            return _useData;
270        }
271
272    }
273
274
275    public enum DataType {
276        LocalVariable(Bundle.getMessage("LogData_Operation_LocalVariable")),
277        Memory(Bundle.getMessage("LogData_Operation_Memory")),
278        Reference(Bundle.getMessage("LogData_Operation_Reference")),
279        Formula(Bundle.getMessage("LogData_Operation_Formula"));
280
281        private final String _text;
282
283        private DataType(String text) {
284            this._text = text;
285        }
286
287        @Override
288        public String toString() {
289            return _text;
290        }
291
292    }
293
294
295    public static class Data {
296
297        private DataType _dataType = DataType.LocalVariable;
298        private String _data = "";
299        private ExpressionNode _expressionNode;
300
301        public Data(Data data) throws ParserException {
302            _dataType = data._dataType;
303            _data = data._data;
304            calculateFormula();
305        }
306
307        public Data(DataType dataType, String data) throws ParserException {
308            _dataType = dataType;
309            _data = data;
310            calculateFormula();
311        }
312
313        private void calculateFormula() throws ParserException {
314            if (_dataType == DataType.Formula) {
315                Map<String, Variable> variables = new HashMap<>();
316                RecursiveDescentParser parser = new RecursiveDescentParser(variables);
317                _expressionNode = parser.parseExpression(_data);
318            } else {
319                _expressionNode = null;
320            }
321        }
322
323        public void setDataType(DataType dataType) { _dataType = dataType; }
324        public DataType getDataType() { return _dataType; }
325
326        public void setData(String data) { _data = data; }
327        public String getData() { return _data; }
328
329    }
330
331
332    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogData.class);
333
334}