/* * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. * Copyright (C) 2011, 2013-2016 The JavaParser Team. * * This file is part of JavaParser. * * JavaParser can be used either under the terms of * a) the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * b) the terms of the Apache License * * You should have received a copy of both licenses in LICENCE.LGPL and * LICENCE.APACHE. Please refer to those files for details. * * JavaParser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ package com.github.javaparser.resolution.types; import com.github.javaparser.resolution.MethodUsage; import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration; import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParameterValueProvider; import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametersMap; import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametrized; import com.github.javaparser.utils.Pair; import java.util.*; import java.util.stream.Collectors; /** * A ReferenceType like a class, an interface or an enum. Note that this type can contain also the values * specified for the type parameters. * * @author Federico Tomassetti */ public abstract class ResolvedReferenceType implements ResolvedType, ResolvedTypeParametrized, ResolvedTypeParameterValueProvider { // // Fields // protected ResolvedReferenceTypeDeclaration typeDeclaration; protected ResolvedTypeParametersMap typeParametersMap; // // Constructors // public ResolvedReferenceType(ResolvedReferenceTypeDeclaration typeDeclaration) { this(typeDeclaration, deriveParams(typeDeclaration)); } public ResolvedReferenceType(ResolvedReferenceTypeDeclaration typeDeclaration, List typeArguments) { if (typeDeclaration.isTypeParameter()) { throw new IllegalArgumentException("You should use only Classes, Interfaces and enums"); } if (typeArguments.size() > 0 && typeArguments.size() != typeDeclaration.getTypeParameters().size()) { throw new IllegalArgumentException(String.format( "expected either zero type arguments or has many as defined in the declaration (%d). Found %d", typeDeclaration.getTypeParameters().size(), typeArguments.size())); } ResolvedTypeParametersMap.Builder typeParametersMapBuilder = new ResolvedTypeParametersMap.Builder(); for (int i = 0; i < typeArguments.size(); i++) { typeParametersMapBuilder.setValue(typeDeclaration.getTypeParameters().get(i), typeArguments.get(i)); } this.typeParametersMap = typeParametersMapBuilder.build(); this.typeDeclaration = typeDeclaration; } // // Public Object methods // @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ResolvedReferenceType that = (ResolvedReferenceType) o; if (!typeDeclaration.equals(that.typeDeclaration)) return false; if (!typeParametersMap.equals(that.typeParametersMap)) return false; return true; } @Override public int hashCode() { int result = typeDeclaration.hashCode(); result = 31 * result + typeParametersMap.hashCode(); return result; } @Override public String toString() { return "ReferenceType{" + getQualifiedName() + ", typeParametersMap=" + typeParametersMap + '}'; } /// /// Relation with other types /// @Override public final boolean isReferenceType() { return true; } /// /// Downcasting /// @Override public ResolvedReferenceType asReferenceType() { return this; } /// /// Naming /// @Override public String describe() { StringBuilder sb = new StringBuilder(); if (hasName()) { sb.append(typeDeclaration.getQualifiedName()); } else { sb.append(""); } if (!typeParametersMap().isEmpty()) { sb.append("<"); sb.append(String.join(", ", typeDeclaration.getTypeParameters().stream() .map(tp -> typeParametersMap().getValue(tp).describe()) .collect(Collectors.toList()))); sb.append(">"); } return sb.toString(); } /// /// TypeParameters /// /** * Execute a transformation on all the type parameters of this element. */ public abstract ResolvedType transformTypeParameters(ResolvedTypeTransformer transformer); @Override public ResolvedType replaceTypeVariables(ResolvedTypeParameterDeclaration tpToReplace, ResolvedType replaced, Map inferredTypes) { if (replaced == null) { throw new IllegalArgumentException(); } ResolvedReferenceType result = this; int i = 0; for (ResolvedType tp : this.typeParametersValues()) { ResolvedType transformedTp = tp.replaceTypeVariables(tpToReplace, replaced, inferredTypes); // Identity comparison on purpose if (tp.isTypeVariable() && tp.asTypeVariable().describe().equals(tpToReplace.getName())) { inferredTypes.put(tp.asTypeParameter(), replaced); } // FIXME if (true) { List typeParametersCorrected = result.asReferenceType().typeParametersValues(); typeParametersCorrected.set(i, transformedTp); result = create(typeDeclaration, typeParametersCorrected); } i++; } List values = result.typeParametersValues(); // FIXME if(values.contains(tpToReplace)){ int index = values.indexOf(tpToReplace); values.set(index, replaced); return create(result.getTypeDeclaration(), values); } return result; } /// /// Assignability /// /** * This method checks if ThisType t = new OtherType() would compile. */ @Override public abstract boolean isAssignableBy(ResolvedType other); /// /// Ancestors /// /** * Return all ancestors, that means all superclasses and interfaces. * This list should always include Object (unless this is a reference to Object). * The type typeParametersValues should be expressed in terms of this type typeParametersValues. *

* For example, given: *

* class Foo<A, B> {} * class Bar<C> extends Foo<C, String> {} *

* a call to getAllAncestors on a reference to Bar having type parameter Boolean should include * Foo<Boolean, String>. */ public abstract List getAllAncestors(); public final List getAllInterfacesAncestors() { return getAllAncestors().stream() .filter(it -> it.getTypeDeclaration().isInterface()) .collect(Collectors.toList()); } public final List getAllClassesAncestors() { return getAllAncestors().stream() .filter(it -> it.getTypeDeclaration().isClass()) .collect(Collectors.toList()); } /// /// Type parameters /// /** * Get the type associated with the type parameter with the given name. * It returns Optional.empty unless the type declaration declares a type parameter with the given name. */ public Optional getGenericParameterByName(String name) { for (ResolvedTypeParameterDeclaration tp : typeDeclaration.getTypeParameters()) { if (tp.getName().equals(name)) { return Optional.of(this.typeParametersMap().getValue(tp)); } } return Optional.empty(); } /** * Get the values for all type parameters declared on this type. * The list can be empty for raw types. */ public List typeParametersValues() { return this.typeParametersMap.isEmpty() ? Collections.emptyList() : typeDeclaration.getTypeParameters().stream().map(tp -> typeParametersMap.getValue(tp)).collect(Collectors.toList()); } /** * Get the values for all type parameters declared on this type. * In case of raw types the values correspond to TypeVariables. */ public List> getTypeParametersMap() { List> typeParametersMap = new ArrayList<>(); if (!isRawType()) { for (int i = 0; i < typeDeclaration.getTypeParameters().size(); i++) { typeParametersMap.add(new Pair<>(typeDeclaration.getTypeParameters().get(0), typeParametersValues().get(i))); } } return typeParametersMap; } @Override public ResolvedTypeParametersMap typeParametersMap() { return typeParametersMap; } /// /// Other methods introduced by ReferenceType /// /** * Corresponding TypeDeclaration */ public final ResolvedReferenceTypeDeclaration getTypeDeclaration() { return typeDeclaration; } /** * The type of the field could be different from the one in the corresponding FieldDeclaration because * type variables would be solved. */ public Optional getFieldType(String name) { if (!typeDeclaration.hasField(name)) { return Optional.empty(); } ResolvedType type = typeDeclaration.getField(name).getType(); type = useThisTypeParametersOnTheGivenType(type); return Optional.of(type); } /** * Has the TypeDeclaration a name? Anonymous classes do not have one. */ public boolean hasName() { return typeDeclaration.hasName(); } /** * Qualified name of the declaration. */ public String getQualifiedName() { return typeDeclaration.getQualifiedName(); } /** * Id of the declaration. It corresponds to the qualified name, unless for local classes. */ public String getId() { return typeDeclaration.getId(); } /** * Methods declared on this type. */ public abstract Set getDeclaredMethods(); public boolean isRawType() { if (!typeDeclaration.getTypeParameters().isEmpty()) { if (typeParametersMap().isEmpty()) { return true; } for (String name : typeParametersMap().getNames()) { Optional value = typeParametersMap().getValueBySignature(name); if (value.isPresent() && value.get().isTypeVariable() && value.get().asTypeVariable().qualifiedName().equals(name)) { // nothing to do } else { return false; } } return true; } return false; } public Optional typeParamValue(ResolvedTypeParameterDeclaration typeParameterDeclaration) { if (typeParameterDeclaration.declaredOnMethod()) { throw new IllegalArgumentException(); } String typeId = this.getTypeDeclaration().getId(); if (typeId.equals(typeParameterDeclaration.getContainerId())) { return Optional.of(this.typeParametersMap().getValue(typeParameterDeclaration)); } for (ResolvedReferenceType ancestor : this.getAllAncestors()) { if (ancestor.getId().equals(typeParameterDeclaration.getContainerId())) { return Optional.of(ancestor.typeParametersMap().getValue(typeParameterDeclaration)); } } return Optional.empty(); } public abstract ResolvedType toRawType(); // // Protected methods // protected abstract ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration, List typeParameters); protected ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration, ResolvedTypeParametersMap typeParametersMap) { return create(typeDeclaration, typeDeclaration.getTypeParameters().stream() .map(typeParametersMap::getValue) .collect(Collectors.toList())); } protected abstract ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration); protected boolean isCorrespondingBoxingType(String typeName) { switch (typeName) { case "boolean": return getQualifiedName().equals(Boolean.class.getCanonicalName()); case "char": return getQualifiedName().equals(Character.class.getCanonicalName()); case "byte": return getQualifiedName().equals(Byte.class.getCanonicalName()); case "short": return getQualifiedName().equals(Short.class.getCanonicalName()); case "int": return getQualifiedName().equals(Integer.class.getCanonicalName()); case "long": return getQualifiedName().equals(Long.class.getCanonicalName()); case "float": return getQualifiedName().equals(Float.class.getCanonicalName()); case "double": return getQualifiedName().equals(Double.class.getCanonicalName()); default: throw new UnsupportedOperationException(typeName); } } protected boolean compareConsideringTypeParameters(ResolvedReferenceType other) { if (other.equals(this)) { return true; } if (this.getQualifiedName().equals(other.getQualifiedName())) { if (this.isRawType() || other.isRawType()) { return true; } if (this.typeParametersValues().size() != other.typeParametersValues().size()) { throw new IllegalStateException(); } for (int i = 0; i < typeParametersValues().size(); i++) { ResolvedType thisParam = typeParametersValues().get(i); ResolvedType otherParam = other.typeParametersValues().get(i); if (!thisParam.equals(otherParam)) { if (thisParam instanceof ResolvedWildcard) { ResolvedWildcard thisParamAsWildcard = (ResolvedWildcard) thisParam; if (thisParamAsWildcard.isSuper() && otherParam.isAssignableBy(thisParamAsWildcard.getBoundedType())) { // ok } else if (thisParamAsWildcard.isExtends() && thisParamAsWildcard.getBoundedType().isAssignableBy(otherParam)) { // ok } else if (!thisParamAsWildcard.isBounded()) { // ok } else { return false; } } else { if (thisParam instanceof ResolvedTypeVariable && otherParam instanceof ResolvedTypeVariable) { List thisBounds = thisParam.asTypeVariable().asTypeParameter().getBounds().stream().map(bound -> bound.getType()).collect(Collectors.toList()); List otherBounds = otherParam.asTypeVariable().asTypeParameter().getBounds().stream().map(bound -> bound.getType()).collect(Collectors.toList()); if (thisBounds.size() == otherBounds.size() && otherBounds.containsAll(thisBounds)) { return true; } } return false; } } } return true; } return false; } // // Private methods // private static List deriveParams(ResolvedReferenceTypeDeclaration typeDeclaration) { return typeDeclaration.getTypeParameters().stream().map((tp) -> new ResolvedTypeVariable(tp)).collect(Collectors.toList()); } public abstract ResolvedReferenceType deriveTypeParameters(ResolvedTypeParametersMap typeParametersMap); }