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