From 72a71dd7ac3c3d9d120a977f786eb3246de83041 Mon Sep 17 00:00:00 2001 From: Dmitri Boulytchev Date: Mon, 21 Feb 2005 14:55:05 +0300 Subject: [PATCH] Type Cook Advanced --- .../refactoring/RefactoringFactory.java | 3 +- .../refactoring/RefactoringSettings.java | 1 + .../openapi/impl/RefactoringFactoryImpl.java | 5 +- .../openapi/impl/TypeCookRefactoringImpl.java | 9 +- .../refactoring/typeCook/Settings.java | 1 + .../refactoring/typeCook/TypeCookDialog.java | 20 +++- .../typeCook/TypeCookProcessor.java | 2 + .../intellij/refactoring/typeCook/Util.java | 42 ++++++-- .../deductive/PsiTypeVariableFactory.java | 101 ++++++++++++++++-- .../typeCook/deductive/builder/Result.java | 12 ++- .../deductive/builder/SystemBuilder.java | 6 +- .../typeCook/deductive/resolver/Binding.java | 2 + .../deductive/resolver/BindingFactory.java | 16 ++- .../deductive/resolver/SolutionHolder.java | 2 +- .../deductive/util/VictimCollector.java | 8 +- .../intellij/refactoring/TypeCookTest.java | 4 + 16 files changed, 204 insertions(+), 30 deletions(-) diff --git a/openapi/src/com/intellij/refactoring/RefactoringFactory.java b/openapi/src/com/intellij/refactoring/RefactoringFactory.java index d49c9ee91a6c..829af3bc0b29 100644 --- a/openapi/src/com/intellij/refactoring/RefactoringFactory.java +++ b/openapi/src/com/intellij/refactoring/RefactoringFactory.java @@ -64,7 +64,8 @@ public abstract class RefactoringFactory { boolean dropObsoleteCasts, boolean leaveObjectsRaw, boolean preserveRawArrays, - boolean exhaustive); + boolean exhaustive, + boolean cookObjects); /** * Creates Introduce Parameter refactoring that replaces local variable with parameter. diff --git a/source/com/intellij/refactoring/RefactoringSettings.java b/source/com/intellij/refactoring/RefactoringSettings.java index f1d0c9f4078b..1105a4b9624d 100644 --- a/source/com/intellij/refactoring/RefactoringSettings.java +++ b/source/com/intellij/refactoring/RefactoringSettings.java @@ -55,6 +55,7 @@ public class RefactoringSettings implements JDOMExternalizable, ApplicationCompo public boolean TYPE_COOK_PRESERVE_RAW_ARRAYS = true; public boolean TYPE_COOK_LEAVE_OBJECT_PARAMETERIZED_TYPES_RAW = true; public boolean TYPE_COOK_EXHAUSTIVE = false; + public final boolean TYPE_COOK_COOK_OBJECTS = false; public boolean TYPE_MIGRATION_PREVIEW_USAGES = true; diff --git a/source/com/intellij/refactoring/openapi/impl/RefactoringFactoryImpl.java b/source/com/intellij/refactoring/openapi/impl/RefactoringFactoryImpl.java index f414023078c3..ae889c3877b0 100644 --- a/source/com/intellij/refactoring/openapi/impl/RefactoringFactoryImpl.java +++ b/source/com/intellij/refactoring/openapi/impl/RefactoringFactoryImpl.java @@ -113,8 +113,9 @@ public class RefactoringFactoryImpl extends RefactoringFactory implements Projec boolean dropObsoleteCasts, boolean leaveObjectsRaw, boolean preserveRawArrays, - boolean exhaustive) { - return new TypeCookRefactoringImpl(myProject, elements, dropObsoleteCasts, leaveObjectsRaw, preserveRawArrays, exhaustive); + boolean exhaustive, + boolean cookObjects) { + return new TypeCookRefactoringImpl(myProject, elements, dropObsoleteCasts, leaveObjectsRaw, preserveRawArrays, exhaustive, cookObjects); } public IntroduceParameterRefactoring createIntroduceParameterRefactoring(PsiMethod methodToReplaceIn, diff --git a/source/com/intellij/refactoring/openapi/impl/TypeCookRefactoringImpl.java b/source/com/intellij/refactoring/openapi/impl/TypeCookRefactoringImpl.java index d4a359282273..59bb66bb33a8 100644 --- a/source/com/intellij/refactoring/openapi/impl/TypeCookRefactoringImpl.java +++ b/source/com/intellij/refactoring/openapi/impl/TypeCookRefactoringImpl.java @@ -22,7 +22,8 @@ public class TypeCookRefactoringImpl extends RefactoringImpl final boolean dropObsoleteCasts, final boolean leaveObjectsRaw, final boolean preserveRawArrays, - final boolean exh) { + final boolean exhaustiveSearch, + final boolean cookObjects) { super(new TypeCookProcessor(project, elements, new Settings() { public boolean dropObsoleteCasts() { return dropObsoleteCasts; @@ -33,7 +34,11 @@ public class TypeCookRefactoringImpl extends RefactoringImpl } public boolean exhaustive() { - return exh; + return exhaustiveSearch; + } + + public boolean cookObjects() { + return cookObjects; } public boolean preserveRawArrays() { diff --git a/source/com/intellij/refactoring/typeCook/Settings.java b/source/com/intellij/refactoring/typeCook/Settings.java index 3195e0d547ce..3dd67d25c4e2 100644 --- a/source/com/intellij/refactoring/typeCook/Settings.java +++ b/source/com/intellij/refactoring/typeCook/Settings.java @@ -12,4 +12,5 @@ public interface Settings { boolean preserveRawArrays(); boolean leaveObjectParameterizedTypesRaw(); boolean exhaustive(); + boolean cookObjects(); } diff --git a/source/com/intellij/refactoring/typeCook/TypeCookDialog.java b/source/com/intellij/refactoring/typeCook/TypeCookDialog.java index 5be9e6b275fb..848820db8ef6 100644 --- a/source/com/intellij/refactoring/typeCook/TypeCookDialog.java +++ b/source/com/intellij/refactoring/typeCook/TypeCookDialog.java @@ -27,6 +27,7 @@ public class TypeCookDialog extends RefactoringDialog { private JCheckBox myCbPreserveRawArrays = new JCheckBox("Preserve raw arrays"); private JCheckBox myCbLeaveObjectParameterizedTypesRaw = new JCheckBox("Leave Object-parameterized types raw"); private JCheckBox myCbExhaustive = new JCheckBox("Perform exhaustive search"); + private JCheckBox myCbCookObjects = new JCheckBox("Generify Objects"); public TypeCookDialog(Project project, PsiElement[] elements) { super(project, true); @@ -96,10 +97,16 @@ public class TypeCookDialog extends RefactoringDialog { RefactoringSettings.getInstance().TYPE_COOK_EXHAUSTIVE); } + if (myCbCookObjects.isEnabled()) { + myCbCookObjects.setSelected( + RefactoringSettings.getInstance().TYPE_COOK_COOK_OBJECTS); + } + myCbDropCasts.setMnemonic('D'); myCbPreserveRawArrays.setMnemonic('P'); myCbLeaveObjectParameterizedTypesRaw.setMnemonic('L'); myCbExhaustive.setMnemonic('E'); + myCbCookObjects.setMnemonic('O'); gbConstraints.insets = new Insets(4, 8, 4, 8); @@ -129,6 +136,10 @@ public class TypeCookDialog extends RefactoringDialog { gbConstraints.gridwidth = 2; optionsPanel.add(myCbExhaustive, gbConstraints); + gbConstraints.gridx = 0; + gbConstraints.gridwidth = 2; + optionsPanel.add(myCbCookObjects, gbConstraints); + return optionsPanel; } @@ -145,7 +156,8 @@ public class TypeCookDialog extends RefactoringDialog { final boolean dropCasts = myCbDropCasts.isSelected(); final boolean preserveRawArrays = true; //myCbPreserveRawArrays.isSelected(); final boolean leaveObjectParameterizedTypesRaw = myCbLeaveObjectParameterizedTypesRaw.isSelected(); - final boolean exh = myCbExhaustive.isSelected(); + final boolean exhaustive = myCbExhaustive.isSelected(); + final boolean cookObjects = myCbCookObjects.isSelected(); return new Settings() { public boolean dropObsoleteCasts() { @@ -161,7 +173,11 @@ public class TypeCookDialog extends RefactoringDialog { } public boolean exhaustive() { - return exh; + return exhaustive; + } + + public boolean cookObjects() { + return cookObjects; } }; } diff --git a/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java b/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java index ea0fb929199d..6bb53bb763e6 100644 --- a/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java +++ b/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java @@ -87,6 +87,8 @@ public class TypeCookProcessor extends BaseRefactoringProcessor { myResult.apply (victims); + java.lang.System.out.println(myResult.getReport()); + UndoManager.getInstance(myProject).undoableActionPerformed(new DummyComplexUndoableAction()); // force confirmation dialog for undo } diff --git a/source/com/intellij/refactoring/typeCook/Util.java b/source/com/intellij/refactoring/typeCook/Util.java index 73019a61cd74..b0d8b7b050e2 100644 --- a/source/com/intellij/refactoring/typeCook/Util.java +++ b/source/com/intellij/refactoring/typeCook/Util.java @@ -2,6 +2,7 @@ package com.intellij.refactoring.typeCook; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; @@ -117,7 +118,11 @@ public class Util { } } - public static boolean isRaw(PsiType t, boolean arrays) { + public static boolean isRaw(PsiType t, final Settings settings) { + return isRaw(t, settings, true); + } + + private static boolean isRaw(PsiType t, final Settings settings, final boolean upper) { if (t instanceof PsiClassType) { final PsiClassType.ClassResolveResult resolveResult = resolveType(t); @@ -131,18 +136,24 @@ public class Util { final PsiSubstitutor subst = resolveResult.getSubstitutor(); final PsiClass element = resolveResult.getElement(); + final PsiManager manager = element.getManager(); + + if (settings.cookObjects() && upper && + t.equals(PsiType.getJavaLangObject(manager, GlobalSearchScope.allScope(manager.getProject())))) { + return true; + } final PsiTypeParameter[] parameters = getTypeParametersList(element); for (int i = 0; i < parameters.length; i++) { final PsiType actual = subst.substitute(parameters[i]); - if (!(actual instanceof PsiTypeParameter) && isRaw(actual, arrays)) return true; + if (!(actual instanceof PsiTypeParameter) && isRaw(actual, settings, false)) return true; } return false; } else if (t instanceof PsiArrayType) { - return arrays ? false : isRaw(((PsiArrayType)t).getComponentType(), arrays); + return settings.preserveRawArrays() ? false : isRaw(((PsiArrayType)t).getComponentType(), settings, upper); } return false; @@ -308,9 +319,20 @@ public class Util { return type; } + public static PsiType createParameterizedType(final PsiType t, final PsiTypeVariableFactory factory, final PsiElement context) { + return createParameterizedType(t, factory, true, context); + } + public static PsiType createParameterizedType(final PsiType t, final PsiTypeVariableFactory factory) { - if (t == null) { - return factory.create(); + return createParameterizedType(t, factory, true, null); + } + + private static PsiType createParameterizedType(final PsiType t, + final PsiTypeVariableFactory factory, + final boolean upper, + final PsiElement context) { + if (t == null || (upper && t.getCanonicalText().equals("java.lang.Object"))) { + return factory.create(context); } if (t instanceof PsiClassType) { @@ -324,7 +346,7 @@ public class Util { for (Iterator i = aSubst.getSubstitutionMap().keySet().iterator(); i.hasNext();) { final PsiTypeParameter parm = i.next(); - final PsiType type = createParameterizedType(aSubst.substitute(parm), factory); + final PsiType type = createParameterizedType(aSubst.substitute(parm), factory, false, context); if (type instanceof PsiTypeVariable) { cluster.add((PsiTypeVariable)type); @@ -340,7 +362,7 @@ public class Util { return aClass.getManager().getElementFactory().createType(aClass, theSubst); } else if (t instanceof PsiArrayType) { - return createParameterizedType(((PsiArrayType)t).getComponentType(), factory).createArrayType(); + return createParameterizedType(((PsiArrayType)t).getComponentType(), factory, upper, context).createArrayType(); } return t; @@ -463,7 +485,7 @@ public class Util { final PsiSubstitutor subst = result.getSubstitutor(); final PsiTypeParameter[] parms = Util.getTypeParametersList(result.getElement()); - if (parms.length >= 0 && subst.substitute(parms[0]) != null) { + if (parms.length > 0 && subst.substitute(parms[0]) != null) { PsiJavaCodeReferenceElement classReference = newx.getClassReference(); PsiReferenceParameterList list = null; @@ -488,7 +510,7 @@ public class Util { for (int i = 0; i < parms.length; i++) { PsiType aType = subst.substitute(parms[i]); - if (aType instanceof PsiWildcardType){ + if (aType instanceof PsiWildcardType) { aType = ((PsiWildcardType)aType).getBound(); } @@ -497,7 +519,7 @@ public class Util { } } else { - LOG.error ("Unexpected element type " + element.getClass().getName()); + LOG.error("Unexpected element type " + element.getClass().getName()); } } catch (IncorrectOperationException e) { diff --git a/source/com/intellij/refactoring/typeCook/deductive/PsiTypeVariableFactory.java b/source/com/intellij/refactoring/typeCook/deductive/PsiTypeVariableFactory.java index 92cd228dbaa8..8bfd3b6bfe43 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/PsiTypeVariableFactory.java +++ b/source/com/intellij/refactoring/typeCook/deductive/PsiTypeVariableFactory.java @@ -1,7 +1,8 @@ package com.intellij.refactoring.typeCook.deductive; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeVisitor; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import com.intellij.psi.search.GlobalSearchScope; import java.util.HashSet; @@ -28,8 +29,8 @@ public class PsiTypeVariableFactory { public final void registerCluster(final HashSet cluster) { myClusters.add(cluster); - for (final Iterator v=cluster.iterator(); v.hasNext();){ - myVarCluster.put (new Integer(v.next().getIndex()), cluster); + for (final Iterator v = cluster.iterator(); v.hasNext();) { + myVarCluster.put(new Integer(v.next().getIndex()), cluster); } } @@ -37,13 +38,101 @@ public class PsiTypeVariableFactory { return myClusters; } - public final HashSet getClusterOf (final int var){ - return myVarCluster.get(new Integer (var)); + public final HashSet getClusterOf(final int var) { + return myVarCluster.get(new Integer(var)); } public final PsiTypeVariable create() { + return create(null); + } + + public final PsiTypeVariable create(final PsiElement context) { return new PsiTypeVariable() { private int myIndex = myCurrent++; + private final PsiElement myContext = context; + + public boolean isValidInContext(final PsiType type) { + if (myContext == null) { + return true; + } + + if (type == null) { + return true; + } + + return type.accept(new PsiTypeVisitor() { + public Boolean visitType(final PsiType type) { + return Boolean.TRUE; + } + + public Boolean visitArrayType(final PsiArrayType arrayType) { + return arrayType.getDeepComponentType().accept(this); + } + + public Boolean visitWildcardType(final PsiWildcardType wildcardType) { + final PsiType bound = wildcardType.getBound(); + + if (bound != null) { + bound.accept(this); + } + + return Boolean.TRUE; + } + + public Boolean visitClassType(final PsiClassType classType) { + final PsiClassType.ClassResolveResult result = classType.resolveGenerics(); + final PsiClass aClass = result.getElement(); + final PsiSubstitutor aSubst = result.getSubstitutor(); + final PsiManager manager = aClass.getManager(); + + if (aClass != null) { + if (aClass instanceof PsiTypeParameter) { + final PsiTypeParameterListOwner owner = + PsiTreeUtil.getParentOfType(myContext, PsiTypeParameterListOwner.class); + + if (owner != null) { + boolean found = false; + + for (final Iterator p = PsiUtil.typeParametersIterator(owner); + p.hasNext() && !found;) { + final PsiTypeParameter parm = p.next(); + + found = manager.areElementsEquivalent(parm, aClass); + } + + if (!found) { + return Boolean.FALSE; + } + } + else { + return Boolean.FALSE; + } + } + else if (!manager.getResolveHelper().isAccessible(aClass, myContext, null)) { + return Boolean.FALSE; + } + + for (final Iterator p = aSubst.getSubstitutionMap().keySet().iterator(); p.hasNext();) { + final PsiTypeParameter parm = p.next(); + final PsiType type = aSubst.substitute(parm); + + if (type != null){ + final Boolean b = type.accept(this); + + if (! b.booleanValue()){ + return Boolean.FALSE; + } + } + } + + return Boolean.TRUE; + } + else { + return Boolean.FALSE; + } + } + }).booleanValue(); + } public String getPresentableText() { return "$" + myIndex; diff --git a/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java b/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java index 72b92959215c..59263035ae09 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java +++ b/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java @@ -53,11 +53,19 @@ public class Result { } public PsiType getCookedType(final PsiElement element) { + final PsiType originalType = Util.getType(element); + if (myBinding != null) { - return myBinding.substitute(myTypes.get(element)); + final PsiType type = myBinding.substitute(myTypes.get(element)); + + if (type == null && originalType.getCanonicalText().equals("java.lang.Object")){ + return originalType; + } + + return type; } - return Util.getType(element); + return originalType; } public HashSet getCookedElements() { diff --git a/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java b/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java index 299e72eb9e72..4f1c014e7a29 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java +++ b/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java @@ -170,7 +170,7 @@ public class SystemBuilder { t = Util.getType(e); - final PsiType parameterizedType = Util.createParameterizedType(t, myTypeVariableFactory); + final PsiType parameterizedType = Util.createParameterizedType(t, myTypeVariableFactory, e); myTypes.put(e, parameterizedType); @@ -810,6 +810,10 @@ public class SystemBuilder { } } }.brrrr(definedType, elemenType); + + if (mySettings.cookObjects() && elemenType.getCanonicalText().equals("java.lang.Object")){ + system.addSubtypeConstraint(definedType, elemenType); + } } } diff --git a/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java b/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java index 1728a5269195..98dca60fcaa7 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java +++ b/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java @@ -40,4 +40,6 @@ public abstract class Binding { public abstract HashSet getBoundVariables(); public abstract int getWidth(); + + public abstract boolean isValid(); } diff --git a/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java b/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java index 99f47608c3a8..2adb58e31f7d 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java +++ b/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java @@ -121,7 +121,7 @@ public class BindingFactory { return theClass.getManager().getElementFactory().createType(theClass, theSubst); } else { - return null; + return type; } } else if (type instanceof PsiWildcardType) { @@ -541,6 +541,19 @@ public class BindingFactory { return w; } + public boolean isValid() { + for (final Iterator v = myBoundVariables.iterator(); v.hasNext();) { + final PsiTypeVariable var = v.next(); + final PsiType type = substitute(var); + + if (!var.isValidInContext(type)) { + return false; + } + } + + return true; + } + public PsiType substitute(final PsiType t) { if (t instanceof PsiWildcardType) { final PsiWildcardType wcType = (PsiWildcardType)t; @@ -677,6 +690,7 @@ public class BindingFactory { else { return null; } + break; case 1: return balancer.varType((PsiTypeVariable)x, y); diff --git a/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java b/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java index c362aaedd368..e285dd0e4db9 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java +++ b/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java @@ -47,7 +47,7 @@ public class SolutionHolder { final Binding binding = b.next(); final int w = binding.getWidth(); - if (w > width) { + if (w > width && binding.isValid()) { width = w; best = binding; } diff --git a/source/com/intellij/refactoring/typeCook/deductive/util/VictimCollector.java b/source/com/intellij/refactoring/typeCook/deductive/util/VictimCollector.java index 975e7c7fd99c..f1c5e7ddcb44 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/util/VictimCollector.java +++ b/source/com/intellij/refactoring/typeCook/deductive/util/VictimCollector.java @@ -24,7 +24,11 @@ public class VictimCollector extends Visitor { } private void testNAdd(final PsiElement element, final PsiType t) { - if (Util.isRaw(t, mySettings.preserveRawArrays())) { + if (Util.isRaw(t, mySettings)) { + if (element instanceof PsiNewExpression && t.getCanonicalText().equals("java.lang.Object")){ + return; + } + myVictims.add(element); } } @@ -48,7 +52,7 @@ public class VictimCollector extends Visitor { testNAdd(parms[i], parms[i].getType()); } - if (Util.isRaw(method.getReturnType(), mySettings.preserveRawArrays())) { + if (Util.isRaw(method.getReturnType(), mySettings)) { myVictims.add(method); } diff --git a/testSource/com/intellij/refactoring/TypeCookTest.java b/testSource/com/intellij/refactoring/TypeCookTest.java index 53c1fc24e295..0e8c8f00cec6 100644 --- a/testSource/com/intellij/refactoring/TypeCookTest.java +++ b/testSource/com/intellij/refactoring/TypeCookTest.java @@ -649,6 +649,10 @@ public class TypeCookTest extends MultiFileTestCase { public boolean exhaustive() { return false; } + + public boolean cookObjects(){ + return false; + } }); final com.intellij.refactoring.typeCook.deductive.builder.System commonSystem = b.build(new PsiElement[]{aClass});