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.analysis;
017
018import org.androidtransfuse.adapter.ASTMethod;
019import org.androidtransfuse.adapter.ASTType;
020import org.androidtransfuse.adapter.PackageClass;
021import org.androidtransfuse.adapter.classes.ASTClassFactory;
022import org.androidtransfuse.adapter.element.ASTElementFactory;
023import org.androidtransfuse.adapter.element.ASTTypeBuilderVisitor;
024import org.androidtransfuse.analysis.repository.InjectionNodeBuilderRepository;
025import org.androidtransfuse.analysis.repository.InjectionNodeBuilderRepositoryFactory;
026import org.androidtransfuse.annotations.*;
027import org.androidtransfuse.experiment.ComponentDescriptor;
028import org.androidtransfuse.experiment.ScopesGeneration;
029import org.androidtransfuse.experiment.generators.FragmentLayoutGenerator;
030import org.androidtransfuse.experiment.generators.ObservesExpressionGenerator;
031import org.androidtransfuse.experiment.generators.OnCreateInjectionGenerator;
032import org.androidtransfuse.gen.componentBuilder.ComponentBuilderFactory;
033import org.androidtransfuse.gen.componentBuilder.ListenerRegistrationGenerator;
034import org.androidtransfuse.gen.variableBuilder.*;
035import org.androidtransfuse.model.InjectionSignature;
036import org.androidtransfuse.scope.ApplicationScope;
037import org.androidtransfuse.util.AndroidLiterals;
038
039import javax.inject.Inject;
040import javax.lang.model.type.TypeMirror;
041
042import static org.androidtransfuse.util.TypeMirrorUtil.getTypeMirror;
043
044/**
045 * @author John Ericksen
046 */
047public class FragmentAnalysis implements Analysis<ComponentDescriptor> {
048
049    private final ASTClassFactory astClassFactory;
050    private final ASTElementFactory astElementFactory;
051    private final ProviderInjectionNodeBuilderFactory providerInjectionNodeBuilder;
052    private final AnalysisContextFactory analysisContextFactory;
053    private final InjectionBindingBuilder injectionBindingBuilder;
054    private final ASTTypeBuilderVisitor astTypeBuilderVisitor;
055    private final InjectionNodeBuilderRepositoryFactory injectionNodeBuilderRepositoryFactory;
056    private final ComponentBuilderFactory componentBuilderFactory;
057    private final ListenerRegistrationGenerator.ListerRegistrationGeneratorFactory listenerRegistrationGeneratorFactory;
058    private final ObservesExpressionGenerator.ObservesExpressionGeneratorFactory observesExpressionGeneratorFactory;
059    private final ExtraInjectionNodeBuilder extraInjectionNodeBuilder;
060    private final SystemServiceBindingInjectionNodeBuilder systemServiceBindingInjectionNodeBuilder;
061    private final ResourceInjectionNodeBuilder resourceInjectionNodeBuilder;
062    private final PreferenceInjectionNodeBuilder preferenceInjectionNodeBuilder;
063    private final FragmentViewInjectionNodeBuilder fragmentViewInjectionNodeBuilder;
064    private final OnCreateInjectionGenerator.InjectionGeneratorFactory injectionGeneratorFactory;
065    private final FragmentLayoutGenerator fragmentLayoutGenerator;
066    private final ScopesGeneration.ScopesGenerationFactory scopesGenerationFactory;
067    private final ComponentAnalysis componentAnalysis;
068
069    @Inject
070    public FragmentAnalysis(ASTClassFactory astClassFactory,
071                            ASTElementFactory astElementFactory,
072                            ProviderInjectionNodeBuilderFactory providerInjectionNodeBuilder, AnalysisContextFactory analysisContextFactory,
073                            InjectionBindingBuilder injectionBindingBuilder,
074                            ASTTypeBuilderVisitor astTypeBuilderVisitor,
075                            InjectionNodeBuilderRepositoryFactory injectionNodeBuilderRepositoryFactory,
076                            ComponentBuilderFactory componentBuilderFactory,
077                            ListenerRegistrationGenerator.ListerRegistrationGeneratorFactory listenerRegistrationGeneratorFactory,
078                            ObservesExpressionGenerator.ObservesExpressionGeneratorFactory observesExpressionGeneratorFactory,
079                            ExtraInjectionNodeBuilder extraInjectionNodeBuilder,
080                            SystemServiceBindingInjectionNodeBuilder systemServiceBindingInjectionNodeBuilder,
081                            ResourceInjectionNodeBuilder resourceInjectionNodeBuilder,
082                            PreferenceInjectionNodeBuilder preferenceInjectionNodeBuilder,
083                            FragmentViewInjectionNodeBuilder fragmentViewInjectionNodeBuilder,
084                            OnCreateInjectionGenerator.InjectionGeneratorFactory injectionGeneratorFactory,
085                            FragmentLayoutGenerator fragmentLayoutGenerator,
086                            ScopesGeneration.ScopesGenerationFactory scopesGenerationFactory,
087                            ComponentAnalysis componentAnalysis) {
088        this.astClassFactory = astClassFactory;
089        this.astElementFactory = astElementFactory;
090        this.providerInjectionNodeBuilder = providerInjectionNodeBuilder;
091        this.analysisContextFactory = analysisContextFactory;
092        this.injectionBindingBuilder = injectionBindingBuilder;
093        this.astTypeBuilderVisitor = astTypeBuilderVisitor;
094        this.injectionNodeBuilderRepositoryFactory = injectionNodeBuilderRepositoryFactory;
095        this.componentBuilderFactory = componentBuilderFactory;
096        this.extraInjectionNodeBuilder = extraInjectionNodeBuilder;
097        this.systemServiceBindingInjectionNodeBuilder = systemServiceBindingInjectionNodeBuilder;
098        this.resourceInjectionNodeBuilder = resourceInjectionNodeBuilder;
099        this.preferenceInjectionNodeBuilder = preferenceInjectionNodeBuilder;
100        this.fragmentViewInjectionNodeBuilder = fragmentViewInjectionNodeBuilder;
101        this.listenerRegistrationGeneratorFactory = listenerRegistrationGeneratorFactory;
102        this.observesExpressionGeneratorFactory = observesExpressionGeneratorFactory;
103        this.injectionGeneratorFactory = injectionGeneratorFactory;
104        this.fragmentLayoutGenerator = fragmentLayoutGenerator;
105        this.scopesGenerationFactory = scopesGenerationFactory;
106        this.componentAnalysis = componentAnalysis;
107    }
108
109    @Override
110    public ComponentDescriptor analyze(ASTType astType) {
111        Fragment fragmentAnnotation = astType.getAnnotation(Fragment.class);
112        PackageClass fragmentClassName;
113        ComponentDescriptor fragmentDescriptor = null;
114
115        if (!astType.extendsFrom(AndroidLiterals.FRAGMENT)) {
116            //generated Android fragment
117            fragmentClassName = componentAnalysis.buildComponentPackageClass(astType, fragmentAnnotation.name(), "Fragment");
118
119            TypeMirror type = getTypeMirror(fragmentAnnotation, "type");
120
121            ASTType fragmentType = type == null  || type.toString().equals("java.lang.Object") ? AndroidLiterals.FRAGMENT : type.accept(astTypeBuilderVisitor, null);
122
123            AnalysisContext context = analysisContextFactory.buildAnalysisContext(buildVariableBuilderMap(fragmentType));
124
125            fragmentDescriptor = new ComponentDescriptor(astType, fragmentType, fragmentClassName, context);
126
127            componentAnalysis.setupGenerators(fragmentDescriptor, fragmentType, Fragment.class);
128
129            //application generation profile
130            setupFragmentProfile(fragmentDescriptor, astType);
131        }
132
133        return fragmentDescriptor;
134    }
135
136    private void setupFragmentProfile(ComponentDescriptor fragmentDescriptor, ASTType astType) {
137        ASTMethod onCreateViewMethod = getASTMethod("onCreateView", AndroidLiterals.LAYOUT_INFLATER, AndroidLiterals.VIEW_GROUP, AndroidLiterals.BUNDLE);
138
139        fragmentDescriptor.getGenerators().add(injectionGeneratorFactory.build(onCreateViewMethod, astType));
140        fragmentDescriptor.getGenerators().add(scopesGenerationFactory.build(onCreateViewMethod));
141
142        fragmentDescriptor.getGenerators().add(fragmentLayoutGenerator);
143
144
145
146        // onSaveInstanceState
147        ASTMethod onSaveInstanceStateMethod = getASTMethod("onSaveInstanceState", AndroidLiterals.BUNDLE);
148        fragmentDescriptor.getGenerators().add(componentBuilderFactory.buildFragmentOnSaveInstanceStateMethodCallbackGenerator(astClassFactory.getType(OnSaveInstanceState.class), onSaveInstanceStateMethod, getASTMethod("onCreateView", AndroidLiterals.LAYOUT_INFLATER, AndroidLiterals.VIEW_GROUP, AndroidLiterals.BUNDLE)));
149
150        fragmentDescriptor.getGenerators().add(listenerRegistrationGeneratorFactory.build(getASTMethod("onCreateView", AndroidLiterals.LAYOUT_INFLATER, AndroidLiterals.VIEW_GROUP, AndroidLiterals.BUNDLE)));
151
152        fragmentDescriptor.getGenerators().add(observesExpressionGeneratorFactory.build(
153                getASTMethod("onCreateView", AndroidLiterals.LAYOUT_INFLATER, AndroidLiterals.VIEW_GROUP, AndroidLiterals.BUNDLE),
154                getASTMethod("onResume"),
155                getASTMethod("onPause")
156        ));
157
158    }
159
160    private ASTMethod getASTMethod(String methodName, ASTType... args) {
161        return getASTMethod(AndroidLiterals.FRAGMENT, methodName, args);
162    }
163
164    private ASTMethod getASTMethod(ASTType type, String methodName, ASTType... args) {
165        return astElementFactory.findMethod(type, methodName, args);
166    }
167
168    private InjectionNodeBuilderRepository buildVariableBuilderMap(ASTType fragmentType) {
169
170        InjectionNodeBuilderRepository injectionNodeBuilderRepository = componentAnalysis.setupInjectionNodeBuilderRepository(fragmentType, Fragment.class);
171
172        ASTType applicationScopeType = astElementFactory.getType(ApplicationScope.ApplicationScopeQualifier.class);
173        ASTType applicationProvider = astElementFactory.getType(ApplicationScope.ApplicationProvider.class);
174        injectionNodeBuilderRepository.putType(AndroidLiterals.APPLICATION, providerInjectionNodeBuilder.builderProviderBuilder(applicationProvider));
175        injectionNodeBuilderRepository.putScoped(new InjectionSignature(AndroidLiterals.APPLICATION), applicationScopeType);
176
177        injectionNodeBuilderRepository.putType(AndroidLiterals.FRAGMENT, injectionBindingBuilder.buildThis(AndroidLiterals.FRAGMENT));
178        injectionNodeBuilderRepository.putType(AndroidLiterals.ACTIVITY, injectionBindingBuilder.dependency(AndroidLiterals.FRAGMENT).invoke(AndroidLiterals.ACTIVITY, "getActivity").build());
179        injectionNodeBuilderRepository.putType(AndroidLiterals.CONTEXT, injectionBindingBuilder.dependency(AndroidLiterals.FRAGMENT).invoke(AndroidLiterals.CONTEXT, "getActivity").build());
180        injectionNodeBuilderRepository.putType(AndroidLiterals.FRAGMENT_MANAGER, injectionBindingBuilder.dependency(AndroidLiterals.FRAGMENT).invoke(AndroidLiterals.FRAGMENT_MANAGER, "getFragmentManager").build());
181
182
183        while(!fragmentType.equals(AndroidLiterals.FRAGMENT) && fragmentType.inheritsFrom(AndroidLiterals.FRAGMENT)){
184            injectionNodeBuilderRepository.putType(fragmentType, injectionBindingBuilder.buildThis(fragmentType));
185            fragmentType = fragmentType.getSuperClass();
186        }
187
188        injectionNodeBuilderRepository.putAnnotation(Extra.class, extraInjectionNodeBuilder);
189        injectionNodeBuilderRepository.putAnnotation(Resource.class, resourceInjectionNodeBuilder);
190        injectionNodeBuilderRepository.putAnnotation(SystemService.class, systemServiceBindingInjectionNodeBuilder);
191        injectionNodeBuilderRepository.putAnnotation(Preference.class, preferenceInjectionNodeBuilder);
192        injectionNodeBuilderRepository.putAnnotation(org.androidtransfuse.annotations.View.class, fragmentViewInjectionNodeBuilder);
193
194        injectionNodeBuilderRepository.addRepository(injectionNodeBuilderRepositoryFactory.buildApplicationInjections());
195        injectionNodeBuilderRepository.addRepository(injectionNodeBuilderRepositoryFactory.buildModuleConfiguration());
196
197        return injectionNodeBuilderRepository;
198    }
199}