/**
* Copyright 2011-2015 John Ericksen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.androidtransfuse.analysis;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import org.androidtransfuse.EventMapping;
import org.androidtransfuse.TransfuseAnalysisException;
import org.androidtransfuse.adapter.ASTMethod;
import org.androidtransfuse.adapter.ASTType;
import org.androidtransfuse.adapter.MethodSignature;
import org.androidtransfuse.adapter.PackageClass;
import org.androidtransfuse.adapter.classes.ASTClassFactory;
import org.androidtransfuse.adapter.element.ASTElementFactory;
import org.androidtransfuse.analysis.astAnalyzer.RegistrationAnalyzer;
import org.androidtransfuse.analysis.astAnalyzer.registration.RegistrationGenerators;
import org.androidtransfuse.analysis.repository.InjectionNodeBuilderRepository;
import org.androidtransfuse.analysis.repository.RegistrationGeneratorFactory;
import org.androidtransfuse.experiment.ComponentDescriptor;
import org.androidtransfuse.experiment.generators.MethodCallbackGenerator;
import org.androidtransfuse.experiment.generators.SuperGenerator;
import org.androidtransfuse.gen.componentBuilder.ComponentBuilderFactory;
import org.apache.commons.lang.StringUtils;
import javax.inject.Inject;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
/**
* @author John Ericksen
*/
public class ComponentAnalysis {
private final ConfigurationRepositoryImpl repository;
private final ASTElementFactory astElementFactory;
private final ASTClassFactory astClassFactory;
private final ComponentBuilderFactory componentBuilderFactory;
private final SuperGenerator.SuperGeneratorFactory superGeneratorFactory;
private final Provider<InjectionNodeBuilderRepository> injectionNodeBuilderRepositoryProvider;
private final RegistrationGenerators registrationGenerators;
private final ManualSuperGenerator.Factory manualSuperGeneratorFactory;
@Inject
public ComponentAnalysis(ConfigurationRepositoryImpl repository,
ASTElementFactory astElementFactory,
ASTClassFactory astClassFactory,
ComponentBuilderFactory componentBuilderFactory,
SuperGenerator.SuperGeneratorFactory superGeneratorFactory,
Provider<InjectionNodeBuilderRepository> injectionNodeBuilderRepositoryProvider,
RegistrationGenerators registrationGenerators,
ManualSuperGenerator.Factory manualSuperGeneratorFactory){
this.repository = repository;
this.astElementFactory = astElementFactory;
this.astClassFactory = astClassFactory;
this.componentBuilderFactory = componentBuilderFactory;
this.superGeneratorFactory = superGeneratorFactory;
this.injectionNodeBuilderRepositoryProvider = injectionNodeBuilderRepositoryProvider;
this.registrationGenerators = registrationGenerators;
this.manualSuperGeneratorFactory = manualSuperGeneratorFactory;
}
public void setupGenerators(ComponentDescriptor descriptor, ASTType componentType, Class<? extends Annotation> componentAnnotation) {
Registration registration = repository.getRegistration(componentType, componentAnnotation);
ASTMethod registrationMethod = getASTMethod(componentType, registration.getMethodName(), registration.getParameters());
descriptor.getGenerateFirst().add(new MethodSignature(registrationMethod));
addSuperCalls(descriptor, componentType, componentAnnotation);
addEvents(descriptor, componentType, componentAnnotation, registrationMethod);
descriptor.getGenerators().add(manualSuperGeneratorFactory.build(registrationMethod));
}
private void addSuperCalls(ComponentDescriptor descriptor, ASTType componentType, Class<? extends Annotation> componentAnnotation) {
for (SuperCallMapping superCallMapping : repository.getSuperCalls(componentType, componentAnnotation)) {
descriptor.getGenerators().add(superGeneratorFactory.build(getASTMethod(componentType, superCallMapping.getMethodName(), superCallMapping.getParameters()), superCallMapping.isExecuteLast()));
}
}
public void addEvents(ComponentDescriptor descriptor, ASTType componentType, Class<? extends Annotation> componentAnnotation, ASTMethod registrationMethod) {
for (EventMapping eventMapping : repository.getEvents(componentType, componentAnnotation)) {
descriptor.getGenerators().add(buildEventMethod(componentType, eventMapping, registrationMethod));
}
}
private MethodCallbackGenerator buildEventMethod(ASTType componentType, EventMapping eventMapping, ASTMethod registrationMethod){
ASTMethod method = getASTMethod(componentType, eventMapping.getMethodName(), eventMapping.getMethodArguments());
if(method == null){
throw new TransfuseAnalysisException("Unable to find method with signature: " +
componentType + "." +
eventMapping.getMethodName() + "(" + Joiner.on(", ").join(eventMapping.getMethodArguments()) + ")");
}
ASTType eventAnnotation = astClassFactory.getType(eventMapping.getAnnotation());
return componentBuilderFactory.buildMethodCallbackGenerator(eventAnnotation, method, registrationMethod, eventMapping.isNullDelegateCheck());
}
private ASTMethod getASTMethod(ASTType type, String methodName, List<ASTType> args) {
return astElementFactory.findMethod(type, methodName, args.toArray(new ASTType[args.size()]));
}
public InjectionNodeBuilderRepository setupInjectionNodeBuilderRepository(ASTType componentType, Class<? extends Annotation> componentAnnotation) {
InjectionNodeBuilderRepository injectionNodeBuilderRepository = injectionNodeBuilderRepositoryProvider.get();
ImmutableMap.Builder<ASTType, RegistrationGeneratorFactory> builder = ImmutableMap.builder();
for (Map.Entry<ASTType, ListenableMethod> listenerEntry : repository.getListeners(componentType, componentAnnotation).entrySet()) {
builder.put(listenerEntry.getKey(), registrationGenerators.buildViewRegistrationGenerator(listenerEntry.getValue()));
}
for (Class<?> listenerClass : repository.getCallThroughClasses(componentType, componentAnnotation)) {
ASTType listenerType = astElementFactory.getType(listenerClass);
builder.put(listenerType, registrationGenerators.buildCallThroughMethodGenerator(listenerType));
}
injectionNodeBuilderRepository.getAnalysisRepository().add(new RegistrationAnalyzer(builder.build()));
return injectionNodeBuilderRepository;
}
public PackageClass buildComponentPackageClass(ASTType astType, String className, String componentName) {
PackageClass inputPackageClass = astType.getPackageClass();
if (StringUtils.isBlank(className)) {
return inputPackageClass.append(componentName);
} else {
return inputPackageClass.replaceName(className);
}
}
}