Home | History | Annotate | Download | only in types
      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&lt;A, B&gt; {}
    213      * class Bar&lt;C&gt; extends Foo&lt;C, String&gt; {}
    214      * <p>
    215      * a call to getAllAncestors on a reference to Bar having type parameter Boolean should include
    216      * Foo&lt;Boolean, String&gt;.
    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