Skip to content

Commit ac1f01d

Browse files
authored
Merge pull request #295 from SentryMan/constructor
Improve Constructor Detection
2 parents 029de5d + f662334 commit ac1f01d

File tree

4 files changed

+92
-34
lines changed

4 files changed

+92
-34
lines changed

jsonb-generator/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<name>jsonb generator</name>
1212
<description>annotation processor generating source code json adapters for avaje-jsonb</description>
1313
<properties>
14-
<avaje.prisms.version>1.31</avaje.prisms.version>
14+
<avaje.prisms.version>1.36</avaje.prisms.version>
1515
</properties>
1616

1717
<dependencies>

jsonb-generator/src/main/java/io/avaje/jsonb/generator/FieldReader.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ final class FieldReader {
1010

1111
private final Map<String, TypeSubTypeMeta> subTypes = new LinkedHashMap<>();
1212

13+
private final Element element;
1314
private final FieldProperty property;
1415
private final String propertyName;
1516
private final boolean serialize;
@@ -24,23 +25,24 @@ final class FieldReader {
2425
private boolean isCreatorParam;
2526

2627
FieldReader(
27-
Element element,
28-
NamingConvention namingConvention,
29-
TypeSubTypeMeta subType,
30-
List<String> genericTypeParams,
31-
Integer frequency) {
28+
Element element,
29+
NamingConvention namingConvention,
30+
TypeSubTypeMeta subType,
31+
List<String> genericTypeParams,
32+
Integer frequency) {
3233

3334
this(element, namingConvention, subType, genericTypeParams, frequency, false);
3435
}
3536

3637
FieldReader(
37-
Element element,
38-
NamingConvention namingConvention,
39-
TypeSubTypeMeta subType,
40-
List<String> genericTypeParams,
41-
Integer frequency,
42-
boolean jsonCreatorPresent) {
43-
38+
Element element,
39+
NamingConvention namingConvention,
40+
TypeSubTypeMeta subType,
41+
List<String> genericTypeParams,
42+
Integer frequency,
43+
boolean jsonCreatorPresent) {
44+
45+
this.element = element;
4446
num = frequency == 0 ? "" : frequency.toString();
4547
addSubType(subType);
4648
var isMethod = element instanceof ExecutableElement;
@@ -296,4 +298,7 @@ List<String> aliases() {
296298
return aliases;
297299
}
298300

301+
Element element() {
302+
return element;
303+
}
299304
}

jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import javax.lang.model.type.TypeMirror;
55
import javax.lang.model.util.ElementFilter;
66

7+
import static java.util.stream.Collectors.toSet;
8+
79
import java.util.*;
810
import java.util.function.Supplier;
911
import java.util.stream.Collectors;
@@ -41,11 +43,8 @@ final class TypeReader {
4143
private boolean nonAccessibleField;
4244

4345
private final Map<String, Element> mixInFields;
44-
4546
private final String typePropertyKey;
46-
4747
private final Map<String, Integer> frequencyMap = new HashMap<>();
48-
4948
private final List<MethodProperty> methodProperties = new ArrayList<>();
5049

5150
private boolean optional;
@@ -139,10 +138,9 @@ void read(TypeElement type) {
139138
for (var param : constructor.getParams()) {
140139
var name = param.name();
141140
var element = param.element();
142-
var matchingField =
143-
localFields.stream()
144-
.filter(f -> f.propertyName().equals(name) || f.fieldName().equals(name))
145-
.findFirst();
141+
var matchingField = localFields.stream()
142+
.filter(f -> f.propertyName().equals(name) || f.fieldName().equals(name))
143+
.findFirst();
146144
matchingField.ifPresentOrElse(f -> f.readParam(element), () -> readField(element, localFields));
147145
}
148146
}
@@ -161,7 +159,6 @@ void read(TypeElement type) {
161159
} else {
162160
commonField.addSubType(currentSubType);
163161
}
164-
165162
if (commonField == null && currentSubType != null) {
166163
localField.setSubTypeField();
167164
}
@@ -172,22 +169,19 @@ void read(TypeElement type) {
172169
private void readField(Element element, List<FieldReader> localFields) {
173170
final Element mixInField = mixInFields.get(element.getSimpleName().toString());
174171
if (mixInField != null && APContext.types().isSameType(mixInField.asType(), element.asType())) {
175-
176172
var mixinModifiers = new HashSet<>(mixInField.getModifiers());
177173
var modifiers = new HashSet<>(mixInField.getModifiers());
178174

179175
Arrays.stream(Modifier.values())
180-
.filter(m -> m != Modifier.PRIVATE || m != Modifier.PROTECTED || m != Modifier.PUBLIC)
181-
.forEach(
182-
m -> {
183-
modifiers.remove(m);
184-
mixinModifiers.remove(m);
185-
});
176+
.filter(m -> m != Modifier.PRIVATE || m != Modifier.PROTECTED || m != Modifier.PUBLIC)
177+
.forEach(m -> {
178+
modifiers.remove(m);
179+
mixinModifiers.remove(m);
180+
});
186181

187182
if (!modifiers.equals(mixinModifiers)) {
188183
APContext.logError(mixInField, "mixIn fields must have the same modifiers as the target class");
189184
}
190-
191185
element = mixInField;
192186
}
193187
if (element.asType().toString().contains("java.util.Optional")) {
@@ -304,13 +298,17 @@ private void matchFieldsToSetterOrConstructor() {
304298
}
305299

306300
private void matchFieldToSetter(FieldReader field) {
307-
if (!matchFieldToSetter2(field, false)
301+
if (hasNoSetter(field)) {
302+
logError("Non public field " + baseType + " " + field.fieldName() + " with no matching setter or constructor?");
303+
}
304+
}
305+
306+
private boolean hasNoSetter(FieldReader field) {
307+
return !matchFieldToSetter2(field, false)
308308
&& !matchFieldToSetter2(field, true)
309309
&& !matchFieldToSetterByParam(field)
310310
&& !field.isPublicField()
311-
&& !field.isSubTypeField()) {
312-
logError("Non public field " + baseType + " " + field.fieldName() + " with no matching setter or constructor?");
313-
}
311+
&& !field.isSubTypeField();
314312
}
315313

316314
private boolean matchFieldToSetterByParam(FieldReader field) {
@@ -458,7 +456,26 @@ private MethodReader determineConstructor() {
458456
// fallback to the single public constructor
459457
return allPublic.get(0);
460458
}
461-
// find the largest constructor
459+
460+
// find the right constructor
461+
var constructorFields = allFields.stream()
462+
.filter(FieldReader::includeFromJson)
463+
.filter(this::hasNoSetter)
464+
.map(f -> f.element().asType().toString())
465+
.map(Util::trimAnnotations)
466+
.collect(toSet());
467+
468+
return allPublic.stream()
469+
.filter(c -> c.getParams().size() == constructorFields.size())
470+
.filter(c -> c.getParams().stream()
471+
.map(p -> p.element().asType().toString())
472+
.map(Util::trimAnnotations)
473+
.allMatch(constructorFields::contains))
474+
.findFirst()
475+
.orElseGet(this::largest);
476+
}
477+
478+
private MethodReader largest() {
462479
int argCount = 0;
463480
MethodReader largestConstructor = null;
464481
for (MethodReader ctor : publicConstructors) {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.avaje.jsonb.generator.models.valid;
2+
3+
import java.util.List;
4+
5+
import io.avaje.jsonb.Json;
6+
7+
@Json
8+
public class MultiConstruct {
9+
10+
private final List<String> body;
11+
private String setter;
12+
13+
public MultiConstruct(String string) {
14+
this(List.of(string));
15+
}
16+
17+
public MultiConstruct(List<String> body) {
18+
this.body = body;
19+
}
20+
21+
public MultiConstruct(List<String> body, int somethin) {
22+
this.body = body;
23+
}
24+
25+
public String getSetter() {
26+
return setter;
27+
}
28+
29+
public void setSetter(String setter) {
30+
this.setter = setter;
31+
}
32+
33+
public List<String> getBody() {
34+
return body;
35+
}
36+
}

0 commit comments

Comments
 (0)