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