Skip to content

Commit 3716f03

Browse files
committed
Test new constructor-based parsing behaviors via t1-t3 pipeline
Adds two new immutable POJO fixtures wired into TestRootObjectType so the t1 (expected roundtrip), t2 (generated-vs-expected), and t3 (generated roundtrip) scripts all exercise the new behaviors: - TestConstructorWithGenericsType — constructor params List<String>, Map<String,Integer>, Set<String>, int[], String[]. Covers stripGenerics matching plus the variableName overrides on CollectionFieldParser, MapFieldParser, and PrimitiveArrayFieldParser. - TestConstructorWithIgnoredFieldType — final @JsonIgnore field that is NOT a constructor parameter (derived from another). Covers getParseableFields skipping @JsonIgnore on the constructor-based path so the single-arg constructor matches the single parseable field. Plus a focused unit test CustomObjectFieldParserTest for the isJdkMap guard (raw java.util Maps don't route to a non-existent parser; user Map subclasses still pass through).
1 parent cdd1c49 commit 3716f03

7 files changed

Lines changed: 304 additions & 0 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package nl.aerius.codegen.generator.parser;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.HashMap;
7+
import java.util.LinkedHashMap;
8+
import java.util.Map;
9+
import java.util.TreeMap;
10+
11+
import org.junit.jupiter.api.Test;
12+
13+
/**
14+
* Tests the canHandle dispatch guards on CustomObjectFieldParser, in particular the
15+
* JDK-Map exclusion that prevents raw java.util Map types from routing to a
16+
* non-existent generated parser, while still letting user Map subclasses fall through
17+
* so they can be picked up by a custom parser.
18+
*/
19+
class CustomObjectFieldParserTest {
20+
21+
private final CustomObjectFieldParser parser = new CustomObjectFieldParser(new HashMap<>());
22+
23+
@Test
24+
void canHandleRejectsRawJdkMapTypes() {
25+
assertFalse(parser.canHandle(Map.class), "raw Map should not route to CustomObjectFieldParser");
26+
assertFalse(parser.canHandle(HashMap.class), "raw HashMap should not route to CustomObjectFieldParser");
27+
assertFalse(parser.canHandle(LinkedHashMap.class), "raw LinkedHashMap should not route to CustomObjectFieldParser");
28+
assertFalse(parser.canHandle(TreeMap.class), "raw TreeMap should not route to CustomObjectFieldParser");
29+
}
30+
31+
@Test
32+
void canHandleAcceptsUserMapSubclass() {
33+
assertTrue(parser.canHandle(UserMapSubclass.class),
34+
"user-defined Map subclass should fall through so a custom parser can handle it by simple name");
35+
}
36+
37+
/** User-defined Map subclass - by extending HashMap from a non-java.* package, it should not be excluded. */
38+
private static final class UserMapSubclass extends HashMap<String, String> {
39+
private static final long serialVersionUID = 1L;
40+
}
41+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package nl.aerius.codegen.test.types;
2+
3+
import java.util.HashSet;
4+
import java.util.LinkedHashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.Set;
8+
9+
/**
10+
* Immutable test type whose constructor parameters cover the generic-collection
11+
* and primitive-array cases that exercise:
12+
* - ConstructorAnalyzer matching List&lt;X&gt;/Map&lt;K,V&gt;/Set&lt;X&gt; source
13+
* types against raw reflection types (stripGenerics).
14+
* - CollectionFieldParser, MapFieldParser, and PrimitiveArrayFieldParser using
15+
* the field name for their result variable (variableName overload) so sibling
16+
* constructor params don't collide on level-based names.
17+
*/
18+
public class TestConstructorWithGenericsType {
19+
private final List<String> tags;
20+
private final Map<String, Integer> counts;
21+
private final Set<String> labels;
22+
private final int[] sizes;
23+
private final String[] aliases;
24+
25+
public TestConstructorWithGenericsType(final List<String> tags, final Map<String, Integer> counts,
26+
final Set<String> labels, final int[] sizes, final String[] aliases) {
27+
this.tags = tags;
28+
this.counts = counts;
29+
this.labels = labels;
30+
this.sizes = sizes;
31+
this.aliases = aliases;
32+
}
33+
34+
public List<String> getTags() {
35+
return tags;
36+
}
37+
38+
public Map<String, Integer> getCounts() {
39+
return counts;
40+
}
41+
42+
public Set<String> getLabels() {
43+
return labels;
44+
}
45+
46+
public int[] getSizes() {
47+
return sizes;
48+
}
49+
50+
public String[] getAliases() {
51+
return aliases;
52+
}
53+
54+
public static TestConstructorWithGenericsType createFullObject() {
55+
final Map<String, Integer> counts = new LinkedHashMap<>();
56+
counts.put("alpha", 1);
57+
counts.put("beta", 2);
58+
final Set<String> labels = new HashSet<>();
59+
labels.add("first");
60+
labels.add("second");
61+
return new TestConstructorWithGenericsType(
62+
List.of("a", "b", "c"),
63+
counts,
64+
labels,
65+
new int[] {10, 20, 30},
66+
new String[] {"x", "y"});
67+
}
68+
69+
public static TestConstructorWithGenericsType createNullObject() {
70+
return new TestConstructorWithGenericsType(null, null, null, null, null);
71+
}
72+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package nl.aerius.codegen.test.types;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
5+
/**
6+
* Immutable test type with a {@code @JsonIgnore} field that is NOT a constructor
7+
* parameter (it is derived from another field). Verifies that the constructor-based
8+
* code path skips {@code @JsonIgnore} fields in {@code getParseableFields}, so the
9+
* single-arg constructor matches the single parseable field {@code name} instead of
10+
* failing because the analyzer thinks there are two parseable fields.
11+
*
12+
* The {@code derivedHash} getter is annotated so Jackson also leaves it out of the
13+
* round-trip JSON.
14+
*/
15+
public class TestConstructorWithIgnoredFieldType {
16+
private final String name;
17+
18+
@JsonIgnore
19+
private final int derivedHash;
20+
21+
public TestConstructorWithIgnoredFieldType(final String name) {
22+
this.name = name;
23+
this.derivedHash = name == null ? 0 : name.hashCode();
24+
}
25+
26+
public String getName() {
27+
return name;
28+
}
29+
30+
@JsonIgnore
31+
public int getDerivedHash() {
32+
return derivedHash;
33+
}
34+
35+
public static TestConstructorWithIgnoredFieldType createFullObject() {
36+
return new TestConstructorWithIgnoredFieldType("ignored-field-test");
37+
}
38+
39+
public static TestConstructorWithIgnoredFieldType createNullObject() {
40+
return new TestConstructorWithIgnoredFieldType(null);
41+
}
42+
}

gwt-beans-codegen-core/src/test/java/nl/aerius/codegen/test/types/TestRootObjectType.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public class TestRootObjectType {
2323
private TestPolyBase testPolyBase;
2424
private TestPrimitiveArrayType primitiveArrays;
2525
private TestConstructorBasedType constructorBased;
26+
private TestConstructorWithGenericsType constructorWithGenerics;
27+
private TestConstructorWithIgnoredFieldType constructorWithIgnoredField;
2628

2729
public String getFoo() {
2830
return foo;
@@ -144,6 +146,22 @@ public void setConstructorBased(TestConstructorBasedType constructorBased) {
144146
this.constructorBased = constructorBased;
145147
}
146148

149+
public TestConstructorWithGenericsType getConstructorWithGenerics() {
150+
return constructorWithGenerics;
151+
}
152+
153+
public void setConstructorWithGenerics(TestConstructorWithGenericsType constructorWithGenerics) {
154+
this.constructorWithGenerics = constructorWithGenerics;
155+
}
156+
157+
public TestConstructorWithIgnoredFieldType getConstructorWithIgnoredField() {
158+
return constructorWithIgnoredField;
159+
}
160+
161+
public void setConstructorWithIgnoredField(TestConstructorWithIgnoredFieldType constructorWithIgnoredField) {
162+
this.constructorWithIgnoredField = constructorWithIgnoredField;
163+
}
164+
147165
public static TestRootObjectType createFullObject() {
148166
TestRootObjectType obj = new TestRootObjectType();
149167
obj.setFoo("test string");
@@ -161,6 +179,8 @@ public static TestRootObjectType createFullObject() {
161179
obj.setTestPolyBase(new TestPolySubA("BaseValueA", 123));
162180
obj.setPrimitiveArrays(TestPrimitiveArrayType.createFullObject());
163181
obj.setConstructorBased(TestConstructorBasedType.createFullObject());
182+
obj.setConstructorWithGenerics(TestConstructorWithGenericsType.createFullObject());
183+
obj.setConstructorWithIgnoredField(TestConstructorWithIgnoredFieldType.createFullObject());
164184
return obj;
165185
}
166186

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package nl.aerius.codegen.test.generated;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashSet;
5+
import java.util.LinkedHashMap;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.Set;
9+
10+
import javax.annotation.processing.Generated;
11+
12+
import nl.aerius.codegen.test.types.TestConstructorWithGenericsType;
13+
import nl.aerius.json.JSONArrayHandle;
14+
import nl.aerius.json.JSONObjectHandle;
15+
16+
@Generated(value = "nl.aerius.codegen.ParserGenerator", date = "2024-01-01T00:00:00")
17+
public class TestConstructorWithGenericsTypeParser {
18+
public static TestConstructorWithGenericsType parse(final String jsonText) {
19+
if (jsonText == null) {
20+
return null;
21+
}
22+
23+
return parse(JSONObjectHandle.fromText(jsonText));
24+
}
25+
26+
public static TestConstructorWithGenericsType parse(final JSONObjectHandle baseObj) {
27+
if (baseObj == null) {
28+
return null;
29+
}
30+
31+
// Parse tags
32+
if (!baseObj.has("tags")) {
33+
throw new RuntimeException("Required field 'tags' is missing");
34+
}
35+
final JSONArrayHandle tagsArray = baseObj.getArray("tags");
36+
final List<String> tags = new ArrayList<>();
37+
tagsArray.forEachString(tags::add);
38+
39+
// Parse counts
40+
if (!baseObj.has("counts")) {
41+
throw new RuntimeException("Required field 'counts' is missing");
42+
}
43+
final JSONObjectHandle obj = baseObj.getObject("counts");
44+
final Map<String, Integer> counts = new LinkedHashMap<>();
45+
obj.keySet().forEach(key -> {
46+
final Integer level2Value = obj.getInteger(key);
47+
counts.put(key, level2Value);
48+
});
49+
50+
// Parse labels
51+
if (!baseObj.has("labels")) {
52+
throw new RuntimeException("Required field 'labels' is missing");
53+
}
54+
final JSONArrayHandle labelsArray = baseObj.getArray("labels");
55+
final Set<String> labels = new HashSet<>();
56+
labelsArray.forEachString(labels::add);
57+
58+
// Parse sizes
59+
if (!baseObj.has("sizes")) {
60+
throw new RuntimeException("Required field 'sizes' is missing");
61+
}
62+
int[] sizes = null;
63+
final JSONArrayHandle sizesJsonArray = baseObj.getArray("sizes");
64+
if (sizesJsonArray != null) {
65+
final List<Integer> sizesTempList = new ArrayList<>();
66+
sizesJsonArray.forEachInteger(sizesTempList::add);
67+
sizes = sizesTempList.stream().mapToInt(i -> i != null ? i.intValue() : 0).toArray();
68+
}
69+
70+
// Parse aliases
71+
if (!baseObj.has("aliases")) {
72+
throw new RuntimeException("Required field 'aliases' is missing");
73+
}
74+
String[] aliases = null;
75+
final JSONArrayHandle aliasesJsonArray = baseObj.getArray("aliases");
76+
if (aliasesJsonArray != null) {
77+
final List<String> aliasesTempList = new ArrayList<>();
78+
aliasesJsonArray.forEachString(aliasesTempList::add);
79+
aliases = aliasesTempList.toArray(new String[0]);
80+
}
81+
82+
return new TestConstructorWithGenericsType(tags, counts, labels, sizes, aliases);
83+
}
84+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package nl.aerius.codegen.test.generated;
2+
3+
import javax.annotation.processing.Generated;
4+
5+
import nl.aerius.codegen.test.types.TestConstructorWithIgnoredFieldType;
6+
import nl.aerius.json.JSONObjectHandle;
7+
8+
@Generated(value = "nl.aerius.codegen.ParserGenerator", date = "2024-01-01T00:00:00")
9+
public class TestConstructorWithIgnoredFieldTypeParser {
10+
public static TestConstructorWithIgnoredFieldType parse(final String jsonText) {
11+
if (jsonText == null) {
12+
return null;
13+
}
14+
15+
return parse(JSONObjectHandle.fromText(jsonText));
16+
}
17+
18+
public static TestConstructorWithIgnoredFieldType parse(final JSONObjectHandle baseObj) {
19+
if (baseObj == null) {
20+
return null;
21+
}
22+
23+
// Parse name
24+
if (!baseObj.has("name")) {
25+
throw new RuntimeException("Required field 'name' is missing");
26+
}
27+
final String name = baseObj.getString("name");
28+
29+
return new TestConstructorWithIgnoredFieldType(name);
30+
}
31+
}

gwt-beans-codegen-core/src/test/resources/parsers/expected/TestRootObjectTypeParser.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import nl.aerius.codegen.test.types.TestAdvancedMapType;
88
import nl.aerius.codegen.test.types.TestComplexCollectionType;
99
import nl.aerius.codegen.test.types.TestConstructorBasedType;
10+
import nl.aerius.codegen.test.types.TestConstructorWithGenericsType;
11+
import nl.aerius.codegen.test.types.TestConstructorWithIgnoredFieldType;
1012
import nl.aerius.codegen.test.types.TestCustomParserType;
1113
import nl.aerius.codegen.test.types.TestEnumListType;
1214
import nl.aerius.codegen.test.types.TestEnumType;
@@ -132,5 +134,17 @@ public static void parse(final JSONObjectHandle baseObj, final TestRootObjectTyp
132134
final TestConstructorBasedType value = TestConstructorBasedTypeParser.parse(baseObj.getObject("constructorBased"));
133135
config.setConstructorBased(value);
134136
}
137+
138+
// Parse constructorWithGenerics
139+
if (baseObj.has("constructorWithGenerics") && !baseObj.isNull("constructorWithGenerics")) {
140+
final TestConstructorWithGenericsType value = TestConstructorWithGenericsTypeParser.parse(baseObj.getObject("constructorWithGenerics"));
141+
config.setConstructorWithGenerics(value);
142+
}
143+
144+
// Parse constructorWithIgnoredField
145+
if (baseObj.has("constructorWithIgnoredField") && !baseObj.isNull("constructorWithIgnoredField")) {
146+
final TestConstructorWithIgnoredFieldType value = TestConstructorWithIgnoredFieldTypeParser.parse(baseObj.getObject("constructorWithIgnoredField"));
147+
config.setConstructorWithIgnoredField(value);
148+
}
135149
}
136150
}

0 commit comments

Comments
 (0)