ae6d982a44e7b242a3581be336391aca4939412f
[idea/community.git] / python / pydevSrc / com / jetbrains / python / debugger / PyDebugValue.java
1 package com.jetbrains.python.debugger;
2
3 import com.google.common.base.Strings;
4 import com.intellij.icons.AllIcons;
5 import com.intellij.openapi.application.ApplicationManager;
6 import com.intellij.openapi.diagnostic.Logger;
7 import com.intellij.xdebugger.frame.*;
8 import com.jetbrains.python.debugger.pydev.PyVariableLocator;
9 import org.jetbrains.annotations.NotNull;
10 import org.jetbrains.annotations.Nullable;
11
12 import javax.swing.*;
13
14 // todo: load long lists by parts
15 // todo: null modifier for modify modules, class objects etc.
16 public class PyDebugValue extends XNamedValue {
17   private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.pydev.PyDebugValue");
18   public static final int MAX_VALUE = 256;
19
20   private String myTempName = null;
21   private final String myType;
22   private final String myTypeQualifier;
23   private final String myValue;
24   private final boolean myContainer;
25   private final PyDebugValue myParent;
26   private String myId = null;
27
28   private final PyFrameAccessor myFrameAccessor;
29
30   private PyVariableLocator myVariableLocator;
31
32   private final boolean myErrorOnEval;
33
34   public PyDebugValue(@NotNull final String name, final String type, String typeQualifier, final String value, final boolean container,
35                       boolean errorOnEval, final PyFrameAccessor frameAccessor) {
36     this(name, type, typeQualifier, value, container, errorOnEval, null, frameAccessor);
37   }
38
39   public PyDebugValue(@NotNull final String name, final String type, String typeQualifier, final String value, final boolean container,
40                       boolean errorOnEval, final PyDebugValue parent, final PyFrameAccessor frameAccessor) {
41     super(name);
42     myType = type;
43     myTypeQualifier = Strings.isNullOrEmpty(typeQualifier) ? null : typeQualifier;
44     myValue = value;
45     myContainer = container;
46     myErrorOnEval = errorOnEval;
47     myParent = parent;
48     myFrameAccessor = frameAccessor;
49   }
50
51   public String getTempName() {
52     return myTempName != null ? myTempName : myName;
53   }
54
55   public void setTempName(String tempName) {
56     myTempName = tempName;
57   }
58
59   public String getType() {
60     return myType;
61   }
62
63   public String getValue() {
64     return myValue;
65   }
66
67   public boolean isContainer() {
68     return myContainer;
69   }
70
71   public boolean isErrorOnEval() {
72     return myErrorOnEval;
73   }
74   
75   public PyDebugValue setParent(@Nullable PyDebugValue parent) {
76     return new PyDebugValue(myName, myType, null, myValue, myContainer, myErrorOnEval, parent, myFrameAccessor);
77   }
78
79   public PyDebugValue getParent() {
80     return myParent;
81   }
82
83   public PyDebugValue getTopParent() {
84     return myParent == null ? this : myParent.getTopParent();
85   }
86
87   @Override
88   public String getEvaluationExpression() {
89     StringBuilder stringBuilder = new StringBuilder();
90     buildExpression(stringBuilder);
91     return stringBuilder.toString();
92   }
93
94   void buildExpression(StringBuilder result) {
95     if (myParent == null) {
96       result.append(getTempName());
97     }
98     else {
99       myParent.buildExpression(result);
100       if (("dict".equals(myParent.getType()) || "list".equals(myParent.getType()) || "tuple".equals(myParent.getType())) &&
101           !isLen(myName)) {
102         result.append('[').append(removeLeadingZeros(removeId(myName))).append(']');
103       }
104       else if (("set".equals(myParent.getType())) && !isLen(myName)) {
105         //set doesn't support indexing
106       }
107       else if (isLen(myName)) {
108         result.append('.').append(myName).append("()");
109       }
110       else if (("ndarray".equals(myParent.getType()) || "matrix".equals(myParent.getType())) && myName.startsWith("[")) {
111         result.append(removeLeadingZeros(myName));
112       }
113       else {
114         result.append('.').append(myName);
115       }
116     }
117   }
118
119   private static String removeId(@NotNull String name) {
120     if (name.indexOf('(') != -1) {
121       name = name.substring(0, name.indexOf('(')).trim();
122     }
123
124     return name;
125   }
126
127   private static String removeLeadingZeros(@NotNull String name) {
128     //bugs.python.org/issue15254: "0" prefix for octal
129     while (name.length() > 1 && name.startsWith("0")) {
130       name = name.substring(1);
131     }
132     return name;
133   }
134
135   private static boolean isLen(String name) {
136     return "__len__".equals(name);
137   }
138
139   private static boolean isCollection(@NotNull PyDebugValue parent) {
140     String type = parent.getType();
141     return type.equals("dict") || type.equals("list");
142   }
143
144   private static String getChildNamePresentation(@NotNull PyDebugValue parent, @NotNull String childName) {
145     if (isCollection(parent)) {
146       return "[".concat(removeId(childName)).concat("]");
147     }
148     else {
149       return ".".concat(childName);
150     }
151   }
152
153   private String getFullName() {
154     String result = "";
155     String curNodeName = myName;
156     PyDebugValue parent = myParent;
157     while (parent != null) {
158       result = getChildNamePresentation(parent, curNodeName).concat(result);
159       curNodeName = parent.getName();
160       parent = parent.getParent();
161     }
162     return curNodeName.concat(result);
163   }
164
165   @Override
166   public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) {
167     String value = PyTypeHandler.format(this);
168
169     if (value.length() >= MAX_VALUE) {
170       node.setFullValueEvaluator(new PyFullValueEvaluator(myFrameAccessor, getFullName()));
171       value = value.substring(0, MAX_VALUE);
172     }
173
174     node.setPresentation(getValueIcon(), myType, value, myContainer);
175   }
176
177   @Override
178   public void computeChildren(@NotNull final XCompositeNode node) {
179     if (node.isObsolete()) return;
180     ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
181       @Override
182       public void run() {
183         if (myFrameAccessor == null) return;
184
185         try {
186           final XValueChildrenList values = myFrameAccessor.loadVariable(PyDebugValue.this);
187           if (!node.isObsolete()) {
188             node.addChildren(values, true);
189           }
190         }
191         catch (PyDebuggerException e) {
192           if (!node.isObsolete()) {
193             node.setErrorMessage("Unable to display children:" + e.getMessage());
194           }
195           LOG.warn(e);
196         }
197       }
198     });
199   }
200
201   @Override
202   public XValueModifier getModifier() {
203     return new PyValueModifier(myFrameAccessor, this);
204   }
205
206   private Icon getValueIcon() {
207     if (!myContainer) {
208       return AllIcons.Debugger.Db_primitive;
209     }
210     else if ("list".equals(myType) || "tuple".equals(myType)) {
211       return AllIcons.Debugger.Db_array;
212     }
213     else {
214       return AllIcons.Debugger.Value;
215     }
216   }
217   
218   public PyDebugValue setName(String newName) {
219     return new PyDebugValue(newName, myType, null, myValue, myContainer, myErrorOnEval, myParent, myFrameAccessor);
220   }
221
222   @Nullable
223   @Override
224   public XReferrersProvider getReferrersProvider() {
225     if (myFrameAccessor.getReferrersLoader() != null) {
226       return new XReferrersProvider() {
227         @Override
228         public XValue getReferringObjectsValue() {
229           return new PyReferringObjectsValue(PyDebugValue.this);
230         }
231       };
232     } else {
233       return null;
234     }
235   }
236
237   public PyFrameAccessor getFrameAccessor() {
238     return myFrameAccessor;
239   }
240
241   public PyVariableLocator getVariableLocator() {
242     return myVariableLocator;
243   }
244
245   public void setVariableLocator(PyVariableLocator variableLocator) {
246     myVariableLocator = variableLocator;
247   }
248
249   public String getId() {
250     return myId;
251   }
252
253   public void setId(String id) {
254     myId = id;
255   }
256
257   @Override
258   public boolean canNavigateToSource() {
259     return true;
260   }
261
262   @Override
263   public void computeSourcePosition(@NotNull XNavigatable navigatable) {
264     navigatable.setSourcePosition(myFrameAccessor.getSourcePositionForName(myName));
265   }
266
267   @Override
268   public boolean canNavigateToTypeSource() {
269     return true;
270   }
271
272   @Override
273   public void computeTypeSourcePosition(@NotNull XNavigatable navigatable) {
274
275     navigatable.setSourcePosition(myFrameAccessor.getSourcePositionForType(getQualifiedType()));
276   }
277
278   public String getQualifiedType() {
279     if (Strings.isNullOrEmpty(myType))
280       return null;
281     return (myTypeQualifier == null) ? myType : (myTypeQualifier + "." + myType);
282   }
283
284   public String getTypeQualifier() {
285     return myTypeQualifier;
286   }
287 }