Skip to content
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,13 @@ If [Avaje-Jsonb](https://github.com/avaje/avaje-jsonb) is detected, http generat
public class WidgetController$Route implements WebRoutes {

private final WidgetController controller;
private final JsonType<java.util.List<org.example.hello.WidgetController.Widget>> listWidgetJsonType;
private final JsonType<org.example.hello.WidgetController.Widget> widgetJsonType;
private final JsonType<List<Widget>> listWidgetJsonType;
private final JsonType<Widget> widgetJsonType;

public WidgetController$Route(WidgetController controller, Jsonb jsonB) {
this.controller = controller;
this.listWidgetJsonType = jsonB.type(org.example.hello.WidgetController.Widget.class).list();
this.widgetJsonType = jsonB.type(org.example.hello.WidgetController.Widget.class);
this.listWidgetJsonType = jsonB.type(Widget.class).list();
this.widgetJsonType = jsonB.type(Widget.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import javax.lang.model.element.TypeElement;
import java.util.Set;
import java.util.stream.Collectors;

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

ClientMethodWriter(MethodReader method, Append writer, ProcessingContext ctx) {
ClientMethodWriter(MethodReader method, Append writer, ProcessingContext ctx, boolean useJsonb) {
this.method = method;
this.writer = writer;
this.webMethod = method.webMethod();
this.ctx = ctx;
this.returnType = Util.parseType(method.returnType());
this.useJsonb = useJsonb;
}

void addImportTypes(ControllerReader reader) {
reader.addImportTypes(returnType.importTypes());
for (final MethodParam param : method.params()) {
final var type = param.utype();
final var type0 = type.param0();
final var type1 = type.param1();
reader.addImportType(type.mainType().replace("[]", ""));
if (type0 != null) reader.addImportType(type0.replace("[]", ""));
if (type1 != null) reader.addImportType(type1.replace("[]", ""));
reader.addImportTypes(param.utype().importTypes());
}
}

Expand Down Expand Up @@ -116,37 +114,54 @@ private void writeEnd() {

private void writeSyncResponse() {
writer.append(" ");
String type0 = returnType.mainType();
String type1 = returnType.param0();
writeResponse(type0, type1);
writeResponse(returnType);
}

private void writeAsyncResponse() {
writer.append(" .async()");
String type0 = returnType.param0();
String type1 = returnType.param1();
writeResponse(type0, type1);
writeResponse(returnType.paramRaw());
}

private void writeCallResponse() {
writer.append(" .call()");
String type0 = returnType.param0();
String type1 = returnType.param1();
writeResponse(type0, type1);
writeResponse(returnType.paramRaw());
}

private void writeResponse(String type0, String type1) {
if (isList(type0)) {
writer.append(".list(%s.class);", Util.shortName(type1)).eol();
} else if (isStream(type0)) {
writer.append(".stream(%s.class);", Util.shortName(type1)).eol();
} else if (isHttpResponse(type0)){
private void writeResponse(UType type) {
final var mainType = type.mainType();
final var param1 = type.paramRaw();
if (isList(mainType)) {
writer.append(".list(");
writeGeneric(param1);
} else if (isStream(mainType)) {
writer.append(".stream(");
writeGeneric(param1);
} else if (isHttpResponse(mainType)) {
writeWithHandler();
} else {
writer.append(".bean(%s.class);", Util.shortName(type0)).eol();
writer.append(".bean(");
writeGeneric(type);
}
}

void writeGeneric(UType type) {
if (useJsonb && type.isGeneric()) {
final var params =
type.importTypes().stream()
.skip(1)
.map(Util::shortName)
.collect(Collectors.joining(".class, "));

writer.append(
"Types.newParameterizedType(%s.class, %s.class)", Util.shortName(type.mainType()), params);
} else {
writer.append("%s.class", Util.shortName(type.mainType()));
}
writer.append(");");

writer.eol();
}

private void writeWithHandler() {
if (bodyHandlerParam != null) {
writer.append(".handler(%s);", bodyHandlerParam.name()).eol();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ public class ClientProcessor extends AbstractProcessor {

protected ProcessingContext ctx;

private final boolean useJsonB;

public ClientProcessor() {
var jsonBOnClassPath = false;
try {
Class.forName("io.avaje.jsonb.Jsonb");
jsonBOnClassPath = true;
} catch (final ClassNotFoundException e) {
}
useJsonB = jsonBOnClassPath;
}

public ClientProcessor(boolean useJsonb) {
useJsonB = useJsonb;
}

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
Expand Down Expand Up @@ -107,7 +123,7 @@ private void writeClient(Element controller) {
}

protected String writeClientAdapter(ProcessingContext ctx, ControllerReader reader) throws IOException {
return new ClientWriter(reader, ctx).write();
return new ClientWriter(reader, ctx, useJsonB).write();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ class ClientWriter extends BaseControllerWriter {
private static final String SUFFIX = "HttpClient";

private final List<ClientMethodWriter> methodList = new ArrayList<>();
private final boolean useJsonb;

ClientWriter(ControllerReader reader, ProcessingContext ctx) throws IOException {
ClientWriter(ControllerReader reader, ProcessingContext ctx, boolean useJsonB) throws IOException {
super(reader, ctx, SUFFIX);
reader.addImportType(HTTP_CLIENT_CONTEXT);
reader.addImportType(HTTP_API_PROVIDER);
this.useJsonb = useJsonB;
readMethods();
if (useJsonB) reader.addImportType("io.avaje.jsonb.Types");
}

@Override
Expand All @@ -38,7 +41,7 @@ protected String initPackageName(String originName) {
private void readMethods() {
for (MethodReader method : reader.methods()) {
if (method.isWebMethod()) {
ClientMethodWriter methodWriter = new ClientMethodWriter(method, writer, ctx);
final var methodWriter = new ClientMethodWriter(method, writer, ctx, useJsonb);
methodWriter.addImportTypes(reader);
methodList.add(methodWriter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class JsonBUtil {
private JsonBUtil() {}
Expand Down Expand Up @@ -40,23 +41,46 @@ public static void writeJsonbType(UType type, Append writer) {

writer.append(" this.%sJsonType = jsonB.type(", type.shortName());
if (!type.isGeneric()) {
writer.append("%s.class)", PrimitiveUtil.wrap(type.full()));
writer.append("%s.class)", Util.shortName(PrimitiveUtil.wrap(type.full())));
} else {
switch (type.mainType()) {
case "java.util.List":
writer.append("%s.class).list()", type.param0());
writeType(type.paramRaw(), writer);
writer.append(".list()");
break;
case "java.util.Set":
writer.append("%s.class).set()", type.param0());
writeType(type.paramRaw(), writer);
writer.append(".set()");
break;
case "java.util.Map":
writer.append("%s.class).map()", type.param1());
writeType(type.paramRaw(), writer);
writer.append(".map()");
break;
default:
throw new UnsupportedOperationException(
"Only java.util Map, Set and List are supported JsonB Controller Collection Types");
{
if (type.mainType().contains("java.util"))
throw new UnsupportedOperationException(
"Only java.util Map, Set and List are supported JsonB Controller Collection Types");
writeType(type, writer);
}
}
}
writer.append(";").eol();
}

static void writeType(UType type, Append writer) {
if (type.isGeneric()) {
final var params =
type.importTypes().stream()
.skip(1)
.map(Util::shortName)
.collect(Collectors.joining(".class, "));

writer.append(
"Types.newParameterizedType(%s.class, %s.class))",
Util.shortName(type.mainType()), params);
} else {
writer.append("%s.class)", Util.shortName(type.mainType()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public interface UType {
static UType parse(TypeMirror type) {
return Util.parseType(type);
}
/** Create the UType from the given String. */
static UType parse(String type) {
return Util.parse(type);
}

UType VOID = new VoidType();

Expand Down Expand Up @@ -48,9 +52,12 @@ default String param1() {
return null;
}

/**
* Return the raw type.
*/
/** Return the raw generic parameter if this UType is a Collection. */
default UType paramRaw() {
return null;
}

/** Return the raw type. */
String full();

default boolean isGeneric() {
Expand Down Expand Up @@ -106,7 +113,9 @@ public String full() {

@Override
public Set<String> importTypes() {
return Collections.singleton(rawType);
return rawType.startsWith("java.lang.") && rawType.indexOf('.') > -1
? Set.of()
: Collections.singleton(rawType.replace("[]", ""));
}

@Override
Expand All @@ -130,15 +139,38 @@ public String mainType() {
*/
class Generic implements UType {
final String rawType;
final UType rawParamType;
final List<String> allTypes;
final String shortRawType;
final String shortName;

Generic(String rawTypeInput) {
this.rawType = rawTypeInput.replace(" ",""); // trim whitespace
this.rawType = rawTypeInput.replace(" ", ""); // trim whitespace
this.allTypes = Arrays.asList(rawType.split("[<|>|,]"));
this.shortRawType = shortRawType(rawType, allTypes);
this.shortName = Util.name(shortRawType);
final var paramTypeString = extractRawParam();
this.rawParamType = paramTypeString != null ? UType.parse(paramTypeString) : null;
}

private String extractRawParam() {

switch (mainType()) {
case "java.util.Set":
case "java.util.List":
case "java.util.stream.Stream":
case "java.util.concurrent.CompletableFuture":
case "io.avaje.http.client.HttpCall":
var first = rawType.indexOf("<") + 1;
var end = rawType.lastIndexOf(">");
return rawType.substring(first, end);
case "java.util.Map":
first = rawType.indexOf(",") + 1;
end = rawType.lastIndexOf(">");
return rawType.substring(first, end);
default:
return null;
}
}

private String shortRawType(String rawType, List<String> allTypes) {
Expand All @@ -163,7 +195,7 @@ public Set<String> importTypes() {
Set<String> set = new LinkedHashSet<>();
for (String type : allTypes) {
if (!type.startsWith("java.lang.") && type.indexOf('.') > -1) {
set.add(type);
set.add(type.replace("[]", ""));
}
}
return set;
Expand Down Expand Up @@ -210,5 +242,10 @@ public String param0() {
public String param1() {
return allTypes.size() < 3 ? null : allTypes.get(2);
}

@Override
public UType paramRaw() {
return rawParamType;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private void writeContextReturn() {
} else if (MediaType.TEXT_PLAIN.equalsIgnoreCase(produces)) {
writer.append(" res.writerContext().contentType(io.helidon.common.http.MediaType.TEXT_PLAIN);").eol();
} else {
writer.append( "res.writerContext().contentType(io.helidon.common.http.MediaType.parse(\"%s\"));", produces).eol();
writer.append(" res.writerContext().contentType(io.helidon.common.http.MediaType.parse(\"%s\"));", produces).eol();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package io.avaje.http.generator.javalin;

import io.avaje.http.generator.core.*;

import java.io.IOException;
import java.util.Map;

import io.avaje.http.generator.core.BaseControllerWriter;
import io.avaje.http.generator.core.Constants;
import io.avaje.http.generator.core.ControllerReader;
import io.avaje.http.generator.core.JsonBUtil;
import io.avaje.http.generator.core.MethodReader;
import io.avaje.http.generator.core.PrimitiveUtil;
import io.avaje.http.generator.core.ProcessingContext;
import io.avaje.http.generator.core.UType;

/**
* Write Javalin specific Controller WebRoute handling adapter.
*/
Expand All @@ -21,7 +28,11 @@ class ControllerWriter extends BaseControllerWriter {
if (useJsonB) {
reader.addImportType("io.avaje.jsonb.Jsonb");
reader.addImportType("io.avaje.jsonb.JsonType");
reader.addImportType("io.avaje.jsonb.Types");
this.jsonTypes = JsonBUtil.jsonTypes(reader);
jsonTypes.values().stream()
.map(UType::importTypes)
.forEach(reader::addImportTypes);
} else {
this.jsonTypes = Map.of();
}
Expand Down Expand Up @@ -77,7 +88,9 @@ private void writeClassStart() {
}

for (final UType type : jsonTypes.values()) {
writer.append(" private final JsonType<%s> %sJsonType;", PrimitiveUtil.wrap(type.full()), type.shortName()).eol();
final var typeString = PrimitiveUtil.wrap(type.shortType()).replace(",", ", ");

writer.append(" private final JsonType<%s> %sJsonType;", typeString, type.shortName()).eol();
}
writer.eol();

Expand Down
Loading