1
+ /*
2
+ * Copyright 2020 the original author or authors.
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
+ * https://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
+ package org .springframework .batch .item .file .mapping ;
17
+
18
+ import java .lang .reflect .Constructor ;
19
+
20
+ import org .springframework .batch .item .file .transform .FieldSet ;
21
+ import org .springframework .beans .BeanUtils ;
22
+ import org .springframework .beans .SimpleTypeConverter ;
23
+ import org .springframework .core .convert .ConversionService ;
24
+ import org .springframework .core .convert .support .DefaultConversionService ;
25
+ import org .springframework .util .Assert ;
26
+
27
+ /**
28
+ * This is a {@link FieldSetMapper} that supports Java records mapping.
29
+ * It uses the record's canonical constructor to map components with the
30
+ * same name as tokens in the {@link FieldSet}.
31
+ *
32
+ * @param <T> type of mapped items
33
+ * @author Mahmoud Ben Hassine
34
+ * @since 4.3
35
+ */
36
+ public class RecordFieldSetMapper <T > implements FieldSetMapper <T > {
37
+
38
+ private final SimpleTypeConverter typeConverter = new SimpleTypeConverter ();
39
+ private final Constructor <T > mappedConstructor ;
40
+ private String [] constructorParameterNames ;
41
+ private Class <?>[] constructorParameterTypes ;
42
+
43
+ /**
44
+ * Create a new {@link RecordFieldSetMapper}.
45
+ *
46
+ * @param targetType type of mapped items
47
+ */
48
+ public RecordFieldSetMapper (Class <T > targetType ) {
49
+ this (targetType , new DefaultConversionService ());
50
+ }
51
+
52
+ /**
53
+ * Create a new {@link RecordFieldSetMapper}.
54
+ *
55
+ * @param targetType type of mapped items
56
+ * @param conversionService service to use to convert raw data to typed fields
57
+ */
58
+ public RecordFieldSetMapper (Class < T > targetType , ConversionService conversionService ) {
59
+ this .typeConverter .setConversionService (conversionService );
60
+ this .mappedConstructor = BeanUtils .getResolvableConstructor (targetType );
61
+ if (this .mappedConstructor .getParameterCount () > 0 ) {
62
+ this .constructorParameterNames = BeanUtils .getParameterNames (this .mappedConstructor );
63
+ this .constructorParameterTypes = this .mappedConstructor .getParameterTypes ();
64
+ }
65
+ }
66
+
67
+ @ Override
68
+ public T mapFieldSet (FieldSet fieldSet ) {
69
+ Assert .isTrue (fieldSet .getFieldCount () == this .constructorParameterNames .length ,
70
+ "Fields count must be equal to record components count" );
71
+ Assert .isTrue (fieldSet .hasNames (), "Field names must specified" );
72
+ Object [] args = new Object [0 ];
73
+ if (this .constructorParameterNames != null && this .constructorParameterTypes != null ) {
74
+ args = new Object [this .constructorParameterNames .length ];
75
+ for (int i = 0 ; i < args .length ; i ++) {
76
+ String name = this .constructorParameterNames [i ];
77
+ Class <?> type = this .constructorParameterTypes [i ];
78
+ args [i ] = this .typeConverter .convertIfNecessary (fieldSet .readRawString (name ), type );
79
+ }
80
+ }
81
+ return BeanUtils .instantiateClass (this .mappedConstructor , args );
82
+ }
83
+ }
0 commit comments