blob: 758e102cccc326b8fbdc9f4d8ad6fc7917eb2b70 [file] [log] [blame]
/*
* Copyright 2000-2009 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.
*/
/*
* Created by IntelliJ IDEA.
* User: dsl
* Date: 16.04.2002
* Time: 15:37:30
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij.refactoring.makeStatic;
import com.intellij.lang.findUsages.DescriptiveNameUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.listeners.RefactoringEventData;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.ConflictsUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public abstract class MakeMethodOrClassStaticProcessor<T extends PsiTypeParameterListOwner> extends BaseRefactoringProcessor {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.makeMethodStatic.MakeMethodStaticProcessor");
protected T myMember;
protected Settings mySettings;
public MakeMethodOrClassStaticProcessor(Project project,
T member,
Settings settings) {
super(project);
myMember = member;
mySettings = settings;
}
@NotNull
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
return new MakeMethodOrClassStaticViewDescriptor(myMember);
}
@Nullable
@Override
protected String getRefactoringId() {
return "refactoring.makeStatic";
}
@Nullable
@Override
protected RefactoringEventData getBeforeData() {
RefactoringEventData data = new RefactoringEventData();
data.addElement(myMember);
return data;
}
@Nullable
@Override
protected RefactoringEventData getAfterData(UsageInfo[] usages) {
RefactoringEventData data = new RefactoringEventData();
data.addElement(myMember);
return data;
}
protected final boolean preprocessUsages(final Ref<UsageInfo[]> refUsages) {
UsageInfo[] usagesIn = refUsages.get();
if (myPrepareSuccessfulSwingThreadCallback != null) {
MultiMap<PsiElement, String> conflicts = getConflictDescriptions(usagesIn);
if (conflicts.size() > 0) {
ConflictsDialog conflictsDialog = prepareConflictsDialog(conflicts, refUsages.get());
conflictsDialog.show();
if (!conflictsDialog.isOK()) {
if (conflictsDialog.isShowConflicts()) prepareSuccessful();
return false;
}
}
if(!mySettings.isChangeSignature()) {
refUsages.set(filterInternalUsages(usagesIn));
}
}
refUsages.set(filterOverriding(usagesIn));
prepareSuccessful();
return true;
}
private static UsageInfo[] filterOverriding(UsageInfo[] usages) {
ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
for (UsageInfo usage : usages) {
if (!(usage instanceof OverridingMethodUsageInfo)) {
result.add(usage);
}
}
return result.toArray(new UsageInfo[result.size()]);
}
private static UsageInfo[] filterInternalUsages(UsageInfo[] usages) {
ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
for (UsageInfo usage : usages) {
if (!(usage instanceof InternalUsageInfo)) {
result.add(usage);
}
}
return result.toArray(new UsageInfo[result.size()]);
}
protected MultiMap<PsiElement,String> getConflictDescriptions(UsageInfo[] usages) {
MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
HashSet<PsiElement> processed = new HashSet<PsiElement>();
String typeString = StringUtil.capitalize(UsageViewUtil.getType(myMember));
for (UsageInfo usageInfo : usages) {
if (usageInfo instanceof InternalUsageInfo && !(usageInfo instanceof SelfUsageInfo)) {
PsiElement referencedElement = ((InternalUsageInfo)usageInfo).getReferencedElement();
if (!mySettings.isMakeClassParameter()) {
if (referencedElement instanceof PsiModifierListOwner) {
if (((PsiModifierListOwner)referencedElement).hasModifierProperty(PsiModifier.STATIC)) {
continue;
}
}
if (processed.contains(referencedElement)) continue;
processed.add(referencedElement);
if (referencedElement instanceof PsiField) {
PsiField field = (PsiField)referencedElement;
if (mySettings.getNameForField(field) == null) {
String message = RefactoringBundle.message("0.uses.non.static.1.which.is.not.passed.as.a.parameter", typeString,
RefactoringUIUtil.getDescription(field, true));
conflicts.putValue(field, message);
}
}
else {
String message = RefactoringBundle.message("0.uses.1.which.needs.class.instance", typeString, RefactoringUIUtil.getDescription(referencedElement, true));
conflicts.putValue(referencedElement, message);
}
}
}
if (usageInfo instanceof OverridingMethodUsageInfo) {
LOG.assertTrue(myMember instanceof PsiMethod);
final PsiMethod overridingMethod = ((PsiMethod)usageInfo.getElement());
String message = RefactoringBundle.message("method.0.is.overridden.by.1", RefactoringUIUtil.getDescription(myMember, false),
RefactoringUIUtil.getDescription(overridingMethod, true));
conflicts.putValue(overridingMethod, message);
}
else {
PsiElement element = usageInfo.getElement();
PsiElement container = ConflictsUtil.getContainer(element);
if (processed.contains(container)) continue;
processed.add(container);
List<Settings.FieldParameter> fieldParameters = mySettings.getParameterOrderList();
ArrayList<PsiField> inaccessible = new ArrayList<PsiField>();
for (final Settings.FieldParameter fieldParameter : fieldParameters) {
if (!PsiUtil.isAccessible(fieldParameter.field, element, null)) {
inaccessible.add(fieldParameter.field);
}
}
if (inaccessible.isEmpty()) continue;
createInaccessibleFieldsConflictDescription(inaccessible, container, conflicts);
}
}
return conflicts;
}
private static void createInaccessibleFieldsConflictDescription(ArrayList<PsiField> inaccessible, PsiElement container,
MultiMap<PsiElement, String> conflicts) {
if (inaccessible.size() == 1) {
final PsiField field = inaccessible.get(0);
conflicts.putValue(field, RefactoringBundle.message("field.0.is.not.accessible",
CommonRefactoringUtil.htmlEmphasize(field.getName()),
RefactoringUIUtil.getDescription(container, true)));
} else {
for (int j = 0; j < inaccessible.size(); j++) {
PsiField field = inaccessible.get(j);
conflicts.putValue(field, RefactoringBundle.message("field.0.is.not.accessible",
CommonRefactoringUtil.htmlEmphasize(field.getName()),
RefactoringUIUtil.getDescription(container, true)));
}
}
}
@NotNull
protected UsageInfo[] findUsages() {
ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
ContainerUtil.addAll(result, MakeStaticUtil.findClassRefsInMember(myMember, true));
if (mySettings.isReplaceUsages()) {
findExternalUsages(result);
}
if (myMember instanceof PsiMethod) {
final PsiMethod[] overridingMethods =
OverridingMethodsSearch.search((PsiMethod)myMember, myMember.getUseScope(), false).toArray(PsiMethod.EMPTY_ARRAY);
for (PsiMethod overridingMethod : overridingMethods) {
if (overridingMethod != myMember) {
result.add(new OverridingMethodUsageInfo(overridingMethod));
}
}
}
return result.toArray(new UsageInfo[result.size()]);
}
protected abstract void findExternalUsages(ArrayList<UsageInfo> result);
protected void findExternalReferences(final PsiMethod method, final ArrayList<UsageInfo> result) {
for (PsiReference ref : ReferencesSearch.search(method)) {
PsiElement element = ref.getElement();
PsiElement qualifier = null;
if (element instanceof PsiReferenceExpression) {
qualifier = ((PsiReferenceExpression)element).getQualifierExpression();
if (qualifier instanceof PsiThisExpression) qualifier = null;
}
if (!PsiTreeUtil.isAncestor(myMember, element, true) || qualifier != null) {
result.add(new UsageInfo(element));
}
}
}
//should be called before setting static modifier
protected void setupTypeParameterList() throws IncorrectOperationException {
final PsiTypeParameterList list = myMember.getTypeParameterList();
assert list != null;
final PsiTypeParameterList newList = RefactoringUtil.createTypeParameterListWithUsedTypeParameters(myMember);
if (newList != null) {
list.replace(newList);
}
}
protected boolean makeClassParameterFinal(UsageInfo[] usages) {
for (UsageInfo usage : usages) {
if (usage instanceof InternalUsageInfo) {
final InternalUsageInfo internalUsageInfo = (InternalUsageInfo)usage;
PsiElement referencedElement = internalUsageInfo.getReferencedElement();
if (!(referencedElement instanceof PsiField)
|| mySettings.getNameForField((PsiField)referencedElement) == null) {
if (internalUsageInfo.isInsideAnonymous()) {
return true;
}
}
}
}
return false;
}
protected static boolean makeFieldParameterFinal(PsiField field, UsageInfo[] usages) {
for (UsageInfo usage : usages) {
if (usage instanceof InternalUsageInfo) {
final InternalUsageInfo internalUsageInfo = (InternalUsageInfo)usage;
PsiElement referencedElement = internalUsageInfo.getReferencedElement();
if (referencedElement instanceof PsiField && field.equals(referencedElement)) {
if (internalUsageInfo.isInsideAnonymous()) {
return true;
}
}
}
}
return false;
}
protected String getCommandName() {
return RefactoringBundle.message("make.static.command", DescriptiveNameUtil.getDescriptiveName(myMember));
}
public T getMember() {
return myMember;
}
public Settings getSettings() {
return mySettings;
}
protected void performRefactoring(UsageInfo[] usages) {
PsiManager manager = myMember.getManager();
PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
try {
for (UsageInfo usage : usages) {
if (usage instanceof SelfUsageInfo) {
changeSelfUsage((SelfUsageInfo)usage);
}
else if (usage instanceof InternalUsageInfo) {
changeInternalUsage((InternalUsageInfo)usage, factory);
}
else {
changeExternalUsage(usage, factory);
}
}
changeSelf(factory, usages);
}
catch (IncorrectOperationException ex) {
LOG.assertTrue(false);
}
}
protected abstract void changeSelf(PsiElementFactory factory, UsageInfo[] usages) throws IncorrectOperationException;
protected abstract void changeSelfUsage(SelfUsageInfo usageInfo) throws IncorrectOperationException;
protected abstract void changeInternalUsage(InternalUsageInfo usage, PsiElementFactory factory) throws IncorrectOperationException;
protected abstract void changeExternalUsage(UsageInfo usage, PsiElementFactory factory) throws IncorrectOperationException;
}