| /* |
| * 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.refactoring; |
| |
| import com.intellij.codeInsight.PsiEquivalenceUtil; |
| import com.intellij.codeInsight.highlighting.HighlightManager; |
| import com.intellij.lang.Language; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.colors.EditorColors; |
| import com.intellij.openapi.editor.colors.EditorColorsManager; |
| import com.intellij.openapi.editor.markup.RangeHighlighter; |
| import com.intellij.openapi.editor.markup.TextAttributes; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.impl.source.tree.LeafPsiElement; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.util.*; |
| import com.intellij.refactoring.util.RefactoringUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.GroovyLanguage; |
| 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.statements.*; |
| 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.branch.GrBreakStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrContinueStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression; |
| 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.GrAnonymousClassDefinition; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; |
| import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner; |
| |
| import java.util.*; |
| |
| /** |
| * @author ilyas |
| */ |
| public abstract class GroovyRefactoringUtil { |
| private static final Logger LOG = Logger.getInstance(GroovyRefactoringUtil.class); |
| |
| public static final Collection<String> KEYWORDS = ContainerUtil.map( |
| TokenSets.KEYWORDS.getTypes(), StringUtil.createToStringFunction(IElementType.class)); |
| |
| private static final String[] finalModifiers = new String[]{PsiModifier.FINAL}; |
| |
| public static PsiElement[] getExpressionOccurrences(@NotNull PsiElement expr, @NotNull PsiElement scope) { |
| ArrayList<PsiElement> occurrences = new ArrayList<PsiElement>(); |
| Comparator<PsiElement> comparator = new Comparator<PsiElement>() { |
| @Override |
| public int compare(PsiElement element1, PsiElement element2) { |
| if (element1 != null && element1.equals(element2)) return 0; |
| |
| if (element1 instanceof GrParameter && |
| element2 instanceof GrParameter) { |
| final String name1 = ((GrParameter) element1).getName(); |
| final String name2 = ((GrParameter) element2).getName(); |
| return name1.compareTo(name2); |
| } |
| return 1; |
| } |
| }; |
| |
| if (scope instanceof GrLoopStatement) { |
| PsiElement son = expr; |
| while (son.getParent() != null && !(son.getParent() instanceof GrLoopStatement)) { |
| son = son.getParent(); |
| } |
| assert scope.equals(son.getParent()); |
| collectOccurrences(expr, son, occurrences, comparator, false); |
| } else { |
| collectOccurrences(expr, scope, occurrences, comparator, scope instanceof GrTypeDefinition || scope instanceof GroovyFileBase); |
| } |
| return PsiUtilCore.toPsiElementArray(occurrences); |
| } |
| |
| |
| private static void collectOccurrences(@NotNull PsiElement expr, @NotNull PsiElement scope, @NotNull ArrayList<PsiElement> acc, Comparator<PsiElement> comparator, boolean goIntoInner) { |
| if (scope.equals(expr)) { |
| acc.add(expr); |
| return; |
| } |
| for (PsiElement child : scope.getChildren()) { |
| if (goIntoInner || !(child instanceof GrTypeDefinition) && !(child instanceof GrMethod && scope instanceof GroovyFileBase)) { |
| if (PsiEquivalenceUtil.areElementsEquivalent(child, expr, comparator, false)) { |
| acc.add(child); |
| } else { |
| collectOccurrences(expr, child, acc, comparator, goIntoInner); |
| } |
| } |
| } |
| } |
| |
| |
| public static boolean isAppropriateContainerForIntroduceVariable(PsiElement tempContainer) { |
| return tempContainer instanceof GrOpenBlock || |
| tempContainer instanceof GrClosableBlock || |
| tempContainer instanceof GroovyFileBase || |
| tempContainer instanceof GrCaseSection || |
| tempContainer instanceof GrLoopStatement || |
| tempContainer instanceof GrIfStatement; |
| } |
| |
| public static void sortOccurrences(PsiElement[] occurrences) { |
| Arrays.sort(occurrences, new Comparator<PsiElement>() { |
| @Override |
| public int compare(PsiElement elem1, PsiElement elem2) { |
| final int offset1 = elem1.getTextRange().getStartOffset(); |
| final int offset2 = elem2.getTextRange().getStartOffset(); |
| return offset1 - offset2; |
| } |
| }); |
| } |
| |
| |
| public static void highlightOccurrences(Project project, @Nullable Editor editor, PsiElement[] elements) { |
| if (editor == null) return; |
| ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>(); |
| HighlightManager highlightManager = HighlightManager.getInstance(project); |
| EditorColorsManager colorsManager = EditorColorsManager.getInstance(); |
| TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); |
| if (elements.length > 0) { |
| highlightManager.addOccurrenceHighlights(editor, elements, attributes, false, highlighters); |
| } |
| } |
| |
| public static void highlightOccurrencesByRanges(Project project, Editor editor, TextRange[] ranges) { |
| if (editor == null) return; |
| ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>(); |
| HighlightManager highlightManager = HighlightManager.getInstance(project); |
| EditorColorsManager colorsManager = EditorColorsManager.getInstance(); |
| TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); |
| for (TextRange range : ranges) { |
| highlightManager.addRangeHighlight(editor, range.getStartOffset(), range.getEndOffset(), attributes, false, highlighters); |
| } |
| } |
| |
| public static void trimSpacesAndComments(Editor editor, PsiFile file, boolean trimComments) { |
| int start = editor.getSelectionModel().getSelectionStart(); |
| int end = editor.getSelectionModel().getSelectionEnd(); |
| while (file.findElementAt(start) instanceof PsiWhiteSpace || |
| (file.findElementAt(start) instanceof PsiComment && trimComments) || |
| (file.findElementAt(start) != null && |
| GroovyTokenTypes.mNLS.equals(file.findElementAt(start).getNode().getElementType()))) { |
| start++; |
| } |
| while (file.findElementAt(end - 1) instanceof PsiWhiteSpace || |
| (file.findElementAt(end - 1) instanceof PsiComment && trimComments) || |
| (file.findElementAt(end - 1) != null && |
| (GroovyTokenTypes.mNLS.equals(file.findElementAt(end - 1).getNode().getElementType()) || |
| GroovyTokenTypes.mSEMI.equals(file.findElementAt(end - 1).getNode().getElementType())))) { |
| end--; |
| } |
| |
| editor.getSelectionModel().setSelection(start, end); |
| } |
| |
| @NotNull public static PsiElement[] findStatementsInRange(PsiFile file, int startOffset, int endOffset, boolean strict) { |
| if (!(file instanceof GroovyFileBase)) return PsiElement.EMPTY_ARRAY; |
| Language language = GroovyLanguage.INSTANCE; |
| PsiElement element1 = file.getViewProvider().findElementAt(startOffset, language); |
| PsiElement element2 = file.getViewProvider().findElementAt(endOffset - 1, language); |
| |
| if (element1 instanceof PsiWhiteSpace || org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isNewLine(element1)) { |
| startOffset = element1.getTextRange().getEndOffset(); |
| element1 = file.findElementAt(startOffset); |
| } |
| if (element2 instanceof PsiWhiteSpace || org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isNewLine(element2)) { |
| endOffset = element2.getTextRange().getStartOffset(); |
| element2 = file.findElementAt(endOffset - 1); |
| } |
| if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY; |
| |
| PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2); |
| if (parent == null) return PsiElement.EMPTY_ARRAY; |
| while (true) { |
| if (parent instanceof GrCodeBlock) break; |
| if (parent instanceof GroovyFileBase) break; |
| if (parent instanceof GrCaseSection) break; |
| if (parent instanceof GrStatement) { |
| parent = parent.getParent(); |
| break; |
| } |
| if (parent == null) return PsiElement.EMPTY_ARRAY; |
| final PsiElement prev = parent; |
| parent = parent.getParent(); |
| if (parent instanceof GrCodeBlock && prev instanceof LeafPsiElement) { //braces |
| parent = parent.getParent(); |
| } |
| } |
| |
| if (!parent.equals(element1)) { |
| while (!parent.equals(element1.getParent())) { |
| element1 = element1.getParent(); |
| } |
| } |
| if (startOffset != element1.getTextRange().getStartOffset() && strict) return PsiElement.EMPTY_ARRAY; |
| |
| if (!parent.equals(element2)) { |
| while (!parent.equals(element2.getParent())) { |
| element2 = element2.getParent(); |
| } |
| } |
| if (endOffset != element2.getTextRange().getEndOffset() && strict) return PsiElement.EMPTY_ARRAY; |
| |
| if (parent instanceof GrCodeBlock && parent.getParent() instanceof GrBlockStatement && |
| element1 == ((GrCodeBlock) parent).getLBrace() && element2 == ((GrCodeBlock) parent).getRBrace()) { |
| return new PsiElement[]{parent.getParent()}; |
| } |
| |
| // calculate children |
| PsiElement[] children = PsiElement.EMPTY_ARRAY; |
| PsiElement psiChild = parent.getFirstChild(); |
| if (psiChild != null) { |
| List<PsiElement> result = new ArrayList<PsiElement>(); |
| while (psiChild != null) { |
| result.add(psiChild); |
| psiChild = psiChild.getNextSibling(); |
| } |
| children = PsiUtilCore.toPsiElementArray(result); |
| } |
| |
| |
| ArrayList<PsiElement> possibleStatements = new ArrayList<PsiElement>(); |
| boolean flag = false; |
| for (PsiElement child : children) { |
| if (child == element1) { |
| flag = true; |
| } |
| if (flag) { |
| possibleStatements.add(child); |
| } |
| if (child == element2) { |
| break; |
| } |
| } |
| |
| for (PsiElement element : possibleStatements) { |
| if (!(element instanceof GrStatement || |
| element instanceof PsiWhiteSpace || |
| element instanceof PsiComment || |
| TokenSets.SEPARATORS.contains(element.getNode().getElementType()))) { |
| return PsiElement.EMPTY_ARRAY; |
| } |
| } |
| |
| return PsiUtilCore.toPsiElementArray(possibleStatements); |
| } |
| |
| public static boolean isSuperOrThisCall(GrStatement statement, boolean testForSuper, boolean testForThis) { |
| if (!(statement instanceof GrConstructorInvocation)) return false; |
| GrConstructorInvocation expr = (GrConstructorInvocation) statement; |
| return (testForSuper && expr.isSuperCall()) || (testForThis && expr.isThisCall()); |
| } |
| |
| public static boolean hasWrongBreakStatements(PsiElement element) { |
| ArrayList<GrBreakStatement> vector = new ArrayList<GrBreakStatement>(); |
| addBreakStatements(element, vector); |
| return !vector.isEmpty(); |
| } |
| |
| private static void addBreakStatements(PsiElement element, ArrayList<GrBreakStatement> vector) { |
| if (element instanceof GrBreakStatement) { |
| vector.add(((GrBreakStatement) element)); |
| } else if (!(element instanceof GrLoopStatement || |
| element instanceof GrSwitchStatement || |
| element instanceof GrClosableBlock)) { |
| for (PsiElement psiElement : element.getChildren()) { |
| addBreakStatements(psiElement, vector); |
| } |
| } |
| } |
| |
| public static boolean hasWrongContinueStatements(PsiElement element) { |
| ArrayList<GrContinueStatement> vector = new ArrayList<GrContinueStatement>(); |
| addContinueStatements(element, vector); |
| return !vector.isEmpty(); |
| } |
| |
| private static void addContinueStatements(PsiElement element, ArrayList<GrContinueStatement> vector) { |
| if (element instanceof GrContinueStatement) { |
| vector.add(((GrContinueStatement) element)); |
| } else if (!(element instanceof GrLoopStatement || element instanceof GrClosableBlock)) { |
| for (PsiElement psiElement : element.getChildren()) { |
| addContinueStatements(psiElement, vector); |
| } |
| } |
| } |
| |
| |
| public static String getMethodSignature(PsiMethod method) { |
| MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY); |
| String s = signature.getName() + "("; |
| int i = 0; |
| PsiType[] types = signature.getParameterTypes(); |
| for (PsiType type : types) { |
| s += type.getPresentableText(); |
| if (i < types.length - 1) { |
| s += ", "; |
| } |
| i++; |
| } |
| s += ")"; |
| return s; |
| |
| } |
| |
| @Nullable |
| public static GrCall getCallExpressionByMethodReference(@Nullable PsiElement ref) { |
| if (ref == null) return null; |
| if (ref instanceof GrEnumConstant) return (GrEnumConstant)ref; |
| if (ref instanceof GrConstructorInvocation) return (GrCall)ref; |
| PsiElement parent = ref.getParent(); |
| if (parent instanceof GrCall) { |
| return (GrCall)parent; |
| } |
| else if (parent instanceof GrAnonymousClassDefinition) { |
| return (GrCall)parent.getParent(); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| public static boolean isMethodUsage(PsiElement ref) { |
| return (ref instanceof GrEnumConstant) || (ref.getParent() instanceof GrCall) || (ref instanceof GrConstructorInvocation); |
| } |
| |
| public static String createTempVar(GrExpression expr, final GroovyPsiElement context, boolean declareFinal) { |
| expr = addBlockIntoParent(expr); |
| final GrVariableDeclarationOwner block = PsiTreeUtil.getParentOfType(expr, GrVariableDeclarationOwner.class); |
| LOG.assertTrue(block != null); |
| final PsiElement anchorStatement = PsiTreeUtil.findPrevParent(block, expr); |
| LOG.assertTrue(anchorStatement instanceof GrStatement); |
| |
| Project project = expr.getProject(); |
| String[] suggestedNames =GroovyNameSuggestionUtil.suggestVariableNames(expr, new NameValidator() { |
| @Override |
| public String validateName(String name, boolean increaseNumber) { |
| return name; |
| } |
| |
| @Override |
| public Project getProject() { |
| return context.getProject(); |
| } |
| }); |
| /* |
| JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expr, null).names;*/ |
| final String prefix = suggestedNames[0]; |
| final String id = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(prefix, context, true); |
| |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(expr.getProject()); |
| String[] modifiers; |
| if (declareFinal) { |
| modifiers = finalModifiers; |
| } |
| else { |
| modifiers = ArrayUtil.EMPTY_STRING_ARRAY; |
| } |
| GrVariableDeclaration decl = |
| factory.createVariableDeclaration(modifiers, (GrExpression)org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil |
| .skipParentheses(expr, false), expr.getType(), id); |
| /* if (declareFinal) { |
| com.intellij.psi.util.PsiUtil.setModifierProperty((decl.getMembers()[0]), PsiModifier.FINAL, true); |
| }*/ |
| final GrStatement statement = ((GrStatementOwner)anchorStatement.getParent()).addStatementBefore(decl, (GrStatement)anchorStatement); |
| JavaCodeStyleManager.getInstance(statement.getProject()).shortenClassReferences(statement); |
| |
| return id; |
| } |
| |
| public static int verifySafeCopyExpression(GrExpression expression) { |
| return verifySafeCopyExpressionSubElement(expression); |
| } |
| |
| private static int verifySafeCopyExpressionSubElement(PsiElement element) { |
| int result = RefactoringUtil.EXPR_COPY_SAFE; |
| if (element == null) return result; |
| |
| if (element instanceof GrNamedElement) { |
| return RefactoringUtil.EXPR_COPY_SAFE; |
| } |
| |
| if (element instanceof GrMethodCallExpression) { |
| result = RefactoringUtil.EXPR_COPY_UNSAFE; |
| } |
| |
| if (element instanceof GrNewExpression) { |
| return RefactoringUtil.EXPR_COPY_PROHIBITED; |
| } |
| |
| if (element instanceof GrAssignmentExpression) { |
| return RefactoringUtil.EXPR_COPY_PROHIBITED; |
| } |
| |
| if (element instanceof GrClosableBlock) { |
| return RefactoringUtil.EXPR_COPY_PROHIBITED; |
| } |
| |
| if (isPlusPlusOrMinusMinus(element)) { |
| return RefactoringUtil.EXPR_COPY_PROHIBITED; |
| } |
| |
| PsiElement[] children = element.getChildren(); |
| |
| for (PsiElement child : children) { |
| int childResult = verifySafeCopyExpressionSubElement(child); |
| result = Math.max(result, childResult); |
| } |
| return result; |
| } |
| |
| public static boolean isPlusPlusOrMinusMinus(PsiElement element) { |
| if (element instanceof GrUnaryExpression) { |
| IElementType operandSign = ((GrUnaryExpression)element).getOperationTokenType(); |
| return operandSign == GroovyTokenTypes.mDEC || operandSign == GroovyTokenTypes.mINC; |
| } |
| return false; |
| } |
| |
| public static boolean isCorrectReferenceName(String newName, Project project) { |
| if (newName.startsWith("'''") || newName.startsWith("\"\"\"")) { |
| if (newName.length() < 6 || !newName.endsWith("'''")) { |
| return false; |
| } |
| } |
| else if (StringUtil.startsWithChar(newName, '\'') || StringUtil.startsWithChar(newName, '"')) { |
| if (newName.length() < 2 || !newName.endsWith("'")) { |
| return false; |
| } |
| } |
| if (KEYWORDS.contains(newName)) { |
| return false; |
| } |
| try { |
| GroovyPsiElementFactory.getInstance(project).createReferenceNameFromText(newName); |
| } |
| catch (IncorrectOperationException e) { |
| return false; |
| } |
| return true; |
| } |
| |
| public static GrExpression generateArgFromMultiArg(PsiSubstitutor substitutor, |
| List<? extends PsiElement> arguments, |
| @Nullable PsiType type, |
| final Project project) { |
| StringBuilder argText = new StringBuilder(); |
| argText.append("["); |
| for (PsiElement argument : arguments) { |
| argText.append(argument.getText()).append(", "); |
| } |
| if (!arguments.isEmpty()) { |
| argText.delete(argText.length() - 2, argText.length()); |
| } |
| argText.append("]"); |
| if (type instanceof PsiArrayType) { |
| type = substitutor.substitute(type); |
| String typeText = type.getCanonicalText(); |
| if (type instanceof PsiEllipsisType) { |
| typeText = typeText.replace("...", "[]"); |
| } |
| argText.append(" as ").append(typeText); |
| } |
| return GroovyPsiElementFactory.getInstance(project).createExpressionFromText(argText.toString()); |
| } |
| |
| public static boolean hasSideEffect(@NotNull GroovyPsiElement statement) { |
| final Ref<Boolean> hasSideEffect = new Ref<Boolean>(false); |
| statement.accept(new GroovyRecursiveElementVisitor() { |
| @Override |
| public void visitMethodCallExpression(GrMethodCallExpression methodCallExpression) { |
| hasSideEffect.set(true); |
| } |
| |
| @Override |
| public void visitCallExpression(GrCallExpression callExpression) { |
| hasSideEffect.set(true); |
| } |
| |
| @Override |
| public void visitApplicationStatement(GrApplicationStatement applicationStatement) { |
| hasSideEffect.set(true); |
| } |
| |
| @Override |
| public void visitClosure(GrClosableBlock closure) { |
| hasSideEffect.set(true); |
| } |
| |
| @Override |
| public void visitUnaryExpression(GrUnaryExpression expression) { |
| hasSideEffect.set(true); |
| } |
| |
| @Override |
| public void visitElement(GroovyPsiElement element) { |
| if (hasSideEffect.get()) return; |
| super.visitElement(element); |
| } |
| }); |
| |
| return hasSideEffect.get(); |
| } |
| |
| /** |
| * adds block statement in parent of statement if needed. For Example: |
| * while (true) a=foo() |
| * will be replaced with |
| * while(true) {a=foo()} |
| * @param statement |
| * @return corresponding statement inside block if it has been created or statement itself. |
| * @throws com.intellij.util.IncorrectOperationException |
| */ |
| |
| @NotNull |
| public static <Type extends PsiElement> Type addBlockIntoParent(@NotNull Type statement) throws IncorrectOperationException { |
| |
| PsiElement parent = statement.getParent(); |
| PsiElement child = statement; |
| while (!(parent instanceof GrLoopStatement) && |
| !(parent instanceof GrIfStatement) && |
| !(parent instanceof GrVariableDeclarationOwner) && |
| parent != null) { |
| parent = parent.getParent(); |
| child = child.getParent(); |
| } |
| if (parent instanceof GrWhileStatement && child == ((GrWhileStatement)parent).getCondition() || |
| parent instanceof GrIfStatement && child == ((GrIfStatement)parent).getCondition()) { |
| parent = parent.getParent(); |
| } |
| assert parent != null; |
| if (parent instanceof GrVariableDeclarationOwner) { |
| return statement; |
| } |
| |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(statement.getProject()); |
| PsiElement tempStmt = statement; |
| while (parent != tempStmt.getParent()) { |
| tempStmt = tempStmt.getParent(); |
| } |
| GrStatement toAdd = (GrStatement)tempStmt.copy(); |
| GrBlockStatement blockStatement = factory.createBlockStatement(); |
| if (parent instanceof GrLoopStatement) { |
| ((GrLoopStatement)parent).replaceBody(blockStatement); |
| } |
| else { |
| GrIfStatement ifStatement = (GrIfStatement)parent; |
| if (tempStmt == ifStatement.getThenBranch()) { |
| ifStatement.replaceThenBranch(blockStatement); |
| } |
| else if (tempStmt == ifStatement.getElseBranch()) { |
| ifStatement.replaceElseBranch(blockStatement); |
| } |
| } |
| GrStatement result = blockStatement.getBlock().addStatementBefore(toAdd, null); |
| if (result instanceof GrReturnStatement) { |
| //noinspection ConstantConditions,unchecked |
| statement = (Type)((GrReturnStatement)result).getReturnValue(); |
| } |
| else { |
| //noinspection unchecked |
| statement = (Type)result; |
| } |
| |
| return statement; |
| } |
| |
| public static boolean isDiamondNewOperator(GrExpression expression) { |
| PsiElement element = org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.skipParentheses(expression, false); |
| if (!(element instanceof GrNewExpression)) return false; |
| if (((GrNewExpression)element).getArrayCount() > 0) return false; |
| |
| GrCodeReferenceElement referenceElement = ((GrNewExpression)element).getReferenceElement(); |
| if (referenceElement == null) return false; |
| |
| GrTypeArgumentList typeArgumentList = referenceElement.getTypeArgumentList(); |
| return typeArgumentList != null && typeArgumentList.isDiamond(); |
| } |
| |
| @Nullable |
| public static GrStatementOwner getDeclarationOwner(GrStatement statement) { |
| PsiElement parent = statement.getParent(); |
| return parent instanceof GrStatementOwner ? ((GrStatementOwner) parent) : null; |
| } |
| |
| @Nullable |
| public static PsiType getType(@Nullable PsiParameter myParameter) { |
| if (myParameter == null) return null; |
| PsiType type = myParameter.getType(); |
| return type instanceof PsiEllipsisType ? ((PsiEllipsisType)type).toArrayType() : type; |
| } |
| |
| @Nullable |
| public static PsiType getSubstitutedType(@Nullable GrParameter parameter) { |
| if (parameter == null) return null; |
| |
| final PsiType type = getType(parameter); |
| |
| if (type instanceof PsiArrayType) { |
| return type; |
| } |
| |
| final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type); |
| final PsiClass psiClass = result.getElement(); |
| if (psiClass == null) return type; |
| final HashSet<PsiTypeParameter> usedTypeParameters = new HashSet<PsiTypeParameter>(); |
| collectTypeParameters(usedTypeParameters, parameter); |
| for (Iterator<PsiTypeParameter> iterator = usedTypeParameters.iterator(); iterator.hasNext(); ) { |
| PsiTypeParameter usedTypeParameter = iterator.next(); |
| if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) { |
| iterator.remove(); |
| } |
| } |
| final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(parameter.getProject()); |
| PsiSubstitutor subst = PsiSubstitutor.EMPTY; |
| for (PsiTypeParameter usedTypeParameter : usedTypeParameters) { |
| subst = subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter)); |
| } |
| PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; |
| final Map<PsiTypeParameter, PsiType> typeMap = result.getSubstitutor().getSubstitutionMap(); |
| for (PsiTypeParameter typeParameter : typeMap.keySet()) { |
| final PsiType psiType = typeMap.get(typeParameter); |
| substitutor = substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null); |
| } |
| return psiClass instanceof PsiTypeParameter ? subst.substitute((PsiTypeParameter)psiClass) : elementFactory.createType(psiClass, substitutor); |
| } |
| |
| public static void collectTypeParameters(final Set<PsiTypeParameter> used, @NotNull final GroovyPsiElement element) { |
| element.accept(new GroovyRecursiveElementVisitor() { |
| @Override |
| public void visitCodeReferenceElement(GrCodeReferenceElement reference) { |
| super.visitCodeReferenceElement(reference); |
| if (reference.getQualifier() == null) { |
| final PsiElement resolved = reference.resolve(); |
| if (resolved instanceof PsiTypeParameter) { |
| final PsiTypeParameter typeParameter = (PsiTypeParameter)resolved; |
| if (PsiTreeUtil.isAncestor(typeParameter.getOwner(), element, false)) { |
| used.add(typeParameter); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void visitExpression(final GrExpression expression) { |
| super.visitExpression(expression); |
| final PsiType type = expression.getType(); |
| if (type != null) { |
| final TypeParameterSearcher searcher = new TypeParameterSearcher(); |
| type.accept(searcher); |
| for (PsiTypeParameter typeParam : searcher.myTypeParams) { |
| if (PsiTreeUtil.isAncestor(typeParam.getOwner(), element, false)) { |
| used.add(typeParam); |
| } |
| } |
| } |
| } |
| |
| class TypeParameterSearcher extends PsiTypeVisitor<Boolean> { |
| private final Set<PsiTypeParameter> myTypeParams = new HashSet<PsiTypeParameter>(); |
| |
| @Override |
| public Boolean visitType(final PsiType type) { |
| return false; |
| } |
| |
| @Override |
| public Boolean visitArrayType(final PsiArrayType arrayType) { |
| return arrayType.getComponentType().accept(this); |
| } |
| |
| @Override |
| public Boolean visitClassType(final PsiClassType classType) { |
| final PsiClass aClass = classType.resolve(); |
| if (aClass instanceof PsiTypeParameter) { |
| myTypeParams.add((PsiTypeParameter)aClass); |
| } |
| |
| final PsiType[] types = classType.getParameters(); |
| for (final PsiType psiType : types) { |
| psiType.accept(this); |
| } |
| return false; |
| } |
| |
| @Override |
| public Boolean visitWildcardType(final PsiWildcardType wildcardType) { |
| final PsiType bound = wildcardType.getBound(); |
| if (bound != null) { |
| bound.accept(this); |
| } |
| return false; |
| } |
| } |
| }); |
| } |
| } |