Skip to content

Commit e5850b9

Browse files
Merge pull request #1101 from ddcruver/bugfix/fix-raw-type-builders
Fix raw type warnings when extending a builder
2 parents 08d9bf9 + 1b0ee47 commit e5850b9

File tree

6 files changed

+132
-36
lines changed

6 files changed

+132
-36
lines changed

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.util.Spliterator;
2424
import java.util.Spliterators;
2525
import java.util.stream.StreamSupport;
26-
import org.apache.commons.collections15.CollectionUtils;
26+
2727
import org.jsonschema2pojo.Schema;
2828

2929
import com.fasterxml.jackson.databind.JsonNode;
@@ -213,7 +213,7 @@ private JMethod addInnerBuilder(JDefinedClass jclass, JType propertyType, JField
213213
}
214214

215215
private String getBuilderClassName(JDefinedClass c) {
216-
return ruleFactory.getNameHelper().getBuilderClassName(c);
216+
return ruleFactory.getNameHelper().getBaseBuilderClassName(c);
217217
}
218218

219219
}

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/BuilderRule.java

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,33 @@ public class BuilderRule implements Rule<JDefinedClass, JDefinedClass> {
4848
public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDefinedClass instanceClass, Schema currentSchema) {
4949

5050
// Create the inner class for the builder
51+
JDefinedClass concreteBuilderClass;
5152
JDefinedClass builderClass;
5253

5354
try {
54-
String builderName = ruleFactory.getNameHelper().getBuilderClassName(instanceClass);
55-
builderClass = instanceClass._class(JMod.PUBLIC + JMod.STATIC, builderName);
55+
56+
String concreteBuilderClassName = ruleFactory.getNameHelper().getBuilderClassName(instanceClass);
57+
String builderClassName = ruleFactory.getNameHelper().getBaseBuilderClassName(instanceClass);
58+
59+
builderClass = instanceClass._class(JMod.ABSTRACT + JMod.PUBLIC + JMod.STATIC, builderClassName);
60+
61+
concreteBuilderClass = instanceClass._class(JMod.PUBLIC + JMod.STATIC, concreteBuilderClassName);
62+
concreteBuilderClass._extends(builderClass.narrow(instanceClass));
63+
5664
} catch (JClassAlreadyExistsException e) {
5765
return e.getExistingClass();
5866
}
5967

60-
// Determine which builder (if any) this builder should inherit from
68+
// Determine which base builder (if any) this builder should inherit from
6169
JClass parentBuilderClass = null;
6270
JClass parentClass = instanceClass._extends();
6371
if (!(parentClass.isPrimitive() || reflectionHelper.isFinal(parentClass) || Objects.equals(parentClass.fullName(), "java.lang.Object"))) {
64-
parentBuilderClass = reflectionHelper.getBuilderClass(parentClass);
72+
parentBuilderClass = reflectionHelper.getBaseBuilderClass(parentClass);
6573
}
6674

67-
// Determine the generic type 'T' that the builder will create instances of
68-
JTypeVar instanceType = builderClass.generify("T", instanceClass);
75+
// Determine the generic type name and that the builder will create instances of
76+
String builderTypeParameterName = ruleFactory.getNameHelper().getBuilderTypeParameterName(instanceClass);
77+
JTypeVar instanceType = builderClass.generify(builderTypeParameterName, instanceClass);
6978

7079
// For new builders we need to create an instance variable and 'build' method
7180
// for inheriting builders we'll receive these from the superType
@@ -83,31 +92,50 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef
8392
body.assign(JExpr._this().ref(instanceField), JExpr._null());
8493
body._return(result);
8594

86-
// Create the noargs builder constructor
87-
generateNoArgsBuilderConstructor(instanceClass, builderClass);
95+
// Create the noargs builder constructors
96+
generateNoArgsBuilderConstructors(instanceClass, builderClass, concreteBuilderClass);
8897
} else {
8998
// Declare the inheritance
90-
builderClass._extends(parentBuilderClass);
91-
99+
builderClass._extends(parentBuilderClass.narrow(parentBuilderClass.owner().ref(builderTypeParameterName)));
100+
92101
JMethod buildMethod = builderClass.method(JMod.PUBLIC, instanceType, "build");
93102
buildMethod.annotate(Override.class);
94103

95104
JBlock body = buildMethod.body();
96-
body._return(JExpr.cast(instanceType, JExpr._super().invoke("build")));
105+
body._return(JExpr._super().invoke("build"));
97106

98-
// Create the noargs builder constructor
99-
generateNoArgsBuilderConstructor(instanceClass, builderClass);
107+
// Create the noargs builder constructors
108+
generateNoArgsBuilderConstructors(instanceClass, builderClass, concreteBuilderClass);
100109
}
101110

102111
return builderClass;
103112
}
104113

105-
private void generateNoArgsBuilderConstructor(JDefinedClass instanceClass, JDefinedClass builderClass) {
106-
JMethod noargsConstructor = builderClass.constructor(JMod.PUBLIC);
107-
JAnnotationUse warningSuppression = noargsConstructor.annotate(SuppressWarnings.class);
114+
private void generateNoArgsBuilderConstructors(JDefinedClass instanceClass, JDefinedClass baseBuilderClass, JDefinedClass builderClass) {
115+
116+
generateNoArgsBaseBuilderConstructor(instanceClass, baseBuilderClass, builderClass);
117+
generateNoArgsBuilderConstructor(instanceClass, baseBuilderClass, builderClass);
118+
}
119+
120+
private void generateNoArgsBuilderConstructor(JDefinedClass instanceClass, JDefinedClass baseBuilderClass, JDefinedClass builderClass) {
121+
122+
// Add the constructor to builder.
123+
JMethod noArgsConstructor = builderClass.constructor(JMod.PUBLIC);
124+
JBlock constructorBlock = noArgsConstructor.body();
125+
126+
// Determine if we need to invoke the super() method for our parent builder
127+
if (!(baseBuilderClass.isPrimitive() || reflectionHelper.isFinal(baseBuilderClass) || Objects.equals(baseBuilderClass.fullName(), "java.lang.Object"))) {
128+
constructorBlock.invoke("super");
129+
}
130+
}
131+
132+
private void generateNoArgsBaseBuilderConstructor(JDefinedClass instanceClass, JDefinedClass builderClass, JDefinedClass concreteBuilderClass) {
133+
134+
JMethod noArgsConstructor = builderClass.constructor(JMod.PUBLIC);
135+
JAnnotationUse warningSuppression = noArgsConstructor.annotate(SuppressWarnings.class);
108136
warningSuppression.param("value", "unchecked");
109137

110-
JBlock constructorBlock = noargsConstructor.body();
138+
JBlock constructorBlock = noArgsConstructor.body();
111139

112140
JFieldVar instanceField = reflectionHelper.searchClassAndSuperClassesForField("instance", builderClass);
113141

@@ -120,9 +148,8 @@ private void generateNoArgsBuilderConstructor(JDefinedClass instanceClass, JDefi
120148
// Only initialize the instance if the object being constructed is actually this class
121149
// if it's a subtype then ignore the instance initialization since the subclass will initialize it
122150
constructorBlock.directStatement("// Skip initialization when called from subclass");
123-
JInvocation comparison = JExpr._this().invoke("getClass").invoke("equals").arg(JExpr.dotclass(builderClass));
151+
JInvocation comparison = JExpr._this().invoke("getClass").invoke("equals").arg(JExpr.dotclass(concreteBuilderClass));
124152
JConditional ifNotSubclass = constructorBlock._if(comparison);
125153
ifNotSubclass._then().assign(JExpr._this().ref(instanceField), JExpr.cast(instanceField.type(), JExpr._new(instanceClass)));
126154
}
127-
128155
}

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ConstructorRule.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,10 @@ private void addFieldsConstructor(JDefinedClass instanceClass, Set<String> class
147147

148148
// If we're using InnerClassBuilder implementations then we also need to generate those
149149
if (generationConfig.isGenerateBuilders() && generationConfig.isUseInnerClassBuilders()) {
150-
JDefinedClass builderClass = ruleFactory.getReflectionHelper()
151-
.getBuilderClass(instanceClass);
152-
generateFieldsBuilderConstructor(builderClass, instanceClass, instanceConstructor);
150+
JDefinedClass baseBuilderClass = ruleFactory.getReflectionHelper().getBaseBuilderClass(instanceClass);
151+
JDefinedClass concreteBuilderClass = ruleFactory.getReflectionHelper().getConcreteBuilderClass(instanceClass);
152+
153+
generateFieldsBuilderConstructor(baseBuilderClass, concreteBuilderClass, instanceClass, instanceConstructor);
153154
}
154155
}
155156

@@ -161,9 +162,10 @@ private void addCopyConstructor(JDefinedClass instanceClass, Set<String> classPr
161162

162163
// If we're using InnerClassBuilder implementations then we also need to generate those
163164
if (generationConfig.isGenerateBuilders() && generationConfig.isUseInnerClassBuilders()) {
164-
JDefinedClass builderClass = ruleFactory.getReflectionHelper()
165-
.getBuilderClass(instanceClass);
166-
generateFieldsBuilderConstructor(builderClass, instanceClass, instanceConstructor);
165+
JDefinedClass baseBuilderClass = ruleFactory.getReflectionHelper().getBaseBuilderClass(instanceClass);
166+
JDefinedClass concreteBuilderClass = ruleFactory.getReflectionHelper().getConcreteBuilderClass(instanceClass);
167+
168+
generateFieldsBuilderConstructor(baseBuilderClass, concreteBuilderClass, instanceClass, instanceConstructor);
167169
}
168170
}
169171

@@ -234,7 +236,8 @@ private LinkedHashSet<String> getSuperTypeConstructorPropertiesRecursive(JsonNod
234236
return rtn;
235237
}
236238

237-
private void generateFieldsBuilderConstructor(JDefinedClass builderClass, JDefinedClass instanceClass, JMethod instanceConstructor) {
239+
private void generateFieldsBuilderConstructor(JDefinedClass builderClass, JDefinedClass concreteBuilderClass, JDefinedClass instanceClass, JMethod instanceConstructor) {
240+
238241
// Locate the instance field since we'll need it to assign a value
239242
JFieldVar instanceField = reflectionHelper.searchClassAndSuperClassesForField("instance", builderClass);
240243

@@ -268,11 +271,34 @@ private void generateFieldsBuilderConstructor(JDefinedClass builderClass, JDefin
268271
JInvocation comparison = JExpr._this()
269272
.invoke("getClass")
270273
.invoke("equals")
271-
.arg(JExpr.dotclass(builderClass));
274+
.arg(JExpr.dotclass(concreteBuilderClass));
272275
JConditional ifNotSubclass = constructorBlock._if(comparison);
273276
ifNotSubclass._then()
274277
.assign(JExpr._this()
275278
.ref(instanceField), JExpr.cast(instanceField.type(), instanceConstructorInvocation));
279+
280+
generateFieldsConcreteBuilderConstructor(builderClass, concreteBuilderClass, instanceConstructor);
281+
282+
}
283+
284+
private void generateFieldsConcreteBuilderConstructor(JDefinedClass baseBuilderClass, JDefinedClass builderClass, JMethod instanceConstructor) {
285+
286+
// Create Typed Builder Constructor
287+
JMethod builderConstructor = builderClass.constructor(JMod.PUBLIC);
288+
JBlock builderConstructorBlock = builderConstructor.body();
289+
290+
// The typed builder constructor should have the exact same parameters as the inheritable builder.
291+
for (JVar param : instanceConstructor.params()) {
292+
builderConstructor.param(param.type(), param.name());
293+
}
294+
295+
if (!(baseBuilderClass.isPrimitive() || reflectionHelper.isFinal(baseBuilderClass) || Objects.equals(baseBuilderClass.fullName(), "java.lang.Object"))) {
296+
JInvocation superMethod = builderConstructorBlock.invoke("super");
297+
298+
for (JVar param : builderConstructor.params()) {
299+
superMethod.arg(param);
300+
}
301+
}
276302
}
277303

278304
private JMethod generateCopyConstructor(JDefinedClass jclass, Set<String> classProperties, Set<String> combinedSuperProperties) {

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ private JMethod addLegacyBuilder(JDefinedClass c, JFieldVar field, String jsonPr
262262
}
263263

264264
private JMethod addInnerBuilderMethod(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) {
265-
JDefinedClass builderClass = ruleFactory.getReflectionHelper().getBuilderClass(c);
265+
JDefinedClass builderClass = ruleFactory.getReflectionHelper().getBaseBuilderClass(c);
266266

267267
JMethod builderMethod = builderClass.method(JMod.PUBLIC, builderClass, getBuilderName(jsonPropertyName, node));
268268

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/NameHelper.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public NameHelper(GenerationConfig generationConfig) {
4242
this.generationConfig = generationConfig;
4343
}
4444

45+
public String getBuilderTypeParameterName(JDefinedClass instanceClass) {
46+
return "T";
47+
}
48+
4549
public String replaceIllegalCharacters(String name) {
4650
return name.replaceAll(ILLEGAL_CHARACTER_REGEX, "_");
4751
}
@@ -220,8 +224,20 @@ private String getPropertyNameForAccessor(String jsonPropertyName, JsonNode node
220224
return jsonPropertyName;
221225
}
222226

223-
public String getBuilderClassName(JClass outterClass) {
224-
return outterClass.name() + "Builder";
227+
public String getBaseBuilderClassName(JClass outerClass) {
228+
return outerClass.name() + getBuilderClassNameSuffix(outerClass) + getBaseBuilderClassNameSuffix(outerClass);
229+
}
230+
231+
public String getBaseBuilderClassNameSuffix(JClass outerClass) {
232+
return "Base";
233+
}
234+
235+
public String getBuilderClassName(JClass outerClass) {
236+
return outerClass.name() + getBuilderClassNameSuffix(outerClass);
237+
}
238+
239+
public String getBuilderClassNameSuffix(JClass outerClass) {
240+
return "Builder";
225241
}
226242

227243
public String getUniqueClassName(String nodeName, JsonNode node, JPackage _package) {

jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/ReflectionHelper.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Collection;
2929
import java.util.LinkedList;
3030
import java.util.Map;
31+
import java.util.Objects;
3132
import java.util.Spliterator;
3233
import java.util.Spliterators;
3334
import java.util.stream.StreamSupport;
@@ -90,15 +91,28 @@ public JFieldVar searchSuperClassesForField(String property, JDefinedClass jclas
9091
return searchClassAndSuperClassesForField(property, definedSuperClass);
9192
}
9293

93-
public JDefinedClass getBuilderClass(JDefinedClass target) {
94+
public JDefinedClass getConcreteBuilderClass(JDefinedClass instanceClass) {
95+
String builderClassname = ruleFactory.getNameHelper().getBuilderClassName(instanceClass);
96+
97+
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(instanceClass.classes(), Spliterator.ORDERED), false)
98+
.filter(definedClass -> definedClass.name().equals(builderClassname)).findFirst().orElse(null);
99+
}
100+
101+
public JDefinedClass getConcreteBuilderClass(JClass target) {
94102
String builderClassname = ruleFactory.getNameHelper().getBuilderClassName(target);
103+
return getAllPackageClasses(target._package()).stream().filter(definedClass -> definedClass.name().equals(builderClassname)).findFirst()
104+
.orElse(null);
105+
}
106+
107+
public JDefinedClass getBaseBuilderClass(JDefinedClass target) {
108+
String builderClassname = ruleFactory.getNameHelper().getBaseBuilderClassName(target);
95109

96110
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(target.classes(), Spliterator.ORDERED), false)
97111
.filter(definedClass -> definedClass.name().equals(builderClassname)).findFirst().orElse(null);
98112
}
99113

100-
public JDefinedClass getBuilderClass(JClass target) {
101-
String builderClassname = ruleFactory.getNameHelper().getBuilderClassName(target);
114+
public JDefinedClass getBaseBuilderClass(JClass target) {
115+
String builderClassname = ruleFactory.getNameHelper().getBaseBuilderClassName(target);
102116
return getAllPackageClasses(target._package()).stream().filter(definedClass -> definedClass.name().equals(builderClassname)).findFirst()
103117
.orElse(null);
104118
}
@@ -127,7 +141,20 @@ private JDefinedClass definedClassOrNullFromType(JType type) {
127141
}
128142
JClass fieldClass = type.boxify();
129143
JPackage jPackage = fieldClass._package();
130-
return this._getClass(fieldClass.name(), jPackage);
144+
try
145+
{
146+
return this._getClass(fieldClass.name(), jPackage);
147+
} catch (NoClassDefFoundError error) {
148+
String name = fieldClass.name();
149+
String erasureName = fieldClass.erasure().name();
150+
151+
if(!Objects.equals(name, erasureName)) {
152+
ruleFactory.getLogger().debug("Could not get class for type with name: " + name + " trying " + erasureName + " instead.");
153+
return this._getClass(erasureName, jPackage);
154+
} else {
155+
throw error;
156+
}
157+
}
131158
}
132159

133160
private JDefinedClass _getClass(String name, JPackage _package) {

0 commit comments

Comments
 (0)