001package io.avaje.http.generator.core; 002 003import io.avaje.http.api.Controller; 004import io.swagger.v3.oas.annotations.OpenAPIDefinition; 005import io.swagger.v3.oas.annotations.tags.Tag; 006import io.swagger.v3.oas.annotations.tags.Tags; 007 008import javax.annotation.processing.AbstractProcessor; 009import javax.annotation.processing.ProcessingEnvironment; 010import javax.annotation.processing.RoundEnvironment; 011import javax.lang.model.SourceVersion; 012import javax.lang.model.element.Element; 013import javax.lang.model.element.TypeElement; 014import java.io.IOException; 015import java.util.LinkedHashSet; 016import java.util.Set; 017 018public abstract class BaseProcessor extends AbstractProcessor { 019 020 protected ProcessingContext ctx; 021 022 @Override 023 public SourceVersion getSupportedSourceVersion() { 024 return SourceVersion.latest(); 025 } 026 027 @Override 028 public Set<String> getSupportedAnnotationTypes() { 029 Set<String> annotations = new LinkedHashSet<>(); 030 annotations.add(Controller.class.getCanonicalName()); 031 annotations.add(OpenAPIDefinition.class.getCanonicalName()); 032 return annotations; 033 } 034 035 @Override 036 public synchronized void init(ProcessingEnvironment processingEnv) { 037 super.init(processingEnv); 038 this.ctx = new ProcessingContext(processingEnv, providePlatformAdapter()); 039 } 040 041 /** 042 * Provide the platform specific adapter to use for Javalin, Helidon etc. 043 */ 044 protected abstract PlatformAdapter providePlatformAdapter(); 045 046 @Override 047 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment round) { 048 049 if (ctx.isOpenApiAvailable()) { 050 readOpenApiDefinition(round); 051 readTagDefinitions(round); 052 } 053 054 Set<? extends Element> controllers = round.getElementsAnnotatedWith(Controller.class); 055 for (Element controller : controllers) { 056 writeControllerAdapter(controller); 057 } 058 059 if (round.processingOver()) { 060 writeOpenAPI(); 061 } 062 return false; 063 } 064 065 private void readOpenApiDefinition(RoundEnvironment round) { 066 Set<? extends Element> elements = round.getElementsAnnotatedWith(OpenAPIDefinition.class); 067 for (Element element : elements) { 068 ctx.doc().readApiDefinition(element); 069 } 070 } 071 072 private void readTagDefinitions(RoundEnvironment round) { 073 Set<? extends Element> elements = round.getElementsAnnotatedWith(Tag.class); 074 for (Element element : elements) { 075 ctx.doc().addTagDefinition(element); 076 } 077 078 elements = round.getElementsAnnotatedWith(Tags.class); 079 for (Element element : elements) { 080 ctx.doc().addTagsDefinition(element); 081 } 082 } 083 084 private void writeOpenAPI() { 085 ctx.doc().writeApi(); 086 } 087 088 private void writeControllerAdapter(Element controller) { 089 if (controller instanceof TypeElement) { 090 ControllerReader reader = new ControllerReader((TypeElement) controller, ctx); 091 reader.read(); 092 try { 093 writeControllerAdapter(ctx, reader); 094 } catch (Throwable e) { 095 e.printStackTrace(); 096 ctx.logError(reader.getBeanType(), "Failed to write $route class " + e); 097 } 098 } 099 } 100 101 /** 102 * Write the adapter code for the given controller. 103 */ 104 public abstract void writeControllerAdapter(ProcessingContext ctx, ControllerReader reader) throws IOException; 105 106}