1 | /* |
---|---|
2 | * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. |
3 | * Copyright (C) 2011, 2013-2020 The JavaParser Team. |
4 | * |
5 | * This file is part of JavaParser. |
6 | * |
7 | * JavaParser can be used either under the terms of |
8 | * a) the GNU Lesser General Public License as published by |
9 | * the Free Software Foundation, either version 3 of the License, or |
10 | * (at your option) any later version. |
11 | * b) the terms of the Apache License |
12 | * |
13 | * You should have received a copy of both licenses in LICENCE.LGPL and |
14 | * LICENCE.APACHE. Please refer to those files for details. |
15 | * |
16 | * JavaParser is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | * GNU Lesser General Public License for more details. |
20 | */ |
21 | |
22 | package com.github.javaparser.resolution.declarations; |
23 | |
24 | import java.util.ArrayList; |
25 | import java.util.List; |
26 | import java.util.Optional; |
27 | import java.util.Set; |
28 | import java.util.stream.Collectors; |
29 | |
30 | import com.github.javaparser.ast.AccessSpecifier; |
31 | import com.github.javaparser.resolution.MethodUsage; |
32 | import com.github.javaparser.resolution.UnsolvedSymbolException; |
33 | import com.github.javaparser.resolution.types.ResolvedReferenceType; |
34 | import com.github.javaparser.resolution.types.ResolvedType; |
35 | |
36 | /** |
37 | * @author Federico Tomassetti |
38 | */ |
39 | public interface ResolvedReferenceTypeDeclaration extends ResolvedTypeDeclaration, |
40 | ResolvedTypeParametrizable { |
41 | |
42 | @Override |
43 | default ResolvedReferenceTypeDeclaration asReferenceType() { |
44 | return this; |
45 | } |
46 | |
47 | /// |
48 | /// Ancestors |
49 | /// |
50 | |
51 | /** |
52 | * Resolves the types of all direct ancestors (i.e., the directly extended class and the directly implemented |
53 | * interfaces) and returns the list of ancestors as a list of resolved reference types. |
54 | * <p> |
55 | * In case any ancestor cannot be resolved, an {@code UnsolvedSymbolException} is thrown. In order to obtain a list |
56 | * of only the resolvable direct ancestors, use {@link #getAncestors(boolean)} and pass the value {@code true}. |
57 | * <p> |
58 | * Note that an ancestor can be parametrized types with values specified. For example: |
59 | * <p> |
60 | * class A implements Comparable<String> {} |
61 | * <p> |
62 | * In this case the ancestor is Comparable<String> |
63 | * |
64 | * @return The list of resolved ancestors. |
65 | * @throws UnsolvedSymbolException if some ancestor could not be resolved. |
66 | */ |
67 | default List<ResolvedReferenceType> getAncestors() { |
68 | return getAncestors(false); |
69 | } |
70 | |
71 | /** |
72 | * Resolves the types of all direct ancestors (i.e., the directly extended class and the directly implemented |
73 | * interfaces) and returns the list of ancestors as a list of resolved reference types. |
74 | * <p> |
75 | * If {@code acceptIncompleteList} is {@code false}, then an {@code UnsolvedSymbolException} is thrown if any |
76 | * ancestor cannot be resolved. Otherwise, a list of only the resolvable direct ancestors is returned. |
77 | * |
78 | * @param acceptIncompleteList When set to {@code false}, this method throws an {@link UnsolvedSymbolException} if |
79 | * one or more ancestor could not be resolved. When set to {@code true}, this method |
80 | * does not throw an {@link UnsolvedSymbolException}, but the list of returned ancestors |
81 | * may be incomplete in case one or more ancestor could not be resolved. |
82 | * @return The list of resolved ancestors. |
83 | * @throws UnsolvedSymbolException if some ancestor could not be resolved and {@code acceptIncompleteList} is set to |
84 | * {@code false}. |
85 | */ |
86 | List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList); |
87 | |
88 | /** |
89 | * The list of all the ancestors of the current declaration, direct and indirect. |
90 | * This list does not contains duplicates with the exact same type parameters. |
91 | */ |
92 | default List<ResolvedReferenceType> getAllAncestors() { |
93 | List<ResolvedReferenceType> ancestors = new ArrayList<>(); |
94 | // We want to avoid infinite recursion in case of Object having Object as ancestor |
95 | if (!isJavaLangObject()) { |
96 | for (ResolvedReferenceType ancestor : getAncestors()) { |
97 | ancestors.add(ancestor); |
98 | for (ResolvedReferenceType inheritedAncestor : ancestor.getAllAncestors()) { |
99 | if (!ancestors.contains(inheritedAncestor)) { |
100 | ancestors.add(inheritedAncestor); |
101 | } |
102 | } |
103 | } |
104 | } |
105 | return ancestors; |
106 | } |
107 | |
108 | /// |
109 | /// Fields |
110 | /// |
111 | |
112 | /** |
113 | * Note that the type of the field should be expressed using the type variables of this particular type. |
114 | * Consider for example: |
115 | * <p> |
116 | * class Foo<E> { E field; } |
117 | * <p> |
118 | * class Bar extends Foo<String> { } |
119 | * <p> |
120 | * When calling getField("field") on Foo I should get a FieldDeclaration with type E, while calling it on |
121 | * Bar I should get a FieldDeclaration with type String. |
122 | */ |
123 | default ResolvedFieldDeclaration getField(String name) { |
124 | Optional<ResolvedFieldDeclaration> field = this.getAllFields().stream() |
125 | .filter(f -> f.getName().equals(name)) |
126 | .findFirst(); |
127 | if (field.isPresent()) { |
128 | return field.get(); |
129 | } else { |
130 | throw new UnsolvedSymbolException("Field not found: " + name); |
131 | } |
132 | } |
133 | |
134 | /** |
135 | * Consider only field or inherited field which is not private. |
136 | */ |
137 | default ResolvedFieldDeclaration getVisibleField(String name) { |
138 | Optional<ResolvedFieldDeclaration> field = getVisibleFields().stream().filter(f -> f.getName().equals(name)).findFirst(); |
139 | if (field.isPresent()) { |
140 | return field.get(); |
141 | } else { |
142 | throw new IllegalArgumentException(); |
143 | } |
144 | } |
145 | |
146 | /** |
147 | * Has this type a field with the given name? |
148 | */ |
149 | default boolean hasField(String name) { |
150 | return this.getAllFields().stream().anyMatch(f -> f.getName().equals(name)); |
151 | } |
152 | |
153 | /** |
154 | * Either a declared field or inherited field which is not private. |
155 | */ |
156 | default boolean hasVisibleField(String name) { |
157 | return getVisibleFields().stream().anyMatch(f -> f.getName().equals(name)); |
158 | } |
159 | |
160 | /** |
161 | * Return a list of all fields, either declared in this declaration or inherited. |
162 | */ |
163 | List<ResolvedFieldDeclaration> getAllFields(); |
164 | |
165 | /** |
166 | * Return a list of all fields declared and the inherited ones which are not private. |
167 | */ |
168 | default List<ResolvedFieldDeclaration> getVisibleFields() { |
169 | return getAllFields().stream() |
170 | .filter(f -> f.declaringType().equals(this) || f.accessSpecifier() != AccessSpecifier.PRIVATE) |
171 | .collect(Collectors.toList()); |
172 | } |
173 | |
174 | /** |
175 | * Return a list of all the non static fields, either declared or inherited. |
176 | */ |
177 | default List<ResolvedFieldDeclaration> getAllNonStaticFields() { |
178 | return getAllFields().stream().filter(it -> !it.isStatic()).collect(Collectors.toList()); |
179 | } |
180 | |
181 | /** |
182 | * Return a list of all the static fields, either declared or inherited. |
183 | */ |
184 | default List<ResolvedFieldDeclaration> getAllStaticFields() { |
185 | return getAllFields().stream().filter(it -> it.isStatic()).collect(Collectors.toList()); |
186 | } |
187 | |
188 | /** |
189 | * Return a list of all the fields declared in this type. |
190 | */ |
191 | default List<ResolvedFieldDeclaration> getDeclaredFields() { |
192 | return getAllFields().stream().filter(it -> it.declaringType().getQualifiedName() |
193 | .equals(getQualifiedName())).collect(Collectors.toList()); |
194 | } |
195 | |
196 | /// |
197 | /// Methods |
198 | /// |
199 | |
200 | /** |
201 | * Return a list of all the methods declared in this type declaration. |
202 | */ |
203 | Set<ResolvedMethodDeclaration> getDeclaredMethods(); |
204 | |
205 | /** |
206 | * Return a list of all the methods declared of this type declaration, either declared or inherited. |
207 | * Note that it should not include overridden methods. |
208 | */ |
209 | Set<MethodUsage> getAllMethods(); |
210 | |
211 | /// |
212 | /// Assignability |
213 | /// |
214 | |
215 | /** |
216 | * Can we assign instances of the given type to variables having the type defined |
217 | * by this declaration? |
218 | */ |
219 | boolean isAssignableBy(ResolvedType type); |
220 | |
221 | /** |
222 | * Can we assign instances of the type defined by this declaration to variables having the type defined |
223 | * by the given type? |
224 | */ |
225 | default boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) { |
226 | return other.isAssignableBy(this); |
227 | } |
228 | |
229 | /** |
230 | * Can we assign instances of the given type to variables having the type defined |
231 | * by this declaration? |
232 | */ |
233 | boolean isAssignableBy(ResolvedReferenceTypeDeclaration other); |
234 | |
235 | /// |
236 | /// Annotations |
237 | /// |
238 | |
239 | /** |
240 | * Has the type at least one annotation declared having the specified qualified name? |
241 | */ |
242 | boolean hasDirectlyAnnotation(String qualifiedName); |
243 | |
244 | /** |
245 | * Has the type at least one annotation declared or inherited having the specified qualified name? |
246 | */ |
247 | default boolean hasAnnotation(String qualifiedName) { |
248 | if (hasDirectlyAnnotation(qualifiedName)) { |
249 | return true; |
250 | } |
251 | return getAllAncestors().stream() |
252 | .filter(it -> it.asReferenceType().getTypeDeclaration().isPresent()) |
253 | .anyMatch(it -> it.asReferenceType().getTypeDeclaration().get().hasDirectlyAnnotation(qualifiedName)); |
254 | } |
255 | |
256 | /** |
257 | * This means that the type has a functional method. Conceptually, a functional interface has exactly one abstract method. |
258 | * Typically these classes has the FunctionInterface annotation but this is not mandatory. |
259 | */ |
260 | boolean isFunctionalInterface(); |
261 | |
262 | /// |
263 | /// Type parameters |
264 | /// |
265 | |
266 | @Override |
267 | default Optional<ResolvedTypeParameterDeclaration> findTypeParameter(String name) { |
268 | for (ResolvedTypeParameterDeclaration tp : this.getTypeParameters()) { |
269 | if (tp.getName().equals(name)) { |
270 | return Optional.of(tp); |
271 | } |
272 | } |
273 | if (this.containerType().isPresent()) { |
274 | return this.containerType().get().findTypeParameter(name); |
275 | } |
276 | return Optional.empty(); |
277 | } |
278 | |
279 | List<ResolvedConstructorDeclaration> getConstructors(); |
280 | |
281 | |
282 | /** |
283 | * We don't make this _ex_plicit in the data representation because that would affect codegen |
284 | * and make everything generate like {@code <T extends Object>} instead of {@code <T>} |
285 | * |
286 | * @return true, if this represents {@code java.lang.Object} |
287 | * @see ResolvedReferenceType#isJavaLangObject() |
288 | * @see <a href="https://github.com/javaparser/javaparser/issues/2044">https://github.com/javaparser/javaparser/issues/2044</a> |
289 | */ |
290 | default boolean isJavaLangObject() { |
291 | return this.isClass() |
292 | && !isAnonymousClass() |
293 | && hasName() // Consider anonymous classes |
294 | && getQualifiedName().equals(java.lang.Object.class.getCanonicalName()); |
295 | } |
296 | |
297 | /** |
298 | * @return true, if this represents {@code java.lang.Enum} |
299 | * @see ResolvedReferenceType#isJavaLangEnum() |
300 | */ |
301 | default boolean isJavaLangEnum() { |
302 | return this.isEnum() |
303 | && getQualifiedName().equals(java.lang.Enum.class.getCanonicalName()); |
304 | } |
305 | |
306 | } |
307 |
Members