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.core.resolution;
18 
19 import com.github.javaparser.ast.Node;
20 import com.github.javaparser.ast.body.Parameter;
21 import com.github.javaparser.ast.body.VariableDeclarator;
22 import com.github.javaparser.resolution.MethodUsage;
23 import com.github.javaparser.resolution.declarations.*;
24 import com.github.javaparser.resolution.types.ResolvedType;
25 import com.github.javaparser.symbolsolver.javaparsermodel.contexts.AbstractJavaParserContext;
26 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
27 import com.github.javaparser.symbolsolver.model.resolution.Value;
28 
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Optional;
32 
33 /**
34  * Context is very similar to scope.
35  * In the context we look for solving symbols.
36  *
37  * @author Federico Tomassetti
38  */
39 public interface Context {
40 
getParent()41     Context getParent();
42 
43     /* Type resolution */
44 
solveGenericType(String name)45     default Optional<ResolvedType> solveGenericType(String name) {
46         return Optional.empty();
47     }
48 
solveType(String name)49     default SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
50         Context parent = getParent();
51         if (parent == null) {
52             return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
53         } else {
54             return parent.solveType(name);
55         }
56     }
57 
58     /* Symbol resolution */
59 
solveSymbol(String name)60     default SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name) {
61         return getParent().solveSymbol(name);
62     }
63 
solveSymbolAsValue(String name)64     default Optional<Value> solveSymbolAsValue(String name) {
65         SymbolReference<? extends ResolvedValueDeclaration> ref = solveSymbol(name);
66         if (ref.isSolved()) {
67             Value value = Value.from(ref.getCorrespondingDeclaration());
68             return Optional.of(value);
69         } else {
70             return Optional.empty();
71         }
72     }
73 
74     /**
75      * The local variables that are declared in this immediate context and made visible to a given child.
76      * This list could include values which are shadowed.
77      */
localVariablesExposedToChild(Node child)78     default List<VariableDeclarator> localVariablesExposedToChild(Node child) {
79         return Collections.emptyList();
80     }
81 
82     /**
83      * The parameters that are declared in this immediate context and made visible to a given child.
84      * This list could include values which are shadowed.
85      */
parametersExposedToChild(Node child)86     default List<Parameter> parametersExposedToChild(Node child) {
87         return Collections.emptyList();
88     }
89 
90     /**
91      * The fields that are declared and in this immediate context made visible to a given child.
92      * This list could include values which are shadowed.
93      */
fieldsExposedToChild(Node child)94     default List<ResolvedFieldDeclaration> fieldsExposedToChild(Node child) {
95         return Collections.emptyList();
96     }
97 
98     /**
99      * Aim to resolve the given name by looking for a variable matching it.
100      *
101      * To do it consider local variables that are visible in a certain scope as defined in JLS 6.3. Scope of a Declaration.
102      *
103      * 1. The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration
104      * appears, starting with its own initializer and including any further declarators to the right in the local
105      * variable declaration statement.
106      *
107      * 2. The scope of a local variable declared in the ForInit part of a basic for statement (§14.14.1) includes all
108      * of the following:
109      * 2.1 Its own initializer
110      * 2.2 Any further declarators to the right in the ForInit part of the for statement
111      * 2.3 The Expression and ForUpdate parts of the for statement
112      * 2.4 The contained Statement
113      *
114      * 3. The scope of a local variable declared in the FormalParameter part of an enhanced for statement (§14.14.2) is
115      * the contained Statement.
116      * 4. The scope of a parameter of an exception handler that is declared in a catch clause of a try statement
117      * (§14.20) is the entire block associated with the catch.
118      *
119      * 5. The scope of a variable declared in the ResourceSpecification of a try-with-resources statement (§14.20.3) is
120      * from the declaration rightward over the remainder of the ResourceSpecification and the entire try block
121      * associated with the try-with-resources statement.
122      */
localVariableDeclarationInScope(String name)123     default Optional<VariableDeclarator> localVariableDeclarationInScope(String name) {
124         if (getParent() == null) {
125             return Optional.empty();
126         }
127         Optional<VariableDeclarator> localRes = getParent().localVariablesExposedToChild(((AbstractJavaParserContext)this)
128                 .getWrappedNode()).stream().filter(vd -> vd.getNameAsString().equals(name)).findFirst();
129         if (localRes.isPresent()) {
130             return localRes;
131         }
132 
133         return getParent().localVariableDeclarationInScope(name);
134     }
135 
parameterDeclarationInScope(String name)136     default Optional<Parameter> parameterDeclarationInScope(String name) {
137         if (getParent() == null) {
138             return Optional.empty();
139         }
140         Optional<Parameter> localRes = getParent().parametersExposedToChild(((AbstractJavaParserContext)this)
141                 .getWrappedNode()).stream().filter(vd -> vd.getNameAsString().equals(name)).findFirst();
142         if (localRes.isPresent()) {
143             return localRes;
144         }
145 
146         return getParent().parameterDeclarationInScope(name);
147     }
148 
fieldDeclarationInScope(String name)149     default Optional<ResolvedFieldDeclaration> fieldDeclarationInScope(String name) {
150         if (getParent() == null) {
151             return Optional.empty();
152         }
153         Optional<ResolvedFieldDeclaration> localRes = getParent().fieldsExposedToChild(((AbstractJavaParserContext)this)
154                 .getWrappedNode()).stream().filter(vd -> vd.getName().equals(name)).findFirst();
155         if (localRes.isPresent()) {
156             return localRes;
157         }
158 
159         return getParent().fieldDeclarationInScope(name);
160     }
161 
162     /* Constructor resolution */
163 
164     /**
165      * We find the method declaration which is the best match for the given name and list of typeParametersValues.
166      */
solveConstructor(List<ResolvedType> argumentsTypes)167     default SymbolReference<ResolvedConstructorDeclaration> solveConstructor(List<ResolvedType> argumentsTypes) {
168         throw new IllegalArgumentException("Constructor resolution is available only on Class Context");
169     }
170 
171     /* Methods resolution */
172 
173     /**
174      * We find the method declaration which is the best match for the given name and list of typeParametersValues.
175      */
solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly)176     default SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes,
177                                                                    boolean staticOnly) {
178         return getParent().solveMethod(name, argumentsTypes, staticOnly);
179     }
180 
181     /**
182      * Similar to solveMethod but we return a MethodUsage. A MethodUsage corresponds to a MethodDeclaration plus the
183      * resolved type variables.
184      */
solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes)185     default Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes) {
186         SymbolReference<ResolvedMethodDeclaration> methodSolved = solveMethod(name, argumentsTypes, false);
187         if (methodSolved.isSolved()) {
188             ResolvedMethodDeclaration methodDeclaration = methodSolved.getCorrespondingDeclaration();
189 
190             MethodUsage methodUsage;
191             if (methodDeclaration instanceof TypeVariableResolutionCapability) {
192                 methodUsage = ((TypeVariableResolutionCapability) methodDeclaration)
193                                       .resolveTypeVariables(this, argumentsTypes);
194             } else {
195                 throw new UnsupportedOperationException("Resolved method declarations should have the " +
196                                                         TypeVariableResolutionCapability.class.getName() + ".");
197             }
198 
199             return Optional.of(methodUsage);
200         } else {
201             return Optional.empty();
202         }
203     }
204 
205 }
206