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.javaparsermodel.declarations; 18 19 import com.github.javaparser.ast.AccessSpecifier; 20 import com.github.javaparser.ast.Node; 21 import com.github.javaparser.ast.body.*; 22 import com.github.javaparser.ast.expr.AnnotationExpr; 23 import com.github.javaparser.ast.type.ClassOrInterfaceType; 24 import com.github.javaparser.resolution.UnsolvedSymbolException; 25 import com.github.javaparser.resolution.declarations.*; 26 import com.github.javaparser.resolution.types.ResolvedReferenceType; 27 import com.github.javaparser.resolution.types.ResolvedType; 28 import com.github.javaparser.symbolsolver.core.resolution.Context; 29 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; 30 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory; 31 import com.github.javaparser.symbolsolver.logic.AbstractClassDeclaration; 32 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; 33 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 34 import com.github.javaparser.symbolsolver.model.typesystem.LazyType; 35 import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl; 36 import com.github.javaparser.symbolsolver.resolution.SymbolSolver; 37 import com.google.common.collect.ImmutableList; 38 39 import java.util.*; 40 import java.util.stream.Collectors; 41 42 /** 43 * @author Federico Tomassetti 44 */ 45 public class JavaParserClassDeclaration extends AbstractClassDeclaration { 46 47 /// 48 /// Fields 49 /// 50 51 private TypeSolver typeSolver; 52 private com.github.javaparser.ast.body.ClassOrInterfaceDeclaration wrappedNode; 53 private JavaParserTypeAdapter<ClassOrInterfaceDeclaration> javaParserTypeAdapter; 54 55 /// 56 /// Constructors 57 /// 58 59 public JavaParserClassDeclaration(com.github.javaparser.ast.body.ClassOrInterfaceDeclaration wrappedNode, 60 TypeSolver typeSolver) { 61 if (wrappedNode.isInterface()) { 62 throw new IllegalArgumentException("Interface given"); 63 } 64 this.wrappedNode = wrappedNode; 65 this.typeSolver = typeSolver; 66 this.javaParserTypeAdapter = new JavaParserTypeAdapter<>(wrappedNode, typeSolver); 67 } 68 69 /// 70 /// Public methods: from Object 71 /// 72 73 @Override 74 public boolean equals(Object o) { 75 if (this == o) return true; 76 if (o == null || getClass() != o.getClass()) return false; 77 78 JavaParserClassDeclaration that = (JavaParserClassDeclaration) o; 79 80 if (!wrappedNode.equals(that.wrappedNode)) return false; 81 82 return true; 83 } 84 85 @Override 86 public int hashCode() { 87 return wrappedNode.hashCode(); 88 } 89 90 @Override 91 public String toString() { 92 return "JavaParserClassDeclaration{" + 93 "wrappedNode=" + wrappedNode + 94 '}'; 95 } 96 97 /// 98 /// Public methods: fields 99 /// 100 101 @Override 102 public List<ResolvedFieldDeclaration> getAllFields() { 103 List<ResolvedFieldDeclaration> fields = javaParserTypeAdapter.getFieldsForDeclaredVariables(); 104 105 getAncestors().forEach(ancestor -> ancestor.getTypeDeclaration().getAllFields().forEach(f -> { 106 fields.add(new ResolvedFieldDeclaration() { 107 108 @Override 109 public AccessSpecifier accessSpecifier() { 110 return f.accessSpecifier(); 111 } 112 113 @Override 114 public String getName() { 115 return f.getName(); 116 } 117 118 @Override 119 public ResolvedType getType() { 120 return ancestor.useThisTypeParametersOnTheGivenType(f.getType()); 121 } 122 123 @Override 124 public boolean isStatic() { 125 return f.isStatic(); 126 } 127 128 @Override 129 public ResolvedTypeDeclaration declaringType() { 130 return f.declaringType(); 131 } 132 }); 133 })); 134 135 return fields; 136 } 137 138 /// 139 /// Public methods 140 /// 141 142 public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes) { 143 Context ctx = getContext(); 144 return ctx.solveMethod(name, parameterTypes, false, typeSolver); 145 } 146 147 @Deprecated 148 public Context getContext() { 149 return JavaParserFactory.getContext(wrappedNode, typeSolver); 150 } 151 152 public ResolvedType getUsage(Node node) { 153 throw new UnsupportedOperationException(); 154 } 155 156 @Override 157 public String getName() { 158 return wrappedNode.getName().getId(); 159 } 160 161 @Override 162 public ResolvedReferenceType getSuperClass() { 163 if (wrappedNode.getExtendedTypes().isEmpty()) { 164 return object(); 165 } else { 166 return toReferenceType(wrappedNode.getExtendedTypes().get(0)); 167 } 168 } 169 170 @Override 171 public List<ResolvedReferenceType> getInterfaces() { 172 List<ResolvedReferenceType> interfaces = new ArrayList<>(); 173 if (wrappedNode.getImplementedTypes() != null) { 174 for (ClassOrInterfaceType t : wrappedNode.getImplementedTypes()) { 175 interfaces.add(toReferenceType(t)); 176 } 177 } 178 return interfaces; 179 } 180 181 @Override 182 public List<ResolvedConstructorDeclaration> getConstructors() { 183 List<ResolvedConstructorDeclaration> declared = new LinkedList<>(); 184 for (BodyDeclaration<?> member : wrappedNode.getMembers()) { 185 if (member instanceof com.github.javaparser.ast.body.ConstructorDeclaration) { 186 com.github.javaparser.ast.body.ConstructorDeclaration constructorDeclaration = (com.github.javaparser.ast.body.ConstructorDeclaration) member; 187 declared.add(new JavaParserConstructorDeclaration(this, constructorDeclaration, typeSolver)); 188 } 189 } 190 if (declared.isEmpty()) { 191 // If there are no constructors insert the default constructor 192 return ImmutableList.of(new DefaultConstructorDeclaration(this)); 193 } else { 194 return declared; 195 } 196 } 197 198 @Override 199 public boolean hasDirectlyAnnotation(String canonicalName) { 200 for (AnnotationExpr annotationExpr : wrappedNode.getAnnotations()) { 201 if (solveType(annotationExpr.getName().getId(), typeSolver).getCorrespondingDeclaration().getQualifiedName().equals(canonicalName)) { 202 return true; 203 } 204 } 205 return false; 206 } 207 208 @Override 209 public boolean isInterface() { 210 return wrappedNode.isInterface(); 211 } 212 213 @Override 214 public String getPackageName() { 215 return javaParserTypeAdapter.getPackageName(); 216 } 217 218 @Override 219 public String getClassName() { 220 return javaParserTypeAdapter.getClassName(); 221 } 222 223 @Override 224 public String getQualifiedName() { 225 return javaParserTypeAdapter.getQualifiedName(); 226 } 227 228 @Override 229 public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) { 230 return javaParserTypeAdapter.isAssignableBy(other); 231 } 232 233 @Override 234 public boolean isAssignableBy(ResolvedType type) { 235 return javaParserTypeAdapter.isAssignableBy(type); 236 } 237 238 @Override 239 public boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) { 240 // TODO consider generic types 241 if (this.getQualifiedName().equals(other.getQualifiedName())) { 242 return true; 243 } 244 ResolvedClassDeclaration superclass = (ResolvedClassDeclaration) getSuperClass().getTypeDeclaration(); 245 if (superclass != null) { 246 // We want to avoid infinite recursion in case of Object having Object as ancestor 247 if (Object.class.getCanonicalName().equals(superclass.getQualifiedName())) { 248 return true; 249 } 250 if (superclass.canBeAssignedTo(other)) { 251 return true; 252 } 253 } 254 255 if (this.wrappedNode.getImplementedTypes() != null) { 256 for (ClassOrInterfaceType type : wrappedNode.getImplementedTypes()) { 257 ResolvedReferenceTypeDeclaration ancestor = (ResolvedReferenceTypeDeclaration) new SymbolSolver(typeSolver).solveType(type); 258 if (ancestor.canBeAssignedTo(other)) { 259 return true; 260 } 261 } 262 } 263 264 return false; 265 } 266 267 @Override 268 public boolean isTypeParameter() { 269 return false; 270 } 271 272 @Deprecated 273 public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) { 274 if (this.wrappedNode.getName().getId().equals(name)) { 275 return SymbolReference.solved(this); 276 } 277 SymbolReference<ResolvedTypeDeclaration> ref = javaParserTypeAdapter.solveType(name, typeSolver); 278 if (ref.isSolved()) { 279 return ref; 280 } 281 282 String prefix = wrappedNode.getName() + "."; 283 if (name.startsWith(prefix) && name.length() > prefix.length()) { 284 return new JavaParserClassDeclaration(this.wrappedNode, typeSolver).solveType(name.substring(prefix.length()), typeSolver); 285 } 286 287 return getContext().getParent().solveType(name, typeSolver); 288 } 289 290 @Override 291 public List<ResolvedReferenceType> getAncestors() { 292 List<ResolvedReferenceType> ancestors = new ArrayList<>(); 293 294 // We want to avoid infinite recursion in case of Object having Object as ancestor 295 if (!(Object.class.getCanonicalName().equals(getQualifiedName()))) { 296 ResolvedReferenceType superclass = getSuperClass(); 297 if (superclass != null) { 298 ancestors.add(superclass); 299 } 300 if (wrappedNode.getImplementedTypes() != null) { 301 for (ClassOrInterfaceType implemented : wrappedNode.getImplementedTypes()) { 302 ResolvedReferenceType ancestor = toReferenceType(implemented); 303 ancestors.add(ancestor); 304 } 305 } 306 } 307 308 return ancestors; 309 } 310 311 @Override 312 public Set<ResolvedMethodDeclaration> getDeclaredMethods() { 313 Set<ResolvedMethodDeclaration> methods = new HashSet<>(); 314 for (BodyDeclaration<?> member : wrappedNode.getMembers()) { 315 if (member instanceof com.github.javaparser.ast.body.MethodDeclaration) { 316 methods.add(new JavaParserMethodDeclaration((com.github.javaparser.ast.body.MethodDeclaration) member, typeSolver)); 317 } 318 } 319 return methods; 320 } 321 322 @Override 323 public List<ResolvedTypeParameterDeclaration> getTypeParameters() { 324 return this.wrappedNode.getTypeParameters().stream().map( 325 (tp) -> new JavaParserTypeParameter(tp, typeSolver) 326 ).collect(Collectors.toList()); 327 } 328 329 /** 330 * Returns the JavaParser node associated with this JavaParserClassDeclaration. 331 * 332 * @return A visitable JavaParser node wrapped by this object. 333 */ 334 public com.github.javaparser.ast.body.ClassOrInterfaceDeclaration getWrappedNode() { 335 return wrappedNode; 336 } 337 338 @Override 339 public AccessSpecifier accessSpecifier() { 340 return Helper.toAccessLevel(wrappedNode.getModifiers()); 341 } 342 343 /// 344 /// Protected methods 345 /// 346 347 @Override 348 protected ResolvedReferenceType object() { 349 return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver); 350 } 351 352 @Override 353 public Set<ResolvedReferenceTypeDeclaration> internalTypes() { 354 Set<ResolvedReferenceTypeDeclaration> res = new HashSet<>(); 355 for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) { 356 if (member instanceof com.github.javaparser.ast.body.TypeDeclaration) { 357 res.add(JavaParserFacade.get(typeSolver).getTypeDeclaration((com.github.javaparser.ast.body.TypeDeclaration)member)); 358 } 359 } 360 return res; 361 } 362 363 @Override 364 public Optional<ResolvedReferenceTypeDeclaration> containerType() { 365 return javaParserTypeAdapter.containerType(); 366 } 367 368 /// 369 /// Private methods 370 /// 371 372 private ResolvedReferenceType toReferenceType(ClassOrInterfaceType classOrInterfaceType) { 373 String className = classOrInterfaceType.getName().getId(); 374 if (classOrInterfaceType.getScope().isPresent()) { 375 // look for the qualified name (for example class of type Rectangle2D.Double) 376 className = classOrInterfaceType.getScope().get().toString() + "." + className; 377 } 378 SymbolReference<ResolvedTypeDeclaration> ref = solveType(className, typeSolver); 379 if (!ref.isSolved()) { 380 Optional<ClassOrInterfaceType> localScope = classOrInterfaceType.getScope(); 381 if (localScope.isPresent()) { 382 String localName = localScope.get().getName().getId() + "." + classOrInterfaceType.getName().getId(); 383 ref = solveType(localName, typeSolver); 384 } 385 } 386 if (!ref.isSolved()) { 387 throw new UnsolvedSymbolException(classOrInterfaceType.getName().getId()); 388 } 389 if (!classOrInterfaceType.getTypeArguments().isPresent()) { 390 return new ReferenceTypeImpl(ref.getCorrespondingDeclaration().asReferenceType(), typeSolver); 391 } 392 List<ResolvedType> superClassTypeParameters = classOrInterfaceType.getTypeArguments().get() 393 .stream().map(ta -> new LazyType(v -> JavaParserFacade.get(typeSolver).convert(ta, ta))) 394 .collect(Collectors.toList()); 395 return new ReferenceTypeImpl(ref.getCorrespondingDeclaration().asReferenceType(), superClassTypeParameters, typeSolver); 396 } 397 } 398