| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.jetbrains.plugins.groovy.lang.psi.impl; |
| |
| import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator; |
| import com.intellij.lang.ASTNode; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.OrderEntry; |
| import com.intellij.openapi.roots.ProjectFileIndex; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.tree.AstBufferUtil; |
| import com.intellij.psi.impl.source.tree.Factory; |
| import com.intellij.psi.infos.CandidateInfo; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.tree.TokenSet; |
| import com.intellij.psi.util.MethodSignature; |
| import com.intellij.psi.util.MethodSignatureUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.reference.SoftReference; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.ReflectionUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; |
| import org.jetbrains.plugins.groovy.lang.psi.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.util.GrNamedArgumentsOwner; |
| import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals.GrLiteralImpl; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrSyntheticCodeBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrSyntheticExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrSyntheticReferenceList; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrSyntheticTypeElement; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.StringPartInfo; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| public class PsiImplUtil { |
| private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil"); |
| private static final String MAIN_METHOD = "main"; |
| public static final Key<SoftReference<PsiCodeBlock>> PSI_CODE_BLOCK = Key.create("Psi_code_block"); |
| public static final Key<SoftReference<PsiTypeElement>> PSI_TYPE_ELEMENT = Key.create("psi.type.element"); |
| public static final Key<SoftReference<PsiExpression>> PSI_EXPRESSION = Key.create("psi.expression"); |
| private static final Key<SoftReference<PsiReferenceList>> PSI_REFERENCE_LIST = Key.create("psi.reference.list"); |
| |
| private PsiImplUtil() { |
| } |
| |
| /** |
| * @return leading regex or null if it does not exist |
| */ |
| @Nullable |
| private static GrLiteral getRegexAtTheBeginning(PsiElement expr) { |
| PsiElement fchild = expr; |
| while (fchild != null) { |
| if (fchild instanceof GrLiteral && GrStringUtil.isRegex((GrLiteral)fchild)) return (GrLiteral)fchild; |
| fchild = fchild.getFirstChild(); |
| } |
| return null; |
| } |
| |
| private static boolean isAfterIdentifier(PsiElement el) { |
| final PsiElement prev = PsiUtil.getPreviousNonWhitespaceToken(el); |
| return prev != null && prev.getNode().getElementType() == GroovyTokenTypes.mIDENT; |
| } |
| |
| @Nullable |
| public static GrExpression replaceExpression(GrExpression oldExpr, GrExpression newExpr, boolean removeUnnecessaryParentheses) { |
| PsiElement oldParent = oldExpr.getParent(); |
| if (oldParent == null) throw new PsiInvalidElementAccessException(oldExpr); |
| |
| if (!(oldExpr instanceof GrApplicationStatement)) { |
| newExpr = ApplicationStatementUtil.convertToMethodCallExpression(newExpr); |
| } |
| |
| // Remove unnecessary parentheses |
| if (removeUnnecessaryParentheses && |
| oldParent instanceof GrParenthesizedExpression && |
| !(oldParent.getParent() instanceof GrArgumentLabel)) { |
| return ((GrExpression)oldParent).replaceWithExpression(newExpr, removeUnnecessaryParentheses); |
| } |
| |
| //regexes cannot be after identifier , try to replace it with simple string |
| if (getRegexAtTheBeginning(newExpr) != null && isAfterIdentifier(oldExpr)) { |
| final PsiElement copy = newExpr.copy(); |
| final GrLiteral regex = getRegexAtTheBeginning(copy); |
| LOG.assertTrue(regex != null); |
| final GrLiteral stringLiteral = GrStringUtil.createStringFromRegex(regex); |
| if (regex == copy) { |
| return oldExpr.replaceWithExpression(stringLiteral, removeUnnecessaryParentheses); |
| } |
| else { |
| regex.replace(stringLiteral); |
| return oldExpr.replaceWithExpression((GrExpression)copy, removeUnnecessaryParentheses); |
| } |
| } |
| |
| |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject()); |
| if (oldParent instanceof GrStringInjection) { |
| if (newExpr instanceof GrString || newExpr instanceof GrLiteral && ((GrLiteral)newExpr).getValue() instanceof String) { |
| return GrStringUtil.replaceStringInjectionByLiteral((GrStringInjection)oldParent, (GrLiteral)newExpr); |
| } |
| else { |
| GrClosableBlock block = factory.createClosureFromText("{foo}"); |
| oldParent.getNode().replaceChild(oldExpr.getNode(), block.getNode()); |
| GrStatement[] statements = block.getStatements(); |
| return ((GrExpression)statements[0]).replaceWithExpression(newExpr, removeUnnecessaryParentheses); |
| } |
| } |
| |
| if (PsiTreeUtil.getParentOfType(oldExpr, GrStringInjection.class, false, GrCodeBlock.class) != null) { |
| final GrStringInjection stringInjection = PsiTreeUtil.getParentOfType(oldExpr, GrStringInjection.class); |
| GrStringUtil.wrapInjection(stringInjection); |
| assert stringInjection != null; |
| final PsiElement replaced = oldExpr.replaceWithExpression(newExpr, removeUnnecessaryParentheses); |
| return (GrExpression)replaced; |
| } |
| |
| //check priorities |
| if (oldParent instanceof GrExpression && !(oldParent instanceof GrParenthesizedExpression)) { |
| GrExpression addedParenth = addParenthesesIfNeeded(newExpr, oldExpr, (GrExpression)oldParent); |
| if (newExpr != addedParenth) { |
| return oldExpr.replaceWithExpression(addedParenth, removeUnnecessaryParentheses); |
| } |
| } |
| |
| //if replace closure argument with expression |
| //we should add the expression in arg list |
| if (oldExpr instanceof GrClosableBlock && |
| !(newExpr instanceof GrClosableBlock) && |
| oldParent instanceof GrMethodCallExpression && |
| ArrayUtil.contains(oldExpr, ((GrMethodCallExpression)oldParent).getClosureArguments())) { |
| return ((GrMethodCallExpression)oldParent).replaceClosureArgument((GrClosableBlock)oldExpr, newExpr); |
| |
| } |
| |
| newExpr = (GrExpression)oldExpr.replace(newExpr); |
| |
| |
| //if newExpr is the first grand child of command argument list we should replace command arg list with parenthesised arg list. |
| // In other case the code will be broken. So we try to find wrapping command arg list counting levels. After arg list replace we go inside it |
| // to find target parenthesised expression. |
| if (newExpr instanceof GrParenthesizedExpression && isFirstChild(newExpr)) { |
| int parentCount = 0; |
| |
| PsiElement element = oldParent; |
| while (element != null && !(element instanceof GrCommandArgumentList)) { |
| if (element instanceof GrCodeBlock || element instanceof GrParenthesizedExpression) break; |
| if (element instanceof PsiFile) break; |
| |
| |
| final PsiElement parent = element.getParent(); |
| if (parent == null) break; |
| if (!isFirstChild(element)) break; |
| |
| element = parent; |
| parentCount++; |
| } |
| |
| if (element instanceof GrCommandArgumentList) { |
| final GrCommandArgumentList commandArgList = (GrCommandArgumentList)element; |
| |
| final PsiElement parent = commandArgList.getParent(); |
| LOG.assertTrue(parent instanceof GrApplicationStatement); |
| |
| final GrMethodCall methodCall = factory.createMethodCallByAppCall((GrApplicationStatement)parent); |
| final GrMethodCall newCall = (GrMethodCall)parent.replace(methodCall); |
| |
| PsiElement result = newCall.getArgumentList().getAllArguments()[0]; |
| |
| for (int i = 0; i < parentCount; i++) { |
| result = PsiUtil.skipWhitespacesAndComments(result.getFirstChild(), true); |
| } |
| |
| LOG.assertTrue(result instanceof GrParenthesizedExpression); |
| return (GrExpression)result; |
| } |
| } |
| return newExpr; |
| } |
| |
| private static boolean isFirstChild(PsiElement element) { |
| return PsiUtil.skipWhitespacesAndComments(element.getParent().getFirstChild(), true) == element; |
| } |
| |
| /** |
| * @return replaced expression or null if expression is not replaced |
| */ |
| @Nullable |
| private static GrExpression addParenthesesIfNeeded(GrExpression newExpr, GrExpression oldExpr, GrExpression oldParent) { |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject()); |
| |
| int parentPriorityLevel = getExprPriorityLevel(oldParent); |
| int newPriorityLevel = getExprPriorityLevel(newExpr); |
| |
| if (parentPriorityLevel > newPriorityLevel) { |
| newExpr = factory.createParenthesizedExpr(newExpr); |
| } |
| else if (parentPriorityLevel == newPriorityLevel && parentPriorityLevel != 0) { |
| if (oldParent instanceof GrBinaryExpression) { |
| GrBinaryExpression binaryExpression = (GrBinaryExpression)oldParent; |
| if (isNotAssociative(binaryExpression) && oldExpr.equals(binaryExpression.getRightOperand())) { |
| newExpr = factory.createParenthesizedExpr(newExpr); |
| } |
| } |
| } |
| return newExpr; |
| } |
| |
| private static boolean isNotAssociative(GrBinaryExpression binaryExpression) { |
| final IElementType opToken = binaryExpression.getOperationTokenType(); |
| return !TokenSets.ASSOCIATIVE_BINARY_OP_SET.contains(opToken); |
| } |
| |
| @Nullable |
| public static GrExpression getRuntimeQualifier(@NotNull GrReferenceExpression refExpr) { |
| GrExpression qualifier = refExpr.getQualifierExpression(); |
| if (qualifier != null) return qualifier; |
| |
| for (GrClosableBlock closure = PsiTreeUtil.getParentOfType(refExpr, GrClosableBlock.class); |
| closure != null; |
| closure = PsiTreeUtil.getParentOfType(closure, GrClosableBlock.class)) { |
| |
| PsiElement parent = closure.getParent(); |
| if (parent instanceof GrArgumentList) parent = parent.getParent(); |
| if (!(parent instanceof GrMethodCall)) continue; |
| |
| GrExpression funExpr = ((GrMethodCall)parent).getInvokedExpression(); |
| if (!(funExpr instanceof GrReferenceExpression)) return funExpr; |
| |
| final PsiElement resolved = ((GrReferenceExpression)funExpr).resolve(); |
| if (!(resolved instanceof PsiMethod)) return funExpr; |
| |
| if (resolved instanceof GrGdkMethod && |
| isFromDGM((GrGdkMethod)resolved) && |
| !GdkMethodUtil.isWithName(((GrGdkMethod)resolved).getStaticMethod().getName())) { |
| continue; |
| } |
| |
| qualifier = ((GrReferenceExpression)funExpr).getQualifierExpression(); |
| if (qualifier != null) return qualifier; |
| } |
| |
| return null; |
| } |
| |
| private static boolean isFromDGM(GrGdkMethod resolved) { |
| final PsiClass containingClass = resolved.getStaticMethod().getContainingClass(); |
| return containingClass != null && GroovyCommonClassNames.DEFAULT_GROOVY_METHODS.equals(containingClass.getQualifiedName()); |
| } |
| |
| public static void removeVariable(GrVariable variable) { |
| final GrVariableDeclaration varDecl = (GrVariableDeclaration) variable.getParent(); |
| final List<GrVariable> variables = Arrays.asList(varDecl.getVariables()); |
| if (!variables.contains(variable)) { |
| throw new IllegalArgumentException(); |
| } |
| |
| final PsiElement parent = varDecl.getParent(); |
| final ASTNode owner = parent.getNode(); |
| if (variables.size() == 1 && owner != null) { |
| PsiElement next = varDecl.getNextSibling(); |
| |
| // remove redundant semicolons |
| //noinspection ConstantConditions |
| while (next != null && next.getNode() != null && next.getNode().getElementType() == GroovyTokenTypes.mSEMI) { |
| PsiElement tmpNext = next.getNextSibling(); |
| //noinspection ConstantConditions |
| next.delete(); |
| next = tmpNext; |
| } |
| |
| removeNewLineAfter(varDecl); |
| varDecl.delete(); |
| return; |
| } |
| variable.delete(); |
| } |
| |
| @Nullable |
| public static PsiElement realPrevious(PsiElement previousLeaf) { |
| while (previousLeaf != null && |
| (previousLeaf instanceof PsiWhiteSpace || |
| previousLeaf instanceof PsiComment || |
| previousLeaf instanceof PsiErrorElement)) { |
| previousLeaf = previousLeaf.getPrevSibling(); |
| } |
| return previousLeaf; |
| } |
| |
| private static int getExprPriorityLevel(GrExpression expr) { |
| int priority; |
| //if (expr instanceof GrNewExpression) priority = 1; |
| if (expr instanceof GrUnaryExpression) priority = ((GrUnaryExpression)expr).isPostfix() ? 5 : 6; |
| else if (expr instanceof GrTypeCastExpression) priority = 6; |
| |
| |
| else if (expr instanceof GrBinaryExpression) { |
| final IElementType opToken = ((GrBinaryExpression)expr).getOperationTokenType(); |
| |
| if (opToken == GroovyTokenTypes.mSTAR_STAR) priority = 7; |
| else if (opToken == GroovyTokenTypes.mSTAR || opToken == GroovyTokenTypes.mDIV) priority = 8; |
| else if (opToken == GroovyTokenTypes.mPLUS || opToken == GroovyTokenTypes.mMINUS) priority = 9; |
| else if (TokenSets.SHIFT_SIGNS.contains(opToken)) priority = 10; |
| else if (opToken == GroovyTokenTypes.mRANGE_EXCLUSIVE || opToken == GroovyTokenTypes.mRANGE_INCLUSIVE) priority = 11; |
| else if (TokenSets.RELATIONS.contains(opToken)) priority = 12; |
| else if (opToken == GroovyTokenTypes.mEQUAL || opToken == GroovyTokenTypes.mNOT_EQUAL || opToken == GroovyTokenTypes.mCOMPARE_TO) priority = 13; |
| else if (opToken == GroovyTokenTypes.mREGEX_FIND || opToken == GroovyTokenTypes.mREGEX_MATCH) priority = 14; |
| else if (opToken == GroovyTokenTypes.mBAND) priority = 15; |
| else if (opToken == GroovyTokenTypes.mBXOR) priority = 16; |
| else if (opToken == GroovyTokenTypes.mBOR) priority = 17; |
| else if (opToken == GroovyTokenTypes.mLAND) priority = 18; |
| else if (opToken == GroovyTokenTypes.mLOR) priority = 19; |
| else { |
| assert false :"unknown operation:"+opToken; |
| priority = 0; |
| } |
| } |
| else if (expr instanceof GrConditionalExpression) priority = 20; |
| else if (expr instanceof GrSafeCastExpression) priority = 21; |
| else if (expr instanceof GrAssignmentExpression) priority = 22; |
| else if (expr instanceof GrApplicationStatement) priority = 23; |
| else priority = 0; |
| |
| return -priority; |
| } |
| |
| public static void setName(String name, PsiElement nameElement) { |
| final PsiElement newNameElement = GroovyPsiElementFactory.getInstance(nameElement.getProject()).createReferenceNameFromText(name); |
| nameElement.replace(newNameElement); |
| } |
| |
| public static boolean isExtendsSignature(MethodSignature superSignatureCandidate, MethodSignature subSignature) { |
| return MethodSignatureUtil.isSubsignature(superSignatureCandidate, subSignature); |
| } |
| |
| @Nullable |
| public static PsiElement getOriginalElement(PsiClass clazz, PsiFile containingFile) { |
| VirtualFile vFile = containingFile.getVirtualFile(); |
| final JavaPsiFacade facade = JavaPsiFacade.getInstance(clazz.getProject()); |
| final ProjectFileIndex idx = ProjectRootManager.getInstance(facade.getProject()).getFileIndex(); |
| |
| if (vFile == null || !idx.isInLibrarySource(vFile)) return clazz; |
| final String qName = clazz.getQualifiedName(); |
| if (qName == null) return null; |
| final List<OrderEntry> orderEntries = idx.getOrderEntriesForFile(vFile); |
| PsiClass original = facade.findClass(qName, new GlobalSearchScope(facade.getProject()) { |
| @Override |
| public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) { |
| return 0; |
| } |
| |
| @Override |
| public boolean contains(@NotNull VirtualFile file) { |
| // order for file and vFile has non empty intersection. |
| List<OrderEntry> entries = idx.getOrderEntriesForFile(file); |
| //noinspection ForLoopReplaceableByForEach |
| for (int i = 0; i < entries.size(); i++) { |
| final OrderEntry entry = entries.get(i); |
| if (orderEntries.contains(entry)) return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isSearchInModuleContent(@NotNull Module aModule) { |
| return false; |
| } |
| |
| @Override |
| public boolean isSearchInLibraries() { |
| return true; |
| } |
| }); |
| |
| return original != null ? original : clazz; |
| } |
| |
| @Nullable |
| public static PsiMethod extractUniqueElement(@NotNull GroovyResolveResult[] results) { |
| if (results.length != 1) return null; |
| final PsiElement element = results[0].getElement(); |
| return element instanceof PsiMethod ? (PsiMethod) element : null; |
| } |
| |
| @NotNull |
| public static GroovyResolveResult extractUniqueResult(@NotNull GroovyResolveResult[] results) { |
| if (results.length != 1) return GroovyResolveResult.EMPTY_RESULT; |
| return results[0]; |
| } |
| |
| public static PsiMethod[] mapToMethods(@Nullable List<CandidateInfo> list) { |
| if (list == null) return PsiMethod.EMPTY_ARRAY; |
| PsiMethod[] result = new PsiMethod[list.size()]; |
| for (int i = 0; i < list.size(); i++) { |
| result[i] = (PsiMethod) list.get(i).getElement(); |
| |
| } |
| return result; |
| } |
| |
| @NotNull |
| public static String getName(@NotNull GrNamedElement namedElement) { |
| PsiElement nameElement = namedElement.getNameIdentifierGroovy(); |
| ASTNode node = nameElement.getNode(); |
| LOG.assertTrue(node != null); |
| |
| if (node.getElementType() == GroovyTokenTypes.mIDENT) { |
| return nameElement.getText(); |
| } |
| |
| if (node.getElementType() == GroovyTokenTypes.mSTRING_LITERAL || node.getElementType() == GroovyTokenTypes.mGSTRING_LITERAL) { |
| final Object value = GrLiteralImpl.getLiteralValue(nameElement); |
| if (value instanceof String) { |
| return (String)value; |
| } |
| else { |
| return GrStringUtil.removeQuotes(nameElement.getText()); |
| } |
| } |
| |
| throw new IncorrectOperationException("incorrect name element: " + node.getElementType() + ", named element: " + namedElement); |
| } |
| |
| public static void removeNewLineAfter(@NotNull GrStatement statement) { |
| ASTNode parentNode = statement.getParent().getNode(); |
| ASTNode next = statement.getNode().getTreeNext(); |
| if (parentNode != null && next != null && GroovyTokenTypes.mNLS == next.getElementType()) { |
| parentNode.removeChild(next); |
| } |
| } |
| |
| public static boolean isMainMethod(GrMethod method) { |
| if (!method.getName().equals(MAIN_METHOD)) return false; |
| else if (!method.hasModifierProperty(PsiModifier.STATIC))return false; |
| |
| final GrParameter[] parameters = method.getParameters(); |
| |
| if (parameters.length == 0) return false; |
| if (parameters.length == 1 && parameters[0].getTypeElementGroovy() == null) return true; |
| |
| int args_count = 0; |
| int optional_count = 0; |
| |
| for (GrParameter p : parameters) { |
| final GrTypeElement declaredType = p.getTypeElementGroovy(); |
| if ((declaredType == null || declaredType.getType().equalsToText(CommonClassNames.JAVA_LANG_STRING + "[]")) && |
| p.getInitializerGroovy() == null) { |
| args_count++; |
| } |
| if (p.getInitializerGroovy() != null) optional_count++; |
| } |
| |
| return optional_count == parameters.length - 1 && args_count == 1; |
| } |
| |
| public static void deleteStatementTail(PsiElement container, @NotNull PsiElement statement) { |
| PsiElement next = statement.getNextSibling(); |
| while (next != null) { |
| final ASTNode node = next.getNode(); |
| final IElementType type = node.getElementType(); |
| if (type == GroovyTokenTypes.mSEMI || type == TokenType.WHITE_SPACE && !next.getText().contains("\n")) { |
| final PsiElement nnext = next.getNextSibling(); |
| container.deleteChildRange(next, next); |
| next = nnext; |
| } |
| else if (type == GroovyTokenTypes.mNLS || type == TokenType.WHITE_SPACE && next.getText().contains("\n")) { |
| final String text = next.getText(); |
| final int first = text.indexOf("\n"); |
| final int second = text.indexOf("\n", first + 1); |
| if (second < 0) { |
| container.deleteChildRange(next, next); |
| return; |
| } |
| final String substring = text.substring(second); |
| container.getNode() |
| .replaceChild(node, Factory.createSingleLeafElement(type, substring, 0, substring.length(), null, container.getManager())); |
| return; |
| } |
| else { |
| break; |
| } |
| } |
| } |
| |
| public static <T extends PsiElement> void setQualifier(@NotNull GrQualifiedReference<T> ref, @Nullable T newQualifier) { |
| final T oldQualifier = ref.getQualifier(); |
| final ASTNode node = ref.getNode(); |
| final PsiElement refNameElement = ref.getReferenceNameElement(); |
| if (newQualifier == null) { |
| if (oldQualifier != null && refNameElement != null) { |
| ref.deleteChildRange(ref.getFirstChild(), refNameElement.getPrevSibling()); |
| } |
| } else { |
| if (oldQualifier == null) { |
| if (refNameElement != null) { |
| node.addLeaf(GroovyTokenTypes.mDOT, ".", refNameElement.getNode()); |
| ref.addBefore(newQualifier, refNameElement.getPrevSibling()); |
| } |
| } |
| else { |
| oldQualifier.replace(newQualifier); |
| } |
| } |
| } |
| |
| public static boolean isSimpleArrayAccess(PsiType exprType, PsiType[] argTypes, PsiElement context, boolean isLValue) { |
| return exprType instanceof PsiArrayType && |
| (isLValue && argTypes.length == 2 || !isLValue && argTypes.length == 1) && |
| TypesUtil.isAssignableByMethodCallConversion(PsiType.INT, argTypes[0], context); |
| } |
| |
| public static String getTextSkipWhiteSpaceAndComments(ASTNode node) { |
| return AstBufferUtil.getTextSkippingWhitespaceComments(node); |
| } |
| |
| public static PsiCodeBlock getOrCreatePsiCodeBlock(GrOpenBlock block) { |
| if (block == null) return null; |
| |
| final SoftReference<PsiCodeBlock> ref = block.getUserData(PSI_CODE_BLOCK); |
| final PsiCodeBlock body = SoftReference.dereference(ref); |
| if (body != null) return body; |
| final GrSyntheticCodeBlock newBody = new GrSyntheticCodeBlock(block); |
| block.putUserData(PSI_CODE_BLOCK, new SoftReference<PsiCodeBlock>(newBody)); |
| return newBody; |
| } |
| |
| public static PsiTypeElement getOrCreateTypeElement(GrTypeElement typeElement) { |
| if (typeElement == null) return null; |
| |
| final SoftReference<PsiTypeElement> ref = typeElement.getUserData(PSI_TYPE_ELEMENT); |
| final PsiTypeElement element = SoftReference.dereference(ref); |
| if (element != null) return element; |
| final GrSyntheticTypeElement newTypeElement = new GrSyntheticTypeElement(typeElement); |
| typeElement.putUserData(PSI_TYPE_ELEMENT, new SoftReference<PsiTypeElement>(newTypeElement)); |
| return newTypeElement; |
| } |
| |
| public static PsiExpression getOrCreatePisExpression(GrExpression expr) { |
| if (expr == null) return null; |
| |
| final SoftReference<PsiExpression> ref = expr.getUserData(PSI_EXPRESSION); |
| final PsiExpression element = SoftReference.dereference(ref); |
| if (element != null) return element; |
| final GrSyntheticExpression newExpr = new GrSyntheticExpression(expr); |
| expr.putUserData(PSI_EXPRESSION, new SoftReference<PsiExpression>(newExpr)); |
| return newExpr; |
| } |
| |
| public static PsiReferenceList getOrCreatePsiReferenceList(GrReferenceList list, PsiReferenceList.Role role) { |
| if (list == null) return null; |
| |
| final SoftReference<PsiReferenceList> ref = list.getUserData(PSI_REFERENCE_LIST); |
| final PsiReferenceList element = SoftReference.dereference(ref); |
| if (element != null) return element; |
| final GrSyntheticReferenceList newList = new GrSyntheticReferenceList(list, role); |
| list.putUserData(PSI_REFERENCE_LIST, new SoftReference<PsiReferenceList>(newList)); |
| return newList; |
| } |
| |
| |
| public static <T extends GrCondition> T replaceBody(T newBody, GrStatement body, ASTNode node, Project project) { |
| if (body == null || newBody == null) { |
| throw new IncorrectOperationException(); |
| } |
| ASTNode oldBodyNode = body.getNode(); |
| if (oldBodyNode.getTreePrev() != null && GroovyTokenTypes.mNLS.equals(oldBodyNode.getTreePrev().getElementType())) { |
| ASTNode whiteNode = GroovyPsiElementFactory.getInstance(project).createWhiteSpace().getNode(); |
| node.replaceChild(oldBodyNode.getTreePrev(), whiteNode); |
| } |
| node.replaceChild(oldBodyNode, newBody.getNode()); |
| return newBody; |
| } |
| |
| public static boolean isVarArgs(GrParameter[] parameters) { |
| return parameters.length > 0 && parameters[parameters.length - 1].isVarArgs(); |
| } |
| |
| @Nullable |
| public static PsiAnnotation getAnnotation(@NotNull PsiModifierListOwner field, @NotNull String annotationName) { |
| final PsiModifierList modifierList = field.getModifierList(); |
| if (modifierList == null) return null; |
| return modifierList.findAnnotation(annotationName); |
| } |
| |
| @Nullable |
| public static GrConstructorInvocation getChainingConstructorInvocation(GrMethod constructor) { |
| if (constructor instanceof GrReflectedMethod && ((GrReflectedMethod)constructor).getSkippedParameters().length > 0) return null; |
| |
| LOG.assertTrue(constructor.isConstructor()); |
| |
| GrOpenBlock body = constructor.getBlock(); |
| if (body == null) return null; |
| |
| GrStatement[] statements = body.getStatements(); |
| |
| if (statements.length > 0 && statements[0] instanceof GrConstructorInvocation) { |
| return (GrConstructorInvocation) statements[0]; |
| } |
| |
| return null; |
| } |
| |
| public static GrMethod[] getMethodOrReflectedMethods(GrMethod method) { |
| final GrReflectedMethod[] reflectedMethods = method.getReflectedMethods(); |
| return reflectedMethods.length > 0 ? reflectedMethods : new GrMethod[]{method}; |
| } |
| |
| @Nullable |
| public static PsiType inferExpectedTypeForDiamond(GrExpression diamondNew) { |
| PsiElement skipped = PsiUtil.skipParentheses(diamondNew, true); |
| assert skipped != null; |
| PsiElement pparent = skipped.getParent(); |
| if (pparent instanceof GrAssignmentExpression && |
| PsiTreeUtil.isAncestor(((GrAssignmentExpression)pparent).getRValue(), diamondNew, false)) { |
| GrExpression lValue = ((GrAssignmentExpression)pparent).getLValue(); |
| if (PsiUtil.mightBeLValue(lValue)) { |
| return lValue.getNominalType(); |
| } |
| } |
| else if (pparent instanceof GrVariable && ((GrVariable)pparent).getInitializerGroovy() == diamondNew) { |
| return ((GrVariable)pparent).getDeclaredType(); |
| } |
| else if (pparent instanceof GrListOrMap) { |
| PsiElement ppparent = PsiUtil.skipParentheses(pparent.getParent(), true); |
| |
| if (ppparent instanceof GrAssignmentExpression && |
| PsiTreeUtil.isAncestor(((GrAssignmentExpression)ppparent).getRValue(), pparent, false)) { |
| |
| PsiElement lValue = PsiUtil.skipParentheses(((GrAssignmentExpression)ppparent).getLValue(), false); |
| if (lValue instanceof GrTupleExpression) { |
| GrExpression[] initializers = ((GrListOrMap)pparent).getInitializers(); |
| int index = ArrayUtil.find(initializers, diamondNew); |
| GrExpression[] expressions = ((GrTupleExpression)lValue).getExpressions(); |
| if (index < expressions.length) { |
| return expressions[index].getNominalType(); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static PsiType normalizeWildcardTypeByPosition(@NotNull PsiType type, @NotNull GrExpression expression) { |
| GrExpression toplevel = expression; |
| while (toplevel.getParent() instanceof GrIndexProperty && |
| ((GrIndexProperty)toplevel.getParent()).getInvokedExpression() == toplevel) { |
| toplevel = (GrExpression)toplevel.getParent(); |
| } |
| |
| final PsiType normalized = doNormalizeWildcardByPosition(type, expression, toplevel); |
| if (normalized instanceof PsiClassType && !PsiUtil.isAccessedForWriting(toplevel)) { |
| return com.intellij.psi.util.PsiUtil.captureToplevelWildcards(normalized, expression); |
| } |
| |
| return normalized; |
| } |
| |
| @Nullable |
| private static PsiType doNormalizeWildcardByPosition(final PsiType type, final GrExpression expression, final GrExpression toplevel) { |
| if (type instanceof PsiCapturedWildcardType) { |
| return doNormalizeWildcardByPosition(((PsiCapturedWildcardType)type).getWildcard(), expression, toplevel); |
| } |
| |
| |
| if (type instanceof PsiWildcardType) { |
| final PsiWildcardType wildcardType = (PsiWildcardType)type; |
| |
| if (PsiUtil.isAccessedForWriting(toplevel)) { |
| return wildcardType.isSuper() ? wildcardType.getBound() : PsiCapturedWildcardType.create(wildcardType, expression); |
| } |
| else { |
| if (wildcardType.isExtends()) { |
| return wildcardType.getBound(); |
| } |
| else { |
| return TypesUtil.getJavaLangObject(expression); |
| } |
| } |
| } |
| else if (type instanceof PsiArrayType) { |
| final PsiType componentType = ((PsiArrayType)type).getComponentType(); |
| final PsiType normalizedComponentType = doNormalizeWildcardByPosition(componentType, expression, toplevel); |
| if (normalizedComponentType != componentType) { |
| assert normalizedComponentType != null; |
| return normalizedComponentType.createArrayType(); |
| } |
| } |
| |
| return type; |
| } |
| |
| public static boolean hasElementType(@Nullable PsiElement next, @NotNull final IElementType type) { |
| if (next == null) return false; |
| final ASTNode astNode = next.getNode(); |
| return astNode != null && astNode.getElementType() == type; |
| } |
| |
| public static boolean hasElementType(@Nullable PsiElement next, final TokenSet set) { |
| if (next == null) return false; |
| final ASTNode astNode = next.getNode(); |
| return astNode != null && set.contains(astNode.getElementType()); |
| } |
| |
| public static boolean hasNamedArguments(@Nullable GrNamedArgumentsOwner list) { |
| if (list == null) return false; |
| for (PsiElement child = list.getFirstChild(); child != null; child = child.getNextSibling()) { |
| if (child instanceof GrNamedArgument) return true; |
| } |
| return false; |
| } |
| |
| public static boolean hasExpressionArguments(@Nullable GrArgumentList list) { |
| if (list == null) return false; |
| |
| for (PsiElement child = list.getFirstChild(); child != null; child = child.getNextSibling()) { |
| if (child instanceof GrExpression) return true; |
| } |
| return false; |
| } |
| |
| public static boolean hasClosureArguments(@Nullable GrCall call) { |
| if (call == null) return false; |
| |
| for (PsiElement child = call.getFirstChild(); child != null; child = child.getNextSibling()) { |
| if (child instanceof GrClosableBlock) return true; |
| } |
| return false; |
| } |
| |
| public static PsiElement findTailingSemicolon(@NotNull GrStatement statement) { |
| final PsiElement nextNonSpace = PsiUtil.skipWhitespaces(statement.getNextSibling(), true); |
| if (nextNonSpace != null && nextNonSpace.getNode().getElementType() == GroovyTokenTypes.mSEMI) { |
| return nextNonSpace; |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| public static PsiType inferReturnType(PsiElement position) { |
| final GrControlFlowOwner flowOwner = ControlFlowUtils.findControlFlowOwner(position); |
| if (flowOwner == null) return null; |
| |
| final PsiElement parent = flowOwner.getContext(); |
| if (flowOwner instanceof GrOpenBlock && parent instanceof GrMethod) { |
| final GrMethod method = (GrMethod)parent; |
| if (method.isConstructor()) return null; |
| return method.getReturnType(); |
| } |
| |
| return null; |
| } |
| |
| public static GrStatement[] getStatements(final GrStatementOwner statementOwner) { |
| List<GrStatement> result = new ArrayList<GrStatement>(); |
| for (PsiElement cur = statementOwner.getFirstChild(); cur != null; cur = cur.getNextSibling()) { |
| if (cur instanceof GrStatement) { |
| result.add((GrStatement)cur); |
| } |
| } |
| return result.toArray(new GrStatement[result.size()]); |
| } |
| |
| public static GrNamedArgument findNamedArgument(final GrNamedArgumentsOwner namedArgumentOwner, |
| String label) { |
| for (PsiElement cur = namedArgumentOwner.getFirstChild(); cur != null; cur = cur.getNextSibling()) { |
| if (cur instanceof GrNamedArgument) { |
| if (label.equals(((GrNamedArgument)cur).getLabelName())) { |
| return (GrNamedArgument)cur; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| public static boolean hasImmutableAnnotation(PsiModifierList modifierList) { |
| return modifierList.findAnnotation(GroovyCommonClassNames.GROOVY_LANG_IMMUTABLE) != null || |
| modifierList.findAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_IMMUTABLE) != null; |
| } |
| |
| public static boolean isWhiteSpaceOrNls(@Nullable PsiElement sibling) { |
| return sibling != null && isWhiteSpaceOrNls(sibling.getNode()); |
| } |
| |
| public static boolean isWhiteSpaceOrNls(@Nullable ASTNode node) { |
| return node != null && TokenSets.WHITE_SPACES_SET.contains(node.getElementType()); |
| } |
| |
| public static void insertPlaceHolderToModifierListAtEndIfNeeded(GrModifierList modifierList) { |
| PsiElement newLineAfterModifierList = findNewLineAfterElement(modifierList); |
| if (newLineAfterModifierList != null) { |
| modifierList.setModifierProperty(GrModifier.DEF, false); |
| |
| if (modifierList.getModifiers().length > 0) { |
| modifierList.getNode().addLeaf(GroovyTokenTypes.mNLS, newLineAfterModifierList.getText(), null); |
| } |
| modifierList.getNode().addLeaf(GroovyTokenTypes.kDEF, "def", null); |
| final PsiElement newLineUpdated = findNewLineAfterElement(modifierList); |
| if (newLineUpdated != null) newLineUpdated.delete(); |
| if (!isWhiteSpaceOrNls(modifierList.getNextSibling())) { |
| modifierList.getParent().getNode().addLeaf(TokenType.WHITE_SPACE, " ", modifierList.getNextSibling().getNode()); |
| } |
| } |
| else if (modifierList.getModifiers().length == 0) { |
| modifierList.setModifierProperty(GrModifier.DEF, true); |
| } |
| } |
| |
| @Nullable |
| private static PsiElement findNewLineAfterElement(PsiElement element) { |
| PsiElement sibling = element.getNextSibling(); |
| while (sibling != null && isWhiteSpaceOrNls(sibling)) { |
| if (PsiUtil.isNewLine(sibling)) { |
| return sibling; |
| } |
| sibling = sibling.getNextSibling(); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static GrAnnotation getAnnotation(@NotNull GrAnnotationNameValuePair pair) { |
| PsiElement pParent = pair.getParent().getParent(); |
| if (pParent instanceof GrAnnotation) return (GrAnnotation)pParent; |
| PsiElement ppParent = pParent.getParent(); |
| return ppParent instanceof GrAnnotation ? (GrAnnotation)ppParent : null; |
| } |
| |
| @Nullable |
| public static <T extends PsiElement> T findElementInRange(final PsiFile file, |
| int startOffset, |
| int endOffset, |
| final Class<T> klass) { |
| PsiElement element1 = file.getViewProvider().findElementAt(startOffset, file.getLanguage()); |
| PsiElement element2 = file.getViewProvider().findElementAt(endOffset - 1, file.getLanguage()); |
| if (element1 == null || element2 == null) return null; |
| |
| if (isWhiteSpaceOrNls(element1)) { |
| startOffset = element1.getTextRange().getEndOffset(); |
| element1 = file.getViewProvider().findElementAt(startOffset, file.getLanguage()); |
| } |
| if (isWhiteSpaceOrNls(element2)) { |
| endOffset = element2.getTextRange().getStartOffset(); |
| element2 = file.getViewProvider().findElementAt(endOffset - 1, file.getLanguage()); |
| } |
| |
| if (element2 == null || element1 == null) return null; |
| final PsiElement commonParent = PsiTreeUtil.findCommonParent(element1, element2); |
| assert commonParent != null; |
| final T element = ReflectionUtil.isAssignable(klass, commonParent.getClass()) ? (T) commonParent : PsiTreeUtil.getParentOfType(commonParent, klass); |
| if (element == null) { |
| return null; |
| } |
| |
| if (!checkRanges(element, startOffset, endOffset)) { |
| return null; |
| } |
| |
| return element; |
| } |
| |
| private static boolean checkRanges(@NotNull PsiElement element, int startOffset, int endOffset) { |
| if (element instanceof GrLiteral && StringPartInfo.isWholeLiteralContentSelected((GrLiteral)element, startOffset, endOffset)) { |
| return true; |
| } |
| |
| if (element.getTextRange().getStartOffset() == startOffset) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| public static void appendTypeString(StringBuilder buffer, final PsiType type, PsiElement context) { |
| if (type != null) { |
| JavaDocInfoGenerator.generateType(buffer, type, context); |
| } |
| else { |
| buffer.append(GrModifier.DEF); |
| } |
| } |
| |
| public static boolean isSpreadAssignment(GrExpression lValue) { |
| if (lValue instanceof GrReferenceExpression) { |
| GrReferenceExpression expression = (GrReferenceExpression)lValue; |
| final PsiElement dot = expression.getDotToken(); |
| //noinspection ConstantConditions |
| if (dot != null && dot.getNode().getElementType() == GroovyTokenTypes.mSPREAD_DOT) { |
| return true; |
| } |
| else { |
| final GrExpression qualifier = expression.getQualifierExpression(); |
| if (qualifier != null) return isSpreadAssignment(qualifier); |
| } |
| } |
| return false; |
| } |
| |
| public static void replaceExpression(@NotNull String newExpression, @NotNull GrExpression expression) throws IncorrectOperationException { |
| final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(expression.getProject()); |
| final GrExpression newCall = factory.createExpressionFromText(newExpression); |
| expression.replaceWithExpression(newCall, true); |
| } |
| |
| public static GrStatement replaceStatement(@NonNls @NotNull String newStatement, @NonNls @NotNull GrStatement statement) |
| throws IncorrectOperationException { |
| final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(statement.getProject()); |
| final GrStatement newCall = (GrStatement)factory.createTopElementFromText(newStatement); |
| return statement.replaceWithStatement(newCall); |
| } |
| |
| public static boolean seemsToBeQualifiedClassName(@Nullable GrExpression expr) { |
| if (expr == null) return false; |
| while (expr instanceof GrReferenceExpression) { |
| final PsiElement nameElement = ((GrReferenceExpression)expr).getReferenceNameElement(); |
| if (((GrReferenceExpression)expr).getTypeArguments().length > 0) return false; |
| if (nameElement == null || nameElement.getNode().getElementType() != GroovyTokenTypes.mIDENT) return false; |
| IElementType dotType = ((GrReferenceExpression)expr).getDotTokenType(); |
| if (dotType != null && dotType != GroovyTokenTypes.mDOT) return false; |
| expr = ((GrReferenceExpression)expr).getQualifierExpression(); |
| } |
| return expr == null; |
| } |
| |
| @Nullable |
| public static PsiType getQualifierType(@NotNull GrReferenceExpression ref) { |
| final GrExpression rtQualifier = getRuntimeQualifier(ref); |
| if (rtQualifier != null) { |
| return rtQualifier.getType(); |
| } |
| |
| PsiClass containingClass = null; |
| final GrMember member = PsiTreeUtil.getParentOfType(ref, GrMember.class); |
| if (member == null) { |
| final PsiFile file = ref.getContainingFile(); |
| if (file instanceof GroovyFileBase && ((GroovyFileBase)file).isScript()) { |
| containingClass = ((GroovyFileBase)file).getScriptClass(); |
| } |
| else { |
| return null; |
| } |
| } |
| else if (member instanceof GrMethod) { |
| if (!member.hasModifierProperty(PsiModifier.STATIC)) { |
| containingClass = member.getContainingClass(); |
| } |
| } |
| |
| if (containingClass != null) { |
| final PsiClassType categoryType = GdkMethodUtil.getCategoryType(containingClass); |
| if (categoryType != null) { |
| return categoryType; |
| } |
| return JavaPsiFacade.getElementFactory(ref.getProject()).createType(containingClass); |
| } |
| return null; |
| } |
| } |