001    package nl.cwi.sen1.tide.tool.srcviewer;
002    
003    import java.awt.BorderLayout;
004    import java.awt.FontMetrics;
005    import java.awt.Graphics;
006    import java.awt.event.ActionEvent;
007    import java.io.File;
008    import java.util.ArrayList;
009    import java.util.HashMap;
010    import java.util.Iterator;
011    import java.util.List;
012    import java.util.Map;
013    
014    import javax.swing.AbstractAction;
015    import javax.swing.Action;
016    import javax.swing.JFileChooser;
017    import javax.swing.JLabel;
018    import javax.swing.JOptionPane;
019    import javax.swing.JTabbedPane;
020    import javax.swing.JToolBar;
021    
022    import nl.cwi.sen1.tide.tool.ProcessTool;
023    import nl.cwi.sen1.tide.tool.ToolManager;
024    import nl.cwi.sen1.tide.tool.support.DebugAdapter;
025    import nl.cwi.sen1.tide.tool.support.DebugAdapterListener;
026    import nl.cwi.sen1.tide.tool.support.DebugProcess;
027    import nl.cwi.sen1.tide.tool.support.DebugProcessListener;
028    import nl.cwi.sen1.tide.tool.support.Expr;
029    import nl.cwi.sen1.tide.tool.support.Port;
030    import nl.cwi.sen1.tide.tool.support.ProcessStatusChangeListener;
031    import nl.cwi.sen1.tide.tool.support.Rule;
032    import nl.cwi.sen1.tide.tool.support.VarFormat;
033    
034    public class SourceViewer extends ProcessTool implements DebugProcessListener,
035                    ProcessStatusChangeListener, DebugAdapterListener {
036    
037            private static final String TAG_STEP_INTO = "sv-step-into";
038            private static final String TAG_STEP_OVER = "sv-step-over";
039            private static final String TAG_STEP_UP = "sv-step-up";
040            private static final String TAG_VIEW_VAR = "sv-view-var";
041            private static final String TAG_ADD_SOURCE = "sv-add-source";
042    
043            private JToolBar tools;
044            private JTabbedPane center;
045            private JLabel message;
046    
047            private String tag_step_into;
048            private String tag_step_over;
049            private String tag_step_up;
050            private String tag_view_var;
051            private String tag_add_source;
052    
053            private Action stepInto;
054            private Action stepOver;
055            private Action stepUp;
056            private Action run;
057            private Action stop;
058    
059            private Action addSourceFile;
060            private Action delSourceFile;
061    
062            private DebugProcess process;
063            private Rule ruleStepInto;
064            private Rule ruleStepOver;
065            private Rule ruleStepUp;
066    
067            private String currentFile;
068            private SourceFileViewer currentViewer;
069            private Map<String, SourceFileViewer> residentViewers;
070    
071            public SourceViewer(ToolManager manager, final DebugProcess process) {
072                    super(manager, process);
073    
074                    residentViewers = new HashMap<String, SourceFileViewer>();
075    
076                    tag_step_into = TAG_STEP_INTO + "-" + getId();
077                    tag_step_over = TAG_STEP_OVER + "-" + getId();
078                    tag_step_up = TAG_STEP_UP + "-" + getId();
079                    tag_view_var = TAG_VIEW_VAR + "-" + getId();
080                    tag_add_source = TAG_ADD_SOURCE + "-" + getId();
081    
082                    stepInto = new AbstractAction("Step Into", loadIcon("step-into.gif")) {
083                            public void actionPerformed(ActionEvent event) {
084                                    if (process.isStopped()) {
085                                            process.requestRuleEnabling(ruleStepInto, true);
086                                            process.requestResume();
087                                    }
088                            }
089                    };
090                    stepOver = new AbstractAction("Step Over", loadIcon("step-over.gif")) {
091                            public void actionPerformed(ActionEvent event) {
092                                    if (process.isStopped()) {
093                                            process.requestRuleEnabling(ruleStepOver, true);
094                                            process.requestResume();
095                                    }
096                            }
097                    };
098                    stepUp = new AbstractAction("Step Up", loadIcon("step-up.gif")) {
099                            public void actionPerformed(ActionEvent event) {
100                                    if (process.isStopped()) {
101                                            process.requestRuleEnabling(ruleStepUp, true);
102                                            process.requestResume();
103                                    }
104                            }
105                    };
106                    run = new AbstractAction("Run", loadIcon("run.gif")) {
107                            public void actionPerformed(ActionEvent event) {
108                                    if (process.isStopped()) {
109                                            process.requestResume();
110                                    }
111                            }
112                    };
113                    stop = new AbstractAction("Stop", loadIcon("stop.gif")) {
114                            public void actionPerformed(ActionEvent event) {
115                                    if (process.isRunning()) {
116                                            process.requestBreak();
117                                    }
118                            }
119                    };
120    
121                    boolean stopped = process.isStopped();
122                    stepInto.setEnabled(stopped);
123                    stepOver.setEnabled(stopped);
124                    stepUp.setEnabled(stopped);
125                    run.setEnabled(stopped);
126                    stop.setEnabled(!stopped);
127    
128                    addSourceFile = new AbstractAction("Add File", loadIcon("add-source.gif")) {
129                            public void actionPerformed(ActionEvent event) {
130                                    process.requestEvaluation(Expr.makeListSources(),
131                                                    tag_add_source);
132                            }
133                    };
134    
135                    delSourceFile = new AbstractAction("Remove File", loadIcon("del-source.gif")) {
136                            public void actionPerformed(ActionEvent event) {
137                                    unhighlightCpe();
138                                    if (currentViewer != null) {
139                                            residentViewers.remove(currentViewer.getFile());
140                                            center.remove(currentViewer);
141                                    }
142                            }
143                    };
144    
145                    setLayout(new BorderLayout());
146    
147                    tools = new JToolBar();
148                    tools.add(stepInto).setToolTipText("Step Into");
149                    tools.add(stepOver).setToolTipText("Step Over");
150                    tools.add(stepUp).setToolTipText("Step Up");
151                    tools.add(run).setToolTipText("Run");
152                    tools.add(stop).setToolTipText("Stop");
153                    tools.addSeparator();
154                    tools.add(addSourceFile).setToolTipText("Add Sourcefile");
155                    tools.add(delSourceFile).setToolTipText("Remove Sourcefile");
156    
157                    message = new JLabel("-");
158                    message.setBackground(tools.getBackground());
159                    message.setOpaque(true);
160    
161                    center = new JTabbedPane();
162    
163                    add("North", tools);
164                    add("Center", center);
165                    add("South", message);
166    
167                    this.process = process;
168                    process.addDebugProcessListener(this);
169                    process.addProcessStatusChangeListener(this);
170    
171                    DebugAdapter adapter = process.getAdapter();
172                    adapter.addDebugAdapterListener(this);
173    
174                    process.requestRuleCreation(Port.makeStep(), Expr.makeTrue(), Expr
175                                    .makeBreak(), tag_step_into, false);
176    
177                    Expr stepOverCondition = Expr
178                                    .make("higher-equal(start-level,stack-level)");
179                    process.requestRuleCreation(Port.makeStep(), stepOverCondition, Expr
180                                    .makeBreak(), tag_step_over, false);
181    
182                    Expr stepUpCondition = Expr.make("less(stack-level,start-level)");
183                    process.requestRuleCreation(Port.makeStep(), stepUpCondition, Expr
184                                    .makeBreak(), tag_step_up, false);
185    
186                    highlightCpe();
187            }
188    
189            public void processDestroyed(DebugAdapter adapter, DebugProcess proc) {
190                    if (proc == process) {
191                            // Rules do not need to be removed!
192                            ruleStepInto = null;
193                            ruleStepOver = null;
194                            ruleStepUp = null;
195                            destroy();
196                    }
197            }
198    
199            public void processCreated(DebugAdapter adapter, DebugProcess proc){}
200    
201            public void ruleCreated(DebugProcess process, Rule rule) {
202                    if (rule.getTag().equals(tag_step_into)) {
203                            ruleStepInto = rule;
204                    } else if (rule.getTag().equals(tag_step_over)) {
205                            ruleStepOver = rule;
206                    } else if (rule.getTag().equals(tag_step_up)) {
207                            ruleStepUp = rule;
208                    }
209            }
210    
211            public void ruleDeleted(DebugProcess process, Rule rule) {
212                    if (rule == ruleStepInto) {
213                            ruleStepInto = null;
214                    }
215                    if (rule == ruleStepOver) {
216                            ruleStepOver = null;
217                    }
218                    if (rule == ruleStepUp) {
219                            ruleStepUp = null;
220                    }
221            }
222    
223            public void ruleModified(DebugProcess process, Rule rule) {}
224    
225            public void ruleTriggered(DebugProcess process, Rule rule, Expr value) {}
226    
227            public void evaluationResult(DebugProcess process, Expr expr, Expr value,
228                            String tag) {
229                    if (tag.equals(tag_view_var)) {
230                            if (value.isError()) {
231                                    displayError(value);
232                            } else if (value.isVarUnknown()) {
233                                    message.setText("Unknown variable: "
234                                                    + value.getVarUnknownMessage());
235                            } else if (value.isVar()) {
236                                    String var = value.getVarName();
237                                    Expr val = value.getVarValue();
238    
239                                    String strval = val.toString();
240    
241                                    int start = value.getVarSourceStart();
242                                    int linenr = value.getVarSourceLineNr();
243                                    int column = value.getVarSourceStartColumn();
244                                    int length = value.getVarSourceLength();
245    
246                                    message.setText("Value of " + var + " at " + linenr + ","
247                                                    + column + " = " + strval);
248    
249                                    ValuePopup popup = new ValuePopup(getManager(), currentViewer,
250                                                    process, expr, var, value, new VarFormat());
251                                    currentViewer.highlightVariable(start, length, popup);
252                            } else {
253                                    displayError("Strange variable result: ", value);
254                            }
255                    } else if (tag.equals(tag_add_source)) {
256                            if (value.isError()) {
257                                    displayError("Can't retrieve source files", value
258                                                    .getErrorData());
259                            } else if (value.isSourcePath()) {
260                                    addSourceFromDisk(value.getSourcePath());
261                            } else if (value.isSourceList()) {
262                                    // addSourceFromDisk(System.getProperty("user.dir"));
263                                    addSourceFromList(value.sourceIterator());
264                            } else {
265                                    // Not reduced
266                                    addSourceFromDisk(System.getProperty("user.dir"));
267                            }
268                    }
269            }
270    
271            public void processStatusChanged(DebugProcess process) {
272                    boolean stopped = process.isStopped();
273    
274                    stepInto.setEnabled(stopped);
275                    stepOver.setEnabled(stopped);
276                    stepUp.setEnabled(stopped);
277                    run.setEnabled(stopped);
278                    stop.setEnabled(!stopped);
279    
280                    if (stopped) {
281                            if (ruleStepInto.isEnabled()) {
282                                    process.requestRuleEnabling(ruleStepInto, false);
283                            }
284    
285                            if (ruleStepOver.isEnabled()) {
286                                    process.requestRuleEnabling(ruleStepOver, false);
287                            }
288    
289                            if (ruleStepUp.isEnabled()) {
290                                    process.requestRuleEnabling(ruleStepUp, false);
291                            }
292                            highlightCpe();
293                    } else {
294                            unhighlightCpe();
295                    }
296            }
297    
298            void switchToFile(String file) {
299                    unhighlightCpe();
300    
301                    currentViewer = residentViewers.get(file);
302                    if (currentViewer == null) {
303                            ToolManager manager = getManager();
304                            int id = getId();
305                            currentViewer = new SourceFileViewer(manager, process, file, id,
306                                            tag_view_var);
307                            residentViewers.put(file, currentViewer);
308                            synchronized (process) {
309                                    currentViewer.highlightRules(process.ruleIterator());
310                            }
311                            File f = new File(file);
312                            center.insertTab(f.getName(), null, currentViewer, file, center
313                                            .getTabCount());
314                    }
315    
316                    center.setSelectedComponent(currentViewer);
317            }
318    
319            private void highlightCpe() {
320                    Expr expr = process.getLastLocation();
321    
322                    if (currentViewer != null) {
323                            currentViewer.unhighlightCpe();
324                    }
325    
326                    if (expr != null && expr.isLocation()) {
327                            if (expr.isLocationUnknown()) {
328                                    message.setText("Current location is unknown: "
329                                                    + expr.toString());
330                            } else {
331                                    String file = expr.getLocationFileName();
332    
333                                    if (!file.equals(currentFile)) {
334                                            switchToFile(file);
335                                    }
336    
337                                    currentViewer.highlightCpe();
338                            }
339                    }
340            }
341    
342            private void unhighlightCpe() {
343                    if (currentViewer != null) {
344                            currentViewer.unhighlightCpe();
345                    }
346            }
347    
348            public void addSourceFromDisk(String path) {
349                    JFileChooser chooser = new JFileChooser(path);
350                    int option = chooser.showOpenDialog(this);
351                    if (option == JFileChooser.APPROVE_OPTION) {
352                            switchToFile(chooser.getSelectedFile().getAbsolutePath());
353                    }
354            }
355    
356            public void addSourceFromList(Iterator<String> iter) {
357                    List<String> list = new ArrayList<String>();
358                    while (iter.hasNext()) {
359                            list.add(iter.next());
360                    }
361    
362                    if (list.size() > 0) {
363                            String selected = (String) JOptionPane.showInputDialog(this, null,
364                                            "Please pick one of the registered files",
365                                            JOptionPane.QUESTION_MESSAGE, null, list.toArray(), list
366                                                            .get(0));
367    
368                            if (selected != null) {
369                                    switchToFile(selected);
370                            }
371                    }
372            }
373    }
374    
375    class LongLabel extends JLabel {
376            private FontMetrics metrics;
377    
378            public void paint(Graphics g) {
379                    if (metrics == null) {
380                            metrics = getFontMetrics(getFont());
381                    }
382    
383                    String txt = getText();
384                    String str = txt;
385                    int width = metrics.stringWidth(txt);
386                    while (width >= getWidth()) {
387                            str = str.substring(1);
388                            txt = "..." + str;
389                            width = metrics.stringWidth(txt);
390                    }
391    
392                    g.drawString(txt, 0, metrics.getMaxAscent());
393            }
394    }