11package io .avaje .http .generator .helidon .nima ;
22
33import java .io .IOException ;
4+ import java .util .AbstractMap .SimpleImmutableEntry ;
5+ import java .util .HashMap ;
46import java .util .List ;
7+ import java .util .Map ;
58import java .util .Optional ;
69
710import javax .lang .model .type .DeclaredType ;
11+ import javax .lang .model .type .PrimitiveType ;
812
913import io .avaje .http .generator .core .BaseControllerWriter ;
1014import io .avaje .http .generator .core .Constants ;
1115import io .avaje .http .generator .core .ControllerReader ;
1216import io .avaje .http .generator .core .MethodParam ;
1317import io .avaje .http .generator .core .MethodReader ;
1418import io .avaje .http .generator .core .ProcessingContext ;
19+ import io .avaje .http .generator .core .UType ;
1520
1621/** Write Helidon specific web route adapter (a Helidon Service). */
1722class ControllerWriter extends BaseControllerWriter {
1823
1924 private static final String AT_GENERATED = "@Generated(\" avaje-helidon-nima-generator\" )" ;
2025 private final boolean useJsonB ;
26+ private final Map <String , SimpleImmutableEntry <String , String >> jsonTypes ;
2127
22- ControllerWriter (ControllerReader reader , ProcessingContext ctx , boolean jsonB ) throws IOException {
28+ ControllerWriter (ControllerReader reader , ProcessingContext ctx , boolean jsonB )
29+ throws IOException {
2330 super (reader , ctx );
24- this . useJsonB = jsonB ;
31+ useJsonB = jsonB ;
2532 if (useJsonB ) {
2633 reader .addImportType ("io.avaje.jsonb.Jsonb" );
2734 reader .addImportType ("io.avaje.jsonb.JsonType" );
35+ jsonTypes = new HashMap <>();
36+ } else {
37+ jsonTypes = null ;
2838 }
2939 reader .addImportType ("io.helidon.common.http.HttpMediaType" );
3040 reader .addImportType ("io.helidon.common.parameters.Parameters" );
@@ -46,7 +56,7 @@ void write() {
4656 private List <ControllerMethodWriter > getWriterMethods () {
4757 return reader .getMethods ().stream ()
4858 .filter (MethodReader ::isWebMethod )
49- .map (it -> new ControllerMethodWriter (it , writer , ctx , useJsonB ))
59+ .map (it -> new ControllerMethodWriter (it , writer , ctx , useJsonB , jsonTypes ))
5060 .toList ();
5161 }
5262
@@ -88,17 +98,16 @@ private void writeClassStart() {
8898 writer .append (" private final Validator validator;" ).eol ();
8999 }
90100
91- List <MethodReader > jsonMethods ;
92101 if (useJsonB ) {
93- jsonMethods =
102+
103+ final var jsonMethods =
94104 reader .getMethods ().stream ()
95105 .filter (MethodReader ::isWebMethod )
96106 .filter (m -> !"byte[]" .equals (m .getReturnType ().toString ()))
97- .filter (m -> m .getProduces () == null || m .getProduces ().toLowerCase ().contains ("json" ))
107+ .filter (
108+ m -> m .getProduces () == null || m .getProduces ().toLowerCase ().contains ("json" ))
98109 .toList ();
99110 writeJsonBTypeFields (jsonMethods );
100- } else {
101- jsonMethods = null ;
102111 }
103112 writer .eol ();
104113
@@ -115,126 +124,129 @@ private void writeClassStart() {
115124 writer .append (" this.validator = validator;" ).eol ();
116125 }
117126 if (useJsonB ) {
118- writeJsonBTypeAssignments (jsonMethods );
127+ writeJsonBTypeAssignments ();
119128 }
120129 writer .append (" }" ).eol ().eol ();
121130 }
122131
123132 public void writeJsonBTypeFields (List <MethodReader > jsonMethods ) {
124133 for (final MethodReader methodReader : jsonMethods ) {
125- // body types
126- if (methodReader .getBodyType () != null ) {
127- methodReader .getParams ().stream ()
128- .filter (MethodParam ::isBody )
129- .forEach (
130- param ->
131- writer
132- .append (
133- " private final JsonType<%s> %sBodyJsonType;" ,
134- param .getUType ().full (), methodReader .simpleName ())
135- .eol ());
136- }
137-
138- if (methodReader .isVoid ()) {
139- continue ;
140- }
141-
142- // return types
143- if (methodReader .getReturnType () instanceof final DeclaredType fullType ) {
144- final var typeArgs = fullType .getTypeArguments ();
145- final var typeArgSize = typeArgs .size ();
146-
147- writer .append (" private final JsonType<" );
148- switch (typeArgSize ) {
149- case 1 -> {
150- if (fullType .toString ().contains ("java.util.Set" ))
151- writer .append ("java.util.Set<%s>>" , typeArgs .get (0 ));
152- else writer .append ("java.util.List<%s>>" , typeArgs .get (0 ));
153- }
154- case 2 -> writer .append ("java.util.Map<String, %s>>" , typeArgs .get (1 ));
155- default -> writer .append ("%s>" , fullType );
156- }
157- writer .append (" %sReturnedJsonType;" , methodReader .simpleName ()).eol ();
158- } else {
159- throw new UnsupportedOperationException ("Only Objects are supported with Jsonb Return Types" );
160- }
161- }
162- }
163134
164- public void writeJsonBTypeAssignments (List <MethodReader > jsonMethods ) {
165- for (final MethodReader methodReader : jsonMethods ) {
166- // body types
167- writeBodyJsonType (methodReader );
135+ writeFieldJsonBodyType (methodReader );
168136 if (methodReader .isVoid ()) {
169137 continue ;
170138 }
171- writeReturnJsonType (methodReader );
139+ writeFieldJsonReturnType (methodReader );
172140 }
173141 }
174142
175- private void writeBodyJsonType (MethodReader methodReader ) {
143+ public void writeFieldJsonBodyType (MethodReader methodReader ) {
144+ // body types
176145 if (methodReader .getBodyType () != null ) {
177146 methodReader .getParams ().stream ()
178147 .filter (MethodParam ::isBody )
179148 .forEach (
180- p -> {
181- final var type = p .getUType ();
182- final var jsonType =
183- Optional .ofNullable (type .param1 ())
184- .or (() -> Optional .ofNullable (type .param0 ()))
185- .orElseGet (type ::full );
186- writer .append (
187- " this.%sBodyJsonType = jsonB.type(%s.class)" ,
188- methodReader .simpleName (), jsonType );
189-
190- if (type .param0 () != null ) {
191- writer .append ("." );
192- switch (type .mainType ()) {
193- case "java.util.List" -> writer .append ("list" );
194- case "java.util.Map" -> writer .append ("map" );
195- case "java.util.Set" -> writer .append ("set" );
196- default -> throw new UnsupportedOperationException (
197- "Only java.util Map, Set and List are supported JsonB Controller Body Return Types" );
198- }
199- writer .append ("()" );
200- }
201- writer .append (";" ).eol ();
149+ param -> {
150+ final var fullType = param .getUType ().full ();
151+ jsonTypes .computeIfAbsent (
152+ fullType ,
153+ type -> {
154+ final var baseType = getBaseType (param .getUType ());
155+ final var fieldName = createFieldName (baseType , type );
156+ writer
157+ .append ("private final JsonType<%s> %sJsonType;" , type , fieldName )
158+ .eol ();
159+ return new SimpleImmutableEntry <>(baseType , fieldName );
160+ });
202161 });
203162 }
204163 }
205164
206- void writeReturnJsonType (MethodReader methodReader ) {
165+ public void writeFieldJsonReturnType (MethodReader methodReader ) {
166+
167+ // return types
207168 if (methodReader .getReturnType () instanceof final DeclaredType fullType ) {
208- final var typeArgs = fullType .getTypeArguments ();
209- final var typeArgSize = typeArgs .size ();
210- final var jsonType =
211- switch (typeArgSize ) {
212- case 1 -> typeArgs .get (0 );
213- case 2 -> typeArgs .get (1 );
214- default -> fullType ;
215- };
216-
217- writer .append (
218- " this.%sReturnedJsonType = jsonB.type(%s.class)" ,
219- methodReader .simpleName (), jsonType );
220- final var returnType = fullType .toString ();
221- if (typeArgSize != 0 ) {
222- writer .append ("." );
169+ final var fullTypeString = fullType .toString ();
170+ jsonTypes .computeIfAbsent (
171+ fullTypeString ,
172+ type -> {
173+ final var baseType = getBaseType (fullType );
174+ final var fieldName = createFieldName (baseType , type );
175+ writer .append ("private final JsonType<%s> %sJsonType;" , type , fieldName ).eol ();
176+ return new SimpleImmutableEntry <>(baseType , fieldName );
177+ });
178+ } else if (methodReader .getReturnType () instanceof final PrimitiveType fullType ) {
179+
180+ jsonTypes .computeIfAbsent (
181+ fullType .toString (),
182+ type -> {
183+ final var baseType =
184+ "int" .equals (type )
185+ ? "Integer"
186+ : type .substring (0 , 1 ).toUpperCase () + type .substring (1 );
187+ writer .append ("private final JsonType<%s> %sJsonType;" , baseType , type ).eol ();
188+ return new SimpleImmutableEntry <>(baseType , type );
189+ });
190+ } else {
191+ throw new UnsupportedOperationException (
192+ "Only Primitives and Objects are supported with Jsonb Return Types" );
193+ }
194+ }
195+
196+ public void writeJsonBTypeAssignments () {
197+ for (final var entry : jsonTypes .entrySet ()) {
198+ final var fullType = entry .getKey ();
199+ final var element = entry .getValue ();
200+ final var baseType = element .getKey ();
201+ final var fieldName = element .getValue ();
202+
203+ writer .append (" this.%sJsonType = jsonB.type(%s.class)" , fieldName , baseType );
223204
224- switch (returnType .substring (0 , 13 )) {
205+ if (fullType .contains ("<" )) {
206+ writer .append ("." );
207+ switch (fullType .substring (0 , 13 )) {
225208 case "java.util.Lis" -> writer .append ("list" );
226209 case "java.util.Map" -> writer .append ("map" );
227210 case "java.util.Set" -> writer .append ("set" );
228211 default -> throw new UnsupportedOperationException (
229- "Only java.util Map, Set and List are supported JsonB Controller Collection Return Types" );
212+ "Only java.util Map, Set and List are supported JsonB Controller Collection Types" );
230213 }
231-
232214 writer .append ("()" );
233215 }
234216 writer .append (";" ).eol ();
235-
236- } else {
237- throw new UnsupportedOperationException ("Only Objects and Strings are supported with Jsonb Controller Return Types" );
238217 }
239218 }
219+
220+ public static String getBaseType (UType type ) {
221+
222+ return Optional .ofNullable (type .param1 ())
223+ .or (() -> Optional .ofNullable (type .param0 ()))
224+ .orElseGet (type ::full );
225+ }
226+
227+ private static String getBaseType (DeclaredType type ) {
228+ final var typeArgs = type .getTypeArguments ();
229+ return switch (typeArgs .size ()) {
230+ case 1 -> typeArgs .get (0 ).toString ();
231+
232+ case 2 -> typeArgs .get (1 ).toString ();
233+ default -> type .toString ();
234+ };
235+ }
236+
237+ private static String createFieldName (String baseType , String fullType ) {
238+ final var shortType =
239+ baseType
240+ .substring (baseType .lastIndexOf ('.' ) + 1 )
241+ .transform (str -> str .substring (0 , 1 ).toLowerCase () + str .substring (1 ));
242+
243+ if (fullType .length () <= 13 ) return shortType ;
244+
245+ return switch (fullType .substring (0 , 13 )) {
246+ case "java.util.Lis" -> "List" ;
247+ case "java.util.Map" -> "Map" ;
248+ case "java.util.Set" -> "Set" ;
249+ default -> shortType ;
250+ };
251+ }
240252}
0 commit comments