]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2011 IBM Corporation and others. | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * IBM Corporation - initial API and implementation | |
10 | *******************************************************************************/ | |
11 | package org.eclipse.jdt.internal.corext.refactoring.structure; | |
12 | ||
13 | import java.util.ArrayList; | |
14 | import java.util.Arrays; | |
15 | import java.util.HashSet; | |
16 | import java.util.List; | |
17 | import java.util.Set; | |
18 | ||
19 | import org.eclipse.core.runtime.Assert; | |
20 | ||
21 | import org.eclipse.jdt.core.IField; | |
22 | import org.eclipse.jdt.core.IMember; | |
23 | import org.eclipse.jdt.core.IMethod; | |
24 | import org.eclipse.jdt.core.IType; | |
25 | import org.eclipse.jdt.core.ITypeParameter; | |
26 | import org.eclipse.jdt.core.JavaModelException; | |
27 | import org.eclipse.jdt.core.Signature; | |
28 | ||
29 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
30 | ||
31 | /** | |
32 | * Utilities to create mappings between type variables of different types in a type hierarchy. | |
33 | */ | |
34 | public final class TypeVariableUtil { | |
35 | ||
36 | /** | |
37 | * Returns the composition of two type variable mappings. The type variables signatures can have an arbitrary format. | |
38 | * | |
39 | * @param first | |
40 | * the first type variable mapping | |
41 | * @param second | |
42 | * the second type variable mapping | |
43 | * @return the possibly empty composed type variable mapping | |
44 | */ | |
45 | public static TypeVariableMaplet[] composeMappings(final TypeVariableMaplet[] first, final TypeVariableMaplet[] second) { | |
46 | Assert.isNotNull(first); | |
47 | Assert.isNotNull(second); | |
48 | ||
49 | if (first.length == 0) | |
50 | return first; | |
51 | else if (second.length == 0) | |
52 | return second; | |
53 | else { | |
54 | TypeVariableMaplet source= null; | |
55 | TypeVariableMaplet target= null; | |
56 | final Set<TypeVariableMaplet> set= new HashSet<TypeVariableMaplet>(first.length * second.length); | |
57 | for (int index= 0; index < first.length; index++) { | |
58 | for (int offset= 0; offset < second.length; offset++) { | |
59 | source= first[index]; | |
60 | target= second[offset]; | |
61 | if (source.getTargetIndex() == target.getSourceIndex() && source.getTargetName().equals(target.getSourceName())) | |
62 | set.add(new TypeVariableMaplet(source.getSourceName(), index, target.getTargetName(), offset)); | |
63 | } | |
64 | } | |
65 | final TypeVariableMaplet[] mapping= new TypeVariableMaplet[set.size()]; | |
66 | set.toArray(mapping); | |
67 | return mapping; | |
68 | } | |
69 | } | |
70 | ||
71 | /** | |
72 | * Extracts the type variables from a signature | |
73 | * | |
74 | * @param signature | |
75 | * the signature to extract the type variables from | |
76 | * @param variables | |
77 | * the set of variables to fill in | |
78 | */ | |
79 | private static void extractTypeVariables(final String signature, final Set<String> variables) { | |
80 | Assert.isNotNull(signature); | |
81 | Assert.isNotNull(variables); | |
82 | ||
83 | final String[] arguments= Signature.getTypeArguments(signature); | |
84 | if (arguments.length == 0) { | |
85 | variables.add(Signature.toString(signature)); | |
86 | } else { | |
87 | for (int index= 0; index < arguments.length; index++) | |
88 | variables.add(Signature.toString(arguments[index])); | |
89 | } | |
90 | } | |
91 | ||
92 | /** | |
93 | * Returns the type variables referenced in the signature of the specified member. | |
94 | * | |
95 | * @param declaring | |
96 | * The declaring type of the specified member | |
97 | * @param member | |
98 | * the member to get its type variables. Can be a type, field or a method. | |
99 | * @return a possibly empty array of type variable candidates | |
100 | * @throws JavaModelException | |
101 | * if the signature of the specified member could not be resolved | |
102 | */ | |
103 | private static String[] getReferencedVariables(final IType declaring, final IMember member) throws JavaModelException { | |
104 | ||
105 | Assert.isNotNull(declaring); | |
106 | Assert.isNotNull(member); | |
107 | ||
108 | final String[] variables= parametersToVariables(declaring.getTypeParameters()); | |
109 | String[] result= new String[0]; | |
110 | if (member instanceof IField) { | |
111 | final String signature= ((IField) member).getTypeSignature(); | |
112 | final String[] signatures= getVariableSignatures(signature); | |
113 | if (signatures.length == 0) { | |
114 | final String variable= Signature.toString(signature); | |
115 | for (int index= 0; index < variables.length; index++) { | |
116 | if (variable.equals(variables[index])) { | |
117 | result= new String[] { variable}; | |
118 | break; | |
119 | } | |
120 | } | |
121 | } else { | |
122 | result= new String[signatures.length]; | |
123 | for (int index= 0; index < result.length; index++) | |
124 | result[index]= Signature.toString(signatures[index]); | |
125 | } | |
126 | } else if (member instanceof IMethod) { | |
127 | final IMethod method= (IMethod) member; | |
128 | final HashSet<String> set= new HashSet<String>(); | |
129 | final String[] types= method.getParameterTypes(); | |
130 | for (int index= 0; index < types.length; index++) | |
131 | extractTypeVariables(types[index], set); | |
132 | extractTypeVariables(method.getReturnType(), set); | |
133 | final String[] arguments= parametersToVariables(((IMethod) member).getTypeParameters()); | |
134 | for (int index= 0; index < arguments.length; index++) | |
135 | set.add(arguments[index]); | |
136 | result= new String[set.size()]; | |
137 | set.toArray(result); | |
138 | } else if (member instanceof IType) | |
139 | result= parametersToVariables(((IType) member).getTypeParameters()); | |
140 | else { | |
141 | JavaPlugin.logErrorMessage("Unexpected sub-type of IMember: " + member.getClass().getName()); //$NON-NLS-1$ | |
142 | Assert.isTrue(false); | |
143 | } | |
144 | ||
145 | final List<String> list= new ArrayList<String>(variables.length); | |
146 | String variable= null; | |
147 | for (int index= 0; index < variables.length; index++) { | |
148 | variable= variables[index]; | |
149 | for (int offset= 0; offset < result.length; offset++) | |
150 | if (variable.equals(result[offset])) | |
151 | list.add(result[offset]); | |
152 | } | |
153 | result= new String[list.size()]; | |
154 | list.toArray(result); | |
155 | return result; | |
156 | } | |
157 | ||
158 | /** | |
159 | * Returns all type variable names of the indicated member not mapped by the specified type variable mapping. | |
160 | * | |
161 | * @param mapping | |
162 | * the type variable mapping. The entries of this mapping must be simple type variable names. | |
163 | * @param declaring | |
164 | * the declaring type of the indicated member | |
165 | * @param member | |
166 | * the member to determine its unmapped type variable names | |
167 | * @return a possibly empty array of unmapped type variable names | |
168 | * @throws JavaModelException | |
169 | * if the type parameters of the member could not be determined | |
170 | */ | |
171 | public static String[] getUnmappedVariables(final TypeVariableMaplet[] mapping, final IType declaring, final IMember member) throws JavaModelException { | |
172 | ||
173 | Assert.isNotNull(mapping); | |
174 | Assert.isNotNull(declaring); | |
175 | Assert.isNotNull(member); | |
176 | ||
177 | List<String> list= null; | |
178 | final String[] types= getReferencedVariables(declaring, member); | |
179 | if (mapping.length == 0) { | |
180 | list= new ArrayList<String>(types.length); | |
181 | list.addAll(Arrays.asList(types)); | |
182 | } else { | |
183 | final Set<String> mapped= new HashSet<String>(types.length); | |
184 | String type= null; | |
185 | for (int index= 0; index < types.length; index++) { | |
186 | for (int offset= 0; offset < mapping.length; offset++) { | |
187 | type= types[index]; | |
188 | if (mapping[offset].getSourceName().equals(type)) | |
189 | mapped.add(type); | |
190 | } | |
191 | } | |
192 | list= new ArrayList<String>(types.length - mapped.size()); | |
193 | for (int index= 0; index < types.length; index++) { | |
194 | type= types[index]; | |
195 | if (!mapped.contains(type)) | |
196 | list.add(type); | |
197 | } | |
198 | } | |
199 | final String[] result= new String[list.size()]; | |
200 | list.toArray(result); | |
201 | return result; | |
202 | } | |
203 | ||
204 | /** | |
205 | * Returns the type variable signatures of the specified parameterized type signature, or an empty array if none. | |
206 | * | |
207 | * @param signature | |
208 | * the signature to get its type variable signatures from. The signature must be a parameterized type signature. | |
209 | * @return a possibly empty array of type variable signatures | |
210 | * @see Signature#getTypeArguments(String) | |
211 | */ | |
212 | private static String[] getVariableSignatures(final String signature) { | |
213 | Assert.isNotNull(signature); | |
214 | ||
215 | String[] result= null; | |
216 | try { | |
217 | result= Signature.getTypeArguments(signature); | |
218 | } catch (IllegalArgumentException exception) { | |
219 | result= new String[0]; | |
220 | } | |
221 | return result; | |
222 | } | |
223 | ||
224 | /** | |
225 | * Returns the reversed type variable mapping of the specified mapping. | |
226 | * | |
227 | * @param mapping | |
228 | * the mapping to inverse | |
229 | * @return the possibly empty inverse mapping | |
230 | */ | |
231 | public static TypeVariableMaplet[] inverseMapping(final TypeVariableMaplet[] mapping) { | |
232 | Assert.isNotNull(mapping); | |
233 | ||
234 | final TypeVariableMaplet[] result= new TypeVariableMaplet[mapping.length]; | |
235 | TypeVariableMaplet maplet= null; | |
236 | for (int index= 0; index < mapping.length; index++) { | |
237 | maplet= mapping[index]; | |
238 | result[index]= new TypeVariableMaplet(maplet.getTargetName(), maplet.getTargetIndex(), maplet.getSourceName(), maplet.getSourceIndex()); | |
239 | } | |
240 | return result; | |
241 | } | |
242 | ||
243 | /** | |
244 | * Creates a type variable mapping from a domain to a range. | |
245 | * | |
246 | * @param domain | |
247 | * the domain of the mapping | |
248 | * @param range | |
249 | * the range of the mapping | |
250 | * @param indexes | |
251 | * <code>true</code> if the indexes should be compared, <code>false</code> if the names should be compared | |
252 | * @return a possibly empty type variable mapping | |
253 | */ | |
254 | private static TypeVariableMaplet[] parametersToSignatures(final ITypeParameter[] domain, final String[] range, final boolean indexes) { | |
255 | Assert.isNotNull(domain); | |
256 | Assert.isNotNull(range); | |
257 | ||
258 | final Set<TypeVariableMaplet> set= new HashSet<TypeVariableMaplet>(); | |
259 | ITypeParameter source= null; | |
260 | String target= null; | |
261 | String element= null; | |
262 | String signature= null; | |
263 | for (int index= 0; index < domain.length; index++) { | |
264 | source= domain[index]; | |
265 | for (int offset= 0; offset < range.length; offset++) { | |
266 | target= range[offset]; | |
267 | element= source.getElementName(); | |
268 | signature= Signature.toString(target); | |
269 | if (indexes) { | |
270 | if (offset == index) | |
271 | set.add(new TypeVariableMaplet(element, index, signature, offset)); | |
272 | } else { | |
273 | if (element.equals(signature)) | |
274 | set.add(new TypeVariableMaplet(element, index, signature, offset)); | |
275 | } | |
276 | } | |
277 | } | |
278 | final TypeVariableMaplet[] result= new TypeVariableMaplet[set.size()]; | |
279 | set.toArray(result); | |
280 | return result; | |
281 | } | |
282 | ||
283 | /** | |
284 | * Converts the specified type parameters to type variable names. | |
285 | * | |
286 | * @param parameters | |
287 | * the type parameters to convert. | |
288 | * @return the converted type variable names | |
289 | * @see ITypeParameter#getElementName() | |
290 | */ | |
291 | private static String[] parametersToVariables(final ITypeParameter[] parameters) { | |
292 | Assert.isNotNull(parameters); | |
293 | ||
294 | String[] result= new String[parameters.length]; | |
295 | for (int index= 0; index < parameters.length; index++) | |
296 | result[index]= parameters[index].getElementName(); | |
297 | ||
298 | return result; | |
299 | } | |
300 | ||
301 | /** | |
302 | * Creates a type variable mapping from a domain to a range. | |
303 | * | |
304 | * @param domain | |
305 | * the domain of the mapping | |
306 | * @param range | |
307 | * the range of the mapping | |
308 | * @return a possibly empty type variable mapping | |
309 | */ | |
310 | private static TypeVariableMaplet[] signaturesToParameters(final String[] domain, final ITypeParameter[] range) { | |
311 | Assert.isNotNull(domain); | |
312 | Assert.isNotNull(range); | |
313 | Assert.isTrue(domain.length == 0 || domain.length == range.length); | |
314 | ||
315 | final List<TypeVariableMaplet> list= new ArrayList<TypeVariableMaplet>(); | |
316 | String source= null; | |
317 | String target= null; | |
318 | for (int index= 0; index < domain.length; index++) { | |
319 | source= Signature.toString(domain[index]); | |
320 | target= range[index].getElementName(); | |
321 | list.add(new TypeVariableMaplet(source, index, target, index)); | |
322 | } | |
323 | final TypeVariableMaplet[] result= new TypeVariableMaplet[list.size()]; | |
324 | list.toArray(result); | |
325 | return result; | |
326 | } | |
327 | ||
328 | /** | |
329 | * Returns a type variable mapping from a subclass to a superclass. | |
330 | * | |
331 | * @param type | |
332 | * the type representing the subclass class | |
333 | * @return a type variable mapping. The mapping entries consist of simple type variable names. | |
334 | * @throws JavaModelException | |
335 | * if the signature of one of the types involved could not be retrieved | |
336 | */ | |
337 | public static TypeVariableMaplet[] subTypeToInheritedType(final IType type) throws JavaModelException { | |
338 | Assert.isNotNull(type); | |
339 | ||
340 | final ITypeParameter[] domain= type.getTypeParameters(); | |
341 | if (domain.length > 0) { | |
342 | final String signature= type.getSuperclassTypeSignature(); | |
343 | if (signature != null) { | |
344 | final String[] range= getVariableSignatures(signature); | |
345 | if (range.length > 0) | |
346 | return parametersToSignatures(domain, range, false); | |
347 | } | |
348 | } | |
349 | return new TypeVariableMaplet[0]; | |
350 | } | |
351 | ||
352 | /** | |
353 | * Returns a type variable mapping from a subclass to a superclass. | |
354 | * | |
355 | * @param subtype | |
356 | * the type representing the subclass | |
357 | * @param supertype | |
358 | * the type representing the superclass | |
359 | * @return a type variable mapping. The mapping entries consist of simple type variable names. | |
360 | * @throws JavaModelException | |
361 | * if the signature of one of the types involved could not be retrieved | |
362 | */ | |
363 | public static TypeVariableMaplet[] subTypeToSuperType(final IType subtype, final IType supertype) throws JavaModelException { | |
364 | Assert.isNotNull(subtype); | |
365 | Assert.isNotNull(supertype); | |
366 | ||
367 | final TypeVariableMaplet[] mapping= subTypeToInheritedType(subtype); | |
368 | if (mapping.length > 0) { | |
369 | final ITypeParameter[] range= supertype.getTypeParameters(); | |
370 | if (range.length > 0) { | |
371 | final String signature= subtype.getSuperclassTypeSignature(); | |
372 | if (signature != null) { | |
373 | final String[] domain= getVariableSignatures(signature); | |
374 | if (domain.length > 0) | |
375 | return composeMappings(mapping, signaturesToParameters(domain, range)); | |
376 | } | |
377 | } | |
378 | } | |
379 | return mapping; | |
380 | } | |
381 | ||
382 | /** | |
383 | * Returns a type variable mapping from a superclass to a subclass. | |
384 | * | |
385 | * @param supertype | |
386 | * the type representing the superclass | |
387 | * @param subtype | |
388 | * the type representing the subclass | |
389 | * @return a type variable mapping. The mapping entries consist of simple type variable names. | |
390 | * @throws JavaModelException | |
391 | * if the signature of one of the types involved could not be retrieved | |
392 | */ | |
393 | public static TypeVariableMaplet[] superTypeToInheritedType(final IType supertype, final IType subtype) throws JavaModelException { | |
394 | Assert.isNotNull(subtype); | |
395 | Assert.isNotNull(supertype); | |
396 | ||
397 | final ITypeParameter[] domain= supertype.getTypeParameters(); | |
398 | if (domain.length > 0) { | |
399 | final String signature= subtype.getSuperclassTypeSignature(); | |
400 | if (signature != null) { | |
401 | final String[] range= getVariableSignatures(signature); | |
402 | if (range.length > 0) | |
403 | return parametersToSignatures(domain, range, true); | |
404 | } | |
405 | } | |
406 | return new TypeVariableMaplet[0]; | |
407 | } | |
408 | ||
409 | /** | |
410 | * Returns a type variable mapping from a superclass to a subclass. | |
411 | * | |
412 | * @param supertype | |
413 | * the type representing the superclass | |
414 | * @param subtype | |
415 | * the type representing the subclass | |
416 | * @return a type variable mapping. The mapping entries consist of simple type variable names. | |
417 | * @throws JavaModelException | |
418 | * if the signature of one of the types involved could not be retrieved | |
419 | */ | |
420 | public static TypeVariableMaplet[] superTypeToSubType(final IType supertype, final IType subtype) throws JavaModelException { | |
421 | Assert.isNotNull(supertype); | |
422 | Assert.isNotNull(subtype); | |
423 | ||
424 | return inverseMapping(subTypeToSuperType(subtype, supertype)); | |
425 | } | |
426 | ||
427 | private TypeVariableUtil() { | |
428 | // Not to be instantiated | |
429 | } | |
430 | } |