1package com.github.javaparser.symbolsolver;
2
3import com.github.javaparser.ast.CompilationUnit;
4import com.github.javaparser.ast.Node;
5import com.github.javaparser.ast.body.*;
6import com.github.javaparser.ast.expr.Expression;
7import com.github.javaparser.ast.expr.MethodCallExpr;
8import com.github.javaparser.ast.expr.NameExpr;
9import com.github.javaparser.ast.expr.ThisExpr;
10import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
11import com.github.javaparser.ast.type.Type;
12import com.github.javaparser.resolution.SymbolResolver;
13import com.github.javaparser.resolution.UnsolvedSymbolException;
14import com.github.javaparser.resolution.declarations.*;
15import com.github.javaparser.resolution.types.ResolvedType;
16import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
17import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
18import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*;
19import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
20import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
21
22/**
23 * This implementation of the SymbolResolver wraps the functionalities of the library to make them easily usable
24 * from JavaParser nodes.
25 *
26 * An instance of this class should be created once and then injected in all the CompilationUnit for which we
27 * want to enable symbol resolution. To do so the method inject can be used.
28 *
29 * @author Federico Tomassetti
30 */
31public class JavaSymbolSolver implements SymbolResolver {
32
33    private TypeSolver typeSolver;
34
35    public JavaSymbolSolver(TypeSolver typeSolver) {
36        this.typeSolver = typeSolver;
37    }
38
39    /**
40     * Register this SymbolResolver into a CompilationUnit, so that symbol resolution becomes available to
41     * all nodes part of the CompilationUnit.
42     */
43    public void inject(CompilationUnit destination) {
44        destination.setData(Node.SYMBOL_RESOLVER_KEY, this);
45    }
46
47    @Override
48    public <T> T resolveDeclaration(Node node, Class<T> resultClass) {
49        if (node instanceof MethodDeclaration) {
50            return resultClass.cast(new JavaParserMethodDeclaration((MethodDeclaration)node, typeSolver));
51        }
52        if (node instanceof ClassOrInterfaceDeclaration) {
53            ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
54            if (resultClass.isInstance(resolved)) {
55                return resultClass.cast(resolved);
56            }
57        }
58        if (node instanceof EnumDeclaration) {
59            ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
60            if (resultClass.isInstance(resolved)) {
61                return resultClass.cast(resolved);
62            }
63        }
64        if (node instanceof EnumConstantDeclaration) {
65            ResolvedEnumDeclaration enumDeclaration = node.findParent(EnumDeclaration.class).get().resolve().asEnum();
66            ResolvedEnumConstantDeclaration resolved = enumDeclaration.getEnumConstants().stream().filter(c -> ((JavaParserEnumConstantDeclaration)c).getWrappedNode() == node).findFirst().get();
67            if (resultClass.isInstance(resolved)) {
68                return resultClass.cast(resolved);
69            }
70        }
71        if (node instanceof ConstructorDeclaration) {
72            ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration)node;
73            ClassOrInterfaceDeclaration classOrInterfaceDeclaration = (ClassOrInterfaceDeclaration)node.getParentNode().get();
74            ResolvedClassDeclaration resolvedClass = resolveDeclaration(classOrInterfaceDeclaration, ResolvedClassDeclaration.class).asClass();
75            ResolvedConstructorDeclaration resolved =  resolvedClass.getConstructors().stream().filter(c -> ((JavaParserConstructorDeclaration)c).getWrappedNode() == constructorDeclaration).findFirst().get();
76            if (resultClass.isInstance(resolved)) {
77                return resultClass.cast(resolved);
78            }
79        }
80        if (node instanceof AnnotationDeclaration) {
81            ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
82            if (resultClass.isInstance(resolved)) {
83                return resultClass.cast(resolved);
84            }
85        }
86        if (node instanceof AnnotationMemberDeclaration) {
87            ResolvedAnnotationDeclaration annotationDeclaration = node.findParent(AnnotationDeclaration.class).get().resolve();
88            ResolvedAnnotationMemberDeclaration resolved = annotationDeclaration.getAnnotationMembers().stream().filter(c -> ((JavaParserAnnotationMemberDeclaration)c).getWrappedNode() == node).findFirst().get();
89            if (resultClass.isInstance(resolved)) {
90                return resultClass.cast(resolved);
91            }
92        }
93        if (node instanceof FieldDeclaration) {
94            FieldDeclaration fieldDeclaration = (FieldDeclaration)node;
95            if (fieldDeclaration.getVariables().size() != 1) {
96                throw new RuntimeException("Cannot resolve a Field Declaration including multiple variable declarators. Resolve the single variable declarators");
97            }
98            ResolvedFieldDeclaration resolved = new JavaParserFieldDeclaration(fieldDeclaration.getVariable(0), typeSolver);
99            if (resultClass.isInstance(resolved)) {
100                return resultClass.cast(resolved);
101            }
102        }
103        if (node instanceof VariableDeclarator) {
104            ResolvedFieldDeclaration resolved = new JavaParserFieldDeclaration((VariableDeclarator)node, typeSolver);
105            if (resultClass.isInstance(resolved)) {
106                return resultClass.cast(resolved);
107            }
108        }
109        if (node instanceof MethodCallExpr) {
110            SymbolReference<ResolvedMethodDeclaration> result = JavaParserFacade.get(typeSolver).solve((MethodCallExpr)node);
111            if (result.isSolved()) {
112                if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
113                    return resultClass.cast(result.getCorrespondingDeclaration());
114                }
115            } else {
116                throw new UnsolvedSymbolException("We are unable to find the method declaration corresponding to " + node);
117            }
118        }
119        if (node instanceof NameExpr) {
120            SymbolReference<? extends ResolvedValueDeclaration> result = JavaParserFacade.get(typeSolver).solve((NameExpr) node);
121            if (result.isSolved()) {
122                if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
123                    return resultClass.cast(result.getCorrespondingDeclaration());
124                }
125            } else {
126                throw new UnsolvedSymbolException("We are unable to find the value declaration corresponding to " + node);
127            }
128        }
129        if (node instanceof ThisExpr) {
130            SymbolReference<ResolvedTypeDeclaration> result = JavaParserFacade.get(typeSolver).solve((ThisExpr) node);
131            if (result.isSolved()) {
132                if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
133                    return resultClass.cast(result.getCorrespondingDeclaration());
134                }
135            } else {
136                throw new UnsolvedSymbolException("We are unable to find the type declaration corresponding to " + node);
137            }
138        }
139        if (node instanceof ExplicitConstructorInvocationStmt) {
140            SymbolReference<ResolvedConstructorDeclaration> result = JavaParserFacade.get(typeSolver).solve((ExplicitConstructorInvocationStmt) node);
141            if (result.isSolved()) {
142                if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
143                    return resultClass.cast(result.getCorrespondingDeclaration());
144                }
145            } else {
146                throw new UnsolvedSymbolException("We are unable to find the constructor declaration corresponding to " + node);
147            }
148        }
149        if (node instanceof Parameter) {
150            if (ResolvedParameterDeclaration.class.equals(resultClass)) {
151                Parameter parameter = (Parameter)node;
152                CallableDeclaration callableDeclaration = node.findParent(CallableDeclaration.class).get();
153                ResolvedMethodLikeDeclaration resolvedMethodLikeDeclaration;
154                if (callableDeclaration.isConstructorDeclaration()) {
155                    resolvedMethodLikeDeclaration = callableDeclaration.asConstructorDeclaration().resolve();
156                } else {
157                    resolvedMethodLikeDeclaration = callableDeclaration.asMethodDeclaration().resolve();
158                }
159                for (int i=0;i<resolvedMethodLikeDeclaration.getNumberOfParams();i++) {
160                    if (resolvedMethodLikeDeclaration.getParam(i).getName().equals(parameter.getNameAsString())) {
161                        return resultClass.cast(resolvedMethodLikeDeclaration.getParam(i));
162                    }
163                }
164            }
165        }
166        throw new UnsupportedOperationException("Unable to find the declaration of type " + resultClass.getSimpleName()
167                + " from " + node.getClass().getSimpleName());
168    }
169
170    @Override
171    public <T> T toResolvedType(Type javaparserType, Class<T> resultClass) {
172        ResolvedType resolvedType = JavaParserFacade.get(typeSolver).convertToUsage(javaparserType, javaparserType);
173        if (resultClass.isInstance(resolvedType)) {
174            return resultClass.cast(resolvedType);
175        }
176        throw new UnsupportedOperationException("Unable to get the resolved type of class "
177                + resultClass.getSimpleName() + " from " + javaparserType);
178    }
179
180    @Override
181    public ResolvedType calculateType(Expression expression) {
182        return JavaParserFacade.get(typeSolver).getType(expression);
183    }
184}
185