Home | History | Annotate | Download | only in javaparsermodel
      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.javaparsermodel;
     18 
     19 import com.github.javaparser.ast.CompilationUnit;
     20 import com.github.javaparser.ast.Node;
     21 import com.github.javaparser.ast.NodeList;
     22 import com.github.javaparser.ast.body.*;
     23 import com.github.javaparser.ast.body.EnumDeclaration;
     24 import com.github.javaparser.ast.expr.*;
     25 import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
     26 import com.github.javaparser.ast.type.*;
     27 import com.github.javaparser.resolution.MethodUsage;
     28 import com.github.javaparser.resolution.UnsolvedSymbolException;
     29 import com.github.javaparser.resolution.declarations.*;
     30 import com.github.javaparser.resolution.types.*;
     31 import com.github.javaparser.symbolsolver.core.resolution.Context;
     32 import com.github.javaparser.symbolsolver.javaparsermodel.contexts.FieldAccessContext;
     33 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*;
     34 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
     35 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
     36 import com.github.javaparser.symbolsolver.model.typesystem.*;
     37 import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
     38 import com.github.javaparser.symbolsolver.resolution.ConstructorResolutionLogic;
     39 import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
     40 
     41 import java.util.*;
     42 import java.util.logging.ConsoleHandler;
     43 import java.util.logging.Level;
     44 import java.util.logging.Logger;
     45 import java.util.stream.Collectors;
     46 
     47 import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
     48 
     49 /**
     50  * Class to be used by final users to solve symbols for JavaParser ASTs.
     51  *
     52  * @author Federico Tomassetti
     53  */
     54 public class JavaParserFacade {
     55 
     56     private static Logger logger = Logger.getLogger(JavaParserFacade.class.getCanonicalName());
     57 
     58     static {
     59         logger.setLevel(Level.INFO);
     60         ConsoleHandler consoleHandler = new ConsoleHandler();
     61         consoleHandler.setLevel(Level.INFO);
     62         logger.addHandler(consoleHandler);
     63     }
     64 
     65     private static Map<TypeSolver, JavaParserFacade> instances = new WeakHashMap<>();
     66     private TypeSolver typeSolver;
     67     private SymbolSolver symbolSolver;
     68     private Map<Node, ResolvedType> cacheWithLambdasSolved = new IdentityHashMap<>();
     69     private Map<Node, ResolvedType> cacheWithoutLambdasSolved = new IdentityHashMap<>();
     70     private TypeExtractor typeExtractor;
     71 
     72     private JavaParserFacade(TypeSolver typeSolver) {
     73         this.typeSolver = typeSolver.getRoot();
     74         this.symbolSolver = new SymbolSolver(typeSolver);
     75         this.typeExtractor = new TypeExtractor(typeSolver, this);
     76     }
     77 
     78     public TypeSolver getTypeSolver() {
     79         return typeSolver;
     80     }
     81 
     82     public SymbolSolver getSymbolSolver() {
     83         return symbolSolver;
     84     }
     85 
     86     public static JavaParserFacade get(TypeSolver typeSolver) {
     87         return instances.computeIfAbsent(typeSolver, JavaParserFacade::new);
     88     }
     89 
     90     /**
     91      * This method is used to clear internal caches for the sake of releasing memory.
     92      */
     93     public static void clearInstances() {
     94         instances.clear();
     95     }
     96 
     97     protected static ResolvedType solveGenericTypes(ResolvedType type, Context context, TypeSolver typeSolver) {
     98         if (type.isTypeVariable()) {
     99             return context.solveGenericType(type.describe(), typeSolver).orElse(type);
    100         }
    101         if (type.isWildcard()) {
    102             if (type.asWildcard().isExtends() || type.asWildcard().isSuper()) {
    103                 ResolvedWildcard wildcardUsage = type.asWildcard();
    104                 ResolvedType boundResolved = solveGenericTypes(wildcardUsage.getBoundedType(), context, typeSolver);
    105                 if (wildcardUsage.isExtends()) {
    106                     return ResolvedWildcard.extendsBound(boundResolved);
    107                 } else {
    108                     return ResolvedWildcard.superBound(boundResolved);
    109                 }
    110             }
    111         }
    112         return type;
    113     }
    114 
    115     public SymbolReference<? extends ResolvedValueDeclaration> solve(NameExpr nameExpr) {
    116         return symbolSolver.solveSymbol(nameExpr.getName().getId(), nameExpr);
    117     }
    118 
    119     public SymbolReference<? extends ResolvedValueDeclaration> solve(SimpleName nameExpr) {
    120         return symbolSolver.solveSymbol(nameExpr.getId(), nameExpr);
    121     }
    122 
    123     public SymbolReference<? extends ResolvedValueDeclaration> solve(Expression expr) {
    124         return expr.toNameExpr().map(this::solve).orElseThrow(() -> new IllegalArgumentException(expr.getClass().getCanonicalName()));
    125     }
    126 
    127     public SymbolReference<ResolvedMethodDeclaration> solve(MethodCallExpr methodCallExpr) {
    128         return solve(methodCallExpr, true);
    129     }
    130 
    131     public SymbolReference<ResolvedConstructorDeclaration> solve(ObjectCreationExpr objectCreationExpr) {
    132         return solve(objectCreationExpr, true);
    133     }
    134 
    135     public SymbolReference<ResolvedConstructorDeclaration> solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt) {
    136         return solve(explicitConstructorInvocationStmt, true);
    137     }
    138 
    139     public SymbolReference<ResolvedConstructorDeclaration> solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt, boolean solveLambdas) {
    140         List<ResolvedType> argumentTypes = new LinkedList<>();
    141         List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>();
    142 
    143         solveArguments(explicitConstructorInvocationStmt, explicitConstructorInvocationStmt.getArguments(), solveLambdas, argumentTypes, placeholders);
    144 
    145         Optional<ClassOrInterfaceDeclaration> optAncestor = explicitConstructorInvocationStmt.getAncestorOfType(ClassOrInterfaceDeclaration.class);
    146         if (!optAncestor.isPresent()) {
    147             return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
    148         }
    149         ClassOrInterfaceDeclaration classNode = optAncestor.get();
    150         ResolvedTypeDeclaration typeDecl = null;
    151         if (!explicitConstructorInvocationStmt.isThis()) {
    152             ResolvedType classDecl = JavaParserFacade.get(typeSolver).convert(classNode.getExtendedTypes(0), classNode);
    153             if (classDecl.isReferenceType()) {
    154                 typeDecl = classDecl.asReferenceType().getTypeDeclaration();
    155             }
    156         } else {
    157             SymbolReference<ResolvedTypeDeclaration> sr = JavaParserFactory.getContext(classNode, typeSolver).solveType(classNode.getNameAsString(), typeSolver);
    158             if (sr.isSolved()) {
    159                 typeDecl = sr.getCorrespondingDeclaration();
    160             }
    161         }
    162         if (typeDecl == null) {
    163             return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
    164         }
    165         SymbolReference<ResolvedConstructorDeclaration> res = ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) typeDecl).getConstructors(), argumentTypes, typeSolver);
    166         for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
    167             placeholder.setMethod(res);
    168         }
    169         return res;
    170     }
    171 
    172     public SymbolReference<ResolvedTypeDeclaration> solve(ThisExpr node) {
    173         // If 'this' is prefixed by a class eg. MyClass.this
    174         if (node.getClassExpr().isPresent()) {
    175             // Get the class name
    176             String className = node.getClassExpr().get().toString();
    177             // Attempt to resolve using a typeSolver
    178             SymbolReference<ResolvedReferenceTypeDeclaration> clazz = typeSolver.tryToSolveType(className);
    179             if (clazz.isSolved()) {
    180                 return SymbolReference.solved(clazz.getCorrespondingDeclaration());
    181             }
    182             // Attempt to resolve locally in Compilation unit
    183             Optional<CompilationUnit> cu = node.getAncestorOfType(CompilationUnit.class);
    184             if (cu.isPresent()) {
    185                 Optional<ClassOrInterfaceDeclaration> classByName = cu.get().getClassByName(className);
    186                 if (classByName.isPresent()) {
    187                     return SymbolReference.solved(getTypeDeclaration(classByName.get()));
    188                 }
    189             }
    190         }
    191         return SymbolReference.solved(getTypeDeclaration(findContainingTypeDeclOrObjectCreationExpr(node)));
    192     }
    193 
    194     /**
    195      * Given a constructor call find out to which constructor declaration it corresponds.
    196      */
    197     public SymbolReference<ResolvedConstructorDeclaration> solve(ObjectCreationExpr objectCreationExpr, boolean solveLambdas) {
    198         List<ResolvedType> argumentTypes = new LinkedList<>();
    199         List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>();
    200 
    201         solveArguments(objectCreationExpr, objectCreationExpr.getArguments(), solveLambdas, argumentTypes, placeholders);
    202 
    203         ResolvedType classDecl = JavaParserFacade.get(typeSolver).convert(objectCreationExpr.getType(), objectCreationExpr);
    204         if (!classDecl.isReferenceType()) {
    205             return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
    206         }
    207         SymbolReference<ResolvedConstructorDeclaration> res = ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) classDecl.asReferenceType().getTypeDeclaration()).getConstructors(), argumentTypes, typeSolver);
    208         for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
    209             placeholder.setMethod(res);
    210         }
    211         return res;
    212     }
    213 
    214     private void solveArguments(Node node, NodeList<Expression> args, boolean solveLambdas, List<ResolvedType> argumentTypes,
    215                                 List<LambdaArgumentTypePlaceholder> placeholders) {
    216         int i = 0;
    217         for (Expression parameterValue : args) {
    218             if (parameterValue instanceof LambdaExpr || parameterValue instanceof MethodReferenceExpr) {
    219                 LambdaArgumentTypePlaceholder placeholder = new LambdaArgumentTypePlaceholder(i);
    220                 argumentTypes.add(placeholder);
    221                 placeholders.add(placeholder);
    222             } else {
    223                 try {
    224                     argumentTypes.add(JavaParserFacade.get(typeSolver).getType(parameterValue, solveLambdas));
    225                 } catch (com.github.javaparser.resolution.UnsolvedSymbolException e) {
    226                     throw e;
    227                 } catch (Exception e) {
    228                     throw new RuntimeException(String.format("Unable to calculate the type of a parameter of a method call. Method call: %s, Parameter: %s",
    229                             node, parameterValue), e);
    230                 }
    231             }
    232             i++;
    233         }
    234     }
    235 
    236     /**
    237      * Given a method call find out to which method declaration it corresponds.
    238      */
    239     public SymbolReference<ResolvedMethodDeclaration> solve(MethodCallExpr methodCallExpr, boolean solveLambdas) {
    240         List<ResolvedType> argumentTypes = new LinkedList<>();
    241         List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>();
    242 
    243         solveArguments(methodCallExpr, methodCallExpr.getArguments(), solveLambdas, argumentTypes, placeholders);
    244 
    245         SymbolReference<ResolvedMethodDeclaration> res = JavaParserFactory.getContext(methodCallExpr, typeSolver).solveMethod(methodCallExpr.getName().getId(), argumentTypes, false, typeSolver);
    246         for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
    247             placeholder.setMethod(res);
    248         }
    249         return res;
    250     }
    251 
    252     public SymbolReference<ResolvedAnnotationDeclaration> solve(AnnotationExpr annotationExpr) {
    253         Context context = JavaParserFactory.getContext(annotationExpr, typeSolver);
    254         SymbolReference<ResolvedTypeDeclaration> typeDeclarationSymbolReference = context.solveType(annotationExpr.getNameAsString(), typeSolver);
    255         ResolvedAnnotationDeclaration annotationDeclaration = (ResolvedAnnotationDeclaration) typeDeclarationSymbolReference.getCorrespondingDeclaration();
    256         if (typeDeclarationSymbolReference.isSolved()) {
    257             return SymbolReference.solved(annotationDeclaration);
    258         } else {
    259             return SymbolReference.unsolved(ResolvedAnnotationDeclaration.class);
    260         }
    261     }
    262 
    263     public SymbolReference<ResolvedFieldDeclaration> solve(FieldAccessExpr fieldAccessExpr) {
    264         return ((FieldAccessContext) JavaParserFactory.getContext(fieldAccessExpr, typeSolver)).solveField(fieldAccessExpr.getName().getId(), typeSolver);
    265     }
    266 
    267     public ResolvedType getType(Node node) {
    268         return getType(node, true);
    269     }
    270 
    271     public ResolvedType getType(Node node, boolean solveLambdas) {
    272         if (solveLambdas) {
    273             if (!cacheWithLambdasSolved.containsKey(node)) {
    274                 ResolvedType res = getTypeConcrete(node, solveLambdas);
    275 
    276                 cacheWithLambdasSolved.put(node, res);
    277 
    278                 boolean secondPassNecessary = false;
    279                 if (node instanceof MethodCallExpr) {
    280                     MethodCallExpr methodCallExpr = (MethodCallExpr) node;
    281                     for (Node arg : methodCallExpr.getArguments()) {
    282                         if (!cacheWithLambdasSolved.containsKey(arg)) {
    283                             getType(arg, true);
    284                             secondPassNecessary = true;
    285                         }
    286                     }
    287                 }
    288                 if (secondPassNecessary) {
    289                     cacheWithLambdasSolved.remove(node);
    290                     cacheWithLambdasSolved.put(node, getType(node, true));
    291                 }
    292                 logger.finer("getType on " + node + " -> " + res);
    293             }
    294             return cacheWithLambdasSolved.get(node);
    295         } else {
    296             Optional<ResolvedType> res = find(cacheWithLambdasSolved, node);
    297             if (res.isPresent()) {
    298                 return res.get();
    299             }
    300             res = find(cacheWithoutLambdasSolved, node);
    301             if (!res.isPresent()) {
    302                 ResolvedType resType = getTypeConcrete(node, solveLambdas);
    303                 cacheWithoutLambdasSolved.put(node, resType);
    304                 logger.finer("getType on " + node + " (no solveLambdas) -> " + res);
    305                 return resType;
    306             }
    307             return res.get();
    308         }
    309     }
    310 
    311     private Optional<ResolvedType> find(Map<Node, ResolvedType> map, Node node) {
    312         if (map.containsKey(node)) {
    313             return Optional.of(map.get(node));
    314         }
    315         if (node instanceof LambdaExpr) {
    316             return find(map, (LambdaExpr) node);
    317         } else {
    318             return Optional.empty();
    319         }
    320     }
    321 
    322     /**
    323      * For some reasons LambdaExprs are duplicate and the equals method is not implemented correctly.
    324      */
    325     private Optional<ResolvedType> find(Map<Node, ResolvedType> map, LambdaExpr lambdaExpr) {
    326         for (Node key : map.keySet()) {
    327             if (key instanceof LambdaExpr) {
    328                 LambdaExpr keyLambdaExpr = (LambdaExpr) key;
    329                 if (keyLambdaExpr.toString().equals(lambdaExpr.toString()) && requireParentNode(keyLambdaExpr) == requireParentNode(lambdaExpr)) {
    330                     return Optional.of(map.get(keyLambdaExpr));
    331                 }
    332             }
    333         }
    334         return Optional.empty();
    335     }
    336 
    337     protected MethodUsage toMethodUsage(MethodReferenceExpr methodReferenceExpr) {
    338         if (!(methodReferenceExpr.getScope() instanceof TypeExpr)) {
    339             throw new UnsupportedOperationException();
    340         }
    341         TypeExpr typeExpr = (TypeExpr) methodReferenceExpr.getScope();
    342         if (!(typeExpr.getType() instanceof com.github.javaparser.ast.type.ClassOrInterfaceType)) {
    343             throw new UnsupportedOperationException(typeExpr.getType().getClass().getCanonicalName());
    344         }
    345         ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) typeExpr.getType();
    346         SymbolReference<ResolvedTypeDeclaration> typeDeclarationSymbolReference = JavaParserFactory.getContext(classOrInterfaceType, typeSolver).solveType(classOrInterfaceType.getName().getId(), typeSolver);
    347         if (!typeDeclarationSymbolReference.isSolved()) {
    348             throw new UnsupportedOperationException();
    349         }
    350         List<MethodUsage> methodUsages = ((ResolvedReferenceTypeDeclaration) typeDeclarationSymbolReference.getCorrespondingDeclaration()).getAllMethods().stream().filter(it -> it.getName().equals(methodReferenceExpr.getIdentifier())).collect(Collectors.toList());
    351         switch (methodUsages.size()) {
    352             case 0:
    353                 throw new UnsupportedOperationException();
    354             case 1:
    355                 return methodUsages.get(0);
    356             default:
    357                 throw new UnsupportedOperationException();
    358         }
    359     }
    360 
    361     protected ResolvedType getBinaryTypeConcrete(Node left, Node right, boolean solveLambdas) {
    362         ResolvedType leftType = getTypeConcrete(left, solveLambdas);
    363         ResolvedType rightType = getTypeConcrete(right, solveLambdas);
    364         if (rightType.isAssignableBy(leftType)) {
    365             return rightType;
    366         }
    367         return leftType;
    368     }
    369 
    370 
    371     /**
    372      * Should return more like a TypeApplication: a TypeDeclaration and possible typeParametersValues or array
    373      * modifiers.
    374      */
    375     private ResolvedType getTypeConcrete(Node node, boolean solveLambdas) {
    376         if (node == null) throw new IllegalArgumentException();
    377         return node.accept(typeExtractor, solveLambdas);
    378     }
    379 
    380     protected com.github.javaparser.ast.body.TypeDeclaration<?> findContainingTypeDecl(Node node) {
    381         if (node instanceof ClassOrInterfaceDeclaration) {
    382             return (ClassOrInterfaceDeclaration) node;
    383         }
    384         if (node instanceof EnumDeclaration) {
    385             return (EnumDeclaration) node;
    386         }
    387         return findContainingTypeDecl(requireParentNode(node));
    388 
    389     }
    390 
    391     protected Node findContainingTypeDeclOrObjectCreationExpr(Node node) {
    392         if (node instanceof ClassOrInterfaceDeclaration) {
    393             return node;
    394         }
    395         if (node instanceof EnumDeclaration) {
    396             return node;
    397         }
    398         Node parent = requireParentNode(node);
    399         if (parent instanceof ObjectCreationExpr && !((ObjectCreationExpr) parent).getArguments().contains(node)) {
    400             return parent;
    401         }
    402         return findContainingTypeDeclOrObjectCreationExpr(parent);
    403     }
    404 
    405     public ResolvedType convertToUsageVariableType(VariableDeclarator var) {
    406         return get(typeSolver).convertToUsage(var.getType(), var);
    407     }
    408 
    409     public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Node context) {
    410         if (type.isUnknownType()) {
    411             throw new IllegalArgumentException("Inferred lambda parameter type");
    412         }
    413         return convertToUsage(type, JavaParserFactory.getContext(context, typeSolver));
    414     }
    415 
    416     public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type) {
    417         return convertToUsage(type, type);
    418     }
    419 
    420     // This is an hack around an issue in JavaParser
    421     private String qName(ClassOrInterfaceType classOrInterfaceType) {
    422         String name = classOrInterfaceType.getName().getId();
    423         if (classOrInterfaceType.getScope().isPresent()) {
    424             return qName(classOrInterfaceType.getScope().get()) + "." + name;
    425         }
    426         return name;
    427     }
    428 
    429     protected ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Context context) {
    430         if (context == null) {
    431             throw new NullPointerException("Context should not be null");
    432         }
    433         if (type instanceof ClassOrInterfaceType) {
    434             ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type;
    435             String name = qName(classOrInterfaceType);
    436             SymbolReference<ResolvedTypeDeclaration> ref = context.solveType(name, typeSolver);
    437             if (!ref.isSolved()) {
    438                 throw new UnsolvedSymbolException(name);
    439             }
    440             ResolvedTypeDeclaration typeDeclaration = ref.getCorrespondingDeclaration();
    441             List<ResolvedType> typeParameters = Collections.emptyList();
    442             if (classOrInterfaceType.getTypeArguments().isPresent()) {
    443                 typeParameters = classOrInterfaceType.getTypeArguments().get().stream().map((pt) -> convertToUsage(pt, context)).collect(Collectors.toList());
    444             }
    445             if (typeDeclaration.isTypeParameter()) {
    446                 if (typeDeclaration instanceof ResolvedTypeParameterDeclaration) {
    447                     return new ResolvedTypeVariable((ResolvedTypeParameterDeclaration) typeDeclaration);
    448                 } else {
    449                     JavaParserTypeVariableDeclaration javaParserTypeVariableDeclaration = (JavaParserTypeVariableDeclaration) typeDeclaration;
    450                     return new ResolvedTypeVariable(javaParserTypeVariableDeclaration.asTypeParameter());
    451                 }
    452             } else {
    453                 return new ReferenceTypeImpl((ResolvedReferenceTypeDeclaration) typeDeclaration, typeParameters, typeSolver);
    454             }
    455         } else if (type instanceof com.github.javaparser.ast.type.PrimitiveType) {
    456             return ResolvedPrimitiveType.byName(((com.github.javaparser.ast.type.PrimitiveType) type).getType().name());
    457         } else if (type instanceof WildcardType) {
    458             WildcardType wildcardType = (WildcardType) type;
    459             if (wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) {
    460                 return ResolvedWildcard.extendsBound(convertToUsage(wildcardType.getExtendedType().get(), context)); // removed (ReferenceTypeImpl)
    461             } else if (!wildcardType.getExtendedType().isPresent() && wildcardType.getSuperType().isPresent()) {
    462                 return ResolvedWildcard.superBound(convertToUsage(wildcardType.getSuperType().get(), context)); // removed (ReferenceTypeImpl)
    463             } else if (!wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) {
    464                 return ResolvedWildcard.UNBOUNDED;
    465             } else {
    466                 throw new UnsupportedOperationException(wildcardType.toString());
    467             }
    468         } else if (type instanceof com.github.javaparser.ast.type.VoidType) {
    469             return ResolvedVoidType.INSTANCE;
    470         } else if (type instanceof com.github.javaparser.ast.type.ArrayType) {
    471             com.github.javaparser.ast.type.ArrayType jpArrayType = (com.github.javaparser.ast.type.ArrayType) type;
    472             return new ResolvedArrayType(convertToUsage(jpArrayType.getComponentType(), context));
    473         } else if (type instanceof UnionType) {
    474             UnionType unionType = (UnionType) type;
    475             return new ResolvedUnionType(unionType.getElements().stream().map(el -> convertToUsage(el, context)).collect(Collectors.toList()));
    476         } else if (type instanceof VarType) {
    477             Node parent = type.getParentNode().get();
    478             if (!(parent instanceof VariableDeclarator)) {
    479                 throw new IllegalStateException("Trying to resolve a `var` which is not in a variable declaration.");
    480             }
    481             final VariableDeclarator variableDeclarator = (VariableDeclarator) parent;
    482             return variableDeclarator.getInitializer()
    483                     .map(Expression::calculateResolvedType)
    484                     .orElseThrow(() -> new IllegalStateException("Cannot resolve `var` which has no initializer."));
    485         } else {
    486             throw new UnsupportedOperationException(type.getClass().getCanonicalName());
    487         }
    488     }
    489 
    490 
    491     public ResolvedType convert(Type type, Node node) {
    492         return convert(type, JavaParserFactory.getContext(node, typeSolver));
    493     }
    494 
    495     public ResolvedType convert(com.github.javaparser.ast.type.Type type, Context context) {
    496         return convertToUsage(type, context);
    497     }
    498 
    499     public MethodUsage solveMethodAsUsage(MethodCallExpr call) {
    500         List<ResolvedType> params = new ArrayList<>();
    501         if (call.getArguments() != null) {
    502             for (Expression param : call.getArguments()) {
    503                 //getTypeConcrete(Node node, boolean solveLambdas)
    504                 try {
    505                     params.add(getType(param, false));
    506                 } catch (Exception e) {
    507                     throw new RuntimeException(String.format("Error calculating the type of parameter %s of method call %s", param, call), e);
    508                 }
    509                 //params.add(getTypeConcrete(param, false));
    510             }
    511         }
    512         Context context = JavaParserFactory.getContext(call, typeSolver);
    513         Optional<MethodUsage> methodUsage = context.solveMethodAsUsage(call.getName().getId(), params, typeSolver);
    514         if (!methodUsage.isPresent()) {
    515             throw new RuntimeException("Method '" + call.getName() + "' cannot be resolved in context "
    516                     + call + " (line: " + call.getRange().map(r -> "" + r.begin.line).orElse("??") + ") " + context + ". Parameter types: " + params);
    517         }
    518         return methodUsage.get();
    519     }
    520 
    521     public ResolvedReferenceTypeDeclaration getTypeDeclaration(Node node) {
    522         if (node instanceof TypeDeclaration) {
    523             return getTypeDeclaration((TypeDeclaration) node);
    524         } else if (node instanceof ObjectCreationExpr) {
    525             return new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver);
    526         } else {
    527             throw new IllegalArgumentException();
    528         }
    529     }
    530 
    531     public ResolvedReferenceTypeDeclaration getTypeDeclaration(ClassOrInterfaceDeclaration classOrInterfaceDeclaration) {
    532         return JavaParserFactory.toTypeDeclaration(classOrInterfaceDeclaration, typeSolver);
    533     }
    534 
    535     /**
    536      * "this" inserted in the given point, which type would have?
    537      */
    538     public ResolvedType getTypeOfThisIn(Node node) {
    539         // TODO consider static methods
    540         if (node instanceof ClassOrInterfaceDeclaration) {
    541             return new ReferenceTypeImpl(getTypeDeclaration((ClassOrInterfaceDeclaration) node), typeSolver);
    542         } else if (node instanceof EnumDeclaration) {
    543             JavaParserEnumDeclaration enumDeclaration = new JavaParserEnumDeclaration((EnumDeclaration) node, typeSolver);
    544             return new ReferenceTypeImpl(enumDeclaration, typeSolver);
    545         } else if (node instanceof ObjectCreationExpr && ((ObjectCreationExpr) node).getAnonymousClassBody().isPresent()) {
    546             JavaParserAnonymousClassDeclaration anonymousDeclaration = new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver);
    547             return new ReferenceTypeImpl(anonymousDeclaration, typeSolver);
    548         }
    549         return getTypeOfThisIn(requireParentNode(node));
    550     }
    551 
    552     public ResolvedReferenceTypeDeclaration getTypeDeclaration(com.github.javaparser.ast.body.TypeDeclaration<?> typeDeclaration) {
    553         return JavaParserFactory.toTypeDeclaration(typeDeclaration, typeSolver);
    554     }
    555 
    556     public ResolvedType classToResolvedType(Class<?> clazz) {
    557         if (clazz.isPrimitive()) {
    558             return ResolvedPrimitiveType.byName(clazz.getName());
    559         }
    560         return new ReferenceTypeImpl(new ReflectionClassDeclaration(String.class, typeSolver), typeSolver);
    561     }
    562 }
    563