| /* |
| * 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.extract.closure; |
| |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.searches.MethodReferencesSearch; |
| import com.intellij.psi.search.searches.OverridingMethodsSearch; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.util.PsiUtilBase; |
| import com.intellij.refactoring.IntroduceParameterRefactoring; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.refactoring.introduceParameter.*; |
| import com.intellij.refactoring.ui.ConflictsDialog; |
| import com.intellij.refactoring.util.CommonRefactoringUtil; |
| import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo; |
| import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.usageView.UsageViewUtil; |
| import com.intellij.util.containers.MultiMap; |
| import gnu.trove.TIntArrayList; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.plugins.groovy.GroovyLanguage; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; |
| import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; |
| import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; |
| import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.FieldConflictsResolver; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrExpressionWrapper; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterSettings; |
| import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GroovyIntroduceParameterUtil; |
| import org.jetbrains.plugins.groovy.refactoring.util.AnySupers; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * @author Max Medvedev |
| */ |
| public class ExtractClosureFromMethodProcessor extends ExtractClosureProcessorBase { |
| |
| private final GrMethod myMethod; |
| private final GrStatementOwner myDeclarationOwner; |
| |
| public ExtractClosureFromMethodProcessor(@NotNull GrIntroduceParameterSettings helper) { |
| super(helper); |
| final GrStatement[] statements = helper.getStatements(); |
| myDeclarationOwner = statements.length > 0 ? GroovyRefactoringUtil.getDeclarationOwner(statements[0]) : null; |
| myMethod = (GrMethod)myHelper.getToReplaceIn(); |
| } |
| |
| @Override |
| protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) { |
| UsageInfo[] usagesIn = refUsages.get(); |
| MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>(); |
| final GrStatement[] statements = myHelper.getStatements(); |
| |
| for (GrStatement statement : statements) { |
| GroovyIntroduceParameterUtil.detectAccessibilityConflicts(statement, usagesIn, conflicts, false, myProject); |
| } |
| |
| for (UsageInfo info : usagesIn) { |
| if (info instanceof OtherLanguageUsageInfo) { |
| final String lang = CommonRefactoringUtil.htmlEmphasize(info.getElement().getLanguage().getDisplayName()); |
| conflicts.putValue(info.getElement(), GroovyRefactoringBundle.message("cannot.process.usage.in.language.{0}", lang)); |
| } |
| } |
| |
| if (!myMethod.hasModifierProperty(PsiModifier.PRIVATE)) { |
| final AnySupers anySupers = new AnySupers(); |
| for (GrStatement statement : statements) { |
| statement.accept(anySupers); |
| } |
| if (anySupers.containsSupers()) { |
| for (UsageInfo usageInfo : usagesIn) { |
| if (!(usageInfo.getElement() instanceof PsiMethod) && !(usageInfo instanceof InternalUsageInfo)) { |
| if (!PsiTreeUtil.isAncestor(myMethod.getContainingClass(), usageInfo.getElement(), false)) { |
| conflicts.putValue(statements[0], RefactoringBundle |
| .message("parameter.initializer.contains.0.but.not.all.calls.to.method.are.in.its.class", |
| CommonRefactoringUtil.htmlEmphasize(PsiKeyword.SUPER))); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| if (!conflicts.isEmpty() && ApplicationManager.getApplication().isUnitTestMode()) { |
| throw new ConflictsInTestsException(conflicts.values()); |
| } |
| |
| if (!conflicts.isEmpty()) { |
| final ConflictsDialog conflictsDialog = prepareConflictsDialog(conflicts, usagesIn); |
| conflictsDialog.show(); |
| if (!conflictsDialog.isOK()) { |
| if (conflictsDialog.isShowConflicts()) prepareSuccessful(); |
| return false; |
| } |
| } |
| |
| prepareSuccessful(); |
| return true; |
| } |
| |
| @NotNull |
| @Override |
| protected UsageInfo[] findUsages() { |
| List<UsageInfo> result = new ArrayList<UsageInfo>(); |
| |
| final PsiMethod toSearchFor = (PsiMethod)myHelper.getToSearchFor(); |
| |
| for (PsiReference ref1 : MethodReferencesSearch.search(toSearchFor, GlobalSearchScope.projectScope(myProject), true)) { |
| PsiElement ref = ref1.getElement(); |
| if (ref.getLanguage() != GroovyLanguage.INSTANCE) { |
| result.add(new OtherLanguageUsageInfo(ref1)); |
| continue; |
| } |
| |
| if (ref instanceof PsiMethod && ((PsiMethod)ref).isConstructor()) { |
| DefaultConstructorImplicitUsageInfo implicitUsageInfo = |
| new DefaultConstructorImplicitUsageInfo((PsiMethod)ref, ((PsiMethod)ref).getContainingClass(), toSearchFor); |
| result.add(implicitUsageInfo); |
| } |
| else if (ref instanceof PsiClass) { |
| result.add(new NoConstructorClassUsageInfo((PsiClass)ref)); |
| } |
| else if (!PsiTreeUtil.isAncestor(myMethod, ref, false)) { |
| result.add(new ExternalUsageInfo(ref)); |
| } |
| else { |
| result.add(new ChangedMethodCallInfo(ref)); |
| } |
| } |
| |
| Collection<PsiMethod> overridingMethods = OverridingMethodsSearch.search(toSearchFor, true).findAll(); |
| |
| for (PsiMethod overridingMethod : overridingMethods) { |
| result.add(new UsageInfo(overridingMethod)); |
| } |
| |
| final UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]); |
| return UsageViewUtil.removeDuplicatedUsages(usageInfos); |
| } |
| |
| |
| @Override |
| protected void performRefactoring(UsageInfo[] usages) { |
| final IntroduceParameterData data = new IntroduceParameterDataAdapter(); |
| |
| IntroduceParameterUtil.processUsages(usages, data); |
| |
| final PsiMethod toSearchFor = (PsiMethod)myHelper.getToSearchFor(); |
| |
| final boolean methodsToProcessAreDifferent = myMethod != toSearchFor; |
| if (myHelper.generateDelegate()) { |
| GroovyIntroduceParameterUtil.generateDelegate(myMethod, data.getParameterInitializer(), myProject); |
| if (methodsToProcessAreDifferent) { |
| final GrMethod method = GroovyIntroduceParameterUtil.generateDelegate(toSearchFor, data.getParameterInitializer(), myProject); |
| final PsiClass containingClass = method.getContainingClass(); |
| if (containingClass != null && containingClass.isInterface()) { |
| final GrOpenBlock block = method.getBlock(); |
| if (block != null) { |
| block.delete(); |
| } |
| } |
| } |
| } |
| |
| // Changing signature of initial method |
| // (signature of myMethodToReplaceIn will be either changed now or have already been changed) |
| final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(myHelper.getName(), myMethod.getBlock()); |
| IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myMethod), usages, data); |
| if (methodsToProcessAreDifferent) { |
| IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(toSearchFor), usages, data); |
| } |
| |
| // Replacing expression occurrences |
| for (UsageInfo usage : usages) { |
| if (usage instanceof ChangedMethodCallInfo) { |
| PsiElement element = usage.getElement(); |
| |
| GroovyIntroduceParameterUtil.processChangedMethodCall(element, myHelper, myProject); |
| } |
| } |
| |
| final GrStatement newStatement = ExtractUtil.replaceStatement(myDeclarationOwner, myHelper); |
| |
| final Editor editor = PsiUtilBase.findEditor(newStatement); |
| if (editor != null) { |
| PsiDocumentManager.getInstance(myProject).commitDocument(editor.getDocument()); |
| editor.getSelectionModel().removeSelection(); |
| editor.getCaretModel().moveToOffset(newStatement.getTextRange().getEndOffset()); |
| |
| } |
| |
| fieldConflictsResolver.fix(); |
| } |
| |
| private class IntroduceParameterDataAdapter implements IntroduceParameterData { |
| |
| private final GrClosableBlock myClosure; |
| private final GrExpressionWrapper myWrapper; |
| |
| private IntroduceParameterDataAdapter() { |
| myClosure = generateClosure(ExtractClosureFromMethodProcessor.this.myHelper); |
| myWrapper = new GrExpressionWrapper(myClosure); |
| } |
| |
| @NotNull |
| @Override |
| public Project getProject() { |
| return myProject; |
| } |
| |
| @Override |
| public PsiMethod getMethodToReplaceIn() { |
| return myMethod; |
| } |
| |
| @NotNull |
| @Override |
| public PsiMethod getMethodToSearchFor() { |
| return (PsiMethod)myHelper.getToSearchFor(); |
| } |
| |
| @Override |
| public ExpressionWrapper getParameterInitializer() { |
| return myWrapper; |
| } |
| |
| @NotNull |
| @Override |
| public String getParameterName() { |
| return myHelper.getName(); |
| } |
| |
| @Override |
| public int getReplaceFieldsWithGetters() { |
| return IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE; //todo add option to dialog |
| } |
| |
| @Override |
| public boolean isDeclareFinal() { |
| return myHelper.declareFinal(); |
| } |
| |
| @Override |
| public boolean isGenerateDelegate() { |
| return false; //todo |
| } |
| |
| @NotNull |
| @Override |
| public PsiType getForcedType() { |
| PsiType type = myHelper.getSelectedType(); |
| return type != null ? type : PsiType.getJavaLangObject(PsiManager.getInstance(myProject), GlobalSearchScope.allScope(myProject)); |
| } |
| |
| @NotNull |
| @Override |
| public TIntArrayList getParametersToRemove() { |
| return myHelper.parametersToRemove(); |
| } |
| |
| } |
| } |