88import static java .util .stream .Collectors .toMap ;
99
1010import java .io .IOException ;
11+ import java .nio .file .Files ;
12+ import java .nio .file .StandardOpenOption ;
1113import java .util .HashMap ;
14+ import java .util .HashSet ;
1215import java .util .Map ;
1316import java .util .Map .Entry ;
1417import java .util .Set ;
2730
2831@ GenerateAPContext
2932@ GenerateModuleInfoReader
30- @ SupportedOptions ({"useJavax" , "useSingleton" , "instrumentRequests" ,"disableDirectWrites" ,"disableJsonB" })
33+ @ SupportedOptions ({
34+ "useJavax" ,
35+ "useSingleton" ,
36+ "instrumentRequests" ,
37+ "disableDirectWrites" ,
38+ "disableJsonB"
39+ })
3140public abstract class BaseProcessor extends AbstractProcessor {
3241
42+ private static final String HTTP_CONTROLLERS_TXT = "testAPI/controllers.txt" ;
3343 protected String contextPathString ;
3444
3545 protected Map <String , String > packagePaths = new HashMap <>();
3646
47+ private final Set <String > clientFQNs = new HashSet <>();
48+
3749 @ Override
3850 public SourceVersion getSupportedSourceVersion () {
3951 return SourceVersion .latest ();
4052 }
4153
4254 @ Override
4355 public Set <String > getSupportedAnnotationTypes () {
44- return Set .of (PathPrism .PRISM_TYPE , ControllerPrism .PRISM_TYPE , OpenAPIDefinitionPrism .PRISM_TYPE );
56+ return Set .of (
57+ PathPrism .PRISM_TYPE , ControllerPrism .PRISM_TYPE , OpenAPIDefinitionPrism .PRISM_TYPE );
4558 }
4659
4760 @ Override
4861 public synchronized void init (ProcessingEnvironment processingEnv ) {
4962 super .init (processingEnv );
5063 APContext .init (processingEnv );
5164 ProcessingContext .init (processingEnv , providePlatformAdapter ());
65+
66+ try {
67+ var txtFilePath = APContext .getBuildResource (HTTP_CONTROLLERS_TXT );
68+
69+ if (txtFilePath .toFile ().exists ()) {
70+ Files .lines (txtFilePath ).forEach (clientFQNs ::add );
71+ }
72+ if (APContext .isTestCompilation ()) {
73+ for (var path : clientFQNs ) {
74+ TestClientWriter .writeActual (path );
75+ }
76+ }
77+ } catch (IOException e ) {
78+ e .printStackTrace ();
79+ // not worth failing over
80+ }
5281 }
5382
5483 /** Provide the platform specific adapter to use for Javalin, Helidon etc. */
@@ -82,19 +111,34 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
82111 readSecuritySchemes (round );
83112 }
84113
85- for (final Element controller : round .getElementsAnnotatedWith (typeElement (ControllerPrism .PRISM_TYPE ))) {
114+ for (final var controller :
115+ ElementFilter .typesIn (
116+ round .getElementsAnnotatedWith (typeElement (ControllerPrism .PRISM_TYPE )))) {
86117 writeAdapter (controller );
87118 }
88119
89120 if (round .processingOver ()) {
90121 writeOpenAPI ();
91122 ProcessingContext .validateModule ();
123+
124+ if (!APContext .isTestCompilation ()) {
125+ try {
126+ Files .write (
127+ APContext .getBuildResource (HTTP_CONTROLLERS_TXT ),
128+ clientFQNs ,
129+ StandardOpenOption .CREATE ,
130+ StandardOpenOption .WRITE );
131+ } catch (IOException e ) {
132+ // not worth failing over
133+ }
134+ }
92135 }
93136 return false ;
94137 }
95138
96139 private void readOpenApiDefinition (RoundEnvironment round ) {
97- for (final Element element : round .getElementsAnnotatedWith (typeElement (OpenAPIDefinitionPrism .PRISM_TYPE ))) {
140+ for (final Element element :
141+ round .getElementsAnnotatedWith (typeElement (OpenAPIDefinitionPrism .PRISM_TYPE ))) {
98142 doc ().readApiDefinition (element );
99143 }
100144 }
@@ -103,16 +147,19 @@ private void readTagDefinitions(RoundEnvironment round) {
103147 for (final Element element : round .getElementsAnnotatedWith (typeElement (TagPrism .PRISM_TYPE ))) {
104148 doc ().addTagDefinition (element );
105149 }
106- for (final Element element : round .getElementsAnnotatedWith (typeElement (TagsPrism .PRISM_TYPE ))) {
150+ for (final Element element :
151+ round .getElementsAnnotatedWith (typeElement (TagsPrism .PRISM_TYPE ))) {
107152 doc ().addTagsDefinition (element );
108153 }
109154 }
110155
111156 private void readSecuritySchemes (RoundEnvironment round ) {
112- for (final Element element : round .getElementsAnnotatedWith (typeElement (SecuritySchemePrism .PRISM_TYPE ))) {
157+ for (final Element element :
158+ round .getElementsAnnotatedWith (typeElement (SecuritySchemePrism .PRISM_TYPE ))) {
113159 doc ().addSecurityScheme (element );
114160 }
115- for (final Element element : round .getElementsAnnotatedWith (typeElement (SecuritySchemesPrism .PRISM_TYPE ))) {
161+ for (final Element element :
162+ round .getElementsAnnotatedWith (typeElement (SecuritySchemesPrism .PRISM_TYPE ))) {
116163 doc ().addSecuritySchemes (element );
117164 }
118165 }
@@ -121,31 +168,42 @@ private void writeOpenAPI() {
121168 doc ().writeApi ();
122169 }
123170
124- private void writeAdapter (Element controller ) {
125- if (controller instanceof TypeElement ) {
126- final var packageFQN = elements ().getPackageOf (controller ).getQualifiedName ().toString ();
127- final var contextPath = Util .combinePath (contextPathString , packagePath (packageFQN ));
128- final var reader = new ControllerReader ((TypeElement ) controller , contextPath );
129- reader .read (true );
130- try {
131- writeControllerAdapter (reader );
132- } catch (final Throwable e ) {
133- logError (reader .beanType (), "Failed to write $Route class " + e );
171+ private void writeAdapter (TypeElement controller ) {
172+ final var packageFQN = elements ().getPackageOf (controller ).getQualifiedName ().toString ();
173+ final var contextPath = Util .combinePath (contextPathString , packagePath (packageFQN ));
174+ final var reader = new ControllerReader (controller , contextPath );
175+ reader .read (true );
176+ try {
177+
178+ writeControllerAdapter (reader );
179+ writeClientAdapter (reader );
180+
181+ } catch (final Throwable e ) {
182+ logError (reader .beanType (), "Failed to write $Route class " + e );
183+ }
184+ }
185+
186+ private void writeClientAdapter (ControllerReader reader ) {
187+
188+ try {
189+ if (reader .beanType ().getInterfaces ().isEmpty ()
190+ && "java.lang.Object" .equals (reader .beanType ().getSuperclass ().toString ())) {
191+ new TestClientWriter (reader ).write ();
192+ clientFQNs .add (reader .beanType ().getQualifiedName ().toString () + "TestAPI" );
134193 }
194+ } catch (final IOException e ) {
195+ logError (reader .beanType (), "Failed to write $Route class " + e );
135196 }
136197 }
137198
138199 private String packagePath (String packageFQN ) {
139200 return packagePaths .entrySet ().stream ()
140- .filter (k -> packageFQN .startsWith (k .getKey ()))
141- .map (Entry ::getValue )
142- .reduce (Util ::combinePath )
143- .orElse (null );
201+ .filter (k -> packageFQN .startsWith (k .getKey ()))
202+ .map (Entry ::getValue )
203+ .reduce (Util ::combinePath )
204+ .orElse (null );
144205 }
145206
146- /**
147- * Write the adapter code for the given controller.
148- */
207+ /** Write the adapter code for the given controller. */
149208 public abstract void writeControllerAdapter (ControllerReader reader ) throws IOException ;
150-
151209}
0 commit comments