001/** 002 * Copyright 2013 John Ericksen 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.androidtransfuse; 017 018import com.google.auto.service.AutoService; 019import com.google.common.collect.ImmutableSet; 020import org.androidtransfuse.adapter.ASTType; 021import org.androidtransfuse.adapter.classes.ReloadableASTClassFactory; 022import org.androidtransfuse.adapter.element.ASTElementConverterFactory; 023import org.androidtransfuse.adapter.element.ReloadableASTElementFactory; 024import org.androidtransfuse.annotations.*; 025import org.androidtransfuse.bootstrap.Bootstrap; 026import org.androidtransfuse.bootstrap.Bootstraps; 027import org.androidtransfuse.config.EnterableScope; 028import org.androidtransfuse.config.TransfuseAndroidModule; 029import org.androidtransfuse.model.manifest.Manifest; 030import org.androidtransfuse.model.r.RBuilder; 031import org.androidtransfuse.model.r.RResource; 032import org.androidtransfuse.model.r.RResourceComposite; 033import org.androidtransfuse.plugins.PluginModule; 034import org.androidtransfuse.processor.GenerateModuleProcessor; 035import org.androidtransfuse.processor.TransfuseProcessor; 036import org.androidtransfuse.scope.ScopeKey; 037import org.androidtransfuse.util.Logger; 038import org.androidtransfuse.util.ManifestLocator; 039import org.androidtransfuse.util.ManifestSerializer; 040 041import javax.annotation.processing.ProcessingEnvironment; 042import javax.annotation.processing.Processor; 043import javax.annotation.processing.RoundEnvironment; 044import javax.inject.Inject; 045import javax.inject.Provider; 046import javax.inject.Singleton; 047import javax.lang.model.element.Element; 048import javax.lang.model.element.TypeElement; 049import javax.lang.model.util.ElementFilter; 050import javax.lang.model.util.Elements; 051import java.io.File; 052import java.lang.annotation.Annotation; 053import java.util.Collection; 054import java.util.Set; 055 056import static com.google.common.collect.Collections2.transform; 057 058/** 059 * Transfuse Annotation processor. Kicks off the process of analyzing and generating code based on the compiled 060 * codebase. 061 * <p/> 062 * To use this class, you simply have to annotate your classes with the proper root components (Activity, 063 * Application, etc) and have this annotation processor on the classpath during a full compilation. 064 * <p/> 065 * This approach is compatible with Java 6 and above. 066 * <p/> 067 * See http://androidtransfuse.org for more details 068 * 069 * @author John Ericksen 070 */ 071@SupportedAnnotations({ 072 Activity.class, 073 Application.class, 074 BroadcastReceiver.class, 075 Service.class, 076 Fragment.class, 077 TransfuseModule.class, 078 Factory.class, 079 ImplementedBy.class}) 080@Bootstrap 081@AutoService(Processor.class) 082public class TransfuseAnnotationProcessor extends AnnotationProcessorBase { 083 084 @Inject 085 private ASTElementConverterFactory astElementConverterFactory; 086 @Inject 087 private ManifestSerializer manifestParser; 088 @Inject 089 private RBuilder rBuilder; 090 @Inject 091 private ReloadableASTElementFactory reloadableASTElementFactory; 092 @Inject 093 private ReloadableASTClassFactory reloadableASTClassFactory; 094 @Inject 095 private ManifestLocator manifestLocator; 096 @Inject 097 private Logger logger; 098 @Inject 099 @ScopeReference(ConfigurationScope.class) 100 private EnterableScope configurationScope; 101 @Inject 102 private Provider<TransfuseProcessor> processorProvider; 103 @Inject 104 private Elements elements; 105 private boolean baseModuleConfiguration = false; 106 107 @Override 108 public void init(final ProcessingEnvironment processingEnv) { 109 super.init(processingEnv); 110 111 Bootstraps.getInjector(TransfuseAnnotationProcessor.class) 112 .add(Singleton.class, ScopeKey.of(ProcessingEnvironment.class), processingEnv) 113 .inject(this); 114 } 115 116 @Override 117 public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment roundEnvironment) { 118 119 long start = System.currentTimeMillis(); 120 121 //setup transfuse processor with manifest and R classes 122 File manifestFile = manifestLocator.findManifest(); 123 Manifest manifest = manifestParser.readManifest(manifestFile); 124 125 RResourceComposite r = new RResourceComposite( 126 buildR(rBuilder, manifest.getApplicationPackage() + ".R"), 127 buildR(rBuilder, "android.R")); 128 129 configurationScope.enter(); 130 131 configurationScope.seed(ScopeKey.of(File.class).annotatedBy("@javax.inject.Named(value=" + TransfuseAndroidModule.MANIFEST_FILE + ")"), manifestFile); 132 configurationScope.seed(ScopeKey.of(RResource.class), r); 133 configurationScope.seed(ScopeKey.of(Manifest.class).annotatedBy("@javax.inject.Named(value=" + TransfuseAndroidModule.ORIGINAL_MANIFEST + ")"), manifest); 134 135 TransfuseProcessor transfuseProcessor = processorProvider.get(); 136 137 if (!baseModuleConfiguration) { 138 transfuseProcessor.submit(TransfuseModule.class, reloadableASTClassFactory.apply(PluginModule.class)); 139 transfuseProcessor.submit(TransfuseModule.class, reloadableASTElementFactory.apply(elements.getTypeElement(APIModule.class.getName()))); 140 baseModuleConfiguration = true; 141 } 142 143 transfuseProcessor.submit(Application.class, buildASTCollection(roundEnvironment, Application.class)); 144 transfuseProcessor.submit(TransfuseModule.class, buildASTCollection(roundEnvironment, TransfuseModule.class)); 145 transfuseProcessor.submit(ImplementedBy.class, buildASTCollection(roundEnvironment, ImplementedBy.class)); 146 transfuseProcessor.submit(Factory.class, buildASTCollection(roundEnvironment, Factory.class)); 147 transfuseProcessor.submit(Activity.class, buildASTCollection(roundEnvironment, Activity.class)); 148 transfuseProcessor.submit(BroadcastReceiver.class, buildASTCollection(roundEnvironment, BroadcastReceiver.class)); 149 transfuseProcessor.submit(Service.class, buildASTCollection(roundEnvironment, Service.class)); 150 transfuseProcessor.submit(Fragment.class, buildASTCollection(roundEnvironment, Fragment.class)); 151 152 transfuseProcessor.execute(); 153 154 if (roundEnvironment.processingOver()) { 155 transfuseProcessor.checkForErrors(); 156 } 157 158 //todo: debug logging 159 // logger.info("Transfuse took " + (System.currentTimeMillis() - start) + "ms to process"); 160 161 configurationScope.exit(); 162 163 return true; 164 } 165 166 private RResource buildR(RBuilder rBuilder, String className) { 167 TypeElement rTypeElement = elements.getTypeElement(className); 168 if (rTypeElement != null) { 169 Collection<ASTType> rInnerTypes = wrapASTCollection(ElementFilter.typesIn(rTypeElement.getEnclosedElements())); 170 return rBuilder.buildR(rInnerTypes); 171 } 172 return null; 173 } 174 175 private Collection<Provider<ASTType>> buildASTCollection(RoundEnvironment round, Class<? extends Annotation> annotation) { 176 return reloadableASTElementFactory.buildProviders(round.getElementsAnnotatedWith(annotation)); 177 } 178 179 private Collection<ASTType> wrapASTCollection(Collection<? extends Element> elementCollection) { 180 return transform(elementCollection, 181 astElementConverterFactory.buildASTElementConverter(ASTType.class) 182 ); 183 } 184 185 @Override 186 public Set<String> getSupportedOptions() { 187 return ImmutableSet.of( 188 GenerateModuleProcessor.MANIFEST_PROCESSING_OPTION, 189 ManifestLocator.ANDROID_MANIFEST_FILE_OPTION); 190 } 191}