Skip to content

Commit 83c579f

Browse files
authored
Merge pull request #116 from SentryMan/jsonbGeneric
Support generic jsonb types
2 parents 5f46ea5 + e6069bb commit 83c579f

File tree

11 files changed

+167
-50
lines changed

11 files changed

+167
-50
lines changed

http-generator-client/src/main/java/io/avaje/http/generator/client/ClientMethodWriter.java

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import javax.lang.model.element.TypeElement;
66
import java.util.Set;
7+
import java.util.stream.Collectors;
78

89
/**
910
* Write code to register Web route for a given controller method.
@@ -22,24 +23,21 @@ class ClientMethodWriter {
2223
private final UType returnType;
2324
private MethodParam bodyHandlerParam;
2425
private String methodGenericParams = "";
26+
private final boolean useJsonb;
2527

26-
ClientMethodWriter(MethodReader method, Append writer, ProcessingContext ctx) {
28+
ClientMethodWriter(MethodReader method, Append writer, ProcessingContext ctx, boolean useJsonb) {
2729
this.method = method;
2830
this.writer = writer;
2931
this.webMethod = method.webMethod();
3032
this.ctx = ctx;
3133
this.returnType = Util.parseType(method.returnType());
34+
this.useJsonb = useJsonb;
3235
}
3336

3437
void addImportTypes(ControllerReader reader) {
3538
reader.addImportTypes(returnType.importTypes());
3639
for (final MethodParam param : method.params()) {
37-
final var type = param.utype();
38-
final var type0 = type.param0();
39-
final var type1 = type.param1();
40-
reader.addImportType(type.mainType().replace("[]", ""));
41-
if (type0 != null) reader.addImportType(type0.replace("[]", ""));
42-
if (type1 != null) reader.addImportType(type1.replace("[]", ""));
40+
reader.addImportTypes(param.utype().importTypes());
4341
}
4442
}
4543

@@ -116,37 +114,54 @@ private void writeEnd() {
116114

117115
private void writeSyncResponse() {
118116
writer.append(" ");
119-
String type0 = returnType.mainType();
120-
String type1 = returnType.param0();
121-
writeResponse(type0, type1);
117+
writeResponse(returnType);
122118
}
123119

124120
private void writeAsyncResponse() {
125121
writer.append(" .async()");
126-
String type0 = returnType.param0();
127-
String type1 = returnType.param1();
128-
writeResponse(type0, type1);
122+
writeResponse(returnType.paramRaw());
129123
}
130124

131125
private void writeCallResponse() {
132126
writer.append(" .call()");
133-
String type0 = returnType.param0();
134-
String type1 = returnType.param1();
135-
writeResponse(type0, type1);
127+
writeResponse(returnType.paramRaw());
136128
}
137129

138-
private void writeResponse(String type0, String type1) {
139-
if (isList(type0)) {
140-
writer.append(".list(%s.class);", Util.shortName(type1)).eol();
141-
} else if (isStream(type0)) {
142-
writer.append(".stream(%s.class);", Util.shortName(type1)).eol();
143-
} else if (isHttpResponse(type0)){
130+
private void writeResponse(UType type) {
131+
final var mainType = type.mainType();
132+
final var param1 = type.paramRaw();
133+
if (isList(mainType)) {
134+
writer.append(".list(");
135+
writeGeneric(param1);
136+
} else if (isStream(mainType)) {
137+
writer.append(".stream(");
138+
writeGeneric(param1);
139+
} else if (isHttpResponse(mainType)) {
144140
writeWithHandler();
145141
} else {
146-
writer.append(".bean(%s.class);", Util.shortName(type0)).eol();
142+
writer.append(".bean(");
143+
writeGeneric(type);
147144
}
148145
}
149146

147+
void writeGeneric(UType type) {
148+
if (useJsonb && type.isGeneric()) {
149+
final var params =
150+
type.importTypes().stream()
151+
.skip(1)
152+
.map(Util::shortName)
153+
.collect(Collectors.joining(".class, "));
154+
155+
writer.append(
156+
"Types.newParameterizedType(%s.class, %s.class)", Util.shortName(type.mainType()), params);
157+
} else {
158+
writer.append("%s.class", Util.shortName(type.mainType()));
159+
}
160+
writer.append(");");
161+
162+
writer.eol();
163+
}
164+
150165
private void writeWithHandler() {
151166
if (bodyHandlerParam != null) {
152167
writer.append(".handler(%s);", bodyHandlerParam.name()).eol();

http-generator-client/src/main/java/io/avaje/http/generator/client/ClientProcessor.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ public class ClientProcessor extends AbstractProcessor {
2727

2828
protected ProcessingContext ctx;
2929

30+
private final boolean useJsonB;
31+
32+
public ClientProcessor() {
33+
var jsonBOnClassPath = false;
34+
try {
35+
Class.forName("io.avaje.jsonb.Jsonb");
36+
jsonBOnClassPath = true;
37+
} catch (final ClassNotFoundException e) {
38+
}
39+
useJsonB = jsonBOnClassPath;
40+
}
41+
42+
public ClientProcessor(boolean useJsonb) {
43+
useJsonB = useJsonb;
44+
}
45+
3046
@Override
3147
public SourceVersion getSupportedSourceVersion() {
3248
return SourceVersion.latest();
@@ -107,7 +123,7 @@ private void writeClient(Element controller) {
107123
}
108124

109125
protected String writeClientAdapter(ProcessingContext ctx, ControllerReader reader) throws IOException {
110-
return new ClientWriter(reader, ctx).write();
126+
return new ClientWriter(reader, ctx, useJsonB).write();
111127
}
112128

113129
}

http-generator-client/src/main/java/io/avaje/http/generator/client/ClientWriter.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ class ClientWriter extends BaseControllerWriter {
2121
private static final String SUFFIX = "HttpClient";
2222

2323
private final List<ClientMethodWriter> methodList = new ArrayList<>();
24+
private final boolean useJsonb;
2425

25-
ClientWriter(ControllerReader reader, ProcessingContext ctx) throws IOException {
26+
ClientWriter(ControllerReader reader, ProcessingContext ctx, boolean useJsonB) throws IOException {
2627
super(reader, ctx, SUFFIX);
2728
reader.addImportType(HTTP_CLIENT_CONTEXT);
2829
reader.addImportType(HTTP_API_PROVIDER);
30+
this.useJsonb = useJsonB;
2931
readMethods();
32+
if (useJsonB) reader.addImportType("io.avaje.jsonb.Types");
3033
}
3134

3235
@Override
@@ -38,7 +41,7 @@ protected String initPackageName(String originName) {
3841
private void readMethods() {
3942
for (MethodReader method : reader.methods()) {
4043
if (method.isWebMethod()) {
41-
ClientMethodWriter methodWriter = new ClientMethodWriter(method, writer, ctx);
44+
final var methodWriter = new ClientMethodWriter(method, writer, ctx, useJsonb);
4245
methodWriter.addImportTypes(reader);
4346
methodList.add(methodWriter);
4447
}

http-generator-core/src/main/java/io/avaje/http/generator/core/JsonBUtil.java

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.LinkedHashMap;
44
import java.util.Map;
55
import java.util.function.Consumer;
6+
import java.util.stream.Collectors;
67

78
public class JsonBUtil {
89
private JsonBUtil() {}
@@ -40,23 +41,46 @@ public static void writeJsonbType(UType type, Append writer) {
4041

4142
writer.append(" this.%sJsonType = jsonB.type(", type.shortName());
4243
if (!type.isGeneric()) {
43-
writer.append("%s.class)", PrimitiveUtil.wrap(type.full()));
44+
writer.append("%s.class)", Util.shortName(PrimitiveUtil.wrap(type.full())));
4445
} else {
4546
switch (type.mainType()) {
4647
case "java.util.List":
47-
writer.append("%s.class).list()", type.param0());
48+
writeType(type.paramRaw(), writer);
49+
writer.append(".list()");
4850
break;
4951
case "java.util.Set":
50-
writer.append("%s.class).set()", type.param0());
52+
writeType(type.paramRaw(), writer);
53+
writer.append(".set()");
5154
break;
5255
case "java.util.Map":
53-
writer.append("%s.class).map()", type.param1());
56+
writeType(type.paramRaw(), writer);
57+
writer.append(".map()");
5458
break;
5559
default:
56-
throw new UnsupportedOperationException(
57-
"Only java.util Map, Set and List are supported JsonB Controller Collection Types");
60+
{
61+
if (type.mainType().contains("java.util"))
62+
throw new UnsupportedOperationException(
63+
"Only java.util Map, Set and List are supported JsonB Controller Collection Types");
64+
writeType(type, writer);
65+
}
5866
}
5967
}
6068
writer.append(";").eol();
6169
}
70+
71+
static void writeType(UType type, Append writer) {
72+
if (type.isGeneric()) {
73+
final var params =
74+
type.importTypes().stream()
75+
.skip(1)
76+
.map(Util::shortName)
77+
.collect(Collectors.joining(".class, "));
78+
79+
writer.append(
80+
"Types.newParameterizedType(%s.class, %s.class))",
81+
Util.shortName(type.mainType()), params);
82+
} else {
83+
writer.append("%s.class)", Util.shortName(type.mainType()));
84+
}
85+
}
6286
}

http-generator-core/src/main/java/io/avaje/http/generator/core/UType.java

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ public interface UType {
1111
static UType parse(TypeMirror type) {
1212
return Util.parseType(type);
1313
}
14+
/** Create the UType from the given String. */
15+
static UType parse(String type) {
16+
return Util.parse(type);
17+
}
1418

1519
UType VOID = new VoidType();
1620

@@ -48,9 +52,12 @@ default String param1() {
4852
return null;
4953
}
5054

51-
/**
52-
* Return the raw type.
53-
*/
55+
/** Return the raw generic parameter if this UType is a Collection. */
56+
default UType paramRaw() {
57+
return null;
58+
}
59+
60+
/** Return the raw type. */
5461
String full();
5562

5663
default boolean isGeneric() {
@@ -106,7 +113,9 @@ public String full() {
106113

107114
@Override
108115
public Set<String> importTypes() {
109-
return Collections.singleton(rawType);
116+
return rawType.startsWith("java.lang.") && rawType.indexOf('.') > -1
117+
? Set.of()
118+
: Collections.singleton(rawType.replace("[]", ""));
110119
}
111120

112121
@Override
@@ -130,15 +139,38 @@ public String mainType() {
130139
*/
131140
class Generic implements UType {
132141
final String rawType;
142+
final UType rawParamType;
133143
final List<String> allTypes;
134144
final String shortRawType;
135145
final String shortName;
136146

137147
Generic(String rawTypeInput) {
138-
this.rawType = rawTypeInput.replace(" ",""); // trim whitespace
148+
this.rawType = rawTypeInput.replace(" ", ""); // trim whitespace
139149
this.allTypes = Arrays.asList(rawType.split("[<|>|,]"));
140150
this.shortRawType = shortRawType(rawType, allTypes);
141151
this.shortName = Util.name(shortRawType);
152+
final var paramTypeString = extractRawParam();
153+
this.rawParamType = paramTypeString != null ? UType.parse(paramTypeString) : null;
154+
}
155+
156+
private String extractRawParam() {
157+
158+
switch (mainType()) {
159+
case "java.util.Set":
160+
case "java.util.List":
161+
case "java.util.stream.Stream":
162+
case "java.util.concurrent.CompletableFuture":
163+
case "io.avaje.http.client.HttpCall":
164+
var first = rawType.indexOf("<") + 1;
165+
var end = rawType.lastIndexOf(">");
166+
return rawType.substring(first, end);
167+
case "java.util.Map":
168+
first = rawType.indexOf(",") + 1;
169+
end = rawType.lastIndexOf(">");
170+
return rawType.substring(first, end);
171+
default:
172+
return null;
173+
}
142174
}
143175

144176
private String shortRawType(String rawType, List<String> allTypes) {
@@ -163,7 +195,7 @@ public Set<String> importTypes() {
163195
Set<String> set = new LinkedHashSet<>();
164196
for (String type : allTypes) {
165197
if (!type.startsWith("java.lang.") && type.indexOf('.') > -1) {
166-
set.add(type);
198+
set.add(type.replace("[]", ""));
167199
}
168200
}
169201
return set;
@@ -210,5 +242,10 @@ public String param0() {
210242
public String param1() {
211243
return allTypes.size() < 3 ? null : allTypes.get(2);
212244
}
245+
246+
@Override
247+
public UType paramRaw() {
248+
return rawParamType;
249+
}
213250
}
214251
}

http-generator-helidon/src/main/java/io/avaje/http/generator/helidon/ControllerMethodWriter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ private void writeContextReturn() {
103103
} else if (MediaType.TEXT_PLAIN.equalsIgnoreCase(produces)) {
104104
writer.append(" res.writerContext().contentType(io.helidon.common.http.MediaType.TEXT_PLAIN);").eol();
105105
} else {
106-
writer.append( "res.writerContext().contentType(io.helidon.common.http.MediaType.parse(\"%s\"));", produces).eol();
106+
writer.append(" res.writerContext().contentType(io.helidon.common.http.MediaType.parse(\"%s\"));", produces).eol();
107107
}
108108
}
109109

http-generator-javalin/src/main/java/io/avaje/http/generator/javalin/ControllerWriter.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package io.avaje.http.generator.javalin;
22

3-
import io.avaje.http.generator.core.*;
4-
53
import java.io.IOException;
64
import java.util.Map;
75

6+
import io.avaje.http.generator.core.BaseControllerWriter;
7+
import io.avaje.http.generator.core.Constants;
8+
import io.avaje.http.generator.core.ControllerReader;
9+
import io.avaje.http.generator.core.JsonBUtil;
10+
import io.avaje.http.generator.core.MethodReader;
11+
import io.avaje.http.generator.core.PrimitiveUtil;
12+
import io.avaje.http.generator.core.ProcessingContext;
13+
import io.avaje.http.generator.core.UType;
14+
815
/**
916
* Write Javalin specific Controller WebRoute handling adapter.
1017
*/
@@ -21,7 +28,11 @@ class ControllerWriter extends BaseControllerWriter {
2128
if (useJsonB) {
2229
reader.addImportType("io.avaje.jsonb.Jsonb");
2330
reader.addImportType("io.avaje.jsonb.JsonType");
31+
reader.addImportType("io.avaje.jsonb.Types");
2432
this.jsonTypes = JsonBUtil.jsonTypes(reader);
33+
jsonTypes.values().stream()
34+
.map(UType::importTypes)
35+
.forEach(reader::addImportTypes);
2536
} else {
2637
this.jsonTypes = Map.of();
2738
}
@@ -77,7 +88,9 @@ private void writeClassStart() {
7788
}
7889

7990
for (final UType type : jsonTypes.values()) {
80-
writer.append(" private final JsonType<%s> %sJsonType;", PrimitiveUtil.wrap(type.full()), type.shortName()).eol();
91+
final var typeString = PrimitiveUtil.wrap(type.shortType()).replace(",", ", ");
92+
93+
writer.append(" private final JsonType<%s> %sJsonType;", typeString, type.shortName()).eol();
8194
}
8295
writer.eol();
8396

http-generator-javalin/src/main/java/io/avaje/http/generator/javalin/JavalinProcessor.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@
99

1010
public class JavalinProcessor extends BaseProcessor {
1111

12-
private boolean useJsonB;
12+
private final boolean useJsonB;
1313

1414
public JavalinProcessor() {
15+
var jsonBOnClassPath = false;
1516
try {
1617
Class.forName("io.avaje.jsonb.Jsonb");
17-
this.useJsonB = true;
18+
jsonBOnClassPath = true;
1819
} catch (final ClassNotFoundException e) {
19-
this.useJsonB = false;
2020
}
21+
useJsonB = jsonBOnClassPath;
2122
}
2223

2324
public JavalinProcessor(boolean useJsonb) {

0 commit comments

Comments
 (0)