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 }