1 package com.github.javaparser.symbolsolver.model.declarations; 2 3 import com.github.javaparser.symbolsolver.model.methods.MethodUsage; 4 import com.github.javaparser.symbolsolver.model.resolution.UnsolvedSymbolException; 5 import com.github.javaparser.symbolsolver.model.typesystem.ReferenceType; 6 import com.github.javaparser.symbolsolver.model.typesystem.Type; 7 8 import java.util.ArrayList; 9 import java.util.List; 10 import java.util.Optional; 11 import java.util.Set; 12 import java.util.stream.Collectors; 13 14 /** 15 * @author Federico Tomassetti 16 */ 17 public interface ReferenceTypeDeclaration extends TypeDeclaration, TypeParametrizable { 18 19 @Override asReferenceType()20 default ReferenceTypeDeclaration asReferenceType() { 21 return this; 22 } 23 24 /// 25 /// Ancestors 26 /// 27 28 /** 29 * The list of all the direct ancestors of the current declaration. 30 * Note that the ancestor can be parametrized types with values specified. For example: 31 * <p> 32 * class A implements Comparable<String> {} 33 * <p> 34 * In this case the ancestor is Comparable<String> 35 */ getAncestors()36 List<ReferenceType> getAncestors(); 37 38 /** 39 * The list of all the ancestors of the current declaration, direct and indirect. 40 * This list does not contains duplicates with the exacting same type parameters. 41 */ getAllAncestors()42 default List<ReferenceType> getAllAncestors() { 43 List<ReferenceType> ancestors = new ArrayList<>(); 44 // We want to avoid infinite recursion in case of Object having Object as ancestor 45 if (!(Object.class.getCanonicalName().equals(getQualifiedName()))) { 46 for (ReferenceType ancestor : getAncestors()) { 47 ancestors.add(ancestor); 48 for (ReferenceType inheritedAncestor : ancestor.getAllAncestors()) { 49 if (!ancestors.contains(inheritedAncestor)) { 50 ancestors.add(inheritedAncestor); 51 } 52 } 53 } 54 } 55 return ancestors; 56 } 57 58 /// 59 /// Fields 60 /// 61 62 /** 63 * Note that the type of the field should be expressed using the type variables of this particular type. 64 * Consider for example: 65 * <p> 66 * class Foo<E> { E field; } 67 * <p> 68 * class Bar extends Foo<String> { } 69 * <p> 70 * When calling getField("field") on Foo I should get a FieldDeclaration with type E, while calling it on 71 * Bar I should get a FieldDeclaration with type String. 72 */ getField(String name)73 default FieldDeclaration getField(String name) { 74 Optional<FieldDeclaration> field = this.getAllFields().stream().filter(f -> f.getName().equals(name)).findFirst(); 75 if (field.isPresent()) { 76 return field.get(); 77 } else { 78 throw new UnsolvedSymbolException("Field not found: " + name); 79 } 80 } 81 82 /** 83 * Consider only field or inherited field which is not private. 84 */ getVisibleField(String name)85 default FieldDeclaration getVisibleField(String name) { 86 Optional<FieldDeclaration> field = getVisibleFields().stream().filter(f -> f.getName().equals(name)).findFirst(); 87 if (field.isPresent()) { 88 return field.get(); 89 } else { 90 throw new IllegalArgumentException(); 91 } 92 } 93 94 /** 95 * Has this type a field with the given name? 96 */ hasField(String name)97 default boolean hasField(String name) { 98 return this.getAllFields().stream().filter(f -> f.getName().equals(name)).findFirst().isPresent(); 99 } 100 101 /** 102 * Either a declared field or inherited field which is not private. 103 */ hasVisibleField(String name)104 default boolean hasVisibleField(String name) { 105 return getVisibleFields().stream().filter(f -> f.getName().equals(name)).findFirst().isPresent(); 106 } 107 108 /** 109 * Return a list of all fields, either declared in this declaration or inherited. 110 * 111 * Note that they could refer to inherited type variables. 112 */ getAllFields()113 List<FieldDeclaration> getAllFields(); 114 115 /** 116 * Return a list of all fields declared and the inherited ones which are not private. 117 */ getVisibleFields()118 default List<FieldDeclaration> getVisibleFields() { 119 return getAllFields().stream() 120 .filter(f -> f.declaringType().equals(this) || f.accessLevel() != AccessLevel.PRIVATE) 121 .collect(Collectors.toList()); 122 } 123 124 /** 125 * Return a list of all the non static fields, either declared or inherited. 126 */ getAllNonStaticFields()127 default List<FieldDeclaration> getAllNonStaticFields() { 128 return getAllFields().stream().filter(it -> !it.isStatic()).collect(Collectors.toList()); 129 } 130 131 /** 132 * Return a list of all the static fields, either declared or inherited. 133 */ getAllStaticFields()134 default List<FieldDeclaration> getAllStaticFields() { 135 return getAllFields().stream().filter(it -> it.isStatic()).collect(Collectors.toList()); 136 } 137 138 /** 139 * Return a list of all the fields declared in this type. 140 */ getDeclaredFields()141 default List<FieldDeclaration> getDeclaredFields() { 142 return getAllFields().stream().filter(it -> it.declaringType().getQualifiedName().equals(getQualifiedName())).collect(Collectors.toList()); 143 } 144 145 /// 146 /// Methods 147 /// 148 149 /** 150 * Return a list of all the methods declared in this type declaration. 151 */ getDeclaredMethods()152 Set<MethodDeclaration> getDeclaredMethods(); 153 154 /** 155 * Return a list of all the methods declared of this type declaration, either declared or inherited. 156 * Note that it should not include overridden methods. 157 */ getAllMethods()158 Set<MethodUsage> getAllMethods(); 159 160 /// 161 /// Assignability 162 /// 163 164 /** 165 * Can we assign instances of the given type to variables having the type defined 166 * by this declaration? 167 */ isAssignableBy(Type type)168 boolean isAssignableBy(Type type); 169 170 /** 171 * Can we assign instances of the type defined by this declaration to variables having the type defined 172 * by the given type? 173 */ canBeAssignedTo(ReferenceTypeDeclaration other)174 default boolean canBeAssignedTo(ReferenceTypeDeclaration other) { 175 return other.isAssignableBy(this); 176 } 177 178 /** 179 * Can we assign instances of the given type to variables having the type defined 180 * by this declaration? 181 */ isAssignableBy(ReferenceTypeDeclaration other)182 boolean isAssignableBy(ReferenceTypeDeclaration other); 183 184 /// 185 /// Annotations 186 /// 187 188 /** 189 * Has the type at least one annotation declared having the specified qualified name? 190 */ hasDirectlyAnnotation(String qualifiedName)191 boolean hasDirectlyAnnotation(String qualifiedName); 192 193 /** 194 * Has the type at least one annotation declared or inherited having the specified qualified name? 195 */ hasAnnotation(String qualifiedName)196 default boolean hasAnnotation(String qualifiedName) { 197 if (hasDirectlyAnnotation(qualifiedName)) { 198 return true; 199 } 200 return getAllAncestors().stream().anyMatch(it -> it.asReferenceType().getTypeDeclaration().hasDirectlyAnnotation(qualifiedName)); 201 } 202 203 /** 204 * This means that the type has a functional method. Conceptually, a functional interface has exactly one abstract method. 205 * Typically these classes has the FunctionInterface annotation but this is not mandatory. 206 */ isFunctionalInterface()207 boolean isFunctionalInterface(); 208 209 /// 210 /// Type parameters 211 /// 212 213 @Override findTypeParameter(String name)214 default Optional<TypeParameterDeclaration> findTypeParameter(String name) { 215 for (TypeParameterDeclaration tp : this.getTypeParameters()) { 216 if (tp.getName().equals(name)) { 217 return Optional.of(tp); 218 } 219 } 220 if (this.containerType().isPresent()) { 221 return this.containerType().get().findTypeParameter(name); 222 } 223 return Optional.empty(); 224 } 225 } 226