Skip to content

Commit 8503694

Browse files
authored
Add toString method generation (#399)
Signed-off-by: jasperpotts <jasperpotts@users.noreply.github.com> Co-authored-by: jasperpotts <jasperpotts@users.noreply.github.com>
1 parent 2ed3662 commit 8503694

File tree

3 files changed

+443
-69
lines changed

3 files changed

+443
-69
lines changed

pbj-core/pbj-compiler/src/main/java/com/hedera/pbj/compiler/impl/generators/ModelGenerator.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package com.hedera.pbj.compiler.impl.generators;
33

44
import static com.hedera.pbj.compiler.impl.Common.DEFAULT_INDENT;
5+
import static com.hedera.pbj.compiler.impl.Common.FIELD_INDENT;
56
import static com.hedera.pbj.compiler.impl.Common.camelToUpperSnake;
67
import static com.hedera.pbj.compiler.impl.Common.cleanDocStr;
78
import static com.hedera.pbj.compiler.impl.Common.cleanJavaDocComment;
@@ -39,7 +40,7 @@
3940
* Code generator that parses protobuf files and generates nice Java source for record files for each message type and
4041
* enum.
4142
*/
42-
@SuppressWarnings({"EscapedSpace"})
43+
@SuppressWarnings({"EscapedSpace", "StringConcatenationInLoop"})
4344
public final class ModelGenerator implements Generator {
4445

4546
private static final String NON_NULL_ANNOTATION = "@NonNull";
@@ -215,6 +216,11 @@ public void generate(
215216
if (hasComparableFields) {
216217
bodyContent += generateCompareTo(comparableFields, javaRecordName, destinationSrcDir);
217218
}
219+
bodyContent += "\n";
220+
221+
// toString method
222+
bodyContent += generateToString(javaRecordName, fieldsNoPrecomputed);
223+
bodyContent += "\n";
218224

219225
// Has methods
220226
bodyContent += String.join("\n", hasMethods);
@@ -225,7 +231,7 @@ public void generate(
225231
bodyContent += "\n";
226232

227233
// builder copy & new builder methods
228-
bodyContent = genrateBuilderFactoryMethods(bodyContent, fieldsNoPrecomputed);
234+
bodyContent = generateBuilderFactoryMethods(bodyContent, fieldsNoPrecomputed);
229235
bodyContent += "\n";
230236

231237
// generate builder
@@ -483,6 +489,45 @@ public int hashCode() {
483489
return bodyContent;
484490
}
485491

492+
/**
493+
* Generates the toString method, based on how Java records generate toStrings
494+
*
495+
* @param fields the fields to use for the code generation
496+
*
497+
* @return the generated code
498+
*/
499+
@NonNull
500+
private static String generateToString(final String modelClassName, final List<Field> fields) {
501+
// spotless:off
502+
String bodyContent =
503+
"""
504+
/**
505+
* Override the default toString method for $modelClassName to match the format of a Java record.
506+
*/
507+
@Override
508+
public String toString() {
509+
return "$modelClassName["
510+
""".replace("$modelClassName", modelClassName);
511+
// spotless:on
512+
for (int i = 0; i < fields.size(); i++) {
513+
Field f = fields.get(i);
514+
bodyContent +=
515+
FIELD_INDENT + FIELD_INDENT + "+ \"" + f.nameCamelFirstLower() + "=\" + " + f.nameCamelFirstLower();
516+
if (i < fields.size() - 1) {
517+
bodyContent += " + \", \"";
518+
}
519+
bodyContent += "\n";
520+
}
521+
// spotless:off
522+
bodyContent +=
523+
"""
524+
+"]";
525+
}
526+
""";
527+
// spotless:on
528+
return bodyContent.indent(DEFAULT_INDENT);
529+
}
530+
486531
/**
487532
* Generates a pre-populated constructor for a class.
488533
* @param fields the fields to use for the code generation
@@ -777,8 +822,15 @@ private static List<String> generateCodeForOneOf(
777822
return oneofGetters;
778823
}
779824

825+
/**
826+
* Generates the builder methods for the model class
827+
*
828+
* @param bodyContent the body content to append to
829+
* @param fields the fields to use for the code generation
830+
* @return the body content with new code appended
831+
*/
780832
@NonNull
781-
private static String genrateBuilderFactoryMethods(String bodyContent, final List<Field> fields) {
833+
private static String generateBuilderFactoryMethods(String bodyContent, final List<Field> fields) {
782834
// spotless:off
783835
bodyContent +=
784836
"""
@@ -807,6 +859,14 @@ public static Builder newBuilder() {
807859
return bodyContent;
808860
}
809861

862+
/**
863+
* Generates the builder class methods
864+
*
865+
* @param builderMethods the builder methods code to append to
866+
* @param msgDef the message definition
867+
* @param field the field to generate method for
868+
* @param lookupHelper the lookup helper
869+
*/
810870
private static void generateBuilderMethods(
811871
final List<String> builderMethods,
812872
final MessageDefContext msgDef,

pbj-integration-tests/src/main/java/com/hedera/pbj/integration/EverythingTestData.java

Lines changed: 75 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.hedera.pbj.test.proto.pbj.Suit;
88
import com.hedera.pbj.test.proto.pbj.TimestampTest;
99
import java.util.List;
10+
import java.util.Map;
1011
import java.util.stream.IntStream;
1112
import java.util.stream.LongStream;
1213

@@ -22,6 +23,67 @@ public EverythingTestData() {
2223
// no-op
2324
}
2425

26+
/**
27+
* Sample InnerEverything object for testing
28+
*/
29+
public static final InnerEverything INNER_EVERYTHING = new InnerEverything.Builder()
30+
.int32Number(1234)
31+
.sint32Number(-1234)
32+
.uint32Number(Integer.MAX_VALUE)
33+
.fixed32Number(644534)
34+
.sfixed32Number(-31345)
35+
.floatNumber(15834.213581f)
36+
.int64Number(53451121355L)
37+
.sint64Number(-53451121355L)
38+
.uint64Number(2451326663131L)
39+
.fixed64Number(33626188515L)
40+
.sfixed64Number(-531311551515L)
41+
.doubleNumber(135581531681.1535151)
42+
.booleanField(true)
43+
.enumSuit(Suit.SPADES)
44+
.subObject(
45+
new TimestampTest.Builder().seconds(5155135L).nanos(44513).build())
46+
.text("Hello Everything!")
47+
.bytesField(Bytes.wrap(new byte[] {12, 29, 19, 120, 127, 0, -127}))
48+
.int32NumberList(IntStream.range(0, 10).boxed().toList())
49+
.sint32NumberList(IntStream.range(-10, 10).boxed().toList())
50+
.uint32NumberList(IntStream.range(0, 100).boxed().toList())
51+
.fixed32NumberList(IntStream.range(0, 25).boxed().toList())
52+
.sfixed32NumberList(IntStream.range(-10, 25).boxed().toList())
53+
.floatNumberList(List.of(513.51f, 55535351.3545841f, 0f, -1f))
54+
.floatNumberList(List.of(513.51f, 55535351.3545841f, 0f, -1f))
55+
.int64NumberList(LongStream.range(0, 10).boxed().toList())
56+
.sint64NumberList(LongStream.range(-10, 10).boxed().toList())
57+
.uint64NumberList(LongStream.range(0, 10).boxed().toList())
58+
.fixed64NumberList(LongStream.range(0, 10).boxed().toList())
59+
.sfixed64NumberList(LongStream.range(-10, 10).boxed().toList())
60+
.doubleNumberList(List.of(513.51, 55535351.3545841, 0d, -1d))
61+
.booleanList(List.of(true, false, true, true, false))
62+
.enumSuitList(List.of(Suit.ACES, Suit.CLUBS, Suit.DIAMONDS))
63+
.subObjectList(List.of(
64+
new TimestampTest.Builder().seconds(5155135L).nanos(44513).build(),
65+
new TimestampTest.Builder().seconds(486486).nanos(31315).build(),
66+
new TimestampTest.Builder().seconds(0).nanos(58).build()))
67+
.textList(List.of(
68+
"صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ",
69+
"ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम, अयोध्या के महाराज दशरथ के बड़े सपुत्र थे।",
70+
"A quick brown fox jumps over the lazy dog"))
71+
.bytesExampleList(List.of(
72+
Bytes.wrap(new byte[] {12, 29, 19, 120, 127, 0, -127}),
73+
Bytes.wrap(new byte[] {13, 15, 65, 98, -65}),
74+
Bytes.wrap(new byte[] {127, 0, -127})))
75+
.int32Boxed(1234)
76+
.uint32Boxed(Integer.MAX_VALUE)
77+
.floatBoxed(15834.213581f)
78+
.int64Boxed(53451121355L)
79+
.uint64Boxed(2451326663131L)
80+
.doubleBoxed(135581531681.1535151)
81+
.boolBoxed(true)
82+
.bytesBoxed(Bytes.wrap(new byte[] {13, 15, 65, 98, -65}))
83+
.stringBoxed("Hello Everything!")
84+
.doubleNumberOneOf(29292.299d)
85+
.build();
86+
2587
/**
2688
* Sample Everything object for testing
2789
*/
@@ -44,6 +106,18 @@ public EverythingTestData() {
44106
new TimestampTest.Builder().seconds(5155135L).nanos(44513).build())
45107
.text("Hello Everything!")
46108
.bytesField(Bytes.wrap(new byte[] {12, 29, 19, 120, 127, 0, -127}))
109+
.innerEverything(INNER_EVERYTHING)
110+
.mapInt32ToString(Map.of(1, "One", 2, "Two", 3, "Three"))
111+
.mapBoolToDouble(Map.of(true, 100000000.0, false, 0.00000123))
112+
.mapStringToMessage(Map.of(
113+
"One", INNER_EVERYTHING,
114+
"Two", INNER_EVERYTHING.copyBuilder().int32Number(2).build(),
115+
"Three", INNER_EVERYTHING.copyBuilder().int32Number(3).build()))
116+
.mapUInt64ToBytes(Map.of(
117+
1L, Bytes.wrap(new byte[] {12, 29, 19, 120, 127, 0, -127}),
118+
2L, Bytes.wrap(new byte[] {13, 15, 65, 98, -65}),
119+
3L, Bytes.wrap(new byte[] {127, 0, -127})))
120+
.mapInt64ToBool(Map.of(1L, true, 2L, false, 3L, true))
47121
.int32NumberList(IntStream.range(0, 10).boxed().toList())
48122
.sint32NumberList(IntStream.range(-10, 10).boxed().toList())
49123
.uint32NumberList(IntStream.range(0, 100).boxed().toList())
@@ -73,78 +147,13 @@ public EverythingTestData() {
73147
Bytes.wrap(new byte[] {127, 0, -127})))
74148
.int32Boxed(1234)
75149
.uint32Boxed(Integer.MAX_VALUE)
76-
.floatBoxed(15834.213581f)
77150
.int64Boxed(53451121355L)
78151
.uint64Boxed(2451326663131L)
152+
.floatBoxed(15834.213581f)
79153
.doubleBoxed(135581531681.1535151)
80154
.boolBoxed(true)
81155
.bytesBoxed(Bytes.wrap(new byte[] {13, 15, 65, 98, -65}))
82156
.stringBoxed("Hello Everything!")
83157
.doubleNumberOneOf(29292.299d)
84-
.innerEverything(new InnerEverything.Builder()
85-
.int32Number(1234)
86-
.sint32Number(-1234)
87-
.uint32Number(Integer.MAX_VALUE)
88-
.fixed32Number(644534)
89-
.sfixed32Number(-31345)
90-
.floatNumber(15834.213581f)
91-
.int64Number(53451121355L)
92-
.sint64Number(-53451121355L)
93-
.uint64Number(2451326663131L)
94-
.fixed64Number(33626188515L)
95-
.sfixed64Number(-531311551515L)
96-
.doubleNumber(135581531681.1535151)
97-
.booleanField(true)
98-
.enumSuit(Suit.SPADES)
99-
.subObject(new TimestampTest.Builder()
100-
.seconds(5155135L)
101-
.nanos(44513)
102-
.build())
103-
.text("Hello Everything!")
104-
.bytesField(Bytes.wrap(new byte[] {12, 29, 19, 120, 127, 0, -127}))
105-
.int32NumberList(IntStream.range(0, 10).boxed().toList())
106-
.sint32NumberList(IntStream.range(-10, 10).boxed().toList())
107-
.uint32NumberList(IntStream.range(0, 100).boxed().toList())
108-
.fixed32NumberList(IntStream.range(0, 25).boxed().toList())
109-
.sfixed32NumberList(IntStream.range(-10, 25).boxed().toList())
110-
.floatNumberList(List.of(513.51f, 55535351.3545841f, 0f, -1f))
111-
.floatNumberList(List.of(513.51f, 55535351.3545841f, 0f, -1f))
112-
.int64NumberList(LongStream.range(0, 10).boxed().toList())
113-
.sint64NumberList(LongStream.range(-10, 10).boxed().toList())
114-
.uint64NumberList(LongStream.range(0, 10).boxed().toList())
115-
.fixed64NumberList(LongStream.range(0, 10).boxed().toList())
116-
.sfixed64NumberList(LongStream.range(-10, 10).boxed().toList())
117-
.doubleNumberList(List.of(513.51, 55535351.3545841, 0d, -1d))
118-
.booleanList(List.of(true, false, true, true, false))
119-
.enumSuitList(List.of(Suit.ACES, Suit.CLUBS, Suit.DIAMONDS))
120-
.subObjectList(List.of(
121-
new TimestampTest.Builder()
122-
.seconds(5155135L)
123-
.nanos(44513)
124-
.build(),
125-
new TimestampTest.Builder()
126-
.seconds(486486)
127-
.nanos(31315)
128-
.build(),
129-
new TimestampTest.Builder().seconds(0).nanos(58).build()))
130-
.textList(List.of(
131-
"صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ",
132-
"ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम, अयोध्या के महाराज दशरथ के बड़े सपुत्र थे।",
133-
"A quick brown fox jumps over the lazy dog"))
134-
.bytesExampleList(List.of(
135-
Bytes.wrap(new byte[] {12, 29, 19, 120, 127, 0, -127}),
136-
Bytes.wrap(new byte[] {13, 15, 65, 98, -65}),
137-
Bytes.wrap(new byte[] {127, 0, -127})))
138-
.int32Boxed(1234)
139-
.uint32Boxed(Integer.MAX_VALUE)
140-
.floatBoxed(15834.213581f)
141-
.int64Boxed(53451121355L)
142-
.uint64Boxed(2451326663131L)
143-
.doubleBoxed(135581531681.1535151)
144-
.boolBoxed(true)
145-
.bytesBoxed(Bytes.wrap(new byte[] {13, 15, 65, 98, -65}))
146-
.stringBoxed("Hello Everything!")
147-
.doubleNumberOneOf(29292.299d)
148-
.build())
149158
.build();
150159
}

0 commit comments

Comments
 (0)