30
30
import java .io .Writer ;
31
31
import java .util .ArrayList ;
32
32
import java .util .List ;
33
+ import java .util .function .Predicate ;
33
34
34
35
import static uk .co .real_logic .sbe .generation .Generators .toLowerFirstChar ;
35
36
import static uk .co .real_logic .sbe .generation .Generators .toUpperFirstChar ;
43
44
*/
44
45
public class JavaDtoGenerator implements CodeGenerator
45
46
{
47
+ private static final Predicate <Token > ALWAYS_FALSE_PREDICATE = ignored -> false ;
46
48
private static final String INDENT = " " ;
47
49
private static final String BASE_INDENT = "" ;
48
50
@@ -97,7 +99,7 @@ public void generate() throws IOException
97
99
generateVarData (classBuilder , varData , BASE_INDENT + INDENT );
98
100
99
101
generateDecodeWith (classBuilder , dtoClassName , decoderClassName , fields ,
100
- groups , varData , BASE_INDENT + INDENT );
102
+ groups , varData , BASE_INDENT + INDENT , fieldToken -> fieldToken . version () > msgToken . version () );
101
103
generateDecodeFrom (classBuilder , dtoClassName , decoderClassName , BASE_INDENT + INDENT );
102
104
generateEncodeWith (classBuilder , dtoClassName , encoderClassName , fields , groups , varData ,
103
105
BASE_INDENT + INDENT );
@@ -196,6 +198,13 @@ private void generateGroups(
196
198
final String groupClassName = formatDtoClassName (groupName );
197
199
final String qualifiedDtoClassName = qualifiedParentDtoClassName + "." + groupClassName ;
198
200
201
+ final Token dimToken = tokens .get (i + 1 );
202
+ if (dimToken .signal () != Signal .BEGIN_COMPOSITE )
203
+ {
204
+ throw new IllegalStateException ("groups must start with BEGIN_COMPOSITE: token=" + dimToken );
205
+ }
206
+ final int sinceVersion = dimToken .version ();
207
+
199
208
final String fieldName = formatFieldName (groupName );
200
209
final String formattedPropertyName = formatPropertyName (groupName );
201
210
@@ -226,10 +235,22 @@ private void generateGroups(
226
235
i = collectVarData (tokens , i , varData );
227
236
generateVarData (groupClassBuilder , varData , indent + INDENT );
228
237
238
+ final Predicate <Token > wasAddedAfterGroup = token ->
239
+ {
240
+ final boolean addedAfterParent = token .version () > sinceVersion ;
241
+
242
+ if (addedAfterParent && token .signal () == Signal .BEGIN_VAR_DATA )
243
+ {
244
+ throw new IllegalStateException ("Cannot extend var data inside a group." );
245
+ }
246
+
247
+ return addedAfterParent ;
248
+ };
249
+
229
250
generateDecodeListWith (
230
251
groupClassBuilder , groupClassName , qualifiedDecoderClassName , indent + INDENT );
231
252
generateDecodeWith (groupClassBuilder , groupClassName , qualifiedDecoderClassName ,
232
- fields , groups , varData , indent + INDENT );
253
+ fields , groups , varData , indent + INDENT , wasAddedAfterGroup );
233
254
generateEncodeWith (
234
255
groupClassBuilder , groupClassName , qualifiedEncoderClassName , fields , groups , varData , indent + INDENT );
235
256
generateComputeEncodedLength (groupClassBuilder , qualifiedDecoderClassName ,
@@ -324,8 +345,10 @@ private void generateComputeEncodedLength(
324
345
.append (qualifiedDecoderClassName ).append ("." )
325
346
.append (formatPropertyName (propertyName )).append ("HeaderLength();\n " );
326
347
348
+ final String characterEncoding = varDataToken .encoding ().characterEncoding ();
349
+ final String lengthAccessor = characterEncoding == null ? ".length" : ".length()" ;
327
350
lengthBuilder .append (indent ).append (INDENT ).append ("encodedLength += " )
328
- .append (fieldName ).append (".length()" );
351
+ .append (fieldName ).append (lengthAccessor );
329
352
330
353
final int elementByteLength = varDataToken .encoding ().primitiveType ().size ();
331
354
if (elementByteLength != 1 )
@@ -358,7 +381,7 @@ private void generateCompositeDecodeWith(
358
381
final Token token = tokens .get (i );
359
382
360
383
generateFieldDecodeWith (
361
- decodeBuilder , token , token , decoderClassName , indent + INDENT );
384
+ decodeBuilder , token , token , decoderClassName , indent + INDENT , ALWAYS_FALSE_PREDICATE );
362
385
363
386
i += tokens .get (i ).componentTokenCount ();
364
387
}
@@ -426,16 +449,17 @@ private void generateDecodeWith(
426
449
final List <Token > fields ,
427
450
final List <Token > groups ,
428
451
final List <Token > varData ,
429
- final String indent )
452
+ final String indent ,
453
+ final Predicate <Token > wasAddedAfterParent )
430
454
{
431
455
final StringBuilder decodeBuilder = classBuilder .appendPublic ().append ("\n " )
432
456
.append (indent ).append ("public static void decodeWith(" ).append (decoderClassName ).append (" decoder, " )
433
457
.append (dtoClassName ).append (" dto)\n " )
434
458
.append (indent ).append ("{\n " );
435
459
436
- generateMessageFieldsDecodeWith (decodeBuilder , fields , decoderClassName , indent + INDENT );
460
+ generateMessageFieldsDecodeWith (decodeBuilder , fields , decoderClassName , indent + INDENT , wasAddedAfterParent );
437
461
generateGroupsDecodeWith (decodeBuilder , groups , indent + INDENT );
438
- generateVarDataDecodeWith (decodeBuilder , varData , indent + INDENT );
462
+ generateVarDataDecodeWith (decodeBuilder , decoderClassName , varData , indent + INDENT , wasAddedAfterParent );
439
463
decodeBuilder .append (indent ).append ("}\n " );
440
464
}
441
465
@@ -466,7 +490,8 @@ private void generateMessageFieldsDecodeWith(
466
490
final StringBuilder sb ,
467
491
final List <Token > tokens ,
468
492
final String decoderClassName ,
469
- final String indent )
493
+ final String indent ,
494
+ final Predicate <Token > wasAddedAfterParent )
470
495
{
471
496
for (int i = 0 , size = tokens .size (); i < size ; i ++)
472
497
{
@@ -475,7 +500,7 @@ private void generateMessageFieldsDecodeWith(
475
500
{
476
501
final Token encodingToken = tokens .get (i + 1 );
477
502
478
- generateFieldDecodeWith (sb , signalToken , encodingToken , decoderClassName , indent );
503
+ generateFieldDecodeWith (sb , signalToken , encodingToken , decoderClassName , indent , wasAddedAfterParent );
479
504
}
480
505
}
481
506
}
@@ -485,17 +510,18 @@ private void generateFieldDecodeWith(
485
510
final Token fieldToken ,
486
511
final Token typeToken ,
487
512
final String decoderClassName ,
488
- final String indent )
513
+ final String indent ,
514
+ final Predicate <Token > wasAddedAfterParent )
489
515
{
490
516
switch (typeToken .signal ())
491
517
{
492
518
case ENCODING :
493
- generatePrimitiveDecodeWith (sb , fieldToken , typeToken , decoderClassName , indent );
519
+ generatePrimitiveDecodeWith (sb , fieldToken , typeToken , decoderClassName , indent , wasAddedAfterParent );
494
520
break ;
495
521
496
522
case BEGIN_SET :
497
523
final String bitSetName = formatDtoClassName (typeToken .applicableTypeName ());
498
- generateBitSetDecodeWith (sb , fieldToken , bitSetName , indent );
524
+ generateBitSetDecodeWith (sb , decoderClassName , fieldToken , bitSetName , indent , wasAddedAfterParent );
499
525
break ;
500
526
501
527
case BEGIN_ENUM :
@@ -516,7 +542,8 @@ private void generatePrimitiveDecodeWith(
516
542
final Token fieldToken ,
517
543
final Token typeToken ,
518
544
final String decoderClassName ,
519
- final String indent )
545
+ final String indent ,
546
+ final Predicate <Token > wasAddedAfterParent )
520
547
{
521
548
if (typeToken .isConstantEncoding ())
522
549
{
@@ -543,12 +570,13 @@ private void generatePrimitiveDecodeWith(
543
570
indent ,
544
571
"decoder.actingVersion() >= " + decoderClassName + "." + formattedPropertyName + "SinceVersion()" ,
545
572
"decoder." + formattedPropertyName + "()" ,
546
- decoderNullValue
573
+ decoderNullValue ,
574
+ wasAddedAfterParent
547
575
);
548
576
}
549
577
else if (arrayLength > 1 )
550
578
{
551
- generateArrayDecodeWith (sb , decoderClassName , fieldToken , typeToken , indent );
579
+ generateArrayDecodeWith (sb , decoderClassName , fieldToken , typeToken , indent , wasAddedAfterParent );
552
580
}
553
581
}
554
582
@@ -557,7 +585,8 @@ private void generateArrayDecodeWith(
557
585
final String decoderClassName ,
558
586
final Token fieldToken ,
559
587
final Token typeToken ,
560
- final String indent )
588
+ final String indent ,
589
+ final Predicate <Token > wasAddedAfterParent )
561
590
{
562
591
if (fieldToken .isConstantEncoding ())
563
592
{
@@ -566,22 +595,24 @@ private void generateArrayDecodeWith(
566
595
567
596
final String propertyName = fieldToken .name ();
568
597
final String formattedPropertyName = formatPropertyName (propertyName );
598
+ final PrimitiveType primitiveType = typeToken .encoding ().primitiveType ();
569
599
570
- if (typeToken . encoding (). primitiveType () == PrimitiveType .CHAR )
600
+ if (primitiveType == PrimitiveType .CHAR )
571
601
{
572
602
generateRecordPropertyAssignment (
573
603
sb ,
574
604
fieldToken ,
575
605
indent ,
576
606
"decoder.actingVersion() >= " + decoderClassName + "." + formattedPropertyName + "SinceVersion()" ,
577
607
"decoder." + formattedPropertyName + "()" ,
578
- "\" \" "
608
+ "\" \" " ,
609
+ wasAddedAfterParent
579
610
);
580
611
}
581
612
else
582
613
{
583
614
final StringBuilder initializerList = new StringBuilder ();
584
- final String elementType = javaTypeName (typeToken . encoding (). primitiveType () );
615
+ final String elementType = javaTypeName (primitiveType );
585
616
initializerList .append ("new " ).append (elementType ).append ("[] { " );
586
617
final int arrayLength = typeToken .arrayLength ();
587
618
for (int i = 0 ; i < arrayLength ; i ++)
@@ -598,16 +629,19 @@ private void generateArrayDecodeWith(
598
629
indent ,
599
630
"decoder.actingVersion() >= " + decoderClassName + "." + formattedPropertyName + "SinceVersion()" ,
600
631
initializerList ,
601
- null
632
+ "new " + elementType + "[0]" ,
633
+ wasAddedAfterParent
602
634
);
603
635
}
604
636
}
605
637
606
638
private void generateBitSetDecodeWith (
607
639
final StringBuilder sb ,
640
+ final String decoderClassName ,
608
641
final Token fieldToken ,
609
642
final String dtoTypeName ,
610
- final String indent )
643
+ final String indent ,
644
+ final Predicate <Token > wasAddedAfterParent )
611
645
{
612
646
if (fieldToken .isConstantEncoding ())
613
647
{
@@ -617,11 +651,11 @@ private void generateBitSetDecodeWith(
617
651
final String propertyName = fieldToken .name ();
618
652
final String formattedPropertyName = formatPropertyName (propertyName );
619
653
620
- if (fieldToken . isOptionalEncoding ( ))
654
+ if (wasAddedAfterParent . test ( fieldToken ))
621
655
{
622
- sb .append (indent ).append ("if (decoder." ). append ( formattedPropertyName ). append ( "InActingVersion()" );
623
-
624
- sb .append (" )\n " )
656
+ sb .append (indent ).append ("if (decoder.actingVersion() >= " )
657
+ . append ( decoderClassName ). append ( "." )
658
+ .append (formattedPropertyName ). append ( "SinceVersion() )\n " )
625
659
.append (indent ).append ("{\n " );
626
660
627
661
sb .append (indent ).append (INDENT ).append (dtoTypeName ).append (".decodeWith(decoder." )
@@ -710,8 +744,10 @@ private void generateGroupsDecodeWith(
710
744
711
745
private void generateVarDataDecodeWith (
712
746
final StringBuilder sb ,
747
+ final String decoderClassName ,
713
748
final List <Token > tokens ,
714
- final String indent )
749
+ final String indent ,
750
+ final Predicate <Token > wasAddedAfterParent )
715
751
{
716
752
for (int i = 0 ; i < tokens .size (); i ++)
717
753
{
@@ -723,7 +759,7 @@ private void generateVarDataDecodeWith(
723
759
final Token varDataToken = Generators .findFirst ("varData" , tokens , i );
724
760
final String characterEncoding = varDataToken .encoding ().characterEncoding ();
725
761
726
- final boolean isOptional = token . version () > 0 ;
762
+ final boolean isOptional = wasAddedAfterParent . test ( token ) ;
727
763
final String blockIndent = isOptional ? indent + INDENT : indent ;
728
764
729
765
final String dataVar = toLowerFirstChar (propertyName ) + "Data" ;
@@ -734,7 +770,7 @@ private void generateVarDataDecodeWith(
734
770
{
735
771
decoderValueExtraction .append (blockIndent ).append ("byte[] " ).append (dataVar )
736
772
.append (" = new byte[decoder." ).append (formattedPropertyName ).append ("Length()];\n " )
737
- .append (blockIndent ).append ("decoder.get" ).append (formattedPropertyName )
773
+ .append (blockIndent ).append ("decoder.get" ).append (toUpperFirstChar ( formattedPropertyName ) )
738
774
.append ("(" ).append (dataVar ).append (", 0, decoder." ).append (formattedPropertyName )
739
775
.append ("Length());\n " );
740
776
}
@@ -746,17 +782,17 @@ private void generateVarDataDecodeWith(
746
782
747
783
if (isOptional )
748
784
{
749
- sb .append (indent ).append ("if (decoder." ). append ( formattedPropertyName ). append ( "InActingVersion()" );
750
-
751
- sb .append (" )\n " )
785
+ sb .append (indent ).append ("if (decoder.actingVersion() >= " )
786
+ . append ( decoderClassName ). append ( "." )
787
+ .append (formattedPropertyName ). append ( "SinceVersion() )\n " )
752
788
.append (indent ).append ("{\n " );
753
789
754
790
sb .append (decoderValueExtraction );
755
791
756
792
sb .append (indent ).append (INDENT ).append ("dto." ).append (formattedPropertyName ).append ("(" )
757
793
.append (dataVar ).append (");\n " );
758
794
759
- final String nullDtoValue = "\" \" " ;
795
+ final String nullDtoValue = characterEncoding == null ? "new byte[0]" : "\" \" " ;
760
796
761
797
sb .append (indent ).append ("}\n " )
762
798
.append (indent ).append ("else\n " )
@@ -782,27 +818,25 @@ private void generateRecordPropertyAssignment(
782
818
final String indent ,
783
819
final String presenceExpression ,
784
820
final CharSequence getExpression ,
785
- final String nullDecoderValueOrNull )
821
+ final String nullDecoderValue ,
822
+ final Predicate <Token > wasAddedAfterParent )
786
823
{
787
824
final String propertyName = token .name ();
788
825
final String formattedPropertyName = formatPropertyName (propertyName );
789
826
790
- if (token . isOptionalEncoding ( ))
827
+ if (wasAddedAfterParent . test ( token ))
791
828
{
792
-
793
829
sb .append (indent ).append ("if (" ).append (presenceExpression ).append (")\n " )
794
830
.append (indent ).append ("{\n " );
795
831
796
832
sb .append (indent ).append (INDENT ).append ("dto." ).append (formattedPropertyName ).append ("(" )
797
833
.append (getExpression ).append (");\n " );
798
834
799
- final String nullValue = nullDecoderValueOrNull == null ? "null" : nullDecoderValueOrNull ;
800
-
801
835
sb .append (indent ).append ("}\n " )
802
836
.append (indent ).append ("else\n " )
803
837
.append (indent ).append ("{\n " )
804
838
.append (indent ).append (INDENT ).append ("dto." ).append (formattedPropertyName ).append ("(" )
805
- .append (nullValue ).append (");\n " )
839
+ .append (nullDecoderValue ).append (");\n " )
806
840
.append (indent ).append ("}\n " );
807
841
}
808
842
else
@@ -966,19 +1000,16 @@ private void generateArrayEncodeWith(
966
1000
final String propertyName = fieldToken .name ();
967
1001
final String formattedPropertyName = formatPropertyName (propertyName );
968
1002
969
- if (typeToken .encoding ().primitiveType () == PrimitiveType .CHAR )
1003
+ final PrimitiveType primitiveType = typeToken .encoding ().primitiveType ();
1004
+
1005
+ if (primitiveType == PrimitiveType .CHAR )
970
1006
{
971
1007
sb .append (indent ).append ("encoder." ).append (toLowerFirstChar (propertyName )).append ("(" )
972
1008
.append ("dto." ).append (formattedPropertyName ).append ("());\n " );
973
1009
}
974
- else if (typeToken .encoding ().primitiveType () == PrimitiveType .UINT8 )
975
- {
976
- sb .append (indent ).append ("encoder.put" ).append (toUpperFirstChar (propertyName )).append ("(" )
977
- .append ("dto." ).append (formattedPropertyName ).append ("());\n " );
978
- }
979
1010
else
980
1011
{
981
- final String javaTypeName = javaTypeName (typeToken . encoding (). primitiveType () );
1012
+ final String javaTypeName = javaTypeName (primitiveType );
982
1013
sb .append (indent ).append (javaTypeName ).append ("[] " ).append (formattedPropertyName ).append (" = " )
983
1014
.append ("dto." ).append (formattedPropertyName ).append ("();\n " )
984
1015
.append (indent ).append ("for (int i = 0; i < " ).append (formattedPropertyName ).append (".length; i++)\n " )
@@ -1103,7 +1134,9 @@ private void generateVarDataEncodeWith(
1103
1134
if (characterEncoding == null )
1104
1135
{
1105
1136
sb .append (indent ).append ("encoder.put" ).append (toUpperFirstChar (propertyName )).append ("(" )
1106
- .append ("dto." ).append (formattedPropertyName ).append ("());\n " );
1137
+ .append ("dto." ).append (formattedPropertyName ).append ("()," )
1138
+ .append ("0," )
1139
+ .append ("dto." ).append (formattedPropertyName ).append ("().length);\n " );
1107
1140
}
1108
1141
else
1109
1142
{
@@ -1310,7 +1343,9 @@ private void generateArrayProperty(
1310
1343
final String fieldName = formatFieldName (propertyName );
1311
1344
final String validateMethod = "validate" + toUpperFirstChar (propertyName );
1312
1345
1313
- if (typeToken .encoding ().primitiveType () == PrimitiveType .CHAR )
1346
+ final PrimitiveType primitiveType = typeToken .encoding ().primitiveType ();
1347
+
1348
+ if (primitiveType == PrimitiveType .CHAR )
1314
1349
{
1315
1350
final CharSequence typeName = "String" ;
1316
1351
@@ -1345,7 +1380,7 @@ private void generateArrayProperty(
1345
1380
}
1346
1381
else
1347
1382
{
1348
- final String elementTypeName = javaTypeName (typeToken . encoding (). primitiveType () );
1383
+ final String elementTypeName = javaTypeName (primitiveType );
1349
1384
final String typeName = elementTypeName + "[]" ;
1350
1385
1351
1386
classBuilder .appendField ()
0 commit comments