1 /**********************************************************************
2 Copyright (c) 2000, 2002 IBM Corp. and others.
3 All rights reserved. This program and the accompanying materials
4 are made available under the terms of the Common Public License v1.0
5 which accompanies this distribution, and is available at
6 http://www.eclipse.org/legal/cpl-v10.html
9 IBM Corporation - Initial implementation
10 Vicente Fernando - www.alfersoft.com.ar
11 **********************************************************************/
12 package net.sourceforge.phpdt.internal.debug.core.model;
14 import java.util.Collections;
15 import java.util.Vector;
17 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
19 import org.eclipse.core.runtime.IAdaptable;
20 import org.eclipse.debug.core.DebugEvent;
21 import org.eclipse.debug.core.DebugException;
22 import org.eclipse.debug.core.ILaunch;
23 import org.eclipse.debug.core.model.IDebugTarget;
24 import org.eclipse.debug.core.model.IRegisterGroup;
25 import org.eclipse.debug.core.model.IStackFrame;
26 import org.eclipse.debug.core.model.IThread;
27 import org.eclipse.debug.core.model.IVariable;
31 * TODO Remove the variables array and use only the varList vector
32 * Have also to change hasVariables
34 public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable {
36 private PHPThread thread; // The thread to which this stackframe belongs
37 private String file; // The file name???
38 private int lineNumber;
41 private int scope_id; // scope id
42 //private PHPVariable[] variables; // The array of variables TODO: better introduce a vector?
43 private Vector varList = new Vector();
44 private String description; // The source file name with the full path on target/remote system
45 private boolean fUpToDate; // Indicates whether the variable list within this stackframe is
47 private boolean fAvailable; // Needed when updating the stackframe list, shows whether the stackframe
48 // is within the list which was received from dbg
60 public PHPStackFrame(PHPThread thread, String file, int line, int index,
61 String desc, int modno, int scope_id) {
64 this.lineNumber = line;
68 this.description = desc;
70 this.scope_id = scope_id;
71 this.fUpToDate = false;
81 public PHPStackFrame(PHPThread thread, String file, int line, int index) {
84 this.lineNumber = line;
88 this.fUpToDate = false;
95 public int getScopeID() {
102 public void setScopeID(int scope_id) {
103 this.scope_id = scope_id;
110 public IThread getThread() {
117 public void setThread(PHPThread thread) {
118 this.thread = thread;
124 private void setUpToDate(boolean upToDate) {
125 fUpToDate = upToDate;
131 private boolean isUpToDate() {
138 public void setAvailable(boolean available) {
139 fAvailable = available;
145 public boolean isAvailable() {
150 * @see IAdaptable#getAdapter(Class)
152 public Object getAdapter(Class adapter) {
153 if (adapter == PHPStackFrame.class) {
157 return super.getAdapter(adapter);
163 private void resetHasChangedInfo(Vector varList) {
168 for (n = 0; n < varList.size(); n++) { // For every variable in 'DBG list'
169 var = (PHPVariable) varList.get(n); // Get the variable
170 val = (PHPValue) var.getValue(); // Get the variable's value
173 if (val.hasVariables()) { // Do we have other variables within the value
174 if (!hasRecursion(var)) { // Is this variable (value) branch recursive?
175 resetHasChangedInfo(val.getChildVariables()); // No, go into branch
178 } catch (DebugException e) { // That's, because of the hasVariables method
181 var.setValueChanged(false); // Reset the 'has changed' flag
186 * Go up the tree of PHPVariables
187 * look whether the PHPValue is a reference to a parent PHPValue
189 * TODO Check where this recursion can come from.
190 * Whether this back reference is legal or a bug.
195 * <li> false if the PHPValue is not a child of itself
196 * <li> true if the PHPValue is
199 private boolean hasRecursion(PHPVariable var) {
200 PHPVariable parentVar;
203 val = (PHPValue) var.getValue(); // Get the PHPValue from the current PHPVariable
205 while (var != null) { // As long as we have PHPVariable
206 parentVar = var.getParent(); // Get the parent PHPVariable
208 if (parentVar != null) { // Is there a parent?
209 if (parentVar.getValue().equals(val)) { // Get the PHPValue for the parent PHPVariable and check
210 // whether it is the same
211 return true; // Return, if we have recursion
218 return false; // No recursion found
222 * This method updates the 'static' variables list.
223 * It does a replication between the 'static' list (the variable list which
224 * is a member of this DBG interface object) and the DBG variable list
225 * (the list of variables which is received from PHP via DBG with the current suspend)
226 * Replication is done in the following way:
228 * <li> It looks for new variables within the DBG variables list and
229 * adds them to the 'static' list.
230 * <li> It looks for changed variables copies the current value to the variable within
231 * the 'static list' and mark these variables as 'hasChanged' (which uses the UI
232 * for showing the variable with a different color).
233 * <li> It looks for variables within the 'static' list, and removes them
234 * from the 'static' list in case the do not appear within the DBG list.
237 * @param varListOld The 'static' list of variables which are to be updated.
238 * @param varListNew The new list of (current) variables from DBG.
240 private void updateVariableList(Vector varListOld, Vector varListNew) {
241 PHPVariable varOld; // The variable from the 'static' list
242 PHPVariable varNew; // The variable from the DBG list
243 PHPValue valOld; // The value of the current variable from 'static' list
244 PHPValue valNew; // The value of the current variable from DBG list
245 int n; // Index for the DBG list
246 int o; // Index for the static list
248 // Add the variables (and childs) to the static list if they are new
249 // and update the values of variables which are already existend within
250 // the 'static' list.
252 for (n = 0; n < varListNew.size(); n++) { // For every variable in 'DBG list'
253 varNew = (PHPVariable) varListNew.get(n); // Get the DBG variable
255 for (o = 0; o < varListOld.size(); o++) { // For every variable in static list
256 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
258 if (varNew.getName().equals(varOld.getName())) { // Did we found the variable within the 'static' list?
259 valOld = (PHPValue) varOld.getValue(); // Get the value from 'static'
260 valNew = (PHPValue) varNew.getValue(); // Get the value from DBG
263 if (valOld.hasVariables() || // If the 'static' value has child variables
264 valNew.hasVariables()) { // or if the DBG value has child variables
265 if (!hasRecursion(varOld) && !hasRecursion(varNew)) { // Both branches should not have a recursion
266 updateVariableList(valOld.getChildVariables(), // Update the variable list for the child variables
267 valNew.getChildVariables());
269 } else if (!valOld.getValueString().equals(
270 valNew.getValueString())) { // Has the value changed?
271 valOld.setValueString(valNew.getValueString()); // Yes, set the 'static' value (variable) to the new value
272 varOld.setValueChanged(true); // and set the 'has changed' flag, so that the variable view
273 // could show the user the changed status with a different
277 // varOld.setValueChanged (false); // Reset the 'has changed' flag
279 } catch (DebugException e) { // That's, because of the hasVariables method
282 break; // Found the variable,
286 if (o == varListOld.size()) { // Did we found the variable within the static list?
287 varListOld.add(varNew); // No, then add the DBG variable to the static list
291 // Look for the variables we can remove from the 'static' list
293 for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list
294 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
296 for (n = 0; n < varListNew.size(); n++) { // For all variables in 'DBG' list
297 varNew = (PHPVariable) varListNew.get(n); // Get the variable from the 'DBG' list
299 if (varNew.getName().equals(varOld.getName())) { // Did we found the 'static' list variable within the 'DBG' list?
300 break; // Yes we found the variable, then leave the loop
304 if (n == varListNew.size()) { // Did not find the 'static' list variable within the 'DBG' list?
305 varListOld.remove(o); // then remove the 'static' list variable from list
306 o -= 1; // Adjust the 'static' list index
313 * This function returns the array of PHPVariables for this stackframe
314 * The PHPVariables should not change (newly build up) between two steps
316 * A PHPVariable with the same name but with different object ID is
317 * handled as a new variable.
319 * TODO Remove the intermediate storage array
321 * @return The array of PHPVariables for this stackframe.
323 public IVariable[] getVariables() throws DebugException {
325 resetHasChangedInfo(varList);
326 updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
328 Collections.sort(varList, new PHPVariableComparator());
329 //variables = (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
330 //Arrays.sort(variables, new PHPVariableComparator());
333 //return variables; // Give the array back to user interface
334 return (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
340 private PHPVariable findVariable(Vector varList, String varname) {
341 PHPVariable variable;
345 for (i = 0; i < varList.size(); i++) { // For all variables
346 variable = (PHPVariable) varList.get(i); // Get the variable
347 value = (PHPValue) variable.getValue(); // Get the value of the variable
350 if (value.hasVariables()) { // Does the variable/value have children
351 if (!hasRecursion(variable)) { // Don't follow recursive variable/values
352 variable = findVariable(value.getChildVariables(),
355 if (variable != null) {
359 } else if ((variable.getName()).equals(varname)) { //
362 } catch (DebugException e) { // That's, because of the hasVariables method
370 * This method is called from the UI (e.g. from PHPDebugHover
371 * to find the variable the mouse is pointing to)
373 * @param s The variable name we are looking for.
376 public IVariable findVariable(String s) throws DebugException {
378 //resetHasChangedInfo(varList);
379 //updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
381 //variables = (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
385 return (findVariable(varList, s)); // Prefix the variable name with $
391 public boolean hasVariables() throws DebugException {
393 // return (varList.size () > 0);
396 public int getLineNumber() {
400 public void setLineNumber(int line) {
404 public int getCharStart() throws DebugException {
409 public int getCharEnd() throws DebugException {
414 public String getName() {
415 StringBuffer name = new StringBuffer();
417 if (!this.getDescription().equals("")) {
418 name.append(this.getDescription());
420 name.append(this.getFileName());
423 name.append(" [line ");
424 name.append(this.getLineNumber());
427 return name.toString();
430 public String getFileName() {
434 public void setDescription(String desc) {
435 this.description = desc;
438 public String getDescription() {
439 return this.description;
442 public IRegisterGroup[] getRegisterGroups() throws DebugException {
446 public boolean hasRegisterGroups() throws DebugException {
450 public String getModelIdentifier() {
451 return this.getThread().getModelIdentifier();
454 public IDebugTarget getDebugTarget() {
455 return this.getThread().getDebugTarget();
458 public ILaunch getLaunch() {
459 return this.getDebugTarget().getLaunch();
462 public boolean canStepInto() {
466 public boolean canStepOver() {
470 public boolean canStepReturn() {
474 public boolean isStepping() {
481 public void stepInto() throws DebugException {
486 thread.prepareForResume(DebugEvent.STEP_INTO); // Don't know why, but this is necessary
487 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
489 // Commented out sending the RESUME event because it was already sent by prepareForResume.
490 // The second RESUME event leads only to a little flickering within the variables view.
491 // It is also not clear why this event was necessary in eclipse < 3.2
492 // Also sending a SUSPEND event here leads to a total rebuild of the variables view.
493 // (eclipse 3.2 has a build in timeout of 500 ms which leads to a auto suspend, with
494 // no flickering... but why???)
496 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
497 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
503 public void stepOver() throws DebugException {
508 thread.prepareForResume(DebugEvent.STEP_OVER);
509 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
511 // See comment within the previous stepInto method.
513 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
514 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
520 public void stepReturn() throws DebugException {
525 thread.prepareForResume(DebugEvent.STEP_RETURN);
526 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
528 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
529 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
532 public boolean canResume() {
533 return this.getThread().canResume();
536 public boolean canSuspend() {
537 return this.getThread().canSuspend();
540 public boolean isSuspended() {
541 return this.getThread().isSuspended();
544 public void resume() throws DebugException {
546 this.getThread().resume();
549 public void suspend() throws DebugException {
552 public boolean canTerminate() {
553 return this.getThread().canTerminate();
556 public boolean isTerminated() {
557 return this.getThread().isTerminated();
560 public void terminate() throws DebugException {
561 getPHPDBGProxy().stop();
564 public int getIndex() {
568 public void setIndex(int index) {
572 public PHPDBGProxy getPHPDBGProxy() {
573 PHPDebugTarget DebugTarget;
575 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
577 return DebugTarget.getPHPDBGProxy();
580 public void setFile(String file) {
584 public int getModNo() {
589 * This function is needed when sorting the stackframes by their index numbers.
591 * @param obj The stackframe which this one is compared to.
594 * <li> -1 if the index of this stackframe is less.
595 * <li> 0 if the index of both stackfream is equal (should no happen).
596 * <li> 1 if the index of this stackfram is greater.
599 public int compareTo(Object obj) {
600 //if (index < ((PHPStackFrame) obj).getIndex()) {
602 //} else if (index > ((PHPStackFrame) obj).getIndex()) {
607 return Integer.signum(index - ((PHPStackFrame) obj).getIndex());