All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.dinject.generator.Processor Maven / Gradle / Ivy

There is a newer version: 2.3
Show newest version
package io.dinject.generator;

import io.dinject.ContextModule;
import io.dinject.Factory;
import io.dinject.core.DependencyMeta;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.FilerException;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.inject.Singleton;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Processor extends AbstractProcessor {

  private ProcessingContext processingContext;

  private Elements elementUtils;

  private Map metaData = new LinkedHashMap<>();

  private List beanReaders = new ArrayList<>();

  private Set readBeans = new HashSet<>();

  public Processor() {
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.RELEASE_8;
  }

  @Override
  public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    this.processingContext = new ProcessingContext(processingEnv);
    this.elementUtils = processingEnv.getElementUtils();
  }

  @Override
  public Set getSupportedAnnotationTypes() {

    Set annotations = new LinkedHashSet<>();
    annotations.add(ContextModule.class.getCanonicalName());
    annotations.add(Factory.class.getCanonicalName());
    annotations.add(Singleton.class.getCanonicalName());
    annotations.add(Constants.CONTROLLER);
    return annotations;
  }

  @Override
  public boolean process(Set annotations, RoundEnvironment roundEnv) {

    Set controllers = Collections.emptySet();
    TypeElement typeElement = elementUtils.getTypeElement(Constants.CONTROLLER);
    if (typeElement != null) {
      controllers = roundEnv.getElementsAnnotatedWith(typeElement);
    }

    Set factoryBeans = roundEnv.getElementsAnnotatedWith(Factory.class);
    Set beans = roundEnv.getElementsAnnotatedWith(Singleton.class);

    readModule(roundEnv);
    readChangedBeans(factoryBeans, true);
    readChangedBeans(beans, false);
    readChangedBeans(controllers, false);

    mergeMetaData();

    writeBeanHelpers();
    if (roundEnv.processingOver()) {
      writeBeanFactory();
    }

    return false;
  }


  private void writeBeanHelpers() {
    for (BeanReader beanReader : beanReaders) {
      try {
        if (!beanReader.isWrittenToFile()) {
          SimpleBeanWriter writer = new SimpleBeanWriter(beanReader, processingContext);
          writer.write();
          beanReader.setWrittenToFile();
        }
      } catch (FilerException e) {
        processingContext.logWarn("FilerException to write $di class " + beanReader.getBeanType() + " " + e.getMessage());

      } catch (IOException e) {
        e.printStackTrace();
        processingContext.logError(beanReader.getBeanType(), "Failed to write $di class");
      }
    }
  }

  private void writeBeanFactory() {

    MetaDataOrdering ordering = new MetaDataOrdering(metaData.values(), processingContext);
    int remaining = ordering.processQueue();
    if (remaining > 0) {
      processingContext.logWarn("there are " + remaining + " beans with unsatisfied dependencies (assuming external dependencies)");
      ordering.warnOnDependencies();
    }

    try {
      SimpleFactoryWriter factoryWriter = new SimpleFactoryWriter(ordering, processingContext);
      factoryWriter.write();
    } catch (FilerException e) {
      processingContext.logWarn("FilerException trying to write factory " + e.getMessage());
    } catch (IOException e) {
      processingContext.logError("Failed to write factory " + e.getMessage());
    }
  }

  /**
   * Read the beans that have changed.
   */
  private void readChangedBeans(Set beans, boolean factory) {
    for (Element element : beans) {
      if (!(element instanceof TypeElement)) {
        processingContext.logError("unexpected type [" + element + "]");
      } else {
        if (readBeans.add(element.toString())) {
          readBeanMeta((TypeElement) element, factory);
        } else {
          processingContext.logDebug("skipping already processed bean " + element);
        }
      }
    }
  }

  /**
   * Merge the changed bean meta data into the existing (factory) metaData.
   */
  private void mergeMetaData() {

    for (BeanReader beanReader : beanReaders) {
      String simpleName = beanReader.getSimpleName();
      MetaData metaData = this.metaData.get(simpleName);
      if (metaData == null) {
        addMeta(beanReader);

      } else {
        updateMeta(metaData, beanReader);
      }
    }
  }

  /**
   * Add a new previously unknown bean.
   */
  private void addMeta(BeanReader beanReader) {
    MetaData meta = beanReader.createMeta();
    metaData.put(meta.getType(), meta);
    for (MetaData methodMeta : beanReader.createFactoryMethodMeta()) {
      metaData.put(methodMeta.getType(), methodMeta);
    }
  }

  /**
   * Update the meta data on a previously known bean.
   */
  private void updateMeta(MetaData metaData, BeanReader beanReader) {
    metaData.update(beanReader);
  }

  /**
   * Read the dependency injection meta data for the given bean.
   */
  private void readBeanMeta(TypeElement typeElement, boolean factory) {

    if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
      processingContext.logWarn("skipping annotation type " + typeElement);
      return;
    }
    BeanReader beanReader = new BeanReader(typeElement, processingContext);
    beanReader.read(factory);
    beanReaders.add(beanReader);
  }

  /**
   * Read the existing meta data from ContextModule (if found) and the factory bean (if exists).
   */
  private void readModule(RoundEnvironment roundEnv) {

    String factory = processingContext.loadMetaInfServices();
    if (factory != null) {
      TypeElement factoryType = elementUtils.getTypeElement(factory);
      if (factoryType != null) {
        readFactory(factoryType);
      }
    }

    Set elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(ContextModule.class);
    if (!elementsAnnotatedWith.isEmpty()) {
      Iterator iterator = elementsAnnotatedWith.iterator();
      if (iterator.hasNext()) {
        Element element = iterator.next();
        ContextModule annotation = element.getAnnotation(ContextModule.class);
        if (annotation != null) {
          processingContext.setContextDetails(annotation.name(), annotation.provides(), annotation.dependsOn(), element);
        }
      }
    }
  }


  /**
   * Read the existing factory bean. Each of the build methods is annotated with @DependencyMeta
   * which holds the information we need (to regenerate the factory with any changes).
   */
  private void readFactory(TypeElement factoryType) {

    ContextModule module = factoryType.getAnnotation(ContextModule.class);
    processingContext.setContextDetails(module.name(), module.provides(), module.dependsOn(), factoryType);

    List elements = factoryType.getEnclosedElements();
    if (elements != null) {
      for (Element element : elements) {
        ElementKind kind = element.getKind();
        if (ElementKind.METHOD == kind) {

          Name simpleName = element.getSimpleName();
          if (simpleName.toString().startsWith("build")) {
            // read a build method - DependencyMeta
            DependencyMeta meta = element.getAnnotation(DependencyMeta.class);
            if (meta == null) {
              processingContext.logError("Missing @DependencyMeta on method " + simpleName.toString());
            } else {
              metaData.put(meta.type(), new MetaData(meta));
            }
          }
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy