fixing ExecutionManagerTest.testRun - more data required
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / engine / JVMNameUtil.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.debugger.engine;
17
18 import com.intellij.debugger.DebuggerBundle;
19 import com.intellij.debugger.DebuggerManager;
20 import com.intellij.debugger.SourcePosition;
21 import com.intellij.debugger.engine.evaluation.EvaluateException;
22 import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
23 import com.intellij.ide.util.JavaAnonymousClassesHelper;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.progress.ProcessCanceledException;
27 import com.intellij.openapi.util.Comparing;
28 import com.intellij.openapi.util.Computable;
29 import com.intellij.openapi.util.Ref;
30 import com.intellij.openapi.util.text.StringUtil;
31 import com.intellij.psi.*;
32 import com.intellij.psi.jsp.JspFile;
33 import com.intellij.psi.util.ClassUtil;
34 import com.intellij.psi.util.PsiTreeUtil;
35 import com.intellij.psi.util.PsiUtil;
36 import com.intellij.psi.util.TypeConversionUtil;
37 import com.sun.jdi.ReferenceType;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import java.util.ArrayList;
42 import java.util.List;
43
44 /**
45  * User: lex
46  * Date: Sep 2, 2003
47  * Time: 11:25:59 AM
48  */
49 public class JVMNameUtil {
50   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.JVMNameUtil");
51
52   @SuppressWarnings({"HardCodedStringLiteral"})
53   public static String getPrimitiveSignature(String typeName) {
54     if(PsiType.BOOLEAN.getCanonicalText().equals(typeName)) {
55       return "Z";
56     }
57     else if (PsiType.BYTE.getCanonicalText().equals(typeName)) {
58       return "B";
59     }
60     else if (PsiType.CHAR.getCanonicalText().equals(typeName)) {
61       return "C";
62     }
63     else if (PsiType.SHORT.getCanonicalText().equals(typeName)) {
64       return "S";
65     }
66     else if (PsiType.INT.getCanonicalText().equals(typeName)) {
67       return "I";
68     }
69     else if (PsiType.LONG.getCanonicalText().equals(typeName)) {
70       return "J";
71     }
72     else if (PsiType.FLOAT.getCanonicalText().equals(typeName)) {
73       return "F";
74     }
75     else if (PsiType.DOUBLE.getCanonicalText().equals(typeName)) {
76       return "D";
77     }
78     else if (PsiType.VOID.getCanonicalText().equals(typeName)) {
79       return "V";
80     }
81     return null;
82   }
83
84   @SuppressWarnings({"HardCodedStringLiteral"})
85   private static void appendJVMSignature(JVMNameBuffer buffer , PsiType type){
86     if (type == null) {
87       return;
88     }
89     final PsiType psiType = TypeConversionUtil.erasure(type);
90     if (psiType instanceof PsiArrayType) {
91       buffer.append(new JVMRawText("["));
92       appendJVMSignature(buffer, ((PsiArrayType) psiType).getComponentType());
93     }
94     else if (psiType instanceof PsiClassType) {
95       final JVMName jvmName = getJVMQualifiedName(psiType);
96       appendJvmClassQualifiedName(buffer, jvmName);
97     }
98     else if (psiType instanceof PsiPrimitiveType) {
99       buffer.append(getPrimitiveSignature(psiType.getCanonicalText()));
100     }
101     else {
102       LOG.error("unknown type " + type.getCanonicalText());
103     }
104   }
105
106   private static void appendJvmClassQualifiedName(JVMNameBuffer buffer, final JVMName jvmName) {
107     buffer.append("L");
108     if(jvmName instanceof JVMRawText) {
109       buffer.append(((JVMRawText)jvmName).getName().replace('.','/'));
110     }
111     else {
112       buffer.append(new JVMName() {
113         public String getName(DebugProcessImpl process) throws EvaluateException {
114           return jvmName.getName(process).replace('.','/');
115         }
116
117         public String getDisplayName(DebugProcessImpl debugProcess) {
118           return jvmName.getDisplayName(debugProcess);
119         }
120       });
121     }
122     buffer.append(";");
123   }
124
125   private static class JVMNameBuffer {
126     List<JVMName> myList = new ArrayList<JVMName>();
127
128     public void append(@NotNull JVMName evaluator){
129       myList.add(evaluator);
130     }
131
132     public void append(char name){
133       append(Character.toString(name));
134     }
135
136     public void append(String text){
137       myList.add(getJVMRawText(text));
138     }
139
140     public JVMName toName() {
141       final List<JVMName> optimised = new ArrayList<JVMName>();
142       for (JVMName evaluator : myList) {
143         if (evaluator instanceof JVMRawText && !optimised.isEmpty() && optimised.get(optimised.size() - 1) instanceof JVMRawText) {
144           JVMRawText nameEvaluator = (JVMRawText)optimised.get(optimised.size() - 1);
145           nameEvaluator.setName(nameEvaluator.getName() + ((JVMRawText)evaluator).getName());
146         }
147         else {
148           optimised.add(evaluator);
149         }
150       }
151
152       if(optimised.size() == 1) return optimised.get(0);
153       if(optimised.isEmpty()) return new JVMRawText("");
154
155       return new JVMName() {
156         String myName = null;
157         public String getName(DebugProcessImpl process) throws EvaluateException {
158           if(myName == null){
159             String name = "";
160             for (JVMName nameEvaluator : optimised) {
161               name += nameEvaluator.getName(process);
162             }
163             myName = name;
164           }
165           return myName;
166         }
167
168         public String getDisplayName(DebugProcessImpl debugProcess) {
169           if(myName == null) {
170             String displayName = "";
171             for (JVMName nameEvaluator : optimised) {
172               displayName += nameEvaluator.getDisplayName(debugProcess);
173             }
174             return displayName;
175           }
176           return myName;
177         }
178       };
179     }
180   }
181
182   private static class JVMRawText implements JVMName {
183     private String myText;
184
185     public JVMRawText(String text) {
186       myText = text;
187     }
188
189     public String getName(DebugProcessImpl process) throws EvaluateException {
190       return myText;
191     }
192
193     public String getDisplayName(DebugProcessImpl debugProcess) {
194       return myText;
195     }
196
197     public String getName() {
198       return myText;
199     }
200
201     public void setName(String name) {
202       myText = name;
203     }
204   }
205
206   private static class JVMClassAt implements JVMName {
207     private final SourcePosition mySourcePosition;
208
209     public JVMClassAt(SourcePosition sourcePosition) {
210       mySourcePosition = sourcePosition;
211     }
212
213     public String getName(DebugProcessImpl process) throws EvaluateException {
214       List<ReferenceType> allClasses = process.getPositionManager().getAllClasses(mySourcePosition);
215       // If there are more than one available, try to match by name
216       if (allClasses.size() > 1) {
217         String name = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
218           public String compute() {
219             return getClassVMName(getClassAt(mySourcePosition));
220           }
221         });
222         for (ReferenceType aClass : allClasses) {
223           if (Comparing.equal(aClass.name(), name)) {
224             return name;
225           }
226         }
227       }
228       if (!allClasses.isEmpty()) {
229         return allClasses.get(0).name();
230       }
231
232       throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("error.class.not.loaded", getDisplayName(process)));
233     }
234
235     public String getDisplayName(final DebugProcessImpl debugProcess) {
236       return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
237         public String compute() {
238           return getSourcePositionClassDisplayName(debugProcess, mySourcePosition);
239         }
240       });
241     }
242   }
243
244   public static JVMName getJVMRawText(String qualifiedName) {
245     return new JVMRawText(qualifiedName);
246   }
247
248   public static JVMName getJVMQualifiedName(PsiType psiType) {
249     if(psiType instanceof PsiArrayType) {
250       final PsiArrayType arrayType = (PsiArrayType)psiType;
251       JVMName jvmName = getJVMQualifiedName(arrayType.getComponentType());
252       JVMNameBuffer buffer = new JVMNameBuffer();
253       buffer.append(jvmName);
254       buffer.append("[]");
255       return buffer.toName();
256     }
257
258     PsiClass psiClass = PsiUtil.resolveClassInType(psiType);
259     if (psiClass == null) {
260       return getJVMRawText(psiType.getCanonicalText());
261     } 
262     else {
263       return getJVMQualifiedName(psiClass);
264     }
265   }
266                                
267   public static JVMName getJVMQualifiedName(@NotNull PsiClass psiClass) {
268     final String name = getNonAnonymousClassName(psiClass);
269     if (name != null) {
270       return getJVMRawText(name);
271     }
272     else {
273       return new JVMClassAt(SourcePosition.createFromElement(psiClass));
274     }
275   }
276
277   @Nullable
278   public static JVMName getContextClassJVMQualifiedName(@Nullable SourcePosition pos) {
279     final PsiClass psiClass = getClassAt(pos);
280     if (psiClass == null) {
281       return null;
282     }
283     final String name = getNonAnonymousClassName(psiClass);
284     if (name != null) {
285       return getJVMRawText(name);
286     }
287     return new JVMClassAt(pos);
288   }
289
290   @Nullable
291   public static String getNonAnonymousClassName(PsiClass aClass) {
292     if (PsiUtil.isLocalOrAnonymousClass(aClass)) {
293       return null;
294     }
295     String name = aClass.getName();
296     if (name == null) {
297       return null;
298     }
299     PsiClass parentClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
300     if (parentClass != null) {
301       final String parentName = getNonAnonymousClassName(parentClass);
302       if (parentName == null) {
303         return null;
304       }
305       return parentName + "$" + name;
306     }
307     return DebuggerManager.getInstance(aClass.getProject()).getVMClassQualifiedName(aClass);
308   }
309
310   public static JVMName getJVMConstructorSignature(@Nullable PsiMethod method, @Nullable PsiClass declaringClass) {
311     return getJVMSignature(method, true, declaringClass);
312   }
313
314   public static JVMName getJVMSignature(@NotNull PsiMethod method) {
315     return getJVMSignature(method, method.isConstructor(), method.getContainingClass());
316   }
317
318   @SuppressWarnings({"HardCodedStringLiteral"})
319   private static JVMName getJVMSignature(@Nullable PsiMethod method, boolean constructor, @Nullable PsiClass declaringClass) {
320     JVMNameBuffer signature = new JVMNameBuffer();
321     signature.append("(");
322     
323     if (constructor) {
324       if (declaringClass != null) {
325         final PsiClass outerClass = declaringClass.getContainingClass();
326         if (outerClass != null) {
327           // declaring class is an inner class
328           if (!declaringClass.hasModifierProperty(PsiModifier.STATIC)) {
329             appendJvmClassQualifiedName(signature, getJVMQualifiedName(outerClass));
330           }
331         }
332       }
333     }
334     if (method != null) {
335       for (PsiParameter psiParameter : method.getParameterList().getParameters()) {
336         appendJVMSignature(signature, psiParameter.getType());
337       }
338     }
339     signature.append(")");
340     if (!constructor && method != null) {
341       appendJVMSignature(signature, method.getReturnType());
342     }
343     else {
344       signature.append(new JVMRawText("V"));
345     }
346     return signature.toName();
347   }
348
349   @Nullable
350   public static PsiClass getClassAt(@Nullable SourcePosition position) {
351     if (position == null) {
352       return null;
353     }
354     final PsiElement element = position.getElementAt();
355     return (element != null) ? PsiTreeUtil.getParentOfType(element, PsiClass.class, false) : null;
356   }
357
358   @Nullable
359   public static String getSourcePositionClassDisplayName(DebugProcessImpl debugProcess, @Nullable SourcePosition position) {
360     if (position == null) {
361       return null;
362     }
363     final PsiFile positionFile = position.getFile();
364     if (positionFile instanceof JspFile) {
365       return positionFile.getName();
366     }
367
368     final PsiClass psiClass = getClassAt(position);
369
370     if(psiClass != null) {
371       final String qName = psiClass.getQualifiedName();
372       if(qName != null) {
373         return qName;
374       }
375     }
376
377     if(debugProcess != null && debugProcess.isAttached()) {
378       List<ReferenceType> allClasses = debugProcess.getPositionManager().getAllClasses(position);
379       if(!allClasses.isEmpty()) {
380         return allClasses.get(0).name();
381       }
382     }
383     if (psiClass == null) {
384       if (positionFile instanceof PsiClassOwner) {
385         return positionFile.getName();
386       }
387
388       return DebuggerBundle.message("string.file.line.position", positionFile.getName(), position.getLine());
389     }
390     return calcClassDisplayName(psiClass);
391   }
392
393   static String calcClassDisplayName(final PsiClass aClass) {
394     final String qName = aClass.getQualifiedName();
395     if (qName != null)  {
396       return qName;
397     }
398     final PsiClass parent = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
399     if (parent == null) {
400       return null;
401     }
402     
403     final String name = aClass.getName();
404     if (name != null) {
405       return calcClassDisplayName(parent) + "$" + name;
406     }
407     
408     final Ref<Integer> classIndex = new Ref<Integer>(0);
409     try {
410         parent.accept(new JavaRecursiveElementVisitor() {
411           public void visitAnonymousClass(PsiAnonymousClass cls) {
412             classIndex.set(classIndex.get() + 1);
413             if (aClass.equals(cls)) {
414               throw new ProcessCanceledException();
415             }
416           }
417         });
418       }
419       catch (ProcessCanceledException ignored) {
420       }
421     return calcClassDisplayName(parent) + "$" + classIndex.get();
422   }
423
424   @Nullable
425   public static String getSourcePositionPackageDisplayName(DebugProcessImpl debugProcess, @Nullable SourcePosition position) {
426     if (position == null) {
427       return null;
428     }
429     final PsiFile positionFile = position.getFile();
430     if (positionFile instanceof JspFile) {
431       final PsiDirectory dir = positionFile.getContainingDirectory();
432       return dir != null? dir.getVirtualFile().getPresentableUrl() : null;
433     }
434
435     final PsiClass psiClass = getClassAt(position);
436
437     if(psiClass != null) {
438       PsiClass toplevel = PsiUtil.getTopLevelClass(psiClass);
439       if(toplevel != null) {
440         String qName = toplevel.getQualifiedName();
441         if (qName != null) {
442           int i = qName.lastIndexOf('.');
443           return i > 0 ? qName.substring(0, i) : "";
444         }
445       }
446     }
447
448     if (positionFile instanceof PsiClassOwner) {
449       String name = ((PsiClassOwner)positionFile).getPackageName();
450       if (!StringUtil.isEmpty(name)) {
451         return name;
452       }
453     }
454
455     if(debugProcess != null && debugProcess.isAttached()) {
456       List<ReferenceType> allClasses = debugProcess.getPositionManager().getAllClasses(position);
457       if(!allClasses.isEmpty()) {
458         final String className = allClasses.get(0).name();
459         int dotIndex = className.lastIndexOf('.');
460         if (dotIndex >= 0) {
461           return className.substring(0, dotIndex);
462         }
463       }
464     }
465     return "";
466   }
467
468   public static PsiClass getTopLevelParentClass(PsiClass psiClass) {
469     PsiClass enclosing = PsiTreeUtil.getParentOfType(psiClass, PsiClass.class, true);
470     while (enclosing != null) {
471       psiClass = enclosing;
472       enclosing = PsiTreeUtil.getParentOfType(enclosing, PsiClass.class, true); 
473     }
474     return psiClass;
475   }
476
477   @Nullable
478   public static String getClassVMName(PsiClass containingClass) {
479     if (containingClass instanceof PsiAnonymousClass) {
480       return getClassVMName(PsiTreeUtil.getParentOfType(containingClass, PsiClass.class)) +
481              JavaAnonymousClassesHelper.getName((PsiAnonymousClass)containingClass);
482     }
483     return ClassUtil.getJVMClassName(containingClass);
484   }
485 }