IDEA-24452: Autocomplete doesn't work in with context and GString expression
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / PsiImplUtil.java
blob7cf685c2c7e14cf5b61d135c8daff2fd4be1563d
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.jetbrains.plugins.groovy.lang.psi.impl;
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 org.jetbrains.plugins.groovy.lang.psi.GrNamedElement;
35 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
36 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
37 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
41 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
44 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.arithmetic.*;
45 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.bitwise.GrAndExpressionImpl;
46 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.bitwise.GrExclusiveOrExpressionImpl;
47 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.bitwise.GrInclusiveOrExpressionImpl;
48 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.logical.GrLogicalAndExpressionImpl;
49 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.logical.GrLogicalOrExpressionImpl;
50 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.regex.GrRegexExpressionImpl;
51 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.relational.GrEqualityExpressionImpl;
52 import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
53 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
55 import java.util.Arrays;
56 import java.util.List;
58 import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
60 /**
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";
67 private PsiImplUtil() {
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);
75 ASTNode parentNode = oldParent.getNode();
77 if (newExpr instanceof GrApplicationStatement && !(oldExpr instanceof GrApplicationStatement)) {
78 GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject());
79 newExpr = factory.createMethodCallByAppCall(((GrApplicationStatement)newExpr));
82 // Remove unnecessary parentheses
83 if (removeUnnecessaryParentheses && oldParent instanceof GrParenthesizedExpression) {
84 return ((GrExpression)oldParent).replaceWithExpression(newExpr, removeUnnecessaryParentheses);
87 // check priorities
88 GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject());
89 if (GrStringUtil.isReplacedExpressionInGStringInjection(oldExpr)) {
90 /*if (newExpr instanceof GrLiteral) { todo Max Medvedev
91 return GrStringUtil.replaceStringInjectionByLiteral(oldExpr, ((GrLiteral)newExpr));
93 else */if (!(newExpr instanceof GrReferenceExpression)){
94 newExpr = factory.createExpressionFromText("{" + newExpr.getText() + "}");
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);
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);
114 ASTNode newNode = newExpr.copy().getNode();
115 assert newNode != null && parentNode != null;
116 parentNode.replaceChild(oldNode, newNode);
118 return ((GrExpression)newNode.getPsi());
121 private static boolean isNotAssociative(GrBinaryExpression binaryExpression) {
122 if (binaryExpression instanceof GrMultiplicativeExpressionImpl) {
123 return binaryExpression.getOperationTokenType() != mSTAR;
125 if (binaryExpression instanceof GrAdditiveExpressionImpl) {
126 return binaryExpression.getOperationTokenType() == mMINUS;
128 return binaryExpression instanceof GrEqualityExpressionImpl
129 || binaryExpression instanceof GrRegexExpressionImpl
130 || binaryExpression instanceof GrShiftExpressionImpl
131 || binaryExpression instanceof GrPowerExpressionImpl;
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();
148 if (funExpr instanceof GrReferenceExpression) {
149 qualifier = ((GrReferenceExpression) funExpr).getQualifierExpression();
150 if (qualifier != null) break;
153 closure = PsiTreeUtil.getParentOfType(closure, GrClosableBlock.class);
157 return qualifier;
160 public static void removeVariable(GrVariable variable) {
161 final GrVariableDeclaration varDecl = (GrVariableDeclaration) variable.getParent();
162 final List<GrVariable> variables = Arrays.asList(varDecl.getVariables());
163 if (!variables.contains(variable)) {
164 throw new IllegalArgumentException();
167 final ASTNode varDeclNode = varDecl.getNode();
168 final PsiElement parent = varDecl.getParent();
169 final ASTNode owner = parent.getNode();
170 if (variables.size() == 1 && owner != null) {
171 PsiElement next = varDecl.getNextSibling();
173 // remove redundant semicolons
174 //noinspection ConstantConditions
175 while (next != null && next.getNode() != null && next.getNode().getElementType() == mSEMI) {
176 PsiElement tmpNext = next.getNextSibling();
177 //noinspection ConstantConditions
178 owner.removeChild(next.getNode());
179 next = tmpNext;
182 removeNewLineAfter(varDecl);
183 owner.removeChild(varDeclNode);
184 PsiUtil.reformatCode(parent);
185 return;
187 final ASTNode varNode = variable.getNode();
188 if (varNode != null) {
189 varDeclNode.removeChild(varNode);
191 PsiUtil.reformatCode(varDecl);
194 @Nullable
195 public static PsiElement realPrevious(PsiElement previousLeaf) {
196 while (previousLeaf != null &&
197 (previousLeaf instanceof PsiWhiteSpace ||
198 previousLeaf instanceof PsiComment ||
199 previousLeaf instanceof PsiErrorElement)) {
200 previousLeaf = previousLeaf.getPrevSibling();
202 return previousLeaf;
205 private static int getExprPriorityLevel(GrExpression expr) {
206 int priority = 0;
207 //if (expr instanceof GrNewExpression) priority = 1;
208 if (expr instanceof GrPostfixExpression) priority = 5;
209 if (expr instanceof GrUnaryExpression ||
210 expr instanceof GrTypeCastExpression) priority = 6;
211 if (expr instanceof GrPowerExpressionImpl) priority = 7;
212 if (expr instanceof GrMultiplicativeExpressionImpl) priority = 8;
213 if (expr instanceof GrAdditiveExpressionImpl) priority = 9;
214 if (expr instanceof GrShiftExpressionImpl) priority = 10;
215 if (expr instanceof GrRangeExpressionImpl) priority = 11;
216 if (expr instanceof GrRelationalExpression) priority = 12;
217 if (expr instanceof GrEqualityExpressionImpl) priority = 13;
218 if (expr instanceof GrRegexExpressionImpl) priority = 14;
219 if (expr instanceof GrAndExpressionImpl) priority = 15;
220 if (expr instanceof GrExclusiveOrExpressionImpl) priority = 16;
221 if (expr instanceof GrInclusiveOrExpressionImpl) priority = 17;
222 if (expr instanceof GrLogicalAndExpressionImpl) priority = 18;
223 if (expr instanceof GrLogicalOrExpressionImpl) priority = 19;
224 if (expr instanceof GrConditionalExpression) priority = 20;
225 if (expr instanceof GrSafeCastExpression) priority = 21;
226 if (expr instanceof GrAssignmentExpression) priority = 22;
227 if (expr instanceof GrApplicationStatement) priority = 23;
228 return -priority;
231 public static void setName(String name, PsiElement nameElement) {
232 ASTNode node = nameElement.getNode();
233 ASTNode newNameNode = GroovyPsiElementFactory.getInstance(nameElement.getProject()).createReferenceNameFromText(name).getNode();
234 assert newNameNode != null && node != null;
235 node.getTreeParent().replaceChild(node, newNameNode);
238 public static boolean isExtendsSignature(MethodSignature superSignatureCandidate, MethodSignature subSignature) {
239 /*final String name1 = superSignatureCandidate.getName();
240 final String name2 = subSignature.getName();
241 if (!name1.equals(name2)) return false;
243 final PsiType[] superTypes = superSignatureCandidate.getParameterTypes();
244 final PsiType[] subTypes = subSignature.getParameterTypes();
245 if (subTypes.length != superTypes.length) return false;
246 for (int i = 0; i < subTypes.length - 1; i++) {
247 PsiType superType = TypeConversionUtil.erasure(superTypes[i]);
248 PsiType subType = subTypes[i];
249 if (!superType.isAssignableFrom(subType)) return false;
252 if (superTypes.length > 0) {
253 final PsiType lastSuperType = TypeConversionUtil.erasure(superTypes[superTypes.length - 1]);
254 final PsiType lastSubType = subTypes[superTypes.length - 1];
255 if (lastSuperType instanceof PsiArrayType && !(lastSubType instanceof PsiArrayType)) {
256 final PsiType componentType = ((PsiArrayType) lastSuperType).getComponentType();
257 if (!lastSubType.isConvertibleFrom(componentType)) return false;
258 } else {
259 if (!lastSuperType.isAssignableFrom(lastSubType)) return false;
263 return true;*/
264 return MethodSignatureUtil.isSubsignature(superSignatureCandidate, subSignature);
267 @Nullable
268 public static PsiElement getOriginalElement(PsiClass clazz, PsiFile containingFile) {
269 VirtualFile vFile = containingFile.getVirtualFile();
270 final JavaPsiFacade facade = JavaPsiFacade.getInstance(clazz.getProject());
271 final ProjectFileIndex idx = ProjectRootManager.getInstance(facade.getProject()).getFileIndex();
273 if (vFile == null || !idx.isInLibrarySource(vFile)) return clazz;
274 final String qName = clazz.getQualifiedName();
275 if (qName == null) return null;
276 final List<OrderEntry> orderEntries = idx.getOrderEntriesForFile(vFile);
277 PsiClass original = facade.findClass(qName, new GlobalSearchScope(facade.getProject()) {
278 public int compare(VirtualFile file1, VirtualFile file2) {
279 return 0;
282 public boolean contains(VirtualFile file) {
283 // order for file and vFile has non empty intersection.
284 List<OrderEntry> entries = idx.getOrderEntriesForFile(file);
285 //noinspection ForLoopReplaceableByForEach
286 for (int i = 0; i < entries.size(); i++) {
287 final OrderEntry entry = entries.get(i);
288 if (orderEntries.contains(entry)) return true;
290 return false;
293 public boolean isSearchInModuleContent(@NotNull Module aModule) {
294 return false;
297 public boolean isSearchInLibraries() {
298 return true;
302 return original != null ? original : clazz;
305 @Nullable
306 public static PsiMethod extractUniqueElement(@NotNull GroovyResolveResult[] results) {
307 if (results.length != 1) return null;
308 final PsiElement element = results[0].getElement();
309 return element instanceof PsiMethod ? (PsiMethod) element : null;
312 public static GroovyResolveResult extractUniqueResult(@NotNull GroovyResolveResult[] results) {
313 if (results.length != 1) return GroovyResolveResult.EMPTY_RESULT;
314 return results[0];
317 public static PsiMethod[] mapToMethods(@Nullable List<CandidateInfo> list) {
318 if (list == null) return PsiMethod.EMPTY_ARRAY;
319 PsiMethod[] result = new PsiMethod[list.size()];
320 for (int i = 0; i < list.size(); i++) {
321 result[i] = (PsiMethod) list.get(i).getElement();
324 return result;
327 public static String getName(GrNamedElement namedElement) {
328 PsiElement nameElement = namedElement.getNameIdentifierGroovy();
329 ASTNode node = nameElement.getNode();
330 LOG.assertTrue(node != null);
331 if (node.getElementType() == mIDENT) return nameElement.getText();
332 else {
333 if (node.getElementType() == mSTRING_LITERAL) {
334 String text = nameElement.getText();
335 return text.endsWith("'") ? text.substring(1, text.length() - 1) : text.substring(1);
336 } else {
337 LOG.assertTrue(node.getElementType() == mGSTRING_LITERAL);
338 String text = nameElement.getText();
339 return text.endsWith("\"") ? text.substring(1, text.length() - 1) : text.substring(1);
344 public static void removeNewLineAfter(@NotNull GrStatement statement) {
345 ASTNode parentNode = statement.getParent().getNode();
346 ASTNode next = statement.getNode().getTreeNext();
347 if (parentNode != null && next != null && mNLS == next.getElementType()) {
348 parentNode.removeChild(next);
352 public static boolean isMainMethod(GrMethod method) {
353 return method.getName().equals(MAIN_METHOD) &&
354 method.hasModifierProperty(PsiModifier.STATIC);