From ec5cc1f8a9e9a5b71c4116ac5c81da3f6447ea9f Mon Sep 17 00:00:00 2001 From: Dmitri Boulytchev Date: Fri, 18 Feb 2005 18:32:44 +0300 Subject: [PATCH] Type Cook --- .../refactoring/RefactoringFactory.java | 3 +- .../refactoring/RefactoringSettings.java | 1 + .../openapi/impl/RefactoringFactoryImpl.java | 5 +- .../openapi/impl/TypeCookRefactoringImpl.java | 7 +- .../refactoring/typeCook/Settings.java | 1 + .../refactoring/typeCook/TypeCookDialog.java | 17 ++- .../typeCook/TypeCookProcessor.java | 8 +- .../typeCook/deductive/builder/Result.java | 105 +++++---------- .../typeCook/deductive/builder/System.java | 22 +-- .../deductive/builder/SystemBuilder.java | 48 ++----- .../typeCook/deductive/resolver/Binding.java | 8 +- .../deductive/resolver/BindingFactory.java | 74 +++++++++- .../deductive/resolver/ResolverTree.java | 127 +++++++++++------- .../deductive/resolver/SolutionHolder.java | 37 +++-- .../intellij/refactoring/TypeCookTest.java | 10 +- 15 files changed, 287 insertions(+), 186 deletions(-) diff --git a/openapi/src/com/intellij/refactoring/RefactoringFactory.java b/openapi/src/com/intellij/refactoring/RefactoringFactory.java index 20d1627aa69e..d49c9ee91a6c 100644 --- a/openapi/src/com/intellij/refactoring/RefactoringFactory.java +++ b/openapi/src/com/intellij/refactoring/RefactoringFactory.java @@ -63,7 +63,8 @@ public abstract class RefactoringFactory { public abstract TypeCookRefactoring createTypeCook(PsiElement[] elements, boolean dropObsoleteCasts, boolean leaveObjectsRaw, - boolean preserveRawArrays); + boolean preserveRawArrays, + boolean exhaustive); /** * 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 49e19bd805ff..f1d0c9f4078b 100644 --- a/source/com/intellij/refactoring/RefactoringSettings.java +++ b/source/com/intellij/refactoring/RefactoringSettings.java @@ -54,6 +54,7 @@ public class RefactoringSettings implements JDOMExternalizable, ApplicationCompo public boolean TYPE_COOK_DROP_CASTS = true; 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 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 640bafce9239..f414023078c3 100644 --- a/source/com/intellij/refactoring/openapi/impl/RefactoringFactoryImpl.java +++ b/source/com/intellij/refactoring/openapi/impl/RefactoringFactoryImpl.java @@ -112,8 +112,9 @@ public class RefactoringFactoryImpl extends RefactoringFactory implements Projec public TypeCookRefactoring createTypeCook(PsiElement[] elements, boolean dropObsoleteCasts, boolean leaveObjectsRaw, - boolean preserveRawArrays) { - return new TypeCookRefactoringImpl(myProject, elements, dropObsoleteCasts, leaveObjectsRaw, preserveRawArrays); + boolean preserveRawArrays, + boolean exhaustive) { + return new TypeCookRefactoringImpl(myProject, elements, dropObsoleteCasts, leaveObjectsRaw, preserveRawArrays, exhaustive); } 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 90e9b6b8dbce..9803f6dd3657 100644 --- a/source/com/intellij/refactoring/openapi/impl/TypeCookRefactoringImpl.java +++ b/source/com/intellij/refactoring/openapi/impl/TypeCookRefactoringImpl.java @@ -21,7 +21,8 @@ public class TypeCookRefactoringImpl extends RefactoringImpl PsiElement[] elements, final boolean dropObsoleteCasts, final boolean leaveObjectsRaw, - final boolean preserveRawArrays) { + final boolean preserveRawArrays, + final boolean exh) { super(new TypeCookProcessor(project, elements, new Settings() { public boolean dropObsoleteCasts() { return dropObsoleteCasts; @@ -31,6 +32,10 @@ public class TypeCookRefactoringImpl extends RefactoringImpl return leaveObjectsRaw; } + public boolean exhaustive() { + return exh; + } + public boolean preserveRawArrays() { return preserveRawArrays; } diff --git a/source/com/intellij/refactoring/typeCook/Settings.java b/source/com/intellij/refactoring/typeCook/Settings.java index eabf8e29f736..3195e0d547ce 100644 --- a/source/com/intellij/refactoring/typeCook/Settings.java +++ b/source/com/intellij/refactoring/typeCook/Settings.java @@ -11,4 +11,5 @@ public interface Settings { boolean dropObsoleteCasts(); boolean preserveRawArrays(); boolean leaveObjectParameterizedTypesRaw(); + boolean exhaustive(); } diff --git a/source/com/intellij/refactoring/typeCook/TypeCookDialog.java b/source/com/intellij/refactoring/typeCook/TypeCookDialog.java index bae425b7c551..5be9e6b275fb 100644 --- a/source/com/intellij/refactoring/typeCook/TypeCookDialog.java +++ b/source/com/intellij/refactoring/typeCook/TypeCookDialog.java @@ -19,7 +19,6 @@ import java.awt.*; * To change this template use Options | File Templates. */ public class TypeCookDialog extends RefactoringDialog { - public static final String REFACTORING_NAME = "Generify"; private PsiElement[] myElements; @@ -27,6 +26,7 @@ public class TypeCookDialog extends RefactoringDialog { private JCheckBox myCbDropCasts = new JCheckBox("Drop obsolete casts"); 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"); public TypeCookDialog(Project project, PsiElement[] elements) { super(project, true); @@ -91,9 +91,15 @@ public class TypeCookDialog extends RefactoringDialog { RefactoringSettings.getInstance().TYPE_COOK_LEAVE_OBJECT_PARAMETERIZED_TYPES_RAW); } + if (myCbExhaustive.isEnabled()) { + myCbExhaustive.setSelected( + RefactoringSettings.getInstance().TYPE_COOK_EXHAUSTIVE); + } + myCbDropCasts.setMnemonic('D'); myCbPreserveRawArrays.setMnemonic('P'); myCbLeaveObjectParameterizedTypesRaw.setMnemonic('L'); + myCbExhaustive.setMnemonic('E'); gbConstraints.insets = new Insets(4, 8, 4, 8); @@ -119,6 +125,10 @@ public class TypeCookDialog extends RefactoringDialog { gbConstraints.gridwidth = 2; optionsPanel.add(myCbLeaveObjectParameterizedTypesRaw, gbConstraints); + gbConstraints.gridx = 0; + gbConstraints.gridwidth = 2; + optionsPanel.add(myCbExhaustive, gbConstraints); + return optionsPanel; } @@ -135,6 +145,7 @@ 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(); return new Settings() { public boolean dropObsoleteCasts() { @@ -148,6 +159,10 @@ public class TypeCookDialog extends RefactoringDialog { public boolean leaveObjectParameterizedTypesRaw() { return leaveObjectParameterizedTypesRaw; } + + public boolean exhaustive() { + return exh; + } }; } } diff --git a/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java b/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java index 7b9efb4326b6..ebb948d18b5e 100644 --- a/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java +++ b/source/com/intellij/refactoring/typeCook/TypeCookProcessor.java @@ -49,10 +49,10 @@ public class TypeCookProcessor extends BaseRefactoringProcessor { tree.resolve(); - final Binding[] solutions = tree.getSolutions(); + final Binding solution = tree.getBestSolution(); - if (solutions.length > 0) { - myResult.incorporateSolution(solutions[0]); + if (solution!= null) { + myResult.incorporateSolution(solution); } } } @@ -87,6 +87,8 @@ public class TypeCookProcessor extends BaseRefactoringProcessor { myResult.apply (victims); + LOG.debug (myResult.getReport()); + UndoManager.getInstance(myProject).undoableActionPerformed(new DummyComplexUndoableAction()); // force confirmation dialog for undo } diff --git a/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java b/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java index da6fd511d3fa..72b92959215c 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java +++ b/source/com/intellij/refactoring/typeCook/deductive/builder/Result.java @@ -28,76 +28,19 @@ public class Result { private final HashSet myVictims; private final HashMap myTypes; - private final Project myProject; private final Settings mySettings; - private final PsiTypeVariableFactory myFactory; + private final HashSet myCasts; + + private int myCookedNumber = -1; + private int myCastsRemoved = -1; private Binding myBinding; public Result(final System system) { myVictims = system.myElements; myTypes = system.myTypes; - myProject = system.myProject; mySettings = system.mySettings; - myFactory = system.getVariableFactory(); - } - - final private PsiType substitute(final PsiType t) { - if (t instanceof PsiWildcardType) { - final PsiWildcardType wcType = (PsiWildcardType)t; - final PsiType bound = wcType.getBound(); - - if (bound == null) { - return t; - } - - final PsiManager manager = PsiManager.getInstance(myProject); - final PsiType subst = substitute(bound); - return subst instanceof PsiWildcardType ? subst : wcType.isExtends() ? PsiWildcardType.createExtends(manager, subst) : PsiWildcardType.createSuper(manager, subst); - } - else if (t instanceof PsiTypeVariable) { - if (myBinding == null) { - return null; - } - - final PsiType b = myBinding.apply(t); - - if (b instanceof Bottom || b instanceof PsiTypeVariable) { - return null; - } - - return substitute(b); - } - else if (t instanceof Bottom) { - return null; - } - else if (t instanceof PsiArrayType) { - return substitute(((PsiArrayType)t).getComponentType()).createArrayType(); - } - else if (t instanceof PsiClassType) { - final PsiClassType.ClassResolveResult result = ((PsiClassType)t).resolveGenerics(); - - final PsiClass aClass = result.getElement(); - final PsiSubstitutor aSubst = result.getSubstitutor(); - - if (aClass == null) { - return t; - } - - PsiSubstitutor theSubst = PsiSubstitutor.EMPTY; - - for (final Iterator p = aSubst.getSubstitutionMap().keySet().iterator(); p.hasNext();) { - final PsiTypeParameter parm = p.next(); - final PsiType type = aSubst.substitute(parm); - - theSubst = theSubst.put(parm, substitute(type)); - } - - return aClass.getManager().getElementFactory().createType(aClass, theSubst); - } - else { - return t; - } + myCasts = system.myCasts; } public void incorporateSolution(final Binding binding) { @@ -110,24 +53,26 @@ public class Result { } public PsiType getCookedType(final PsiElement element) { - return substitute(myTypes.get(element)); + if (myBinding != null) { + return myBinding.substitute(myTypes.get(element)); + } + + return Util.getType(element); } public HashSet getCookedElements() { + myCookedNumber = 0; + final HashSet set = new HashSet(); for (final Iterator e = myVictims.iterator(); e.hasNext();) { final PsiElement element = e.next(); - final PsiType originalType = - element instanceof PsiMethod - ? ((PsiMethod)element).getReturnType() - : element instanceof PsiVariable - ? ((PsiVariable)element).getType() - : ((PsiExpression)element).getType(); + final PsiType originalType = Util.getType(element); final PsiType cookedType = getCookedType(element); if (cookedType != null && !originalType.equals(cookedType)) { set.add(element); + myCookedNumber++; } } @@ -139,19 +84,33 @@ public class Result { final PsiElement element = e.next(); Util.changeType(element, getCookedType(element)); + } - if (mySettings.dropObsoleteCasts() && element instanceof PsiTypeCastExpression){ - final PsiTypeCastExpression cast = ((PsiTypeCastExpression)element); + if (mySettings.dropObsoleteCasts()) { + myCastsRemoved = 0; - if (cast.getType().equals(cast.getOperand().getType())){ + for (final Iterator c = myCasts.iterator(); c.hasNext();) { + final PsiTypeCastExpression cast = c.next(); + + if (cast.getType().equals(cast.getOperand().getType())) { try { cast.replace(cast.getOperand()); + myCastsRemoved++; } catch (IncorrectOperationException e1) { - LOG.error (e1); + LOG.error(e1); } } } } } + + private String getRatio(final int x, final int y) { + return x != -1 ? x + " of " + y + (y != 0 ? " (" + (x * 100 / y) + "%)" : "") : "not calculated"; + } + + public String getReport() { + return "Items cooked : " + getRatio(myCookedNumber, myVictims.size()) + "\n" + + "Casts removed: " + getRatio(myCastsRemoved, myCasts.size()) + "\n"; + } } diff --git a/source/com/intellij/refactoring/typeCook/deductive/builder/System.java b/source/com/intellij/refactoring/typeCook/deductive/builder/System.java index 6c209687741a..133202ef8c66 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/builder/System.java +++ b/source/com/intellij/refactoring/typeCook/deductive/builder/System.java @@ -24,6 +24,7 @@ import java.util.*; public class System { final HashSet myConstraints = new HashSet(); final HashSet myElements; + final HashSet myCasts; final HashMap myTypes; final PsiTypeVariableFactory myTypeVariableFactory; final Project myProject; @@ -42,6 +43,7 @@ public class System { myTypeVariableFactory = factory; myBoundVariables = null; mySettings = settings; + myCasts = new HashSet (); } public Project getProject() { @@ -52,6 +54,10 @@ public class System { return myConstraints; } + public void addCast (final PsiTypeCastExpression cast){ + myCasts.add(cast); + } + public void addSubtypeConstraint(final PsiType left, final PsiType right) { if (left == null || right == null) { return; @@ -330,11 +336,7 @@ public class System { for (final Iterator e = myElements.iterator(); e.hasNext();) { final PsiElement element = e.next(); - data[i++] = - (element instanceof PsiVariable ? ((PsiVariable)element).getType() : - element instanceof PsiMethod ? ((PsiMethod)element).getReturnType() : ((PsiExpression)element).getType()).getCanonicalText() + - "\\n" + - elementString(element); + data[i++] = Util.getType(element).getCanonicalText() + "\\n" + elementString(element); } Arrays.sort(data, @@ -375,7 +377,7 @@ public class System { return element.toString(); } - public String dumpResult(final Binding[] bindings) { + public String dumpResult(final Binding bestBinding) { final String[] data = new String[myElements.size()]; class Substitutor { @@ -395,8 +397,8 @@ public class System { : PsiWildcardType.createSuper(manager, subst); } else if (t instanceof PsiTypeVariable) { - if (bindings.length > 0) { - final PsiType b = bindings[0].apply(t); + if (bestBinding != null) { + final PsiType b = bestBinding.apply(t); if (b instanceof Bottom || b instanceof PsiTypeVariable) { return null; @@ -471,4 +473,8 @@ public class System { return repr.toString(); } + + public Settings getSettings() { + return mySettings; + } } diff --git a/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java b/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java index aa76495c16c9..299e72eb9e72 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java +++ b/source/com/intellij/refactoring/typeCook/deductive/builder/SystemBuilder.java @@ -168,21 +168,7 @@ public class SystemBuilder { return t; } - if (e instanceof PsiVariable) { - t = ((PsiVariable)e).getType(); - } - else if (e instanceof PsiTypeCastExpression) { - t = ((PsiTypeCastExpression)e).getCastType().getType(); - } - else if (e instanceof PsiNewExpression) { - t = ((PsiNewExpression)e).getType(); - } - else if (e instanceof PsiMethod) { - t = ((PsiMethod)e).getReturnType(); - } - else { - LOG.error("Variable, method, new or cast expected but found " + (e == null ? " null" : e.getClass().getName())); - } + t = Util.getType(e); final PsiType parameterizedType = Util.createParameterizedType(t, myTypeVariableFactory); @@ -198,17 +184,7 @@ public class SystemBuilder { return t; } - if (e instanceof PsiVariable) { - return Util.banalize(((PsiVariable)e).getType()); - } - else if (e instanceof PsiExpression) { - return Util.banalize(((PsiExpression)e).getType()); - } - else if (e instanceof PsiMethod) { - return Util.banalize(((PsiMethod)e).getReturnType()); - } - - return null; + return Util.banalize(Util.getType(e)); } private boolean isCooked(final PsiElement element) { @@ -699,11 +675,6 @@ public class SystemBuilder { e.valuateType(expression.getThenExpression())); } - //public void visitMethodCallExpression(final PsiMethodCallExpression expression) { - // super.visitMethodCallExpression(expression); - // e.valuateType(expression); - //} - public void visitCallExpression(final PsiCallExpression expression) { super.visitCallExpression(expression); e.valuateType(expression); @@ -722,9 +693,20 @@ public class SystemBuilder { public void visitTypeCastExpression(final PsiTypeCastExpression expression) { super.visitTypeCastExpression(expression); + system.addCast(expression); system.addSubtypeConstraint(e.valuateType(expression.getOperand()), e.valuateType(expression)); } + public void visitVariable(final PsiVariable variable) { + super.visitVariable(variable); + + final PsiExpression init = variable.getInitializer(); + + if (init != null) { + system.addSubtypeConstraint(e.valuateType(init), getType(variable)); + } + } + public void visitNewExpression(final PsiNewExpression expression) { super.visitNewExpression(expression); @@ -755,9 +737,7 @@ public class SystemBuilder { } private void addBoundConstraints(final System system, final PsiType definedType, final PsiElement element) { - final PsiType elemenType = - (element instanceof PsiMethod) ? ((PsiMethod)element).getReturnType() : - (element instanceof PsiVariable) ? ((PsiVariable)element).getType() : null; + final PsiType elemenType = Util.getType(element); if (elemenType != null) { new Object() { diff --git a/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java b/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java index 853f057c207e..1728a5269195 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java +++ b/source/com/intellij/refactoring/typeCook/deductive/resolver/Binding.java @@ -16,6 +16,8 @@ import java.util.HashSet; public abstract class Binding { public abstract PsiType apply(PsiType type); + public abstract PsiType substitute(PsiType type); + abstract Binding compose(Binding b); final static int BETTER = 0; @@ -33,7 +35,9 @@ public abstract class Binding { public abstract boolean binds(final PsiTypeVariable var); - public abstract void merge (Binding b, boolean removeObject); + public abstract void merge(Binding b, boolean removeObject); - public abstract HashSet getBoundVariables (); + public abstract HashSet getBoundVariables(); + + public abstract int getWidth(); } diff --git a/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java b/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java index f67e525cd0ae..99f47608c3a8 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java +++ b/source/com/intellij/refactoring/typeCook/deductive/resolver/BindingFactory.java @@ -232,7 +232,7 @@ public class BindingFactory { final BindingImpl b2 = (BindingImpl)binding; final BindingImpl b1 = this; - int directoin = Binding.SAME; + int directoin = Binding.NONCOMPARABLE; boolean first = true; for (int i = 0; i < myBoundVariableIndices.length; i++) { @@ -526,6 +526,76 @@ public class BindingFactory { public HashSet getBoundVariables() { return myBoundVariables; } + + public int getWidth() { + int w = 0; + + for (int i = 0; i < myBindings.length; i++) { + final PsiType type = substitute(myBindings[i]); + + if (type != null) { + w++; + } + } + + return w; + } + + public PsiType substitute(final PsiType t) { + if (t instanceof PsiWildcardType) { + final PsiWildcardType wcType = (PsiWildcardType)t; + final PsiType bound = wcType.getBound(); + + if (bound == null) { + return t; + } + + final PsiManager manager = PsiManager.getInstance(myProject); + final PsiType subst = substitute(bound); + return subst instanceof PsiWildcardType ? subst : wcType.isExtends() + ? PsiWildcardType.createExtends(manager, subst) + : PsiWildcardType.createSuper(manager, subst); + } + else if (t instanceof PsiTypeVariable) { + final PsiType b = apply(t); + + if (b instanceof Bottom || b instanceof PsiTypeVariable) { + return null; + } + + return substitute(b); + } + else if (t instanceof Bottom) { + return null; + } + else if (t instanceof PsiArrayType) { + return substitute(((PsiArrayType)t).getComponentType()).createArrayType(); + } + else if (t instanceof PsiClassType) { + final PsiClassType.ClassResolveResult result = ((PsiClassType)t).resolveGenerics(); + + final PsiClass aClass = result.getElement(); + final PsiSubstitutor aSubst = result.getSubstitutor(); + + if (aClass == null) { + return t; + } + + PsiSubstitutor theSubst = PsiSubstitutor.EMPTY; + + for (final Iterator p = aSubst.getSubstitutionMap().keySet().iterator(); p.hasNext();) { + final PsiTypeParameter parm = p.next(); + final PsiType type = aSubst.substitute(parm); + + theSubst = theSubst.put(parm, substitute(type)); + } + + return aClass.getManager().getElementFactory().createType(aClass, theSubst); + } + else { + return t; + } + } } interface Balancer { @@ -711,7 +781,7 @@ public class BindingFactory { } public Binding typeVar(PsiType x, PsiTypeVariable y) { - if (x == null){ + if (x == null) { return create(y, Bottom.BOTTOM); } diff --git a/source/com/intellij/refactoring/typeCook/deductive/resolver/ResolverTree.java b/source/com/intellij/refactoring/typeCook/deductive/resolver/ResolverTree.java index 89422622f101..0151dc7e7796 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/resolver/ResolverTree.java +++ b/source/com/intellij/refactoring/typeCook/deductive/resolver/ResolverTree.java @@ -13,6 +13,7 @@ import com.intellij.refactoring.typeCook.deductive.builder.Constraint; import com.intellij.refactoring.typeCook.deductive.builder.Subtype; import com.intellij.refactoring.typeCook.Bottom; import com.intellij.refactoring.typeCook.Util; +import com.intellij.refactoring.typeCook.Settings; import com.intellij.util.graph.Graph; import com.intellij.util.graph.DFSTBuilder; import com.intellij.util.ArrayUtil; @@ -35,6 +36,8 @@ public class ResolverTree { private Binding myCurrentBinding; private SolutionHolder mySolutions; private Project myProject; + private HashMap myBindingDegree; + private Settings mySettings; private HashSet myConstraints; @@ -44,6 +47,8 @@ public class ResolverTree { myCurrentBinding = myBindingFactory.create(); myConstraints = system.getConstraints(); myProject = system.getProject(); + myBindingDegree = calculateDegree(system.getConstraints()); + mySettings = system.getSettings (); reduceCyclicVariables(); } @@ -54,6 +59,70 @@ public class ResolverTree { mySolutions = parent.mySolutions; myConstraints = constraints; myProject = parent.myProject; + myBindingDegree = calculateDegree(constraints); + mySettings = parent.mySettings; + } + + private static class PsiTypeVarCollector extends PsiExtendedTypeVisitor { + final HashSet mySet = new HashSet(); + + public Object visitTypeVariable(final PsiTypeVariable var) { + mySet.add(var); + + return null; + } + + public HashSet getSet(final PsiType type) { + type.accept(this); + return mySet; + } + } + + private boolean isBoundElseWhere(final PsiTypeVariable var) { + final Integer deg = myBindingDegree.get(var); + + return deg == null || deg.intValue() > 1; + } + + private boolean canBePruned(final Binding b) { + for (final Iterator v = b.getBoundVariables().iterator(); v.hasNext();) { + final PsiTypeVariable var = v.next(); + final PsiType type = b.apply(var); + + if (!(type instanceof PsiTypeVariable) && isBoundElseWhere(var)) { + return false; + } + } + + return !mySettings.exhaustive(); + } + + private HashMap calculateDegree(final HashSet constraints) { + final HashMap result = new HashMap(); + + for (final Iterator c = constraints.iterator(); c.hasNext();) { + final Constraint constr = c.next(); + + final PsiTypeVarCollector collector = new PsiTypeVarCollector(); + + new Object() { + void setDegree(final HashSet set) { + for (final Iterator v = set.iterator(); v.hasNext();) { + final PsiTypeVariable var = v.next(); + final Integer deg = result.get(var); + + if (deg == null) { + result.put(var, new Integer(1)); + } + else { + result.put(var, new Integer(deg.intValue() + 1)); + } + } + } + }.setDegree(collector.getSet(constr.getRight())); + } + + return result; } private HashSet apply(final Binding b, final HashSet constraints) { @@ -190,21 +259,6 @@ public class ResolverTree { } } - private static class PsiTypeVarCollector extends PsiExtendedTypeVisitor { - final HashSet mySet = new HashSet(); - - public Object visitTypeVariable(final PsiTypeVariable var) { - mySet.add (var); - - return null; - } - - public HashSet getSet (final PsiType type){ - type.accept(this); - return mySet; - } - } - private void reduceTypeType(final Constraint constr) { final PsiType left = constr.getLeft(); final PsiType right = constr.getRight(); @@ -217,21 +271,11 @@ public class ResolverTree { if (indicator == 0) { return; } - else if (indicator == 2){// && riseBinding.equals(sinkBinding)) { - switch (riseBinding.compare(sinkBinding)) { - case Binding.SAME: - //case Binding.BETTER: - indicator = 1; - sinkBinding = null; - break; - - //case Binding.WORSE: - // indicator = 1; - // riseBinding = null; - //break; - } + else if ((indicator == 2 && riseBinding.equals(sinkBinding)) || canBePruned(riseBinding)) { + indicator = 1; + sinkBinding = null; } - + myConstraints.remove(constr); mySons = new ResolverTree[indicator]; @@ -327,18 +371,9 @@ public class ResolverTree { if (indicator == 0) { return; } - else if (indicator == 2){// && riseBinding.equals(sinkBinding)) { - switch (riseBinding.compare(sinkBinding)) { - case Binding.SAME: - indicator = 1; - sinkBinding = null; - break; - - //case Binding.WORSE: - // indicator = 1; - // riseBinding = null; - // break; - } + else if ((indicator == 2 && riseBinding.equals(sinkBinding)) || canBePruned(riseBinding)) { + indicator = 1; + sinkBinding = null; } PsiType[] riseRange = new PsiType[]{}; @@ -629,10 +664,10 @@ public class ResolverTree { if (mySons.length > 0) { for (int i = 0; i < mySons.length; i++) { - final ResolverTree son = mySons[i]; - if (son != null) { - son.resolve(); + if (mySons[i] != null) { + mySons[i].resolve(); + mySons[i] = null; } } } @@ -645,7 +680,7 @@ public class ResolverTree { } } - public Binding[] getSolutions() { - return mySolutions.getBestSolutions(); + public Binding getBestSolution() { + return mySolutions.getBestSolution(); } } diff --git a/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java b/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java index 8b0bddea8f9e..c362aaedd368 100644 --- a/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java +++ b/source/com/intellij/refactoring/typeCook/deductive/resolver/SolutionHolder.java @@ -18,24 +18,41 @@ public class SolutionHolder { final Binding b2 = i.next(); switch (b1.compare(b2)) { - case Binding.WORSE: - case Binding.SAME: - return; + case Binding.WORSE: + case Binding.SAME: + return; - case Binding.BETTER: - mySolutions.remove(b2); - mySolutions.addFirst(b1); - return; + case Binding.BETTER: + mySolutions.remove(b2); + mySolutions.addFirst(b1); + return; - case Binding.NONCOMPARABLE: - continue; + case Binding.NONCOMPARABLE: + continue; } } mySolutions.addFirst(b1); } - public Binding[] getBestSolutions() { + public Binding[] getTopSolutions() { return mySolutions.toArray(new Binding[]{}); } + + public Binding getBestSolution() { + Binding best = null; + int width = 0; + + for (final Iterator b = mySolutions.iterator(); b.hasNext();) { + final Binding binding = b.next(); + final int w = binding.getWidth(); + + if (w > width) { + width = w; + best = binding; + } + } + + return best; + } } diff --git a/testSource/com/intellij/refactoring/TypeCookTest.java b/testSource/com/intellij/refactoring/TypeCookTest.java index 1f1838534444..aa6c5109465c 100644 --- a/testSource/com/intellij/refactoring/TypeCookTest.java +++ b/testSource/com/intellij/refactoring/TypeCookTest.java @@ -647,6 +647,10 @@ public class TypeCookTest extends MultiFileTestCase { public boolean leaveObjectParameterizedTypesRaw() { return false; } + + public boolean exhaustive() { + return false; + } }); final com.intellij.refactoring.typeCook.deductive.builder.System commonSystem = b.build(new PsiElement[]{aClass}); @@ -668,14 +672,14 @@ public class TypeCookTest extends MultiFileTestCase { } } - Binding[] bindings = null; + Binding binding = null; if (system != null) { final ResolverTree tree = new ResolverTree(system); tree.resolve(); - bindings = tree.getSolutions(); + binding = tree.getBestSolution(); } //System.out.println("" + system); @@ -715,7 +719,7 @@ public class TypeCookTest extends MultiFileTestCase { //d.analyze(); //d.relax(); - itemRepr = system != null ? system.dumpResult(bindings) : commonSystem.dumpString(); //d.resultString(); + itemRepr = system != null ? system.dumpResult(binding) : commonSystem.dumpString(); //d.resultString(); itemName = className + ".1.items"; patternName = PathManagerEx.getTestDataPath() + getTestRoot() + getTestName(true) + "/after/" + itemName;