extract enum: change types accordingly (switch cases)

This commit is contained in:
anna
2010-06-09 17:32:55 +04:00
parent dba6215b4b
commit bcdcfd764c
36 changed files with 865 additions and 52 deletions
@@ -0,0 +1,65 @@
/*
* Copyright 2000-2010 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.
*/
/*
* User: anna
* Date: 07-Jun-2010
*/
package com.intellij.refactoring.extractclass;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiSearchScopeUtil;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.rules.TypeConversionRule;
public class EnumTypeConversionRule extends TypeConversionRule {
@Override
public TypeConversionDescriptorBase findConversion(PsiType from,
PsiType to,
PsiMember member,
PsiExpression context,
TypeMigrationLabeler labeler) {
final PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(context, PsiMethodCallExpression.class, false);
if (callExpression != null) {
final PsiMethod resolved = callExpression.resolveMethod();
if (resolved != null) {
final SearchScope searchScope = labeler.getRules().getSearchScope();
if (!PsiSearchScopeUtil.isInScope(searchScope, resolved)) {
return null;
}
}
}
final PsiClass toClass = PsiUtil.resolveClassInType(to);
if (toClass != null && toClass.isEnum()) {
final PsiMethod[] constructors = toClass.getConstructors();
if (constructors.length == 1) {
final PsiMethod constructor = constructors[0];
final PsiParameter[] parameters = constructor.getParameterList().getParameters();
if (parameters.length == 1) {
if (TypeConversionUtil.isAssignable(parameters[0].getType(), from)) {
return new TypeConversionDescriptorBase();
}
}
}
}
return null;
}
}
@@ -342,7 +342,7 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
private static boolean isConstantField(PsiMember member) {
return member instanceof PsiField &&
member.hasModifierProperty(PsiModifier.STATIC) &&
member.hasModifierProperty(PsiModifier.FINAL) &&
// member.hasModifierProperty(PsiModifier.FINAL) &&
((PsiField)member).hasInitializer();
}
@@ -16,10 +16,12 @@
package com.intellij.refactoring.extractclass;
import com.intellij.ide.util.PackageUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
@@ -43,7 +45,6 @@ import com.intellij.refactoring.psi.MethodInheritanceUtils;
import com.intellij.refactoring.psi.TypeParametersVisitor;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.classMembers.MemberInfo;
import com.intellij.usageView.UsageInfo;
@@ -73,6 +74,8 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
private final String delegateFieldName;
private final boolean requiresBackpointer;
private boolean delegationRequired = false;
private ExtractEnumProcessor myExtractEnumProcessor;
private PsiClass myClass;
public ExtractClassProcessor(PsiClass sourceClass,
List<PsiField> fields,
@@ -122,11 +125,25 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
}
typeParams.addAll(typeParamSet);
}
myClass = ApplicationManager.getApplication().runWriteAction(
new Computable<PsiClass>() {
public PsiClass compute() {
return buildClass();
}
}
);
myExtractEnumProcessor = new ExtractEnumProcessor(myProject, this.enumConstants, fields, myClass);
}
@Override
protected boolean preprocessUsages(final Ref<UsageInfo[]> refUsages) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
myExtractEnumProcessor.findEnumConstantConflicts(refUsages, conflicts);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
myClass.delete();
}
});
final Project project = sourceClass.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final PsiClass existingClass =
@@ -153,21 +170,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
conflicts.putValue(field, "Field \'" + field.getName() + "\' needs setter");
}
}
for (final PsiField enumConstant : enumConstants) {
final PsiExpression initializer = enumConstant.getInitializer();
assert initializer != null;
initializer.accept(new JavaRecursiveElementWalkingVisitor(){
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement resolved = expression.resolve();
if (!enumConstants.contains(resolved) && fields.contains(resolved)) {
conflicts.putValue(initializer, "Enum constant " + RefactoringUIUtil.getDescription(enumConstant, false) +
" would forward reference on field " + RefactoringUIUtil.getDescription(resolved, false));
}
}
});
}
checkConflicts(refUsages, conflicts);
return showConflicts(conflicts, refUsages.get());
}
@@ -245,6 +248,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
if (delegationRequired) {
buildDelegate();
}
myExtractEnumProcessor.performEnumConstantTypeMigration(usageInfos);
final Set<PsiMember> members = new HashSet<PsiMember>();
for (PsiMethod method : methods) {
final PsiMethod member = psiClass.findMethodBySignature(method, false);
@@ -470,6 +474,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
findUsagesForField(field, usages);
usages.add(new RemoveField(field));
}
usages.addAll(myExtractEnumProcessor.findEnumConstantUsages(new ArrayList<FixableUsageInfo>(usages)));
for (PsiClass innerClass : innerClasses) {
findUsagesForInnerClass(innerClass, usages);
usages.add(new RemoveInnerClass(innerClass));
@@ -0,0 +1,229 @@
/*
* Copyright 2000-2010 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.
*/
/*
* User: anna
* Date: 08-Jun-2010
*/
package com.intellij.refactoring.extractclass;
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.GlobalSearchScope;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.extractclass.usageInfo.ReplaceStaticVariableAccess;
import com.intellij.refactoring.psi.MutationUtils;
import com.intellij.refactoring.typeMigration.TypeMigrationProcessor;
import com.intellij.refactoring.typeMigration.TypeMigrationRules;
import com.intellij.refactoring.util.EnumConstantsUtil;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.MultiMap;
import java.util.*;
public class ExtractEnumProcessor {
private Project myProject;
private List<PsiField> myEnumConstants;
private PsiClass myClass;
private TypeMigrationProcessor myTypeMigrationProcessor;
private List<PsiField> myFields;
public ExtractEnumProcessor(Project project, List<PsiField> enumConstants, List<PsiField> fields, PsiClass aClass) {
myProject = project;
myEnumConstants = enumConstants;
myFields = fields;
myClass = aClass;
}
public void findEnumConstantConflicts(final Ref<UsageInfo[]> refUsages, final MultiMap<PsiElement, String> conflicts) {
if (hasUsages2Migrate()) {
final List<UsageInfo> resolvableConflicts = new ArrayList<UsageInfo>();
for (UsageInfo failedUsage : myTypeMigrationProcessor.getLabeler().getFailedUsages()) {
final PsiElement element = failedUsage.getElement();
if (element instanceof PsiReferenceExpression) {
resolvableConflicts.add(new FixableUsageInfo(element) {
@Override
public void fixUsage() throws IncorrectOperationException {
final PsiReferenceExpression expression = (PsiReferenceExpression)element;
final String link = PropertyUtil.suggestGetterName("value", expression.getType()) + "()";
MutationUtils.replaceExpression(expression.getReferenceName() + "." + link, expression);
}
});
continue;
}
conflicts.putValue(element, "Failed to migrate");
}
if (!resolvableConflicts.isEmpty()) {
final List<UsageInfo> usageInfos = new ArrayList<UsageInfo>(Arrays.asList(refUsages.get()));
for (Iterator<UsageInfo> iterator = resolvableConflicts.iterator(); iterator.hasNext();) {
final UsageInfo conflict = iterator.next();
for (UsageInfo usageInfo : usageInfos) {
if (conflict.getElement() == usageInfo.getElement()) {
iterator.remove();
break;
}
}
}
resolvableConflicts.addAll(0, usageInfos);
refUsages.set(resolvableConflicts.toArray(new UsageInfo[resolvableConflicts.size()]));
}
}
for (final PsiField enumConstant : myEnumConstants) {
final PsiExpression initializer = enumConstant.getInitializer();
assert initializer != null;
initializer.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement resolved = expression.resolve();
if (!myEnumConstants.contains(resolved) && myFields.contains(resolved)) {
conflicts.putValue(initializer, "Enum constant " + RefactoringUIUtil.getDescription(enumConstant, false) +
" would forward reference on field " + RefactoringUIUtil.getDescription(resolved, false));
}
}
});
}
}
private boolean hasUsages2Migrate() {
return myTypeMigrationProcessor != null;
}
public List<FixableUsageInfo> findEnumConstantUsages(List<FixableUsageInfo> fieldUsages) {
final List<FixableUsageInfo> result = new ArrayList<FixableUsageInfo>();
if (!myEnumConstants.isEmpty()) {
final Set<PsiSwitchStatement> switchStatements = new HashSet<PsiSwitchStatement>();
for (UsageInfo usage : fieldUsages) {
if (usage instanceof ReplaceStaticVariableAccess) {
final PsiElement element = usage.getElement();
final PsiSwitchStatement switchStatement = PsiTreeUtil.getParentOfType(element, PsiSwitchStatement.class);
if (switchStatement != null) {
switchStatements.add(switchStatement);
}
}
}
final PsiConstantEvaluationHelper evaluationHelper =
JavaPsiFacade.getInstance(myProject).getConstantEvaluationHelper();
final Set<Object> enumValues = new HashSet<Object>();
for (PsiField enumConstant : myEnumConstants) {
enumValues.add(evaluationHelper.computeConstantExpression(enumConstant.getInitializer()));
}
final PsiType enumValueType = myEnumConstants.get(0).getType();
final Set<PsiElement> toMigrate = new HashSet<PsiElement>();
for (PsiSwitchStatement switchStatement : switchStatements) {
final PsiStatement errStatement = EnumConstantsUtil.isEnumSwitch(switchStatement, enumValueType, enumValues);
if (errStatement != null) {
String description = null;
if (errStatement instanceof PsiSwitchLabelStatement) {
final PsiExpression caseValue = ((PsiSwitchLabelStatement)errStatement).getCaseValue();
if (caseValue != null) {
description = caseValue.getText() + " can't be replaced with enum";
}
}
result.add(new ConflictUsageInfo(errStatement, description));
}
else {
final PsiExpression expression = switchStatement.getExpression();
if (expression instanceof PsiReferenceExpression) {
final PsiElement element = ((PsiReferenceExpression)expression).resolve();
if (element != null) {
if (!element.getManager().isInProject(element)) {
result.add(new ConflictUsageInfo(expression, StringUtil.capitalize(RefactoringUIUtil.getDescription(element, false)) + " is out of project"));
}
else {
toMigrate.add(element);
}
}
}
else {
result.add(new ConflictUsageInfo(expression, null));
}
}
}
if (!toMigrate.isEmpty()) {
final TypeMigrationRules rules = new TypeMigrationRules(this.myEnumConstants.get(0).getType());
rules.addConversionDescriptor(new EnumTypeConversionRule());
rules.setMigrationRootType(
JavaPsiFacade.getElementFactory(myProject).createType(myClass));
rules.setBoundScope(GlobalSearchScope.projectScope(myProject));
myTypeMigrationProcessor = new TypeMigrationProcessor(myProject, toMigrate.toArray(new PsiElement[toMigrate.size()]), rules);
for (UsageInfo usageInfo : myTypeMigrationProcessor.findUsages()) {
result.add(new EnumTypeMigrationUsageInfo(usageInfo));
}
}
}
return result;
}
public void performEnumConstantTypeMigration(UsageInfo[] usageInfos) {
if (hasUsages2Migrate()) {
final List<UsageInfo> migrationInfos = new ArrayList<UsageInfo>();
for (UsageInfo usageInfo : usageInfos) {
if (usageInfo instanceof EnumTypeMigrationUsageInfo) {
migrationInfos.add(((EnumTypeMigrationUsageInfo)usageInfo).getUsageInfo());
}
}
myTypeMigrationProcessor.performRefactoring(migrationInfos.toArray(new UsageInfo[migrationInfos.size()]));
}
}
private static class EnumTypeMigrationUsageInfo extends FixableUsageInfo {
private UsageInfo myUsageInfo;
public EnumTypeMigrationUsageInfo(UsageInfo usageInfo) {
super(usageInfo.getElement());
myUsageInfo = usageInfo;
}
@Override
public void fixUsage() throws IncorrectOperationException {
}
public UsageInfo getUsageInfo() {
return myUsageInfo;
}
}
private static class ConflictUsageInfo extends FixableUsageInfo {
private final String myDescription;
public ConflictUsageInfo(PsiElement expression, String description) {
super(expression);
myDescription = description;
}
@Override
public void fixUsage() throws IncorrectOperationException {
}
@Override
public String getConflictMessage() {
return "Unable to migrate statement to enum constant." + (myDescription != null ? " " + myDescription : "");
}
}
}
@@ -259,7 +259,7 @@ class ExtractedClassBuilder {
if (hasEnumConstants()) {
out.append(StringUtil.join(enumConstantFields, new Function<PsiField, String>() {
public String fun(PsiField field) {
final StringBuffer fieldStr = new StringBuffer(field.getName().toUpperCase() + "(");
final StringBuffer fieldStr = new StringBuffer(field.getName() + "(");
final PsiExpression initializer = field.getInitializer();
if (initializer != null) {
initializer.accept(new Mutator(fieldStr));
@@ -411,7 +411,7 @@ class ExtractedClassBuilder {
final String name = field.getName();
if (enumConstantFields.contains(field)) {
out.append(name.toUpperCase()).append(".").append(getterName()).append("()");
out.append(name).append(".").append(getterName()).append("()");
} else {
if (qualifier != null && name.equals(expression.getReferenceName())) {
out.append("this.");
@@ -15,26 +15,63 @@
*/
package com.intellij.refactoring.extractclass.usageInfo;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.psi.MutationUtils;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
public class ReplaceStaticVariableAccess extends FixableUsageInfo {
private final PsiReferenceExpression expression;
private final String delegateClass;
private final PsiReferenceExpression expression;
private final String delegateClass;
private final boolean myEnumConstant;
private static final Logger LOGGER = Logger.getInstance("#" + ReplaceStaticVariableAccess.class.getName());
public ReplaceStaticVariableAccess(PsiReferenceExpression expression, String delegateClass, boolean enumConstant) {
super(expression);
this.expression = expression;
this.delegateClass = delegateClass;
super(expression);
this.expression = expression;
this.delegateClass = delegateClass;
myEnumConstant = enumConstant;
}
public void fixUsage() throws IncorrectOperationException {
MutationUtils.replaceExpression(delegateClass + '.' + expression.getReferenceName() + (myEnumConstant ? "." + PropertyUtil.suggestGetterName("value", expression.getType())+
"()" : ""), expression);
public void fixUsage() throws IncorrectOperationException {
if (myEnumConstant) {
final PsiSwitchLabelStatement switchStatement = PsiTreeUtil.getParentOfType(expression, PsiSwitchLabelStatement.class);
if (switchStatement != null) {
MutationUtils.replaceExpression(expression.getReferenceName(), expression);
return;
}
}
boolean replaceWithGetEnumValue = myEnumConstant;
if (replaceWithGetEnumValue) {
final PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class);
if (callExpression != null) {
final PsiElement resolved = callExpression.getMethodExpression().resolve();
if (resolved instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)resolved).getParameterList().getParameters();
final PsiExpression[] args = callExpression.getArgumentList().getExpressions();
final int idx = ArrayUtil.find(args, expression);
if (idx != -1 && parameters[idx].getType().getCanonicalText().equals(delegateClass)) {
replaceWithGetEnumValue = false;
}
}
}
else {
final PsiReturnStatement returnStatement = PsiTreeUtil.getParentOfType(expression, PsiReturnStatement.class);
if (returnStatement != null) {
final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(expression, PsiMethod.class);
LOGGER.assertTrue(psiMethod != null);
final PsiType returnType = psiMethod.getReturnType();
if (returnType != null && returnType.getCanonicalText().equals(delegateClass)) {
replaceWithGetEnumValue = false;
}
}
}
}
final String link = replaceWithGetEnumValue ? "." + PropertyUtil.suggestGetterName("value", expression.getType()) + "()" : "";
MutationUtils.replaceExpression(delegateClass + '.' + expression.getReferenceName() + link, expression);
}
}
@@ -204,10 +204,10 @@ public class TypeMigrationLabeler {
return myConversions.get(element);
}
public TypeMigrationUsageInfo[] getMigratedUsages(final PsiElement element, boolean autoMigrate) {
public TypeMigrationUsageInfo[] getMigratedUsages(boolean autoMigrate, final PsiElement... roots) {
if (myMigratedUsages == null) {
myShowWarning = autoMigrate;
migrate(element, autoMigrate);
migrate(autoMigrate, roots);
myMigratedUsages = getMigratedUsages();
}
return myMigratedUsages;
@@ -695,11 +695,15 @@ public class TypeMigrationLabeler {
}
}
private void migrate(final PsiElement victim, boolean autoMigrate) {
private void migrate(boolean autoMigrate, final PsiElement... victims) {
myMigrationRoots = new LinkedList<Pair<TypeMigrationUsageInfo, PsiType>>();
myTypeEvaluator = new TypeEvaluator(myMigrationRoots, this);
addMigrationRoot(victim, myRules.getMigrationRootType(), null, false, true, true);
final PsiType rootType = myRules.getMigrationRootType();
for (PsiElement victim : victims) {
addMigrationRoot(victim, rootType, null, false, true, true);
}
if (autoMigrate) {
while (myMigrationRoots.size() > 0) {
@@ -23,22 +23,26 @@ import java.util.List;
public class TypeMigrationProcessor extends BaseRefactoringProcessor {
private PsiElement myRoot;
private PsiElement[] myRoot;
private final TypeMigrationRules myRules;
private TypeMigrationLabeler myLabeler;
public TypeMigrationProcessor(final Project project, final PsiElement root, final TypeMigrationRules rules) {
this(project, new PsiElement[]{root}, rules);
}
public TypeMigrationProcessor(final Project project, final PsiElement[] roots, final TypeMigrationRules rules) {
super(project);
myRoot = root;
myRoot = roots;
myRules = rules;
}
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
return new TypeMigrationViewDescriptor(myRoot);
return new TypeMigrationViewDescriptor(myRoot[0]);
}
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
if (myLabeler.hasFailedConversions()) {
if (hasFailedConversions()) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new RuntimeException(StringUtil.join(myLabeler.getFailedConversionsReport(), "\n"));
}
@@ -57,18 +61,22 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor {
return true;
}
public boolean hasFailedConversions() {
return myLabeler.hasFailedConversions();
}
@Override
protected void previewRefactoring(final UsageInfo[] usages) {
MigrationPanel panel = new MigrationPanel(myRoot, myLabeler, myProject, isPreviewUsages());
MigrationPanel panel = new MigrationPanel(myRoot[0], myLabeler, myProject, isPreviewUsages());
String text;
if (myRoot instanceof PsiField) {
text = "field \'" + ((PsiField)myRoot).getName() + "\'";
} else if (myRoot instanceof PsiParameter) {
text = "parameter \'" + ((PsiParameter)myRoot).getName() + "\'";
} else if (myRoot instanceof PsiLocalVariable) {
text = "variable \'" + ((PsiLocalVariable)myRoot).getName() + "\'";
} else if (myRoot instanceof PsiMethod) {
text = "method \'" + ((PsiMethod)myRoot).getName() + "\' return";
if (myRoot[0] instanceof PsiField) {
text = "field \'" + ((PsiField)myRoot[0]).getName() + "\'";
} else if (myRoot[0] instanceof PsiParameter) {
text = "parameter \'" + ((PsiParameter)myRoot[0]).getName() + "\'";
} else if (myRoot[0] instanceof PsiLocalVariable) {
text = "variable \'" + ((PsiLocalVariable)myRoot[0]).getName() + "\'";
} else if (myRoot[0] instanceof PsiMethod) {
text = "method \'" + ((PsiMethod)myRoot[0]).getName() + "\' return";
} else {
text = myRoot.toString();
}
@@ -76,7 +84,7 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor {
.addContent("Migrate Type of " +
text +
" from \'" +
TypeMigrationLabeler.getElementType(myRoot).getPresentableText() +
TypeMigrationLabeler.getElementType(myRoot[0]).getPresentableText() +
"\' to \'" +
myRules.getMigrationRootType().getPresentableText() +
"\'", false, panel, true, true);
@@ -85,23 +93,23 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor {
}
@NotNull
protected UsageInfo[] findUsages() {
public UsageInfo[] findUsages() {
myLabeler = new TypeMigrationLabeler(myRules);
try {
return myLabeler.getMigratedUsages(myRoot, !isPreviewUsages());
return myLabeler.getMigratedUsages(!isPreviewUsages(), myRoot);
}
catch (TypeMigrationLabeler.MigrateException e) {
setPreviewUsages(true);
return myLabeler.getMigratedUsages(myRoot, false);
return myLabeler.getMigratedUsages(false, myRoot);
}
}
protected void refreshElements(PsiElement[] elements) {
myRoot = elements[0];
myRoot = elements;
}
protected void performRefactoring(UsageInfo[] usages) {
public void performRefactoring(UsageInfo[] usages) {
change(myLabeler, usages);
}
@@ -87,8 +87,11 @@ public class TypeMigrationReplacementUtil {
}
}
static void migratePsiMemeberType(final PsiElement element, final Project project, final PsiType migratedType) {
static void migratePsiMemeberType(final PsiElement element, final Project project, PsiType migratedType) {
try {
if (!migratedType.isValid()) {
migratedType = JavaPsiFacade.getElementFactory(project).createTypeByFQClassName(migratedType.getCanonicalText());
}
final PsiTypeElement typeElement =
JavaPsiFacade.getInstance(project).getElementFactory().createTypeElement(migratedType);
if (element instanceof PsiMethod) {
@@ -102,6 +102,22 @@ class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
}
}
@Override
public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) {
super.visitSwitchLabelStatement(statement);
final PsiExpression caseValue = statement.getCaseValue();
if (caseValue != null) {
final TypeView typeView = new TypeView(caseValue);
if (typeView.isChanged()) {
final PsiSwitchStatement switchStatement = statement.getEnclosingSwitchStatement();
if (switchStatement != null) {
final PsiExpression expression = switchStatement.getExpression();
myLabeler.migrateExpressionType(expression, typeView.getType(), myStatement, false, false);
}
}
}
}
@Override
public void visitInstanceOfExpression(final PsiInstanceOfExpression expression) {
super.visitInstanceOfExpression(expression);
@@ -23,6 +23,9 @@ package com.intellij.refactoring.util;
import com.intellij.psi.*;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public class EnumConstantsUtil {
private EnumConstantsUtil() {
@@ -48,4 +51,31 @@ public class EnumConstantsUtil {
public static PsiEnumConstant createEnumConstant(PsiClass enumClass, PsiLocalVariable local, final String fieldName) throws IncorrectOperationException {
return createEnumConstant(enumClass, fieldName, local.getInitializer());
}
@Nullable
public static PsiStatement isEnumSwitch(final PsiSwitchStatement switchStatement,
final PsiType enumValueType,
final Set<Object> enumValues) {
final PsiExpression expression = switchStatement.getExpression();
if (expression != null) {
final PsiType expressionType = expression.getType();
if (expressionType != null && !TypeConversionUtil.isAssignable(expressionType, enumValueType)) {
return switchStatement;
}
final PsiConstantEvaluationHelper evaluationHelper =
JavaPsiFacade.getInstance(expression.getProject()).getConstantEvaluationHelper();
final PsiCodeBlock body = switchStatement.getBody();
if (body != null) {
for (PsiStatement statement : body.getStatements()) {
if (statement instanceof PsiSwitchLabelStatement) {
final PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)statement;
final Object caseValue = evaluationHelper.computeConstantExpression(labelStatement.getCaseValue());
if (caseValue != null && !enumValues.contains(caseValue)) return statement;
}
}
return null;
}
}
return switchStatement;
}
}
@@ -0,0 +1,18 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
int max = Math.max(FOO, BAR);
foo(max);
}
}
@@ -0,0 +1,18 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
int max = Math.max(FOO, BAR);
foo(max);
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i * i, i + i);
switch (k) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i * i, i + i);
switch (k) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,13 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (args.length) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,13 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (args.length) {
case FOO:
break;
case BAR:
break;
}
}
}
@@ -0,0 +1,17 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (boo(args)) {
case FOO:
break;
case BAR:
break;
}
}
int boo(String[] args) {
return args.length;
}
}
@@ -0,0 +1,17 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo(String[] args) {
switch (boo(args)) {
case FOO:
break;
case BAR:
break;
}
}
int boo(String[] args) {
return args.length;
}
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(1);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,16 @@
public class Test {
void foo(EEnum i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
foo(EEnum.FOO);
foo(EEnum.BAR);
}
}
@@ -0,0 +1,18 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
foo(FOO);
foo(BAR);
}
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(1);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,12 @@
public class Test {
void foo(EEnum i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i.getValue() * i.getValue(), i.getValue() + i.getValue());
}
}
@@ -0,0 +1,14 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
int k = Math.max(i * i, i + i);
}
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(1);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,19 @@
public class Test {
void foo() {
EEnum i = foobar(false);
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
EEnum foobar(boolean flag) {
if (flag) {
return EEnum.FOO;
}
return EEnum.BAR;
}
}
@@ -0,0 +1,21 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo() {
int i = foobar(false);
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
int foobar(boolean flag) {
if (flag) {
return FOO;
}
return BAR;
}
}
@@ -0,0 +1,12 @@
public enum EEnum {
FOO(0), BAR(2);
private int value;
public int getValue() {
return value;
}
EEnum(int value) {
this.value = value;
}
}
@@ -0,0 +1,5 @@
class Test {
void foo() {
System.out.println(EEnum.FOO.getValue());
}
}
@@ -0,0 +1,15 @@
class Usage {
void foo(EEnum i) {
switch (i) {
case FOO:
break;
case BAR:
break;
}
}
void foobar() {
foo(EEnum.FOO);
foo(EEnum.BAR);
}
}
@@ -0,0 +1,7 @@
class Test {
public static final int FOO = 0;
public static final int BAR = 2;
void foo() {
System.out.println(FOO);
}
}
@@ -0,0 +1,15 @@
class Usage {
void foo(int i) {
switch (i) {
case Test.FOO:
break;
case Test.BAR:
break;
}
}
void foobar() {
foo(Test.FOO);
foo(Test.BAR);
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
case 8:
break;
}
}
void foobar() {
foo(FOO);
foo(BAR);
}
}
@@ -0,0 +1,20 @@
public class Test {
public static final int FOO = 0;
public static final int BAR = 1;
void foo(int i) {
switch (i) {
case FOO:
break;
case BAR:
break;
case 8:
break;
}
}
void foobar() {
foo(FOO);
foo(BAR);
}
}
@@ -70,6 +70,56 @@ public class ExtractEnumTest extends MultiFileTestCase {
new RefactoringTestUtil.MemberDescriptor("value", PsiField.class, false));
}
public void testChangeMethodParameter() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testCantChangeMethodParameter() throws Exception {
doTest("Failed to migrate", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testCantChangeMethodParameter1() throws Exception {
doTest("Failed to migrate", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testChangeReturnType() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testCantChangeReturnType() throws Exception {
doTest("Unable to migrate statement to enum constant. Field &lt;b&gt;&lt;code&gt;length&lt;/code&gt;&lt;/b&gt; is out of project", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testCantChangeReturnType1() throws Exception {
doTest("Unable to migrate statement to enum constant.", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testChangeMethodParameterAndReplaceOtherUsages() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testReferencesOnEnumConstantElsewhere() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
public void testUnknownSwitchLabel() throws Exception {
doTest("Unable to migrate statement to enum constant. 8 can&apos;t be replaced with enum", false,
new RefactoringTestUtil.MemberDescriptor("FOO", PsiField.class, true),
new RefactoringTestUtil.MemberDescriptor("BAR", PsiField.class, true));
}
private void doTest(final RefactoringTestUtil.MemberDescriptor... memberDescriptors) throws Exception {
doTest(null, false, memberDescriptors);
}