1
1
package com .introproventures .graphql .jpa .query .schema .impl ;
2
2
3
- import com .introproventures .graphql .jpa .query .annotation .GraphQLIgnore ;
4
-
5
3
import java .beans .BeanInfo ;
6
4
import java .beans .IntrospectionException ;
7
5
import java .beans .Introspector ;
8
6
import java .beans .PropertyDescriptor ;
9
7
import java .lang .annotation .Annotation ;
8
+ import java .lang .reflect .Field ;
9
+ import java .lang .reflect .Modifier ;
10
+ import java .util .Arrays ;
10
11
import java .util .Collection ;
12
+ import java .util .Iterator ;
11
13
import java .util .LinkedHashMap ;
12
14
import java .util .Map ;
15
+ import java .util .Objects ;
13
16
import java .util .Optional ;
17
+ import java .util .Spliterator ;
18
+ import java .util .Spliterators ;
19
+ import java .util .function .Function ;
14
20
import java .util .stream .Collectors ;
15
21
import java .util .stream .Stream ;
22
+ import java .util .stream .StreamSupport ;
16
23
17
24
import javax .persistence .Transient ;
18
25
26
+ import com .introproventures .graphql .jpa .query .annotation .GraphQLIgnore ;
27
+
19
28
public class IntrospectionUtils {
20
29
private static final Map <Class <?>, CachedIntrospectionResult > map = new LinkedHashMap <>();
21
30
@@ -24,25 +33,38 @@ public static CachedIntrospectionResult introspect(Class<?> entity) {
24
33
}
25
34
26
35
public static boolean isTransient (Class <?> entity , String propertyName ) {
27
- return isAnnotationPresent (entity , propertyName , Transient .class );
36
+ if (!introspect (entity ).hasPropertyDescriptor (propertyName )) {
37
+ throw new RuntimeException (new NoSuchFieldException (propertyName ));
38
+ }
39
+
40
+ return Stream .of (isAnnotationPresent (entity , propertyName , Transient .class ),
41
+ isModifierPresent (entity , propertyName , Modifier ::isTransient ))
42
+ .anyMatch (it -> it .isPresent () && it .get () == true );
28
43
}
29
-
44
+
30
45
public static boolean isIgnored (Class <?> entity , String propertyName ) {
31
- return isAnnotationPresent (entity , propertyName , GraphQLIgnore .class );
46
+ return isAnnotationPresent (entity , propertyName , GraphQLIgnore .class )
47
+ .orElseThrow (() -> new RuntimeException (new NoSuchFieldException (propertyName )));
32
48
}
33
49
34
- private static boolean isAnnotationPresent (Class <?> entity , String propertyName , Class <? extends Annotation > annotation ){
50
+ private static Optional < Boolean > isAnnotationPresent (Class <?> entity , String propertyName , Class <? extends Annotation > annotation ){
35
51
return introspect (entity ).getPropertyDescriptor (propertyName )
36
- .map (it -> it .isAnnotationPresent (annotation ))
37
- .orElse (false );
52
+ .map (it -> it .isAnnotationPresent (annotation ));
38
53
}
39
54
55
+ private static Optional <Boolean > isModifierPresent (Class <?> entity , String propertyName , Function <Integer , Boolean > function ){
56
+ return introspect (entity ).getField (propertyName )
57
+ .map (it -> function .apply (it .getModifiers ()));
58
+ }
59
+
40
60
public static class CachedIntrospectionResult {
41
61
42
62
private final Map <String , CachedPropertyDescriptor > map ;
43
63
private final Class <?> entity ;
44
64
private final BeanInfo beanInfo ;
45
-
65
+ private final Map <String , Field > fields ;
66
+
67
+ @ SuppressWarnings ("rawtypes" )
46
68
public CachedIntrospectionResult (Class <?> entity ) {
47
69
try {
48
70
this .beanInfo = Introspector .getBeanInfo (entity );
@@ -54,6 +76,11 @@ public CachedIntrospectionResult(Class<?> entity) {
54
76
this .map = Stream .of (beanInfo .getPropertyDescriptors ())
55
77
.map (CachedPropertyDescriptor ::new )
56
78
.collect (Collectors .toMap (CachedPropertyDescriptor ::getName , it -> it ));
79
+
80
+ this .fields = iterate ((Class ) entity , k -> Optional .ofNullable (k .getSuperclass ()))
81
+ .flatMap (k -> Arrays .stream (k .getDeclaredFields ()))
82
+ .filter (f -> map .containsKey (f .getName ()))
83
+ .collect (Collectors .toMap (Field ::getName , it -> it ));
57
84
}
58
85
59
86
public Collection <CachedPropertyDescriptor > getPropertyDescriptors () {
@@ -64,6 +91,14 @@ public Optional<CachedPropertyDescriptor> getPropertyDescriptor(String fieldName
64
91
return Optional .ofNullable (map .getOrDefault (fieldName , null ));
65
92
}
66
93
94
+ public boolean hasPropertyDescriptor (String fieldName ) {
95
+ return map .containsKey (fieldName );
96
+ }
97
+
98
+ public Optional <Field > getField (String fieldName ) {
99
+ return Optional .ofNullable (fields .get (fieldName ));
100
+ }
101
+
67
102
public Class <?> getEntity () {
68
103
return entity ;
69
104
}
@@ -83,6 +118,10 @@ public PropertyDescriptor getDelegate() {
83
118
return delegate ;
84
119
}
85
120
121
+ public Class <?> getPropertyType () {
122
+ return delegate .getPropertyType ();
123
+ }
124
+
86
125
public String getName () {
87
126
return delegate .getName ();
88
127
}
@@ -92,11 +131,9 @@ public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
92
131
}
93
132
94
133
private boolean isAnnotationPresentOnField (Class <? extends Annotation > annotation ) {
95
- try {
96
- return entity .getDeclaredField (delegate .getName ()).isAnnotationPresent (annotation );
97
- } catch (NoSuchFieldException e ) {
98
- return false ;
99
- }
134
+ return Optional .ofNullable (fields .get (delegate .getName ()))
135
+ .map (f -> f .isAnnotationPresent (annotation ))
136
+ .orElse (false );
100
137
}
101
138
102
139
private boolean isAnnotationPresentOnReadMethod (Class <? extends Annotation > annotation ) {
@@ -105,4 +142,38 @@ private boolean isAnnotationPresentOnReadMethod(Class<? extends Annotation> anno
105
142
106
143
}
107
144
}
145
+
146
+ /**
147
+ * The following method is borrowed from Streams.iterate,
148
+ * however Streams.iterate is designed to create infinite streams.
149
+ *
150
+ * This version has been modified to end when Optional.empty()
151
+ * is returned from the fetchNextFunction.
152
+ */
153
+ protected static <T > Stream <T > iterate ( T seed , Function <T , Optional <T >> fetchNextFunction ) {
154
+ Objects .requireNonNull (fetchNextFunction );
155
+
156
+ Iterator <T > iterator = new Iterator <T >() {
157
+ private Optional <T > t = Optional .ofNullable (seed );
158
+
159
+ @ Override
160
+ public boolean hasNext () {
161
+ return t .isPresent ();
162
+ }
163
+
164
+ @ Override
165
+ public T next () {
166
+ T v = t .get ();
167
+
168
+ t = fetchNextFunction .apply (v );
169
+
170
+ return v ;
171
+ }
172
+ };
173
+
174
+ return StreamSupport .stream (
175
+ Spliterators .spliteratorUnknownSize ( iterator , Spliterator .ORDERED | Spliterator .IMMUTABLE ),
176
+ false
177
+ );
178
+ }
108
179
}
0 commit comments