001package jmri.jmrit.logixng.expressions.swing;
002
003import java.awt.BorderLayout;
004import java.awt.Dimension;
005import java.awt.event.ActionEvent;
006import java.util.*;
007
008import javax.annotation.CheckForNull;
009import javax.annotation.Nonnull;
010import javax.swing.*;
011import javax.swing.table.TableColumn;
012
013import jmri.InstanceManager;
014import jmri.jmrit.logixng.*;
015import jmri.jmrit.logixng.expressions.LogData;
016import jmri.jmrit.logixng.util.parser.*;
017import jmri.util.table.ButtonEditor;
018import jmri.util.table.ButtonRenderer;
019
020/**
021 * Configures an LogData object with a Swing JPanel.
022 *
023 * @author Daniel Bergqvist Copyright 2021
024 */
025public class LogDataSwing extends AbstractDigitalExpressionSwing {
026
027    private JComboBox<ResultType> _resultType;
028    private JCheckBox _logToLogCheckBox;
029    private JCheckBox _logToScriptOutputCheckBox;
030    private JComboBox<LogData.FormatType> _formatType;
031    private JTextField _format;
032    private JTable _logDataTable;
033    private LogDataTableModel _logDataTableModel;
034
035    @Override
036    protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) {
037        if ((object != null) && (! (object instanceof LogData))) {
038            throw new IllegalArgumentException("object is not a LogData: " + object.getClass().getName());
039        }
040        LogData logData = (LogData)object;
041
042        panel = new JPanel();
043
044        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
045
046
047        JPanel resultTypePanel = new JPanel();
048        _resultType = new JComboBox<>();
049        for (ResultType formatType : ResultType.values()) {
050            _resultType.addItem(formatType);
051        }
052        resultTypePanel.add(new JLabel(Bundle.getMessage("LogData_ResultType")));
053        resultTypePanel.add(_resultType);
054        panel.add(resultTypePanel);
055
056
057        _logToLogCheckBox = new JCheckBox(Bundle.getMessage("LogData_LogToLog"));
058        _logToLogCheckBox.setSelected(true);    // Selected as default
059        panel.add(_logToLogCheckBox);
060
061        _logToScriptOutputCheckBox = new JCheckBox(Bundle.getMessage("LogData_LogToScriptOutput"));
062        panel.add(_logToScriptOutputCheckBox);
063
064
065        JPanel formatTypePanel = new JPanel();
066        _formatType = new JComboBox<>();
067        for (LogData.FormatType formatType : LogData.FormatType.values()) {
068            _formatType.addItem(formatType);
069        }
070        formatTypePanel.add(new JLabel(Bundle.getMessage("LogData_FormatType")));
071        formatTypePanel.add(_formatType);
072        panel.add(formatTypePanel);
073
074        JPanel formatPanel = new JPanel();
075        _format = new JTextField(20);
076        formatPanel.add(new JLabel(Bundle.getMessage("LogData_Format")));
077        formatPanel.add(_format);
078        panel.add(formatPanel);
079
080
081        if (logData != null) {
082            _resultType.setSelectedItem(ResultType.parseResult(logData.getResult()));
083            _logToLogCheckBox.setSelected(logData.getLogToLog());
084            _logToScriptOutputCheckBox.setSelected(logData.getLogToScriptOutput());
085            _formatType.setSelectedItem(logData.getFormatType());
086            _format.setText(logData.getFormat());
087        }
088
089
090        _logDataTable = new JTable();
091
092        if (logData != null) {
093            List<LogData.Data> dataList
094                    = new ArrayList<>(logData.getDataList());
095
096            _logDataTableModel = new LogDataTableModel(dataList);
097        } else {
098            _logDataTableModel = new LogDataTableModel(null);
099        }
100
101        _logDataTable.setModel(_logDataTableModel);
102        _logDataTable.setDefaultRenderer(LogData.DataType.class,
103                new LogDataTableModel.CellRenderer());
104        _logDataTable.setDefaultEditor(LogData.DataType.class,
105                new LogDataTableModel.DataTypeCellEditor());
106        _logDataTableModel.setColumnsForComboBoxes(_logDataTable);
107        _logDataTable.setDefaultRenderer(JButton.class, new ButtonRenderer());
108        _logDataTable.setDefaultEditor(JButton.class, new ButtonEditor(new JButton()));
109
110        JButton testButton = new JButton("XXXXXX");  // NOI18N
111        _logDataTable.setRowHeight(testButton.getPreferredSize().height);
112        TableColumn deleteColumn = _logDataTable.getColumnModel()
113                .getColumn(LogDataTableModel.COLUMN_DUMMY);
114        deleteColumn.setMinWidth(testButton.getPreferredSize().width);
115        deleteColumn.setMaxWidth(testButton.getPreferredSize().width);
116        deleteColumn.setResizable(false);
117
118        // The dummy column is used to be able to force update of the
119        // other columns when the panel is closed.
120        TableColumn dummyColumn = _logDataTable.getColumnModel()
121                .getColumn(LogDataTableModel.COLUMN_DUMMY);
122        dummyColumn.setMinWidth(0);
123        dummyColumn.setPreferredWidth(0);
124        dummyColumn.setMaxWidth(0);
125
126        JScrollPane scrollpane = new JScrollPane(_logDataTable);
127        scrollpane.setPreferredSize(new Dimension(400, 200));
128        panel.add(scrollpane);
129
130        // Add parameter
131        JButton add = new JButton(Bundle.getMessage("LogData_TableAdd"));
132        buttonPanel.add(add);
133        add.addActionListener((ActionEvent e) -> {
134            _logDataTableModel.add();
135        });
136    }
137
138    /** {@inheritDoc} */
139    @Override
140    public boolean validate(@Nonnull List<String> errorMessages) {
141        for (LogData.Data data : _logDataTableModel.getDataList()) {
142            if (data.getDataType() == LogData.DataType.Formula) {
143                try {
144                    Map<String, Variable> variables = new HashMap<>();
145                    RecursiveDescentParser parser = new RecursiveDescentParser(variables);
146                    parser.parseExpression(data.getData());
147                } catch (ParserException e) {
148                    errorMessages.add(e.getLocalizedMessage());
149                }
150            }
151        }
152        return true;
153    }
154
155    /** {@inheritDoc} */
156    @Override
157    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) {
158        LogData expression = new LogData(systemName, userName);
159        updateObject(expression);
160        return InstanceManager.getDefault(DigitalExpressionManager.class).registerExpression(expression);
161    }
162
163    /** {@inheritDoc} */
164    @Override
165    public void updateObject(@Nonnull Base object) {
166        if (! (object instanceof LogData)) {
167            throw new IllegalArgumentException("object is not a LogData: " + object.getClass().getName());
168        }
169        LogData logData = (LogData)object;
170
171
172        logData.setResult(_resultType.getItemAt(_resultType.getSelectedIndex()).getResult());
173
174        logData.setLogToLog(_logToLogCheckBox.isSelected());
175        logData.setLogToScriptOutput(_logToScriptOutputCheckBox.isSelected());
176
177        logData.setFormatType(_formatType.getItemAt(_formatType.getSelectedIndex()));
178        logData.setFormat(_format.getText());
179
180
181        // Do this to force update of the table
182        _logDataTable.editCellAt(0, 2);
183
184        logData.getDataList().clear();
185
186        for (LogData.Data data : _logDataTableModel.getDataList()) {
187            logData.getDataList().add(data);
188        }
189    }
190
191    /** {@inheritDoc} */
192    @Override
193    public String toString() {
194        return Bundle.getMessage("LogData_Short");
195    }
196
197    @Override
198    public void dispose() {
199    }
200
201
202    public enum ResultType {
203        True(Bundle.getMessage("LogData_ReturnType_True"), true),
204        False(Bundle.getMessage("LogData_ReturnType_False"), false);
205
206        private final String _text;
207        private boolean _result;
208
209        private ResultType(String text, boolean result) {
210            this._text = text;
211            this._result = result;
212        }
213
214        @Override
215        public String toString() {
216            return _text;
217        }
218
219        public boolean getResult() {
220            return _result;
221        }
222
223        public static ResultType parseResult(boolean result) {
224            return result ? ResultType.True : ResultType.False;
225        }
226
227    }
228
229}