| /******************************************************************************* |
| * Copyright (c) 2000, 2011 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.refactoring.structure.constraints; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.Assert; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CastVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ConstraintVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraint2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraintVariable; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeSet; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ImmutableTypeVariable2; |
| import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.TypeEquivalenceSet; |
| |
| /** |
| * Type constraint solver to solve supertype constraint models. |
| * |
| * @since 3.1 |
| */ |
| public class SuperTypeConstraintsSolver { |
| |
| /** The type estimate data (type: <code>TType</code>) */ |
| public static final String DATA_TYPE_ESTIMATE= "te"; //$NON-NLS-1$ |
| |
| /** The type constraint model to solve */ |
| protected final SuperTypeConstraintsModel fModel; |
| |
| /** The obsolete casts (element type: <code><ICompilationUnit, Collection<CastVariable2>></code>) */ |
| protected Map<ICompilationUnit, Collection<CastVariable2>> fObsoleteCasts= null; |
| |
| /** The list of constraint variables to be processed */ |
| protected LinkedList<ConstraintVariable2> fProcessable= null; |
| |
| /** The type occurrences (element type: <code><ICompilationUnit, Collection<ITypeConstraintVariable></code>) */ |
| protected Map<ICompilationUnit, Collection<ITypeConstraintVariable>> fTypeOccurrences= null; |
| |
| /** |
| * Creates a new super type constraints solver. |
| * |
| * @param model the model to solve |
| */ |
| public SuperTypeConstraintsSolver(final SuperTypeConstraintsModel model) { |
| Assert.isNotNull(model); |
| |
| fModel= model; |
| } |
| |
| /** |
| * Computes the necessary equality constraints for conditional expressions. |
| * |
| * @param constraints the type constraints (element type: <code>ITypeConstraint2</code>) |
| * @param level the compliance level |
| */ |
| private void computeConditionalTypeConstraints(final Collection<ITypeConstraint2> constraints, final int level) { |
| ITypeConstraint2 constraint= null; |
| for (final Iterator<ITypeConstraint2> iterator= constraints.iterator(); iterator.hasNext();) { |
| constraint= iterator.next(); |
| if (constraint instanceof ConditionalTypeConstraint) { |
| final ConditionalTypeConstraint conditional= (ConditionalTypeConstraint) constraint; |
| fModel.createEqualityConstraint(constraint.getLeft(), constraint.getRight()); |
| fModel.createEqualityConstraint(conditional.getExpression(), constraint.getLeft()); |
| fModel.createEqualityConstraint(conditional.getExpression(), constraint.getRight()); |
| } |
| } |
| } |
| |
| /** |
| * Computes the necessary equality constraints for non-covariant return types. |
| * |
| * @param constraints the type constraints (element type: <code>ITypeConstraint2</code>) |
| * @param level the compliance level |
| */ |
| private void computeNonCovariantConstraints(final Collection<ITypeConstraint2> constraints, final int level) { |
| if (level != 3) { |
| ITypeConstraint2 constraint= null; |
| for (final Iterator<ITypeConstraint2> iterator= constraints.iterator(); iterator.hasNext();) { |
| constraint= iterator.next(); |
| if (constraint instanceof CovariantTypeConstraint) |
| fModel.createEqualityConstraint(constraint.getLeft(), constraint.getRight()); |
| } |
| } |
| } |
| |
| /** |
| * Computes the obsolete casts for the specified cast variables. |
| * |
| * @param variables the cast variables (element type: <code>CastVariable2</code>) |
| */ |
| private void computeObsoleteCasts(final Collection<CastVariable2> variables) { |
| fObsoleteCasts= new HashMap<>(); |
| CastVariable2 variable= null; |
| for (final Iterator<CastVariable2> iterator= variables.iterator(); iterator.hasNext();) { |
| variable= iterator.next(); |
| final TType type= (TType) variable.getExpressionVariable().getData(DATA_TYPE_ESTIMATE); |
| if (type != null && type.canAssignTo(variable.getType())) { |
| final ICompilationUnit unit= variable.getCompilationUnit(); |
| Collection<CastVariable2> casts= fObsoleteCasts.get(unit); |
| if (casts != null) |
| casts.add(variable); |
| else { |
| casts= new ArrayList<>(1); |
| casts.add(variable); |
| fObsoleteCasts.put(unit, casts); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Computes the initial type estimate for the specified constraint variable. |
| * |
| * @param variable the constraint variable |
| * @return the initial type estimate |
| */ |
| protected ITypeSet computeTypeEstimate(final ConstraintVariable2 variable) { |
| final TType type= variable.getType(); |
| if (variable instanceof ImmutableTypeVariable2 || !type.getErasure().equals(fModel.getSubType().getErasure())) |
| return SuperTypeSet.createTypeSet(type); |
| return SuperTypeSet.createTypeSet(type, fModel.getSuperType()); |
| } |
| |
| /** |
| * Computes the initial type estimates for the specified variables. |
| * |
| * @param variables the constraint variables (element type: <code>ConstraintVariable2</code>) |
| */ |
| private void computeTypeEstimates(final Collection<ConstraintVariable2> variables) { |
| ConstraintVariable2 variable= null; |
| for (final Iterator<ConstraintVariable2> iterator= variables.iterator(); iterator.hasNext();) { |
| variable= iterator.next(); |
| TypeEquivalenceSet set= variable.getTypeEquivalenceSet(); |
| if (set == null) { |
| set= new TypeEquivalenceSet(variable); |
| set.setTypeEstimate(computeTypeEstimate(variable)); |
| variable.setTypeEquivalenceSet(set); |
| } else { |
| ITypeSet estimate= variable.getTypeEstimate(); |
| if (estimate == null) { |
| estimate= SuperTypeSet.getUniverse(); |
| for (ConstraintVariable2 v : set.getContributingVariables()) { |
| estimate= estimate.restrictedTo(computeTypeEstimate(v)); |
| } |
| set.setTypeEstimate(estimate); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Computes a single type for each of the specified constraint variables. |
| * |
| * @param variables the constraint variables (element type: <code>ConstraintVariable2</code>) |
| */ |
| private void computeTypeOccurrences(final Collection<ConstraintVariable2> variables) { |
| fTypeOccurrences= new HashMap<>(); |
| final TType superErasure= fModel.getSuperType().getErasure(); |
| TType estimatedType= null; |
| ITypeSet set= null; |
| ICompilationUnit unit= null; |
| ConstraintVariable2 variable= null; |
| ITypeConstraintVariable declaration= null; |
| TType variableType= null; |
| for (final Iterator<ConstraintVariable2> iterator= variables.iterator(); iterator.hasNext();) { |
| variable= iterator.next(); |
| if (variable instanceof ITypeConstraintVariable) { |
| declaration= (ITypeConstraintVariable) variable; |
| variableType= variable.getType(); |
| set= declaration.getTypeEstimate(); |
| if (set != null) { |
| estimatedType= set.chooseSingleType(); |
| if (estimatedType != null) { |
| final TType typeErasure= estimatedType.getErasure(); |
| if (!typeErasure.equals(variableType.getErasure()) && typeErasure.equals(superErasure)) { |
| declaration.setData(DATA_TYPE_ESTIMATE, estimatedType); |
| unit= declaration.getCompilationUnit(); |
| if (unit != null) { |
| Collection<ITypeConstraintVariable> matches= fTypeOccurrences.get(unit); |
| if (matches != null) |
| matches.add(declaration); |
| else { |
| matches= new ArrayList<>(1); |
| matches.add(declaration); |
| fTypeOccurrences.put(unit, matches); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the computed obsolete casts. |
| * |
| * @return the obsolete casts (element type: <code><ICompilationUnit, Collection<CastVariable2>></code>) |
| */ |
| public final Map<ICompilationUnit, Collection<CastVariable2>> getObsoleteCasts() { |
| return fObsoleteCasts; |
| } |
| |
| /** |
| * Returns the computed type occurrences. |
| * |
| * @return the type occurrences (element type: <code><ICompilationUnit, Collection<IDeclaredConstraintVariable></code>) |
| */ |
| public final Map<ICompilationUnit, Collection<ITypeConstraintVariable>> getTypeOccurrences() { |
| return fTypeOccurrences; |
| } |
| |
| /** |
| * Processes the given constraints on the constraint variable and propagates it. |
| * |
| * @param constraints the type constraints to process (element type: <code>ITypeConstraint2</code>) |
| */ |
| private void processConstraints(final Collection<ITypeConstraint2> constraints) { |
| final int level= fModel.getCompliance(); |
| ITypeConstraint2 constraint= null; |
| for (final Iterator<ITypeConstraint2> iterator= constraints.iterator(); iterator.hasNext();) { |
| constraint= iterator.next(); |
| if ((level == 3 || !(constraint instanceof CovariantTypeConstraint)) && !(constraint instanceof ConditionalTypeConstraint)) { |
| final ConstraintVariable2 leftVariable= constraint.getLeft(); |
| final ITypeSet leftEstimate= leftVariable.getTypeEstimate(); |
| final TypeEquivalenceSet set= leftVariable.getTypeEquivalenceSet(); |
| final ITypeSet newEstimate= leftEstimate.restrictedTo(constraint.getRight().getTypeEstimate()); |
| if (leftEstimate != newEstimate) { |
| set.setTypeEstimate(newEstimate); |
| fProcessable.addAll(Arrays.asList(set.getContributingVariables())); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Solves the constraints of the associated model. |
| */ |
| public final void solveConstraints() { |
| fProcessable= new LinkedList<>(); |
| final Collection<ConstraintVariable2> variables= fModel.getConstraintVariables(); |
| final Collection<ITypeConstraint2> constraints= fModel.getTypeConstraints(); |
| final int level= fModel.getCompliance(); |
| computeNonCovariantConstraints(constraints, level); |
| |
| // TODO: use most specific common type for AST.JLS3 |
| computeConditionalTypeConstraints(constraints, level); |
| |
| computeTypeEstimates(variables); |
| fProcessable.addAll(variables); |
| Collection<ITypeConstraint2> usage= null; |
| ConstraintVariable2 variable= null; |
| while (!fProcessable.isEmpty()) { |
| variable= fProcessable.removeFirst(); |
| usage= SuperTypeConstraintsModel.getVariableUsage(variable); |
| if (!usage.isEmpty()) |
| processConstraints(usage); |
| else |
| variable.setData(DATA_TYPE_ESTIMATE, variable.getTypeEstimate().chooseSingleType()); |
| } |
| computeTypeOccurrences(variables); |
| computeObsoleteCasts(fModel.getCastVariables()); |
| } |
| } |