Home | History | Annotate | Download | only in typesystem
      1 /*
      2  * Copyright 2016 Federico Tomassetti
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.github.javaparser.symbolsolver.model.typesystem;
     18 
     19 import com.github.javaparser.resolution.MethodUsage;
     20 import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
     21 import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
     22 import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
     23 import com.github.javaparser.resolution.types.ResolvedReferenceType;
     24 import com.github.javaparser.resolution.types.ResolvedType;
     25 import com.github.javaparser.resolution.types.ResolvedTypeTransformer;
     26 import com.github.javaparser.resolution.types.ResolvedTypeVariable;
     27 import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametersMap;
     28 import com.github.javaparser.symbolsolver.javaparsermodel.LambdaArgumentTypePlaceholder;
     29 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeVariableDeclaration;
     30 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
     31 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
     32 
     33 import java.util.Collections;
     34 import java.util.HashSet;
     35 import java.util.List;
     36 import java.util.Set;
     37 import java.util.stream.Collectors;
     38 
     39 /**
     40  * @author Federico Tomassetti
     41  */
     42 // TODO Remove references to typeSolver: it is needed to instantiate other instances of ReferenceTypeUsage
     43 //      and to get the Object type declaration
     44 public class ReferenceTypeImpl extends ResolvedReferenceType {
     45 
     46     private TypeSolver typeSolver;
     47 
     48     public static ResolvedReferenceType undeterminedParameters(ResolvedReferenceTypeDeclaration typeDeclaration, TypeSolver typeSolver) {
     49         return new ReferenceTypeImpl(typeDeclaration, typeDeclaration.getTypeParameters().stream().map(
     50                 ResolvedTypeVariable::new
     51         ).collect(Collectors.toList()), typeSolver);
     52     }
     53 
     54     @Override
     55     protected ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration, List<ResolvedType> typeParametersCorrected) {
     56         return new ReferenceTypeImpl(typeDeclaration, typeParametersCorrected, typeSolver);
     57     }
     58 
     59     @Override
     60     protected ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration) {
     61         return new ReferenceTypeImpl(typeDeclaration, typeSolver);
     62     }
     63 
     64     public ReferenceTypeImpl(ResolvedReferenceTypeDeclaration typeDeclaration, TypeSolver typeSolver) {
     65         super(typeDeclaration);
     66         this.typeSolver = typeSolver;
     67     }
     68 
     69     public ReferenceTypeImpl(ResolvedReferenceTypeDeclaration typeDeclaration, List<ResolvedType> typeArguments, TypeSolver typeSolver) {
     70         super(typeDeclaration, typeArguments);
     71         this.typeSolver = typeSolver;
     72     }
     73 
     74     @Override
     75     public ResolvedTypeParameterDeclaration asTypeParameter() {
     76         if (this.typeDeclaration instanceof JavaParserTypeVariableDeclaration) {
     77             JavaParserTypeVariableDeclaration javaParserTypeVariableDeclaration = (JavaParserTypeVariableDeclaration) this.typeDeclaration;
     78             return javaParserTypeVariableDeclaration.asTypeParameter();
     79         }
     80         throw new UnsupportedOperationException(this.typeDeclaration.getClass().getCanonicalName());
     81     }
     82 
     83     /**
     84      * This method checks if ThisType t = new OtherType() would compile.
     85      */
     86     @Override
     87     public boolean isAssignableBy(ResolvedType other) {
     88         if (other instanceof NullType) {
     89             return !this.isPrimitive();
     90         }
     91         // everything is assignable to Object except void
     92         if (!other.isVoid() && this.getQualifiedName().equals(Object.class.getCanonicalName())) {
     93             return true;
     94         }
     95         // consider boxing
     96         if (other.isPrimitive()) {
     97             if (this.getQualifiedName().equals(Object.class.getCanonicalName())) {
     98                 return true;
     99             } else {
    100                 // Check if 'other' can be boxed to match this type
    101                 if (isCorrespondingBoxingType(other.describe())) return true;
    102 
    103                 // Resolve the boxed type and check if it can be assigned via widening reference conversion
    104                 SymbolReference<ResolvedReferenceTypeDeclaration> type = typeSolver.tryToSolveType(other.asPrimitive().getBoxTypeQName());
    105                 return type.getCorrespondingDeclaration().canBeAssignedTo(super.typeDeclaration);
    106             }
    107         }
    108         if (other instanceof LambdaArgumentTypePlaceholder) {
    109             return this.getTypeDeclaration().hasAnnotation(FunctionalInterface.class.getCanonicalName());
    110         } else if (other instanceof ReferenceTypeImpl) {
    111             ReferenceTypeImpl otherRef = (ReferenceTypeImpl) other;
    112             if (compareConsideringTypeParameters(otherRef)) {
    113                 return true;
    114             }
    115             for (ResolvedReferenceType otherAncestor : otherRef.getAllAncestors()) {
    116                 if (compareConsideringTypeParameters(otherAncestor)) {
    117                     return true;
    118                 }
    119             }
    120             return false;
    121         } else if (other.isTypeVariable()) {
    122             for (ResolvedTypeParameterDeclaration.Bound bound : other.asTypeVariable().asTypeParameter().getBounds()) {
    123                 if (bound.isExtends()) {
    124                     if (this.isAssignableBy(bound.getType())) {
    125                         return true;
    126                     }
    127                 }
    128             }
    129             return false;
    130         } else if (other.isConstraint()){
    131             return isAssignableBy(other.asConstraintType().getBound());
    132         } else if (other.isWildcard()) {
    133             if (this.getQualifiedName().equals(Object.class.getCanonicalName())) {
    134                 return true;
    135             } else if (other.asWildcard().isExtends()) {
    136                 return isAssignableBy(other.asWildcard().getBoundedType());
    137             } else {
    138                 return false;
    139             }
    140         } else {
    141             return false;
    142         }
    143     }
    144 
    145     @Override
    146     public Set<MethodUsage> getDeclaredMethods() {
    147         // TODO replace variables
    148         Set<MethodUsage> methods = new HashSet<>();
    149         for (ResolvedMethodDeclaration methodDeclaration : getTypeDeclaration().getDeclaredMethods()) {
    150             MethodUsage methodUsage = new MethodUsage(methodDeclaration);
    151             methods.add(methodUsage);
    152         }
    153         return methods;
    154     }
    155 
    156     @Override
    157     public ResolvedType toRawType() {
    158         if (this.isRawType()) {
    159                 return this;
    160         } else {
    161             return new ReferenceTypeImpl(typeDeclaration, typeSolver);
    162         }
    163     }
    164 
    165     @Override
    166     public boolean mention(List<ResolvedTypeParameterDeclaration> typeParameters) {
    167         return typeParametersValues().stream().anyMatch(tp -> tp.mention(typeParameters));
    168     }
    169 
    170     /**
    171      * Execute a transformation on all the type parameters of this element.
    172      */
    173     @Override
    174     public ResolvedType transformTypeParameters(ResolvedTypeTransformer transformer) {
    175         ResolvedType result = this;
    176         int i = 0;
    177         for (ResolvedType tp : this.typeParametersValues()) {
    178             ResolvedType transformedTp = transformer.transform(tp);
    179             // Identity comparison on purpose
    180             if (transformedTp != tp) {
    181                 List<ResolvedType> typeParametersCorrected = result.asReferenceType().typeParametersValues();
    182                 typeParametersCorrected.set(i, transformedTp);
    183                 result = create(typeDeclaration, typeParametersCorrected);
    184             }
    185             i++;
    186         }
    187         return result;
    188     }
    189 
    190     public List<ResolvedReferenceType> getAllAncestors() {
    191         // We need to go through the inheritance line and propagate the type parametes
    192 
    193         List<ResolvedReferenceType> ancestors = typeDeclaration.getAllAncestors();
    194 
    195         ancestors = ancestors.stream()
    196                 .map(a -> typeParametersMap().replaceAll(a).asReferenceType())
    197                 .collect(Collectors.toList());
    198 
    199         // Avoid repetitions of Object
    200         ancestors.removeIf(a -> a.getQualifiedName().equals(Object.class.getCanonicalName()));
    201         ResolvedReferenceTypeDeclaration objectType = typeSolver.solveType(Object.class.getCanonicalName());
    202         ResolvedReferenceType objectRef = create(objectType);
    203         ancestors.add(objectRef);
    204         return ancestors;
    205     }
    206 
    207     public ResolvedReferenceType deriveTypeParameters(ResolvedTypeParametersMap typeParametersMap) {
    208         return create(typeDeclaration, typeParametersMap);
    209     }
    210 
    211 }
    212