001    package nl.cwi.sen1.tide.tool.ruleinspector;
002    
003    //{{{ imports
004    
005    import java.awt.BorderLayout;
006    import java.awt.Color;
007    import java.awt.GridLayout;
008    import java.awt.event.ActionEvent;
009    import java.awt.event.ActionListener;
010    import java.awt.event.InputMethodEvent;
011    import java.awt.event.InputMethodListener;
012    import java.util.ArrayList;
013    import java.util.Iterator;
014    import java.util.List;
015    
016    import javax.swing.JButton;
017    import javax.swing.JCheckBox;
018    import javax.swing.JComboBox;
019    import javax.swing.JLabel;
020    import javax.swing.JOptionPane;
021    import javax.swing.JPanel;
022    import javax.swing.JScrollPane;
023    import javax.swing.JTable;
024    import javax.swing.JTextArea;
025    import javax.swing.JTextField;
026    import javax.swing.ListSelectionModel;
027    import javax.swing.SwingConstants;
028    import javax.swing.border.LineBorder;
029    import javax.swing.border.TitledBorder;
030    import javax.swing.event.DocumentEvent;
031    import javax.swing.event.DocumentListener;
032    import javax.swing.event.ListSelectionEvent;
033    import javax.swing.event.ListSelectionListener;
034    import javax.swing.event.TableModelEvent;
035    import javax.swing.table.AbstractTableModel;
036    import javax.swing.table.DefaultTableCellRenderer;
037    import javax.swing.table.TableColumn;
038    
039    import nl.cwi.sen1.tide.tool.ProcessTool;
040    import nl.cwi.sen1.tide.tool.ToolManager;
041    import nl.cwi.sen1.tide.tool.support.DebugAdapter;
042    import nl.cwi.sen1.tide.tool.support.DebugAdapterListener;
043    import nl.cwi.sen1.tide.tool.support.DebugProcess;
044    import nl.cwi.sen1.tide.tool.support.DebugProcessListener;
045    import nl.cwi.sen1.tide.tool.support.Expr;
046    import nl.cwi.sen1.tide.tool.support.Port;
047    import nl.cwi.sen1.tide.tool.support.Rule;
048    
049    //}}}
050    
051    public class RuleInspector
052      extends ProcessTool
053      implements DebugAdapterListener
054    {
055      private RuleSelector selector;
056      private RuleEditor   editor;
057    
058      //{{{ public RuleInspector(ToolManager manager, final DebugProcess process)
059    
060      public RuleInspector(ToolManager manager, final DebugProcess process)
061      {
062        super(manager, process);
063    
064        setSize(360, 350);
065    
066        setLayout(new GridLayout(0,1));
067    
068        selector = new RuleSelector(process);
069        editor   = new RuleEditor(process);
070        selector.addRuleSelectionListener(editor);
071    
072        add(selector);
073        add(editor);
074    
075        selector.setBackground(Color.white);
076        editor.setBackground(Color.white);
077    
078        DebugAdapter adapter = process.getAdapter();
079        adapter.addDebugAdapterListener(this);
080      }
081    
082      //}}}
083      //{{{ public void editRule(Rule rule)
084    
085      public void editRule(Rule rule)
086      {
087        selector.selectRule(rule);
088      }
089    
090      //}}}
091    
092      //{{{ public void processDestroyed(DebugAdapter adapter, DebugProcess proc)
093    
094      public void processDestroyed(DebugAdapter adapter, DebugProcess proc)
095      {
096        if (proc == process) {
097          destroy();
098        }
099      }
100    
101      //}}}
102      //{{{ public void processCreated(DebugAdapter adapter, DebugProcess proc)
103    
104      public void processCreated(DebugAdapter adapter, DebugProcess proc)
105      {
106      }
107    
108      //}}}
109    }
110    
111    class RuleSelector
112      extends JPanel
113      implements DebugProcessListener, ListSelectionListener
114    {
115      private DebugProcess process;
116    
117      List<RuleSelectionListener> listeners;
118    
119      RuleSelectorTableModel tableModel;
120      JTable table;
121    
122      //{{{ public RuleSelector(DebugProcess process)
123    
124      public RuleSelector(DebugProcess process)
125      {
126        this.process = process;
127        listeners = new ArrayList<RuleSelectionListener>();
128        setBorder(new TitledBorder(new LineBorder(Color.gray), "Rule Selector"));
129    
130        tableModel = new RuleSelectorTableModel(process);
131        table = new JTable(tableModel);
132    
133        ListSelectionModel selModel = table.getSelectionModel();
134        selModel.addListSelectionListener(this);
135    
136        TableColumn column_id = table.getColumn(tableModel.getColumnName(0));
137        column_id.setMaxWidth(30);
138        DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
139        renderer.setHorizontalAlignment(SwingConstants.CENTER);
140        column_id.setCellRenderer(renderer);
141    
142        setLayout(new BorderLayout());
143        add(new JScrollPane(table), "Center");
144    
145        process.addDebugProcessListener(this);
146      }
147    
148      //}}}
149      //{{{ public void cleanup()
150    
151      public void cleanup()
152      {
153        process.removeDebugProcessListener(this);
154      }
155    
156      //}}}
157      //{{{ public void addRuleSelectionListener(RuleSelectionListener listener)
158    
159      public void addRuleSelectionListener(RuleSelectionListener listener)
160      {
161        listeners.add(listener);
162      }
163    
164      //}}}
165      //{{{ private void fireRuleSelected(Rule rule)
166    
167      private void fireRuleSelected(Rule rule)
168      {
169        Iterator<RuleSelectionListener> iter = listeners.iterator();
170        while (iter.hasNext()) {
171          RuleSelectionListener listener = iter.next();
172          listener.ruleSelected(rule);
173        }
174      }
175    
176      //}}}
177      //{{{ public void ruleCreated(DebugProcess process, Rule rule)
178    
179      public void ruleCreated(DebugProcess process, Rule rule)
180      {
181        int index = process.getNrOfRules()-1;
182        tableModel.fireTableChanged(new TableModelEvent(tableModel, index, index,
183                                                        TableModelEvent.ALL_COLUMNS,
184                                                        TableModelEvent.INSERT));
185      }
186    
187      //}}}
188      //{{{ public void ruleDeleted(DebugProcess process, Rule rule)
189    
190      public void ruleDeleted(DebugProcess process, Rule rule)
191      {
192        int index = process.getRuleIndex(rule);
193        tableModel.fireTableChanged(new TableModelEvent(tableModel, index, index,
194                                                        TableModelEvent.ALL_COLUMNS,
195                                                        TableModelEvent.DELETE));
196      }
197    
198      //}}}
199      //{{{ public void ruleModified(DebugProcess process, Rule rule)
200    
201      public void ruleModified(DebugProcess process, Rule rule)
202      {
203        int index = process.getRuleIndex(rule);
204        tableModel.fireTableChanged(new TableModelEvent(tableModel, index, index,
205                                                        TableModelEvent.ALL_COLUMNS,
206                                                        TableModelEvent.UPDATE));
207      }
208    
209      //}}}
210      //{{{ public void ruleTriggered(DebugProcess process, Rule rule, Expr value)
211    
212      public void ruleTriggered(DebugProcess process, Rule rule, Expr value){}
213    
214      //}}}
215      //{{{ public void evaluationResult(process, expr, value, tag)
216    
217      public void evaluationResult(DebugProcess process, Expr expr, Expr value, String tag){}
218    
219      //}}}
220    
221      //{{{ void selectRule(Rule rule)
222    
223      void selectRule(Rule rule)
224      {
225        int index = process.getRuleIndex(rule);
226        table.changeSelection(index, 0, false, false);
227        fireRuleSelected(rule);
228      }
229    
230      //}}}
231    
232      //{{{ public void valueChanged(ListSelectionEvent event)
233    
234      public void valueChanged(ListSelectionEvent event)
235      {
236            int row = table.getSelectedRow();
237            
238            if (row != -1) {
239          Rule rule = process.getRuleAt(row);
240          fireRuleSelected(rule);
241            }
242      }
243    
244      //}}}
245    }
246    
247    class RuleSelectorTableModel
248      extends AbstractTableModel
249    {
250      private DebugProcess process;
251    
252      //{{{ public RuleSelectorTableModel(DebugProcess process)
253    
254      public RuleSelectorTableModel(DebugProcess process)
255      {
256        this.process = process;
257      }
258    
259      //}}}
260      //{{{ public int getRowCount()
261    
262      public int getRowCount()
263      {
264        return process.getNrOfRules();
265      }
266    
267      //}}}
268      //{{{ public int getColumnCount()
269    
270      public int getColumnCount()
271      {
272        return 3;
273      }
274    
275      //}}}
276      //{{{ public Object getValueAt(int row, int col)
277    
278      public Object getValueAt(int row, int col)
279      {
280        Rule rule = process.getRuleAt(row);
281        switch (col) {
282          case 0:
283            return new Integer(rule.getRid());
284          case 1:
285            return rule.getTag();
286          case 2:
287            return rule.getPort().toTerm();
288        }
289    
290        throw new RuntimeException("illegal column: " + col);
291      }
292    
293      //}}}
294      //{{{ public String getColumnName(int col)
295    
296      public String getColumnName(int col)
297      {
298        switch (col) {
299          case 0: return "Id";
300          case 1: return "Tag";
301          case 2: return "Port";
302        }
303        throw new RuntimeException("illegal column: " + col);
304      }
305    
306      //}}}
307      //{{{ public Class getColumnClass(int col)
308    
309      public Class<?> getColumnClass(int col)
310      {
311        return getValueAt(0, col).getClass();
312      }
313    
314      //}}}
315    }
316    
317    class RuleEditor
318      extends JPanel
319      implements RuleSelectionListener, ActionListener, DebugProcessListener
320    {
321      private final static Color COLOR_MODIFIED   = Color.red;
322      private final static Color COLOR_UNMODIFIED = Color.black;
323    
324      DebugProcess process;
325      Rule rule;
326      TitledBorder border;
327    
328      boolean modified;
329    
330      //{{{ UI attributes
331    
332      JLabel     tag;
333      JCheckBox  enabled;
334      JLabel     portLabel;
335      JComboBox  port;
336      JLabel     conditionLabel;
337      JTextField condition;
338      JLabel     actionLabel;
339      JTextArea  action;
340    
341      JButton createButton;
342      JButton applyButton;
343      JButton revertButton;
344      JButton deleteButton;
345    
346      //}}}
347    
348      //{{{ public RuleEditor(DebugProcess process)
349    
350      public RuleEditor(DebugProcess process)
351      {
352        this.process = process;
353    
354        //{{{ Create UI components 
355    
356        border = new TitledBorder(new LineBorder(Color.gray), "Rule Editor");
357        setBorder(border);
358    
359        tag = new JLabel();
360        tag.setForeground(COLOR_UNMODIFIED);
361        enabled = new JCheckBox("Enabled");
362        portLabel = new JLabel("Port:");
363        port = new JComboBox();
364        port.setEnabled(false);
365    
366        for (int i=0; i<DebugProcess.PORT_TYPES.length; i++) {
367          port.addItem(DebugProcess.PORT_TYPES[i]);
368        }
369    
370        conditionLabel = new JLabel("Condition:");
371        condition = new JTextField();
372        actionLabel = new JLabel("Action:", SwingConstants.CENTER);
373        action = new JTextArea();
374        createButton = new JButton("Create");
375        applyButton = new JButton("Apply");
376        revertButton = new JButton("Revert");
377        deleteButton = new JButton("Delete");
378    
379    
380        //}}}
381    
382        //{{{ Add event listeners
383    
384        process.addDebugProcessListener(this);
385    
386        enabled.addActionListener(this);
387        createButton.addActionListener(this);
388        applyButton.addActionListener(this);
389        revertButton.addActionListener(this);
390        deleteButton.addActionListener(this);
391        port.addActionListener(this);
392        condition.addActionListener(this);
393        
394        condition.getDocument().addDocumentListener(new DocumentListener()
395          {
396            public void changedUpdate(DocumentEvent evt) { 
397              conditionLabel.setForeground(COLOR_MODIFIED);
398              setModified(true);
399            }
400            public void insertUpdate(DocumentEvent evt) { 
401              conditionLabel.setForeground(COLOR_MODIFIED);
402              setModified(true);
403            }
404            public void removeUpdate(DocumentEvent evt) { 
405              conditionLabel.setForeground(COLOR_MODIFIED);
406              setModified(true);
407            }
408          });
409    
410        action.addInputMethodListener(new InputMethodListener()
411          {
412            public void caretPositionChanged(InputMethodEvent evt) { }
413            public void inputMethodTextChanged(InputMethodEvent evt) {
414              actionLabel.setForeground(COLOR_MODIFIED);
415            }
416          });
417    
418        //}}}
419    
420        //{{{ Build UI
421    
422        setLayout(new BorderLayout());
423        JPanel top = new JPanel();
424        top.setLayout(new BorderLayout());
425        JPanel labels = new JPanel();
426        labels.setLayout(new GridLayout(3,1));
427    
428        JLabel tagLabel = new JLabel("Tag:");
429        tagLabel.setForeground(COLOR_UNMODIFIED);
430        labels.add(tagLabel);
431        labels.add(portLabel);
432        labels.add(conditionLabel);
433        top.add("West", labels);
434    
435        JPanel controls = new JPanel();
436        controls.setLayout(new GridLayout(3,1));
437    
438        JPanel tagAndEnabling = new JPanel();
439        tagAndEnabling.setLayout(new BorderLayout());
440        tagAndEnabling.add("West", tag);
441        tagAndEnabling.add("East", enabled);
442        controls.add(tagAndEnabling);
443        controls.add(port);
444        controls.add(condition);
445        top.add("Center", controls);
446    
447        JPanel bottom = new JPanel();
448        bottom.setLayout(new BorderLayout());
449        bottom.add(actionLabel, "North");
450        bottom.add(action, "Center");
451    
452        JPanel buttons = new JPanel();
453        buttons.setLayout(new GridLayout(1,0));
454        buttons.add(createButton);
455        buttons.add(applyButton);
456        buttons.add(revertButton);
457        buttons.add(deleteButton);
458    
459        add("North",  top);
460        add("Center", bottom);
461        add("South",  buttons);
462    
463    
464        //}}}
465    
466        clear();
467      }
468    
469      //}}}
470      //{{{ public void cleanup()
471    
472      public void cleanup()
473      {
474        process.removeDebugProcessListener(this);
475      }
476    
477      //}}}
478      //{{{ public void clear()
479    
480      public void clear()
481      {
482        tag.setText("-");
483        enabled.setSelected(false);
484        enabled.setEnabled(false);
485        port.setSelectedIndex(0);
486        port.setEnabled(false);
487        condition.setText("");
488        condition.setEnabled(false);
489        action.setText("");
490        action.setEnabled(false);
491    
492    
493        applyButton.setEnabled(false);
494        revertButton.setEnabled(false);
495        deleteButton.setEnabled(false);
496    
497        enabled.setForeground(COLOR_UNMODIFIED);
498        portLabel.setForeground(COLOR_UNMODIFIED);
499        conditionLabel.setForeground(COLOR_UNMODIFIED);
500        actionLabel.setForeground(COLOR_UNMODIFIED);
501      }
502    
503      //}}}
504      //{{{ public void ruleSelected(Rule rule)
505    
506      public void ruleSelected(Rule rule)
507      {
508        this.rule = rule;
509        border.setTitle("Rule Editor: Rule " + rule.getRid());
510    
511        tag.setText(rule.getTag());
512        enabled.setSelected(rule.isEnabled());
513        enabled.setEnabled(true);
514        port.setSelectedItem(rule.getPort().toTerm().toString());
515        port.setEnabled(true);
516        condition.setText(rule.getCondition().toTerm().toString());
517        condition.setEnabled(true);
518        action.setText(rule.getAction().toTerm().toString());
519        action.setEnabled(true);
520    
521        applyButton.setEnabled(false);
522        revertButton.setEnabled(false);
523        deleteButton.setEnabled(true);
524    
525        enabled.setForeground(COLOR_UNMODIFIED);
526        portLabel.setForeground(COLOR_UNMODIFIED);
527        conditionLabel.setForeground(COLOR_UNMODIFIED);
528        actionLabel.setForeground(COLOR_UNMODIFIED);
529    
530        setModified(false);
531    
532    
533        repaint();
534      }
535    
536      //}}}
537      //{{{ public void ruleCreated(DebugProcess process, Rule rule)
538    
539      public void ruleCreated(DebugProcess process, Rule rule){}
540    
541      //}}}
542      //{{{ public void ruleDeleted(DebugProcess process, Rule rule)
543    
544      public void ruleDeleted(DebugProcess process, Rule rule)
545      {
546        if (rule == this.rule) {
547          clear();
548          rule = null;
549        }
550      }
551    
552      //}}}
553      //{{{ public void ruleModified(DebugProcess process, Rule rule)
554    
555      public void ruleModified(DebugProcess process, Rule rule)
556      {
557        if (rule == this.rule) {
558          ruleSelected(rule);
559        }
560      }
561    
562      //}}}
563      //{{{ public void ruleTriggered(DebugProcess process, Rule rule, Expr value)
564    
565      public void ruleTriggered(DebugProcess process, Rule rule, Expr value){}
566    
567      //}}}
568      //{{{ public void evaluationResult(process, expr, value, tag)
569    
570      public void evaluationResult(DebugProcess process, Expr expr, Expr value, String tag){}
571    
572      //}}}
573      //{{{ private void setModified(boolean on)
574    
575      private void setModified(boolean on)
576      {
577        modified = on;
578        revertButton.setEnabled(on);
579        applyButton.setEnabled(on);
580      }
581    
582      //}}}
583    
584      //{{{ public void actionPerformed(ActionEvent event)
585    
586      public void actionPerformed(ActionEvent event)
587      {
588        Object source = event.getSource();
589    
590        if (source == enabled) {
591          //{{{ Change enabled button to modified
592    
593          enabled.setForeground(COLOR_MODIFIED);
594          setModified(true);
595    
596          //}}}
597        } else if (source == port) {
598          //{{{ Change port combobox to modified
599    
600          if (port.isEnabled()) {
601            portLabel.setForeground(COLOR_MODIFIED);
602            setModified(true);
603          }
604    
605          //}}}
606        } else if (source == createButton) {
607          //{{{ Handle create rule
608    
609          String tag = (String)
610            JOptionPane.showInternalInputDialog(this,
611                                                "What tag should the new rule have?",
612                                                "Create a new Rule",
613                                                JOptionPane.PLAIN_MESSAGE,
614                                                null, null, "manual-rule");
615          if (tag != null) {
616            process.requestRuleCreation(Port.makeStep(), Expr.makeTrue(), Expr.makeTrue(),
617                                        tag, false);
618          }
619    
620          //}}}
621        } else if (source == revertButton) {
622          //{{{ Handle revert rule
623    
624          ruleSelected(rule);
625    
626          //}}}
627        } else if (source == applyButton) {
628          //{{{ Handle apply changes
629    
630          String portItem = (String)port.getSelectedItem();
631          Port port = Port.parse(portItem);
632          if (port == null) {
633            JOptionPane.showInternalMessageDialog(this, "Illegal port: " + portItem,
634                                                  "Illegal Port",
635                                                  JOptionPane.ERROR_MESSAGE);
636            return;
637          }
638    
639          String condTxt = condition.getText();
640          Expr cond = Expr.parse(condTxt);
641          if (cond == null) {
642            JOptionPane.showInternalMessageDialog(this, "Not an expression: " + condTxt,
643                                                  "Illegal Condition",
644                                                  JOptionPane.ERROR_MESSAGE);
645            return;
646          }
647    
648          String actTxt = action.getText();
649          Expr act = Expr.parse(actTxt);
650          if (act == null) {
651            JOptionPane.showInternalMessageDialog(this, "Not an expression: " + actTxt,
652                                                  "Illegal Action",
653                                                  JOptionPane.ERROR_MESSAGE);
654            return;
655          }
656    
657          boolean enabled = this.enabled.isSelected();
658    
659          process.requestRuleModification(rule, port, cond, act, enabled);
660    
661          //}}}
662        } else if (source == deleteButton) {
663          //{{{ Handle delete rule
664    
665          process.requestRuleDeletion(rule);
666    
667          //}}}
668        }
669      }
670    
671      //}}}
672    }
673    
674    //{{{ interface RuleSelectionListener
675    
676    interface RuleSelectionListener
677    {
678      public void ruleSelected(Rule rule);
679    }
680    
681    //}}}
682