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