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 }