| /* |
| * 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.introduce.variable; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.util.PsiUtilCore; |
| import com.intellij.refactoring.util.RefactoringUtil; |
| import com.intellij.util.ArrayUtilRt; |
| 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.lang.psi.GroovyPsiElementFactory; |
| 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.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContext; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase; |
| |
| import java.util.List; |
| |
| /** |
| * @author Max Medvedev |
| */ |
| public abstract class GrIntroduceLocalVariableProcessor { |
| private static final Logger LOG = Logger.getInstance(GrIntroduceLocalVariableProcessor.class); |
| |
| private final GrIntroduceContext myContext; |
| private final GroovyIntroduceVariableSettings mySettings; |
| private final boolean myProcessUsages; |
| private final PsiElement[] myOccurrences; |
| private final GrExpression myExpression; |
| |
| public GrIntroduceLocalVariableProcessor(@NotNull GrIntroduceContext context, |
| @NotNull GroovyIntroduceVariableSettings settings, |
| @NotNull PsiElement[] occurrences, |
| @NotNull GrExpression expression, |
| boolean processUsages) { |
| |
| myContext = context; |
| mySettings = settings; |
| myProcessUsages = processUsages; |
| myOccurrences = settings.replaceAllOccurrences() ? occurrences : new PsiElement[]{expression}; |
| myExpression = expression; |
| } |
| |
| @NotNull |
| public GrVariable processExpression(@NotNull GrVariableDeclaration declaration) { |
| resolveLocalConflicts(myContext.getScope(), mySettings.getName()); |
| |
| preprocessOccurrences(); |
| |
| int expressionIndex = ArrayUtilRt.find(myOccurrences, myExpression); |
| final PsiElement[] replaced = myProcessUsages ? processOccurrences() : myOccurrences; |
| PsiElement replacedExpression = replaced[expressionIndex]; |
| GrStatement anchor = GrIntroduceHandlerBase.getAnchor(replaced, myContext.getScope()); |
| |
| RefactoringUtil.highlightAllOccurrences(myContext.getProject(), replaced, myContext.getEditor()); |
| |
| return insertVariableDefinition(declaration, anchor, replacedExpression); |
| } |
| |
| protected abstract void refreshPositionMarker(PsiElement e); |
| |
| private static boolean isControlStatementBranch(GrStatement statement) { |
| return statement.getParent() instanceof GrLoopStatement && statement == ((GrLoopStatement)statement.getParent()).getBody() || |
| statement.getParent() instanceof GrIfStatement && |
| (statement == ((GrIfStatement)statement.getParent()).getThenBranch() || |
| statement == ((GrIfStatement)statement.getParent()).getElseBranch()); |
| } |
| |
| private PsiElement[] processOccurrences() { |
| |
| List<PsiElement> result = ContainerUtil.newArrayList(); |
| |
| GrReferenceExpression templateRef = |
| GroovyPsiElementFactory.getInstance(myContext.getProject()).createReferenceExpressionFromText(mySettings.getName()); |
| for (PsiElement occurrence : myOccurrences) { |
| if (!(occurrence instanceof GrExpression)) { |
| throw new IncorrectOperationException("Expression occurrence to be replaced is not instance of GroovyPsiElement"); |
| } |
| |
| final GrExpression replaced = ((GrExpression)occurrence).replaceWithExpression(templateRef, true); |
| result.add(replaced); |
| } |
| |
| return PsiUtilCore.toPsiElementArray(result); |
| } |
| |
| @NotNull |
| private GrExpression preprocessOccurrences() { |
| GroovyRefactoringUtil.sortOccurrences(myOccurrences); |
| if (myOccurrences.length == 0 || !(myOccurrences[0] instanceof GrExpression)) { |
| throw new IncorrectOperationException("Wrong expression occurrence"); |
| } |
| |
| return (GrExpression)myOccurrences[0]; |
| } |
| |
| private static void resolveLocalConflicts(@NotNull PsiElement tempContainer, @NotNull String varName) { |
| for (PsiElement child : tempContainer.getChildren()) { |
| if (child instanceof GrReferenceExpression && !child.getText().contains(".")) { |
| PsiReference psiReference = child.getReference(); |
| if (psiReference != null) { |
| final PsiElement resolved = psiReference.resolve(); |
| if (resolved != null) { |
| String fieldName = getFieldName(resolved); |
| if (fieldName != null && varName.equals(fieldName)) { |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(tempContainer.getProject()); |
| ((GrReferenceExpression)child).replaceWithExpression(factory.createExpressionFromText("this." + child.getText()), true); |
| } |
| } |
| } |
| } |
| else { |
| resolveLocalConflicts(child, varName); |
| } |
| } |
| } |
| |
| @NotNull |
| private GrVariable insertVariableDefinition(@NotNull GrVariableDeclaration declaration, |
| @NotNull GrStatement anchor, |
| @Nullable PsiElement expression) throws IncorrectOperationException { |
| GrLabeledStatement labeledStatement = expression != null && expression.getParent() instanceof GrLabeledStatement ? (GrLabeledStatement)expression.getParent() : null; |
| |
| boolean expressionMustBeDeleted = expression != null && PsiUtil.isExpressionStatement(expression) && !isSingleGStringInjectionExpr(expression); |
| boolean anchorEqualsExpression = anchor == expression || labeledStatement == anchor; |
| |
| String usedLabel = labeledStatement != null ? labeledStatement.getName() : null; |
| |
| if (expressionMustBeDeleted && !anchorEqualsExpression) { |
| expression.delete(); |
| } |
| |
| boolean isInsideControlStatement = isControlStatementBranch(anchor); |
| if (isInsideControlStatement) { |
| anchor = insertBraces(anchor); |
| } |
| |
| LOG.assertTrue(myOccurrences.length > 0); |
| |
| GrStatementOwner block = (GrStatementOwner)anchor.getParent(); |
| |
| if (usedLabel != null && expressionMustBeDeleted && anchorEqualsExpression) { |
| GrLabeledStatement definitionWithLabel = (GrLabeledStatement)GroovyPsiElementFactory.getInstance(anchor.getProject()).createStatementFromText(usedLabel + ": foo()"); |
| GrLabeledStatement inserted = insertStatement(definitionWithLabel, anchor, block, true); |
| declaration = inserted.getStatement().replaceWithStatement(declaration); |
| } |
| else { |
| declaration = insertStatement(declaration, anchor, block, expressionMustBeDeleted && anchorEqualsExpression); |
| } |
| |
| final GrVariable variable = declaration.getVariables()[0]; |
| JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration); |
| |
| |
| PsiElement markerPlace = expressionMustBeDeleted ? variable : |
| isInsideControlStatement ? declaration.getParent() |
| : expression; |
| refreshPositionMarker(markerPlace); |
| |
| return variable; |
| } |
| |
| private static <T extends GrStatement> T insertStatement(T declaration, |
| GrStatement anchor, |
| GrStatementOwner block, |
| boolean replaceAnchor) { |
| if (replaceAnchor) { |
| return (T)anchor.replace(declaration); |
| } |
| else { |
| return (T)block.addStatementBefore(declaration, anchor); |
| } |
| } |
| |
| @NotNull |
| static GrStatement insertBraces(@NotNull GrStatement anchor) { |
| GrBlockStatement blockStatement = GroovyPsiElementFactory.getInstance(anchor.getProject()).createBlockStatement(); |
| |
| blockStatement.getBlock().addStatementBefore(anchor, null); |
| GrBlockStatement newBlockStatement = ((GrBlockStatement)anchor.replace(blockStatement)); |
| return newBlockStatement.getBlock().getStatements()[0]; |
| } |
| |
| private static boolean isSingleGStringInjectionExpr(PsiElement expression) { |
| PsiElement parent = expression.getParent(); |
| return parent instanceof GrClosableBlock && parent.getParent() instanceof GrStringInjection; |
| } |
| |
| @Nullable |
| private static String getFieldName(@Nullable PsiElement element) { |
| if (element instanceof GrAccessorMethod) element = ((GrAccessorMethod)element).getProperty(); |
| return element instanceof GrField ? ((GrField)element).getName() : null; |
| } |
| |
| } |