Merge branch 'master' of git.labs.intellij.net:idea/community
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / PsiImplUtil.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
17 package org.jetbrains.plugins.groovy.lang.psi.impl;
18
19 import com.intellij.lang.ASTNode;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.module.Module;
22 import com.intellij.openapi.roots.OrderEntry;
23 import com.intellij.openapi.roots.ProjectFileIndex;
24 import com.intellij.openapi.roots.ProjectRootManager;
25 import com.intellij.openapi.vfs.VirtualFile;
26 import com.intellij.psi.*;
27 import com.intellij.psi.infos.CandidateInfo;
28 import com.intellij.psi.search.GlobalSearchScope;
29 import com.intellij.psi.util.MethodSignature;
30 import com.intellij.psi.util.MethodSignatureUtil;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34 import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
35 import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement;
36 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
37 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
41 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
44 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
46 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.arithmetic.*;
47 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.bitwise.GrAndExpressionImpl;
48 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.bitwise.GrExclusiveOrExpressionImpl;
49 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.bitwise.GrInclusiveOrExpressionImpl;
50 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.logical.GrLogicalAndExpressionImpl;
51 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.logical.GrLogicalOrExpressionImpl;
52 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.regex.GrRegexExpressionImpl;
53 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.relational.GrEqualityExpressionImpl;
54 import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
55 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
56
57 import java.util.Arrays;
58 import java.util.List;
59
60 /**
61  *
62  */
63 public class PsiImplUtil {
64   private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil");
65   private static final String MAIN_METHOD = "main";
66
67   private PsiImplUtil() {
68   }
69
70   public static GrExpression replaceExpression(GrExpression oldExpr, GrExpression newExpr, boolean removeUnnecessaryParentheses) {
71     ASTNode oldNode = oldExpr.getNode();
72     PsiElement oldParent = oldExpr.getParent();
73     if (oldParent == null) throw new PsiInvalidElementAccessException(oldExpr);
74
75     ASTNode parentNode = oldParent.getNode();
76
77     if (newExpr instanceof GrApplicationStatement && !(oldExpr instanceof GrApplicationStatement)) {
78       GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject());
79       newExpr = factory.createMethodCallByAppCall(((GrApplicationStatement)newExpr));
80     }
81
82     // Remove unnecessary parentheses
83     if (removeUnnecessaryParentheses && oldParent instanceof GrParenthesizedExpression) {
84       return ((GrExpression)oldParent).replaceWithExpression(newExpr, removeUnnecessaryParentheses);
85     }
86
87     // check priorities
88     GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject());
89     if (GrStringUtil.isReplacedExpressionInGStringInjection(oldExpr)) {
90       if (newExpr instanceof GrLiteral) {
91         return GrStringUtil.replaceExpressionInjectionByLiteral(oldExpr, ((GrLiteral)newExpr));
92       }
93       else if (!(newExpr instanceof GrReferenceExpression)){
94         newExpr = factory.createExpressionFromText("{" + newExpr.getText() + "}");
95       }
96     }
97     else if (oldParent instanceof GrExpression && !(oldParent instanceof GrParenthesizedExpression)) {
98       GrExpression parentExpr = (GrExpression)oldParent;
99       int parentPriorityLevel = getExprPriorityLevel(parentExpr);
100       int newPriorityLevel = getExprPriorityLevel(newExpr);
101       if (parentPriorityLevel > newPriorityLevel) {
102         newExpr = factory.createParenthesizedExpr(newExpr);
103       }
104       else if (parentPriorityLevel == newPriorityLevel && parentPriorityLevel != 0) {
105         if (parentExpr instanceof GrBinaryExpression) {
106           GrBinaryExpression binaryExpression = (GrBinaryExpression)parentExpr;
107           if (isNotAssociative(binaryExpression) && oldExpr.equals(binaryExpression.getRightOperand())) {
108             newExpr = factory.createParenthesizedExpr(newExpr);
109           }
110         }
111       }
112     }
113
114     ASTNode newNode = newExpr.copy().getNode();
115     assert newNode != null && parentNode != null;
116     parentNode.replaceChild(oldNode, newNode);
117
118     return ((GrExpression)newNode.getPsi());
119   }
120
121   private static boolean isNotAssociative(GrBinaryExpression binaryExpression) {
122     if (binaryExpression instanceof GrMultiplicativeExpressionImpl) {
123       return binaryExpression.getOperationTokenType() != mSTAR;
124     }
125     if (binaryExpression instanceof GrAdditiveExpressionImpl) {
126       return binaryExpression.getOperationTokenType() == mMINUS;
127     }
128     return binaryExpression instanceof GrEqualityExpressionImpl
129         || binaryExpression instanceof GrRegexExpressionImpl
130         || binaryExpression instanceof GrShiftExpressionImpl
131         || binaryExpression instanceof GrPowerExpressionImpl;
132   }
133
134   @Nullable
135   public static GrExpression getRuntimeQualifier(GrReferenceExpression refExpr) {
136     GrExpression qualifier = refExpr.getQualifierExpression();
137     if (qualifier == null) {
138       GrClosableBlock closure = PsiTreeUtil.getParentOfType(refExpr, GrClosableBlock.class);
139       while (closure != null) {
140         GrExpression funExpr = null;
141         PsiElement parent = closure.getParent();
142         if (parent instanceof GrApplicationStatement) {
143           funExpr = ((GrApplicationStatement) parent).getFunExpression();
144         } else if (parent instanceof GrMethodCallExpression) {
145           funExpr = ((GrMethodCallExpression) parent).getInvokedExpression();
146         }
147         if (funExpr instanceof GrReferenceExpression) {
148           qualifier = ((GrReferenceExpression) funExpr).getQualifierExpression();
149           if (qualifier != null) break;
150         } else break;
151
152         closure = PsiTreeUtil.getParentOfType(closure, GrClosableBlock.class);
153       }
154     }
155
156     return qualifier;
157   }
158
159   public static void removeVariable(GrVariable variable) {
160     final GrVariableDeclaration varDecl = (GrVariableDeclaration) variable.getParent();
161     final List<GrVariable> variables = Arrays.asList(varDecl.getVariables());
162     if (!variables.contains(variable)) {
163       throw new IllegalArgumentException();
164     }
165
166     final ASTNode varDeclNode = varDecl.getNode();
167     final PsiElement parent = varDecl.getParent();
168     final ASTNode owner = parent.getNode();
169     if (variables.size() == 1 && owner != null) {
170       PsiElement next = varDecl.getNextSibling();
171
172       // remove redundant semicolons
173       //noinspection ConstantConditions
174       while (next != null && next.getNode() != null && next.getNode().getElementType() == mSEMI) {
175         PsiElement tmpNext = next.getNextSibling();
176         //noinspection ConstantConditions
177         owner.removeChild(next.getNode());
178         next = tmpNext;
179       }
180
181       removeNewLineAfter(varDecl);
182       owner.removeChild(varDeclNode);
183       PsiUtil.reformatCode(parent);
184       return;
185     }
186     final ASTNode varNode = variable.getNode();
187     if (varNode != null) {
188       varDeclNode.removeChild(varNode);
189     }
190     PsiUtil.reformatCode(varDecl);
191   }
192
193   @Nullable
194   public static PsiElement realPrevious(PsiElement previousLeaf) {
195     while (previousLeaf != null &&
196         (previousLeaf instanceof PsiWhiteSpace ||
197             previousLeaf instanceof PsiComment ||
198             previousLeaf instanceof PsiErrorElement)) {
199       previousLeaf = previousLeaf.getPrevSibling();
200     }
201     return previousLeaf;
202   }
203
204   private static int getExprPriorityLevel(GrExpression expr) {
205     int priority = 0;
206     //if (expr instanceof GrNewExpression) priority = 1;
207     if (expr instanceof GrPostfixExpression) priority = 5;
208     if (expr instanceof GrUnaryExpression ||
209         expr instanceof GrTypeCastExpression) priority = 6;
210     if (expr instanceof GrPowerExpressionImpl) priority = 7;
211     if (expr instanceof GrMultiplicativeExpressionImpl) priority = 8;
212     if (expr instanceof GrAdditiveExpressionImpl) priority = 9;
213     if (expr instanceof GrShiftExpressionImpl) priority = 10;
214     if (expr instanceof GrRangeExpressionImpl) priority = 11;
215     if (expr instanceof GrRelationalExpression) priority = 12;
216     if (expr instanceof GrEqualityExpressionImpl) priority = 13;
217     if (expr instanceof GrRegexExpressionImpl) priority = 14;
218     if (expr instanceof GrAndExpressionImpl) priority = 15;
219     if (expr instanceof GrExclusiveOrExpressionImpl) priority = 16;
220     if (expr instanceof GrInclusiveOrExpressionImpl) priority = 17;
221     if (expr instanceof GrLogicalAndExpressionImpl) priority = 18;
222     if (expr instanceof GrLogicalOrExpressionImpl) priority = 19;
223     if (expr instanceof GrConditionalExpression) priority = 20;
224     if (expr instanceof GrSafeCastExpression) priority = 21;
225     if (expr instanceof GrAssignmentExpression) priority = 22;
226     if (expr instanceof GrApplicationStatement) priority = 23;
227     return -priority;
228   }
229
230   public static void setName(String name, PsiElement nameElement) {
231     ASTNode node = nameElement.getNode();
232     ASTNode newNameNode = GroovyPsiElementFactory.getInstance(nameElement.getProject()).createReferenceNameFromText(name).getNode();
233     assert newNameNode != null && node != null;
234     node.getTreeParent().replaceChild(node, newNameNode);
235   }
236
237   public static boolean isExtendsSignature(MethodSignature superSignatureCandidate, MethodSignature subSignature) {
238     /*final String name1 = superSignatureCandidate.getName();
239     final String name2 = subSignature.getName();
240     if (!name1.equals(name2)) return false;
241
242     final PsiType[] superTypes = superSignatureCandidate.getParameterTypes();
243     final PsiType[] subTypes = subSignature.getParameterTypes();
244     if (subTypes.length != superTypes.length) return false;
245     for (int i = 0; i < subTypes.length - 1; i++) {
246       PsiType superType = TypeConversionUtil.erasure(superTypes[i]);
247       PsiType subType = subTypes[i];
248       if (!superType.isAssignableFrom(subType)) return false;
249     }
250
251     if (superTypes.length > 0) {
252       final PsiType lastSuperType = TypeConversionUtil.erasure(superTypes[superTypes.length - 1]);
253       final PsiType lastSubType = subTypes[superTypes.length - 1];
254       if (lastSuperType instanceof PsiArrayType && !(lastSubType instanceof PsiArrayType)) {
255         final PsiType componentType = ((PsiArrayType) lastSuperType).getComponentType();
256         if (!lastSubType.isConvertibleFrom(componentType)) return false;
257       } else {
258         if (!lastSuperType.isAssignableFrom(lastSubType)) return false;
259       }
260     }
261
262     return true;*/
263     return MethodSignatureUtil.isSubsignature(superSignatureCandidate, subSignature);
264   }
265
266   @Nullable
267   public static PsiElement getOriginalElement(PsiClass clazz, PsiFile containingFile) {
268     VirtualFile vFile = containingFile.getVirtualFile();
269     final JavaPsiFacade facade = JavaPsiFacade.getInstance(clazz.getProject());
270     final ProjectFileIndex idx = ProjectRootManager.getInstance(facade.getProject()).getFileIndex();
271
272     if (vFile == null || !idx.isInLibrarySource(vFile)) return clazz;
273     final String qName = clazz.getQualifiedName();
274     if (qName == null) return null;
275     final List<OrderEntry> orderEntries = idx.getOrderEntriesForFile(vFile);
276     PsiClass original = facade.findClass(qName, new GlobalSearchScope(facade.getProject()) {
277       public int compare(VirtualFile file1, VirtualFile file2) {
278         return 0;
279       }
280
281       public boolean contains(VirtualFile file) {
282         // order for file and vFile has non empty intersection.
283         List<OrderEntry> entries = idx.getOrderEntriesForFile(file);
284         //noinspection ForLoopReplaceableByForEach
285         for (int i = 0; i < entries.size(); i++) {
286           final OrderEntry entry = entries.get(i);
287           if (orderEntries.contains(entry)) return true;
288         }
289         return false;
290       }
291
292       public boolean isSearchInModuleContent(@NotNull Module aModule) {
293         return false;
294       }
295
296       public boolean isSearchInLibraries() {
297         return true;
298       }
299     });
300
301     return original != null ? original : clazz;
302   }
303
304   @Nullable
305   public static PsiMethod extractUniqueElement(@NotNull GroovyResolveResult[] results) {
306     if (results.length != 1) return null;
307     final PsiElement element = results[0].getElement();
308     return element instanceof PsiMethod ? (PsiMethod) element : null;
309   }
310
311   public static GroovyResolveResult extractUniqueResult(@NotNull GroovyResolveResult[] results) {
312     if (results.length != 1) return GroovyResolveResult.EMPTY_RESULT;
313     return results[0];
314   }
315
316   public static PsiMethod[] mapToMethods(@Nullable List<CandidateInfo> list) {
317     if (list == null) return PsiMethod.EMPTY_ARRAY;
318     PsiMethod[] result = new PsiMethod[list.size()];
319     for (int i = 0; i < list.size(); i++) {
320       result[i] = (PsiMethod) list.get(i).getElement();
321
322     }
323     return result;
324   }
325
326   public static String getName(GrNamedElement namedElement) {
327     PsiElement nameElement = namedElement.getNameIdentifierGroovy();
328     ASTNode node = nameElement.getNode();
329     LOG.assertTrue(node != null);
330     if (node.getElementType() == mIDENT) return nameElement.getText();
331     else {
332       if (node.getElementType() == mSTRING_LITERAL) {
333         String text = nameElement.getText();
334         return text.endsWith("'") ? text.substring(1, text.length() - 1) : text.substring(1);
335       } else {
336         LOG.assertTrue(node.getElementType() == mGSTRING_LITERAL);
337         String text = nameElement.getText();
338         return text.endsWith("\"") ? text.substring(1, text.length() - 1) : text.substring(1);
339       }
340     }
341   }
342
343   public static void removeNewLineAfter(@NotNull GrStatement statement) {
344     ASTNode parentNode = statement.getParent().getNode();
345     ASTNode next = statement.getNode().getTreeNext();
346     if (parentNode != null && next != null && mNLS == next.getElementType()) {
347       parentNode.removeChild(next);
348     }
349   }
350
351   public static boolean isMainMethod(GrMethod method) {
352     return method.getName().equals(MAIN_METHOD) &&
353         method.hasModifierProperty(PsiModifier.STATIC);
354   }
355 }