1 /* 2 * Copyright (C) 2007-2010 Jlio Vilmar Gesser. 3 * Copyright (C) 2011, 2013-2016 The JavaParser Team. 4 * 5 * This file is part of JavaParser. 6 * 7 * JavaParser can be used either under the terms of 8 * a) the GNU Lesser General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * b) the terms of the Apache License 12 * 13 * You should have received a copy of both licenses in LICENCE.LGPL and 14 * LICENCE.APACHE. Please refer to those files for details. 15 * 16 * JavaParser is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU Lesser General Public License for more details. 20 */ 21 22 package com.github.javaparser.resolution.types; 23 24 import com.github.javaparser.resolution.MethodUsage; 25 import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; 26 import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration; 27 import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParameterValueProvider; 28 import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametersMap; 29 import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametrized; 30 import com.github.javaparser.utils.Pair; 31 32 import java.util.*; 33 import java.util.stream.Collectors; 34 35 /** 36 * A ReferenceType like a class, an interface or an enum. Note that this type can contain also the values 37 * specified for the type parameters. 38 * 39 * @author Federico Tomassetti 40 */ 41 public abstract class ResolvedReferenceType implements ResolvedType, 42 ResolvedTypeParametrized, ResolvedTypeParameterValueProvider { 43 44 // 45 // Fields 46 // 47 48 protected ResolvedReferenceTypeDeclaration typeDeclaration; 49 protected ResolvedTypeParametersMap typeParametersMap; 50 51 // 52 // Constructors 53 // 54 55 public ResolvedReferenceType(ResolvedReferenceTypeDeclaration typeDeclaration) { 56 this(typeDeclaration, deriveParams(typeDeclaration)); 57 } 58 59 public ResolvedReferenceType(ResolvedReferenceTypeDeclaration typeDeclaration, List<ResolvedType> typeArguments) { 60 if (typeDeclaration.isTypeParameter()) { 61 throw new IllegalArgumentException("You should use only Classes, Interfaces and enums"); 62 } 63 if (typeArguments.size() > 0 && typeArguments.size() != typeDeclaration.getTypeParameters().size()) { 64 throw new IllegalArgumentException(String.format( 65 "expected either zero type arguments or has many as defined in the declaration (%d). Found %d", 66 typeDeclaration.getTypeParameters().size(), typeArguments.size())); 67 } 68 ResolvedTypeParametersMap.Builder typeParametersMapBuilder = new ResolvedTypeParametersMap.Builder(); 69 for (int i = 0; i < typeArguments.size(); i++) { 70 typeParametersMapBuilder.setValue(typeDeclaration.getTypeParameters().get(i), typeArguments.get(i)); 71 } 72 this.typeParametersMap = typeParametersMapBuilder.build(); 73 this.typeDeclaration = typeDeclaration; 74 } 75 76 // 77 // Public Object methods 78 // 79 80 @Override 81 public boolean equals(Object o) { 82 if (this == o) return true; 83 if (o == null || getClass() != o.getClass()) return false; 84 85 ResolvedReferenceType that = (ResolvedReferenceType) o; 86 87 if (!typeDeclaration.equals(that.typeDeclaration)) return false; 88 if (!typeParametersMap.equals(that.typeParametersMap)) return false; 89 90 return true; 91 } 92 93 @Override 94 public int hashCode() { 95 int result = typeDeclaration.hashCode(); 96 result = 31 * result + typeParametersMap.hashCode(); 97 return result; 98 } 99 100 @Override 101 public String toString() { 102 return "ReferenceType{" + getQualifiedName() + 103 ", typeParametersMap=" + typeParametersMap + 104 '}'; 105 } 106 107 /// 108 /// Relation with other types 109 /// 110 111 @Override 112 public final boolean isReferenceType() { 113 return true; 114 } 115 116 /// 117 /// Downcasting 118 /// 119 120 @Override 121 public ResolvedReferenceType asReferenceType() { 122 return this; 123 } 124 125 /// 126 /// Naming 127 /// 128 129 @Override 130 public String describe() { 131 StringBuilder sb = new StringBuilder(); 132 if (hasName()) { 133 sb.append(typeDeclaration.getQualifiedName()); 134 } else { 135 sb.append("<anonymous class>"); 136 } 137 if (!typeParametersMap().isEmpty()) { 138 sb.append("<"); 139 sb.append(String.join(", ", typeDeclaration.getTypeParameters().stream() 140 .map(tp -> typeParametersMap().getValue(tp).describe()) 141 .collect(Collectors.toList()))); 142 sb.append(">"); 143 } 144 return sb.toString(); 145 } 146 147 /// 148 /// TypeParameters 149 /// 150 151 /** 152 * Execute a transformation on all the type parameters of this element. 153 */ 154 public abstract ResolvedType transformTypeParameters(ResolvedTypeTransformer transformer); 155 156 @Override 157 public ResolvedType replaceTypeVariables(ResolvedTypeParameterDeclaration tpToReplace, ResolvedType replaced, 158 Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes) { 159 if (replaced == null) { 160 throw new IllegalArgumentException(); 161 } 162 163 ResolvedReferenceType result = this; 164 int i = 0; 165 for (ResolvedType tp : this.typeParametersValues()) { 166 ResolvedType transformedTp = tp.replaceTypeVariables(tpToReplace, replaced, inferredTypes); 167 // Identity comparison on purpose 168 if (tp.isTypeVariable() && tp.asTypeVariable().describe().equals(tpToReplace.getName())) { 169 inferredTypes.put(tp.asTypeParameter(), replaced); 170 } 171 // FIXME 172 if (true) { 173 List<ResolvedType> typeParametersCorrected = result.asReferenceType().typeParametersValues(); 174 typeParametersCorrected.set(i, transformedTp); 175 result = create(typeDeclaration, typeParametersCorrected); 176 } 177 i++; 178 } 179 180 List<ResolvedType> values = result.typeParametersValues(); 181 // FIXME 182 if(values.contains(tpToReplace)){ 183 int index = values.indexOf(tpToReplace); 184 values.set(index, replaced); 185 return create(result.getTypeDeclaration(), values); 186 } 187 188 return result; 189 } 190 191 /// 192 /// Assignability 193 /// 194 195 /** 196 * This method checks if ThisType t = new OtherType() would compile. 197 */ 198 @Override 199 public abstract boolean isAssignableBy(ResolvedType other); 200 201 /// 202 /// Ancestors 203 /// 204 205 /** 206 * Return all ancestors, that means all superclasses and interfaces. 207 * This list should always include Object (unless this is a reference to Object). 208 * The type typeParametersValues should be expressed in terms of this type typeParametersValues. 209 * <p> 210 * For example, given: 211 * <p> 212 * class Foo<A, B> {} 213 * class Bar<C> extends Foo<C, String> {} 214 * <p> 215 * a call to getAllAncestors on a reference to Bar having type parameter Boolean should include 216 * Foo<Boolean, String>. 217 */ 218 public abstract List<ResolvedReferenceType> getAllAncestors(); 219 220 public final List<ResolvedReferenceType> getAllInterfacesAncestors() { 221 return getAllAncestors().stream() 222 .filter(it -> it.getTypeDeclaration().isInterface()) 223 .collect(Collectors.toList()); 224 } 225 226 public final List<ResolvedReferenceType> getAllClassesAncestors() { 227 return getAllAncestors().stream() 228 .filter(it -> it.getTypeDeclaration().isClass()) 229 .collect(Collectors.toList()); 230 } 231 232 /// 233 /// Type parameters 234 /// 235 236 /** 237 * Get the type associated with the type parameter with the given name. 238 * It returns Optional.empty unless the type declaration declares a type parameter with the given name. 239 */ 240 public Optional<ResolvedType> getGenericParameterByName(String name) { 241 for (ResolvedTypeParameterDeclaration tp : typeDeclaration.getTypeParameters()) { 242 if (tp.getName().equals(name)) { 243 return Optional.of(this.typeParametersMap().getValue(tp)); 244 } 245 } 246 return Optional.empty(); 247 } 248 249 /** 250 * Get the values for all type parameters declared on this type. 251 * The list can be empty for raw types. 252 */ 253 public List<ResolvedType> typeParametersValues() { 254 return this.typeParametersMap.isEmpty() ? Collections.emptyList() : typeDeclaration.getTypeParameters().stream().map(tp -> typeParametersMap.getValue(tp)).collect(Collectors.toList()); 255 } 256 257 /** 258 * Get the values for all type parameters declared on this type. 259 * In case of raw types the values correspond to TypeVariables. 260 */ 261 public List<Pair<ResolvedTypeParameterDeclaration, ResolvedType>> getTypeParametersMap() { 262 List<Pair<ResolvedTypeParameterDeclaration, ResolvedType>> typeParametersMap = new ArrayList<>(); 263 if (!isRawType()) { 264 for (int i = 0; i < typeDeclaration.getTypeParameters().size(); i++) { 265 typeParametersMap.add(new Pair<>(typeDeclaration.getTypeParameters().get(0), typeParametersValues().get(i))); 266 } 267 } 268 return typeParametersMap; 269 } 270 271 @Override 272 public ResolvedTypeParametersMap typeParametersMap() { 273 return typeParametersMap; 274 } 275 276 /// 277 /// Other methods introduced by ReferenceType 278 /// 279 280 /** 281 * Corresponding TypeDeclaration 282 */ 283 public final ResolvedReferenceTypeDeclaration getTypeDeclaration() { 284 return typeDeclaration; 285 } 286 287 /** 288 * The type of the field could be different from the one in the corresponding FieldDeclaration because 289 * type variables would be solved. 290 */ 291 public Optional<ResolvedType> getFieldType(String name) { 292 if (!typeDeclaration.hasField(name)) { 293 return Optional.empty(); 294 } 295 ResolvedType type = typeDeclaration.getField(name).getType(); 296 type = useThisTypeParametersOnTheGivenType(type); 297 return Optional.of(type); 298 } 299 300 /** 301 * Has the TypeDeclaration a name? Anonymous classes do not have one. 302 */ 303 public boolean hasName() { 304 return typeDeclaration.hasName(); 305 } 306 307 /** 308 * Qualified name of the declaration. 309 */ 310 public String getQualifiedName() { 311 return typeDeclaration.getQualifiedName(); 312 } 313 314 /** 315 * Id of the declaration. It corresponds to the qualified name, unless for local classes. 316 */ 317 public String getId() { 318 return typeDeclaration.getId(); 319 } 320 321 /** 322 * Methods declared on this type. 323 */ 324 public abstract Set<MethodUsage> getDeclaredMethods(); 325 326 public boolean isRawType() { 327 if (!typeDeclaration.getTypeParameters().isEmpty()) { 328 if (typeParametersMap().isEmpty()) { 329 return true; 330 } 331 for (String name : typeParametersMap().getNames()) { 332 Optional<ResolvedType> value = typeParametersMap().getValueBySignature(name); 333 if (value.isPresent() && value.get().isTypeVariable() && value.get().asTypeVariable().qualifiedName().equals(name)) { 334 // nothing to do 335 } else { 336 return false; 337 } 338 } 339 return true; 340 } 341 return false; 342 } 343 344 public Optional<ResolvedType> typeParamValue(ResolvedTypeParameterDeclaration typeParameterDeclaration) { 345 if (typeParameterDeclaration.declaredOnMethod()) { 346 throw new IllegalArgumentException(); 347 } 348 String typeId = this.getTypeDeclaration().getId(); 349 if (typeId.equals(typeParameterDeclaration.getContainerId())) { 350 return Optional.of(this.typeParametersMap().getValue(typeParameterDeclaration)); 351 } 352 for (ResolvedReferenceType ancestor : this.getAllAncestors()) { 353 if (ancestor.getId().equals(typeParameterDeclaration.getContainerId())) { 354 return Optional.of(ancestor.typeParametersMap().getValue(typeParameterDeclaration)); 355 } 356 } 357 return Optional.empty(); 358 } 359 360 public abstract ResolvedType toRawType(); 361 362 // 363 // Protected methods 364 // 365 366 protected abstract ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration, List<ResolvedType> typeParameters); 367 368 protected ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration, ResolvedTypeParametersMap typeParametersMap) { 369 return create(typeDeclaration, typeDeclaration.getTypeParameters().stream() 370 .map(typeParametersMap::getValue) 371 .collect(Collectors.toList())); 372 } 373 374 protected abstract ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration); 375 376 protected boolean isCorrespondingBoxingType(String typeName) { 377 switch (typeName) { 378 case "boolean": 379 return getQualifiedName().equals(Boolean.class.getCanonicalName()); 380 case "char": 381 return getQualifiedName().equals(Character.class.getCanonicalName()); 382 case "byte": 383 return getQualifiedName().equals(Byte.class.getCanonicalName()); 384 case "short": 385 return getQualifiedName().equals(Short.class.getCanonicalName()); 386 case "int": 387 return getQualifiedName().equals(Integer.class.getCanonicalName()); 388 case "long": 389 return getQualifiedName().equals(Long.class.getCanonicalName()); 390 case "float": 391 return getQualifiedName().equals(Float.class.getCanonicalName()); 392 case "double": 393 return getQualifiedName().equals(Double.class.getCanonicalName()); 394 default: 395 throw new UnsupportedOperationException(typeName); 396 } 397 } 398 399 protected boolean compareConsideringTypeParameters(ResolvedReferenceType other) { 400 if (other.equals(this)) { 401 return true; 402 } 403 if (this.getQualifiedName().equals(other.getQualifiedName())) { 404 if (this.isRawType() || other.isRawType()) { 405 return true; 406 } 407 if (this.typeParametersValues().size() != other.typeParametersValues().size()) { 408 throw new IllegalStateException(); 409 } 410 for (int i = 0; i < typeParametersValues().size(); i++) { 411 ResolvedType thisParam = typeParametersValues().get(i); 412 ResolvedType otherParam = other.typeParametersValues().get(i); 413 if (!thisParam.equals(otherParam)) { 414 if (thisParam instanceof ResolvedWildcard) { 415 ResolvedWildcard thisParamAsWildcard = (ResolvedWildcard) thisParam; 416 if (thisParamAsWildcard.isSuper() && otherParam.isAssignableBy(thisParamAsWildcard.getBoundedType())) { 417 // ok 418 } else if (thisParamAsWildcard.isExtends() && thisParamAsWildcard.getBoundedType().isAssignableBy(otherParam)) { 419 // ok 420 } else if (!thisParamAsWildcard.isBounded()) { 421 // ok 422 } else { 423 return false; 424 } 425 } else { 426 if (thisParam instanceof ResolvedTypeVariable && otherParam instanceof ResolvedTypeVariable) { 427 List<ResolvedType> thisBounds = thisParam.asTypeVariable().asTypeParameter().getBounds().stream().map(bound -> bound.getType()).collect(Collectors.toList()); 428 List<ResolvedType> otherBounds = otherParam.asTypeVariable().asTypeParameter().getBounds().stream().map(bound -> bound.getType()).collect(Collectors.toList()); 429 if (thisBounds.size() == otherBounds.size() && otherBounds.containsAll(thisBounds)) { 430 return true; 431 } 432 } 433 return false; 434 } 435 } 436 } 437 return true; 438 } 439 return false; 440 } 441 442 // 443 // Private methods 444 // 445 446 private static List<ResolvedType> deriveParams(ResolvedReferenceTypeDeclaration typeDeclaration) { 447 return typeDeclaration.getTypeParameters().stream().map((tp) -> new ResolvedTypeVariable(tp)).collect(Collectors.toList()); 448 } 449 450 public abstract ResolvedReferenceType deriveTypeParameters(ResolvedTypeParametersMap typeParametersMap); 451 } 452