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>&lt;ICompilationUnit, Collection&lt;CastVariable2&gt;&gt;</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>&lt;ICompilationUnit, Collection&lt;ITypeConstraintVariable&gt;</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>&lt;ICompilationUnit, Collection&lt;CastVariable2&gt;&gt;</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>&lt;ICompilationUnit, Collection&lt;IDeclaredConstraintVariable&gt;</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