3030import java .io .Writer ;
3131import java .util .ArrayList ;
3232import java .util .List ;
33+ import java .util .function .Predicate ;
3334
3435import static uk .co .real_logic .sbe .generation .Generators .toLowerFirstChar ;
3536import static uk .co .real_logic .sbe .generation .Generators .toUpperFirstChar ;
4344 */
4445public class JavaDtoGenerator implements CodeGenerator
4546{
47+ private static final Predicate <Token > ALWAYS_FALSE_PREDICATE = ignored -> false ;
4648 private static final String INDENT = " " ;
4749 private static final String BASE_INDENT = "" ;
4850
@@ -97,7 +99,7 @@ public void generate() throws IOException
9799 generateVarData (classBuilder , varData , BASE_INDENT + INDENT );
98100
99101 generateDecodeWith (classBuilder , dtoClassName , decoderClassName , fields ,
100- groups , varData , BASE_INDENT + INDENT );
102+ groups , varData , BASE_INDENT + INDENT , fieldToken -> fieldToken . version () > msgToken . version () );
101103 generateDecodeFrom (classBuilder , dtoClassName , decoderClassName , BASE_INDENT + INDENT );
102104 generateEncodeWith (classBuilder , dtoClassName , encoderClassName , fields , groups , varData ,
103105 BASE_INDENT + INDENT );
@@ -196,6 +198,13 @@ private void generateGroups(
196198 final String groupClassName = formatDtoClassName (groupName );
197199 final String qualifiedDtoClassName = qualifiedParentDtoClassName + "." + groupClassName ;
198200
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+
199208 final String fieldName = formatFieldName (groupName );
200209 final String formattedPropertyName = formatPropertyName (groupName );
201210
@@ -226,10 +235,22 @@ private void generateGroups(
226235 i = collectVarData (tokens , i , varData );
227236 generateVarData (groupClassBuilder , varData , indent + INDENT );
228237
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+
229250 generateDecodeListWith (
230251 groupClassBuilder , groupClassName , qualifiedDecoderClassName , indent + INDENT );
231252 generateDecodeWith (groupClassBuilder , groupClassName , qualifiedDecoderClassName ,
232- fields , groups , varData , indent + INDENT );
253+ fields , groups , varData , indent + INDENT , wasAddedAfterGroup );
233254 generateEncodeWith (
234255 groupClassBuilder , groupClassName , qualifiedEncoderClassName , fields , groups , varData , indent + INDENT );
235256 generateComputeEncodedLength (groupClassBuilder , qualifiedDecoderClassName ,
@@ -324,8 +345,10 @@ private void generateComputeEncodedLength(
324345 .append (qualifiedDecoderClassName ).append ("." )
325346 .append (formatPropertyName (propertyName )).append ("HeaderLength();\n " );
326347
348+ final String characterEncoding = varDataToken .encoding ().characterEncoding ();
349+ final String lengthAccessor = characterEncoding == null ? ".length" : ".length()" ;
327350 lengthBuilder .append (indent ).append (INDENT ).append ("encodedLength += " )
328- .append (fieldName ).append (".length()" );
351+ .append (fieldName ).append (lengthAccessor );
329352
330353 final int elementByteLength = varDataToken .encoding ().primitiveType ().size ();
331354 if (elementByteLength != 1 )
@@ -358,7 +381,7 @@ private void generateCompositeDecodeWith(
358381 final Token token = tokens .get (i );
359382
360383 generateFieldDecodeWith (
361- decodeBuilder , token , token , decoderClassName , indent + INDENT );
384+ decodeBuilder , token , token , decoderClassName , indent + INDENT , ALWAYS_FALSE_PREDICATE );
362385
363386 i += tokens .get (i ).componentTokenCount ();
364387 }
@@ -426,16 +449,17 @@ private void generateDecodeWith(
426449 final List <Token > fields ,
427450 final List <Token > groups ,
428451 final List <Token > varData ,
429- final String indent )
452+ final String indent ,
453+ final Predicate <Token > wasAddedAfterParent )
430454 {
431455 final StringBuilder decodeBuilder = classBuilder .appendPublic ().append ("\n " )
432456 .append (indent ).append ("public static void decodeWith(" ).append (decoderClassName ).append (" decoder, " )
433457 .append (dtoClassName ).append (" dto)\n " )
434458 .append (indent ).append ("{\n " );
435459
436- generateMessageFieldsDecodeWith (decodeBuilder , fields , decoderClassName , indent + INDENT );
460+ generateMessageFieldsDecodeWith (decodeBuilder , fields , decoderClassName , indent + INDENT , wasAddedAfterParent );
437461 generateGroupsDecodeWith (decodeBuilder , groups , indent + INDENT );
438- generateVarDataDecodeWith (decodeBuilder , varData , indent + INDENT );
462+ generateVarDataDecodeWith (decodeBuilder , decoderClassName , varData , indent + INDENT , wasAddedAfterParent );
439463 decodeBuilder .append (indent ).append ("}\n " );
440464 }
441465
@@ -466,7 +490,8 @@ private void generateMessageFieldsDecodeWith(
466490 final StringBuilder sb ,
467491 final List <Token > tokens ,
468492 final String decoderClassName ,
469- final String indent )
493+ final String indent ,
494+ final Predicate <Token > wasAddedAfterParent )
470495 {
471496 for (int i = 0 , size = tokens .size (); i < size ; i ++)
472497 {
@@ -475,7 +500,7 @@ private void generateMessageFieldsDecodeWith(
475500 {
476501 final Token encodingToken = tokens .get (i + 1 );
477502
478- generateFieldDecodeWith (sb , signalToken , encodingToken , decoderClassName , indent );
503+ generateFieldDecodeWith (sb , signalToken , encodingToken , decoderClassName , indent , wasAddedAfterParent );
479504 }
480505 }
481506 }
@@ -485,17 +510,18 @@ private void generateFieldDecodeWith(
485510 final Token fieldToken ,
486511 final Token typeToken ,
487512 final String decoderClassName ,
488- final String indent )
513+ final String indent ,
514+ final Predicate <Token > wasAddedAfterParent )
489515 {
490516 switch (typeToken .signal ())
491517 {
492518 case ENCODING :
493- generatePrimitiveDecodeWith (sb , fieldToken , typeToken , decoderClassName , indent );
519+ generatePrimitiveDecodeWith (sb , fieldToken , typeToken , decoderClassName , indent , wasAddedAfterParent );
494520 break ;
495521
496522 case BEGIN_SET :
497523 final String bitSetName = formatDtoClassName (typeToken .applicableTypeName ());
498- generateBitSetDecodeWith (sb , fieldToken , bitSetName , indent );
524+ generateBitSetDecodeWith (sb , decoderClassName , fieldToken , bitSetName , indent , wasAddedAfterParent );
499525 break ;
500526
501527 case BEGIN_ENUM :
@@ -516,7 +542,8 @@ private void generatePrimitiveDecodeWith(
516542 final Token fieldToken ,
517543 final Token typeToken ,
518544 final String decoderClassName ,
519- final String indent )
545+ final String indent ,
546+ final Predicate <Token > wasAddedAfterParent )
520547 {
521548 if (typeToken .isConstantEncoding ())
522549 {
@@ -543,12 +570,13 @@ private void generatePrimitiveDecodeWith(
543570 indent ,
544571 "decoder.actingVersion() >= " + decoderClassName + "." + formattedPropertyName + "SinceVersion()" ,
545572 "decoder." + formattedPropertyName + "()" ,
546- decoderNullValue
573+ decoderNullValue ,
574+ wasAddedAfterParent
547575 );
548576 }
549577 else if (arrayLength > 1 )
550578 {
551- generateArrayDecodeWith (sb , decoderClassName , fieldToken , typeToken , indent );
579+ generateArrayDecodeWith (sb , decoderClassName , fieldToken , typeToken , indent , wasAddedAfterParent );
552580 }
553581 }
554582
@@ -557,7 +585,8 @@ private void generateArrayDecodeWith(
557585 final String decoderClassName ,
558586 final Token fieldToken ,
559587 final Token typeToken ,
560- final String indent )
588+ final String indent ,
589+ final Predicate <Token > wasAddedAfterParent )
561590 {
562591 if (fieldToken .isConstantEncoding ())
563592 {
@@ -566,22 +595,24 @@ private void generateArrayDecodeWith(
566595
567596 final String propertyName = fieldToken .name ();
568597 final String formattedPropertyName = formatPropertyName (propertyName );
598+ final PrimitiveType primitiveType = typeToken .encoding ().primitiveType ();
569599
570- if (typeToken . encoding (). primitiveType () == PrimitiveType .CHAR )
600+ if (primitiveType == PrimitiveType .CHAR )
571601 {
572602 generateRecordPropertyAssignment (
573603 sb ,
574604 fieldToken ,
575605 indent ,
576606 "decoder.actingVersion() >= " + decoderClassName + "." + formattedPropertyName + "SinceVersion()" ,
577607 "decoder." + formattedPropertyName + "()" ,
578- "\" \" "
608+ "\" \" " ,
609+ wasAddedAfterParent
579610 );
580611 }
581612 else
582613 {
583614 final StringBuilder initializerList = new StringBuilder ();
584- final String elementType = javaTypeName (typeToken . encoding (). primitiveType () );
615+ final String elementType = javaTypeName (primitiveType );
585616 initializerList .append ("new " ).append (elementType ).append ("[] { " );
586617 final int arrayLength = typeToken .arrayLength ();
587618 for (int i = 0 ; i < arrayLength ; i ++)
@@ -598,16 +629,19 @@ private void generateArrayDecodeWith(
598629 indent ,
599630 "decoder.actingVersion() >= " + decoderClassName + "." + formattedPropertyName + "SinceVersion()" ,
600631 initializerList ,
601- null
632+ "new " + elementType + "[0]" ,
633+ wasAddedAfterParent
602634 );
603635 }
604636 }
605637
606638 private void generateBitSetDecodeWith (
607639 final StringBuilder sb ,
640+ final String decoderClassName ,
608641 final Token fieldToken ,
609642 final String dtoTypeName ,
610- final String indent )
643+ final String indent ,
644+ final Predicate <Token > wasAddedAfterParent )
611645 {
612646 if (fieldToken .isConstantEncoding ())
613647 {
@@ -617,11 +651,11 @@ private void generateBitSetDecodeWith(
617651 final String propertyName = fieldToken .name ();
618652 final String formattedPropertyName = formatPropertyName (propertyName );
619653
620- if (fieldToken . isOptionalEncoding ( ))
654+ if (wasAddedAfterParent . test ( fieldToken ))
621655 {
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 " )
625659 .append (indent ).append ("{\n " );
626660
627661 sb .append (indent ).append (INDENT ).append (dtoTypeName ).append (".decodeWith(decoder." )
@@ -710,8 +744,10 @@ private void generateGroupsDecodeWith(
710744
711745 private void generateVarDataDecodeWith (
712746 final StringBuilder sb ,
747+ final String decoderClassName ,
713748 final List <Token > tokens ,
714- final String indent )
749+ final String indent ,
750+ final Predicate <Token > wasAddedAfterParent )
715751 {
716752 for (int i = 0 ; i < tokens .size (); i ++)
717753 {
@@ -723,7 +759,7 @@ private void generateVarDataDecodeWith(
723759 final Token varDataToken = Generators .findFirst ("varData" , tokens , i );
724760 final String characterEncoding = varDataToken .encoding ().characterEncoding ();
725761
726- final boolean isOptional = token . version () > 0 ;
762+ final boolean isOptional = wasAddedAfterParent . test ( token ) ;
727763 final String blockIndent = isOptional ? indent + INDENT : indent ;
728764
729765 final String dataVar = toLowerFirstChar (propertyName ) + "Data" ;
@@ -734,7 +770,7 @@ private void generateVarDataDecodeWith(
734770 {
735771 decoderValueExtraction .append (blockIndent ).append ("byte[] " ).append (dataVar )
736772 .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 ) )
738774 .append ("(" ).append (dataVar ).append (", 0, decoder." ).append (formattedPropertyName )
739775 .append ("Length());\n " );
740776 }
@@ -746,17 +782,17 @@ private void generateVarDataDecodeWith(
746782
747783 if (isOptional )
748784 {
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 " )
752788 .append (indent ).append ("{\n " );
753789
754790 sb .append (decoderValueExtraction );
755791
756792 sb .append (indent ).append (INDENT ).append ("dto." ).append (formattedPropertyName ).append ("(" )
757793 .append (dataVar ).append (");\n " );
758794
759- final String nullDtoValue = "\" \" " ;
795+ final String nullDtoValue = characterEncoding == null ? "new byte[0]" : "\" \" " ;
760796
761797 sb .append (indent ).append ("}\n " )
762798 .append (indent ).append ("else\n " )
@@ -782,27 +818,25 @@ private void generateRecordPropertyAssignment(
782818 final String indent ,
783819 final String presenceExpression ,
784820 final CharSequence getExpression ,
785- final String nullDecoderValueOrNull )
821+ final String nullDecoderValue ,
822+ final Predicate <Token > wasAddedAfterParent )
786823 {
787824 final String propertyName = token .name ();
788825 final String formattedPropertyName = formatPropertyName (propertyName );
789826
790- if (token . isOptionalEncoding ( ))
827+ if (wasAddedAfterParent . test ( token ))
791828 {
792-
793829 sb .append (indent ).append ("if (" ).append (presenceExpression ).append (")\n " )
794830 .append (indent ).append ("{\n " );
795831
796832 sb .append (indent ).append (INDENT ).append ("dto." ).append (formattedPropertyName ).append ("(" )
797833 .append (getExpression ).append (");\n " );
798834
799- final String nullValue = nullDecoderValueOrNull == null ? "null" : nullDecoderValueOrNull ;
800-
801835 sb .append (indent ).append ("}\n " )
802836 .append (indent ).append ("else\n " )
803837 .append (indent ).append ("{\n " )
804838 .append (indent ).append (INDENT ).append ("dto." ).append (formattedPropertyName ).append ("(" )
805- .append (nullValue ).append (");\n " )
839+ .append (nullDecoderValue ).append (");\n " )
806840 .append (indent ).append ("}\n " );
807841 }
808842 else
@@ -966,19 +1000,16 @@ private void generateArrayEncodeWith(
9661000 final String propertyName = fieldToken .name ();
9671001 final String formattedPropertyName = formatPropertyName (propertyName );
9681002
969- if (typeToken .encoding ().primitiveType () == PrimitiveType .CHAR )
1003+ final PrimitiveType primitiveType = typeToken .encoding ().primitiveType ();
1004+
1005+ if (primitiveType == PrimitiveType .CHAR )
9701006 {
9711007 sb .append (indent ).append ("encoder." ).append (toLowerFirstChar (propertyName )).append ("(" )
9721008 .append ("dto." ).append (formattedPropertyName ).append ("());\n " );
9731009 }
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- }
9791010 else
9801011 {
981- final String javaTypeName = javaTypeName (typeToken . encoding (). primitiveType () );
1012+ final String javaTypeName = javaTypeName (primitiveType );
9821013 sb .append (indent ).append (javaTypeName ).append ("[] " ).append (formattedPropertyName ).append (" = " )
9831014 .append ("dto." ).append (formattedPropertyName ).append ("();\n " )
9841015 .append (indent ).append ("for (int i = 0; i < " ).append (formattedPropertyName ).append (".length; i++)\n " )
@@ -1103,7 +1134,9 @@ private void generateVarDataEncodeWith(
11031134 if (characterEncoding == null )
11041135 {
11051136 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 " );
11071140 }
11081141 else
11091142 {
@@ -1310,7 +1343,9 @@ private void generateArrayProperty(
13101343 final String fieldName = formatFieldName (propertyName );
13111344 final String validateMethod = "validate" + toUpperFirstChar (propertyName );
13121345
1313- if (typeToken .encoding ().primitiveType () == PrimitiveType .CHAR )
1346+ final PrimitiveType primitiveType = typeToken .encoding ().primitiveType ();
1347+
1348+ if (primitiveType == PrimitiveType .CHAR )
13141349 {
13151350 final CharSequence typeName = "String" ;
13161351
@@ -1345,7 +1380,7 @@ private void generateArrayProperty(
13451380 }
13461381 else
13471382 {
1348- final String elementTypeName = javaTypeName (typeToken . encoding (). primitiveType () );
1383+ final String elementTypeName = javaTypeName (primitiveType );
13491384 final String typeName = elementTypeName + "[]" ;
13501385
13511386 classBuilder .appendField ()
0 commit comments