Home | History | Annotate | Download | only in declarations
      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