3695d0c462b0fd6c36243230011898a3fb2e57c8
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / ui / impl / watch / DebuggerTreeNodeExpression.java
1 /*
2  * Copyright 2000-2009 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.ui.impl.watch;
17
18 import com.intellij.codeInsight.ChangeContextUtil;
19 import com.intellij.debugger.DebuggerBundle;
20 import com.intellij.debugger.codeinsight.RuntimeTypeEvaluator;
21 import com.intellij.debugger.engine.evaluation.EvaluateException;
22 import com.intellij.debugger.engine.evaluation.TextWithImports;
23 import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
24 import com.intellij.debugger.impl.DebuggerContextImpl;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.Computable;
28 import com.intellij.pom.java.LanguageLevel;
29 import com.intellij.psi.*;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import com.intellij.psi.util.PsiUtil;
33 import com.intellij.util.IncorrectOperationException;
34 import com.sun.jdi.ObjectReference;
35 import com.sun.jdi.ReferenceType;
36 import com.sun.jdi.Value;
37
38 /**
39  * User: lex
40  * Date: Oct 29, 2003
41  * Time: 9:24:52 PM
42  */
43 public class DebuggerTreeNodeExpression {
44   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeExpression");
45
46 //  private static PsiExpression beautifyExpression(PsiExpression expression) throws IncorrectOperationException {
47 //    final PsiElementFactory elementFactory = expression.getManager().getElementFactory();
48 //    final PsiParenthesizedExpression utility = (PsiParenthesizedExpression)elementFactory.createExpressionFromText(
49 //      "(expr)", expression.getContext());
50 //    utility.getExpression().replace(expression);
51 //
52 //    PsiRecursiveElementVisitor visitor = new PsiRecursiveElementVisitor() {
53 //      @Override public void visitTypeCastExpression(PsiTypeCastExpression expression) {
54 //        try {
55 //          super.visitTypeCastExpression(expression);
56 //
57 //          PsiElement parent;
58 //          PsiElement toBeReplaced = expression;
59 //          for (parent = expression.getParent();
60 //               parent instanceof PsiParenthesizedExpression && parent != utility;
61 //               parent = parent.getParent()) {
62 //            toBeReplaced = parent;
63 //          }
64 //
65 //          if (parent instanceof PsiReferenceExpression) {
66 //            PsiReferenceExpression reference = ((PsiReferenceExpression)parent);
67 //            //((TypeCast)).member
68 //            PsiElement oldResolved = reference.resolve();
69 //
70 //            if (oldResolved != null) {
71 //              PsiReferenceExpression newReference = ((PsiReferenceExpression)reference.copy());
72 //              newReference.getQualifierExpression().replace(expression.getOperand());
73 //              PsiElement newResolved = newReference.resolve();
74 //
75 //              if (oldResolved == newResolved) {
76 //                toBeReplaced.replace(expression.getOperand());
77 //              }
78 //              else if (newResolved instanceof PsiMethod && oldResolved instanceof PsiMethod) {
79 //                if (isSuperMethod((PsiMethod)newResolved, (PsiMethod)oldResolved)) {
80 //                  toBeReplaced.replace(expression.getOperand());
81 //                }
82 //              }
83 //            }
84 //          }
85 //          else {
86 //            toBeReplaced.replace(expression.getOperand());
87 //          }
88 //        }
89 //        catch (IncorrectOperationException e) {
90 //          throw new IncorrectOperationRuntimeException(e);
91 //        }
92 //      }
93 //
94 //      @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
95 //        expression.acceptChildren(this);
96 //
97 //        try {
98 //          JavaResolveResult resolveResult = expression.advancedResolve(false);
99 //
100 //          PsiElement oldResolved = resolveResult.getElement();
101 //
102 //          if(oldResolved == null) return;
103 //
104 //          PsiReferenceExpression newReference;
105 //          if (expression instanceof PsiMethodCallExpression) {
106 //            int length = expression.getQualifierExpression().getTextRange().getLength();
107 //            PsiMethodCallExpression methodCall = (PsiMethodCallExpression)elementFactory.createExpressionFromText(
108 //              expression.getText().substring(length), expression.getContext());
109 //            newReference = methodCall.getMethodExpression();
110 //          }
111 //          else {
112 //            newReference =
113 //            (PsiReferenceExpression)elementFactory.createExpressionFromText(expression.getReferenceName(),
114 //                                                                            expression.getContext());
115 //          }
116 //
117 //          PsiElement newResolved = newReference.resolve();
118 //          if (oldResolved == newResolved) {
119 //            expression.replace(newReference);
120 //          }
121 //        }
122 //        catch (IncorrectOperationException e) {
123 //          LOG.debug(e);
124 //        }
125 //      }
126 //    };
127 //
128 //    try {
129 //      utility.accept(visitor);
130 //    }
131 //    catch (IncorrectOperationRuntimeException e) {
132 //      throw e.getException();
133 //    }
134 //    return utility.getExpression();
135 //  }
136
137   private static boolean isSuperMethod(PsiMethod superMethod, PsiMethod overridingMethod) {
138     PsiMethod[] superMethods = overridingMethod.findSuperMethods();
139       for (int i = 0; i < superMethods.length; i++) {
140         if (superMethods[i] == superMethod) {
141           return true;
142         }
143         else if (isSuperMethod(superMethod, superMethods[i])) {
144           return true;
145         }
146       }
147       return false;
148     }
149
150   public static PsiExpression substituteThis(PsiExpression expressionWithThis, PsiExpression howToEvaluateThis, Value howToEvaluateThisValue)
151     throws EvaluateException {
152     PsiExpression result = (PsiExpression)expressionWithThis.copy();
153
154     PsiClass thisClass = PsiTreeUtil.getContextOfType(result, PsiClass.class, true);
155
156     boolean castNeeded = true;
157
158     if (thisClass != null) {
159       PsiType type = howToEvaluateThis.getType();
160       if(type != null) {
161         if(type instanceof PsiClassType) {
162           PsiClass psiClass = ((PsiClassType) type).resolve();
163           if(psiClass != null && (psiClass == thisClass || psiClass.isInheritor(thisClass, true))) {
164             castNeeded = false;
165           }
166         }
167         else if(type instanceof PsiArrayType) {
168           LanguageLevel languageLevel = PsiUtil.getLanguageLevel(expressionWithThis);
169           if(thisClass == JavaPsiFacade.getInstance(expressionWithThis.getProject()).getElementFactory().getArrayClass(languageLevel)) {
170             castNeeded = false;
171           }
172         }
173       }
174     }
175
176     if (castNeeded) {
177       howToEvaluateThis = castToRuntimeType(howToEvaluateThis, howToEvaluateThisValue, howToEvaluateThis.getContext());
178     }
179
180     ChangeContextUtil.encodeContextInfo(result, false);
181     PsiExpression psiExpression;
182     try {
183       psiExpression = (PsiExpression) ChangeContextUtil.decodeContextInfo(result, thisClass, howToEvaluateThis);
184     }
185     catch (IncorrectOperationException e) {
186       throw new EvaluateException(
187         DebuggerBundle.message("evaluation.error.invalid.this.expression", result.getText(), howToEvaluateThis.getText()), null);
188     }
189
190     try {
191       return JavaPsiFacade.getInstance(howToEvaluateThis.getProject()).getElementFactory()
192         .createExpressionFromText(psiExpression.getText(), howToEvaluateThis.getContext());
193     }
194     catch (IncorrectOperationException e) {
195       throw new EvaluateException(e.getMessage(), e);
196     }
197   }
198
199   public static PsiExpression castToRuntimeType(PsiExpression expression, Value value, PsiElement contextElement) throws EvaluateException {
200     if (!(value instanceof ObjectReference)) {
201       return expression;
202     }
203     
204     ReferenceType valueType = ((ObjectReference)value).referenceType();
205     if (valueType == null) {
206       return expression;
207     }
208     
209     Project project = expression.getProject();
210
211     PsiClass type = RuntimeTypeEvaluator.getCastableRuntimeType(project, value);
212     if (type == null) {
213       return expression;
214     }
215
216     PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
217     try {
218       PsiParenthesizedExpression parenthExpression = (PsiParenthesizedExpression)elementFactory.createExpressionFromText(
219         "((" + type.getQualifiedName() + ")expression)", null);
220       ((PsiTypeCastExpression)parenthExpression.getExpression()).getOperand().replace(expression);
221       return parenthExpression;
222     }
223     catch (IncorrectOperationException e) {
224       throw new EvaluateException(DebuggerBundle.message("error.invalid.type.name", type.getQualifiedName()), e);
225     }
226   }
227
228   /**
229    * @param qualifiedName the class qualified name to be resolved against the current execution context
230    * @return short name if the class could be resolved using short name,
231    * otherwise returns qualifiedName
232    */
233   public static String normalize(final String qualifiedName, PsiElement contextElement, Project project) {
234     if (contextElement == null) {
235       return qualifiedName;
236     }
237
238     final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
239     PsiClass aClass = facade.findClass(qualifiedName, GlobalSearchScope.allScope(project));
240     if (aClass != null) {
241       return normalizePsiClass(aClass, contextElement, facade.getResolveHelper());
242     }
243     return qualifiedName;
244   }
245
246   private static String normalizePsiClass(PsiClass psiClass, PsiElement contextElement, PsiResolveHelper helper) {
247     String name = psiClass.getName();
248     PsiClass aClass = helper.resolveReferencedClass(name, contextElement);
249     if (psiClass.equals(aClass)) {
250       return name;
251     }
252     PsiClass parentClass = psiClass.getContainingClass();
253     if (parentClass != null) {
254       return normalizePsiClass(parentClass, contextElement, helper) + "." + name;
255     }
256     return psiClass.getQualifiedName();
257   }
258
259   public static PsiExpression getEvaluationExpression(DebuggerTreeNodeImpl node, DebuggerContextImpl context) throws EvaluateException {
260     if(node.getDescriptor() instanceof ValueDescriptorImpl) {
261       throw new IllegalStateException("Not supported any more");
262       //return ((ValueDescriptorImpl)node.getDescriptor()).getTreeEvaluation(node, context);
263     }
264     else {
265       LOG.error(node.getDescriptor() != null ? node.getDescriptor().getClass().getName() : "null");
266       return null;
267     }
268   }
269
270   public static TextWithImports createEvaluationText(final DebuggerTreeNodeImpl node, final DebuggerContextImpl context) throws EvaluateException {
271     final EvaluateException[] ex = new EvaluateException[] {null};
272     final TextWithImports textWithImports = PsiDocumentManager.getInstance(context.getProject()).commitAndRunReadAction(new Computable<TextWithImports>() {
273       public TextWithImports compute() {
274         try {
275           final PsiExpression expressionText = getEvaluationExpression(node, context);
276           if (expressionText != null) {
277             return new TextWithImportsImpl(expressionText);
278           }
279         }
280         catch (EvaluateException e) {
281           ex[0] = e;
282         }
283         return null;
284       }
285     });
286     if (ex[0] != null) {
287       throw ex[0];
288     }
289     return textWithImports;
290   }
291
292   private static class IncorrectOperationRuntimeException extends RuntimeException {
293     private final IncorrectOperationException myException;
294
295     public IncorrectOperationRuntimeException(IncorrectOperationException exception) {
296       myException = exception;
297     }
298
299     public IncorrectOperationException getException() { return myException; }
300   }
301 }