88import  org .junit .jupiter .api .Test ;
99
1010import  java .beans .ConstructorProperties ;
11+ import  java .lang .reflect .ParameterizedType ;
1112import  java .lang .reflect .Type ;
1213import  java .math .BigDecimal ;
1314import  java .math .BigInteger ;
1718import  java .util .HashMap ;
1819import  java .util .List ;
1920import  java .util .Map ;
21+ import  java .util .Objects ;
2022import  java .util .Optional ;
2123import  java .util .TimeZone ;
2224
3032import  static  java .util .Collections .emptyMap ;
3133import  static  java .util .Collections .singletonList ;
3234import  static  java .util .Locale .ENGLISH ;
35+ import  static  java .util .stream .Collectors .toMap ;
3336import  static  org .hamcrest .CoreMatchers .is ;
3437import  static  org .hamcrest .CoreMatchers .startsWith ;
3538import  static  org .hamcrest .MatcherAssert .assertThat ;
@@ -93,7 +96,11 @@ class DataTableTypeRegistryTableConverterTest {
9396    }.getType ();
9497    private  static  final  Type  MAP_OF_STRING_TO_MAP_OF_INTEGER_TO_PIECE  = new  TypeReference <Map <String , Map <Integer , Piece >>>() {
9598    }.getType ();
96-     public  static  final  Type  OPTIONAL_CHESS_BOARD_TYPE  = new  TypeReference <Optional <ChessBoard >>() {
99+     private  static  final  Type  OPTIONAL_CHESS_BOARD_TYPE  = new  TypeReference <Optional <ChessBoard >>() {
100+     }.getType ();
101+     private  static  final  Type  NUMBERED_AUTHOR  = new  TypeReference <NumberedObject <Author >>() {
102+     }.getType ();
103+     private  static  final  Type  LIST_OF_NUMBERED_AUTHOR  = new  TypeReference <List <NumberedObject <Author >>>() {
97104    }.getType ();
98105    private  static  final  TableTransformer <ChessBoard > CHESS_BOARD_TABLE_TRANSFORMER  = table  -> new  ChessBoard (
99106        table .subTable (1 , 1 ).values ());
@@ -120,11 +127,30 @@ class DataTableTypeRegistryTableConverterTest {
120127    };
121128    private  static  final  TableEntryByTypeTransformer  JACKSON_TABLE_ENTRY_BY_TYPE_CONVERTER  = (entry , type ,
122129            cellTransformer ) -> objectMapper .convertValue (entry , objectMapper .constructType (type ));
130+     private  static  final  TableEntryByTypeTransformer  JACKSON_NUMBERED_OBJECT_TABLE_ENTRY_CONVERTER  = (entry , type ,
131+             cellTransformer ) -> {
132+         if  (!(type  instanceof  ParameterizedType )) {
133+             throw  new  IllegalArgumentException ("Unsupported type "  + type );
134+         }
135+         ParameterizedType  parameterizedType  = (ParameterizedType ) type ;
136+         if  (!NumberedObject .class .equals (parameterizedType .getRawType ())) {
137+             throw  new  IllegalArgumentException ("Unsupported type "  + parameterizedType );
138+         }
139+         return  convertToNumberedObject (entry , parameterizedType .getActualTypeArguments ()[0 ]);
140+     };
123141    private  static  final  TableCellByTypeTransformer  JACKSON_TABLE_CELL_BY_TYPE_CONVERTER  = (value ,
124142            cellType ) -> objectMapper .convertValue (value , objectMapper .constructType (cellType ));
125143    private  static  final  DataTableType  DATE_TABLE_CELL_TRANSFORMER  = new  DataTableType (Date .class ,
126144        (TableCellTransformer <Date >) SIMPLE_DATE_FORMAT ::parse );
127145
146+     private  static  Object  convertToNumberedObject (Map <String , String > numberedEntry , Type  type ) {
147+         int  number  = Integer .parseInt (numberedEntry .get ("#" ));
148+         Map <String , String > entry  = numberedEntry .entrySet ().stream ()
149+                 .filter (e  -> !"#" .equals (e .getKey ()))
150+                 .collect (toMap (Map .Entry ::getKey , Map .Entry ::getValue ));
151+         return  new  NumberedObject <>(number , objectMapper .convertValue (entry , objectMapper .constructType (type )));
152+     }
153+ 
128154    static  {
129155        SIMPLE_DATE_FORMAT .setTimeZone (TimeZone .getTimeZone ("UTC" ));
130156    }
@@ -427,6 +453,26 @@ void convert_to_empty_list_of_object__using_default_converter__throws_exception(
427453                "Note: Usually solving one is enough" ));
428454    }
429455
456+     @ Test 
457+     void  convert_to_list_of_parameterized_object__using_default_converter () {
458+         DataTable  table  = parse ("" ,
459+             "| # | firstName   | lastName | birthDate  |" ,
460+             "| 1 | Annie M. G. | Schmidt  | 1911-03-20 |" ,
461+             "| 2 | Roald       | Dahl     | 1916-09-13 |" ,
462+             "| 3 | Astrid      | Lindgren | 1907-11-14 |" );
463+ 
464+         registry .setDefaultDataTableEntryTransformer (JACKSON_NUMBERED_OBJECT_TABLE_ENTRY_CONVERTER );
465+         registry .setDefaultDataTableCellTransformer (TABLE_CELL_BY_TYPE_CONVERTER_SHOULD_NOT_BE_USED );
466+ 
467+         List <NumberedObject <Author >> expected  = asList (
468+             new  NumberedObject <>(1 , new  Author ("Annie M. G." , "Schmidt" , "1911-03-20" )),
469+             new  NumberedObject <>(2 , new  Author ("Roald" , "Dahl" , "1916-09-13" )),
470+             new  NumberedObject <>(3 , new  Author ("Astrid" , "Lindgren" , "1907-11-14" )));
471+ 
472+         assertEquals (expected , converter .toList (table , NUMBERED_AUTHOR ));
473+         assertEquals (expected , converter .convert (table , LIST_OF_NUMBERED_AUTHOR ));
474+     }
475+ 
430476    @ Test 
431477    void  convert_to_list_of_primitive () {
432478        DataTable  table  = parse ("" ,
@@ -1222,6 +1268,20 @@ void convert_to_single_object__single_cell__using_default_transformer() {
12221268        assertEquals (Piece .BLACK_BISHOP , converter .convert (table , Piece .class ));
12231269    }
12241270
1271+     @ Test 
1272+     void  convert_to_parameterized_object__using_default_converter () {
1273+         DataTable  table  = parse ("" ,
1274+             "| # | firstName   | lastName | birthDate  |" ,
1275+             "| 1 | Annie M. G. | Schmidt  | 1911-03-20 |" );
1276+ 
1277+         registry .setDefaultDataTableEntryTransformer (JACKSON_NUMBERED_OBJECT_TABLE_ENTRY_CONVERTER );
1278+         registry .setDefaultDataTableCellTransformer (TABLE_CELL_BY_TYPE_CONVERTER_SHOULD_NOT_BE_USED );
1279+ 
1280+         NumberedObject <Author > expected  = new  NumberedObject <>(1 , new  Author ("Annie M. G." , "Schmidt" , "1911-03-20" ));
1281+ 
1282+         assertEquals (expected , converter .convert (table , NUMBERED_AUTHOR ));
1283+     }
1284+ 
12251285    @ Test 
12261286    void  convert_to_table__table_transformer_takes_precedence_over_identity_transform () {
12271287        DataTable  table  = parse ("" ,
@@ -1713,6 +1773,28 @@ void to_maps_of_unknown_value_type__throws_exception__register_table_cell_transf
17131773                "Note: Usually solving one is enough" ));
17141774    }
17151775
1776+     private  static  class  NumberedObject <T > {
1777+         private  final  int  number ;
1778+         private  final  T  value ;
1779+ 
1780+         private  NumberedObject (int  number , T  value ) {
1781+             this .number  = number ;
1782+             this .value  = value ;
1783+         }
1784+ 
1785+         @ Override 
1786+         public  boolean  equals (Object  obj ) {
1787+             return  obj  instanceof  NumberedObject 
1788+                     && ((NumberedObject <?>) obj ).number  == number 
1789+                     && Objects .equals (((NumberedObject <?>) obj ).value , value );
1790+         }
1791+ 
1792+         @ Override 
1793+         public  String  toString () {
1794+             return  String .format ("%d: %s" , number , value );
1795+         }
1796+     }
1797+ 
17161798    private  enum  Piece  {
17171799        BLACK_PAWN ("♟" ),
17181800        BLACK_BISHOP ("♝" ),
0 commit comments