001package jmri.jmrit.logixng.expressions.swing;
002
003import java.awt.GridBagConstraints;
004import java.awt.GridBagLayout;
005import java.awt.event.ActionEvent;
006import java.util.List;
007
008import javax.annotation.CheckForNull;
009import javax.annotation.Nonnull;
010import javax.swing.*;
011
012import jmri.InstanceManager;
013import jmri.jmrit.logixng.*;
014import jmri.jmrit.logixng.expressions.ExpressionScript;
015import jmri.jmrit.logixng.swing.SwingConfiguratorInterface;
016import jmri.jmrit.logixng.util.parser.ParserException;
017import jmri.script.ScriptEngineSelector;
018import jmri.script.swing.ScriptEngineSelectorSwing;
019import jmri.script.swing.ScriptFileChooser;
020import jmri.util.FileUtil;
021import jmri.util.swing.JComboBoxUtil;
022
023/**
024 * Configures an ExpressionScript object with a Swing JPanel.
025 *
026 * @author Daniel Bergqvist 2021
027 */
028public class ExpressionScriptSwing extends AbstractDigitalExpressionSwing {
029
030    public static final int NUM_COLUMNS_TEXT_FIELDS = 20;
031
032    private JTabbedPane _tabbedPaneOperationType;
033    private JPanel _panelOperationTypeDirect;
034    private JPanel _panelOperationTypeReference;
035    private JPanel _panelOperationTypeLocalVariable;
036    private JPanel _panelOperationTypeFormula;
037
038    private JComboBox<ExpressionScript.OperationType> _operationComboBox;
039    private JTextField _scriptOperationReferenceTextField;
040    private JTextField _scriptOperationLocalVariableTextField;
041    private JTextField _scriptOperationFormulaTextField;
042
043    private JTabbedPane _tabbedPaneScriptType;
044    private JPanel _panelScriptTypeDirect;
045    private JPanel _panelScriptTypeReference;
046    private JPanel _panelScriptTypeLocalVariable;
047    private JPanel _panelScriptTypeFormula;
048
049    private ScriptFileChooser scriptFileChooser;
050    private JTextField _scriptTextField;
051    private JTextField _scriptReferenceTextField;
052    private JTextField _scriptLocalVariableTextField;
053    private JTextField _scriptFormulaTextField;
054
055    private JLabel _registerListenerLabel;
056    private JLabel _unregisterListenerLabel;
057    private JTextField _registerListener;
058    private JTextField _unregisterListener;
059
060    private ScriptEngineSelectorSwing _scriptEngineSelectorSwing;
061
062
063    @Override
064    protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) {
065        ExpressionScript expression = (ExpressionScript)object;
066
067        if (expression != null) {
068            _scriptEngineSelectorSwing = new ScriptEngineSelectorSwing(expression.getScriptEngineSelector());
069        } else {
070            _scriptEngineSelectorSwing = new ScriptEngineSelectorSwing(new ScriptEngineSelector());
071        }
072
073        panel = new JPanel();
074        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
075
076        JPanel expressionPanel = new JPanel();
077
078
079        // Set up the tabbed pane for selecting the operation
080        _tabbedPaneOperationType = new JTabbedPane();
081        _panelOperationTypeDirect = new javax.swing.JPanel();
082        _panelOperationTypeDirect.setLayout(new BoxLayout(_panelOperationTypeDirect, BoxLayout.Y_AXIS));
083        _panelOperationTypeReference = new javax.swing.JPanel();
084        _panelOperationTypeReference.setLayout(new BoxLayout(_panelOperationTypeReference, BoxLayout.Y_AXIS));
085        _panelOperationTypeLocalVariable = new javax.swing.JPanel();
086        _panelOperationTypeLocalVariable.setLayout(new BoxLayout(_panelOperationTypeLocalVariable, BoxLayout.Y_AXIS));
087        _panelOperationTypeFormula = new javax.swing.JPanel();
088        _panelOperationTypeFormula.setLayout(new BoxLayout(_panelOperationTypeFormula, BoxLayout.Y_AXIS));
089
090        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Direct.toString(), _panelOperationTypeDirect);
091        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Reference.toString(), _panelOperationTypeReference);
092        _tabbedPaneOperationType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelOperationTypeLocalVariable);
093        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Formula.toString(), _panelOperationTypeFormula);
094
095        _operationComboBox = new JComboBox<>();
096        for (ExpressionScript.OperationType e : ExpressionScript.OperationType.values()) {
097            _operationComboBox.addItem(e);
098        }
099        JComboBoxUtil.setupComboBoxMaxRows(_operationComboBox);
100        _panelOperationTypeDirect.add(new JLabel(Bundle.getMessage("ExpressionScript_Operation")));
101        _panelOperationTypeDirect.add(_operationComboBox);
102
103        _scriptOperationReferenceTextField = new JTextField();
104        _scriptOperationReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
105        _panelOperationTypeReference.add(new JLabel(Bundle.getMessage("ExpressionScript_Operation")));
106        _panelOperationTypeReference.add(_scriptOperationReferenceTextField);
107
108        _scriptOperationLocalVariableTextField = new JTextField();
109        _scriptOperationLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
110        _panelOperationTypeLocalVariable.add(new JLabel(Bundle.getMessage("ExpressionScript_Operation")));
111        _panelOperationTypeLocalVariable.add(_scriptOperationLocalVariableTextField);
112
113        _scriptOperationFormulaTextField = new JTextField();
114        _scriptOperationFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
115        _panelOperationTypeFormula.add(new JLabel(Bundle.getMessage("ExpressionScript_Operation")));
116        _panelOperationTypeFormula.add(_scriptOperationFormulaTextField);
117
118
119        // Set up the tabbed pane for selecting the appearance
120        _tabbedPaneScriptType = new JTabbedPane();
121        _panelScriptTypeDirect = new javax.swing.JPanel();
122//        _panelScriptTypeDirect.setLayout(new BoxLayout(_panelScriptTypeDirect, BoxLayout.Y_AXIS));
123        _panelScriptTypeReference = new javax.swing.JPanel();
124        _panelScriptTypeReference.setLayout(new BoxLayout(_panelScriptTypeReference, BoxLayout.Y_AXIS));
125        _panelScriptTypeLocalVariable = new javax.swing.JPanel();
126        _panelScriptTypeLocalVariable.setLayout(new BoxLayout(_panelScriptTypeLocalVariable, BoxLayout.Y_AXIS));
127        _panelScriptTypeFormula = new javax.swing.JPanel();
128        _panelScriptTypeFormula.setLayout(new BoxLayout(_panelScriptTypeFormula, BoxLayout.Y_AXIS));
129
130        _tabbedPaneScriptType.addTab(NamedBeanAddressing.Direct.toString(), _panelScriptTypeDirect);
131        _tabbedPaneScriptType.addTab(NamedBeanAddressing.Reference.toString(), _panelScriptTypeReference);
132        _tabbedPaneScriptType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelScriptTypeLocalVariable);
133        _tabbedPaneScriptType.addTab(NamedBeanAddressing.Formula.toString(), _panelScriptTypeFormula);
134
135        JButton _expressionSelectFileButton = new JButton("..."); // "File" replaced by ...
136        _expressionSelectFileButton.setMaximumSize(_expressionSelectFileButton.getPreferredSize());
137        _expressionSelectFileButton.setToolTipText(Bundle.getMessage("FileButtonHint"));  // NOI18N
138        _expressionSelectFileButton.addActionListener((ActionEvent e) -> {
139            scriptFileChooser = new ScriptFileChooser(FileUtil.getScriptsPath());
140            scriptFileChooser.rescanCurrentDirectory();
141            int retVal = scriptFileChooser.showOpenDialog(null);
142            // handle selection or cancel
143            if (retVal == JFileChooser.APPROVE_OPTION) {
144                // set selected file location
145                try {
146                    _scriptTextField.setText(FileUtil.getPortableFilename(scriptFileChooser.getSelectedFile().getCanonicalPath()));
147                } catch (java.io.IOException ex) {
148                    log.error("exception setting file location", ex);  // NOI18N
149                    _scriptTextField.setText("");
150                }
151            }
152        });
153        _panelScriptTypeDirect.add(_expressionSelectFileButton);
154        JPanel _scriptTextPanel = new JPanel();
155        _scriptTextPanel.setLayout(new BoxLayout(_scriptTextPanel, BoxLayout.Y_AXIS));
156        _scriptTextField = new JTextField(30);
157        _scriptTextPanel.add(new JLabel(Bundle.getMessage("ExpressionScript_Script")));
158        _scriptTextPanel.add(_scriptTextField);
159        _panelScriptTypeDirect.add(_scriptTextPanel);
160
161        _scriptReferenceTextField = new JTextField();
162        _scriptReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
163        _panelScriptTypeReference.add(new JLabel(Bundle.getMessage("ExpressionScript_Script")));
164        _panelScriptTypeReference.add(_scriptReferenceTextField);
165
166        _scriptLocalVariableTextField = new JTextField();
167        _scriptLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
168        _panelScriptTypeLocalVariable.add(new JLabel(Bundle.getMessage("ExpressionScript_Script")));
169        _panelScriptTypeLocalVariable.add(_scriptLocalVariableTextField);
170
171        _scriptFormulaTextField = new JTextField();
172        _scriptFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
173        _panelScriptTypeFormula.add(new JLabel(Bundle.getMessage("ExpressionScript_Script")));
174        _panelScriptTypeFormula.add(_scriptFormulaTextField);
175
176
177        if (expression != null) {
178            switch (expression.getOperationAddressing()) {
179                case Direct: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeDirect); break;
180                case Reference: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeReference); break;
181                case LocalVariable: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeLocalVariable); break;
182                case Formula: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeFormula); break;
183                default: throw new IllegalArgumentException("invalid _addressing state: " + expression.getOperationAddressing().name());
184            }
185            _operationComboBox.setSelectedItem(expression.getOperationType());
186            _scriptOperationReferenceTextField.setText(expression.getOperationReference());
187            _scriptOperationLocalVariableTextField.setText(expression.getOperationLocalVariable());
188            _scriptOperationFormulaTextField.setText(expression.getOperationFormula());
189
190            switch (expression.getScriptAddressing()) {
191                case Direct: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeDirect); break;
192                case Reference: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeReference); break;
193                case LocalVariable: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeLocalVariable); break;
194                case Formula: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeFormula); break;
195                default: throw new IllegalArgumentException("invalid _addressing state: " + expression.getScriptAddressing().name());
196            }
197            _scriptTextField.setText(expression.getScript());
198            _scriptReferenceTextField.setText(expression.getScriptReference());
199            _scriptLocalVariableTextField.setText(expression.getScriptLocalVariable());
200            _scriptFormulaTextField.setText(expression.getScriptFormula());
201        }
202
203        JComponent[] components = new JComponent[]{
204            _tabbedPaneOperationType,
205            _tabbedPaneScriptType
206        };
207
208        List<JComponent> componentList = SwingConfiguratorInterface.parseMessage(
209                Bundle.getMessage("ExpressionScript_Components"), components);
210
211        for (JComponent c : componentList) expressionPanel.add(c);
212        panel.add(expressionPanel);
213
214        JPanel listernerPanel = new JPanel();
215        panel.add(listernerPanel);
216
217        _registerListenerLabel = new JLabel(Bundle.getMessage("ExpressionScript_RegisterListener"));
218        _unregisterListenerLabel = new JLabel(Bundle.getMessage("ExpressionScript_UnregisterListener"));
219        _registerListener = new JTextField(30);
220        _unregisterListener = new JTextField(30);
221
222        if (expression != null) {
223            _registerListener.setText(expression.getRegisterListenerScript());
224            _unregisterListener.setText(expression.getUnregisterListenerScript());
225        }
226
227        listernerPanel.setLayout(new GridBagLayout());
228        GridBagConstraints c = new GridBagConstraints();
229        c.gridwidth = 1;
230        c.gridheight = 1;
231        c.gridx = 0;
232        c.gridy = 0;
233        c.anchor = GridBagConstraints.EAST;
234        listernerPanel.add(_registerListenerLabel, c);
235        _registerListenerLabel.setLabelFor(_registerListener);
236        c.gridy = 1;
237        listernerPanel.add(_unregisterListenerLabel, c);
238        _unregisterListenerLabel.setLabelFor(_unregisterListener);
239        c.gridx = 1;
240        c.gridy = 0;
241        c.anchor = GridBagConstraints.WEST;
242        listernerPanel.add(_registerListener, c);
243//        _registerListener.setToolTipText(Bundle.getMessage("SysNameToolTip", "Y"));
244        c.gridy = 1;
245        listernerPanel.add(_unregisterListener, c);
246//        _unregisterListener.setToolTipText(Bundle.getMessage("SysNameToolTip", "Y"));
247
248        JPanel scriptSelectorPanel = new JPanel();
249        scriptSelectorPanel.add(new JLabel(Bundle.getMessage("ExpressionScript_ScriptSelector")));
250        scriptSelectorPanel.add(_scriptEngineSelectorSwing.getComboBox());
251
252        panel.add(scriptSelectorPanel);
253    }
254
255    /** {@inheritDoc} */
256    @Override
257    public boolean validate(@Nonnull List<String> errorMessages) {
258        // Create a temporary expression to test formula
259        ExpressionScript expression = new ExpressionScript("IQDE1", null);
260
261        try {
262            if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) {
263                expression.setOperationReference(_scriptOperationReferenceTextField.getText());
264            }
265        } catch (IllegalArgumentException e) {
266            errorMessages.add(e.getMessage());
267            return false;
268        }
269
270        try {
271            expression.setScriptFormula(_scriptFormulaTextField.getText());
272            if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeDirect) {
273                expression.setScriptAddressing(NamedBeanAddressing.Direct);
274            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeReference) {
275                expression.setScriptAddressing(NamedBeanAddressing.Reference);
276            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeLocalVariable) {
277                expression.setScriptAddressing(NamedBeanAddressing.LocalVariable);
278            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeFormula) {
279                expression.setScriptAddressing(NamedBeanAddressing.Formula);
280            } else {
281                throw new IllegalArgumentException("_tabbedPaneScriptType has unknown selection");
282            }
283        } catch (ParserException e) {
284            errorMessages.add("Cannot parse formula: " + e.getMessage());
285        }
286        return true;
287    }
288
289    /** {@inheritDoc} */
290    @Override
291    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) {
292        ExpressionScript expression = new ExpressionScript(systemName, userName);
293        updateObject(expression);
294        return InstanceManager.getDefault(DigitalExpressionManager.class).registerExpression(expression);
295    }
296
297    /** {@inheritDoc} */
298    @Override
299    public void updateObject(@Nonnull Base object) {
300        if (! (object instanceof ExpressionScript)) {
301            throw new IllegalArgumentException("object must be an ExpressionScript but is a: "+object.getClass().getName());
302        }
303        ExpressionScript expression = (ExpressionScript)object;
304
305        try {
306            if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeDirect) {
307                expression.setOperationAddressing(NamedBeanAddressing.Direct);
308                expression.setOperationType((ExpressionScript.OperationType)_operationComboBox.getSelectedItem());
309            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) {
310                expression.setOperationAddressing(NamedBeanAddressing.Reference);
311                expression.setOperationReference(_scriptOperationReferenceTextField.getText());
312            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeLocalVariable) {
313                expression.setOperationAddressing(NamedBeanAddressing.LocalVariable);
314                expression.setOperationLocalVariable(_scriptOperationLocalVariableTextField.getText());
315            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeFormula) {
316                expression.setOperationAddressing(NamedBeanAddressing.Formula);
317                expression.setOperationFormula(_scriptOperationFormulaTextField.getText());
318            } else {
319                throw new IllegalArgumentException("_tabbedPaneOperationType has unknown selection");
320            }
321
322            if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeDirect) {
323                expression.setScriptAddressing(NamedBeanAddressing.Direct);
324                expression.setScript(_scriptTextField.getText());
325            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeReference) {
326                expression.setScriptAddressing(NamedBeanAddressing.Reference);
327                expression.setScriptReference(_scriptReferenceTextField.getText());
328            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeLocalVariable) {
329                expression.setScriptAddressing(NamedBeanAddressing.LocalVariable);
330                expression.setScriptLocalVariable(_scriptLocalVariableTextField.getText());
331            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeFormula) {
332                expression.setScriptAddressing(NamedBeanAddressing.Formula);
333                expression.setScriptFormula(_scriptFormulaTextField.getText());
334            } else {
335                throw new IllegalArgumentException("_tabbedPaneAspectType has unknown selection");
336            }
337        } catch (ParserException e) {
338            throw new RuntimeException("ParserException: "+e.getMessage(), e);
339        }
340
341        expression.setRegisterListenerScript(_registerListener.getText());
342        expression.setUnregisterListenerScript(_unregisterListener.getText());
343
344        _scriptEngineSelectorSwing.update();
345    }
346
347    /** {@inheritDoc} */
348    @Override
349    public String toString() {
350        return Bundle.getMessage("ExpressionScript_Short");
351    }
352
353    @Override
354    public void dispose() {
355    }
356
357
358    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionScriptSwing.class);
359
360}