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