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.TESTSCOPE); 049 annotations.add(Constants.CONTROLLER); 050 return annotations; 051 } 052 053 @Override 054 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 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 defaultScope.write(roundEnv.processingOver()); 071 allScopes.write(roundEnv.processingOver()); 072 return false; 073 } 074 075 private void readScopes(Set<? extends Element> scopes) { 076 for (Element element : scopes) { 077 if (element.getKind() == ElementKind.ANNOTATION_TYPE) { 078 // context.logDebug("detected scope annotation " + element); 079 TypeElement type = (TypeElement) element; 080 allScopes.addScopeAnnotation(type); 081 } 082 } 083 addTestScope(); 084 } 085 086 /** 087 * Add built-in test scope for <code>@TestScope</code> if available. 088 */ 089 private void addTestScope() { 090 TypeElement testScopeType = elementUtils.getTypeElement(Constants.TESTSCOPE); 091 if (testScopeType != null) { 092 allScopes.addScopeAnnotation(testScopeType); 093 } 094 } 095 096 /** 097 * Read the beans that have changed. 098 */ 099 private void readChangedBeans(Set<? extends Element> beans, boolean factory) { 100 for (Element element : beans) { 101 if (!(element instanceof TypeElement)) { 102 context.logError("unexpected type [" + element + "]"); 103 } else { 104 TypeElement typeElement = (TypeElement) element; 105 final ScopeInfo scope = findScope(typeElement); 106 if (!factory) { 107 // will be found via custom scope so effectively ignore additional @Singleton 108 if (scope == null) { 109 defaultScope.read(typeElement, false); 110 } 111 } else { 112 if (scope != null) { 113 // context.logWarn("Adding factory to custom scope "+element+" scope: "+scope); 114 scope.read(typeElement, true); 115 } else { 116 defaultScope.read(typeElement, true); 117 } 118 } 119 } 120 } 121 } 122 123 /** 124 * Find the scope if the Factory has a scope annotation. 125 */ 126 private ScopeInfo findScope(Element element) { 127 for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { 128 final ScopeInfo scopeInfo = allScopes.get(annotationMirror.getAnnotationType().toString()); 129 if (scopeInfo != null) { 130 return scopeInfo; 131 } 132 } 133 return null; 134 } 135 136 /** 137 * Read the existing meta data from InjectModule (if found) and the factory bean (if exists). 138 */ 139 private void readModule(RoundEnvironment roundEnv) { 140 if (readModuleInfo) { 141 // only read the module meta data once 142 return; 143 } 144 readModuleInfo = true; 145 String factory = context.loadMetaInfServices(); 146 if (factory != null) { 147 TypeElement moduleType = elementUtils.getTypeElement(factory); 148 if (moduleType != null) { 149 defaultScope.readModuleMetaData(moduleType); 150 } 151 } 152 allScopes.readModules(context.loadMetaInfCustom()); 153 readInjectModule(roundEnv); 154 } 155 156 /** 157 * Read InjectModule for things like package-info etc (not for custom scopes) 158 */ 159 private void readInjectModule(RoundEnvironment roundEnv) { 160 // read other that are annotated with InjectModule 161 Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(InjectModule.class); 162 if (!elementsAnnotatedWith.isEmpty()) { 163 Iterator<? extends Element> iterator = elementsAnnotatedWith.iterator(); 164 if (iterator.hasNext()) { 165 Element element = iterator.next(); 166 Scope scope = element.getAnnotation(Scope.class); 167 if (scope == null) { 168 // it it not a custom scope annotation 169 InjectModule annotation = element.getAnnotation(InjectModule.class); 170 if (annotation != null) { 171 defaultScope.details(annotation.name(), element); 172 } 173 } 174 } 175 } 176 } 177 178}