Skip to content

Commit 798b30d

Browse files
authored
fix: support uppercase attribute names introspection (#173)
* fix: support uppercase attribute names introspection * fix: make getFieldName private * fix: polish * Add duplicated property test case (#178) * fix: add test case for private getter (#175) * fix: add test case for private getter * fix: polish test * fix: refactor IntrospectionUtils * fix: remove redundant code * fix: polish IntrospectionUtils * polish IntrospectionUtils * fix: use JPA's ManagedType for entity introspection * fix: cannot find symbol compile error * fix: cannot find symbol symbol: method isEmpty() * fix: add protected getter test case * fix: add ignored attribute property descriptor tests * fix: polish IntrospectionUtils * fix: add protectedGetter to CalculatedEntityTests * fix: refactor ObjectValue conversion * fix: refactor persistent attribute detection * fix: remove redundant code persistent detection logic * fix: add introspection support for protected getter methods * feat: refactor class introspection utils * Add custom metamodel test case * fix: refactor IntrospectionUtils to EntityIntrospector * fix: add ArrayUtil test coverage * fix: add ReflectionUtil tests * fix: add ClassIntrospector tests * fix: add ClassUtil tests * fix: filter static methods * fix: failing test methods * fix: add ObjectUtil tests * feat: add scanStatics option to ClassIntrospector * fix: add test coverage for annotation descriptors * fix: add constructor and property descriptor tests * fix: polish ClassIntrospectorTest format * fix: add PropertyDescriptor tests * fix: add PropertyDescriptor equals and hashCode * fix: add ConstructorDescriptor tests * fix: add PropertyDescriptor equals and hashCode tests * fix: add AnnotationDescriptor tests * fix: add FieldDescriptor tests * fix: add MethodDescriptor tests * fix: add BeanUtil test coverage * fix: add missing PropertyDescriptorTest coverage * fix: PropertyDecriptor NPE check * fix: add basic introspection tests
1 parent 6552cb8 commit 798b30d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+5860
-564
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.introproventures.graphql.jpa.query.introspection;
2+
3+
4+
import java.lang.annotation.Annotation;
5+
import java.lang.annotation.Documented;
6+
import java.lang.annotation.ElementType;
7+
import java.lang.annotation.Inherited;
8+
import java.lang.annotation.Retention;
9+
import java.lang.annotation.RetentionPolicy;
10+
import java.lang.annotation.Target;
11+
import java.util.Arrays;
12+
import java.util.Objects;
13+
14+
public class AnnotationDescriptor {
15+
16+
private final Annotation annotation;
17+
18+
private final Class<? extends Annotation> annotationType;
19+
20+
private final ElementType[] elementTypes;
21+
22+
private final RetentionPolicy policy;
23+
24+
private final boolean isDocumented;
25+
26+
private final boolean isInherited;
27+
28+
public <A extends Annotation> AnnotationDescriptor(A annotation) {
29+
this.annotation = annotation;
30+
annotationType = annotation.annotationType();
31+
32+
Target target = annotationType.getAnnotation(Target.class);
33+
elementTypes = (target == null) ? ElementType.values() : target.value();
34+
35+
Retention retention = annotationType.getAnnotation(Retention.class);
36+
policy = (retention == null) ? RetentionPolicy.CLASS : retention.value();
37+
38+
Documented documented = annotationType.getAnnotation(Documented.class);
39+
isDocumented = (documented != null);
40+
41+
Inherited inherited = annotationType.getAnnotation(Inherited.class);
42+
isInherited = (inherited != null);
43+
}
44+
45+
@SuppressWarnings("unchecked")
46+
public <A extends Annotation> A getAnnotation() {
47+
return (A) annotation;
48+
}
49+
50+
public Class<? extends Annotation> getAnnotationType() {
51+
return annotationType;
52+
}
53+
54+
public ElementType[] getElementTypes() {
55+
return elementTypes;
56+
}
57+
58+
public RetentionPolicy getPolicy() {
59+
return policy;
60+
}
61+
62+
public boolean isDocumented() {
63+
return isDocumented;
64+
}
65+
66+
public boolean isInherited() {
67+
return isInherited;
68+
}
69+
70+
@Override
71+
public String toString() {
72+
StringBuilder builder = new StringBuilder();
73+
builder.append("AnnotationDescriptor [annotation=")
74+
.append(annotation)
75+
.append(", annotationType=")
76+
.append(annotationType)
77+
.append(", elementTypes=")
78+
.append(Arrays.toString(elementTypes))
79+
.append(", policy=")
80+
.append(policy)
81+
.append(", isDocumented=")
82+
.append(isDocumented)
83+
.append(", isInherited=")
84+
.append(isInherited)
85+
.append("]");
86+
return builder.toString();
87+
}
88+
89+
@Override
90+
public int hashCode() {
91+
final int prime = 31;
92+
int result = 1;
93+
result = prime * result + Arrays.hashCode(elementTypes);
94+
result = prime * result + Objects.hash(annotation, annotationType, isDocumented, isInherited, policy);
95+
return result;
96+
}
97+
98+
@Override
99+
public boolean equals(Object obj) {
100+
if (this == obj)
101+
return true;
102+
if (obj == null)
103+
return false;
104+
if (getClass() != obj.getClass())
105+
return false;
106+
AnnotationDescriptor other = (AnnotationDescriptor) obj;
107+
return Objects.equals(annotation, other.annotation)
108+
&& Objects.equals(annotationType, other.annotationType)
109+
&& Arrays.equals(elementTypes, other.elementTypes)
110+
&& isDocumented == other.isDocumented
111+
&& isInherited == other.isInherited
112+
&& policy == other.policy;
113+
}
114+
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.introproventures.graphql.jpa.query.introspection;
2+
3+
import java.lang.annotation.Annotation;
4+
import java.lang.reflect.AnnotatedElement;
5+
import java.util.Arrays;
6+
import java.util.Comparator;
7+
import java.util.LinkedHashMap;
8+
import java.util.Map;
9+
import java.util.Objects;
10+
11+
public class Annotations {
12+
13+
protected final AnnotatedElement annotatedElement;
14+
15+
protected final Map<Class<? extends Annotation>, AnnotationDescriptor> annotationsMap;
16+
17+
// cache
18+
private AnnotationDescriptor[] allAnnotations;
19+
20+
public Annotations(AnnotatedElement annotatedElement) {
21+
this.annotatedElement = annotatedElement;
22+
this.annotationsMap = inspectAnnotations();
23+
}
24+
25+
private Map<Class<? extends Annotation>, AnnotationDescriptor> inspectAnnotations() {
26+
27+
Annotation[] annotations = ReflectionUtil.getAnnotation(annotatedElement);
28+
if (ArrayUtil.isEmpty(annotations)) {
29+
return null;
30+
}
31+
32+
Map<Class<? extends Annotation>, AnnotationDescriptor> map = new LinkedHashMap<>(annotations.length);
33+
34+
for (Annotation annotation : annotations) {
35+
map.put(annotation.annotationType(), new AnnotationDescriptor(annotation));
36+
}
37+
38+
return map;
39+
}
40+
41+
public AnnotationDescriptor getAnnotationDescriptor(Class<? extends Annotation> clazz) {
42+
if (annotationsMap == null) {
43+
return null;
44+
}
45+
46+
return annotationsMap.get(clazz);
47+
}
48+
49+
public AnnotationDescriptor[] getAllAnnotationDescriptors() {
50+
if (annotationsMap == null) {
51+
return null;
52+
}
53+
54+
if (allAnnotations == null) {
55+
AnnotationDescriptor[] allAnnotations = new AnnotationDescriptor[annotationsMap.size()];
56+
57+
int index = 0;
58+
for (AnnotationDescriptor annotationDescriptor : annotationsMap.values()) {
59+
allAnnotations[index] = annotationDescriptor;
60+
index++;
61+
}
62+
63+
Arrays.sort(allAnnotations, new Comparator<AnnotationDescriptor>() {
64+
@Override
65+
public int compare(AnnotationDescriptor ad1, AnnotationDescriptor ad2) {
66+
return ad1.getClass().getName().compareTo(ad2.getClass().getName());
67+
}
68+
});
69+
70+
this.allAnnotations = allAnnotations;
71+
}
72+
73+
return allAnnotations;
74+
}
75+
76+
@Override
77+
public String toString() {
78+
StringBuilder builder = new StringBuilder();
79+
builder.append("Annotations [annotatedElement=").append(annotatedElement).append("]");
80+
return builder.toString();
81+
}
82+
83+
@Override
84+
public int hashCode() {
85+
return Objects.hash(annotatedElement);
86+
}
87+
88+
@Override
89+
public boolean equals(Object obj) {
90+
if (this == obj)
91+
return true;
92+
if (obj == null)
93+
return false;
94+
if (getClass() != obj.getClass())
95+
return false;
96+
Annotations other = (Annotations) obj;
97+
return Objects.equals(annotatedElement, other.annotatedElement);
98+
}
99+
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.introproventures.graphql.jpa.query.introspection;
2+
3+
import java.lang.reflect.Array;
4+
5+
public class ArrayUtil {
6+
7+
private static final int INDEX_NOT_FOUND = -1;
8+
9+
public static boolean isEmpty(Object array) {
10+
if(array == null) {
11+
return true;
12+
}
13+
14+
// not an array
15+
if(!array.getClass().isArray()) {
16+
return false;
17+
}
18+
19+
// check array length
20+
return Array.getLength(array) == 0;
21+
}
22+
23+
public static boolean isNotEmpty(Object array) {
24+
return !isEmpty(array);
25+
}
26+
27+
public static int indexOf(Object[] array, Object objectToFind) {
28+
return indexOf(array, objectToFind, 0);
29+
}
30+
31+
public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
32+
if (array == null) {
33+
return INDEX_NOT_FOUND;
34+
}
35+
36+
if (startIndex < 0) {
37+
startIndex = 0;
38+
}
39+
40+
if (objectToFind == null) {
41+
for (int i = startIndex; i < array.length; i++) {
42+
if (array[i] == null) {
43+
return i;
44+
}
45+
}
46+
} else {
47+
for (int i = startIndex; i < array.length; i++) {
48+
if (objectToFind.equals(array[i])) {
49+
return i;
50+
}
51+
}
52+
}
53+
54+
return INDEX_NOT_FOUND;
55+
}
56+
57+
public static <T> T[] addAll(T[] array1, T[] array2) {
58+
if (array1 == null) {
59+
return (T[]) clone(array2);
60+
} else if (array2 == null) {
61+
return (T[]) clone(array1);
62+
}
63+
@SuppressWarnings("unchecked")
64+
T[] joinedArray = (T[]) Array.newInstance(array1.getClass().getComponentType(), array1.length + array2.length);
65+
System.arraycopy(array1, 0, joinedArray, 0, array1.length);
66+
try {
67+
System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
68+
} catch (ArrayStoreException ase) {
69+
// Check if problem was due to incompatible types
70+
/*
71+
* We do this here, rather than before the copy because: - it would be a wasted check most of the time -
72+
* safer, in case check turns out to be too strict
73+
*/
74+
final Class<?> type1 = array1.getClass().getComponentType();
75+
final Class<?> type2 = array2.getClass().getComponentType();
76+
if (!type1.isAssignableFrom(type2)) {
77+
throw new IllegalArgumentException("Cannot store " + type2.getName() + " in an array of "
78+
+ type1.getName());
79+
}
80+
throw ase; // No, so rethrow original
81+
}
82+
return joinedArray;
83+
}
84+
85+
public static <T> T[] clone(T[] array) {
86+
if (array == null) {
87+
return null;
88+
}
89+
90+
return (T[]) array.clone();
91+
}
92+
93+
}

0 commit comments

Comments
 (0)