/**********************************************************************
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; // The source file name with the full path on target/remote system
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
*
* - false if the PHPValue is not a child of itself
*
- true if the PHPValue is
*
*/
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:
*
* - It looks for new variables within the DBG variables list and
* adds them to the 'static' list.
*
- It looks for changed variables copies the current value to the variable within
* the 'static list' and mark these variables as 'hasChanged' (which uses the UI
* for showing the variable with a different color).
*
- It looks for variables within the 'static' list, and removes them
* from the 'static' list in case the do not appear within the DBG list.
*
*
* @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() {
StringBuffer name = new StringBuffer();
if (!this.getDescription().equals ("")) {
name.append (this.getDescription ());
}
else {
name.append (this.getFileName ());
}
name.append (" [line ");
name.append (this.getLineNumber ());
name.append ("]");
return name.toString();
}
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
*
* - -1 if the index of this stackframe is less.
*
- 0 if the index of both stackfream is equal (should no happen).
*
- 1 if the index of this stackfram is greater.
*
*/
public int compareTo (Object obj)
{
if (index < ((PHPStackFrame) obj).getIndex ()) {
return -1;
}
else if (index > ((PHPStackFrame) obj).getIndex ()) {
return 1;
}
return 0;
}
}