/********************************************************************** Copyright (c) 2000, 2002 IBM Corp. and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html Contributors: IBM Corporation - Initial implementation Vicente Fernando - www.alfersoft.com.ar **********************************************************************/ package net.sourceforge.phpdt.internal.debug.core.model; import java.util.Vector; import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IRegisterGroup; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.core.model.IVariable; /** * * TODO Remove the variables array and use only the varList vector * Have also to change hasVariables */ public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable{ private PHPThread thread; // The thread to which this stackframe belongs private String file; // The file name??? private int lineNumber; // private int index; // private int modno; // private PHPVariable[] variables; // The array of variables TODO: better introduce a vector? private Vector varList = new Vector (); private String description; // private boolean fUpToDate; // /** * * @param thread * @param file * @param line * @param index * @param desc * @param modno */ public PHPStackFrame (PHPThread thread, String file, int line, int index, String desc, int modno) { super (null); this.lineNumber = line; this.index = index; this.file = file; this.thread = thread; this.description = desc; this.modno = modno; this.fUpToDate = false; } /** * * @param thread * @param file * @param line * @param index */ public PHPStackFrame (PHPThread thread, String file, int line, int index) { super (null); this.lineNumber = line; this.index = index; this.file = file; this.thread = thread; this.fUpToDate = false; } /** * */ public IThread getThread () { return thread; } /** * @param thread */ public void setThread (PHPThread thread) { this.thread = thread; } /** * */ private void setUpToDate (boolean upToDate) { fUpToDate = upToDate; } /** * */ private boolean isUpToDate () { return fUpToDate; } /** * * */ private void resetHasChangedInfo (Vector varList) { int n; PHPVariable var; PHPValue val; for (n = 0; n < varList.size (); n++) { // For every variable in 'DBG list' var = (PHPVariable) varList.get (n); // Get the variable val = (PHPValue) var.getValue (); // Get the variable's value try { if (val.hasVariables ()) { // Do we have other variables within the value if (!hasRecursion (var)) { // Is this variable (value) branch recursive? resetHasChangedInfo (val.getChildVariables ()); // No, go into branch } } } catch (DebugException e) { // That's, because of the hasVariables method } var.setValueChanged (false); // Reset the 'has changed' flag } } /** * Go up the tree of PHPVariables * look whether the PHPValue is a reference to a parent PHPValue * * TODO Check where this recursion can come from. * Whether this back reference is legal or a bug. * * @param var * @return * */ private boolean hasRecursion (PHPVariable var) { PHPVariable parentVar; PHPValue val; val = (PHPValue) var.getValue (); // Get the PHPValue from the current PHPVariable while (var != null) { // As long as we have PHPVariable parentVar = var.getParent (); // Get the parent PHPVariable if (parentVar != null) { // Is there a parent? if (parentVar.getValue ().equals (val)) { // Get the PHPValue for the parent PHPVariable and check // whether it is the same return true; // Return, if we have recursion } } var = parentVar; } return false; // No recursion found } /** * This method updates the 'static' variables list. * It does a replication between the 'static' list (the variable list which * is a member of this DBG interface object) and the DBG variable list * (the list of variables which is received from PHP via DBG with the current suspend) * Replication is done in the following way: * * * @param varListOld The 'static' list of variables which are to be updated. * @param varListNew The new list of (current) variables from DBG. */ private void updateVariableList (Vector varListOld, Vector varListNew) { PHPVariable varOld; // The variable from the 'static' list PHPVariable varNew; // The variable from the DBG list PHPValue valOld; // The value of the current variable from 'static' list PHPValue valNew; // The value of the current variable from DBG list int n; // Index for the DBG list int o; // Index for the static list // Add the variables (and childs) to the static list if they are new // and update the values of variables which are already existend within // the 'static' list. for (n = 0; n < varListNew.size (); n++) { // For every variable in 'DBG list' varNew = (PHPVariable) varListNew.get (n); // Get the DBG variable for (o = 0; o < varListOld.size (); o++) { // For every variable in static list varOld = (PHPVariable) varListOld.get (o); // Get the static variable if (varNew.getName ().equals (varOld.getName ())) { // Did we found the variable within the 'static' list? valOld = (PHPValue) varOld.getValue (); // Get the value from 'static' valNew = (PHPValue) varNew.getValue (); // Get the value from DBG try { if (valOld.hasVariables () || // If the 'static' value has child variables valNew.hasVariables ()) { // or if the DBG value has child variables if (!hasRecursion (varOld) && !hasRecursion (varNew)) { // Both branches should not have a recursion updateVariableList (valOld.getChildVariables (), // Update the variable list for the child variables valNew.getChildVariables ()); } } else if (!valOld.getValueString ().equals (valNew.getValueString ())) { // Has the value changed? valOld.setValueString (valNew.getValueString ()); // Yes, set the 'static' value (variable) to the new value varOld.setValueChanged (true); // and set the 'has changed' flag, so that the variable view // could show the user the changed status with a different // color } //else { // varOld.setValueChanged (false); // Reset the 'has changed' flag //} } catch (DebugException e) { // That's, because of the hasVariables method } break; // Found the variable, } } if (o == varListOld.size ()) { // Did we found the variable within the static list? varListOld.add (varNew); // No, then add the DBG variable to the static list } } // Look for the variables we can remove from the 'static' list for (o = 0; o < varListOld.size (); o++) { // For every variable in 'static' list varOld = (PHPVariable) varListOld.get (o); // Get the static variable for (n = 0; n < varListNew.size (); n++) { // For all variables in 'DBG' list varNew = (PHPVariable) varListNew.get (n); // Get the variable from the 'DBG' list if (varNew.getName ().equals (varOld.getName ())) { // Did we found the 'static' list variable within the 'DBG' list? break; // Yes we found the variable, then leave the loop } } if (n == varListNew.size ()) { // Did not find the 'static' list variable within the 'DBG' list? varListOld.remove (o); // then remove the 'static' list variable from list o -= 1; // Adjust the 'static' list index } } } /** * * This function returns the array of PHPVariables for this stackframe * The PHPVariables should not change (newly build up) between two steps * (or breaks). * A PHPVariable with the same name but with different object ID is * handled as a new variable. * * TODO Remove the intermediate storage array * * @return The array of PHPVariables for this stackframe. */ public IVariable[] getVariables() throws DebugException { PHPVariable[] variablesNew; // The intermediate storage of the variable array we get from DBG proxy //variablesNew = this.getPHPDBGProxy ().readVariables (this); // Get the variable array from DBG proxy //variables = variablesNew; // Store the array the stackframes member variable if (!isUpToDate ()) { resetHasChangedInfo (varList); updateVariableList (varList, this.getPHPDBGProxy ().readVariables (this)); setUpToDate (true); variables = (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]); } return variables; // Give the array back to user interface } /** * */ private PHPVariable findVariable (Vector varList, String varname) { PHPVariable variable; PHPValue value; int i; for (i = 0; i < varList.size (); i++) { // For all variables variable = (PHPVariable) varList.get (i); // Get the variable value = (PHPValue) variable.getValue (); // Get the value of the variable try { if (value.hasVariables ()) { // Does the variable/value have children if (!hasRecursion (variable)) { // Don't follow recursive variable/values variable = findVariable (value.getChildVariables (), varname); if (variable != null) { return variable; } } } else if ((variable.getName ()).equals (varname)) { // return variable; // } } catch (DebugException e) { // That's, because of the hasVariables method } } return null; } /** * This method is called from the UI (e.g. from PHPDebugHover * to find the variable the mouse is pointing to) * * @param s The variable name we are looking for. * @return */ public IVariable findVariable (String s) throws DebugException { if (!isUpToDate ()) { resetHasChangedInfo (varList); updateVariableList (varList, this.getPHPDBGProxy ().readVariables (this)); setUpToDate (true); variables = (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]); } return (findVariable (varList, s)); // Prefix the variable name with $ } /** * */ public boolean hasVariables () throws DebugException { return (varList.size () > 0); } public int getLineNumber() { return lineNumber; } public void setLineNumber(int line) { lineNumber = line; } public int getCharStart() throws DebugException { // not supported return -1; } public int getCharEnd() throws DebugException { // not supported return -1; } public String getName() { if(!this.getDescription().equals("")) return this.getDescription() + " [line: " + this.getLineNumber() + "]"; else return this.getFileName() + " [line: " + this.getLineNumber() + "]"; } public String getFileName() { return file; } public void setDescription(String desc) { this.description= desc; } public String getDescription() { return this.description; } public IRegisterGroup[] getRegisterGroups() throws DebugException { return null; } public boolean hasRegisterGroups() throws DebugException { return false; } public String getModelIdentifier() { return this.getThread().getModelIdentifier(); } public IDebugTarget getDebugTarget() { return this.getThread().getDebugTarget(); } public ILaunch getLaunch() { return this.getDebugTarget().getLaunch(); } public boolean canStepInto() { return canResume(); } public boolean canStepOver() { return canResume(); } public boolean canStepReturn() { return canResume(); } public boolean isStepping() { return false; } /** * */ public void stepInto () throws DebugException { DebugEvent ev; setUpToDate (false); thread.prepareForResume (DebugEvent.STEP_INTO); // Don't know why, but this is necessary this.getPHPDBGProxy ().readStepIntoEnd (PHPStackFrame.this); ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO); DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev }); } /** * */ public void stepOver () throws DebugException { DebugEvent ev; setUpToDate (false); thread.prepareForResume (DebugEvent.STEP_OVER); this.getPHPDBGProxy ().readStepOverEnd (PHPStackFrame.this) ; ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER); DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev }); } /** * */ public void stepReturn () throws DebugException { DebugEvent ev; setUpToDate (false); thread.prepareForResume (DebugEvent.STEP_RETURN); this.getPHPDBGProxy ().readStepReturnEnd (PHPStackFrame.this) ; ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN); DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev }); } public boolean canResume() { return this.getThread().canResume(); } public boolean canSuspend() { return this.getThread().canSuspend(); } public boolean isSuspended() { return this.getThread().isSuspended(); } public void resume() throws DebugException { setUpToDate (false); this.getThread().resume(); } public void suspend() throws DebugException { } public boolean canTerminate() { return this.getThread().canTerminate(); } public boolean isTerminated() { return this.getThread().isTerminated(); } public void terminate() throws DebugException { getPHPDBGProxy().stop(); } public int getIndex() { return index; } public void setIndex (int index) { this.index = index; } public PHPDBGProxy getPHPDBGProxy() { PHPDebugTarget DebugTarget; DebugTarget = (PHPDebugTarget) thread.getDebugTarget (); return DebugTarget.getPHPDBGProxy (); } public void setFile(String file) { this.file = file; } public int getModNo() { return modno; } /** * This function is needed when sorting the stackframes by their index numbers. * * @param obj The stackframe which this one is compared to. * @return * */ public int compareTo (Object obj) { if (index < ((PHPStackFrame) obj).getIndex ()) { return -1; } else if (index > ((PHPStackFrame) obj).getIndex ()) { return 1; } return 0; } }