mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Type Cook
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -21,7 +21,8 @@ public class TypeCookRefactoringImpl extends RefactoringImpl<TypeCookProcessor>
|
||||
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<TypeCookProcessor>
|
||||
return leaveObjectsRaw;
|
||||
}
|
||||
|
||||
public boolean exhaustive() {
|
||||
return exh;
|
||||
}
|
||||
|
||||
public boolean preserveRawArrays() {
|
||||
return preserveRawArrays;
|
||||
}
|
||||
|
||||
@@ -11,4 +11,5 @@ public interface Settings {
|
||||
boolean dropObsoleteCasts();
|
||||
boolean preserveRawArrays();
|
||||
boolean leaveObjectParameterizedTypesRaw();
|
||||
boolean exhaustive();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -28,76 +28,19 @@ public class Result {
|
||||
|
||||
private final HashSet<PsiElement> myVictims;
|
||||
private final HashMap<PsiElement, PsiType> myTypes;
|
||||
private final Project myProject;
|
||||
private final Settings mySettings;
|
||||
private final PsiTypeVariableFactory myFactory;
|
||||
private final HashSet<PsiTypeCastExpression> 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<PsiTypeParameter> 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<PsiElement> getCookedElements() {
|
||||
myCookedNumber = 0;
|
||||
|
||||
final HashSet<PsiElement> set = new HashSet<PsiElement>();
|
||||
|
||||
for (final Iterator<PsiElement> 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<PsiTypeCastExpression> 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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.*;
|
||||
public class System {
|
||||
final HashSet<Constraint> myConstraints = new HashSet<Constraint>();
|
||||
final HashSet<PsiElement> myElements;
|
||||
final HashSet<PsiTypeCastExpression> myCasts;
|
||||
final HashMap<PsiElement, PsiType> myTypes;
|
||||
final PsiTypeVariableFactory myTypeVariableFactory;
|
||||
final Project myProject;
|
||||
@@ -42,6 +43,7 @@ public class System {
|
||||
myTypeVariableFactory = factory;
|
||||
myBoundVariables = null;
|
||||
mySettings = settings;
|
||||
myCasts = new HashSet<PsiTypeCastExpression> ();
|
||||
}
|
||||
|
||||
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<PsiElement> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<PsiTypeVariable> getBoundVariables ();
|
||||
public abstract HashSet<PsiTypeVariable> getBoundVariables();
|
||||
|
||||
public abstract int getWidth();
|
||||
}
|
||||
|
||||
@@ -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<PsiTypeVariable> 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<PsiTypeParameter> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<PsiTypeVariable, Integer> myBindingDegree;
|
||||
private Settings mySettings;
|
||||
|
||||
private HashSet<Constraint> 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<PsiTypeVariable> mySet = new HashSet<PsiTypeVariable>();
|
||||
|
||||
public Object visitTypeVariable(final PsiTypeVariable var) {
|
||||
mySet.add(var);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public HashSet<PsiTypeVariable> 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<PsiTypeVariable> 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<PsiTypeVariable, Integer> calculateDegree(final HashSet<Constraint> constraints) {
|
||||
final HashMap<PsiTypeVariable, Integer> result = new HashMap<PsiTypeVariable, Integer>();
|
||||
|
||||
for (final Iterator<Constraint> c = constraints.iterator(); c.hasNext();) {
|
||||
final Constraint constr = c.next();
|
||||
|
||||
final PsiTypeVarCollector collector = new PsiTypeVarCollector();
|
||||
|
||||
new Object() {
|
||||
void setDegree(final HashSet<PsiTypeVariable> set) {
|
||||
for (final Iterator<PsiTypeVariable> 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<Constraint> apply(final Binding b, final HashSet<Constraint> constraints) {
|
||||
@@ -190,21 +259,6 @@ public class ResolverTree {
|
||||
}
|
||||
}
|
||||
|
||||
private static class PsiTypeVarCollector extends PsiExtendedTypeVisitor {
|
||||
final HashSet<PsiTypeVariable> mySet = new HashSet<PsiTypeVariable>();
|
||||
|
||||
public Object visitTypeVariable(final PsiTypeVariable var) {
|
||||
mySet.add (var);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public HashSet<PsiTypeVariable> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Binding> b = mySolutions.iterator(); b.hasNext();) {
|
||||
final Binding binding = b.next();
|
||||
final int w = binding.getWidth();
|
||||
|
||||
if (w > width) {
|
||||
width = w;
|
||||
best = binding;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user