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}