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
17package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
18
19import com.github.javaparser.ast.CompilationUnit;
20import com.github.javaparser.ast.ImportDeclaration;
21import com.github.javaparser.ast.body.AnnotationDeclaration;
22import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
23import com.github.javaparser.ast.body.EnumDeclaration;
24import com.github.javaparser.ast.body.TypeDeclaration;
25import com.github.javaparser.ast.expr.MethodCallExpr;
26import com.github.javaparser.ast.expr.Name;
27import com.github.javaparser.ast.type.ClassOrInterfaceType;
28import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
29import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
30import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
31import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
32import com.github.javaparser.resolution.types.ResolvedType;
33import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
34import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnnotationDeclaration;
35import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
36import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
37import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
38import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
39import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
40
41import java.util.List;
42
43/**
44 * @author Federico Tomassetti
45 */
46public class CompilationUnitContext extends AbstractJavaParserContext<CompilationUnit> {
47
48    ///
49    /// Static methods
50    ///
51
52    private static boolean isQualifiedName(String name) {
53        return name.contains(".");
54    }
55
56    ///
57    /// Constructors
58    ///
59
60    public CompilationUnitContext(CompilationUnit wrappedNode, TypeSolver typeSolver) {
61        super(wrappedNode, typeSolver);
62    }
63
64    ///
65    /// Public methods
66    ///
67
68    @Override
69    public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
70
71        // solve absolute references
72        String itName = name;
73        while (itName.contains(".")) {
74            String typeName = getType(itName);
75            String memberName = getMember(itName);
76            SymbolReference<ResolvedTypeDeclaration> type = this.solveType(typeName, typeSolver);
77            if (type.isSolved()) {
78                return new SymbolSolver(typeSolver).solveSymbolInType(type.getCorrespondingDeclaration(), memberName);
79            } else {
80                itName = typeName;
81            }
82        }
83
84        // Look among statically imported values
85        if (wrappedNode.getImports() != null) {
86            for (ImportDeclaration importDecl : wrappedNode.getImports()) {
87                if(importDecl.isStatic()){
88                    if(importDecl.isAsterisk()) {
89                        String qName = importDecl.getNameAsString();
90                        ResolvedTypeDeclaration importedType = typeSolver.solveType(qName);
91                        SymbolReference<? extends ResolvedValueDeclaration> ref = new SymbolSolver(typeSolver).solveSymbolInType(importedType, name);
92                        if (ref.isSolved()) {
93                            return ref;
94                        }
95                    } else{
96                        String whole = importDecl.getNameAsString();
97
98                        // split in field/method name and type name
99                        String memberName = getMember(whole);
100                        String typeName = getType(whole);
101
102                        if (memberName.equals(name)) {
103                            ResolvedTypeDeclaration importedType = typeSolver.solveType(typeName);
104                            return new SymbolSolver(typeSolver).solveSymbolInType(importedType, memberName);
105                        }
106                    }
107                }
108            }
109        }
110
111        return SymbolReference.unsolved(ResolvedValueDeclaration.class);
112    }
113
114    @Override
115    public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
116        if (wrappedNode.getTypes() != null) {
117            for (TypeDeclaration<?> type : wrappedNode.getTypes()) {
118                if (type.getName().getId().equals(name)) {
119                    if (type instanceof ClassOrInterfaceDeclaration) {
120                        return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration((ClassOrInterfaceDeclaration) type));
121                    } else if (type instanceof AnnotationDeclaration) {
122                        return SymbolReference.solved(new JavaParserAnnotationDeclaration((AnnotationDeclaration) type, typeSolver));
123                    } else if (type instanceof EnumDeclaration) {
124                        return SymbolReference.solved(new JavaParserEnumDeclaration((EnumDeclaration) type, typeSolver));
125                    } else {
126                        throw new UnsupportedOperationException(type.getClass().getCanonicalName());
127                    }
128                }
129            }
130        }
131
132        if (wrappedNode.getImports() != null) {
133            int dotPos = name.indexOf('.');
134            String prefix = null;
135            if (dotPos > -1) {
136                prefix = name.substring(0, dotPos);
137            }
138            // look into type imports
139            for (ImportDeclaration importDecl : wrappedNode.getImports()) {
140                if (!importDecl.isAsterisk()) {
141                    String qName = importDecl.getNameAsString();
142                    boolean defaultPackage = !importDecl.getName().getQualifier().isPresent();
143                    boolean found = !defaultPackage && importDecl.getName().getIdentifier().equals(name);
144                    if (!found) {
145                        if (prefix != null) {
146                            found = qName.endsWith("." + prefix);
147                            if (found) {
148                                qName = qName + name.substring(dotPos);
149                            }
150                        }
151                    }
152                    if (found) {
153                        SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
154                        if (ref.isSolved()) {
155                            return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
156                        }
157                    }
158                }
159            }
160            // look into type imports on demand
161            for (ImportDeclaration importDecl : wrappedNode.getImports()) {
162                if (importDecl.isAsterisk()) {
163                    String qName = importDecl.getNameAsString() + "." + name;
164                    SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
165                    if (ref.isSolved()) {
166                        return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
167                    }
168                }
169            }
170        }
171
172        // Look in current package
173        if (this.wrappedNode.getPackageDeclaration().isPresent()) {
174            String qName = this.wrappedNode.getPackageDeclaration().get().getName().toString() + "." + name;
175            SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
176            if (ref.isSolved()) {
177                return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
178            }
179        } else {
180            // look for classes in the default package
181            String qName = name;
182            SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
183            if (ref.isSolved()) {
184                return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
185            }
186        }
187
188        // Look in the java.lang package
189        SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType("java.lang." + name);
190        if (ref.isSolved()) {
191            return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
192        }
193
194        // DO NOT look for absolute name if this name is not qualified: you cannot import classes from the default package
195        if (isQualifiedName(name)) {
196            return SymbolReference.adapt(typeSolver.tryToSolveType(name), ResolvedTypeDeclaration.class);
197        } else {
198            return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
199        }
200    }
201
202    private String qName(ClassOrInterfaceType type) {
203        if (type.getScope().isPresent()) {
204            return qName(type.getScope().get()) + "." + type.getName().getId();
205        } else {
206            return type.getName().getId();
207        }
208    }
209
210    private String qName(Name name) {
211        if (name.getQualifier().isPresent()) {
212            return qName(name.getQualifier().get()) + "." + name.getId();
213        } else {
214            return name.getId();
215        }
216    }
217
218    private String toSimpleName(String qName) {
219        String[] parts = qName.split("\\.");
220        return parts[parts.length - 1];
221    }
222
223    private String packageName(String qName) {
224        int lastDot = qName.lastIndexOf('.');
225        if (lastDot == -1) {
226            throw new UnsupportedOperationException();
227        } else {
228            return qName.substring(0, lastDot);
229        }
230    }
231
232    @Override
233    public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
234        for (ImportDeclaration importDecl : wrappedNode.getImports()) {
235            if(importDecl.isStatic()){
236                if(importDecl.isAsterisk()){
237                    String importString = importDecl.getNameAsString();
238
239                    if (this.wrappedNode.getPackageDeclaration().isPresent()
240                            && this.wrappedNode.getPackageDeclaration().get().getName().getIdentifier().equals(packageName(importString))
241                            && this.wrappedNode.getTypes().stream().anyMatch(it -> it.getName().getIdentifier().equals(toSimpleName(importString)))) {
242                        // We are using a static import on a type defined in this file. It means the value was not found at
243                        // a lower level so this will fail
244                        return SymbolReference.unsolved(ResolvedMethodDeclaration.class);
245                    }
246
247                    ResolvedTypeDeclaration ref = typeSolver.solveType(importString);
248                    SymbolReference<ResolvedMethodDeclaration> method = MethodResolutionLogic.solveMethodInType(ref, name, argumentsTypes, true, typeSolver);
249
250                    if (method.isSolved()) {
251                        return method;
252                    }
253                } else{
254                    String qName = importDecl.getNameAsString();
255
256                    if (qName.equals(name) || qName.endsWith("." + name)) {
257                        String typeName = getType(qName);
258                        ResolvedTypeDeclaration ref = typeSolver.solveType(typeName);
259                        SymbolReference<ResolvedMethodDeclaration> method = MethodResolutionLogic.solveMethodInType(ref, name, argumentsTypes, true, typeSolver);
260                        if (method.isSolved()) {
261                            return method;
262                        }
263                    }
264                }
265            }
266        }
267        return SymbolReference.unsolved(ResolvedMethodDeclaration.class);
268    }
269
270    ///
271    /// Private methods
272    ///
273
274    private String getType(String qName) {
275        int index = qName.lastIndexOf('.');
276        if (index == -1) {
277            throw new UnsupportedOperationException();
278        }
279        String typeName = qName.substring(0, index);
280        return typeName;
281    }
282
283    private String getMember(String qName) {
284        int index = qName.lastIndexOf('.');
285        if (index == -1) {
286            throw new UnsupportedOperationException();
287        }
288        String memberName = qName.substring(index + 1);
289        return memberName;
290    }
291}
292