1package com.github.javaparser.symbolsolver.reflectionmodel;
2
3import com.github.javaparser.resolution.UnsolvedSymbolException;
4import com.github.javaparser.resolution.declarations.*;
5import com.github.javaparser.resolution.types.ResolvedReferenceType;
6import com.github.javaparser.resolution.types.ResolvedType;
7import com.github.javaparser.symbolsolver.javaparsermodel.LambdaArgumentTypePlaceholder;
8import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
9import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
10import com.github.javaparser.symbolsolver.model.typesystem.NullType;
11import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
12
13import java.lang.annotation.Annotation;
14import java.lang.reflect.Field;
15import java.lang.reflect.ParameterizedType;
16import java.lang.reflect.TypeVariable;
17import java.util.*;
18import java.util.stream.Collectors;
19
20/**
21 * @author Federico Tomassetti
22 */
23class ReflectionClassAdapter {
24
25    private Class<?> clazz;
26    private TypeSolver typeSolver;
27    private ResolvedReferenceTypeDeclaration typeDeclaration;
28
29    public ReflectionClassAdapter(Class<?> clazz, TypeSolver typeSolver, ResolvedReferenceTypeDeclaration typeDeclaration) {
30        this.clazz = clazz;
31        this.typeSolver = typeSolver;
32        this.typeDeclaration = typeDeclaration;
33    }
34
35    public ReferenceTypeImpl getSuperClass() {
36        if (clazz.getGenericSuperclass() == null) {
37            return null;
38        }
39        java.lang.reflect.Type superType = clazz.getGenericSuperclass();
40        if (superType instanceof ParameterizedType) {
41            ParameterizedType parameterizedType = (ParameterizedType) superType;
42            List<ResolvedType> typeParameters = Arrays.stream(parameterizedType.getActualTypeArguments())
43                    .map((t) -> ReflectionFactory.typeUsageFor(t, typeSolver))
44                    .collect(Collectors.toList());
45            return new ReferenceTypeImpl(new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver), typeParameters, typeSolver);
46        }
47        return new ReferenceTypeImpl(new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver), typeSolver);
48    }
49
50    public List<ResolvedReferenceType> getInterfaces() {
51        List<ResolvedReferenceType> interfaces = new ArrayList<>();
52        for (java.lang.reflect.Type superInterface : clazz.getGenericInterfaces()) {
53            if (superInterface instanceof ParameterizedType) {
54                ParameterizedType parameterizedType = (ParameterizedType) superInterface;
55                List<ResolvedType> typeParameters = Arrays.stream(parameterizedType.getActualTypeArguments())
56                        .map((t) -> ReflectionFactory.typeUsageFor(t, typeSolver))
57                        .collect(Collectors.toList());
58                interfaces.add(new ReferenceTypeImpl(new ReflectionInterfaceDeclaration((Class<?>) ((ParameterizedType) superInterface).getRawType(), typeSolver), typeParameters, typeSolver));
59            } else {
60                interfaces.add(new ReferenceTypeImpl(new ReflectionInterfaceDeclaration((Class<?>) superInterface, typeSolver), typeSolver));
61            }
62        }
63        return interfaces;
64    }
65
66    public List<ResolvedReferenceType> getAncestors() {
67        List<ResolvedReferenceType> ancestors = new LinkedList<>();
68        if (getSuperClass() != null) {
69            ReferenceTypeImpl superClass = getSuperClass();
70            ancestors.add(superClass);
71        } else {
72            ReferenceTypeImpl object = new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, typeSolver), typeSolver);
73            ancestors.add(object);
74        }
75        ancestors.addAll(getInterfaces());
76        for (int i = 0; i < ancestors.size(); i++) {
77            ResolvedReferenceType ancestor = ancestors.get(i);
78            if (ancestor.hasName() && ancestor.getQualifiedName().equals(Object.class.getCanonicalName())) {
79                ancestors.remove(i);
80                i--;
81            }
82        }
83        return ancestors;
84    }
85
86    public ResolvedFieldDeclaration getField(String name) {
87        for (Field field : clazz.getDeclaredFields()) {
88            if (field.getName().equals(name)) {
89                return new ReflectionFieldDeclaration(field, typeSolver);
90            }
91        }
92        for (ResolvedReferenceType ancestor : typeDeclaration.getAllAncestors()) {
93            if (ancestor.getTypeDeclaration().hasField(name)) {
94                ReflectionFieldDeclaration reflectionFieldDeclaration = (ReflectionFieldDeclaration) ancestor.getTypeDeclaration().getField(name);
95                return reflectionFieldDeclaration.replaceType(ancestor.getFieldType(name).get());
96            }
97        }
98        throw new UnsolvedSymbolException(name, "Field in " + this);
99    }
100
101    public boolean hasField(String name) {
102        for (Field field : clazz.getDeclaredFields()) {
103            if (field.getName().equals(name)) {
104                return true;
105            }
106        }
107        ReferenceTypeImpl superclass = getSuperClass();
108        if (superclass == null) {
109            return false;
110        } else {
111            return superclass.getTypeDeclaration().hasField(name);
112        }
113    }
114
115    public List<ResolvedFieldDeclaration> getAllFields() {
116        ArrayList<ResolvedFieldDeclaration> fields = new ArrayList<>();
117        for (Field field : clazz.getDeclaredFields()) {
118            fields.add(new ReflectionFieldDeclaration(field, typeSolver));
119        }
120        for (ResolvedReferenceType ancestor : typeDeclaration.getAllAncestors()) {
121            fields.addAll(ancestor.getTypeDeclaration().getAllFields());
122        }
123        return fields;
124    }
125
126    public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
127        return Arrays.stream(clazz.getDeclaredMethods())
128                .filter(m -> !m.isSynthetic() && !m.isBridge())
129                .map(m -> new ReflectionMethodDeclaration(m, typeSolver))
130                .collect(Collectors.toSet());
131    }
132
133    public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
134        List<ResolvedTypeParameterDeclaration> params = new ArrayList<>();
135        for (TypeVariable<?> tv : this.clazz.getTypeParameters()) {
136            params.add(new ReflectionTypeParameter(tv, true, typeSolver));
137        }
138        return params;
139    }
140
141    public boolean isAssignableBy(ResolvedType type) {
142        if (type instanceof NullType) {
143            return true;
144        }
145        if (type instanceof LambdaArgumentTypePlaceholder) {
146            return isFunctionalInterface();
147        }
148        if (type.isArray()) {
149            return false;
150        }
151        if (type.isPrimitive()) {
152            return false;
153        }
154        if (type.describe().equals(typeDeclaration.getQualifiedName())) {
155            return true;
156        }
157        if (type instanceof ReferenceTypeImpl) {
158            ReferenceTypeImpl otherTypeDeclaration = (ReferenceTypeImpl) type;
159            return otherTypeDeclaration.getTypeDeclaration().canBeAssignedTo(typeDeclaration);
160        }
161
162        return false;
163    }
164
165    public boolean hasDirectlyAnnotation(String canonicalName) {
166        for (Annotation a : clazz.getDeclaredAnnotations()) {
167            if (a.annotationType().getCanonicalName().equals(canonicalName)) {
168                return true;
169            }
170        }
171        return false;
172    }
173
174    private final boolean isFunctionalInterface() {
175        return FunctionalInterfaceLogic.getFunctionalMethod(typeDeclaration).isPresent();
176    }
177
178    public List<ResolvedConstructorDeclaration> getConstructors() {
179        return Arrays.stream(clazz.getConstructors())
180                .map(m -> new ReflectionConstructorDeclaration(m, typeSolver))
181                .collect(Collectors.toList());
182    }
183
184    public Optional<ResolvedReferenceTypeDeclaration> containerType() {
185        Class<?> declaringClass = clazz.getDeclaringClass();
186        return declaringClass == null ?
187                Optional.empty() :
188                Optional.of(ReflectionFactory.typeDeclarationFor(declaringClass, typeSolver));
189    }
190}
191