1 package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
2 
3 import com.github.javaparser.ast.AccessSpecifier;
4 import com.github.javaparser.ast.body.BodyDeclaration;
5 import com.github.javaparser.ast.nodeTypes.NodeWithTypeParameters;
6 import com.github.javaparser.ast.type.TypeParameter;
7 import com.github.javaparser.resolution.declarations.*;
8 import com.github.javaparser.resolution.types.ResolvedReferenceType;
9 import com.github.javaparser.resolution.types.ResolvedType;
10 import com.github.javaparser.symbolsolver.core.resolution.Context;
11 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
12 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
13 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
14 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
15 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
16 import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
17 import com.github.javaparser.symbolsolver.resolution.ConstructorResolutionLogic;
18 import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
19 
20 import java.util.List;
21 import java.util.stream.Collectors;
22 
23 /**
24  * @author Federico Tomassetti
25  */
26 public class JavaParserTypeDeclarationAdapter {
27 
28     private com.github.javaparser.ast.body.TypeDeclaration<?> wrappedNode;
29     private TypeSolver typeSolver;
30     private Context context;
31     private ResolvedReferenceTypeDeclaration typeDeclaration;
32 
JavaParserTypeDeclarationAdapter(com.github.javaparser.ast.body.TypeDeclaration<?> wrappedNode, TypeSolver typeSolver, ResolvedReferenceTypeDeclaration typeDeclaration, Context context)33     public JavaParserTypeDeclarationAdapter(com.github.javaparser.ast.body.TypeDeclaration<?> wrappedNode, TypeSolver typeSolver,
34                                             ResolvedReferenceTypeDeclaration typeDeclaration,
35                                             Context context) {
36         this.wrappedNode = wrappedNode;
37         this.typeSolver = typeSolver;
38         this.typeDeclaration = typeDeclaration;
39         this.context = context;
40     }
41 
solveType(String name)42     public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
43         if (this.wrappedNode.getName().getId().equals(name)) {
44             return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration(wrappedNode));
45         }
46 
47         // Internal classes
48         for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
49             if (member instanceof com.github.javaparser.ast.body.TypeDeclaration) {
50                 com.github.javaparser.ast.body.TypeDeclaration<?> internalType = (com.github.javaparser.ast.body.TypeDeclaration<?>) member;
51                 if (internalType.getName().getId().equals(name)) {
52                     return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration(internalType));
53                 } else if (name.startsWith(String.format("%s.%s", wrappedNode.getName(), internalType.getName()))) {
54                     return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(wrappedNode.getName().getId().length() + 1));
55                 } else if (name.startsWith(String.format("%s.", internalType.getName()))) {
56                     return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(internalType.getName().getId().length() + 1));
57                 }
58             }
59         }
60 
61         if (wrappedNode instanceof NodeWithTypeParameters) {
62             NodeWithTypeParameters<?> nodeWithTypeParameters = (NodeWithTypeParameters<?>) wrappedNode;
63             for (TypeParameter astTpRaw : nodeWithTypeParameters.getTypeParameters()) {
64                 TypeParameter astTp = astTpRaw;
65                 if (astTp.getName().getId().equals(name)) {
66                     return SymbolReference.solved(new JavaParserTypeParameter(astTp, typeSolver));
67                 }
68             }
69         }
70 
71         // Look into extended classes and implemented interfaces
72         ResolvedTypeDeclaration type = checkAncestorsForType(name, this.typeDeclaration);
73         return ((type != null) ? SymbolReference.solved(type) : context.getParent().solveType(name));
74     }
75 
76     /**
77      * Recursively checks the ancestors of the {@param declaration} if an internal type is declared with a name equal
78      * to {@param name}.
79      * @return A ResolvedTypeDeclaration matching the {@param name}, null otherwise
80      */
checkAncestorsForType(String name, ResolvedReferenceTypeDeclaration declaration)81     private ResolvedTypeDeclaration checkAncestorsForType(String name, ResolvedReferenceTypeDeclaration declaration) {
82         for (ResolvedReferenceType ancestor : declaration.getAncestors(true)) {
83             try {
84                 for (ResolvedTypeDeclaration internalTypeDeclaration : ancestor.getTypeDeclaration().internalTypes()) {
85                     boolean visible = true;
86                     if (internalTypeDeclaration instanceof ResolvedReferenceTypeDeclaration) {
87                         ResolvedReferenceTypeDeclaration resolvedReferenceTypeDeclaration = internalTypeDeclaration.asReferenceType();
88                         if (resolvedReferenceTypeDeclaration instanceof HasAccessSpecifier) {
89                             visible = ((HasAccessSpecifier) resolvedReferenceTypeDeclaration).accessSpecifier() != AccessSpecifier.PRIVATE;
90                         }
91                     }
92                     if (internalTypeDeclaration.getName().equals(name)) {
93                         if (visible) {
94                             return internalTypeDeclaration;
95                         } else {
96                             return null;
97                         }
98                     }
99                 }
100                 // check recursively the ancestors of this ancestor
101                 ResolvedTypeDeclaration ancestorDeclaration = checkAncestorsForType(name, ancestor.getTypeDeclaration());
102                 if (ancestorDeclaration != null) {
103                     return ancestorDeclaration;
104                 }
105             } catch (UnsupportedOperationException e) {
106                 // just continue using the next ancestor
107             }
108         }
109         return null;
110     }
111 
solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly)112     public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly) {
113         List<ResolvedMethodDeclaration> candidateMethods = typeDeclaration.getDeclaredMethods().stream()
114                 .filter(m -> m.getName().equals(name))
115                 .filter(m -> !staticOnly || m.isStatic())
116                 .collect(Collectors.toList());
117         // We want to avoid infinite recursion in case of Object having Object as ancestor
118         if (!Object.class.getCanonicalName().equals(typeDeclaration.getQualifiedName())) {
119             for (ResolvedReferenceType ancestor : typeDeclaration.getAncestors(true)) {
120                 // Avoid recursion on self
121                 if (typeDeclaration != ancestor.getTypeDeclaration()) {
122                     candidateMethods.addAll(ancestor.getAllMethodsVisibleToInheritors()
123                             .stream()
124                             .filter(m -> m.getName().equals(name))
125                             .collect(Collectors.toList()));
126                     SymbolReference<ResolvedMethodDeclaration> res = MethodResolutionLogic
127                             .solveMethodInType(ancestor.getTypeDeclaration(), name, argumentsTypes, staticOnly);
128                     // consider methods from superclasses and only default methods from interfaces :
129                     // not true, we should keep abstract as a valid candidate
130                     // abstract are removed in MethodResolutionLogic.isApplicable is necessary
131                     if (res.isSolved()) {
132                         candidateMethods.add(res.getCorrespondingDeclaration());
133                     }
134                 }
135             }
136         }
137         // We want to avoid infinite recursion when a class is using its own method
138         // see issue #75
139         if (candidateMethods.isEmpty()) {
140             SymbolReference<ResolvedMethodDeclaration> parentSolution = context.getParent().solveMethod(name, argumentsTypes, staticOnly);
141             if (parentSolution.isSolved()) {
142                 candidateMethods.add(parentSolution.getCorrespondingDeclaration());
143             }
144         }
145 
146         // if is interface and candidate method list is empty, we should check the Object Methods
147         if (candidateMethods.isEmpty() && typeDeclaration.isInterface()) {
148             SymbolReference<ResolvedMethodDeclaration> res = MethodResolutionLogic.solveMethodInType(new ReflectionClassDeclaration(Object.class, typeSolver), name, argumentsTypes, false);
149             if (res.isSolved()) {
150                 candidateMethods.add(res.getCorrespondingDeclaration());
151             }
152         }
153 
154         return MethodResolutionLogic.findMostApplicable(candidateMethods, name, argumentsTypes, typeSolver);
155     }
156 
solveConstructor(List<ResolvedType> argumentsTypes)157     public SymbolReference<ResolvedConstructorDeclaration> solveConstructor(List<ResolvedType> argumentsTypes) {
158         if (typeDeclaration instanceof ResolvedClassDeclaration) {
159             return ConstructorResolutionLogic.findMostApplicable(typeDeclaration.getConstructors(), argumentsTypes, typeSolver);
160         }
161         return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
162     }
163 }
164