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

io.avaje.inject.generator.ProcessingContext Maven / Gradle / Ivy

package io.avaje.inject.generator;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.nio.file.NoSuchFileException;

import javax.annotation.processing.FilerException;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.util.*;

import static io.avaje.inject.generator.APContext.*;
import static java.util.stream.Collectors.toSet;

final class ProcessingContext {

  private static final String EVENTS_SPI = "io.avaje.inject.events.spi.ObserverManagerPlugin";
  private static final ThreadLocal CTX = ThreadLocal.withInitial(Ctx::new);
  private static boolean processingOver;
  private ProcessingContext() {}

  static final class Ctx {
    private final Set uniqueModuleNames = new HashSet<>();
    private final Set providedTypes = new HashSet<>();
    private final Set optionalTypes = new LinkedHashSet<>();
    private final Map aspectImportPrisms = new HashMap<>();
    private final List modules = new ArrayList<>();
    private final List delayQueue = new ArrayList<>();
    private final Set spiServices = new TreeSet<>();
    private boolean strictWiring;
    private final boolean mergeServices = APContext.getOption("mergeServices").map(Boolean::valueOf).orElse(true);

    void registerProvidedTypes(Set moduleFileProvided) {
      ExternalProvider.registerModuleProvidedTypes(providedTypes);
      providedTypes.addAll(moduleFileProvided);
    }
  }

  static void registerProvidedTypes(Set moduleFileProvided) {
    CTX.get().registerProvidedTypes(moduleFileProvided);
    addEventSPI();
  }

  private static void addEventSPI() {
    try {
      if (typeElement(EVENTS_SPI) != null || Class.forName(EVENTS_SPI) != null) {
        addInjectSPI(EVENTS_SPI);
      }
    } catch (final ClassNotFoundException e) {
      // nothing
    }
  }

  static String loadMetaInfServices() {
    return loadMetaInf(Constants.META_INF_SPI).stream()
        .filter(ProcessingContext::isInjectModule)
        .findFirst()
        .orElse(null);
  }

  private static boolean isInjectModule(String spi) {
    var moduleType = APContext.typeElement(spi);
    return moduleType != null && moduleType.getInterfaces().stream()
      .map(TypeMirror::toString)
      .anyMatch(s -> s.contains("AvajeModule"));
  }

  static List loadMetaInfCustom() {
    return loadMetaInf(Constants.META_INF_CUSTOM);
  }

  private static List loadMetaInf(String fullName) {
    try {
      final var fileObject = filer().getResource(StandardLocation.CLASS_OUTPUT, "", fullName);
      if (fileObject != null) {
        final List lines = new ArrayList<>();
        final var reader = fileObject.openReader(true);
        final var lineReader = new LineNumberReader(reader);
        String line;
        while ((line = lineReader.readLine()) != null) {
          line = line.trim();
          if (!line.isEmpty()) {
            lines.add(line);
          }
        }
        return lines;
      }

    } catch (FileNotFoundException | NoSuchFileException e) {
      // logDebug("no services file yet");
    } catch (final FilerException e) {
      logNote("FilerException reading services file");
    } catch (final Exception e) {
      logWarn("Error reading services file: %s", e.getMessage());
    }
    return Collections.emptyList();
  }

  static void addInjectSPI(String type) {
    CTX.get().spiServices.add(type);
  }

  static void addExternalInjectSPI(String type) {
    if (CTX.get().mergeServices) {
      CTX.get().spiServices.add(type);
    }
  }

  static FileObject createMetaInfWriterFor(String interfaceType) throws IOException {
    return filer().createResource(StandardLocation.CLASS_OUTPUT, "", interfaceType);
  }

  static TypeElement elementMaybe(String rawType) {
    if (rawType == null) {
      return null;
    } else {
      return elements().getTypeElement(rawType);
    }
  }

  static TypeElement asElement(TypeMirror returnType) {
    final var wrapper = PrimitiveUtil.wrap(returnType.toString());
    return wrapper == null ? asTypeElement(returnType) : typeElement(wrapper);
  }

  static boolean isUncheckedException(TypeMirror returnType) {
    final var runtime = typeElement("java.lang.RuntimeException").asType();
    return types().isSubtype(returnType, runtime);
  }

  static void addModule(String moduleFullName) {
    if (moduleFullName != null) {
      CTX.get().uniqueModuleNames.add(moduleFullName);
    }
  }

  static boolean isDuplicateModule(String moduleFullName) {
    return CTX.get().uniqueModuleNames.contains(moduleFullName);
  }

  static boolean externallyProvided(String type) {
    return CTX.get().providedTypes.contains(type) || CTX.get().optionalTypes.contains(type);
  }

  static void addOptionalType(String paramType, String name) {
    if (!CTX.get().providedTypes.contains(paramType)) {
      CTX.get().optionalTypes.add(ProcessorUtils.trimAnnotations(Util.addQualifierSuffixTrim(name, paramType)));
    }
  }

  static void addImportedAspects(Map importedMap) {
    CTX.get().aspectImportPrisms.putAll(importedMap);
  }

  static void validateModule() {
    APContext.moduleInfoReader().ifPresent(reader -> {
      CTX.get().spiServices.remove(EVENTS_SPI);
      reader.validateServices("io.avaje.inject.spi.InjectExtension", CTX.get().spiServices);
    });
  }

  static Optional getImportedAspect(String type) {
    return Optional.ofNullable(CTX.get().aspectImportPrisms.get(type));
  }

  static Set delayedElements() {
    var set =
      CTX.get().delayQueue.stream()
        .map(t -> t.getQualifiedName().toString())
        .map(APContext::typeElement)
        .collect(toSet());
    CTX.get().delayQueue.clear();
    return set;
  }

  static boolean delayUntilNextRound(TypeElement element) {
    if (!processingOver) {
      CTX.get().delayQueue.add(element);
    }
    return !processingOver;
  }

  static void clear() {
    CTX.remove();
    APContext.clear();
  }

  static void addModule(ModuleData module) {
    CTX.get().modules.add(module);
  }

  static List modules() {
    return CTX.get().modules;
  }

  static void strictWiring(boolean strictWiring) {
    CTX.get().strictWiring = strictWiring;
  }

  static boolean strictWiring() {
    return CTX.get().strictWiring;
  }

  static void processingOver(boolean over) {
    processingOver = over;
  }

  static void writeSPIServicesFile() {
    readExistingMetaInfServices();
    if (CTX.get().spiServices.isEmpty()) {
      // no services to register
      return;
    }
    try {
      FileObject jfo = createMetaInfWriterFor(Constants.META_INF_SPI);
      if (jfo != null) {
        var writer = new Append(jfo.openWriter());
        for (var service : CTX.get().spiServices) {
          writer.append(service).eol();
        }
        writer.close();
      }
    } catch (IOException e) {
      logError("Failed to write services file %s", e.getMessage());
    }
  }

  private static void readExistingMetaInfServices() {
    try (final var file =
           APContext.filer()
             .getResource(StandardLocation.CLASS_OUTPUT, "", Constants.META_INF_SPI)
             .toUri()
             .toURL()
             .openStream();
         final var buffer = new BufferedReader(new InputStreamReader(file));) {

      String line;
      while ((line = buffer.readLine()) != null) {
        line.replaceAll("\\s", "")
          .replace(",", "\n")
          .lines()
          .forEach(ProcessingContext::addInjectSPI);
      }
    } catch (Exception e) {
      // not a critical error
    }
  }

  static void registerExternalProvidedTypes(ScopeInfo scopeInfo) {
    ExternalProvider.scanAllInjectPlugins(scopeInfo);
    ExternalProvider.scanAllAvajeModules(CTX.get().providedTypes);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy