type migration: migration type information moved from TypeMigrationRules to corresponding Labeler and RefactoringProcessor

This commit is contained in:
Dmitry Batkovich
2015-12-10 15:15:31 +03:00
parent 432fe065e6
commit 1d5c2ecf39
18 changed files with 80 additions and 98 deletions
@@ -352,9 +352,9 @@ public class MethodReturnTypeFix extends LocalQuickFixAndIntentionActionOnPsiEle
final TypeMigrationRules rules = new TypeMigrationRules();
final PsiSubstitutor compoundSubstitutor =
TypeConversionUtil.getSuperClassSubstitutor(superClass, derivedClass, PsiSubstitutor.EMPTY).putAll(psiSubstitutor);
rules.setMigrationRootType(JavaPsiFacade.getElementFactory(project).createType(baseClass, compoundSubstitutor));
rules.setBoundScope(new LocalSearchScope(derivedClass));
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, referenceParameterList);
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, referenceParameterList,
JavaPsiFacade.getElementFactory(project).createType(baseClass, compoundSubstitutor));
return false;
}
@@ -72,10 +72,9 @@ public class VariableTypeFromCallFix implements IntentionAction {
@Override
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(myExpressionType);
rules.setBoundScope(PsiSearchHelper.SERVICE.getInstance(project).getUseScope(myVar));
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, myVar);
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, myVar, myExpressionType);
}
@Override
@@ -36,6 +36,7 @@ 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.Functions;
import com.intellij.util.IncorrectOperationException;
import java.util.*;
@@ -146,10 +147,11 @@ public class ExtractEnumProcessor {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.addConversionDescriptor(new EnumTypeConversionRule(myEnumConstants));
rules.setMigrationRootType(
JavaPsiFacade.getElementFactory(myProject).createType(myClass));
rules.setBoundScope(GlobalSearchScope.projectScope(myProject));
myTypeMigrationProcessor = new TypeMigrationProcessor(myProject, PsiUtilCore.toPsiElementArray(myEnumConstants), rules);
myTypeMigrationProcessor = new TypeMigrationProcessor(myProject,
PsiUtilCore.toPsiElementArray(myEnumConstants),
Functions.<PsiElement, PsiType>constant(JavaPsiFacade.getElementFactory(myProject).createType(myClass)),
rules);
for (UsageInfo usageInfo : myTypeMigrationProcessor.findUsages()) {
final PsiElement migrateElement = usageInfo.getElement();
if (migrateElement instanceof PsiField) {
@@ -42,7 +42,7 @@ public class ChangeTypeSignatureHandler implements RefactoringActionHandler {
while (typeElement != null) {
final PsiElement parent = typeElement.getParent();
if (parent instanceof PsiVariable || (parent instanceof PsiMember && !(parent instanceof PsiClass)) || isClassArgument(parent)) {
invoke(project, parent, null, editor);
invoke(project, parent, null, null, editor);
return;
}
typeElement = PsiTreeUtil.getParentOfType(parent, PsiTypeElement.class, false);
@@ -61,11 +61,11 @@ public class ChangeTypeSignatureHandler implements RefactoringActionHandler {
public static boolean invokeOnElement(final Project project, final PsiElement element) {
if (element instanceof PsiVariable || (element instanceof PsiMember && !(element instanceof PsiClass)) || element instanceof PsiFile) {
invoke(project, element, null, null);
invoke(project, element, null, null, null);
return true;
}
if (isClassArgument(element)) {
invoke(project, element, null, null);
invoke(project, element, null, null, null);
return true;
}
return false;
@@ -87,9 +87,9 @@ public class ChangeTypeSignatureHandler implements RefactoringActionHandler {
return false;
}
public static void invoke(final Project project, final PsiElement root, final TypeMigrationRules rules, final Editor editor) {
public static void invoke(final Project project, final PsiElement root, final PsiType type, final TypeMigrationRules rules, final Editor editor) {
if (Util.canBeMigrated(root)) {
TypeMigrationDialog dialog = new TypeMigrationDialog(project, root, rules);
TypeMigrationDialog dialog = new TypeMigrationDialog(project, root, type, rules);
dialog.show();
return;
}
@@ -65,6 +65,7 @@ public class TypeMigrationLabeler {
}
private final TypeMigrationRules myRules;
private final Function<PsiElement, PsiType> myRootTypes;
private TypeEvaluator myTypeEvaluator;
private final LinkedHashMap<PsiElement, Object> myConversions;
private final HashSet<Pair<SmartPsiElementPointer<PsiExpression>, PsiType>> myFailedConversions;
@@ -80,10 +81,14 @@ public class TypeMigrationLabeler {
private final Map<Pair<TypeMigrationUsageInfo, TypeMigrationUsageInfo>, Set<PsiElement>> myRootUsagesTree = new HashMap<Pair<TypeMigrationUsageInfo, TypeMigrationUsageInfo>, Set<PsiElement>>();
private final Set<TypeMigrationUsageInfo> myProcessedRoots = new HashSet<TypeMigrationUsageInfo>();
public TypeMigrationLabeler(final TypeMigrationRules rules, PsiType rootType) {
this(rules, Functions.<PsiElement, PsiType>constant(rootType));
}
public TypeMigrationLabeler(final TypeMigrationRules rules) {
public TypeMigrationLabeler(final TypeMigrationRules rules, Function<PsiElement, PsiType> rootTypes) {
myRules = rules;
myRootTypes = rootTypes;
myConversions = new LinkedHashMap<PsiElement, Object>();
myFailedConversions = new HashSet<Pair<SmartPsiElementPointer<PsiExpression>, PsiType>>();
myNewExpressionTypeChange = new LinkedHashMap<TypeMigrationUsageInfo, PsiType>();
@@ -94,6 +99,10 @@ public class TypeMigrationLabeler {
return myFailedConversions.size() > 0;
}
public Function<PsiElement, PsiType> getRootTypes() {
return myRootTypes;
}
public String[] getFailedConversionsReport() {
final String[] report = new String[myFailedConversions.size()];
int j = 0;
@@ -884,9 +893,8 @@ public class TypeMigrationLabeler {
myTypeEvaluator = new TypeEvaluator(myMigrationRoots, this);
final PsiType rootType = myRules.getMigrationRootType();
for (PsiElement victim : victims) {
addMigrationRoot(victim, rootType, null, false, true, true);
addMigrationRoot(victim, myRootTypes.fun(victim), null, false, true, true);
}
if (autoMigrate) {
@@ -34,9 +34,7 @@ import com.intellij.ui.content.Content;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.usageView.UsageViewManager;
import com.intellij.util.Consumer;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.*;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
@@ -49,34 +47,38 @@ import static com.intellij.util.ObjectUtils.assertNotNull;
public class TypeMigrationProcessor extends BaseRefactoringProcessor {
private PsiElement[] myRoot;
private Function<PsiElement, PsiType> myRootTypes;
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 root, final PsiType migrationType, final TypeMigrationRules rules) {
this(project, new PsiElement[]{root}, Functions.<PsiElement, PsiType>constant(migrationType), rules);
}
public TypeMigrationProcessor(final Project project, final PsiElement[] roots, final TypeMigrationRules rules) {
public TypeMigrationProcessor(final Project project, final PsiElement[] roots, final Function<PsiElement, PsiType> rootTypes, final TypeMigrationRules rules) {
super(project);
myRoot = roots;
myRules = rules;
myRootTypes = rootTypes;
}
public static void runHighlightingTypeMigration(final Project project,
final Editor editor,
final TypeMigrationRules rules,
final PsiElement root) {
runHighlightingTypeMigration(project, editor, rules, root, false);
final Editor editor,
final TypeMigrationRules rules,
final PsiElement root,
final PsiType migrationType) {
runHighlightingTypeMigration(project, editor, rules, root, migrationType, false);
}
public static void runHighlightingTypeMigration(final Project project,
final Editor editor,
final TypeMigrationRules rules,
final PsiElement root,
final boolean optimizeImports) {
final Editor editor,
final TypeMigrationRules rules,
final PsiElement root,
final PsiType migrationType,
final boolean optimizeImports) {
final PsiFile containingFile = root.getContainingFile();
final TypeMigrationProcessor processor = new TypeMigrationProcessor(project, root, rules) {
final TypeMigrationProcessor processor = new TypeMigrationProcessor(project, root, migrationType, rules) {
@Override
public void performRefactoring(@NotNull final UsageInfo[] usages) {
super.performRefactoring(usages);
@@ -170,7 +172,7 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor {
text = Arrays.toString(myRoot);
}
String fromType = assertNotNull(TypeMigrationLabeler.getElementType(myRoot[0])).getPresentableText();
String toType = myRules.getMigrationRootType().getPresentableText();
String toType = myRootTypes.fun(myRoot[0]).getPresentableText();
String name = "Migrate Type of " + text + " from \'" + fromType + "\' to \'" + toType + "\'";
Content content = UsageViewManager.getInstance(myProject).addContent(name, false, panel, true, true);
panel.setContent(content);
@@ -180,7 +182,7 @@ public class TypeMigrationProcessor extends BaseRefactoringProcessor {
@NotNull
@Override
public UsageInfo[] findUsages() {
myLabeler = new TypeMigrationLabeler(myRules);
myLabeler = new TypeMigrationLabeler(myRules, myRootTypes);
try {
return myLabeler.getMigratedUsages(!isPreviewUsages(), myRoot);
@@ -35,8 +35,6 @@ import java.util.LinkedList;
*/
public class TypeMigrationRules {
private final LinkedList<TypeConversionRule> myConversionRules = new LinkedList<TypeConversionRule>();
private PsiType myMigrationRootType;
private SearchScope mySearchScope;
public TypeMigrationRules() {
@@ -45,14 +43,6 @@ public class TypeMigrationRules {
ContainerUtil.addAll(myConversionRules, Extensions.getExtensions(TypeConversionRule.EP_NAME));
}
public void setMigrationRootType(PsiType migrationRootType) {
myMigrationRootType = migrationRootType;
}
public PsiType getMigrationRootType() {
return myMigrationRootType;
}
public void addConversionDescriptor(TypeConversionRule rule) {
myConversionRules.add(rule);
}
@@ -254,7 +254,7 @@ public class MigrationPanel extends JPanel implements Disposable {
UsageViewManager.getInstance(myProject).closeContent(myContent);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ChangeTypeSignatureHandler.invoke(myProject, myInitialRoot, myLabeler.getRules(), null);
ChangeTypeSignatureHandler.invoke(myProject, myInitialRoot, myLabeler.getRootTypes().fun(myInitialRoot), myLabeler.getRules(), null);
}
});
}
@@ -62,7 +62,7 @@ public class MigrationRootNode extends AbstractTreeNode<TypeMigrationLabeler> im
}
}
else {
addRoot(new TypeMigrationUsageInfo(myRoot), myLabeler.getRules().getMigrationRootType());
addRoot(new TypeMigrationUsageInfo(myRoot), myLabeler.getRootTypes().fun(myRoot));
}
}
return myCachedChildren;
@@ -67,12 +67,12 @@ public class TypeMigrationDialog extends RefactoringDialog {
private final PsiTypeCodeFragment myTypeCodeFragment;
private final ScopeChooserCombo myScopeChooserCombo;
public TypeMigrationDialog(@NotNull Project project, PsiElement root, TypeMigrationRules rules) {
public TypeMigrationDialog(@NotNull Project project, PsiElement root, PsiType migrationType, TypeMigrationRules rules) {
super(project, false);
myRoot = root;
myRules = rules;
final PsiType migrationRootType = rules != null ? rules.getMigrationRootType() : null;
final PsiType migrationRootType = migrationType;
final PsiType rootType = getRootType();
final String text = migrationRootType != null ? migrationRootType.getCanonicalText(true) :
rootType != null ? rootType.getCanonicalText(true) : "";
@@ -209,10 +209,9 @@ public class TypeMigrationDialog extends RefactoringDialog {
if (myRules == null) {
myRules = new TypeMigrationRules();
myRules.setMigrationRootType(migrationType);
myRules.setBoundScope(myScopeChooserCombo.getSelectedScope());
}
invokeRefactoring(new TypeMigrationProcessor(myProject, myRoot, myRules));
invokeRefactoring(new TypeMigrationProcessor(myProject, myRoot, migrationType, myRules));
}
@Nullable
@@ -72,9 +72,8 @@ public class TypeMigrationVariableTypeFixProvider implements ChangeVariableTypeQ
try {
variable.normalizeDeclaration();
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(targetType);
rules.setBoundScope(GlobalSearchScope.projectScope(project));
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, variable, optimizeImports);
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, variable, targetType, optimizeImports);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(variable);
UndoUtil.markPsiFileForUndo(variable.getContainingFile());
}
@@ -273,10 +273,9 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
final PsiMethodCallExpression expr = (PsiMethodCallExpression)startElement;
final boolean isIterableAssignment = isIterable(expr);
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(myTargetType);
rules.setBoundScope(GlobalSearchScope.fileScope(file));
final TypeConversionDescriptorBase conversion =
rules.findConversion(myInitialType, myTargetType, expr.resolveMethod(), expr, new TypeMigrationLabeler(rules));
rules.findConversion(myInitialType, myTargetType, expr.resolveMethod(), expr, new TypeMigrationLabeler(rules, myTargetType));
LOG.assertTrue(conversion != null);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
@@ -365,9 +364,8 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
try {
PsiMethod method = (PsiMethod)getStartElement();
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(myTargetType);
rules.setBoundScope(method.getUseScope());
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, method, true);
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, method, myTargetType, true);
UndoUtil.markPsiFileForUndo(file);
}
catch (IncorrectOperationException e) {
@@ -118,10 +118,9 @@ public class ChangeClassParametersIntention extends PsiElementBaseIntentionActio
final TypeMigrationRules myRules = new TypeMigrationRules();
final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, targetParam);
final PsiType targetClassType = elementFactory.createType(baseClass, substitutor);
myRules.setMigrationRootType(targetClassType);
myRules.setBoundScope(new LocalSearchScope(aClass));
TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, myRules,
((PsiAnonymousClass)aClass).getBaseClassReference().getParameterList());
((PsiAnonymousClass)aClass).getBaseClassReference().getParameterList(), targetClassType);
}
catch (IncorrectOperationException e) {
HintManager.getInstance().showErrorHint(editor, "Incorrect type");
@@ -99,9 +99,8 @@ public class ConvertFieldToThreadLocalIntention extends PsiElementBaseIntentionA
try {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(toType);
rules.setBoundScope(GlobalSearchScope.fileScope(element.getContainingFile()));
final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules);
final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules, toType);
labeler.getMigratedUsages(false, psiField);
for (PsiReference reference : refs) {
PsiElement psiElement = reference.getElement();
@@ -36,9 +36,11 @@ public class ChangeTypeSignatureTest extends LightCodeInsightTestCase {
try {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(PsiSubstitutor.EMPTY.put(superClass.getTypeParameters()[0], migrationType).substitute(new PsiImmediateClassType(superClass, PsiSubstitutor.EMPTY)));
rules.setBoundScope(GlobalSearchScope.projectScope(getProject()));
new TypeMigrationProcessor(getProject(), parameterList, rules).run();
new TypeMigrationProcessor(getProject(),
parameterList,
PsiSubstitutor.EMPTY.put(superClass.getTypeParameters()[0], migrationType).substitute(new PsiImmediateClassType(superClass, PsiSubstitutor.EMPTY)),
rules).run();
if (success) {
checkResultByFile(dataPath + getTestName(false) + ".java.after");
} else {
@@ -579,10 +579,8 @@ public class MigrateTypeSignatureTest extends TypeMigrationTestBase {
final PsiType toType) {
final RulesProvider provider = new RulesProvider() {
@Override
public TypeMigrationRules provide() throws Exception {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(toType);
return rules;
public PsiType migrationType() throws Exception {
return toType;
}
@Override
@@ -804,10 +804,8 @@ public class TypeMigrationTest extends TypeMigrationTestBase {
private void doTestCatchParameter(final PsiType rootType, final PsiType migrationType) {
start(new RulesProvider() {
@Override
public TypeMigrationRules provide() {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(migrationType);
return rules;
public PsiType migrationType() {
return migrationType;
}
@Override
@@ -921,10 +919,8 @@ public class TypeMigrationTest extends TypeMigrationTestBase {
private void doTestReturnType(final String methodName, final PsiType migrationType) {
start(new RulesProvider() {
@Override
public TypeMigrationRules provide() throws Exception {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(migrationType);
return rules;
public PsiType migrationType() throws Exception {
return migrationType;
}
@Override
@@ -942,10 +938,8 @@ public class TypeMigrationTest extends TypeMigrationTestBase {
private void doTestForeachParameter(final PsiType migrationType) {
start(new RulesProvider() {
@Override
public TypeMigrationRules provide() {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(migrationType);
return rules;
public PsiType migrationType() {
return migrationType;
}
@Override
@@ -58,10 +58,8 @@ public abstract class TypeMigrationTestBase extends MultiFileTestCase {
final PsiType toType) {
final RulesProvider provider = new RulesProvider() {
@Override
public TypeMigrationRules provide() throws Exception {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(toType);
return rules;
public PsiType migrationType() throws Exception {
return toType;
}
@Override
@@ -87,10 +85,8 @@ public abstract class TypeMigrationTestBase extends MultiFileTestCase {
protected void doTestFieldType(@NonNls final String fieldName, String className, final PsiType rootType, final PsiType migrationType) {
final RulesProvider provider = new RulesProvider() {
@Override
public TypeMigrationRules provide() throws Exception {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(migrationType);
return rules;
public PsiType migrationType() throws Exception {
return migrationType;
}
@Override
@@ -111,10 +107,8 @@ public abstract class TypeMigrationTestBase extends MultiFileTestCase {
protected void doTestMethodType(@NonNls final String methodName, @NonNls String className, final PsiType rootType, final PsiType migrationType) {
final RulesProvider provider = new RulesProvider() {
@Override
public TypeMigrationRules provide() throws Exception {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(migrationType);
return rules;
public PsiType migrationType() throws Exception {
return migrationType;
}
@Override
@@ -133,10 +127,8 @@ public abstract class TypeMigrationTestBase extends MultiFileTestCase {
protected void doTestFirstParamType(@NonNls final String methodName, String className, final PsiType rootType, final PsiType migrationType) {
final RulesProvider provider = new RulesProvider() {
@Override
public TypeMigrationRules provide() throws Exception {
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setMigrationRootType(migrationType);
return rules;
public PsiType migrationType() throws Exception {
return migrationType;
}
@Override
@@ -166,9 +158,10 @@ public abstract class TypeMigrationTestBase extends MultiFileTestCase {
assertNotNull("Class " + className + " not found", aClass);
final TypeMigrationRules rules = provider.provide();
final PsiType migrationType = provider.migrationType();
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setBoundScope(new LocalSearchScope(aClass.getContainingFile()));
final TestTypeMigrationProcessor pr = new TestTypeMigrationProcessor(getProject(), provider.victims(aClass), rules);
final TestTypeMigrationProcessor pr = new TestTypeMigrationProcessor(getProject(), provider.victims(aClass), migrationType, rules);
final UsageInfo[] usages = pr.findUsages();
final String report = pr.getLabeler().getMigrationReport();
@@ -216,14 +209,14 @@ public abstract class TypeMigrationTestBase extends MultiFileTestCase {
}
interface RulesProvider {
TypeMigrationRules provide() throws Exception;
PsiType migrationType() throws Exception;
PsiElement victims(PsiClass aClass);
}
private static class TestTypeMigrationProcessor extends TypeMigrationProcessor {
public TestTypeMigrationProcessor(final Project project, final PsiElement root, final TypeMigrationRules rules) {
super(project, root, rules);
public TestTypeMigrationProcessor(final Project project, final PsiElement root, final PsiType migrationType, final TypeMigrationRules rules) {
super(project, root, migrationType, rules);
}
@NotNull