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
.*;
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
);
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
;
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);
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());
182 removeNewLineAfter(varDecl
);
183 owner
.removeChild(varDeclNode
);
184 PsiUtil
.reformatCode(parent
);
187 final ASTNode varNode
= variable
.getNode();
188 if (varNode
!= null) {
189 varDeclNode
.removeChild(varNode
);
191 PsiUtil
.reformatCode(varDecl
);
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();
205 private static int getExprPriorityLevel(GrExpression expr
) {
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;
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;
259 if (!lastSuperType.isAssignableFrom(lastSubType)) return false;
264 return MethodSignatureUtil
.isSubsignature(superSignatureCandidate
, subSignature
);
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
) {
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;
293 public boolean isSearchInModuleContent(@NotNull Module aModule
) {
297 public boolean isSearchInLibraries() {
302 return original
!= null ? original
: clazz
;
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
;
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();
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();
333 if (node
.getElementType() == mSTRING_LITERAL
) {
334 String text
= nameElement
.getText();
335 return text
.endsWith("'") ? text
.substring(1, text
.length() - 1) : text
.substring(1);
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
);