11package io .avaje .http .generator .helidon .nima ;
22
33import java .io .IOException ;
4+ import java .util .HashMap ;
45import java .util .List ;
6+ import java .util .Map ;
57import java .util .Optional ;
68
79import javax .lang .model .type .DeclaredType ;
1214import io .avaje .http .generator .core .MethodParam ;
1315import io .avaje .http .generator .core .MethodReader ;
1416import io .avaje .http .generator .core .ProcessingContext ;
17+ import io .avaje .http .generator .core .UType ;
1518
1619/** Write Helidon specific web route adapter (a Helidon Service). */
1720class ControllerWriter extends BaseControllerWriter {
1821
1922 private static final String AT_GENERATED = "@Generated(\" avaje-helidon-nima-generator\" )" ;
2023 private final boolean useJsonB ;
24+ private final Map <String , JsonbType > jsonTypes ;
2125
22- ControllerWriter (ControllerReader reader , ProcessingContext ctx , boolean jsonB ) throws IOException {
26+ ControllerWriter (ControllerReader reader , ProcessingContext ctx , boolean jsonB )
27+ throws IOException {
2328 super (reader , ctx );
24- this . useJsonB = jsonB ;
29+ useJsonB = jsonB ;
2530 if (useJsonB ) {
2631 reader .addImportType ("io.avaje.jsonb.Jsonb" );
2732 reader .addImportType ("io.avaje.jsonb.JsonType" );
33+ jsonTypes = new HashMap <>();
34+ } else {
35+ jsonTypes = null ;
2836 }
2937 reader .addImportType ("io.helidon.common.http.HttpMediaType" );
3038 reader .addImportType ("io.helidon.common.parameters.Parameters" );
@@ -46,7 +54,7 @@ void write() {
4654 private List <ControllerMethodWriter > getWriterMethods () {
4755 return reader .getMethods ().stream ()
4856 .filter (MethodReader ::isWebMethod )
49- .map (it -> new ControllerMethodWriter (it , writer , ctx , useJsonB ))
57+ .map (it -> new ControllerMethodWriter (it , writer , ctx , useJsonB , jsonTypes ))
5058 .toList ();
5159 }
5260
@@ -88,17 +96,16 @@ private void writeClassStart() {
8896 writer .append (" private final Validator validator;" ).eol ();
8997 }
9098
91- List <MethodReader > jsonMethods ;
9299 if (useJsonB ) {
93- jsonMethods =
100+
101+ final var jsonMethods =
94102 reader .getMethods ().stream ()
95103 .filter (MethodReader ::isWebMethod )
96104 .filter (m -> !"byte[]" .equals (m .getReturnType ().toString ()))
97- .filter (m -> m .getProduces () == null || m .getProduces ().toLowerCase ().contains ("json" ))
105+ .filter (
106+ m -> m .getProduces () == null || m .getProduces ().toLowerCase ().contains ("json" ))
98107 .toList ();
99108 writeJsonBTypeFields (jsonMethods );
100- } else {
101- jsonMethods = null ;
102109 }
103110 writer .eol ();
104111
@@ -115,126 +122,112 @@ private void writeClassStart() {
115122 writer .append (" this.validator = validator;" ).eol ();
116123 }
117124 if (useJsonB ) {
118- writeJsonBTypeAssignments (jsonMethods );
125+ writeJsonBTypeAssignments ();
119126 }
120127 writer .append (" }" ).eol ().eol ();
121128 }
122129
123130 public void writeJsonBTypeFields (List <MethodReader > jsonMethods ) {
124131 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- }
137132
133+ writeFieldJsonBodyType (methodReader );
138134 if (methodReader .isVoid ()) {
139135 continue ;
140136 }
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- }
137+ writeFieldJsonReturnType (methodReader );
161138 }
162- }
163-
164- public void writeJsonBTypeAssignments (List <MethodReader > jsonMethods ) {
165- for (final MethodReader methodReader : jsonMethods ) {
166- // body types
167- writeBodyJsonType (methodReader );
168- if (methodReader .isVoid ()) {
169- continue ;
170- }
171- writeReturnJsonType (methodReader );
172- }
173- }
174-
175- private void writeBodyJsonType (MethodReader methodReader ) {
139+ } public void writeFieldJsonBodyType (MethodReader methodReader ) {
140+ // body types
176141 if (methodReader .getBodyType () != null ) {
177142 methodReader .getParams ().stream ()
178143 .filter (MethodParam ::isBody )
179144 .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 ();
145+ param -> {
146+ final var fullType = param .getUType ().full ();
147+ jsonTypes .computeIfAbsent (
148+ fullType ,
149+ k -> {
150+ final var baseType = getBaseType (param .getUType ());
151+ final var fieldName = createFieldName (baseType , k );
152+ writer .append ("private final JsonType<%s> %sJsonType;" , k , fieldName ).eol ();
153+ return new JsonbType (baseType , fieldName );
154+ });
202155 });
203156 }
204157 }
205158
206- void writeReturnJsonType (MethodReader methodReader ) {
159+ public void writeFieldJsonReturnType (MethodReader methodReader ) {
160+
161+ // return types
207162 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 ("." );
163+ final var fullTypeString = fullType .toString ();
164+ jsonTypes .computeIfAbsent (
165+ fullTypeString ,
166+ k -> {
167+ final var baseType = getBaseType (fullType );
168+ final var fieldName = createFieldName (baseType , k );
169+ writer .append ("private final JsonType<%s> %sJsonType;" , k , fieldName ).eol ();
170+ return new JsonbType (baseType , fieldName );
171+ });
172+ } else {
173+ throw new UnsupportedOperationException ("Only Objects are supported with Jsonb Return Types" );
174+ }
175+ }
176+
177+ public void writeJsonBTypeAssignments () {
178+ for (final var entry : jsonTypes .entrySet ()) {
179+ final var fullType = entry .getKey ();
180+ final var element = entry .getValue ();
181+ final var fieldName = element .fieldName ();
182+ final var baseType = element .baseType ();
223183
224- switch (returnType .substring (0 , 13 )) {
184+ writer .append (" this.%sJsonType = jsonB.type(%s.class)" , fieldName , baseType );
185+
186+ if (fullType .contains ("<" )) {
187+ writer .append ("." );
188+ switch (fullType .substring (0 , 13 )) {
225189 case "java.util.Lis" -> writer .append ("list" );
226190 case "java.util.Map" -> writer .append ("map" );
227191 case "java.util.Set" -> writer .append ("set" );
228192 default -> throw new UnsupportedOperationException (
229- "Only java.util Map, Set and List are supported JsonB Controller Collection Return Types" );
193+ "Only java.util Map, Set and List are supported JsonB Controller Collection Types" );
230194 }
231-
232195 writer .append ("()" );
233196 }
234197 writer .append (";" ).eol ();
235-
236- } else {
237- throw new UnsupportedOperationException ("Only Objects and Strings are supported with Jsonb Controller Return Types" );
238198 }
239199 }
200+
201+ public static String getBaseType (UType type ) {
202+
203+ return Optional .ofNullable (type .param1 ())
204+ .or (() -> Optional .ofNullable (type .param0 ()))
205+ .orElseGet (type ::full );
206+ }
207+
208+ private static String getBaseType (DeclaredType type ) {
209+ final var typeArgs = type .getTypeArguments ();
210+ return switch (typeArgs .size ()) {
211+ case 1 -> typeArgs .get (0 ).toString ();
212+
213+ case 2 -> typeArgs .get (1 ).toString ();
214+ default -> type .toString ();
215+ };
216+ }
217+
218+ private static String createFieldName (String baseType , String fullType ) {
219+ final var shortType =
220+ baseType
221+ .substring (baseType .lastIndexOf ('.' ) + 1 )
222+ .transform (str -> str .substring (0 , 1 ).toLowerCase () + str .substring (1 ));
223+
224+ if (shortType .length () <= 13 ) return shortType ;
225+
226+ return switch (fullType .substring (0 , 13 )) {
227+ case "java.util.Lis" -> "List" ;
228+ case "java.util.Map" -> "Map" ;
229+ case "java.util.Set" -> "Set" ;
230+ default -> "" ;
231+ };
232+ }
240233}
0 commit comments