/** * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Author: atotic * Created on Apr 28, 2004 */ package org.python.pydev.debug.model; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.ui.views.properties.IPropertySource; import org.eclipse.ui.views.tasklist.ITaskListResourceAdapter; import org.python.pydev.debug.model.remote.ChangeVariableCommand; import org.python.pydev.shared_interactive_console.console.codegen.IScriptConsoleCodeGenerator; /** * Represents a python variable. * * Eclipse gives you an option to separate implementation of variable * and its value. I've found it convenient to roll both of them into 1 * class. * */ public class PyVariable extends PlatformObject implements IVariable, IValue, IVariableLocator { protected String name; protected String type; protected String value; protected AbstractDebugTarget target; protected boolean isModified; protected IVariableLocator locator; //Only create one instance of an empty array to be returned private static final IVariable[] EMPTY_IVARIABLE_ARRAY = new IVariable[0]; public PyVariable(AbstractDebugTarget target, String name, String type, String value, IVariableLocator locator) { this.value = value; this.name = name; this.type = type; this.target = target; this.locator = locator; isModified = false; } /** * This is usually not set. It's only set on special cases where the variable must be accessed by the global objects list. */ protected String id; private boolean isReturnValue; private boolean isIPythonHidden; private boolean isErrorOnEval; /** * This method sets information about how this variable was found. */ public void setRefererrerFoundInfo(String id, String foundAs) { if (foundAs != null && foundAs.length() > 0) { name += " Found as: " + foundAs; } if (id != null && id.length() > 0) { this.id = id; } } @Override public String getThreadId() { return locator.getThreadId(); } @Override public String getPyDBLocation() { if (id == null) { return locator.getPyDBLocation() + "\t" + name; } //Ok, this only happens when we're dealing with references with no proper scope given and we need to get //things by id (which is usually not ideal). In this case we keep the proper thread id and set the frame id //as the id of the object to be searched later on based on the list of all alive objects. return locator.getThreadId() + "\t" + id + "\tBY_ID"; } public String getDetailText() throws DebugException { return getValueString(); } @Override public IValue getValue() throws DebugException { return this; } @Override public String getValueString() throws DebugException { if (value == null) { return ""; } if ("StringType".equals(type) || "UnicodeType".equals(type)) { return "\"" + value + "\""; } return value; } public void copyValueString(PyVariable newVariable) { this.type = newVariable.type; this.value = newVariable.value; } @Override public String getName() throws DebugException { return name; } @Override public String getModelIdentifier() { return target.getModelIdentifier(); } @Override public IDebugTarget getDebugTarget() { return target; } @Override public ILaunch getLaunch() { return target.getLaunch(); } /** * LATER valueChanging nterface has not been implemented yet. * When implemented, recently changed variables are shown in red. */ @Override public boolean supportsValueModification() { return this.locator != null; } @Override public boolean hasValueChanged() throws DebugException { return isModified; } public void setModified(boolean mod) { isModified = mod; } /** * This method is called when some value has to be changed to some other expression. * * Note that it will (currently) only work for changing local values that are in the topmost frame. * -- python has no way of making it work right now (see: pydevd_vars.changeAttrExpression) */ @Override public void setValue(String expression) throws DebugException { ChangeVariableCommand changeVariableCommand = getChangeVariableCommand(target, expression); target.postCommand(changeVariableCommand); this.value = expression; target.fireEvent(new DebugEvent(this, DebugEvent.CONTENT | DebugEvent.CHANGE)); } @Override public void setValue(IValue value) throws DebugException { } @Override public boolean verifyValue(String expression) throws DebugException { return true; } @Override public boolean verifyValue(IValue value) throws DebugException { return false; } @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapter) { AdapterDebug.print(this, adapter); if (adapter.equals(ILaunch.class)) { return target.getAdapter(adapter); } else if (adapter.equals(org.eclipse.debug.ui.actions.IRunToLineTarget.class)) { return (T) this.target.getRunToLineTarget(); } else if (adapter.equals(IScriptConsoleCodeGenerator.class)) { return (T) new PyConsoleCodeGeneratorVariable(this); } else if (adapter.equals(IPropertySource.class) || adapter.equals(ITaskListResourceAdapter.class) || adapter.equals(org.eclipse.ui.IContributorResourceAdapter.class) || adapter.equals(org.eclipse.ui.IActionFilter.class) || adapter.equals(org.eclipse.ui.model.IWorkbenchAdapter.class) || adapter.equals(org.eclipse.debug.ui.actions.IToggleBreakpointsTarget.class) || adapter.equals(IResource.class) || adapter.equals(org.eclipse.core.resources.IFile.class)) { return super.getAdapter(adapter); } //cannot check for the actual interface because it may not be available on eclipse 3.2 (it's only available //from 3.3 onwards... and this is only a hack for it to work with eclipse 3.4) if (adapter.toString().endsWith( "org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider")) { return (T) new PyVariableContentProviderHack(); } AdapterDebug.printDontKnow(this, adapter); return super.getAdapter(adapter); } @Override public boolean isAllocated() throws DebugException { return true; } @Override public IVariable[] getVariables() throws DebugException { return EMPTY_IVARIABLE_ARRAY; } @Override public boolean hasVariables() throws DebugException { return false; } @Override public String getReferenceTypeName() throws DebugException { return type; } public ChangeVariableCommand getChangeVariableCommand(AbstractDebugTarget dbg, String expression) { return new ChangeVariableCommand(dbg, getPyDBLocation(), expression); } public void forceGetNewVariables() { //no-op for variable (only really available for PyVariableCollection). } public void setIsReturnValue(boolean isReturnValue) { this.isReturnValue = isReturnValue; } public boolean isReturnValue() { return isReturnValue; } public void setIsIPythonHidden(boolean isIPythonHidden) { this.isIPythonHidden = isIPythonHidden; } public boolean isIPythonHidden() { return isIPythonHidden; } public void setIsErrorOnEval(boolean isErrorOnEval) { this.isErrorOnEval = isErrorOnEval; } public boolean isErrorOnEval() { return isErrorOnEval; } }