1
1
package com .fasterxml .jackson .databind .jdk14 ;
2
2
3
3
import java .lang .reflect .Method ;
4
+ import java .util .Collections ;
5
+ import java .util .List ;
6
+ import java .util .Map ;
4
7
8
+ import com .fasterxml .jackson .annotation .JsonCreator ;
9
+ import com .fasterxml .jackson .annotation .JsonCreator .Mode ;
10
+ import com .fasterxml .jackson .databind .AnnotationIntrospector ;
11
+ import com .fasterxml .jackson .databind .BeanDescription ;
12
+ import com .fasterxml .jackson .databind .DeserializationConfig ;
13
+ import com .fasterxml .jackson .databind .DeserializationContext ;
14
+ import com .fasterxml .jackson .databind .deser .impl .CreatorCollector ;
15
+ import com .fasterxml .jackson .databind .introspect .AnnotatedConstructor ;
16
+ import com .fasterxml .jackson .databind .introspect .AnnotatedWithParams ;
17
+ import com .fasterxml .jackson .databind .introspect .BeanPropertyDefinition ;
18
+ import com .fasterxml .jackson .databind .introspect .VisibilityChecker ;
5
19
import com .fasterxml .jackson .databind .util .ClassUtil ;
6
20
7
21
/**
@@ -18,6 +32,12 @@ public static String[] getRecordFieldNames(Class<?> recordType) {
18
32
return RecordAccessor .instance ().getRecordFieldNames (recordType );
19
33
}
20
34
35
+ public static AnnotatedConstructor findRecordConstructor (DeserializationContext ctxt ,
36
+ BeanDescription beanDesc , List <String > names ) {
37
+ return new CreatorLocator (ctxt , beanDesc )
38
+ .locate (names );
39
+ }
40
+
21
41
static class RecordAccessor {
22
42
private final Method RECORD_GET_RECORD_COMPONENTS ;
23
43
private final Method RECORD_COMPONENT_GET_NAME ;
@@ -74,6 +94,32 @@ public String[] getRecordFieldNames(Class<?> recordType) throws IllegalArgumentE
74
94
return names ;
75
95
}
76
96
97
+ public RawTypeName [] getRecordFields (Class <?> recordType ) throws IllegalArgumentException
98
+ {
99
+ final Object [] components = recordComponents (recordType );
100
+ final RawTypeName [] results = new RawTypeName [components .length ];
101
+ for (int i = 0 ; i < components .length ; i ++) {
102
+ String name ;
103
+ try {
104
+ name = (String ) RECORD_COMPONENT_GET_NAME .invoke (components [i ]);
105
+ } catch (Exception e ) {
106
+ throw new IllegalArgumentException (String .format (
107
+ "Failed to access name of field #%d (of %d) of Record type %s" ,
108
+ i , components .length , ClassUtil .nameOf (recordType )), e );
109
+ }
110
+ Class <?> type ;
111
+ try {
112
+ type = (Class <?>) RECORD_COMPONENT_GET_TYPE .invoke (components [i ]);
113
+ } catch (Exception e ) {
114
+ throw new IllegalArgumentException (String .format (
115
+ "Failed to access type of field #%d (of %d) of Record type %s" ,
116
+ i , components .length , ClassUtil .nameOf (recordType )), e );
117
+ }
118
+ results [i ] = new RawTypeName (type , name );
119
+ }
120
+ return results ;
121
+ }
122
+
77
123
protected Object [] recordComponents (Class <?> recordType ) throws IllegalArgumentException
78
124
{
79
125
try {
@@ -84,4 +130,92 @@ protected Object[] recordComponents(Class<?> recordType) throws IllegalArgumentE
84
130
}
85
131
}
86
132
}
133
+
134
+ static class RawTypeName {
135
+ public final Class <?> rawType ;
136
+ public final String name ;
137
+
138
+ public RawTypeName (Class <?> rt , String n ) {
139
+ rawType = rt ;
140
+ name = n ;
141
+ }
142
+ }
143
+
144
+ static class CreatorLocator {
145
+ protected final BeanDescription _beanDesc ;
146
+ protected final DeserializationConfig _config ;
147
+ protected final AnnotationIntrospector _intr ;
148
+
149
+ protected final List <AnnotatedConstructor > _constructors ;
150
+ protected final AnnotatedConstructor _primaryConstructor ;
151
+ protected final RawTypeName [] _recordFields ;
152
+
153
+ CreatorLocator (DeserializationContext ctxt , BeanDescription beanDesc )
154
+ {
155
+ _beanDesc = beanDesc ;
156
+
157
+ _intr = ctxt .getAnnotationIntrospector ();
158
+ _config = ctxt .getConfig ();
159
+
160
+ _recordFields = RecordAccessor .instance ().getRecordFields (beanDesc .getBeanClass ());
161
+ final int argCount = _recordFields .length ;
162
+
163
+ // And then locate the canonical constructor; must be found, if not, fail
164
+ // altogether (so we can figure out what went wrong)
165
+ AnnotatedConstructor primary = null ;
166
+
167
+ // One special case: empty Records, empty constructor is separate case
168
+ if (argCount == 0 ) {
169
+ primary = beanDesc .findDefaultConstructor ();
170
+ _constructors = Collections .singletonList (primary );
171
+ } else {
172
+ _constructors = beanDesc .getConstructors ();
173
+ main_loop :
174
+ for (AnnotatedConstructor ctor : _constructors ) {
175
+ if (ctor .getParameterCount () != argCount ) {
176
+ continue ;
177
+ }
178
+ for (int i = 0 ; i < argCount ; ++i ) {
179
+ if (!ctor .getRawParameterType (i ).equals (_recordFields [i ].rawType )) {
180
+ continue main_loop ;
181
+ }
182
+ }
183
+ primary = ctor ;
184
+ break ;
185
+ }
186
+ }
187
+ if (primary == null ) {
188
+ throw new IllegalArgumentException ("Failed to find the canonical Record constructor of type "
189
+ +ClassUtil .getTypeDescription (_beanDesc .getType ()));
190
+ }
191
+ _primaryConstructor = primary ;
192
+ }
193
+
194
+ public AnnotatedConstructor locate (List <String > names )
195
+ {
196
+ // First things first: ensure that either there are no explicit marked constructors
197
+ // or that there is just one and it is the canonical one and it is not
198
+ // declared as "delegating" constructor
199
+ for (AnnotatedConstructor ctor : _constructors ) {
200
+ JsonCreator .Mode creatorMode = _intr .findCreatorAnnotation (_config , ctor );
201
+ if ((null == creatorMode ) || (Mode .DISABLED == creatorMode )) {
202
+ continue ;
203
+ }
204
+ // If there's a delegating Creator let caller figure out
205
+ if (Mode .DELEGATING == creatorMode ) {
206
+ return null ;
207
+ }
208
+ if (ctor != _primaryConstructor ) {
209
+ return null ;
210
+ }
211
+ }
212
+
213
+ // By now we have established that the canonical constructor is the one to use
214
+ // and just need to gather implicit names to return
215
+ for (RawTypeName field : _recordFields ) {
216
+ names .add (field .name );
217
+ }
218
+ return _primaryConstructor ;
219
+ }
220
+ }
87
221
}
0 commit comments