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}