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