1 /******************************************************************************* 2 * Copyright (c) 2000, 2011 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.corext.refactoring.structure.constraints; 15 16 import java.util.ArrayList; 17 import java.util.Arrays; 18 import java.util.Collection; 19 import java.util.HashMap; 20 import java.util.Iterator; 21 import java.util.LinkedList; 22 import java.util.Map; 23 24 import org.eclipse.core.runtime.Assert; 25 26 import org.eclipse.jdt.core.ICompilationUnit; 27 28 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType; 29 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.CastVariable2; 30 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ConstraintVariable2; 31 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraint2; 32 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraintVariable; 33 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeSet; 34 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ImmutableTypeVariable2; 35 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.TypeEquivalenceSet; 36 37 /** 38 * Type constraint solver to solve supertype constraint models. 39 * 40 * @since 3.1 41 */ 42 public class SuperTypeConstraintsSolver { 43 44 /** The type estimate data (type: <code>TType</code>) */ 45 public static final String DATA_TYPE_ESTIMATE= "te"; //$NON-NLS-1$ 46 47 /** The type constraint model to solve */ 48 protected final SuperTypeConstraintsModel fModel; 49 50 /** The obsolete casts (element type: <code><ICompilationUnit, Collection<CastVariable2>></code>) */ 51 protected Map<ICompilationUnit, Collection<CastVariable2>> fObsoleteCasts= null; 52 53 /** The list of constraint variables to be processed */ 54 protected LinkedList<ConstraintVariable2> fProcessable= null; 55 56 /** The type occurrences (element type: <code><ICompilationUnit, Collection<ITypeConstraintVariable></code>) */ 57 protected Map<ICompilationUnit, Collection<ITypeConstraintVariable>> fTypeOccurrences= null; 58 59 /** 60 * Creates a new super type constraints solver. 61 * 62 * @param model the model to solve 63 */ SuperTypeConstraintsSolver(final SuperTypeConstraintsModel model)64 public SuperTypeConstraintsSolver(final SuperTypeConstraintsModel model) { 65 Assert.isNotNull(model); 66 67 fModel= model; 68 } 69 70 /** 71 * Computes the necessary equality constraints for conditional expressions. 72 * 73 * @param constraints the type constraints (element type: <code>ITypeConstraint2</code>) 74 * @param level the compliance level 75 */ computeConditionalTypeConstraints(final Collection<ITypeConstraint2> constraints, final int level)76 private void computeConditionalTypeConstraints(final Collection<ITypeConstraint2> constraints, final int level) { 77 ITypeConstraint2 constraint= null; 78 for (final Iterator<ITypeConstraint2> iterator= constraints.iterator(); iterator.hasNext();) { 79 constraint= iterator.next(); 80 if (constraint instanceof ConditionalTypeConstraint) { 81 final ConditionalTypeConstraint conditional= (ConditionalTypeConstraint) constraint; 82 fModel.createEqualityConstraint(constraint.getLeft(), constraint.getRight()); 83 fModel.createEqualityConstraint(conditional.getExpression(), constraint.getLeft()); 84 fModel.createEqualityConstraint(conditional.getExpression(), constraint.getRight()); 85 } 86 } 87 } 88 89 /** 90 * Computes the necessary equality constraints for non-covariant return types. 91 * 92 * @param constraints the type constraints (element type: <code>ITypeConstraint2</code>) 93 * @param level the compliance level 94 */ computeNonCovariantConstraints(final Collection<ITypeConstraint2> constraints, final int level)95 private void computeNonCovariantConstraints(final Collection<ITypeConstraint2> constraints, final int level) { 96 if (level != 3) { 97 ITypeConstraint2 constraint= null; 98 for (final Iterator<ITypeConstraint2> iterator= constraints.iterator(); iterator.hasNext();) { 99 constraint= iterator.next(); 100 if (constraint instanceof CovariantTypeConstraint) 101 fModel.createEqualityConstraint(constraint.getLeft(), constraint.getRight()); 102 } 103 } 104 } 105 106 /** 107 * Computes the obsolete casts for the specified cast variables. 108 * 109 * @param variables the cast variables (element type: <code>CastVariable2</code>) 110 */ computeObsoleteCasts(final Collection<CastVariable2> variables)111 private void computeObsoleteCasts(final Collection<CastVariable2> variables) { 112 fObsoleteCasts= new HashMap<>(); 113 CastVariable2 variable= null; 114 for (final Iterator<CastVariable2> iterator= variables.iterator(); iterator.hasNext();) { 115 variable= iterator.next(); 116 final TType type= (TType) variable.getExpressionVariable().getData(DATA_TYPE_ESTIMATE); 117 if (type != null && type.canAssignTo(variable.getType())) { 118 final ICompilationUnit unit= variable.getCompilationUnit(); 119 Collection<CastVariable2> casts= fObsoleteCasts.get(unit); 120 if (casts != null) 121 casts.add(variable); 122 else { 123 casts= new ArrayList<>(1); 124 casts.add(variable); 125 fObsoleteCasts.put(unit, casts); 126 } 127 } 128 } 129 } 130 131 /** 132 * Computes the initial type estimate for the specified constraint variable. 133 * 134 * @param variable the constraint variable 135 * @return the initial type estimate 136 */ computeTypeEstimate(final ConstraintVariable2 variable)137 protected ITypeSet computeTypeEstimate(final ConstraintVariable2 variable) { 138 final TType type= variable.getType(); 139 if (variable instanceof ImmutableTypeVariable2 || !type.getErasure().equals(fModel.getSubType().getErasure())) 140 return SuperTypeSet.createTypeSet(type); 141 return SuperTypeSet.createTypeSet(type, fModel.getSuperType()); 142 } 143 144 /** 145 * Computes the initial type estimates for the specified variables. 146 * 147 * @param variables the constraint variables (element type: <code>ConstraintVariable2</code>) 148 */ computeTypeEstimates(final Collection<ConstraintVariable2> variables)149 private void computeTypeEstimates(final Collection<ConstraintVariable2> variables) { 150 ConstraintVariable2 variable= null; 151 for (final Iterator<ConstraintVariable2> iterator= variables.iterator(); iterator.hasNext();) { 152 variable= iterator.next(); 153 TypeEquivalenceSet set= variable.getTypeEquivalenceSet(); 154 if (set == null) { 155 set= new TypeEquivalenceSet(variable); 156 set.setTypeEstimate(computeTypeEstimate(variable)); 157 variable.setTypeEquivalenceSet(set); 158 } else { 159 ITypeSet estimate= variable.getTypeEstimate(); 160 if (estimate == null) { 161 estimate= SuperTypeSet.getUniverse(); 162 for (ConstraintVariable2 v : set.getContributingVariables()) { 163 estimate= estimate.restrictedTo(computeTypeEstimate(v)); 164 } 165 set.setTypeEstimate(estimate); 166 } 167 } 168 } 169 } 170 171 /** 172 * Computes a single type for each of the specified constraint variables. 173 * 174 * @param variables the constraint variables (element type: <code>ConstraintVariable2</code>) 175 */ computeTypeOccurrences(final Collection<ConstraintVariable2> variables)176 private void computeTypeOccurrences(final Collection<ConstraintVariable2> variables) { 177 fTypeOccurrences= new HashMap<>(); 178 final TType superErasure= fModel.getSuperType().getErasure(); 179 TType estimatedType= null; 180 ITypeSet set= null; 181 ICompilationUnit unit= null; 182 ConstraintVariable2 variable= null; 183 ITypeConstraintVariable declaration= null; 184 TType variableType= null; 185 for (final Iterator<ConstraintVariable2> iterator= variables.iterator(); iterator.hasNext();) { 186 variable= iterator.next(); 187 if (variable instanceof ITypeConstraintVariable) { 188 declaration= (ITypeConstraintVariable) variable; 189 variableType= variable.getType(); 190 set= declaration.getTypeEstimate(); 191 if (set != null) { 192 estimatedType= set.chooseSingleType(); 193 if (estimatedType != null) { 194 final TType typeErasure= estimatedType.getErasure(); 195 if (!typeErasure.equals(variableType.getErasure()) && typeErasure.equals(superErasure)) { 196 declaration.setData(DATA_TYPE_ESTIMATE, estimatedType); 197 unit= declaration.getCompilationUnit(); 198 if (unit != null) { 199 Collection<ITypeConstraintVariable> matches= fTypeOccurrences.get(unit); 200 if (matches != null) 201 matches.add(declaration); 202 else { 203 matches= new ArrayList<>(1); 204 matches.add(declaration); 205 fTypeOccurrences.put(unit, matches); 206 } 207 } 208 } 209 } 210 } 211 } 212 } 213 } 214 215 /** 216 * Returns the computed obsolete casts. 217 * 218 * @return the obsolete casts (element type: <code><ICompilationUnit, Collection<CastVariable2>></code>) 219 */ getObsoleteCasts()220 public final Map<ICompilationUnit, Collection<CastVariable2>> getObsoleteCasts() { 221 return fObsoleteCasts; 222 } 223 224 /** 225 * Returns the computed type occurrences. 226 * 227 * @return the type occurrences (element type: <code><ICompilationUnit, Collection<IDeclaredConstraintVariable></code>) 228 */ getTypeOccurrences()229 public final Map<ICompilationUnit, Collection<ITypeConstraintVariable>> getTypeOccurrences() { 230 return fTypeOccurrences; 231 } 232 233 /** 234 * Processes the given constraints on the constraint variable and propagates it. 235 * 236 * @param constraints the type constraints to process (element type: <code>ITypeConstraint2</code>) 237 */ processConstraints(final Collection<ITypeConstraint2> constraints)238 private void processConstraints(final Collection<ITypeConstraint2> constraints) { 239 final int level= fModel.getCompliance(); 240 ITypeConstraint2 constraint= null; 241 for (final Iterator<ITypeConstraint2> iterator= constraints.iterator(); iterator.hasNext();) { 242 constraint= iterator.next(); 243 if ((level == 3 || !(constraint instanceof CovariantTypeConstraint)) && !(constraint instanceof ConditionalTypeConstraint)) { 244 final ConstraintVariable2 leftVariable= constraint.getLeft(); 245 final ITypeSet leftEstimate= leftVariable.getTypeEstimate(); 246 final TypeEquivalenceSet set= leftVariable.getTypeEquivalenceSet(); 247 final ITypeSet newEstimate= leftEstimate.restrictedTo(constraint.getRight().getTypeEstimate()); 248 if (leftEstimate != newEstimate) { 249 set.setTypeEstimate(newEstimate); 250 fProcessable.addAll(Arrays.asList(set.getContributingVariables())); 251 } 252 } 253 } 254 } 255 256 /** 257 * Solves the constraints of the associated model. 258 */ solveConstraints()259 public final void solveConstraints() { 260 fProcessable= new LinkedList<>(); 261 final Collection<ConstraintVariable2> variables= fModel.getConstraintVariables(); 262 final Collection<ITypeConstraint2> constraints= fModel.getTypeConstraints(); 263 final int level= fModel.getCompliance(); 264 computeNonCovariantConstraints(constraints, level); 265 266 // TODO: use most specific common type for AST.JLS3 267 computeConditionalTypeConstraints(constraints, level); 268 269 computeTypeEstimates(variables); 270 fProcessable.addAll(variables); 271 Collection<ITypeConstraint2> usage= null; 272 ConstraintVariable2 variable= null; 273 while (!fProcessable.isEmpty()) { 274 variable= fProcessable.removeFirst(); 275 usage= SuperTypeConstraintsModel.getVariableUsage(variable); 276 if (!usage.isEmpty()) 277 processConstraints(usage); 278 else 279 variable.setData(DATA_TYPE_ESTIMATE, variable.getTypeEstimate().chooseSingleType()); 280 } 281 computeTypeOccurrences(variables); 282 computeObsoleteCasts(fModel.getCastVariables()); 283 } 284 } 285