001package io.avaje.inject.generator; 002 003import io.avaje.inject.Factory; 004import io.avaje.inject.InjectModule; 005import jakarta.inject.Scope; 006import jakarta.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}