/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.debug.core.zend.debugger;
import static org.eclipse.php.internal.debug.core.model.IPHPDataType.DataType.PHP_OBJECT;
import static org.eclipse.php.internal.debug.core.model.IPHPDataType.DataType.PHP_VIRTUAL_CLASS;
import java.util.*;
import org.eclipse.php.internal.debug.core.model.VariablesUtil;
import org.eclipse.php.internal.debug.core.preferences.PHPProjectPreferences;
/**
* @author guy
*/
public class DefaultExpressionsManager implements ExpressionsManager {
private static final Expression[] EMPTY_VARIABLE_ARRAY = new Expression[0];
private static final byte[] ILLEGAL_VAR = { 'N' };
private Debugger debugger;
private Map<String, Object> hashResultDepthOne = new HashMap<String, Object>();
private Map<String, byte[]> hashResultDepthZero = new HashMap<String, byte[]>();
private ExpressionsValueDeserializer expressionValueDeserializer;
private ExpressionsUtil fExpressionsUtil;
/**
* Creates new DefaultExpressionsManager
*/
public DefaultExpressionsManager(Debugger debugger, String transferEncoding) {
this.debugger = debugger;
expressionValueDeserializer = new ExpressionsValueDeserializer(transferEncoding);
fExpressionsUtil = ExpressionsUtil.getInstance(this);
}
public byte[] getExpressionValue(Expression expression, int depth) {
if (!debugger.isActive()) {
return ILLEGAL_VAR;
}
if (expression instanceof StackVariable) {
return getStackVariableValue((StackVariable) expression, depth);
}
String[] name = minimizeArray(expression.getName());
return getVariableValue(name, depth);
}
public boolean assignValue(Expression expression, String value, int depth) {
String[] name = minimizeArray(expression.getName());
String[] path = new String[name.length - 1];
System.arraycopy(name, 1, path, 0, name.length - 1);
boolean status = true;
status = debugger.assignValue(name[0], value, depth, path);
byte[] eValue = debugger.getVariableValue(name[0], depth, path);
if (status) {
String key = buildKey(name);
if (depth == 1) {
hashResultDepthOne.put(key, eValue);
} else if (depth == 0) {
hashResultDepthZero.put(key, eValue);
}
}
return status;
}
public Expression[] getCurrentVariables(int depth) {
Expression contextExpression = CurrentContextExpression.build(debugger);
byte[] value = getExpressionValue(contextExpression, depth);
ExpressionValue variableValue = expressionValueDeserializer.deserializer(contextExpression, value);
Expression[] variables = variableValue.getOriChildren();
if (variables == null || variables.length == 0) {
return EMPTY_VARIABLE_ARRAY;
}
boolean hasThis = false;
List<Expression> currentVariables = new ArrayList<Expression>();
for (int i = 0; i < variables.length - 1; i++) {
String s = variables[i].getFullName();
// Skip $GLOBALS variable (since PHP 5.0.0)
if (s.equals("$GLOBALS")) //$NON-NLS-1$
continue;
// Check if object context is active
if (s.equals("$this")) //$NON-NLS-1$
hasThis = true;
currentVariables.add(variables[i]);
}
// Last one in the list is dummy for a current class name
Expression dummyClass = variables[variables.length - 1];
String className = (String) dummyClass.getValue().getValue();
// Check if we are in static context
if (!hasThis && !"0".equals(className)) { //$NON-NLS-1$
Expression staticClassContext = fExpressionsUtil.fetchStaticContext(className);
if (staticClassContext != null)
currentVariables.add(staticClassContext);
}
variables = currentVariables.toArray(new Expression[currentVariables.size()]);
// Sort by type (default order: this or class, locals, super globals)
VariablesUtil.sortContextMembers(variables);
hashResultDepthOne.put("LOCALS", variables); //$NON-NLS-1$
return variables;
}
public Expression buildExpression(String name) {
return new DefaultExpression(name);
}
public void update(Expression expression, int depth) {
if (expression.getValue().getDataType() == PHP_VIRTUAL_CLASS)
return;
byte[] value = getExpressionValue(expression, depth);
ExpressionValue expressionValue = expressionValueDeserializer.deserializer(expression, value);
// Workaround for fetching static members for objects
if (expressionValue.getDataType() == PHP_OBJECT && CurrentContextExpression.supportsStaticContext(debugger)) {
Expression[] expressionStaticNodes = fExpressionsUtil
.fetchStaticMembers((String) expressionValue.getValue());
List<Expression> allNodes = new ArrayList<Expression>();
allNodes.addAll(Arrays.asList(expressionStaticNodes));
allNodes.addAll(Arrays.asList(expressionValue.getChildren()));
expressionValue = new ExpressionValue(PHP_OBJECT, expressionValue.getValue(),
expressionValue.getValueAsString(), allNodes.toArray(new Expression[allNodes.size()]),
expressionValue.getChildrenCount() + expressionStaticNodes.length);
}
// Sort object members by type & name
if (!PHPProjectPreferences.isSortByName() && expressionValue.getDataType() == PHP_OBJECT)
VariablesUtil.sortObjectMembers(expressionValue.getOriChildren());
expression.setValue(expressionValue);
}
private byte[] getVariableValue(String[] name, int depth) {
String key = buildKey(name);
if (hashResultDepthOne.containsKey(key)) {
return (byte[]) hashResultDepthOne.get(key);
}
if (depth == 0 && hashResultDepthZero.containsKey(key)) {
return (byte[]) hashResultDepthZero.get(key);
}
String[] path = new String[name.length - 1];
System.arraycopy(name, 1, path, 0, name.length - 1);
byte[] value = debugger.getVariableValue(name[0], depth, path);
if (value != null) {
if (depth == 1) {
hashResultDepthOne.put(key, value);
} else if (depth == 0) {
hashResultDepthZero.put(key, value);
}
} else {
value = new byte[] { 'N' };
}
return value;
}
private byte[] getStackVariableValue(StackVariable variable, int depth) {
int layer = variable.getStackDepth();
String[] name = variable.getName();
String[] path = new String[name.length - 1];
System.arraycopy(name, 1, path, 0, name.length - 1);
return debugger.getStackVariableValue(layer, name[0], depth, path);
}
private static String buildKey(String[] name) {
// 5 as the average size of variable name
StringBuilder buffer = new StringBuilder(name.length * 5);
for (int i = 0; i < name.length; i++) {
buffer.append(name[i]);
buffer.append(' ');
}
return buffer.toString();
}
private static String[] minimizeArray(String[] name) {
String firstName = name[0];
if (firstName.startsWith("$GLOBALS[GLOBALS]")) { //$NON-NLS-1$
firstName = "$GLOBALS" + firstName.substring(17); //$NON-NLS-1$
name[0] = firstName;
return minimizeArray(name);
}
if (name.length < 2) {
return name;
}
if (name[0].equals("get_defined_vars()")) { //$NON-NLS-1$
if (name[1].equals("GLOBALS")) { //$NON-NLS-1$
String[] newName = new String[name.length - 1];
newName[0] = "$GLOBALS"; //$NON-NLS-1$
System.arraycopy(name, 2, newName, 1, name.length - 2);
return minimizeArray(newName);
}
}
if (name[0].equals("$GLOBALS") && name[1].equals("GLOBALS")) { //$NON-NLS-1$ //$NON-NLS-2$
String[] newName = new String[name.length - 1];
newName[0] = name[0];
System.arraycopy(name, 2, newName, 1, name.length - 2);
return minimizeArray(newName);
}
return name;
}
}