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