1616package io .helidon .codegen .classmodel ;
1717
1818import java .io .IOException ;
19+ import java .util .HashSet ;
20+ import java .util .List ;
21+ import java .util .Map ;
1922import java .util .Objects ;
2023import java .util .Set ;
24+ import java .util .stream .Collectors ;
2125
26+ import io .helidon .common .types .Annotation ;
2227import io .helidon .common .types .EnumValue ;
2328import io .helidon .common .types .TypeName ;
2429
2732 */
2833public final class AnnotationParameter extends CommonComponent {
2934
30- private final String value ;
31- private final TypeName importedType ;
35+ private final Set < TypeName > importedTypes ;
36+ private final Object objectValue ;
3237
3338 private AnnotationParameter (Builder builder ) {
3439 super (builder );
35- this .value = resolveValueToString (builder .type (), builder .value );
36- this .importedType = resolveImport (builder .value );
40+
41+ this .objectValue = builder .value ;
42+ this .importedTypes = resolveImports (builder .value );
3743 }
3844
3945 /**
@@ -45,51 +51,135 @@ public static Builder builder() {
4551 return new Builder ();
4652 }
4753
54+ @ Override
55+ public String toString () {
56+ return objectValue + " (" + type ().simpleTypeName () + ")" ;
57+ }
58+
4859 @ Override
4960 void writeComponent (ModelWriter writer , Set <String > declaredTokens , ImportOrganizer imports , ClassType classType )
5061 throws IOException {
51- writer .write (name () + " = " + value );
62+ writer .write (name () + " = " );
63+ writeValue (writer , imports );
5264 }
5365
5466 @ Override
5567 void addImports (ImportOrganizer .Builder imports ) {
56- if (importedType != null ) {
57- imports .addImport (importedType );
58- }
68+ importedTypes .forEach (imports ::addImport );
69+ }
70+
71+ void writeValue (ModelWriter writer , ImportOrganizer imports ) throws IOException {
72+ writer .write (resolveValueToString (imports , type (), objectValue ));
5973 }
6074
61- private static TypeName resolveImport (Object value ) {
75+ private static Set <TypeName > resolveImports (Object value ) {
76+ Set <TypeName > imports = new HashSet <>();
77+
78+ resolveImports (imports , value );
79+
80+ return imports ;
81+ }
82+
83+ private static void resolveImports (Set <TypeName > imports , Object value ) {
6284 if (value .getClass ().isEnum ()) {
63- return TypeName .create (value .getClass ());
85+ imports .add (TypeName .create (value .getClass ()));
86+ return ;
6487 }
65- if (value instanceof TypeName tn ) {
66- return tn ;
88+ switch (value ) {
89+ case TypeName tn -> imports .add (tn );
90+ case EnumValue ev -> imports .add (ev .type ());
91+ case Annotation an -> {
92+ imports .add (an .typeName ());
93+ an .values ()
94+ .values ()
95+ .forEach (nestedValue -> resolveImports (imports , nestedValue ));
96+ }
97+ default -> {
6798 }
68- if (value instanceof EnumValue ev ) {
69- return ev .type ();
7099 }
71- return null ;
72100 }
73101
74- private static String resolveValueToString (Type type , Object value ) {
102+ // takes the annotation value objects and converts it to its string representation (as seen in class source)
103+ private static String resolveValueToString (ImportOrganizer imports , Type type , Object value ) {
75104 Class <?> valueClass = value .getClass ();
76105 if (valueClass .isEnum ()) {
77- return valueClass .getSimpleName () + "." + ((Enum <?>) value ).name ();
78- } else if (type .fqTypeName ().equals (String .class .getName ())) {
106+ return imports .typeName (Type .fromTypeName (TypeName .create (valueClass )), true )
107+ + "." + ((Enum <?>) value ).name ();
108+ }
109+ if (type != null && type .fqTypeName ().equals (String .class .getName ())) {
79110 String stringValue = value .toString ();
80111 if (!stringValue .startsWith ("\" " ) && !stringValue .endsWith ("\" " )) {
81112 return "\" " + stringValue + "\" " ;
82113 }
83- } else if (value instanceof TypeName typeName ) {
84- return typeName .classNameWithEnclosingNames () + ".class" ;
85- } else if (value instanceof EnumValue enumValue ) {
86- return enumValue .type ().classNameWithEnclosingNames () + "." + enumValue .name ();
114+ return stringValue ;
115+ }
116+
117+ if (type != null && type .fqTypeName ().equals (Object .class .getName ())) {
118+ // we expect this to be "as is" - such as when parsing annotations
119+ return value .toString ();
120+ }
121+
122+ return switch (value ) {
123+ case TypeName typeName -> imports .typeName (Type .fromTypeName (typeName ), true ) + ".class" ;
124+ case EnumValue enumValue -> imports .typeName (Type .fromTypeName (enumValue .type ()), true )
125+ + "." + enumValue .name ();
126+ case Character character -> "'" + character + "'" ;
127+ case Long longValue -> longValue + "L" ;
128+ case Float floatValue -> floatValue + "F" ;
129+ case Double doubleValue -> doubleValue + "D" ;
130+ case Byte byteValue -> "(byte) " + byteValue ;
131+ case Short shortValue -> "(short) " + shortValue ;
132+ case Class <?> clazz -> imports .typeName (Type .fromTypeName (TypeName .create (clazz )), true ) + ".class" ;
133+ case Annotation annotation -> nestedAnnotationValue (imports , annotation );
134+ case List <?> list -> nestedListValue (imports , list );
135+ case String str -> str .startsWith ("\" " ) && str .endsWith ("\" " ) ? str : "\" " + str + "\" " ;
136+ default -> value .toString ();
137+ };
138+
139+ }
140+
141+ private static String nestedListValue (ImportOrganizer imports , List <?> list ) {
142+ if (list .isEmpty ()) {
143+ return "{}" ;
144+ }
145+ StringBuilder result = new StringBuilder ();
146+ if (list .size () > 1 ) {
147+ result .append ("{" );
87148 }
88- return value .toString ();
149+
150+ result .append (list .stream ()
151+ .map (it -> resolveValueToString (imports , null , it ))
152+ .collect (Collectors .joining (", " )));
153+
154+ if (list .size () > 1 ) {
155+ result .append ("}" );
156+ }
157+ return result .toString ();
89158 }
90159
91- String value () {
92- return value ;
160+ private static String nestedAnnotationValue (ImportOrganizer imports , Annotation annotation ) {
161+ StringBuilder sb = new StringBuilder ("@" );
162+ sb .append (imports .typeName (Type .fromTypeName (annotation .typeName ()), true ));
163+
164+ Map <String , Object > values = annotation .values ();
165+ if (values .isEmpty ()) {
166+ return sb .toString ();
167+ }
168+
169+ sb .append ("(" );
170+ if (values .size () == 1 && values .containsKey ("value" )) {
171+ sb .append (resolveValueToString (imports , null , values .get ("value" )));
172+ } else {
173+ values .forEach ((key , value ) -> {
174+ sb .append (key )
175+ .append (" = " )
176+ .append (resolveValueToString (imports , null , value ))
177+ .append (", " );
178+ });
179+ sb .delete (sb .length () - 2 , sb .length ());
180+ }
181+ sb .append (")" );
182+ return sb .toString ();
93183 }
94184
95185 /**
0 commit comments