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 com.google.common.base.Joiner;
019import com.google.common.collect.ImmutableMap;
020import org.androidtransfuse.EventMapping;
021import org.androidtransfuse.TransfuseAnalysisException;
022import org.androidtransfuse.adapter.ASTMethod;
023import org.androidtransfuse.adapter.ASTType;
024import org.androidtransfuse.adapter.MethodSignature;
025import org.androidtransfuse.adapter.PackageClass;
026import org.androidtransfuse.adapter.classes.ASTClassFactory;
027import org.androidtransfuse.adapter.element.ASTElementFactory;
028import org.androidtransfuse.analysis.astAnalyzer.RegistrationAnalyzer;
029import org.androidtransfuse.analysis.astAnalyzer.registration.RegistrationGenerators;
030import org.androidtransfuse.analysis.repository.InjectionNodeBuilderRepository;
031import org.androidtransfuse.analysis.repository.RegistrationGeneratorFactory;
032import org.androidtransfuse.experiment.ComponentDescriptor;
033import org.androidtransfuse.experiment.generators.MethodCallbackGenerator;
034import org.androidtransfuse.experiment.generators.SuperGenerator;
035import org.androidtransfuse.gen.componentBuilder.ComponentBuilderFactory;
036import org.apache.commons.lang.StringUtils;
037
038import javax.inject.Inject;
039import javax.inject.Provider;
040import java.lang.annotation.Annotation;
041import java.util.List;
042import java.util.Map;
043
044/**
045 * @author John Ericksen
046 */
047public class ComponentAnalysis {
048
049    private final ConfigurationRepositoryImpl repository;
050    private final ASTElementFactory astElementFactory;
051    private final ASTClassFactory astClassFactory;
052    private final ComponentBuilderFactory componentBuilderFactory;
053    private final SuperGenerator.SuperGeneratorFactory superGeneratorFactory;
054    private final Provider<InjectionNodeBuilderRepository> injectionNodeBuilderRepositoryProvider;
055    private final RegistrationGenerators registrationGenerators;
056    private final ManualSuperGenerator.Factory manualSuperGeneratorFactory;
057
058    @Inject
059    public ComponentAnalysis(ConfigurationRepositoryImpl repository,
060                             ASTElementFactory astElementFactory,
061                             ASTClassFactory astClassFactory,
062                             ComponentBuilderFactory componentBuilderFactory,
063                             SuperGenerator.SuperGeneratorFactory superGeneratorFactory,
064                             Provider<InjectionNodeBuilderRepository> injectionNodeBuilderRepositoryProvider,
065                             RegistrationGenerators registrationGenerators,
066                             ManualSuperGenerator.Factory manualSuperGeneratorFactory){
067        this.repository = repository;
068        this.astElementFactory = astElementFactory;
069        this.astClassFactory = astClassFactory;
070        this.componentBuilderFactory = componentBuilderFactory;
071        this.superGeneratorFactory = superGeneratorFactory;
072        this.injectionNodeBuilderRepositoryProvider = injectionNodeBuilderRepositoryProvider;
073        this.registrationGenerators = registrationGenerators;
074        this.manualSuperGeneratorFactory = manualSuperGeneratorFactory;
075    }
076
077
078    public void setupGenerators(ComponentDescriptor descriptor, ASTType componentType, Class<? extends Annotation> componentAnnotation) {
079        Registration registration = repository.getRegistration(componentType, componentAnnotation);
080        ASTMethod registrationMethod = getASTMethod(componentType, registration.getMethodName(), registration.getParameters());
081        descriptor.getGenerateFirst().add(new MethodSignature(registrationMethod));
082        addSuperCalls(descriptor, componentType, componentAnnotation);
083        addEvents(descriptor, componentType, componentAnnotation, registrationMethod);
084
085        descriptor.getGenerators().add(manualSuperGeneratorFactory.build(registrationMethod));
086    }
087
088    private void addSuperCalls(ComponentDescriptor descriptor, ASTType componentType, Class<? extends Annotation> componentAnnotation) {
089        for (SuperCallMapping superCallMapping : repository.getSuperCalls(componentType, componentAnnotation)) {
090            descriptor.getGenerators().add(superGeneratorFactory.build(getASTMethod(componentType, superCallMapping.getMethodName(), superCallMapping.getParameters())));
091        }
092    }
093
094    public void addEvents(ComponentDescriptor descriptor, ASTType componentType, Class<? extends Annotation> componentAnnotation, ASTMethod registrationMethod) {
095        for (EventMapping eventMapping : repository.getEvents(componentType, componentAnnotation)) {
096            descriptor.getGenerators().add(buildEventMethod(componentType, eventMapping, registrationMethod));
097        }
098    }
099
100    private MethodCallbackGenerator buildEventMethod(ASTType componentType, EventMapping eventMapping, ASTMethod registrationMethod){
101        ASTMethod method = getASTMethod(componentType, eventMapping.getMethodName(), eventMapping.getMethodArguments());
102
103        if(method == null){
104            throw new TransfuseAnalysisException("Unable to find method with signature: " +
105                    componentType + "." +
106                    eventMapping.getMethodName() + "(" + Joiner.on(", ").join(eventMapping.getMethodArguments()) + ")");
107        }
108
109        ASTType eventAnnotation = astClassFactory.getType(eventMapping.getAnnotation());
110
111        return componentBuilderFactory.buildMethodCallbackGenerator(eventAnnotation, method, registrationMethod);
112    }
113
114    private ASTMethod getASTMethod(ASTType type, String methodName, List<ASTType> args) {
115        return astElementFactory.findMethod(type, methodName, args.toArray(new ASTType[args.size()]));
116    }
117
118    public InjectionNodeBuilderRepository setupInjectionNodeBuilderRepository(ASTType componentType, Class<? extends Annotation> componentAnnotation) {
119        InjectionNodeBuilderRepository injectionNodeBuilderRepository = injectionNodeBuilderRepositoryProvider.get();
120
121        ImmutableMap.Builder<ASTType, RegistrationGeneratorFactory> builder = ImmutableMap.builder();
122
123        for (Map.Entry<ASTType, ListenableMethod> listenerEntry : repository.getListeners(componentType, componentAnnotation).entrySet()) {
124            builder.put(listenerEntry.getKey(), registrationGenerators.buildViewRegistrationGenerator(listenerEntry.getValue()));
125        }
126
127        for (Class<?> listenerClass : repository.getCallThroughClasses(componentType, componentAnnotation)) {
128            ASTType listenerType = astElementFactory.getType(listenerClass);
129            builder.put(listenerType, registrationGenerators.buildCallThroughMethodGenerator(listenerType));
130        }
131
132        injectionNodeBuilderRepository.getAnalysisRepository().add(new RegistrationAnalyzer(builder.build()));
133
134        return injectionNodeBuilderRepository;
135    }
136
137    public PackageClass buildComponentPackageClass(ASTType astType, String className, String componentName) {
138        PackageClass inputPackageClass = astType.getPackageClass();
139
140        if (StringUtils.isBlank(className)) {
141            return inputPackageClass.append(componentName);
142        } else {
143            return inputPackageClass.replaceName(className);
144        }
145    }
146}