/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.litho.specmodels.processor;
import javax.annotation.Nullable;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import com.facebook.litho.annotations.FromBind;
import com.facebook.litho.annotations.FromBoundsDefined;
import com.facebook.litho.annotations.FromMeasure;
import com.facebook.litho.annotations.FromMeasureBaseline;
import com.facebook.litho.annotations.FromPrepare;
import com.facebook.litho.annotations.MountSpec;
import com.facebook.litho.annotations.OnCreateMountContent;
import com.facebook.litho.annotations.OnCreateTreeProp;
import com.facebook.litho.annotations.ShouldUpdate;
import com.facebook.litho.specmodels.internal.ImmutableList;
import com.facebook.litho.specmodels.model.ClassNames;
import com.facebook.litho.specmodels.model.DelegateMethodDescriptions;
import com.facebook.litho.specmodels.model.DependencyInjectionHelper;
import com.facebook.litho.specmodels.model.MountSpecModel;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
/**
* Factory for creating {@link MountSpecModel}s.
*/
public class MountSpecModelFactory {
private static final List<Class<? extends Annotation>> INTER_STAGE_INPUT_ANNOTATIONS =
new ArrayList<>();
private static final List<Class<? extends Annotation>> DELEGATE_METHOD_ANNOTATIONS =
new ArrayList<>();
static {
INTER_STAGE_INPUT_ANNOTATIONS.add(FromPrepare.class);
INTER_STAGE_INPUT_ANNOTATIONS.add(FromMeasureBaseline.class);
INTER_STAGE_INPUT_ANNOTATIONS.add(FromMeasure.class);
INTER_STAGE_INPUT_ANNOTATIONS.add(FromBoundsDefined.class);
INTER_STAGE_INPUT_ANNOTATIONS.add(FromBind.class);
DELEGATE_METHOD_ANNOTATIONS.addAll(
DelegateMethodDescriptions.MOUNT_SPEC_DELEGATE_METHODS_MAP.keySet());
DELEGATE_METHOD_ANNOTATIONS.add(OnCreateTreeProp.class);
DELEGATE_METHOD_ANNOTATIONS.add(ShouldUpdate.class);
}
/**
* Create a {@link MountSpecModel} from the given {@link TypeElement} and an optional
* {@link DependencyInjectionHelper}.
*/
public static MountSpecModel create(
Elements elements,
TypeElement element,
@Nullable DependencyInjectionHelper dependencyInjectionHelper) {
return new MountSpecModel(
element.getQualifiedName().toString(),
getValue(element),
DelegateMethodExtractor.getDelegateMethods(
element,
DELEGATE_METHOD_ANNOTATIONS,
INTER_STAGE_INPUT_ANNOTATIONS),
EventMethodExtractor.getOnEventMethods(
elements, element, INTER_STAGE_INPUT_ANNOTATIONS),
UpdateStateMethodExtractor.getOnUpdateStateMethods(
element,
INTER_STAGE_INPUT_ANNOTATIONS),
ImmutableList.copyOf(TypeVariablesExtractor.getTypeVariables(element)),
ImmutableList.copyOf(PropDefaultsExtractor.getPropDefaults(element)),
EventDeclarationsExtractor.getEventDeclarations(elements, element, MountSpec.class),
JavadocExtractor.getClassJavadoc(elements, element),
JavadocExtractor.getPropJavadocs(elements, element),
isPublic(element),
dependencyInjectionHelper,
isPureRender(element),
canMountIncrementally(element),
shouldUseDisplayList(element),
getPoolSize(element),
getMountType(element),
element);
}
private static String getValue(TypeElement element) {
final MountSpec mountSpec = element.getAnnotation(MountSpec.class);
return mountSpec != null ? mountSpec.value() : "";
}
private static boolean isPublic(TypeElement element) {
final MountSpec mountSpec = element.getAnnotation(MountSpec.class);
return mountSpec == null || mountSpec.isPublic();
}
private static boolean isPureRender(TypeElement element) {
final MountSpec mountSpec = element.getAnnotation(MountSpec.class);
return mountSpec != null && mountSpec.isPureRender();
}
private static boolean canMountIncrementally(TypeElement element) {
final MountSpec mountSpec = element.getAnnotation(MountSpec.class);
return mountSpec != null && mountSpec.canMountIncrementally();
}
private static boolean shouldUseDisplayList(TypeElement element) {
final MountSpec mountSpec = element.getAnnotation(MountSpec.class);
return mountSpec != null && mountSpec.shouldUseDisplayList();
}
private static int getPoolSize(TypeElement element) {
final MountSpec mountSpec = element.getAnnotation(MountSpec.class);
return mountSpec != null ? mountSpec.poolSize() : 15;
}
private static TypeName getMountType(TypeElement element) {
for (Element enclosedElement : element.getEnclosedElements()) {
if (enclosedElement.getKind() != ElementKind.METHOD) {
continue;
}
if (enclosedElement.getAnnotation(OnCreateMountContent.class) != null) {
TypeMirror returnType = ((ExecutableElement) enclosedElement).getReturnType();
while (returnType.getKind() != TypeKind.NONE && returnType.getKind() != TypeKind.VOID) {
final TypeElement returnElement = (TypeElement) ((DeclaredType) returnType).asElement();
final TypeName type = ClassName.get(returnElement);
if (type.equals(ClassNames.VIEW)) {
return ClassNames.COMPONENT_LIFECYCLE_MOUNT_TYPE_VIEW;
} else if (type.equals(ClassNames.DRAWABLE)) {
return ClassNames.COMPONENT_LIFECYCLE_MOUNT_TYPE_DRAWABLE;
}
returnType = returnElement.getSuperclass();
}
}
}
return ClassNames.COMPONENT_LIFECYCLE_MOUNT_TYPE_NONE;
}
}