001package io.avaje.inject.generator;
002
003import io.avaje.inject.Factory;
004import io.avaje.inject.InjectModule;
005import javax.inject.Scope;
006import javax.inject.Singleton;
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.*;
013import javax.lang.model.util.Elements;
014import java.util.*;
015
016public class Processor extends AbstractProcessor {
017
018  private ProcessingContext context;
019  private Elements elementUtils;
020  private ScopeInfo defaultScope;
021  private AllScopes allScopes;
022  private boolean readModuleInfo;
023
024  public Processor() {
025  }
026
027  @Override
028  public SourceVersion getSupportedSourceVersion() {
029    return SourceVersion.latest();
030  }
031
032  @Override
033  public synchronized void init(ProcessingEnvironment processingEnv) {
034    super.init(processingEnv);
035    this.context = new ProcessingContext(processingEnv);
036    this.elementUtils = processingEnv.getElementUtils();
037    this.allScopes = new AllScopes(context);
038    this.defaultScope = allScopes.defaultScope();
039  }
040
041  @Override
042  public Set<String> getSupportedAnnotationTypes() {
043    Set<String> annotations = new LinkedHashSet<>();
044    annotations.add(InjectModule.class.getCanonicalName());
045    annotations.add(Factory.class.getCanonicalName());
046    annotations.add(Singleton.class.getCanonicalName());
047    annotations.add(Scope.class.getCanonicalName());
048    annotations.add(Constants.CONTROLLER);
049    return annotations;
050  }
051
052  @Override
053  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
054
055    Set<? extends Element> controllers = Collections.emptySet();
056    TypeElement typeElement = elementUtils.getTypeElement(Constants.CONTROLLER);
057    if (typeElement != null) {
058      controllers = roundEnv.getElementsAnnotatedWith(typeElement);
059    }
060
061    Set<? extends Element> factoryBeans = roundEnv.getElementsAnnotatedWith(Factory.class);
062    Set<? extends Element> beans = roundEnv.getElementsAnnotatedWith(Singleton.class);
063    Set<? extends Element> scopes = roundEnv.getElementsAnnotatedWith(Scope.class);
064    readScopes(scopes);
065    readModule(roundEnv);
066    readChangedBeans(factoryBeans, true);
067    readChangedBeans(beans, false);
068    readChangedBeans(controllers, false);
069    allScopes.readBeans(roundEnv);
070
071    defaultScope.write(roundEnv.processingOver());
072    allScopes.write(roundEnv.processingOver());
073    return false;
074  }
075
076  private void readScopes(Set<? extends Element> scopes) {
077    for (Element element : scopes) {
078      if (element.getKind() == ElementKind.ANNOTATION_TYPE) {
079        // context.logDebug("detected scope annotation " + element);
080        TypeElement type = (TypeElement) element;
081        allScopes.addScopeAnnotation(type);
082      }
083    }
084  }
085
086  /**
087   * Read the beans that have changed.
088   */
089  private void readChangedBeans(Set<? extends Element> beans, boolean factory) {
090    for (Element element : beans) {
091      if (!(element instanceof TypeElement)) {
092        context.logError("unexpected type [" + element + "]");
093      } else {
094        TypeElement typeElement = (TypeElement) element;
095        if (!factory) {
096          defaultScope.read(typeElement, factory);
097        } else {
098          final ScopeInfo scope = findScope(typeElement);
099          if (scope != null) {
100            // context.logWarn("Adding factory to custom scope "+element+" scope: "+scope);
101            scope.read(typeElement, true);
102          } else {
103            defaultScope.read(typeElement, true);
104          }
105        }
106      }
107    }
108  }
109
110  /**
111   * Find the scope if the Factory has a scope annotation.
112   */
113  private ScopeInfo findScope(Element element) {
114    for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
115      final ScopeInfo scopeInfo = allScopes.get(annotationMirror.getAnnotationType().toString());
116      if (scopeInfo != null) {
117        return scopeInfo;
118      }
119    }
120    return null;
121  }
122
123  /**
124   * Read the existing meta data from InjectModule (if found) and the factory bean (if exists).
125   */
126  private void readModule(RoundEnvironment roundEnv) {
127    if (readModuleInfo) {
128      // only read the module meta data once
129      return;
130    }
131    readModuleInfo = true;
132    String factory = context.loadMetaInfServices();
133    if (factory != null) {
134      TypeElement moduleType = elementUtils.getTypeElement(factory);
135      if (moduleType != null) {
136        defaultScope.readModuleMetaData(moduleType);
137      }
138    }
139    allScopes.readModules(context.loadMetaInfCustom());
140    readInjectModule(roundEnv);
141  }
142
143  /**
144   * Read InjectModule for things like package-info etc (not for custom scopes)
145   */
146  private void readInjectModule(RoundEnvironment roundEnv) {
147    // read other that are annotated with InjectModule
148    Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(InjectModule.class);
149    if (!elementsAnnotatedWith.isEmpty()) {
150      Iterator<? extends Element> iterator = elementsAnnotatedWith.iterator();
151      if (iterator.hasNext()) {
152        Element element = iterator.next();
153        Scope scope = element.getAnnotation(Scope.class);
154        if (scope == null) {
155          // it it not a custom scope annotation
156          InjectModule annotation = element.getAnnotation(InjectModule.class);
157          if (annotation != null) {
158            defaultScope.details(annotation.name(), element);
159          }
160        }
161      }
162    }
163  }
164
165}